summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNathaniel McCallum <nathaniel@themccallums.org>2013-04-09 14:06:33 -0400
committerNathaniel McCallum <nathaniel@themccallums.org>2013-04-09 14:06:33 -0400
commit8d291c8c0a03c8e87fe437169336d8603dd717a7 (patch)
treeca4d7e7d3fcc4f82737911fccad2101c385f2e9a
parentffcebd6c2b9a347c58cabfa8b161be034c9dd4d4 (diff)
downloadkrb5-8d291c8c0a03c8e87fe437169336d8603dd717a7.tar.gz
krb5-8d291c8c0a03c8e87fe437169336d8603dd717a7.tar.xz
krb5-8d291c8c0a03c8e87fe437169336d8603dd717a7.zip
Update otp plugin backport patches
-rw-r--r--0001-add-k5memdup.patch34
-rw-r--r--0001-add-libk5radius.patch3797
-rw-r--r--0002-add-libkrad.patch3902
-rw-r--r--0003-Add-internal-KDC_DIR-macro.patch (renamed from 0002-Add-internal-KDC_DIR-macro.patch)6
-rw-r--r--0004-add-otp-plugin.patch (renamed from 0003-add-otp-plugin.patch)169
-rw-r--r--krb5.spec24
6 files changed, 4037 insertions, 3895 deletions
diff --git a/0001-add-k5memdup.patch b/0001-add-k5memdup.patch
new file mode 100644
index 0000000..e3b7cbb
--- /dev/null
+++ b/0001-add-k5memdup.patch
@@ -0,0 +1,34 @@
+From 5f7844ece4f81ce06f861c65a48c4e9dbeaa215e Mon Sep 17 00:00:00 2001
+From: Nathaniel McCallum <npmccallum@redhat.com>
+Date: Tue, 9 Apr 2013 11:17:04 -0400
+Subject: [PATCH 1/4] add k5memdup()
+
+---
+ src/include/k5-int.h | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/src/include/k5-int.h b/src/include/k5-int.h
+index 75e6783..7b5ab2c 100644
+--- a/src/include/k5-int.h
++++ b/src/include/k5-int.h
+@@ -2600,6 +2600,17 @@ k5alloc(size_t len, krb5_error_code *code)
+ return ptr;
+ }
+
++/* Return a copy of the len bytes of memory at in; set *code to 0 or ENOMEM. */
++static inline void *
++k5memdup(const void *in, size_t len, krb5_error_code *code)
++{
++ void *ptr = k5alloc(len, code);
++
++ if (ptr != NULL)
++ memcpy(ptr, in, len);
++ return ptr;
++}
++
+ krb5_error_code KRB5_CALLCONV
+ krb5_get_credentials_for_user(krb5_context context, krb5_flags options,
+ krb5_ccache ccache,
+--
+1.8.2
+
diff --git a/0001-add-libk5radius.patch b/0001-add-libk5radius.patch
deleted file mode 100644
index 68a25db..0000000
--- a/0001-add-libk5radius.patch
+++ /dev/null
@@ -1,3797 +0,0 @@
-From 9fe951d804d05901646fe5db31fc89efacd5bc6a Mon Sep 17 00:00:00 2001
-From: Nathaniel McCallum <npmccallum@redhat.com>
-Date: Thu, 7 Mar 2013 18:12:25 -0500
-Subject: [PATCH 1/3] add libk5radius
-
----
- src/configure.in | 2 +-
- src/lib/Makefile.in | 2 +-
- src/lib/radius/Makefile.in | 74 +++++
- src/lib/radius/attr.c | 313 ++++++++++++++++++++
- src/lib/radius/attrset.c | 278 ++++++++++++++++++
- src/lib/radius/client.c | 325 +++++++++++++++++++++
- src/lib/radius/code.c | 109 +++++++
- src/lib/radius/deps | 21 ++
- src/lib/radius/internal.h | 159 ++++++++++
- src/lib/radius/k5radius.h | 200 +++++++++++++
- src/lib/radius/libk5radius.exports | 23 ++
- src/lib/radius/packet.c | 453 ++++++++++++++++++++++++++++
- src/lib/radius/remote.c | 583 +++++++++++++++++++++++++++++++++++++
- src/lib/radius/test/attr.c | 88 ++++++
- src/lib/radius/test/attrset.c | 98 +++++++
- src/lib/radius/test/client.c | 141 +++++++++
- src/lib/radius/test/code.c | 52 ++++
- src/lib/radius/test/daemon.h | 85 ++++++
- src/lib/radius/test/daemon.py | 76 +++++
- src/lib/radius/test/packet.c | 203 +++++++++++++
- src/lib/radius/test/remote.c | 186 ++++++++++++
- src/lib/radius/test/test.c | 63 ++++
- src/lib/radius/test/test.h | 62 ++++
- 23 files changed, 3594 insertions(+), 2 deletions(-)
- create mode 100644 src/lib/radius/Makefile.in
- create mode 100644 src/lib/radius/attr.c
- create mode 100644 src/lib/radius/attrset.c
- create mode 100644 src/lib/radius/client.c
- create mode 100644 src/lib/radius/code.c
- create mode 100644 src/lib/radius/deps
- create mode 100644 src/lib/radius/internal.h
- create mode 100644 src/lib/radius/k5radius.h
- create mode 100644 src/lib/radius/libk5radius.exports
- create mode 100644 src/lib/radius/packet.c
- create mode 100644 src/lib/radius/remote.c
- create mode 100644 src/lib/radius/test/attr.c
- create mode 100644 src/lib/radius/test/attrset.c
- create mode 100644 src/lib/radius/test/client.c
- create mode 100644 src/lib/radius/test/code.c
- create mode 100644 src/lib/radius/test/daemon.h
- create mode 100644 src/lib/radius/test/daemon.py
- create mode 100644 src/lib/radius/test/packet.c
- create mode 100644 src/lib/radius/test/remote.c
- create mode 100644 src/lib/radius/test/test.c
- create mode 100644 src/lib/radius/test/test.h
-
-diff --git a/src/configure.in b/src/configure.in
-index faf93a1..6a9757f 100644
---- a/src/configure.in
-+++ b/src/configure.in
-@@ -1318,7 +1318,7 @@ dnl lib/krb5/ccache/ccapi
- lib/rpc lib/rpc/unit-test
-
- lib/kadm5 lib/kadm5/clnt lib/kadm5/srv lib/kadm5/unit-test
--
-+ lib/radius
- lib/apputils
-
- dnl ccapi ccapi/lib ccapi/lib/unix ccapi/server ccapi/server/unix ccapi/test
-diff --git a/src/lib/Makefile.in b/src/lib/Makefile.in
-index 485db40..0c9a2fb 100644
---- a/src/lib/Makefile.in
-+++ b/src/lib/Makefile.in
-@@ -1,5 +1,5 @@
- mydir=lib
--SUBDIRS=crypto krb5 gssapi rpc kdb kadm5 apputils
-+SUBDIRS=crypto krb5 gssapi rpc kdb kadm5 apputils radius
- WINSUBDIRS=crypto krb5 gssapi
- BUILDTOP=$(REL)..
-
-diff --git a/src/lib/radius/Makefile.in b/src/lib/radius/Makefile.in
-new file mode 100644
-index 0000000..0916c6c
---- /dev/null
-+++ b/src/lib/radius/Makefile.in
-@@ -0,0 +1,74 @@
-+mydir=lib$(S)radius
-+BUILDTOP=$(REL)..$(S)..
-+RELDIR=../lib/radius
-+
-+LIBBASE=k5radius
-+LIBMAJOR=0
-+LIBMINOR=0
-+
-+LOCALINCLUDES=-I$(srcdir) -I.
-+
-+SHLIB_EXPLIBS= -lkrb5 -lcom_err -lk5crypto -lverto $(SUPPORT_LIB) $(LIBS)
-+
-+STLIBOBJS=attr.o attrset.o client.o code.o packet.o remote.o
-+LIBOBJS=$(OUTPRE)attr.$(OBJEXT) \
-+ $(OUTPRE)attrset.$(OBJEXT) \
-+ $(OUTPRE)client.$(OBJEXT) \
-+ $(OUTPRE)code.$(OBJEXT) \
-+ $(OUTPRE)packet.$(OBJEXT) \
-+ $(OUTPRE)remote.$(OBJEXT)
-+SRCS=attr.c attrset.c client.c code.c packet.c remote.c
-+
-+STOBJLISTS=OBJS.ST
-+
-+RADIUS_HDR=$(BUILDTOP)$(S)include$(S)k5radius.h
-+
-+includes:: $(RADIUS_HDR)
-+depend:: $(RADIUS_HDR)
-+all-unix:: all-liblinks includes
-+install-unix:: install-libs
-+
-+clean-unix:: clean-liblinks clean-libs clean-libobjs
-+ $(RM) $(RADIUS_HDR)
-+
-+
-+$(RADIUS_HDR): $(srcdir)/k5radius.h
-+ $(RM) $@
-+ $(CP) $(srcdir)/k5radius.h $@
-+
-+install::
-+ $(INSTALL_DATA) $(srcdir)/k5radius.h $(DESTDIR)$(KRB5_INCDIR)/k5radius.h
-+
-+check-unix:: t_attr t_attrset t_code t_packet t_remote t_client
-+ $(RUN_SETUP) $(VALGRIND) ./t_attr
-+ $(RUN_SETUP) $(VALGRIND) ./t_attrset
-+ $(RUN_SETUP) $(VALGRIND) ./t_code
-+ $(RUN_SETUP) $(VALGRIND) ./t_packet $(srcdir)/test/daemon.py
-+ $(RUN_SETUP) $(VALGRIND) ./t_remote $(srcdir)/test/daemon.py
-+ $(RUN_SETUP) $(VALGRIND) ./t_client $(srcdir)/test/daemon.py
-+
-+TESTARGS=$(srcdir)/test/test.c $(SHLIB_EXPLIBS)
-+
-+t_attr: attr.o
-+ $(CC_LINK) -o $@ $^ $(srcdir)/test/attr.c $(TESTARGS)
-+
-+t_attrset: attr.o attrset.o
-+ $(CC_LINK) -o $@ $^ $(srcdir)/test/attrset.c $(TESTARGS)
-+
-+t_code: code.o
-+ $(CC_LINK) -o $@ $^ $(srcdir)/test/code.c $(TESTARGS)
-+
-+t_packet: attr.o attrset.o code.o packet.o
-+ $(CC_LINK) -o $@ $^ $(srcdir)/test/packet.c $(TESTARGS)
-+
-+t_remote: attr.o attrset.o code.o packet.o remote.o
-+ $(CC_LINK) -o $@ $^ $(srcdir)/test/remote.c $(TESTARGS)
-+
-+t_client: attr.o attrset.o code.o packet.o remote.o client.o
-+ $(CC_LINK) -o $@ $^ $(srcdir)/test/client.c $(TESTARGS)
-+
-+clean-unix:: clean-libobjs
-+ $(RM) t_attr t_attrset t_code t_packet t_remote t_client
-+
-+@lib_frag@
-+@libobj_frag@
-diff --git a/src/lib/radius/attr.c b/src/lib/radius/attr.c
-new file mode 100644
-index 0000000..cabcf5d
---- /dev/null
-+++ b/src/lib/radius/attr.c
-@@ -0,0 +1,313 @@
-+/*
-+ * Copyright 2012 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include "internal.h"
-+
-+#include <string.h>
-+
-+#define BLOCK_SIZE RADIUS_PACKET_SIZE_AUTH
-+
-+typedef krb5_error_code
-+(*attribute_transform)(krb5_context ctx, const char *secret,
-+ const uint8_t *auth, krb5_data *attr);
-+
-+typedef struct {
-+ const char *name;
-+ uint8_t min;
-+ uint8_t max;
-+ attribute_transform encode;
-+ attribute_transform decode;
-+} attribute_record;
-+
-+static krb5_error_code
-+user_password_encode(krb5_context ctx, const char *secret,
-+ const uint8_t *auth, krb5_data *attr);
-+
-+static krb5_error_code
-+user_password_decode(krb5_context ctx, const char *secret,
-+ const uint8_t *auth, krb5_data *attr);
-+
-+static const attribute_record attributes[UINT8_MAX] = {
-+ {"User-Name", 1, RADIUS_ATTR_SIZE_MAX, NULL, NULL},
-+ {"User-Password", 1, 128, user_password_encode, user_password_decode},
-+ {"CHAP-Password", 17, 17, NULL, NULL},
-+ {"NAS-IP-Address", 4, 4, NULL, NULL},
-+ {"NAS-Port", 4, 4, NULL, NULL},
-+ {"Service-Type", 4, 4, NULL, NULL},
-+ {"Framed-Protocol", 4, 4, NULL, NULL},
-+ {"Framed-IP-Address", 4, 4, NULL, NULL},
-+ {"Framed-IP-Netmask", 4, 4, NULL, NULL},
-+ {"Framed-Routing", 4, 4, NULL, NULL},
-+ {"Filter-Id", 1, RADIUS_ATTR_SIZE_MAX, NULL, NULL},
-+ {"Framed-MTU", 4, 4, NULL, NULL},
-+ {"Framed-Compression", 4, 4, NULL, NULL},
-+ {"Login-IP-Host", 4, 4, NULL, NULL},
-+ {"Login-Service", 4, 4, NULL, NULL},
-+ {"Login-TCP-Port", 4, 4, NULL, NULL},
-+ {NULL, 0, 0, NULL, NULL}, /* Unassigned */
-+ {"Reply-Message", 1, RADIUS_ATTR_SIZE_MAX, NULL, NULL},
-+ {"Callback-Number", 1, RADIUS_ATTR_SIZE_MAX, NULL, NULL},
-+ {"Callback-Id", 1, RADIUS_ATTR_SIZE_MAX, NULL, NULL},
-+ {NULL, 0, 0, NULL, NULL}, /* Unassigned */
-+ {"Framed-Route", 1, RADIUS_ATTR_SIZE_MAX, NULL, NULL},
-+ {"Framed-IPX-Network", 4, 4, NULL, NULL},
-+ {"State", 1, RADIUS_ATTR_SIZE_MAX, NULL, NULL},
-+ {"Class", 1, RADIUS_ATTR_SIZE_MAX, NULL, NULL},
-+ {"Vendor-Specific", 5, RADIUS_ATTR_SIZE_MAX, NULL, NULL},
-+ {"Session-Timeout", 4, 4, NULL, NULL},
-+ {"Idle-Timeout", 4, 4, NULL, NULL},
-+ {"Termination-Action", 4, 4, NULL, NULL},
-+ {"Called-Station-Id", 1, RADIUS_ATTR_SIZE_MAX, NULL, NULL},
-+ {"Calling-Station-Id", 1, RADIUS_ATTR_SIZE_MAX, NULL, NULL},
-+ {"NAS-Identifier", 1, RADIUS_ATTR_SIZE_MAX, NULL, NULL},
-+ {"Proxy-State", 1, RADIUS_ATTR_SIZE_MAX, NULL, NULL},
-+ {"Login-LAT-Service", 1, RADIUS_ATTR_SIZE_MAX, NULL, NULL},
-+ {"Login-LAT-Node", 1, RADIUS_ATTR_SIZE_MAX, NULL, NULL},
-+ {"Login-LAT-Group", 32, 32, NULL, NULL},
-+ {"Framed-AppleTalk-Link", 4, 4, NULL, NULL},
-+ {"Framed-AppleTalk-Network", 4, 4, NULL, NULL},
-+ {"Framed-AppleTalk-Zone", 1, RADIUS_ATTR_SIZE_MAX, NULL, NULL},
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {"CHAP-Challenge", 5, RADIUS_ATTR_SIZE_MAX, NULL, NULL},
-+ {"NAS-Port-Type", 4, 4, NULL, NULL},
-+ {"Port-Limit", 4, 4, NULL, NULL},
-+ {"Login-LAT-Port", 1, RADIUS_ATTR_SIZE_MAX, NULL, NULL},
-+};
-+
-+static krb5_error_code
-+user_password_encode(krb5_context ctx, const char *secret,
-+ const uint8_t *auth, krb5_data *attr)
-+{
-+ krb5_error_code retval;
-+ unsigned int seclen;
-+ krb5_checksum sum;
-+ size_t blck, i;
-+ krb5_data tmp;
-+
-+ seclen = strlen(secret);
-+ tmp.length = seclen + BLOCK_SIZE;
-+ tmp.data = malloc(tmp.length);
-+ if (tmp.data == NULL)
-+ return ENOMEM;
-+
-+ /* Pad with zeros to the block size. */
-+ if (attr->length % BLOCK_SIZE != 0) {
-+ i = BLOCK_SIZE - attr->length % BLOCK_SIZE;
-+ memset(attr->data + attr->length, 0, i);
-+ attr->length += i;
-+ }
-+
-+ for (blck = 0; blck * BLOCK_SIZE < attr->length; blck++) {
-+ memcpy(tmp.data, secret, seclen);
-+ if (blck == 0)
-+ memcpy(tmp.data + seclen, auth, BLOCK_SIZE);
-+ else
-+ memcpy(tmp.data + seclen,
-+ &attr->data[(blck - 1) * BLOCK_SIZE],
-+ BLOCK_SIZE);
-+
-+ retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0,
-+ &tmp, &sum);
-+ if (retval != 0) {
-+ krb5_free_data_contents(ctx, &tmp);
-+ return retval;
-+ }
-+
-+ for (i = 0; i < BLOCK_SIZE; i++)
-+ attr->data[blck * BLOCK_SIZE + i] ^= sum.contents[i];
-+ krb5_free_checksum_contents(ctx, &sum);
-+ }
-+
-+ krb5_free_data_contents(ctx, &tmp);
-+ return 0;
-+}
-+
-+static krb5_error_code
-+user_password_decode(krb5_context ctx, const char *secret,
-+ const uint8_t *auth, krb5_data *attr)
-+{
-+ krb5_error_code retval;
-+ unsigned int seclen;
-+ krb5_checksum sum;
-+ ssize_t blck, i;
-+ krb5_data tmp;
-+
-+ if (attr->length % BLOCK_SIZE != 0)
-+ return EINVAL;
-+
-+ seclen = strlen(secret);
-+ tmp.length = seclen + BLOCK_SIZE;
-+ tmp.data = malloc(tmp.length);
-+ if (tmp.data == NULL)
-+ return ENOMEM;
-+
-+ for (blck = attr->length / BLOCK_SIZE - 1; blck >= 0; blck --) {
-+ memcpy(tmp.data, secret, seclen);
-+ if (blck == 0)
-+ memcpy(tmp.data + seclen, auth, BLOCK_SIZE);
-+ else
-+ memcpy(tmp.data + seclen,
-+ &attr->data[(blck - 1) * BLOCK_SIZE],
-+ BLOCK_SIZE);
-+
-+ retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0,
-+ &tmp, &sum);
-+ if (retval != 0) {
-+ krb5_free_data_contents(ctx, &tmp);
-+ return retval;
-+ }
-+
-+ for (i = 0; i < BLOCK_SIZE; i++)
-+ attr->data[blck * BLOCK_SIZE + i] ^= sum.contents[i];
-+ krb5_free_checksum_contents(ctx, &sum);
-+ }
-+
-+ /* Strip off trailing NULL bytes. */
-+ while (attr->length > 0 && attr->data[attr->length - 1] == '\0')
-+ attr->length--;
-+
-+ krb5_free_data_contents(ctx, &tmp);
-+ return 0;
-+}
-+
-+krb5_error_code
-+k5_radius_attr_valid(k5_radius_attr type, const krb5_data *data)
-+{
-+ const attribute_record *ar;
-+
-+ if (type == 0)
-+ return EINVAL;
-+
-+ ar = &attributes[type - 1];
-+ return (data->length >= ar->min && data->length <= ar->max) ? 0 : EMSGSIZE;
-+}
-+
-+krb5_error_code
-+k5_radius_attr_encode(krb5_context ctx, const char *secret,
-+ const uint8_t *auth, k5_radius_attr type,
-+ const krb5_data *in, krb5_data *out)
-+{
-+ char buffer[RADIUS_ATTR_SIZE_MAX];
-+ krb5_error_code retval;
-+ krb5_data tmp;
-+
-+ retval = k5_radius_attr_valid(type, in);
-+ if (retval != 0)
-+ return retval;
-+
-+ tmp = *in;
-+ if (attributes[type - 1].encode == NULL)
-+ goto egress;
-+
-+ tmp.data = buffer;
-+ memcpy(tmp.data, in->data, in->length);
-+ retval = (*attributes[type - 1].encode)(ctx, secret, auth, &tmp);
-+ if (retval != 0)
-+ return 0;
-+
-+egress:
-+ if (tmp.length > out->length)
-+ return EMSGSIZE;
-+
-+ out->length = tmp.length;
-+ memcpy(out->data, tmp.data, tmp.length);
-+ return 0;
-+}
-+
-+krb5_error_code
-+k5_radius_attr_decode(krb5_context ctx, const char *secret,
-+ const uint8_t *auth, k5_radius_attr type,
-+ const krb5_data *in, krb5_data *out)
-+{
-+ char buffer[RADIUS_ATTR_SIZE_MAX];
-+ krb5_error_code retval;
-+ krb5_data tmp;
-+
-+ retval = k5_radius_attr_valid(type, in);
-+ if (retval != 0)
-+ return retval;
-+
-+ tmp = *in;
-+ if (attributes[type - 1].decode != NULL) {
-+ tmp.data = buffer;
-+ memcpy(tmp.data, in->data, in->length);
-+ retval = (*attributes[type - 1].decode)(ctx, secret, auth, &tmp);
-+ if (retval != 0)
-+ return 0;
-+ }
-+
-+ if (tmp.length > out->length)
-+ return EMSGSIZE;
-+
-+ out->length = tmp.length;
-+ memcpy(out->data, tmp.data, tmp.length);
-+ return 0;
-+}
-+
-+k5_radius_attr
-+k5_radius_attr_name2num(const char *name)
-+{
-+ uint8_t i;
-+
-+ for (i = 0; i < UINT8_MAX; i++) {
-+ if (attributes[i].name == NULL)
-+ continue;
-+
-+ if (strcmp(attributes[i].name, name) == 0)
-+ return ++i;
-+ }
-+
-+ return 0;
-+}
-+
-+const char *
-+k5_radius_attr_num2name(k5_radius_attr type)
-+{
-+ if (type == 0)
-+ return NULL;
-+
-+ return attributes[type - 1].name;
-+}
-diff --git a/src/lib/radius/attrset.c b/src/lib/radius/attrset.c
-new file mode 100644
-index 0000000..694ea38
---- /dev/null
-+++ b/src/lib/radius/attrset.c
-@@ -0,0 +1,278 @@
-+#include "internal.h"
-+
-+#include <string.h>
-+
-+typedef struct attr_ attr;
-+struct attr_ {
-+ attr *next;
-+ k5_radius_attr type;
-+ krb5_data attr;
-+ char buffer[RADIUS_ATTR_SIZE_MAX];
-+};
-+
-+struct k5_radius_attrset_ {
-+ krb5_context ctx;
-+ attr *attrs;
-+};
-+
-+static void
-+attrs_free(attr *a)
-+{
-+ if (a == NULL)
-+ return;
-+
-+ attrs_free(a->next);
-+ free(a);
-+}
-+
-+static krb5_boolean
-+attrs_find(attr ***prev, k5_radius_attr type, size_t *indx)
-+{
-+ attr **tmp;
-+
-+ if (prev == NULL || *prev == NULL)
-+ return FALSE;
-+
-+ tmp = &(**prev)->next;
-+ if (attrs_find(&tmp, type, indx)) {
-+ *prev = tmp;
-+ return TRUE;
-+ }
-+
-+ if (**prev != NULL && (**prev)->type == type) {
-+ if (*indx == 0)
-+ return TRUE;
-+ *indx -= 1;
-+ }
-+
-+ return FALSE;
-+}
-+
-+static krb5_error_code
-+attrs_copy(attr *a, k5_radius_attrset *copy)
-+{
-+ krb5_error_code retval;
-+
-+ if (a == NULL)
-+ return 0;
-+
-+ retval = attrs_copy(a->next, copy);
-+ if (retval != 0)
-+ return retval;
-+
-+ return k5_radius_attrset_add(copy, a->type, &a->attr);
-+}
-+
-+static krb5_error_code
-+attrset_encode(const attr *a, krb5_context ctx, const char *secret,
-+ const uint8_t *auth, krb5_data *out)
-+{
-+ krb5_error_code retval;
-+ krb5_data tmp;
-+
-+ if (a == NULL) {
-+ out->length = 0;
-+ return 0;
-+ }
-+
-+ tmp = *out;
-+ retval = attrset_encode(a->next, ctx, secret, auth, out);
-+ if (retval != 0)
-+ return retval;
-+
-+ tmp.data = out->data + out->length + 2;
-+ tmp.length = RADIUS_PACKET_SIZE_ATTR_MAX - out->length;
-+ if (tmp.length < 3)
-+ return EMSGSIZE;
-+ tmp.length -= 2;
-+
-+ retval = k5_radius_attr_encode(ctx, secret, auth, a->type, &a->attr, &tmp);
-+ if (retval != 0)
-+ return retval;
-+
-+ *(tmp.data - 2) = a->type;
-+ *(tmp.data - 1) = tmp.length + 2;
-+
-+ out->length += tmp.length + 2;
-+ return retval;
-+}
-+
-+krb5_error_code
-+k5_radius_attrset_new(krb5_context ctx, k5_radius_attrset **set)
-+{
-+ k5_radius_attrset *tmp;
-+
-+ tmp = calloc(1, sizeof(k5_radius_attrset));
-+ if (tmp == NULL)
-+ return ENOMEM;
-+ tmp->ctx = ctx;
-+
-+ *set = tmp;
-+ return 0;
-+}
-+
-+void
-+k5_radius_attrset_free(k5_radius_attrset *set)
-+{
-+ if (set == NULL)
-+ return;
-+
-+ attrs_free(set->attrs);
-+ free(set);
-+}
-+
-+krb5_error_code
-+k5_radius_attrset_add(k5_radius_attrset *set, k5_radius_attr type,
-+ const krb5_data *data)
-+{
-+ krb5_error_code retval;
-+ attr *tmp;
-+
-+ retval = k5_radius_attr_valid(type, data);
-+ if (retval != 0)
-+ return retval;
-+
-+ tmp = calloc(1, sizeof(attr));
-+ if (tmp == NULL)
-+ return ENOMEM;
-+
-+ tmp->type = type;
-+ tmp->attr.data = tmp->buffer;
-+ tmp->attr.length = data->length;
-+ memcpy(tmp->attr.data, data->data, data->length);
-+
-+ tmp->next = set->attrs;
-+ set->attrs = tmp;
-+ return 0;
-+}
-+
-+krb5_error_code
-+k5_radius_attrset_add_number(k5_radius_attrset *set, k5_radius_attr type,
-+ uint32_t num)
-+{
-+ krb5_data data;
-+
-+ num = htonl(num);
-+ data.data = (char *)&num;
-+ data.length = sizeof(num);
-+ return k5_radius_attrset_add(set, type, &data);
-+}
-+
-+void
-+k5_radius_attrset_del(k5_radius_attrset *set, k5_radius_attr type, size_t indx)
-+{
-+ attr **prev = &set->attrs;
-+ attr *tmp;
-+
-+ if (attrs_find(&prev, type, &indx)) {
-+ tmp = *prev;
-+ *prev = tmp->next;
-+ free(tmp);
-+ }
-+}
-+
-+const krb5_data *
-+k5_radius_attrset_get(const k5_radius_attrset *set, k5_radius_attr type,
-+ size_t indx)
-+{
-+ attr **prev = &((k5_radius_attrset*) set)->attrs;
-+
-+ if (attrs_find(&prev, type, &indx))
-+ return &(*prev)->attr;
-+
-+ return NULL ;
-+}
-+
-+krb5_error_code
-+k5_radius_attrset_copy(const k5_radius_attrset *set, k5_radius_attrset **copy)
-+{
-+ krb5_error_code retval;
-+ k5_radius_attrset *tmp;
-+
-+ retval = k5_radius_attrset_new(set->ctx, &tmp);
-+ if (retval != 0)
-+ return retval;
-+
-+ retval = attrs_copy(set->attrs, tmp);
-+ if (retval != 0) {
-+ k5_radius_attrset_free(tmp);
-+ return retval;
-+ }
-+
-+ *copy = tmp;
-+ return 0;
-+}
-+
-+krb5_error_code
-+k5_radius_attrset_encode(const k5_radius_attrset *set, const char *secret,
-+ const uint8_t *auth, krb5_data *out)
-+{
-+ char buffer[RADIUS_ATTR_SIZE_MAX];
-+ krb5_error_code retval;
-+ krb5_data data;
-+
-+ if (set == NULL) {
-+ out->length = 0;
-+ return 0;
-+ }
-+
-+ data.data = buffer;
-+ data.length = sizeof(buffer);
-+
-+ retval = attrset_encode(set->attrs, set->ctx, secret, auth, &data);
-+ if (retval != 0)
-+ return retval;
-+
-+ if (out->length < data.length)
-+ return EMSGSIZE;
-+
-+ memcpy(out->data, data.data, data.length);
-+ out->length = data.length;
-+ return 0;
-+}
-+
-+krb5_error_code
-+k5_radius_attrset_decode(krb5_context ctx, const krb5_data *in,
-+ const char *secret, const uint8_t *auth,
-+ k5_radius_attrset **set)
-+{
-+ char buffer[RADIUS_ATTR_SIZE_MAX];
-+ krb5_data intmp, outtmp;
-+ krb5_error_code retval;
-+ k5_radius_attr type;
-+ k5_radius_attrset *tmp;
-+ size_t i;
-+
-+ retval = k5_radius_attrset_new(ctx, &tmp);
-+ if (retval != 0)
-+ return retval;
-+
-+ outtmp.data = buffer;
-+ for (i = 0; i + 2 < in->length; ) {
-+ type = in->data[i++];
-+ intmp.length = in->data[i++] - 2;
-+ intmp.data = &in->data[i];
-+ i += intmp.length;
-+
-+ retval = (in->length < i) ? EBADMSG : 0;
-+ if (retval != 0)
-+ goto error;
-+
-+ outtmp.length = sizeof(buffer);
-+ retval = k5_radius_attr_decode(ctx, secret, auth, type,
-+ &intmp, &outtmp);
-+ if (retval != 0)
-+ goto error;
-+
-+ retval = k5_radius_attrset_add(tmp, type, &outtmp);
-+ if (retval != 0)
-+ goto error;
-+ }
-+
-+ *set = tmp;
-+ return 0;
-+
-+error:
-+ k5_radius_attrset_free(tmp);
-+ return retval;
-+}
-diff --git a/src/lib/radius/client.c b/src/lib/radius/client.c
-new file mode 100644
-index 0000000..0267750
---- /dev/null
-+++ b/src/lib/radius/client.c
-@@ -0,0 +1,325 @@
-+/*
-+ * Copyright 2012 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include "internal.h"
-+
-+#include <string.h>
-+#include <sys/un.h>
-+#include <unistd.h>
-+#include <stdio.h>
-+#include <limits.h>
-+
-+#ifndef HOST_NAME_MAX
-+/* SUSv2 */
-+#define HOST_NAME_MAX 255
-+#endif
-+
-+typedef struct remote_state_ remote_state;
-+typedef struct request_ request;
-+typedef struct server_ server;
-+
-+struct remote_state_ {
-+ const k5_radius_packet *packet;
-+ k5_radius_remote *remote;
-+};
-+
-+struct request_ {
-+ k5_radius_client *rc;
-+
-+ k5_radius_code code;
-+ k5_radius_attrset *attrs;
-+ time_t timeout;
-+ size_t retries;
-+ k5_radius_cb *cb;
-+ void *data;
-+
-+ remote_state *remotes;
-+ ssize_t current;
-+ ssize_t count;
-+};
-+
-+struct server_ {
-+ k5_radius_remote *serv;
-+ time_t last;
-+ server *next;
-+};
-+
-+struct k5_radius_client_ {
-+ krb5_context kctx;
-+ verto_ctx *vctx;
-+ server *servers;
-+};
-+
-+/* Return either a pre-existing server that matches the
-+ * address info and the secret or create a new one. */
-+static krb5_error_code
-+get_server(k5_radius_client *rc, const struct addrinfo *ai,
-+ const char *secret, k5_radius_remote **out)
-+{
-+ krb5_error_code retval;
-+ time_t currtime;
-+ server *srv;
-+
-+ if (time(&currtime) == (time_t) -1)
-+ return errno;
-+
-+ for (srv = rc->servers; srv != NULL; srv = srv->next) {
-+ if (k5_radius_remote_equals(srv->serv, ai, secret)) {
-+ srv->last = currtime;
-+ *out = srv->serv;
-+ return 0;
-+ }
-+ }
-+
-+ srv = calloc(1, sizeof(server));
-+ if (srv == NULL)
-+ return ENOMEM;
-+ srv->last = currtime;
-+
-+ retval = k5_radius_remote_new(rc->kctx, rc->vctx, ai, secret, &srv->serv);
-+ if (retval != 0) {
-+ free(srv);
-+ return retval;
-+ }
-+
-+ srv->next = rc->servers;
-+ rc->servers = srv;
-+
-+ *out = srv->serv;
-+ return 0;
-+}
-+
-+static void
-+request_free(request *req)
-+{
-+ k5_radius_attrset_free(req->attrs);
-+ free(req->remotes);
-+ free(req);
-+}
-+
-+static krb5_error_code
-+request_new(k5_radius_client *rc, k5_radius_code code,
-+ const k5_radius_attrset *attrs, const struct addrinfo *ai,
-+ const char *secret, time_t timeout, size_t retries,
-+ k5_radius_cb cb, void *data, request **req)
-+{
-+ const struct addrinfo *tmp;
-+ krb5_error_code retval;
-+ request *rqst;
-+ size_t i;
-+
-+ if (ai == NULL)
-+ return EINVAL;
-+
-+ rqst = calloc(1, sizeof(request));
-+ if (rqst == NULL)
-+ return ENOMEM;
-+
-+ for (tmp = ai; tmp != NULL; tmp = tmp->ai_next)
-+ rqst->count++;
-+
-+ rqst->rc = rc;
-+ rqst->code = code;
-+ rqst->cb = cb;
-+ rqst->data = data;
-+ rqst->timeout = timeout / rqst->count;
-+ rqst->retries = retries;
-+
-+ retval = k5_radius_attrset_copy(attrs, &rqst->attrs);
-+ if (retval != 0) {
-+ request_free(rqst);
-+ return retval;
-+ }
-+
-+ rqst->remotes = calloc(rqst->count + 1, sizeof(remote_state));
-+ if (rqst->remotes == NULL) {
-+ request_free(rqst);
-+ return ENOMEM;
-+ }
-+
-+ i = 0;
-+ for (tmp = ai; tmp != NULL; tmp = tmp->ai_next) {
-+ retval = get_server(rc, tmp, secret, &rqst->remotes[i++].remote);
-+ if (retval != 0) {
-+ request_free(rqst);
-+ return retval;
-+ }
-+ }
-+
-+ *req = rqst;
-+ return 0;
-+}
-+
-+static void
-+recursive_age(server *srv, server **prev, time_t currtime)
-+{
-+ if (srv == NULL)
-+ return;
-+
-+ recursive_age(srv->next, &srv->next, currtime);
-+ if (currtime == (time_t)-1 || currtime - srv->last > 60 * 60) {
-+ *prev = srv->next;
-+ k5_radius_remote_free(srv->serv);
-+ free(srv);
-+ }
-+}
-+
-+static void
-+on_response(krb5_error_code retval, const k5_radius_packet *reqp,
-+ const k5_radius_packet *rspp, void *data)
-+{
-+ request *req = (request *)data;
-+ time_t currtime;
-+ size_t i;
-+
-+ /* Do nothing if we are already completed. */
-+ if (req->count < 0)
-+ return;
-+
-+ /* If we have timed out and have more remotes to try, do so. */
-+ if (retval == ETIMEDOUT && req->remotes[++req->current].remote != NULL) {
-+ retval = k5_radius_remote_send(req->remotes[req->current].remote,
-+ req->code, req->attrs, on_response,
-+ req, req->timeout, req->retries,
-+ &req->remotes[req->current].packet);
-+ if (retval == 0)
-+ return;
-+ }
-+
-+ /* Mark the request as complete. */
-+ req->count = -1;
-+
-+ /* Inform the callback. */
-+ (*req->cb)(retval, reqp, rspp, req->data);
-+
-+ /* Cancel the outstanding packets. */
-+ for (i = 0; req->remotes[i].remote != NULL; i++)
-+ k5_radius_remote_cancel(req->remotes[i].remote,
-+ req->remotes[i].packet);
-+
-+ /* Age out servers that haven't been used in a while. */
-+ if (time(&currtime) != (time_t)-1)
-+ recursive_age(req->rc->servers, &req->rc->servers, currtime);
-+
-+ request_free(req);
-+}
-+
-+krb5_error_code
-+k5_radius_client_new(krb5_context kctx, verto_ctx *vctx,
-+ k5_radius_client **out)
-+{
-+ k5_radius_client *tmp;
-+
-+ tmp = calloc(1, sizeof(k5_radius_client));
-+ if (tmp == NULL)
-+ return ENOMEM;
-+
-+ tmp->kctx = kctx;
-+ tmp->vctx = vctx;
-+
-+ *out = tmp;
-+ return 0;
-+}
-+
-+void
-+k5_radius_client_free(k5_radius_client *rc)
-+{
-+ if (rc == NULL)
-+ return;
-+
-+ recursive_age(rc->servers, &rc->servers, -1);
-+ free(rc);
-+}
-+
-+krb5_error_code
-+k5_radius_client_send(k5_radius_client *rc, k5_radius_code code,
-+ const k5_radius_attrset *attrs, const char *remote,
-+ const char *secret, time_t timeout, size_t retries,
-+ k5_radius_cb cb, void *data)
-+{
-+ char *sep, srv[HOST_NAME_MAX + 1], *svc = "radius";
-+ struct addrinfo hints, *ai = NULL;
-+ krb5_error_code retval;
-+ struct sockaddr_un ua;
-+ request *req;
-+
-+ memset(&hints, 0, sizeof(hints));
-+
-+ if (remote[0] == '/') {
-+ ua.sun_family = AF_UNIX;
-+ snprintf(ua.sun_path, sizeof(ua.sun_path), "%s", remote);
-+ hints.ai_family = AF_UNIX;
-+ hints.ai_socktype = SOCK_STREAM;
-+ hints.ai_addr = (struct sockaddr *)&ua;
-+ hints.ai_addrlen = sizeof(ua);
-+ ai = &hints;
-+ } else {
-+ /* Isolate the port number if it exists. */
-+ snprintf(srv, sizeof(srv), "%s", remote);
-+
-+ /* IPv6 */
-+ if (srv[0] == '[') {
-+ sep = strrchr(srv, ']');
-+ if (sep != NULL && sep[1] == ':') {
-+ sep[1] = '\0';
-+ svc = &sep[2];
-+ }
-+
-+ /* IPv4 or DNS */
-+ } else {
-+ sep = strrchr(srv, ':');
-+ if (sep != NULL && sep[1] != '\0') {
-+ sep[0] = '\0';
-+ svc = &sep[1];
-+ }
-+ }
-+
-+ /* Perform the lookup. */
-+ hints.ai_socktype = SOCK_DGRAM;
-+ retval = gai_error_(getaddrinfo(srv, svc, &hints, &ai));
-+ if (retval != 0)
-+ return retval;
-+ }
-+
-+ retval = request_new(rc, code, attrs, ai, secret, timeout, retries, cb,
-+ data, &req);
-+ if (ai != &hints)
-+ freeaddrinfo(ai);
-+ if (retval != 0)
-+ return retval;
-+
-+ retval = k5_radius_remote_send(req->remotes[req->current].remote,
-+ req->code, req->attrs, on_response, req,
-+ req->timeout, req->retries,
-+ &req->remotes[req->current].packet);
-+ if (retval != 0) {
-+ request_free(req);
-+ return retval;
-+ }
-+
-+ return 0;
-+}
-diff --git a/src/lib/radius/code.c b/src/lib/radius/code.c
-new file mode 100644
-index 0000000..9ecbe93
---- /dev/null
-+++ b/src/lib/radius/code.c
-@@ -0,0 +1,109 @@
-+/*
-+ * Copyright 2012 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include "internal.h"
-+
-+#include <string.h>
-+
-+static const char *codes[UINT8_MAX] = {
-+ "Access-Request",
-+ "Access-Accept",
-+ "Access-Reject",
-+ "Accounting-Request",
-+ "Accounting-Response",
-+ "Accounting-Status",
-+ "Password-Request",
-+ "Password-Ack",
-+ "Password-Reject",
-+ "Accounting-Message",
-+ "Access-Challenge",
-+ "Status-Server",
-+ "Status-Client",
-+ NULL,
-+ NULL,
-+ NULL,
-+ NULL,
-+ NULL,
-+ NULL,
-+ NULL,
-+ "Resource-Free-Request",
-+ "Resource-Free-Response",
-+ "Resource-Query-Request",
-+ "Resource-Query-Response",
-+ "Alternate-Resource-Reclaim-Request",
-+ "NAS-Reboot-Request",
-+ "NAS-Reboot-Response",
-+ NULL,
-+ "Next-Passcode",
-+ "New-Pin",
-+ "Terminate-Session",
-+ "Password-Expired",
-+ "Event-Request",
-+ "Event-Response",
-+ NULL,
-+ NULL,
-+ NULL,
-+ NULL,
-+ NULL,
-+ "Disconnect-Request",
-+ "Disconnect-Ack",
-+ "Disconnect-Nak",
-+ "Change-Filters-Request",
-+ "Change-Filters-Ack",
-+ "Change-Filters-Nak",
-+ NULL,
-+ NULL,
-+ NULL,
-+ NULL,
-+ "IP-Address-Allocate",
-+ "IP-Address-Release",
-+};
-+
-+k5_radius_code
-+k5_radius_code_name2num(const char *name)
-+{
-+ uint8_t i;
-+
-+ for (i = 0; i < UINT8_MAX; i++) {
-+ if (codes[i] == NULL)
-+ continue;
-+
-+ if (strcmp(codes[i], name) == 0)
-+ return ++i;
-+ }
-+
-+ return 0;
-+}
-+
-+const char *
-+k5_radius_code_num2name(k5_radius_code code)
-+{
-+ if (code == 0)
-+ return NULL;
-+
-+ return codes[code - 1];
-+}
-diff --git a/src/lib/radius/deps b/src/lib/radius/deps
-new file mode 100644
-index 0000000..73ac906
---- /dev/null
-+++ b/src/lib/radius/deps
-@@ -0,0 +1,21 @@
-+#
-+# Generated makefile dependencies follow.
-+#
-+attr.so attr.po $(OUTPRE)attr.$(OBJEXT): $(BUILDTOP)/include/krb5/krb5.h \
-+ $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h attr.c \
-+ internal.h k5radius.h
-+attrset.so attrset.po $(OUTPRE)attrset.$(OBJEXT): $(BUILDTOP)/include/krb5/krb5.h \
-+ $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h attrset.c \
-+ internal.h k5radius.h
-+client.so client.po $(OUTPRE)client.$(OBJEXT): $(BUILDTOP)/include/krb5/krb5.h \
-+ $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h client.c \
-+ internal.h k5radius.h
-+code.so code.po $(OUTPRE)code.$(OBJEXT): $(BUILDTOP)/include/krb5/krb5.h \
-+ $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h code.c \
-+ internal.h k5radius.h
-+packet.so packet.po $(OUTPRE)packet.$(OBJEXT): $(BUILDTOP)/include/krb5/krb5.h \
-+ $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h internal.h \
-+ k5radius.h packet.c
-+remote.so remote.po $(OUTPRE)remote.$(OBJEXT): $(BUILDTOP)/include/krb5/krb5.h \
-+ $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h internal.h \
-+ k5radius.h remote.c
-diff --git a/src/lib/radius/internal.h b/src/lib/radius/internal.h
-new file mode 100644
-index 0000000..c8bc32c
---- /dev/null
-+++ b/src/lib/radius/internal.h
-@@ -0,0 +1,159 @@
-+/*
-+ * Copyright 2012 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#ifndef RADIUS_INTERNAL_H_
-+#define RADIUS_INTERNAL_H_
-+
-+#include "k5radius.h"
-+
-+#include <errno.h>
-+
-+#include <sys/types.h>
-+#include <sys/socket.h>
-+#include <netdb.h>
-+
-+/* RFC 2865 */
-+#define RADIUS_PACKET_OFFSET_CODE 0
-+#define RADIUS_PACKET_OFFSET_ID 1
-+#define RADIUS_PACKET_OFFSET_LENGTH 2
-+#define RADIUS_PACKET_OFFSET_AUTH 4
-+#define RADIUS_PACKET_OFFSET_ATTR 20
-+#define RADIUS_ATTR_SIZE_MAX (UINT8_MAX-2)
-+#define RADIUS_PACKET_SIZE_HEAD 4
-+#define RADIUS_PACKET_SIZE_AUTH 16
-+#define RADIUS_PACKET_SIZE_EMPTY \
-+ (RADIUS_PACKET_SIZE_HEAD + RADIUS_PACKET_SIZE_AUTH)
-+#define RADIUS_PACKET_SIZE_ATTR_MAX \
-+ (K5_RADIUS_PACKET_SIZE_MAX - RADIUS_PACKET_SIZE_EMPTY)
-+
-+typedef struct k5_radius_remote_ k5_radius_remote;
-+
-+/* Validates constraints of an attribute. */
-+krb5_error_code
-+k5_radius_attr_valid(k5_radius_attr type, const krb5_data *data);
-+
-+/* Encodes an attribute. */
-+krb5_error_code
-+k5_radius_attr_encode(krb5_context ctx, const char *secret,
-+ const uint8_t *auth, k5_radius_attr type,
-+ const krb5_data *in, krb5_data *out);
-+
-+/* Decodes an attribute. */
-+krb5_error_code
-+k5_radius_attr_decode(krb5_context ctx, const char *secret,
-+ const uint8_t *auth, k5_radius_attr type,
-+ const krb5_data *in, krb5_data *out);
-+
-+/* Encode the attributes into the buffer. */
-+krb5_error_code
-+k5_radius_attrset_encode(const k5_radius_attrset *set, const char *secret,
-+ const uint8_t *auth, krb5_data *out);
-+
-+/* Decodes attributes from a buffer. */
-+krb5_error_code
-+k5_radius_attrset_decode(krb5_context ctx, const krb5_data *in,
-+ const char *secret, const uint8_t *auth,
-+ k5_radius_attrset **set);
-+
-+/* Creates a new remote object which manages a
-+ * socket and the state of outstanding requests. */
-+krb5_error_code
-+k5_radius_remote_new(krb5_context kctx, verto_ctx *vctx,
-+ const struct addrinfo *info, const char *secret,
-+ k5_radius_remote **rr);
-+
-+/* Frees the remote object. */
-+void
-+k5_radius_remote_free(k5_radius_remote *rr);
-+
-+/*
-+ * Sends the packet to the remote. The cb will be called when a response is
-+ * received, the request times out, the request is canceled or an error occurs.
-+ *
-+ * The timeout parameter is the total timeout across all retries.
-+ *
-+ * If the cb is called with a retval of ETIMEDOUT it indicates that the alloted
-+ * time has elapsed. However, in the case of a timeout, we continue to listen
-+ * for the packet until k5_radius_remote_cancel() is called or a response is
-+ * received. This means that cb will always be called twice in the event of a
-+ * timeout. This permits you to pursue other remotes while still listening for
-+ * a response from the first one.
-+ */
-+krb5_error_code
-+k5_radius_remote_send(k5_radius_remote *rr, k5_radius_code code,
-+ k5_radius_attrset *attrs, k5_radius_cb *cb, void *data,
-+ time_t timeout, size_t retries,
-+ const k5_radius_packet **pkt);
-+
-+/* Removes packet from the queue of requests awaiting responses. */
-+void
-+k5_radius_remote_cancel(k5_radius_remote *rr, const k5_radius_packet *pkt);
-+
-+/* Determines if this remote object refers to the remote resource identified
-+ * by the addrinfo struct and the secret. */
-+krb5_boolean
-+k5_radius_remote_equals(const k5_radius_remote *rr,
-+ const struct addrinfo *info, const char *secret);
-+
-+/* Adapted from lib/krb5/os/sendto_kdc.c. */
-+static inline int
-+gai_error_(int err)
-+{
-+ switch (err) {
-+ case 0:
-+ return 0;
-+ case EAI_BADFLAGS:
-+ case EAI_FAMILY:
-+ case EAI_SOCKTYPE:
-+ case EAI_SERVICE:
-+#ifdef EAI_ADDRFAMILY
-+ case EAI_ADDRFAMILY:
-+#endif
-+ return EINVAL;
-+ case EAI_AGAIN:
-+ return EAGAIN;
-+ case EAI_MEMORY:
-+ return ENOMEM;
-+#if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME
-+ case EAI_NODATA:
-+#endif
-+ case EAI_NONAME:
-+ return EADDRNOTAVAIL;
-+#ifdef EAI_OVERFLOW
-+ case EAI_OVERFLOW:
-+ return EOVERFLOW;
-+#endif
-+#ifdef EAI_SYSTEM
-+ case EAI_SYSTEM:
-+ return errno;
-+#endif
-+ default:
-+ return EINVAL;
-+ }
-+}
-+
-+#endif /* RADIUS_INTERNAL_H_ */
-diff --git a/src/lib/radius/k5radius.h b/src/lib/radius/k5radius.h
-new file mode 100644
-index 0000000..857af19
---- /dev/null
-+++ b/src/lib/radius/k5radius.h
-@@ -0,0 +1,200 @@
-+/*
-+ * Copyright 2012 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#ifndef K5_RADIUS_H_
-+#define K5_RADIUS_H_
-+
-+#include <krb5.h>
-+#include <verto.h>
-+#include <stddef.h>
-+#include <stdint.h>
-+#include <stdio.h>
-+
-+#define K5_RADIUS_PACKET_SIZE_MAX 4096
-+
-+#define K5_RADIUS_SERVICE_TYPE_LOGIN 1
-+#define K5_RADIUS_SERVICE_TYPE_FRAMED 2
-+#define K5_RADIUS_SERVICE_TYPE_CALLBACK_LOGIN 3
-+#define K5_RADIUS_SERVICE_TYPE_CALLBACK_FRAMED 4
-+#define K5_RADIUS_SERVICE_TYPE_OUTBOUND 5
-+#define K5_RADIUS_SERVICE_TYPE_ADMINISTRATIVE 6
-+#define K5_RADIUS_SERVICE_TYPE_NAS_PROMPT 7
-+#define K5_RADIUS_SERVICE_TYPE_AUTHENTICATE_ONLY 8
-+#define K5_RADIUS_SERVICE_TYPE_CALLBACK_NAS_PROMPT 9
-+#define K5_RADIUS_SERVICE_TYPE_CALL_CHECK 10
-+#define K5_RADIUS_SERVICE_TYPE_CALLBACK_ADMINISTRATIVE 11
-+
-+typedef struct k5_radius_attrset_ k5_radius_attrset;
-+typedef struct k5_radius_packet_ k5_radius_packet;
-+typedef struct k5_radius_client_ k5_radius_client;
-+typedef uint8_t k5_radius_code;
-+typedef uint8_t k5_radius_attr;
-+
-+/* Called when a response is received or the request times out. The response
-+ * is free'd automatically when the function is returned. */
-+typedef void
-+(k5_radius_cb)(krb5_error_code retval,
-+ const k5_radius_packet *request,
-+ const k5_radius_packet *response,
-+ void *data);
-+
-+/*
-+ * Called to iterate over the collection of outstanding requests.
-+ * Either the callback will be called until it returns NULL or it will
-+ * call with cancel = TRUE to terminate in the middle of an iteration.
-+ */
-+typedef k5_radius_packet *
-+(k5_radius_packet_iter_cb)(void *data, krb5_boolean cancel);
-+
-+/* Converts a code name to its number. Only works for codes defined
-+ * by RFC 2875 or 2882. Returns 0 if the name was not found. */
-+k5_radius_code
-+k5_radius_code_name2num(const char *name);
-+
-+/* Converts a code number to its name. Only works for attributes defined
-+ * by RFC 2865 or 2882. Returns NULL if the name was not found. */
-+const char *
-+k5_radius_code_num2name(k5_radius_code code);
-+
-+/* Converts an attribute name to its number. Only works for attributes defined
-+ * by RFC 2865. Returns 0 if the name was not found. */
-+k5_radius_attr
-+k5_radius_attr_name2num(const char *name);
-+
-+/* Converts an attribute number to its name. Only works for attributes defined
-+ * by RFC 2865. Returns NULL if the name was not found. */
-+const char *
-+k5_radius_attr_num2name(k5_radius_attr type);
-+
-+/* Create a new attrset. */
-+krb5_error_code
-+k5_radius_attrset_new(krb5_context ctx, k5_radius_attrset **set);
-+
-+/* Creates a deep copy of an attrset. */
-+krb5_error_code
-+k5_radius_attrset_copy(const k5_radius_attrset *set, k5_radius_attrset **copy);
-+
-+/* Free an attrset. */
-+void
-+k5_radius_attrset_free(k5_radius_attrset *set);
-+
-+/* Add an attribute to the given set. */
-+krb5_error_code
-+k5_radius_attrset_add(k5_radius_attrset *set, k5_radius_attr type,
-+ const krb5_data *data);
-+
-+/* Add a four-octet unsigned number attribute to the given set. */
-+krb5_error_code
-+k5_radius_attrset_add_number(k5_radius_attrset *set, k5_radius_attr type,
-+ uint32_t num);
-+
-+/* Delete the specified attribute. */
-+void
-+k5_radius_attrset_del(k5_radius_attrset *set, k5_radius_attr type,
-+ size_t indx);
-+
-+/* Get the specified attribute. */
-+const krb5_data *
-+k5_radius_attrset_get(const k5_radius_attrset *set, k5_radius_attr type,
-+ size_t indx);
-+
-+/* Determines the bytes needed from the socket to get the whole packet.
-+ * Don't cache the return value as it can change! Returns -1 on EBADMSG. */
-+ssize_t
-+k5_radius_packet_bytes_needed(const krb5_data *buffer);
-+
-+/* Free a packet. */
-+void
-+k5_radius_packet_free(k5_radius_packet *pkt);
-+
-+/* Create a new request packet. */
-+krb5_error_code
-+k5_radius_packet_new_request(krb5_context ctx, const char *secret,
-+ k5_radius_code code, const k5_radius_attrset *set,
-+ k5_radius_packet_iter_cb *cb, void *data,
-+ k5_radius_packet **request);
-+
-+/* Create a new response packet. */
-+krb5_error_code
-+k5_radius_packet_new_response(krb5_context ctx, const char *secret,
-+ k5_radius_code code,
-+ const k5_radius_attrset *set,
-+ const k5_radius_packet *request,
-+ k5_radius_packet **response);
-+
-+/*
-+ * Decode a radius packet from krb5_data.
-+ *
-+ * If request == TRUE, the resulting packet will be a request packet. In this
-+ * case, if cb is not NULL, the iterator will be used to determine if this
-+ * packet has already been received. If this packet has already been received,
-+ * the return value will be EAGAIN and *reqpkt will contain a reference to the
-+ * previously received packet. In all cases where request == TRUE, the rsppkt
-+ * parameter is ignored.
-+ *
-+ * If request == FALSE, the resulting packet will be a response packet. In this
-+ * case, if cb is not NULL, the iterator will be used to find a corresponding
-+ * request packet. The decoded response packet will be stored in rsppkt. If
-+ * a corresponding request packet is found, it will be set in reqpkt.
-+ */
-+krb5_error_code
-+k5_radius_packet_decode(krb5_context ctx, const char *secret,
-+ const krb5_data *buffer, krb5_boolean request,
-+ k5_radius_packet_iter_cb *cb, void *data,
-+ k5_radius_packet **reqpkt, k5_radius_packet **rsppkt);
-+
-+/* Encode packet. */
-+const krb5_data *
-+k5_radius_packet_encode(const k5_radius_packet *pkt);
-+
-+/* Get the code for the given packet. */
-+k5_radius_code
-+k5_radius_packet_get_code(const k5_radius_packet *pkt);
-+
-+/* Get the specified attribute. */
-+const krb5_data *
-+k5_radius_packet_get_attr(const k5_radius_packet *pkt, k5_radius_attr type,
-+ size_t indx);
-+
-+/* Prints a user readable description of the packet to the stream. */
-+int
-+k5_radius_packet_print(FILE *stream, const krb5_data *pkt);
-+
-+krb5_error_code
-+k5_radius_client_new(krb5_context kctx, verto_ctx *vctx,
-+ k5_radius_client **out);
-+
-+void
-+k5_radius_client_free(k5_radius_client *rc);
-+
-+krb5_error_code
-+k5_radius_client_send(k5_radius_client *rc, k5_radius_code code,
-+ const k5_radius_attrset *attrs, const char *remote,
-+ const char *secret, time_t timeout, size_t retries,
-+ k5_radius_cb cb, void *data);
-+
-+#endif /* K5_RADIUS_H_ */
-diff --git a/src/lib/radius/libk5radius.exports b/src/lib/radius/libk5radius.exports
-new file mode 100644
-index 0000000..2945dcb
---- /dev/null
-+++ b/src/lib/radius/libk5radius.exports
-@@ -0,0 +1,23 @@
-+k5_radius_code_name2num
-+k5_radius_code_num2name
-+k5_radius_attr_name2num
-+k5_radius_attr_num2name
-+k5_radius_attrset_new
-+k5_radius_attrset_copy
-+k5_radius_attrset_free
-+k5_radius_attrset_add
-+k5_radius_attrset_add_number
-+k5_radius_attrset_del
-+k5_radius_attrset_get
-+k5_radius_packet_bytes_needed
-+k5_radius_packet_free
-+k5_radius_packet_new_request
-+k5_radius_packet_new_response
-+k5_radius_packet_decode
-+k5_radius_packet_encode
-+k5_radius_packet_get_code
-+k5_radius_packet_get_attr
-+k5_radius_packet_print
-+k5_radius_client_new
-+k5_radius_client_free
-+k5_radius_client_send
-diff --git a/src/lib/radius/packet.c b/src/lib/radius/packet.c
-new file mode 100644
-index 0000000..0466342
---- /dev/null
-+++ b/src/lib/radius/packet.c
-@@ -0,0 +1,453 @@
-+/*
-+ * Copyright 2012 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include "internal.h"
-+
-+#include <string.h>
-+
-+#include <arpa/inet.h>
-+
-+#define offset(d, o, t) ((t *)&(d)->data[o])
-+#define pkt_code(p) offset(&p->pkt, RADIUS_PACKET_OFFSET_CODE, k5_radius_code)
-+#define pkt_id(p) offset(&p->pkt, RADIUS_PACKET_OFFSET_ID, uint8_t)
-+#define pkt_len(p) offset(&p->pkt, RADIUS_PACKET_OFFSET_LENGTH, uint16_t)
-+#define pkt_auth(p) offset(&p->pkt, RADIUS_PACKET_OFFSET_AUTH, uint8_t)
-+#define pkt_attr(p) offset(&p->pkt, RADIUS_PACKET_OFFSET_ATTR, char)
-+
-+struct k5_radius_packet_ {
-+ char buffer[K5_RADIUS_PACKET_SIZE_MAX];
-+ k5_radius_attrset *attrset;
-+ krb5_data pkt;
-+};
-+
-+typedef struct {
-+ uint8_t x[(UINT8_MAX + 1) / 8];
-+} idmap;
-+
-+/* Ensure the map is empty. */
-+static inline void
-+idmap_init(idmap *map)
-+{
-+ memset(map, 0, sizeof(*map));
-+}
-+
-+/* Set an id as already allocated. */
-+static inline void
-+idmap_set(idmap *map, uint8_t id)
-+{
-+ map->x[id / 8] = 1 << (id % 8);
-+}
-+
-+/* Returns TRUE if the id is unused. */
-+static inline krb5_boolean
-+idmap_get(const idmap *map, uint8_t id)
-+{
-+ return (map->x[id / 8] & (1 << (id % 8))) == 0;
-+}
-+
-+/* Finds an unused id starting the search at the value specified in id.
-+ * NOTE: For optimal security, the initial value of id should be random. */
-+static inline krb5_error_code
-+idmap_find(const idmap *map, uint8_t *id)
-+{
-+ int16_t i;
-+
-+ for (i = *id; i >= 0 && i <= UINT8_MAX; *id % 2 == 0 ? i++ : i--) {
-+ if (idmap_get(map, i))
-+ goto success;
-+ }
-+
-+ for (i = *id; i >= 0 && i <= UINT8_MAX; *id % 2 == 1 ? i++ : i--) {
-+ if (idmap_get(map, i))
-+ goto success;
-+ }
-+
-+ return ERANGE;
-+
-+success:
-+ *id = i;
-+ return 0;
-+}
-+
-+static inline krb5_error_code
-+randomize(krb5_context ctx, unsigned int size, void *buffer)
-+{
-+ krb5_data random;
-+
-+ random.data = buffer;
-+ random.length = size;
-+ return krb5_c_random_make_octets(ctx, &random);
-+}
-+
-+static krb5_error_code
-+id_generate(krb5_context ctx, k5_radius_packet_iter_cb *cb, void *data,
-+ uint8_t *id)
-+{
-+ krb5_error_code retval;
-+ k5_radius_packet *tmp;
-+ idmap used;
-+ uint8_t i;
-+
-+ retval = randomize(ctx, sizeof(i), &i);
-+ if (retval != 0) {
-+ if (cb != NULL)
-+ (*cb)(data, TRUE);
-+ return retval;
-+ }
-+
-+ if (cb != NULL) {
-+ idmap_init(&used);
-+ for (tmp = (*cb)(data, FALSE); tmp != NULL; tmp = (*cb)(data, FALSE))
-+ idmap_set(&used, tmp->pkt.data[1]);
-+
-+ retval = idmap_find(&used, &i);
-+ if (retval != 0)
-+ return retval;
-+ }
-+
-+ *id = i;
-+ return 0;
-+}
-+
-+static krb5_error_code
-+auth_generate_random(krb5_context ctx, uint8_t *rauth)
-+{
-+ uint32_t trunctime;
-+ time_t currtime;
-+
-+ /* Get the least-significant four bytes of the current time. */
-+ currtime = time(NULL);
-+ if (currtime == ((time_t) -1))
-+ return errno;
-+ trunctime = (uint32_t)currtime;
-+ memcpy(rauth, &trunctime, sizeof(trunctime));
-+
-+ /* Randomize the rest of the buffer. */
-+ return randomize(ctx, RADIUS_PACKET_SIZE_AUTH - sizeof(trunctime),
-+ rauth + sizeof(trunctime));
-+}
-+
-+static krb5_error_code
-+auth_generate_response(krb5_context ctx, const char *secret,
-+ const k5_radius_packet *response, const uint8_t *auth,
-+ uint8_t *rauth)
-+{
-+ krb5_error_code retval;
-+ krb5_checksum hash;
-+ krb5_data data;
-+
-+ /* Allocate the temporary buffer. */
-+ data.length = response->pkt.length + strlen(secret);
-+ data.data = calloc(data.length, sizeof(char));
-+ if (data.data == NULL)
-+ return ENOMEM;
-+
-+ /* Encoded RADIUS packet with the request's
-+ * authenticator and the secret at the end. */
-+ memcpy(data.data, response->pkt.data, response->pkt.length);
-+ memcpy(data.data + RADIUS_PACKET_OFFSET_AUTH, auth,
-+ RADIUS_PACKET_SIZE_AUTH);
-+ memcpy(data.data + response->pkt.length, secret, strlen(secret));
-+
-+ /* Hash it. */
-+ retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0,
-+ &data, &hash);
-+ free(data.data);
-+ if (retval != 0)
-+ return retval;
-+
-+ memcpy(rauth, hash.contents, RADIUS_PACKET_SIZE_AUTH);
-+ krb5_free_checksum_contents(ctx, &hash);
-+ return 0;
-+}
-+
-+static k5_radius_packet *
-+packet_new()
-+{
-+ k5_radius_packet *pkt;
-+
-+ pkt = calloc(1, sizeof(k5_radius_packet));
-+ if (pkt == NULL)
-+ return NULL;
-+ pkt->pkt.data = pkt->buffer;
-+ pkt->pkt.length = sizeof(pkt->buffer);
-+
-+ return pkt;
-+}
-+
-+static krb5_error_code
-+packet_set_attrset(krb5_context ctx, const char *secret, k5_radius_packet *pkt)
-+{
-+ krb5_data tmp;
-+
-+ tmp.data = pkt_attr(pkt);
-+ tmp.length = pkt->pkt.length - RADIUS_PACKET_OFFSET_ATTR;
-+ return k5_radius_attrset_decode(ctx, &tmp, secret, pkt_auth(pkt),
-+ &pkt->attrset);
-+}
-+
-+#include <stdio.h>
-+
-+ssize_t
-+k5_radius_packet_bytes_needed(const krb5_data *buffer)
-+{
-+ size_t len;
-+
-+ if (buffer->length < RADIUS_PACKET_OFFSET_AUTH)
-+ return RADIUS_PACKET_OFFSET_AUTH - buffer->length;
-+
-+ len = ntohs(*offset(buffer, RADIUS_PACKET_OFFSET_LENGTH, uint16_t));
-+ if (len > K5_RADIUS_PACKET_SIZE_MAX)
-+ return -1;
-+
-+ return buffer->length > len ? 0 : len - buffer->length;
-+}
-+
-+void
-+k5_radius_packet_free(k5_radius_packet *pkt)
-+{
-+ if (pkt)
-+ k5_radius_attrset_free(pkt->attrset);
-+ free(pkt);
-+}
-+
-+/* Create a new request packet. */
-+krb5_error_code
-+k5_radius_packet_new_request(krb5_context ctx, const char *secret,
-+ k5_radius_code code, const k5_radius_attrset *set,
-+ k5_radius_packet_iter_cb *cb, void *data,
-+ k5_radius_packet **request)
-+{
-+ krb5_error_code retval;
-+ k5_radius_packet *pkt;
-+ krb5_data tmp;
-+
-+ pkt = packet_new();
-+ if (pkt == NULL) {
-+ if (cb != NULL)
-+ (*cb)(data, TRUE);
-+ return ENOMEM;
-+ }
-+
-+ /* Generate the ID. */
-+ retval = id_generate(ctx, cb, data, pkt_id(pkt));
-+ if (retval != 0)
-+ goto error;
-+
-+ /* Generate the authenticator. */
-+ retval = auth_generate_random(ctx, pkt_auth(pkt));
-+ if (retval != 0)
-+ goto error;
-+
-+ /* Encode the attributes. */
-+ tmp.data = pkt_attr(pkt);
-+ tmp.length = pkt->pkt.length - RADIUS_PACKET_OFFSET_ATTR;
-+ retval = k5_radius_attrset_encode(set, secret, pkt_auth(pkt), &tmp);
-+ if (retval != 0)
-+ goto error;
-+
-+ /* Set the code, ID and length. */
-+ pkt->pkt.length = tmp.length + RADIUS_PACKET_OFFSET_ATTR;
-+ *pkt_code(pkt) = code;
-+ *pkt_len(pkt) = htons(pkt->pkt.length);
-+
-+ /* Copy the attrset for future use. */
-+ retval = packet_set_attrset(ctx, secret, pkt);
-+ if (retval != 0)
-+ goto error;
-+
-+ *request = pkt;
-+ return 0;
-+
-+error:
-+ free(pkt);
-+ return retval;
-+}
-+
-+/* Create a new request packet. */
-+krb5_error_code
-+k5_radius_packet_new_response(krb5_context ctx, const char *secret,
-+ k5_radius_code code,
-+ const k5_radius_attrset *set,
-+ const k5_radius_packet *request,
-+ k5_radius_packet **response)
-+{
-+ krb5_error_code retval;
-+ k5_radius_packet *pkt;
-+ krb5_data tmp;
-+
-+ pkt = packet_new();
-+ if (pkt == NULL)
-+ return ENOMEM;
-+
-+ /* Encode the attributes. */
-+ tmp.data = pkt_attr(pkt);
-+ tmp.length = pkt->pkt.length - RADIUS_PACKET_OFFSET_ATTR;
-+ retval = k5_radius_attrset_encode(set, secret, pkt_auth(request), &tmp);
-+ if (retval != 0)
-+ goto error;
-+
-+ /* Set the code, ID and length. */
-+ pkt->pkt.length = tmp.length + RADIUS_PACKET_OFFSET_ATTR;
-+ *pkt_code(pkt) = code;
-+ *pkt_id(pkt) = *pkt_id(request);
-+ *pkt_len(pkt) = htons(pkt->pkt.length);
-+
-+ /* Generate the authenticator. */
-+ retval = auth_generate_response(ctx, secret, pkt, pkt_auth(request),
-+ pkt_auth(pkt));
-+ if (retval != 0)
-+ goto error;
-+
-+ /* Copy the attrset for future use. */
-+ retval = packet_set_attrset(ctx, secret, pkt);
-+ if (retval != 0)
-+ goto error;
-+
-+ *response = pkt;
-+ return 0;
-+
-+error:
-+ free(pkt);
-+ return retval;
-+}
-+
-+krb5_error_code
-+k5_radius_packet_decode(krb5_context ctx, const char *secret,
-+ const krb5_data *buffer, krb5_boolean request,
-+ k5_radius_packet_iter_cb *cb, void *data,
-+ k5_radius_packet **reqpkt, k5_radius_packet **rsppkt)
-+{
-+ uint8_t auth[RADIUS_PACKET_SIZE_AUTH];
-+ k5_radius_packet *pkt, *tmp;
-+ krb5_error_code retval;
-+ uint16_t len;
-+
-+ pkt = packet_new();
-+ if (pkt == NULL) {
-+ retval = ENOMEM;
-+ goto error;
-+ }
-+
-+ /* Ensure a proper message length. */
-+ retval = buffer->length < RADIUS_PACKET_OFFSET_ATTR ? EMSGSIZE : 0;
-+ if (retval != 0)
-+ goto error;
-+ len = ntohs(*offset(buffer, RADIUS_PACKET_OFFSET_LENGTH, uint16_t));
-+ retval = len < RADIUS_PACKET_OFFSET_ATTR ? EBADMSG : 0;
-+ if (retval != 0)
-+ goto error;
-+ retval = (len > buffer->length || len > pkt->pkt.length) ? EBADMSG : 0;
-+ if (retval != 0)
-+ goto error;
-+
-+ /* Copy over the buffer. */
-+ pkt->pkt.length = len;
-+ memcpy(pkt->pkt.data, buffer->data, len);
-+
-+ /* Parse the packet to ensure it is well-formed. */
-+ retval = packet_set_attrset(ctx, secret, pkt);
-+ if (retval != 0)
-+ goto error;
-+
-+ if (cb != NULL) {
-+ for (tmp = (*cb)(data, FALSE);
-+ tmp != NULL;
-+ tmp = (*cb)(data, FALSE)) {
-+ if (*pkt_id(pkt) != *pkt_id(tmp))
-+ continue;
-+
-+ /* Request */
-+ if (request) {
-+ *reqpkt = tmp;
-+ retval = EAGAIN;
-+ goto error;
-+ }
-+
-+ /* Response */
-+ retval = auth_generate_response(ctx, secret, pkt, pkt_auth(tmp),
-+ auth);
-+ if (retval != 0)
-+ goto error;
-+
-+ /* If the authenticator doesn't match,
-+ * then the response is invalid. */
-+ if (memcmp(pkt_auth(pkt), auth, sizeof(auth)) != 0)
-+ continue;
-+
-+ *reqpkt = tmp;
-+ (*cb)(data, TRUE);
-+ break;
-+ }
-+ }
-+
-+ if (request)
-+ *reqpkt = pkt;
-+ else
-+ *rsppkt = pkt;
-+ return 0;
-+
-+error:
-+ if (cb != NULL)
-+ (*cb)(data, TRUE);
-+ k5_radius_packet_free(pkt);
-+ return retval;
-+}
-+
-+const krb5_data *
-+k5_radius_packet_encode(const k5_radius_packet *pkt)
-+{
-+ return &pkt->pkt;
-+}
-+
-+k5_radius_code
-+k5_radius_packet_get_code(const k5_radius_packet *pkt)
-+{
-+ if (pkt == NULL)
-+ return 0;
-+
-+ return *pkt_code(pkt);
-+}
-+
-+const krb5_data *
-+k5_radius_packet_get_attr(const k5_radius_packet *pkt, k5_radius_attr type,
-+ size_t indx)
-+{
-+ return k5_radius_attrset_get(pkt->attrset, type, indx);
-+}
-+
-+int
-+k5_radius_packet_print(FILE *stream, const krb5_data *pkt)
-+{
-+ if (pkt->length < RADIUS_PACKET_OFFSET_ATTR)
-+ return fprintf(stream, "PACKET = INVALID!\n");
-+
-+ return fprintf(stream, "PACKET = code: %hhu; id: %hhu; length: %hu\n",
-+ *offset(pkt, RADIUS_PACKET_OFFSET_CODE, k5_radius_code),
-+ *offset(pkt, RADIUS_PACKET_OFFSET_ID, uint8_t),
-+ ntohs(*offset(pkt, RADIUS_PACKET_OFFSET_LENGTH, uint16_t)));
-+}
-diff --git a/src/lib/radius/remote.c b/src/lib/radius/remote.c
-new file mode 100644
-index 0000000..4839767
---- /dev/null
-+++ b/src/lib/radius/remote.c
-@@ -0,0 +1,583 @@
-+/*
-+ * Copyright 2012 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include "internal.h"
-+
-+#include <string.h>
-+#include <unistd.h>
-+
-+#include <sys/un.h>
-+
-+#define FLAGS_READ ( VERTO_EV_FLAG_PERSIST \
-+ | VERTO_EV_FLAG_IO_CLOSE_FD \
-+ | VERTO_EV_FLAG_IO_ERROR \
-+ | VERTO_EV_FLAG_IO_READ )
-+#define FLAGS_WRITE (FLAGS_READ | VERTO_EV_FLAG_IO_WRITE)
-+
-+typedef struct request_ request;
-+struct request_ {
-+ request *prev, *next;
-+ k5_radius_remote *rr;
-+ k5_radius_packet *request;
-+ k5_radius_cb *cb;
-+ void *data;
-+ verto_ev *timer;
-+ time_t timeout;
-+ size_t retries;
-+ size_t sent;
-+};
-+
-+struct k5_radius_remote_ {
-+ krb5_context kctx;
-+ verto_ctx *vctx;
-+ verto_ev *io;
-+ char *secret;
-+ struct addrinfo *info;
-+ request *head, *tail;
-+ char buffer_[K5_RADIUS_PACKET_SIZE_MAX];
-+ krb5_data buffer;
-+};
-+
-+static void
-+on_io(verto_ctx *ctx, verto_ev *ev);
-+
-+static inline void *
-+memdup(const void *src, size_t size)
-+{
-+ void *tmp;
-+
-+ tmp = malloc(size);
-+ if (tmp == NULL)
-+ return NULL;
-+
-+ memcpy(tmp, src, size);
-+ return tmp;
-+}
-+
-+static const k5_radius_packet *
-+iterator(request **out)
-+{
-+ request *tmp = *out;
-+
-+ if (tmp == NULL)
-+ return NULL;
-+
-+ *out = tmp->prev;
-+ return tmp->request;
-+}
-+
-+static krb5_error_code
-+request_new(k5_radius_remote *rr, k5_radius_packet *rqst, time_t timeout,
-+ size_t retries, k5_radius_cb *cb, void *data, request **out)
-+{
-+ request *tmp;
-+
-+ tmp = calloc(1, sizeof(request));
-+ if (tmp == NULL)
-+ return ENOMEM;
-+
-+ tmp->rr = rr;
-+ tmp->request = rqst;
-+ tmp->cb = cb;
-+ tmp->data = data;
-+ tmp->timeout = timeout;
-+ tmp->retries = retries;
-+
-+ *out = tmp;
-+ return 0;
-+}
-+
-+static void
-+request_add(request *reqst)
-+{
-+ reqst->prev = reqst->rr->tail;
-+ reqst->rr->tail = reqst;
-+ if (reqst->prev != NULL)
-+ reqst->prev->next = reqst;
-+ if (reqst->rr->head == NULL)
-+ reqst->rr->head = reqst;
-+}
-+
-+static void
-+request_del(request *reqst)
-+{
-+ if (reqst->rr->head == reqst)
-+ reqst->rr->head = reqst->next;
-+ else if (reqst->prev != NULL)
-+ reqst->prev->next = reqst->next;
-+ if (reqst->rr->tail == reqst)
-+ reqst->rr->tail = reqst->prev;
-+ else if (reqst->next != NULL)
-+ reqst->next->prev = reqst->prev;
-+}
-+
-+static inline void
-+request_finish(request *reqst, krb5_error_code retval,
-+ const k5_radius_packet *response)
-+{
-+ if (retval != ETIMEDOUT)
-+ request_del(reqst);
-+
-+ (*reqst->cb)(retval, reqst->request, response, reqst->data);
-+
-+ if (retval != ETIMEDOUT) {
-+ k5_radius_packet_free(reqst->request);
-+ verto_del(reqst->timer);
-+ free(reqst);
-+ }
-+}
-+
-+static void
-+on_timeout(verto_ctx *ctx, verto_ev *ev)
-+{
-+ request *reqst = verto_get_private(ev);
-+ (void)ctx;
-+
-+ reqst->timer = NULL; /* Void the timer event. */
-+
-+ /* If we have more retries to perform, resend the packet. */
-+ if (reqst->retries-- > 1) {
-+ reqst->sent = 0;
-+ verto_set_flags(reqst->rr->io, FLAGS_WRITE);
-+ return;
-+ }
-+
-+ request_finish(reqst, ETIMEDOUT, NULL);
-+}
-+
-+static krb5_error_code
-+k5_radius_remote_connect(k5_radius_remote *rr)
-+{
-+ int i, sock = -1;
-+ verto_ev *ev;
-+
-+ sock = socket(rr->info->ai_family,
-+ rr->info->ai_socktype,
-+ rr->info->ai_protocol);
-+ if (sock < 0)
-+ return errno;
-+
-+ i = connect(sock, rr->info->ai_addr, rr->info->ai_addrlen);
-+ if (i < 0) {
-+ i = errno;
-+ close(sock);
-+ return i;
-+ }
-+
-+ ev = verto_add_io(rr->vctx, FLAGS_READ, on_io, sock);
-+ if (ev == NULL) {
-+ close(sock);
-+ return ENOMEM;
-+ }
-+
-+ rr->io = ev;
-+ verto_set_private(rr->io, rr, NULL);
-+ return 0;
-+}
-+
-+static krb5_error_code
-+k5_radius_remote_reconnect(k5_radius_remote *rr, int errnum)
-+{
-+ krb5_error_code retval;
-+ const krb5_data *tmp;
-+ request *r;
-+
-+ verto_del(rr->io);
-+ rr->io = NULL;
-+ retval = k5_radius_remote_connect(rr);
-+ if (retval != 0)
-+ return retval;
-+
-+ for (r = rr->head; r != NULL; r = r->next) {
-+ tmp = k5_radius_packet_encode(r->request);
-+
-+ /* Error out sent requests. */
-+ if (r->sent == tmp->length)
-+ request_finish(r, errnum, NULL);
-+
-+ /* Reset partially sent requests. */
-+ else
-+ r->sent = 0;
-+ }
-+
-+ return 0;
-+}
-+
-+static void
-+k5_radius_remote_shutdown(k5_radius_remote *rr, int errnum)
-+{
-+ verto_del(rr->io);
-+ rr->io = NULL;
-+ while (rr->head != NULL)
-+ request_finish(rr->head, errnum, NULL);
-+}
-+
-+static void
-+on_io_write(k5_radius_remote *rr)
-+{
-+ const krb5_data *tmp;
-+ request *r;
-+ int i;
-+
-+ for (r = rr->head; r != NULL; r = r->next) {
-+ tmp = k5_radius_packet_encode(r->request);
-+
-+ /* If the packet has already been sent, do nothing. */
-+ if (r->sent == tmp->length)
-+ continue;
-+
-+ /* Send the packet. */
-+ i = sendto(verto_get_fd(rr->io), tmp->data + r->sent,
-+ tmp->length - r->sent, 0, NULL, 0);
-+ if (i < 0) {
-+ i = errno;
-+
-+ switch (i) {
-+#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || EAGAIN - EWOULDBLOCK != 0)
-+ case EWOULDBLOCK:
-+#endif
-+#if defined(EAGAIN)
-+ case EAGAIN:
-+#endif
-+ case ENOBUFS:
-+ case EINTR:
-+ /* In this case, we just need to try again. */
-+ return;
-+
-+ case ECONNRESET:
-+ case ENOTCONN:
-+ case ENOTSOCK:
-+ case EBADF:
-+ case EPIPE:
-+ /* In this case, we need to re-connect. */
-+ i = k5_radius_remote_reconnect(rr, i);
-+ if (i == 0)
-+ return;
-+ break;
-+
-+ default:
-+ /* Unrecoverable. */
-+ break;
-+ }
-+
-+ /* Do a full reset. */
-+ k5_radius_remote_shutdown(rr, i);
-+ return;
-+ }
-+
-+ /* SOCK_STREAM permits partial writes. */
-+ if (rr->info->ai_socktype == SOCK_STREAM)
-+ r->sent += i;
-+ else if (i == (int)tmp->length)
-+ r->sent = i;
-+
-+ /* If the packet was completely sent, set a timeout. */
-+ if (r->sent == tmp->length) {
-+ verto_del(r->timer);
-+ r->timer = verto_add_timeout(rr->vctx, VERTO_EV_FLAG_NONE,
-+ on_timeout, r->timeout);
-+ if (r->timer == NULL)
-+ request_finish(r, ENOMEM, NULL);
-+ else
-+ verto_set_private(r->timer, r, NULL);
-+ }
-+
-+ return;
-+ }
-+
-+ verto_set_flags(rr->io, FLAGS_READ);
-+}
-+
-+static void
-+on_io_read(k5_radius_remote *rr)
-+{
-+ k5_radius_packet *req = NULL, *rsp = NULL;
-+ krb5_error_code retval;
-+ ssize_t pktlen;
-+ request *tmp;
-+ request *r;
-+ int i;
-+
-+ pktlen = sizeof(rr->buffer_);
-+ if (rr->info->ai_socktype == SOCK_STREAM) {
-+ pktlen = k5_radius_packet_bytes_needed(&rr->buffer);
-+ if (pktlen < 0) {
-+ retval = k5_radius_remote_reconnect(rr, EBADMSG);
-+ if (retval != 0)
-+ k5_radius_remote_shutdown(rr, retval);
-+ return;
-+ }
-+ }
-+
-+ /* Read the packet. */
-+ i = recv(verto_get_fd(rr->io), rr->buffer.data + rr->buffer.length,
-+ pktlen - rr->buffer.length, 0);
-+ if (i < 0) {
-+ i = errno;
-+
-+ switch (i) {
-+#if !defined(EWOULDBLOCK) || !defined(EAGAIN) || EAGAIN - EWOULDBLOCK != 0
-+ case EWOULDBLOCK:
-+#endif
-+ case EAGAIN:
-+ case EINTR:
-+ /* In this case, we just need to try again. */
-+ return;
-+
-+ case ECONNREFUSED:
-+ case ECONNRESET:
-+ case ENOTCONN:
-+ /*
-+ * When doing UDP against a local socket, the kernel will notify
-+ * when the daemon closes. But not against remote sockets. We want
-+ * to treat them both the same. Returning here will cause an
-+ * eventual timeout.
-+ */
-+ if (rr->info->ai_socktype != SOCK_STREAM)
-+ return;
-+
-+ /* Fall through. */
-+ case ENOTSOCK:
-+ case EBADF:
-+ case EPIPE:
-+ /* In this case, we need to re-connect. */
-+ i = k5_radius_remote_reconnect(rr, i);
-+ if (i == 0)
-+ return;
-+ break;
-+
-+ default:
-+ /* Unrecoverable. */
-+ break;
-+ }
-+
-+ /* Do a full reset. */
-+ k5_radius_remote_shutdown(rr, i);
-+ return;
-+ }
-+
-+ /* If we have a partial read or just the header, try again. */
-+ rr->buffer.length += i;
-+ pktlen = k5_radius_packet_bytes_needed(&rr->buffer);
-+ if (rr->info->ai_socktype == SOCK_STREAM && pktlen > 0)
-+ return;
-+
-+ /* Decode the packet. */
-+ tmp = rr->tail;
-+ retval = k5_radius_packet_decode(rr->kctx, rr->secret, &rr->buffer, FALSE,
-+ (k5_radius_packet_iter_cb*)iterator, &tmp,
-+ &req, &rsp);
-+ rr->buffer.length = 0;
-+ if (retval != 0)
-+ return;
-+
-+ /* Match the response with an outstanding request. */
-+ for (r = rr->tail; req != NULL && r != NULL; r = r->prev) {
-+ if (r->request == req &&
-+ r->sent == k5_radius_packet_encode(r->request)->length) {
-+ request_finish(r, 0, rsp);
-+ break;
-+ }
-+ }
-+
-+ k5_radius_packet_free(rsp);
-+}
-+
-+static void
-+on_io(verto_ctx *ctx, verto_ev *ev)
-+{
-+ k5_radius_remote *rr;
-+ (void)ctx;
-+
-+ rr = verto_get_private(ev);
-+
-+ if (verto_get_fd_state(ev) & VERTO_EV_FLAG_IO_WRITE)
-+ on_io_write(rr);
-+
-+ else
-+ on_io_read(rr);
-+}
-+
-+krb5_error_code
-+k5_radius_remote_new(krb5_context kctx, verto_ctx *vctx,
-+ const struct addrinfo *info, const char *secret,
-+ k5_radius_remote **rr)
-+{
-+ krb5_error_code retval = ENOMEM;
-+ k5_radius_remote *tmp = NULL;
-+
-+ tmp = calloc(1, sizeof(k5_radius_remote));
-+ if (tmp == NULL)
-+ goto error;
-+ tmp->kctx = kctx;
-+ tmp->vctx = vctx;
-+ tmp->buffer.data = tmp->buffer_;
-+
-+ tmp->secret = strdup(secret);
-+ if (tmp->secret == NULL)
-+ goto error;
-+
-+ tmp->info = memdup(info, sizeof(*info));
-+ if (tmp->info == NULL)
-+ goto error;
-+
-+ tmp->info->ai_addr = memdup(info->ai_addr, info->ai_addrlen);
-+ if (tmp->info == NULL)
-+ goto error;
-+ tmp->info->ai_next = NULL;
-+ tmp->info->ai_canonname = NULL;
-+
-+ retval = k5_radius_remote_connect(tmp);
-+ if (retval != 0)
-+ goto error;
-+
-+ *rr = tmp;
-+ return 0;
-+
-+error:
-+ k5_radius_remote_free(tmp);
-+ return retval;
-+}
-+
-+void
-+k5_radius_remote_free(k5_radius_remote *rr)
-+{
-+ if (rr == NULL)
-+ return;
-+
-+ while (rr->head != NULL)
-+ request_finish(rr->head, ECANCELED, NULL);
-+
-+ free(rr->secret);
-+ if (rr->info != NULL)
-+ free(rr->info->ai_addr);
-+ free(rr->info);
-+ verto_del(rr->io);
-+ free(rr);
-+}
-+
-+krb5_error_code
-+k5_radius_remote_send(k5_radius_remote *rr, k5_radius_code code,
-+ k5_radius_attrset *attrs, k5_radius_cb *cb, void *data,
-+ time_t timeout, size_t retries,
-+ const k5_radius_packet **pkt)
-+{
-+ k5_radius_packet *tmp = NULL;
-+ krb5_error_code retval;
-+ request *r;
-+
-+ r = rr->tail;
-+ retval = k5_radius_packet_new_request(rr->kctx, rr->secret, code, attrs,
-+ (k5_radius_packet_iter_cb*)iterator, &r,
-+ &tmp);
-+ if (retval != 0)
-+ goto error;
-+
-+ for (r = rr->head; r != NULL; r = r->next) {
-+ if (r->request == tmp) {
-+ retval = EALREADY;
-+ goto error;
-+ }
-+ }
-+
-+ if (rr->io == NULL) {
-+ retval = k5_radius_remote_connect(rr);
-+ if (retval != 0)
-+ goto error;
-+ }
-+
-+ if (rr->info->ai_socktype == SOCK_STREAM)
-+ retries = 0;
-+ timeout = timeout / (retries + 1);
-+ retval = request_new(rr, tmp, timeout, retries, cb, data, &r);
-+ if (retval != 0)
-+ goto error;
-+
-+ if ((verto_get_flags(rr->io) & VERTO_EV_FLAG_IO_WRITE) == 0)
-+ verto_set_flags(rr->io, FLAGS_WRITE);
-+
-+ request_add(r);
-+ if (pkt != NULL)
-+ *pkt = tmp;
-+ return 0;
-+
-+error:
-+ k5_radius_packet_free(tmp);
-+ return retval;
-+}
-+
-+void
-+k5_radius_remote_cancel(k5_radius_remote *rr, const k5_radius_packet *pkt)
-+{
-+ request *r;
-+
-+ for (r = rr->head; r != NULL; r = r->next) {
-+ if (r->request == pkt) {
-+ request_finish(r, ECANCELED, NULL);
-+ return;
-+ }
-+ }
-+}
-+
-+krb5_boolean
-+k5_radius_remote_equals(const k5_radius_remote *rr,
-+ const struct addrinfo *info, const char *secret)
-+{
-+ struct sockaddr_un *a, *b;
-+
-+ if (strcmp(rr->secret, secret) != 0)
-+ return FALSE;
-+
-+ if (info->ai_addrlen != rr->info->ai_addrlen)
-+ return FALSE;
-+
-+ if (info->ai_family != rr->info->ai_family)
-+ return FALSE;
-+
-+ if (info->ai_socktype != rr->info->ai_socktype)
-+ return FALSE;
-+
-+ if (info->ai_protocol != rr->info->ai_protocol)
-+ return FALSE;
-+
-+ if (info->ai_flags != rr->info->ai_flags)
-+ return FALSE;
-+
-+ if (memcmp(rr->info->ai_addr, info->ai_addr, info->ai_addrlen) != 0) {
-+ /* AF_UNIX fails the memcmp() test due to
-+ * uninitialized bytes after the socket name. */
-+ if (info->ai_family != AF_UNIX)
-+ return FALSE;
-+
-+ a = (struct sockaddr_un*) info->ai_addr;
-+ b = (struct sockaddr_un*) rr->info->ai_addr;
-+ if (strncmp(a->sun_path, b->sun_path, sizeof(a->sun_path)) != 0)
-+ return FALSE;
-+ }
-+
-+ return TRUE;
-+}
-diff --git a/src/lib/radius/test/attr.c b/src/lib/radius/test/attr.c
-new file mode 100644
-index 0000000..fdde4fe
---- /dev/null
-+++ b/src/lib/radius/test/attr.c
-@@ -0,0 +1,88 @@
-+/*
-+ * Copyright 2012 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include "test.h"
-+
-+int
-+main()
-+{
-+ const char encoded[] = { 0xba, 0xfc, 0xed, 0x50, 0xe1, 0xeb, 0xa6, 0xc3,
-+ 0xc1, 0x75, 0x20, 0xe9, 0x10, 0xce, 0xc2, 0xcb };
-+ const uint8_t auth[] = { 0xac, 0x9d, 0xc1, 0x62, 0x08, 0xc4, 0xc7, 0x8b,
-+ 0xa1, 0x2f, 0x25, 0x0a, 0xc4, 0x1d, 0x36, 0x41 };
-+ char outbuf[RADIUS_ATTR_SIZE_MAX];
-+ const char *decoded = "accept";
-+ const char *secret = "foo";
-+ krb5_error_code retval;
-+ krb5_context ctx;
-+ const char *tmp;
-+ krb5_data in, out;
-+
-+ noerror(krb5_init_context(&ctx));
-+
-+ /* Make sure User-Name is 1. */
-+ insist(k5_radius_attr_name2num("User-Name") == 1);
-+
-+ /* Make sure 2 is User-Password. */
-+ tmp = k5_radius_attr_num2name(2);
-+ insist(tmp != NULL);
-+ insist(strcmp(tmp, "User-Password") == 0);
-+
-+ /* Test decoding. */
-+ in.data = (char *)encoded;
-+ in.length = sizeof(encoded);
-+ out.data = outbuf;
-+ out.length = sizeof(outbuf);
-+ noerror(k5_radius_attr_decode(ctx, secret, auth,
-+ k5_radius_attr_name2num("User-Password"),
-+ &in, &out));
-+ insist(out.length == strlen(decoded));
-+ insist(memcmp(out.data, decoded, out.length) == 0);
-+
-+ /* Test encoding. */
-+ in.data = (char *)decoded;
-+ in.length = strlen(decoded);
-+ out.data = outbuf;
-+ out.length = sizeof(outbuf);
-+ retval = k5_radius_attr_encode(ctx, secret, auth,
-+ k5_radius_attr_name2num("User-Password"),
-+ &in, &out);
-+ insist(retval == 0);
-+ insist(out.length == sizeof(encoded));
-+ insist(memcmp(out.data, encoded, out.length) == 0);
-+
-+ /* Test constraint. */
-+ in.length = 100;
-+ insist(k5_radius_attr_valid(k5_radius_attr_name2num("User-Password"),
-+ &in) == 0);
-+ in.length = 200;
-+ insist(k5_radius_attr_valid(k5_radius_attr_name2num("User-Password"),
-+ &in) != 0);
-+
-+ krb5_free_context(ctx);
-+ return 0;
-+}
-diff --git a/src/lib/radius/test/attrset.c b/src/lib/radius/test/attrset.c
-new file mode 100644
-index 0000000..ba41724
---- /dev/null
-+++ b/src/lib/radius/test/attrset.c
-@@ -0,0 +1,98 @@
-+/*
-+ * Copyright 2012 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include "test.h"
-+
-+int
-+main()
-+{
-+ const uint8_t auth[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
-+ const char encpass[] = { 0x58, 0x8d, 0xff, 0xda, 0x37, 0xf9, 0xe4, 0xca,
-+ 0x19, 0xae, 0x49, 0xb7, 0x16, 0x6d, 0x58, 0x27 };
-+ char buffer[K5_RADIUS_PACKET_SIZE_MAX], encoded[RADIUS_ATTR_SIZE_MAX];
-+ const char *username = "testUser";
-+ const char *password = "accept";
-+ const krb5_data *tmpp;
-+ k5_radius_attrset *set;
-+ krb5_context ctx;
-+ size_t len = 0;
-+ krb5_data tmp;
-+
-+ noerror(krb5_init_context(&ctx));
-+ noerror(k5_radius_attrset_new(ctx, &set));
-+
-+ /* Add username. */
-+ tmp.data = (char *)username;
-+ tmp.length = strlen(username);
-+ noerror(k5_radius_attrset_add(set, k5_radius_attr_name2num("User-Name"),
-+ &tmp));
-+
-+ /* Add password. */
-+ tmp.data = (char *)password;
-+ tmp.length = strlen(password);
-+ noerror(k5_radius_attrset_add(set,
-+ k5_radius_attr_name2num("User-Password"),
-+ &tmp));
-+
-+ /* Encode attrset. */
-+ tmp.data = buffer;
-+ tmp.length = sizeof(buffer);
-+ noerror(k5_radius_attrset_encode(set, "foo", auth, &tmp));
-+ k5_radius_attrset_free(set);
-+
-+ /* Manually encode User-Name. */
-+ encoded[len + 0] = k5_radius_attr_name2num("User-Name");
-+ encoded[len + 1] = strlen(username) + 2;
-+ memcpy(encoded + len + 2, username, strlen(username));
-+ len += encoded[len + 1];
-+
-+ /* Manually encode User-Password. */
-+ encoded[len + 0] = k5_radius_attr_name2num("User-Password");
-+ encoded[len + 1] = sizeof(encpass) + 2;
-+ memcpy(encoded + len + 2, encpass, sizeof(encpass));
-+ len += encoded[len + 1];
-+
-+ /* Compare output. */
-+ insist(len == tmp.length);
-+ insist(memcmp(encoded, tmp.data, tmp.length) == 0);
-+
-+ /* Decode output. */
-+ noerror(k5_radius_attrset_decode(ctx, &tmp, "foo", auth, &set));
-+
-+ /* Test getting an attribute. */
-+ tmp.data = (char *)username;
-+ tmp.length = strlen(username);
-+ tmpp = k5_radius_attrset_get(set, k5_radius_attr_name2num("User-Name"), 0);
-+ insist(tmpp != NULL);
-+ insist(tmpp->length == tmp.length);
-+ insist(strncmp(tmpp->data, tmp.data, tmp.length) == 0);
-+
-+ k5_radius_attrset_free(set);
-+ krb5_free_context(ctx);
-+ return 0;
-+}
-diff --git a/src/lib/radius/test/client.c b/src/lib/radius/test/client.c
-new file mode 100644
-index 0000000..9315b92
---- /dev/null
-+++ b/src/lib/radius/test/client.c
-@@ -0,0 +1,141 @@
-+/*
-+ * Copyright 2012 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include "daemon.h"
-+
-+#define EVENT_COUNT 4
-+
-+static struct
-+{
-+ int count;
-+ struct event events[EVENT_COUNT];
-+} record;
-+
-+static verto_ctx *vctx;
-+
-+static void
-+callback(krb5_error_code retval, const k5_radius_packet *request,
-+ const k5_radius_packet *response, void *data)
-+{
-+ struct event *evt;
-+
-+ /* Silence unused parameter warnings. */
-+ (void)data;
-+ (void)request;
-+
-+ evt = &record.events[record.count++];
-+ evt->error = retval != 0;
-+ if (evt->error)
-+ evt->result.retval = retval;
-+ else
-+ evt->result.code = k5_radius_packet_get_code(response);
-+/*
-+ if (evt->error)
-+ printf("event: %d %s\n", record.count - 1,
-+ strerror(evt->result.retval));
-+ else
-+ printf("event: %d %d\n", record.count - 1,
-+ evt->result.code);
-+*/
-+ verto_break(vctx);
-+}
-+
-+int
-+main(int argc, const char **argv)
-+{
-+ k5_radius_attrset *attrs;
-+ k5_radius_client *rc;
-+ krb5_context kctx;
-+ krb5_data tmp;
-+
-+ if (!daemon_start(argc, argv)) {
-+ fprintf(stderr, "Unable to start pyrad daemon, skipping test...\n");
-+ return 0;
-+ }
-+
-+ noerror(krb5_init_context(&kctx));
-+ insist((vctx = verto_new(NULL, VERTO_EV_TYPE_IO |
-+ VERTO_EV_TYPE_TIMEOUT)) != NULL);
-+ noerror(k5_radius_client_new(kctx, vctx, &rc));
-+
-+ tmp = make_data_string("testUser");
-+ noerror(k5_radius_attrset_new(kctx, &attrs));
-+ noerror(k5_radius_attrset_add(attrs, k5_radius_attr_name2num("User-Name"),
-+ &tmp));
-+
-+ /* Test accept. */
-+ tmp = make_data_string("accept");
-+ noerror(k5_radius_attrset_add(attrs, k5_radius_attr_name2num("User-Password"),
-+ &tmp));
-+ noerror(k5_radius_client_send(rc, k5_radius_code_name2num("Access-Request"),
-+ attrs, "localhost", "foo", 1000, 3,
-+ callback, NULL));
-+ verto_run(vctx);
-+
-+ /* Test reject. */
-+ tmp = make_data_string("reject");
-+ k5_radius_attrset_del(attrs, k5_radius_attr_name2num("User-Password"), 0);
-+ noerror(k5_radius_attrset_add(attrs, k5_radius_attr_name2num("User-Password"),
-+ &tmp));
-+ noerror(k5_radius_client_send(rc, k5_radius_code_name2num("Access-Request"),
-+ attrs, "localhost", "foo", 1000, 3,
-+ callback, NULL));
-+ verto_run(vctx);
-+
-+ /* Test timeout. */
-+ daemon_stop();
-+ noerror(k5_radius_client_send(rc, k5_radius_code_name2num("Access-Request"),
-+ attrs, "localhost", "foo", 1000, 3,
-+ callback, NULL));
-+ verto_run(vctx);
-+
-+ /* Test outstanding packet freeing. */
-+ noerror(k5_radius_client_send(rc, k5_radius_code_name2num("Access-Request"),
-+ attrs, "localhost", "foo", 1000, 3,
-+ callback, NULL));
-+ k5_radius_client_free(rc);
-+ rc = NULL;
-+
-+ /* Verify the results. */
-+ insist(record.count == EVENT_COUNT);
-+ insist(record.events[0].error == FALSE);
-+ insist(record.events[0].result.code ==
-+ k5_radius_code_name2num("Access-Accept"));
-+ insist(record.events[1].error == FALSE);
-+ insist(record.events[1].result.code ==
-+ k5_radius_code_name2num("Access-Reject"));
-+ insist(record.events[2].error == TRUE);
-+ insist(record.events[2].result.retval == ETIMEDOUT);
-+ insist(record.events[3].error == TRUE);
-+ insist(record.events[3].result.retval == ECANCELED);
-+
-+ k5_radius_attrset_free(attrs);
-+ k5_radius_client_free(rc);
-+ verto_free(vctx);
-+ krb5_free_context(kctx);
-+ return 0;
-+}
-diff --git a/src/lib/radius/test/code.c b/src/lib/radius/test/code.c
-new file mode 100644
-index 0000000..c1b5af0
---- /dev/null
-+++ b/src/lib/radius/test/code.c
-@@ -0,0 +1,52 @@
-+/*
-+ * Copyright 2012 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include "test.h"
-+
-+int
-+main()
-+{
-+ const char *tmp;
-+
-+ insist(k5_radius_code_name2num("Access-Request") == 1);
-+ insist(k5_radius_code_name2num("Access-Accept") == 2);
-+ insist(k5_radius_code_name2num("Access-Reject") == 3);
-+
-+ tmp = k5_radius_code_num2name(1);
-+ insist(tmp != NULL);
-+ insist(strcmp(tmp, "Access-Request") == 0);
-+
-+ tmp = k5_radius_code_num2name(2);
-+ insist(tmp != NULL);
-+ insist(strcmp(tmp, "Access-Accept") == 0);
-+
-+ tmp = k5_radius_code_num2name(3);
-+ insist(tmp != NULL);
-+ insist(strcmp(tmp, "Access-Reject") == 0);
-+
-+ return 0;
-+}
-diff --git a/src/lib/radius/test/daemon.h b/src/lib/radius/test/daemon.h
-new file mode 100644
-index 0000000..7501013
---- /dev/null
-+++ b/src/lib/radius/test/daemon.h
-@@ -0,0 +1,85 @@
-+/*
-+ * Copyright 2012 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include "test.h"
-+#include <libgen.h>
-+
-+#define DAEMON "/daemon.py"
-+
-+static pid_t __daemon_pid;
-+
-+static void
-+daemon_stop(void)
-+{
-+ if (__daemon_pid == 0)
-+ return;
-+ kill(__daemon_pid, SIGTERM);
-+ waitpid(__daemon_pid, NULL, 0);
-+ __daemon_pid = 0;
-+}
-+
-+static krb5_boolean
-+daemon_start(int argc, const char **argv)
-+{
-+ sigset_t set;
-+ size_t i;
-+ int sig;
-+
-+ if (argc != 2 || argv == NULL)
-+ return FALSE;
-+
-+ if (__daemon_pid != 0)
-+ return TRUE;
-+
-+ if (sigemptyset(&set) != 0)
-+ return FALSE;
-+
-+ if (sigaddset(&set, SIGUSR1) != 0)
-+ return FALSE;
-+
-+ if (sigaddset(&set, SIGCHLD) != 0)
-+ return FALSE;
-+
-+ if (sigprocmask(SIG_BLOCK, &set, NULL) != 0)
-+ return FALSE;
-+
-+ __daemon_pid = fork();
-+ if (__daemon_pid == 0)
-+ exit(execlp("python", "python", argv[1], NULL));
-+
-+ if (sigwait(&set, &sig) != 0 || sig == SIGCHLD) {
-+ daemon_stop();
-+ __daemon_pid = 0;
-+ return FALSE;
-+ }
-+
-+ atexit(daemon_stop);
-+ usleep(100000);
-+ return TRUE;
-+}
-+
-+#undef DAEMON
-diff --git a/src/lib/radius/test/daemon.py b/src/lib/radius/test/daemon.py
-new file mode 100644
-index 0000000..021607d
---- /dev/null
-+++ b/src/lib/radius/test/daemon.py
-@@ -0,0 +1,76 @@
-+#!/usr/bin/python
-+#
-+# Copyright 2012 Red Hat, Inc. All rights reserved.
-+#
-+# Redistribution and use in source and binary forms, with or without
-+# modification, are permitted provided that the following conditions are met:
-+#
-+# 1. Redistributions of source code must retain the above copyright
-+# notice, this list of conditions and the following disclaimer.
-+#
-+# 2. Redistributions in binary form must reproduce the above copyright
-+# notice, this list of conditions and the following disclaimer in
-+# the documentation and/or other materials provided with the
-+# distribution.
-+#
-+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+
-+import StringIO
-+import os
-+import sys
-+import signal
-+
-+try:
-+ from pyrad import dictionary, packet, server
-+except ImportError:
-+ sys.stdout.write("pyrad not found!\n")
-+ sys.exit(0)
-+
-+# We could use a dictionary file, but since we need
-+# such few attributes, we'll just include them here
-+DICTIONARY = """
-+ATTRIBUTE User-Name 1 string
-+ATTRIBUTE User-Password 2 octets
-+ATTRIBUTE NAS-Identifier 32 string
-+"""
-+
-+class TestServer(server.Server):
-+ def _HandleAuthPacket(self, pkt):
-+ server.Server._HandleAuthPacket(self, pkt)
-+
-+ passwd = []
-+
-+ print "Request: "
-+ for key in pkt.keys():
-+ if key == "User-Password":
-+ passwd = map(pkt.PwDecrypt, pkt[key])
-+ print "\t%s\t%s" % (key, passwd)
-+ else:
-+ print "\t%s\t%s" % (key, pkt[key])
-+
-+ reply = self.CreateReplyPacket(pkt)
-+ if passwd == ['accept']:
-+ reply.code = packet.AccessAccept
-+ print "Response: %s" % "Access-Accept"
-+ else:
-+ reply.code = packet.AccessReject
-+ print "Response: %s" % "Access-Reject"
-+ print
-+ self.SendReplyPacket(pkt.fd, reply)
-+
-+srv = TestServer(addresses=["localhost"],
-+ hosts={"127.0.0.1":
-+ server.RemoteHost("127.0.0.1", "foo", "localhost")},
-+ dict=dictionary.Dictionary(StringIO.StringIO(DICTIONARY)))
-+os.kill(os.getppid(), signal.SIGUSR1)
-+srv.Run()
-diff --git a/src/lib/radius/test/packet.c b/src/lib/radius/test/packet.c
-new file mode 100644
-index 0000000..85e0758
---- /dev/null
-+++ b/src/lib/radius/test/packet.c
-@@ -0,0 +1,203 @@
-+/*
-+ * Copyright 2012 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include "test.h"
-+#include "daemon.h"
-+
-+#define ACCEPT_PACKET 0
-+#define REJECT_PACKET 1
-+
-+static k5_radius_packet *packets[3];
-+
-+static k5_radius_packet *
-+iterator(void *data, krb5_boolean cancel)
-+{
-+ k5_radius_packet *tmp;
-+ int *i = data;
-+
-+ if (cancel || packets[*i] == NULL)
-+ return NULL;
-+
-+ tmp = packets[*i];
-+ *i += 1;
-+ return tmp;
-+}
-+
-+static krb5_error_code
-+make_packet(krb5_context ctx, const krb5_data *username,
-+ const krb5_data *password, k5_radius_packet **pkt)
-+{
-+ k5_radius_attrset *set = NULL;
-+ k5_radius_packet *tmp = NULL;
-+ krb5_error_code retval;
-+ const krb5_data *data;
-+ int i = 0;
-+
-+ retval = k5_radius_attrset_new(ctx, &set);
-+ if (retval != 0)
-+ goto out;
-+
-+ retval = k5_radius_attrset_add(set,
-+ k5_radius_attr_name2num("User-Name"),
-+ username);
-+ if (retval != 0)
-+ goto out;
-+
-+ retval = k5_radius_attrset_add(set,
-+ k5_radius_attr_name2num("User-Password"),
-+ password);
-+ if (retval != 0)
-+ goto out;
-+
-+ retval = k5_radius_packet_new_request(ctx, "foo",
-+ k5_radius_code_name2num("Access-Request"), set,
-+ iterator, &i, &tmp);
-+ if (retval != 0)
-+ goto out;
-+
-+ data = k5_radius_packet_get_attr(tmp,
-+ k5_radius_attr_name2num("User-Name"),
-+ 0);
-+ if (data == NULL) {
-+ retval = ENOENT;
-+ goto out;
-+ }
-+
-+ if (data->length != username->length ||
-+ memcmp(data->data, username->data, data->length) != 0) {
-+ retval = EINVAL;
-+ goto out;
-+ }
-+
-+ *pkt = tmp;
-+ tmp = NULL;
-+
-+out:
-+ k5_radius_attrset_free(set);
-+ k5_radius_packet_free(tmp);
-+ return retval;
-+}
-+
-+static krb5_error_code
-+do_auth(krb5_context ctx, struct addrinfo *ai, const char *secret,
-+ const k5_radius_packet *rqst, krb5_boolean *auth)
-+{
-+ k5_radius_packet *req = NULL, *rsp = NULL;
-+ char tmp[K5_RADIUS_PACKET_SIZE_MAX];
-+ const krb5_data *request;
-+ krb5_error_code retval;
-+ krb5_data response;
-+ int sock = -1, i;
-+
-+ response.data = tmp;
-+ response.length = sizeof(tmp);
-+
-+ sock = socket(ai->ai_family, ai->ai_socktype, 0);
-+ if (sock < 0) {
-+ retval = errno;
-+ goto out;
-+ }
-+
-+ request = k5_radius_packet_encode(rqst);
-+ k5_radius_packet_print(stderr, request);
-+ if (sendto(sock, request->data, request->length, 0,
-+ ai->ai_addr, ai->ai_addrlen) < 0) {
-+ retval = errno;
-+ goto out;
-+ }
-+
-+ i = recv(sock, response.data, sizeof(tmp), 0);
-+ if (i < 0) {
-+ retval = errno;
-+ goto out;
-+ }
-+ response.length = i;
-+
-+ i = 0;
-+ retval = k5_radius_packet_decode(ctx, secret, &response, FALSE,
-+ iterator, &i, &req, &rsp);
-+ if (retval != 0)
-+ goto out;
-+
-+ if (req != rqst) {
-+ retval = EBADMSG;
-+ goto out;
-+ }
-+
-+ *auth = k5_radius_packet_get_code(rsp) ==
-+ k5_radius_code_name2num("Access-Accept");
-+
-+out:
-+ k5_radius_packet_free(rsp);
-+ if (sock >= 0)
-+ close(sock);
-+ return retval;
-+}
-+
-+int
-+main(int argc, const char **argv)
-+{
-+ struct addrinfo *ai = NULL, hints;
-+ krb5_data username, password;
-+ krb5_boolean auth = FALSE;
-+ krb5_context ctx;
-+
-+ username.data = "testUser";
-+ username.length = strlen(username.data);
-+
-+ if (!daemon_start(argc, argv)) {
-+ fprintf(stderr, "Unable to start pyrad daemon, skipping test...\n");
-+ return 0;
-+ }
-+
-+ noerror(krb5_init_context(&ctx));
-+
-+ password.data = "accept";
-+ password.length = strlen(password.data);
-+ noerror(make_packet(ctx, &username, &password, &packets[ACCEPT_PACKET]));
-+
-+ password.data = "reject";
-+ password.length = strlen(password.data);
-+ noerror(make_packet(ctx, &username, &password, &packets[REJECT_PACKET]));
-+
-+ memset(&hints, 0, sizeof(hints));
-+ hints.ai_family = AF_INET;
-+ hints.ai_socktype = SOCK_DGRAM;
-+ noerror(gai_error_(getaddrinfo("127.0.0.1", "radius", &hints, &ai)));
-+
-+ noerror(do_auth(ctx, ai, "foo", packets[ACCEPT_PACKET], &auth));
-+ insist(auth == TRUE);
-+
-+ noerror(do_auth(ctx, ai, "foo", packets[REJECT_PACKET], &auth));
-+ insist(auth == FALSE);
-+
-+ k5_radius_packet_free(packets[ACCEPT_PACKET]);
-+ k5_radius_packet_free(packets[REJECT_PACKET]);
-+ krb5_free_context(ctx);
-+ freeaddrinfo(ai);
-+ return 0;
-+}
-diff --git a/src/lib/radius/test/remote.c b/src/lib/radius/test/remote.c
-new file mode 100644
-index 0000000..9132d0e
---- /dev/null
-+++ b/src/lib/radius/test/remote.c
-@@ -0,0 +1,186 @@
-+/*
-+ * Copyright 2012 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include "test.h"
-+#include "daemon.h"
-+
-+#define EVENT_COUNT 6
-+
-+static struct
-+{
-+ int count;
-+ struct event events[EVENT_COUNT];
-+} record;
-+
-+static k5_radius_attrset *set;
-+static k5_radius_remote *rr;
-+static verto_ctx *vctx;
-+
-+static void
-+callback(krb5_error_code retval, const k5_radius_packet *request,
-+ const k5_radius_packet *response, void *data)
-+{
-+ struct event *evt;
-+
-+ /* Silence unused parameter warnings. */
-+ (void)data;
-+ (void)request;
-+
-+ evt = &record.events[record.count++];
-+ evt->error = retval != 0;
-+ if (evt->error)
-+ evt->result.retval = retval;
-+ else
-+ evt->result.code = k5_radius_packet_get_code(response);
-+/*
-+ if (evt->error)
-+ fprintf(stderr, "event: %d %s\n", record.count - 1,
-+ strerror(evt->result.retval));
-+ else
-+ fprintf(stderr, "event: %d %d\n", record.count - 1,
-+ evt->result.code);
-+*/
-+ verto_break(vctx);
-+}
-+
-+static void
-+remote_new(krb5_context kctx, verto_ctx *vctx, k5_radius_remote **remote)
-+{
-+ struct addrinfo *ai = NULL, hints;
-+
-+ memset(&hints, 0, sizeof(hints));
-+ hints.ai_family = AF_INET;
-+ hints.ai_socktype = SOCK_DGRAM;
-+ noerror(gai_error_(getaddrinfo("127.0.0.1", "radius", &hints, &ai)));
-+
-+ noerror(k5_radius_remote_new(kctx, vctx, ai, "foo", remote));
-+ insist(k5_radius_remote_equals(*remote, ai, "foo"));
-+ freeaddrinfo(ai);
-+}
-+
-+static krb5_error_code
-+do_auth(k5_radius_remote *rr, k5_radius_attrset *set, char *password,
-+ const k5_radius_packet **pkt)
-+{
-+ const k5_radius_packet *tmppkt;
-+ krb5_error_code retval;
-+ krb5_data tmp;
-+
-+ k5_radius_attrset_del(set, k5_radius_attr_name2num("User-Password"), 0);
-+
-+ tmp = make_data_string(password);
-+ retval = k5_radius_attrset_add(set, k5_radius_attr_name2num("User-Password"),
-+ &tmp);
-+ if (retval != 0)
-+ return retval;
-+
-+ retval = k5_radius_remote_send(rr, k5_radius_code_name2num("Access-Request"),
-+ set, callback, NULL, 1000, 3, &tmppkt);
-+ if (retval != 0)
-+ return retval;
-+
-+ if (pkt != NULL)
-+ *pkt = tmppkt;
-+ return 0;
-+}
-+
-+static void
-+test_timeout(verto_ctx *ctx, verto_ev *ev)
-+{
-+ static const k5_radius_packet *pkt;
-+ (void)ctx;
-+ (void)ev;
-+ noerror(do_auth(rr, set, "accept", &pkt));
-+ k5_radius_remote_cancel(rr, pkt);
-+}
-+
-+int
-+main(int argc, const char **argv)
-+{
-+ krb5_context kctx = NULL;
-+ krb5_data tmp;
-+
-+ if (!daemon_start(argc, argv)) {
-+ fprintf(stderr, "Unable to start pyrad daemon, skipping test...\n");
-+ return 0;
-+ }
-+
-+ /* Initialize. */
-+ noerror(krb5_init_context(&kctx));
-+ insist((vctx = verto_new(NULL, VERTO_EV_TYPE_IO |
-+ VERTO_EV_TYPE_TIMEOUT)) != NULL);
-+ remote_new(kctx, vctx, &rr);
-+
-+ /* Create attribute set. */
-+ noerror(k5_radius_attrset_new(kctx, &set));
-+ tmp = make_data_string("testUser");
-+ noerror(k5_radius_attrset_add(set, k5_radius_attr_name2num("User-Name"), &tmp));
-+
-+ /* Send accept packet. */
-+ noerror(do_auth(rr, set, "accept", NULL));
-+ verto_run(vctx);
-+
-+ /* Send reject packet. */
-+ noerror(do_auth(rr, set, "reject", NULL));
-+ verto_run(vctx);
-+
-+ /* Send canceled packet. */
-+ insist(verto_add_timeout(vctx, VERTO_EV_FLAG_NONE,
-+ test_timeout, 0) != NULL);
-+ verto_run(vctx);
-+
-+ /* Test timeout. */
-+ daemon_stop();
-+ noerror(do_auth(rr, set, "accept", NULL));
-+ verto_run(vctx);
-+
-+ /* Test outstanding packet freeing. */
-+ noerror(do_auth(rr, set, "accept", NULL));
-+ k5_radius_remote_free(rr);
-+ k5_radius_attrset_free(set);
-+
-+ /* Verify the results. */
-+ insist(record.count == EVENT_COUNT);
-+ insist(record.events[0].error == FALSE);
-+ insist(record.events[0].result.code ==
-+ k5_radius_code_name2num("Access-Accept"));
-+ insist(record.events[1].error == FALSE);
-+ insist(record.events[1].result.code ==
-+ k5_radius_code_name2num("Access-Reject"));
-+ insist(record.events[2].error == TRUE);
-+ insist(record.events[2].result.retval == ECANCELED);
-+ insist(record.events[3].error == TRUE);
-+ insist(record.events[3].result.retval == ETIMEDOUT);
-+ insist(record.events[4].error == TRUE);
-+ insist(record.events[4].result.retval == ECANCELED);
-+ insist(record.events[5].error == TRUE);
-+ insist(record.events[5].result.retval == ECANCELED);
-+
-+ verto_free(vctx);
-+ krb5_free_context(kctx);
-+ return 0;
-+}
-diff --git a/src/lib/radius/test/test.c b/src/lib/radius/test/test.c
-new file mode 100644
-index 0000000..8469c17
---- /dev/null
-+++ b/src/lib/radius/test/test.c
-@@ -0,0 +1,63 @@
-+/*
-+ * Copyright 2012 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include <krb5.h>
-+
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <string.h>
-+
-+void
-+noerror_(const char *file, int line, const char *cmd, int retval)
-+{
-+ if (retval == 0)
-+ return;
-+
-+ fprintf(stderr, "%s:%d: %s:\n\t%s\n", file, line, strerror(retval), cmd);
-+ exit(1);
-+}
-+
-+void
-+insist_(const char *file, int line, const char *cmd, krb5_boolean result)
-+{
-+ if (result)
-+ return;
-+
-+ fprintf(stderr, "%s:%d: insist failed:\n\t%s\n", file, line, cmd);
-+ exit(1);
-+}
-+
-+krb5_data
-+make_data_string(char *str)
-+{
-+ krb5_data d;
-+
-+ d.data = str;
-+ d.length = strlen(str);
-+
-+ return d;
-+}
-diff --git a/src/lib/radius/test/test.h b/src/lib/radius/test/test.h
-new file mode 100644
-index 0000000..4c3f62f
---- /dev/null
-+++ b/src/lib/radius/test/test.h
-@@ -0,0 +1,62 @@
-+/*
-+ * Copyright 2012 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#ifndef TEST_H_
-+#define TEST_H_
-+
-+#include "../internal.h"
-+
-+#include <sys/wait.h>
-+#include <signal.h>
-+#include <unistd.h>
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <string.h>
-+
-+#define insist(x) insist_(__FILE__, __LINE__, #x, x)
-+#define noerror(x) noerror_(__FILE__, __LINE__, #x, x)
-+
-+struct event
-+{
-+ krb5_boolean error;
-+ union
-+ {
-+ krb5_error_code retval;
-+ k5_radius_code code;
-+ } result;
-+};
-+
-+void
-+noerror_(const char *file, int line, const char *cmd, int retval);
-+
-+void
-+insist_(const char *file, int line, const char *cmd, krb5_boolean result);
-+
-+krb5_data
-+make_data_string(char *str);
-+
-+#endif /* TEST_H_ */
---
-1.8.1.4
-
diff --git a/0002-add-libkrad.patch b/0002-add-libkrad.patch
new file mode 100644
index 0000000..219a3da
--- /dev/null
+++ b/0002-add-libkrad.patch
@@ -0,0 +1,3902 @@
+From b15c58d7417c7fdd53994fb406213cfdaa8069ec Mon Sep 17 00:00:00 2001
+From: Nathaniel McCallum <npmccallum@redhat.com>
+Date: Thu, 4 Apr 2013 13:39:21 -0400
+Subject: [PATCH 2/4] add libkrad
+
+---
+ src/configure.in | 2 +-
+ src/include/Makefile.in | 1 +
+ src/include/krad.h | 267 ++++++++++++++++++++++
+ src/lib/Makefile.in | 2 +-
+ src/lib/krad/Makefile.in | 74 ++++++
+ src/lib/krad/attr.c | 317 ++++++++++++++++++++++++++
+ src/lib/krad/attrset.c | 244 ++++++++++++++++++++
+ src/lib/krad/client.c | 335 ++++++++++++++++++++++++++++
+ src/lib/krad/code.c | 111 +++++++++
+ src/lib/krad/deps | 156 +++++++++++++
+ src/lib/krad/internal.h | 155 +++++++++++++
+ src/lib/krad/libkrad.exports | 23 ++
+ src/lib/krad/packet.c | 475 +++++++++++++++++++++++++++++++++++++++
+ src/lib/krad/remote.c | 519 +++++++++++++++++++++++++++++++++++++++++++
+ src/lib/krad/t_attr.c | 89 ++++++++
+ src/lib/krad/t_attrset.c | 98 ++++++++
+ src/lib/krad/t_client.c | 126 +++++++++++
+ src/lib/krad/t_code.c | 54 +++++
+ src/lib/krad/t_daemon.h | 92 ++++++++
+ src/lib/krad/t_daemon.py | 76 +++++++
+ src/lib/krad/t_packet.c | 194 ++++++++++++++++
+ src/lib/krad/t_remote.c | 170 ++++++++++++++
+ src/lib/krad/t_test.c | 50 +++++
+ src/lib/krad/t_test.h | 60 +++++
+ 24 files changed, 3688 insertions(+), 2 deletions(-)
+ create mode 100644 src/include/krad.h
+ create mode 100644 src/lib/krad/Makefile.in
+ create mode 100644 src/lib/krad/attr.c
+ create mode 100644 src/lib/krad/attrset.c
+ create mode 100644 src/lib/krad/client.c
+ create mode 100644 src/lib/krad/code.c
+ create mode 100644 src/lib/krad/deps
+ create mode 100644 src/lib/krad/internal.h
+ create mode 100644 src/lib/krad/libkrad.exports
+ create mode 100644 src/lib/krad/packet.c
+ create mode 100644 src/lib/krad/remote.c
+ create mode 100644 src/lib/krad/t_attr.c
+ create mode 100644 src/lib/krad/t_attrset.c
+ create mode 100644 src/lib/krad/t_client.c
+ create mode 100644 src/lib/krad/t_code.c
+ create mode 100644 src/lib/krad/t_daemon.h
+ create mode 100644 src/lib/krad/t_daemon.py
+ create mode 100644 src/lib/krad/t_packet.c
+ create mode 100644 src/lib/krad/t_remote.c
+ create mode 100644 src/lib/krad/t_test.c
+ create mode 100644 src/lib/krad/t_test.h
+
+diff --git a/src/configure.in b/src/configure.in
+index faf93a1..d8676e5 100644
+--- a/src/configure.in
++++ b/src/configure.in
+@@ -1318,7 +1318,7 @@ dnl lib/krb5/ccache/ccapi
+ lib/rpc lib/rpc/unit-test
+
+ lib/kadm5 lib/kadm5/clnt lib/kadm5/srv lib/kadm5/unit-test
+-
++ lib/krad
+ lib/apputils
+
+ dnl ccapi ccapi/lib ccapi/lib/unix ccapi/server ccapi/server/unix ccapi/test
+diff --git a/src/include/Makefile.in b/src/include/Makefile.in
+index c69b809..869b04b 100644
+--- a/src/include/Makefile.in
++++ b/src/include/Makefile.in
+@@ -145,5 +145,6 @@ install-headers-unix install:: krb5/krb5.h profile.h
+ $(INSTALL_DATA) $(srcdir)/krb5/kadm5_hook_plugin.h $(DESTDIR)$(KRB5_INCDIR)$(S)krb5$(S)kadm5_hook_plugin.h
+ $(INSTALL_DATA) profile.h $(DESTDIR)$(KRB5_INCDIR)$(S)profile.h
+ $(INSTALL_DATA) $(srcdir)/gssapi.h $(DESTDIR)$(KRB5_INCDIR)$(S)gssapi.h
++ $(INSTALL_DATA) $(srcdir)/krad.h $(DESTDIR)$(KRB5_INCDIR)/krad.h
+
+ depend:: krb5/krb5.h $(BUILT_HEADERS)
+diff --git a/src/include/krad.h b/src/include/krad.h
+new file mode 100644
+index 0000000..d88b543
+--- /dev/null
++++ b/src/include/krad.h
+@@ -0,0 +1,267 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/*
++ * Copyright 2013 Red Hat, Inc. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in
++ * the documentation and/or other materials provided with the
++ * distribution.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
++ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
++ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++/*
++ * This API is not considered as stable as the main krb5 API.
++ *
++ * - We may make arbitrary incompatible changes between feature releases
++ * (e.g. from 1.12 to 1.13).
++ * - We will make some effort to avoid making incompatible changes for
++ * bugfix releases, but will make them if necessary.
++ */
++
++#ifndef KRAD_H_
++#define KRAD_H_
++
++#include <krb5.h>
++#include <verto.h>
++#include <stddef.h>
++#include <stdio.h>
++
++#define KRAD_PACKET_SIZE_MAX 4096
++
++#define KRAD_SERVICE_TYPE_LOGIN 1
++#define KRAD_SERVICE_TYPE_FRAMED 2
++#define KRAD_SERVICE_TYPE_CALLBACK_LOGIN 3
++#define KRAD_SERVICE_TYPE_CALLBACK_FRAMED 4
++#define KRAD_SERVICE_TYPE_OUTBOUND 5
++#define KRAD_SERVICE_TYPE_ADMINISTRATIVE 6
++#define KRAD_SERVICE_TYPE_NAS_PROMPT 7
++#define KRAD_SERVICE_TYPE_AUTHENTICATE_ONLY 8
++#define KRAD_SERVICE_TYPE_CALLBACK_NAS_PROMPT 9
++#define KRAD_SERVICE_TYPE_CALL_CHECK 10
++#define KRAD_SERVICE_TYPE_CALLBACK_ADMINISTRATIVE 11
++
++typedef struct krad_attrset_st krad_attrset;
++typedef struct krad_packet_st krad_packet;
++typedef struct krad_client_st krad_client;
++typedef unsigned char krad_code;
++typedef unsigned char krad_attr;
++
++/* Called when a response is received or the request times out. */
++typedef void
++(*krad_cb)(krb5_error_code retval, const krad_packet *request,
++ const krad_packet *response, void *data);
++
++/*
++ * Called to iterate over a set of requests. Either the callback will be
++ * called until it returns NULL, or it will be called with cancel = TRUE to
++ * terminate in the middle of an iteration.
++ */
++typedef const krad_packet *
++(*krad_packet_iter_cb)(void *data, krb5_boolean cancel);
++
++/*
++ * Code
++ */
++
++/* Convert a code name to its number. Only works for codes defined
++ * by RFC 2875 or 2882. Returns 0 if the name was not found. */
++krad_code
++krad_code_name2num(const char *name);
++
++/* Convert a code number to its name. Only works for attributes defined
++ * by RFC 2865 or 2882. Returns NULL if the name was not found. */
++const char *
++krad_code_num2name(krad_code code);
++
++/*
++ * Attribute
++ */
++
++/* Convert an attribute name to its number. Only works for attributes defined
++ * by RFC 2865. Returns 0 if the name was not found. */
++krad_attr
++krad_attr_name2num(const char *name);
++
++/* Convert an attribute number to its name. Only works for attributes defined
++ * by RFC 2865. Returns NULL if the name was not found. */
++const char *
++krad_attr_num2name(krad_attr type);
++
++/*
++ * Attribute set
++ */
++
++/* Create a new attribute set. */
++krb5_error_code
++krad_attrset_new(krb5_context ctx, krad_attrset **set);
++
++/* Create a deep copy of an attribute set. */
++krb5_error_code
++krad_attrset_copy(const krad_attrset *set, krad_attrset **copy);
++
++/* Free an attribute set. */
++void
++krad_attrset_free(krad_attrset *set);
++
++/* Add an attribute to a set. */
++krb5_error_code
++krad_attrset_add(krad_attrset *set, krad_attr type, const krb5_data *data);
++
++/* Add a four-octet unsigned number attribute to the given set. */
++krb5_error_code
++krad_attrset_add_number(krad_attrset *set, krad_attr type, krb5_ui_4 num);
++
++/* Delete the specified attribute. */
++void
++krad_attrset_del(krad_attrset *set, krad_attr type, size_t indx);
++
++/* Get the specified attribute. */
++const krb5_data *
++krad_attrset_get(const krad_attrset *set, krad_attr type, size_t indx);
++
++/*
++ * Packet
++ */
++
++/* Determine the bytes needed from the socket to get the whole packet. Don't
++ * cache the return value as it can change! Returns -1 on EBADMSG. */
++ssize_t
++krad_packet_bytes_needed(const krb5_data *buffer);
++
++/* Free a packet. */
++void
++krad_packet_free(krad_packet *pkt);
++
++/*
++ * Create a new request packet.
++ *
++ * This function takes the attributes specified in set and converts them into a
++ * radius packet. The packet will have a randomized id. If cb is not NULL, it
++ * will be called passing data as the argument to iterate over a set of
++ * outstanding requests. In this case, the id will be both random and unique
++ * across the set of requests.
++ */
++krb5_error_code
++krad_packet_new_request(krb5_context ctx, const char *secret, krad_code code,
++ const krad_attrset *set, krad_packet_iter_cb cb,
++ void *data, krad_packet **request);
++
++/*
++ * Create a new response packet.
++ *
++ * This function is similar to krad_packet_new_requst() except that it crafts a
++ * packet in response to a request packet. This new packet will borrow values
++ * from the request such as the id and the authenticator.
++ */
++krb5_error_code
++krad_packet_new_response(krb5_context ctx, const char *secret, krad_code code,
++ const krad_attrset *set, const krad_packet *request,
++ krad_packet **response);
++
++/*
++ * Decode a request radius packet from krb5_data.
++ *
++ * The resulting decoded packet will be a request packet stored in *reqpkt.
++ *
++ * If cb is NULL, *duppkt will always be NULL.
++ *
++ * If cb is not NULL, it will be called (with the data argument) to iterate
++ * over a set of requests currently being processed. In this case, if the
++ * packet is a duplicate of an already received request, the original request
++ * will be set in *duppkt.
++ */
++krb5_error_code
++krad_packet_decode_request(krb5_context ctx, const char *secret,
++ const krb5_data *buffer, krad_packet_iter_cb cb,
++ void *data, const krad_packet **duppkt,
++ krad_packet **reqpkt);
++
++/*
++ * Decode a response radius packet from krb5_data.
++ *
++ * The resulting decoded packet will be a response packet stored in *rsppkt.
++ *
++ * If cb is NULL, *reqpkt will always be NULL.
++ *
++ * If cb is not NULL, it will be called (with the data argument) to iterate
++ * over a set of requests awaiting responses. In this case, if the response
++ * packet matches one of these requests, the original request will be set in
++ * *reqpkt.
++ */
++krb5_error_code
++krad_packet_decode_response(krb5_context ctx, const char *secret,
++ const krb5_data *buffer, krad_packet_iter_cb cb,
++ void *data, const krad_packet **reqpkt,
++ krad_packet **rsppkt);
++
++/* Encode packet. */
++const krb5_data *
++krad_packet_encode(const krad_packet *pkt);
++
++/* Get the code for the given packet. */
++krad_code
++krad_packet_get_code(const krad_packet *pkt);
++
++/* Get the specified attribute. */
++const krb5_data *
++krad_packet_get_attr(const krad_packet *pkt, krad_attr type, size_t indx);
++
++/*
++ * Client
++ */
++
++/* Create a new client. */
++krb5_error_code
++krad_client_new(krb5_context kctx, verto_ctx *vctx, krad_client **client);
++
++/* Free the client. */
++void
++krad_client_free(krad_client *client);
++
++/*
++ * Send a request to a radius server.
++ *
++ * The remote host may be specified by one of the following formats:
++ * - /path/to/unix.socket
++ * - IPv4
++ * - IPv4:port
++ * - IPv4:service
++ * - [IPv6]
++ * - [IPv6]:port
++ * - [IPv6]:service
++ * - hostname
++ * - hostname:port
++ * - hostname:service
++ *
++ * The timeout parameter (milliseconds) is the total timeout across all remote
++ * hosts (when DNS returns multiple entries) and all retries.
++ *
++ * The cb function will be called with the data argument when either a response
++ * is received or the request times out on all possible remote hosts.
++ *
++ * If the remote host is a unix domain socket, retries is ignored due to
++ * guaranteed delivery.
++ */
++krb5_error_code
++krad_client_send(krad_client *rc, krad_code code, const krad_attrset *attrs,
++ const char *remote, const char *secret, time_t timeout,
++ size_t retries, krad_cb cb, void *data);
++
++#endif /* KRAD_H_ */
+diff --git a/src/lib/Makefile.in b/src/lib/Makefile.in
+index 485db40..4dde514 100644
+--- a/src/lib/Makefile.in
++++ b/src/lib/Makefile.in
+@@ -1,5 +1,5 @@
+ mydir=lib
+-SUBDIRS=crypto krb5 gssapi rpc kdb kadm5 apputils
++SUBDIRS=crypto krb5 gssapi rpc kdb kadm5 apputils krad
+ WINSUBDIRS=crypto krb5 gssapi
+ BUILDTOP=$(REL)..
+
+diff --git a/src/lib/krad/Makefile.in b/src/lib/krad/Makefile.in
+new file mode 100644
+index 0000000..75431a0
+--- /dev/null
++++ b/src/lib/krad/Makefile.in
+@@ -0,0 +1,74 @@
++mydir=lib$(S)krad
++BUILDTOP=$(REL)..$(S)..
++RELDIR=krad
++
++RUN_SETUP=@KRB5_RUN_ENV@
++PROG_LIBPATH=-L$(TOPLIBD)
++PROG_RPATH=$(KRB5_LIBDIR)
++
++SHLIB_EXPLIBS=$(KRB5_BASE_LIBS) $(VERTO_LIBS)
++SHLIB_EXPDEPLIBS=$(KRB5_BASE_DEPLIBS) $(VERTO_DEPLIB)
++SHLIB_DIRS=-L$(TOPLIBD)
++SHLIB_RDIRS=$(KRB5_LIBDIR)
++
++LIBBASE=krad
++LIBMAJOR=0
++LIBMINOR=0
++
++STLIBOBJS=attr.o attrset.o client.o code.o packet.o remote.o
++LIBOBJS=$(OUTPRE)attr.$(OBJEXT) \
++ $(OUTPRE)attrset.$(OBJEXT) \
++ $(OUTPRE)client.$(OBJEXT) \
++ $(OUTPRE)code.$(OBJEXT) \
++ $(OUTPRE)packet.$(OBJEXT) \
++ $(OUTPRE)remote.$(OBJEXT)
++SRCS=attr.c attrset.c client.c code.c packet.c remote.c \
++ t_attr.c t_attrset.c t_client.c t_code.c t_packet.c t_remote.c t_test.c
++
++STOBJLISTS=OBJS.ST
++
++all-unix:: all-liblinks
++install-unix:: install-libs
++
++clean-unix:: clean-liblinks clean-libs clean-libobjs
++
++check-unix:: t_attr t_attrset t_code t_packet t_remote t_client
++ $(RUN_SETUP) $(VALGRIND) ./t_attr
++ $(RUN_SETUP) $(VALGRIND) ./t_attrset
++ $(RUN_SETUP) $(VALGRIND) ./t_code
++ $(RUN_SETUP) $(VALGRIND) ./t_packet $(PYTHON) $(srcdir)/t_daemon.py
++ $(RUN_SETUP) $(VALGRIND) ./t_remote $(PYTHON) $(srcdir)/t_daemon.py
++ $(RUN_SETUP) $(VALGRIND) ./t_client $(PYTHON) $(srcdir)/t_daemon.py
++
++TESTDEPS=t_test.o $(KRB5_BASE_DEPLIBS)
++TESTLIBS=t_test.o $(KRB5_BASE_LIBS)
++
++T_ATTR_OBJS=attr.o t_attr.o
++t_attr: $(T_ATTR_OBJS) $(TESTDEPS)
++ $(CC_LINK) -o $@ $(T_ATTR_OBJS) $(TESTLIBS)
++
++T_ATTRSET_OBJS=attr.o attrset.o t_attrset.o
++t_attrset: $(T_ATTRSET_OBJS) $(TESTDEPS)
++ $(CC_LINK) -o $@ $(T_ATTRSET_OBJS) $(TESTLIBS)
++
++T_CODE_OBJS=code.o t_code.o
++t_code: $(T_CODE_OBJS) $(TESTDEPS)
++ $(CC_LINK) -o $@ $(T_CODE_OBJS) $(TESTLIBS)
++
++T_PACKET_OBJS=attr.o attrset.o code.o packet.o t_packet.o
++t_packet: $(T_PACKET_OBJS) $(TESTDEPS)
++ $(CC_LINK) -o $@ $(T_PACKET_OBJS) $(TESTLIBS)
++
++T_REMOTE_OBJS=attr.o attrset.o code.o packet.o remote.o t_remote.o
++t_remote: $(T_REMOTE_OBJS) $(TESTDEPS) $(VERTO_DEPLIB)
++ $(CC_LINK) -o $@ $(T_REMOTE_OBJS) $(TESTLIBS) $(VERTO_LIBS)
++
++T_CLIENT_OBJS=attr.o attrset.o code.o packet.o remote.o client.o t_client.o
++t_client: $(T_CLIENT_OBJS) $(TESTDEPS) $(VERTO_DEPLIB)
++ $(CC_LINK) -o $@ $(T_CLIENT_OBJS) $(TESTLIBS) $(VERTO_LIBS)
++
++clean-unix:: clean-libobjs
++ $(RM) *.o t_attr t_attrset t_code t_packet t_remote t_client
++
++@lib_frag@
++@libobj_frag@
+diff --git a/src/lib/krad/attr.c b/src/lib/krad/attr.c
+new file mode 100644
+index 0000000..9c13d9d
+--- /dev/null
++++ b/src/lib/krad/attr.c
+@@ -0,0 +1,317 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/attr.c - RADIUS attribute functions for libkrad */
++/*
++ * Copyright 2013 Red Hat, Inc. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in
++ * the documentation and/or other materials provided with the
++ * distribution.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
++ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
++ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include <k5-int.h>
++#include "internal.h"
++
++#include <string.h>
++
++/* RFC 2865 */
++#define BLOCKSIZE 16
++
++typedef krb5_error_code
++(*attribute_transform_fn)(krb5_context ctx, const char *secret,
++ const unsigned char *auth, const krb5_data *in,
++ unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
++
++typedef struct {
++ const char *name;
++ unsigned char minval;
++ unsigned char maxval;
++ attribute_transform_fn encode;
++ attribute_transform_fn decode;
++} attribute_record;
++
++static krb5_error_code
++user_password_encode(krb5_context ctx, const char *secret,
++ const unsigned char *auth, const krb5_data *in,
++ unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
++
++static krb5_error_code
++user_password_decode(krb5_context ctx, const char *secret,
++ const unsigned char *auth, const krb5_data *in,
++ unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
++
++static const attribute_record attributes[UCHAR_MAX] = {
++ {"User-Name", 1, MAX_ATTRSIZE, NULL, NULL},
++ {"User-Password", 1, 128, user_password_encode, user_password_decode},
++ {"CHAP-Password", 17, 17, NULL, NULL},
++ {"NAS-IP-Address", 4, 4, NULL, NULL},
++ {"NAS-Port", 4, 4, NULL, NULL},
++ {"Service-Type", 4, 4, NULL, NULL},
++ {"Framed-Protocol", 4, 4, NULL, NULL},
++ {"Framed-IP-Address", 4, 4, NULL, NULL},
++ {"Framed-IP-Netmask", 4, 4, NULL, NULL},
++ {"Framed-Routing", 4, 4, NULL, NULL},
++ {"Filter-Id", 1, MAX_ATTRSIZE, NULL, NULL},
++ {"Framed-MTU", 4, 4, NULL, NULL},
++ {"Framed-Compression", 4, 4, NULL, NULL},
++ {"Login-IP-Host", 4, 4, NULL, NULL},
++ {"Login-Service", 4, 4, NULL, NULL},
++ {"Login-TCP-Port", 4, 4, NULL, NULL},
++ {NULL, 0, 0, NULL, NULL}, /* Unassigned */
++ {"Reply-Message", 1, MAX_ATTRSIZE, NULL, NULL},
++ {"Callback-Number", 1, MAX_ATTRSIZE, NULL, NULL},
++ {"Callback-Id", 1, MAX_ATTRSIZE, NULL, NULL},
++ {NULL, 0, 0, NULL, NULL}, /* Unassigned */
++ {"Framed-Route", 1, MAX_ATTRSIZE, NULL, NULL},
++ {"Framed-IPX-Network", 4, 4, NULL, NULL},
++ {"State", 1, MAX_ATTRSIZE, NULL, NULL},
++ {"Class", 1, MAX_ATTRSIZE, NULL, NULL},
++ {"Vendor-Specific", 5, MAX_ATTRSIZE, NULL, NULL},
++ {"Session-Timeout", 4, 4, NULL, NULL},
++ {"Idle-Timeout", 4, 4, NULL, NULL},
++ {"Termination-Action", 4, 4, NULL, NULL},
++ {"Called-Station-Id", 1, MAX_ATTRSIZE, NULL, NULL},
++ {"Calling-Station-Id", 1, MAX_ATTRSIZE, NULL, NULL},
++ {"NAS-Identifier", 1, MAX_ATTRSIZE, NULL, NULL},
++ {"Proxy-State", 1, MAX_ATTRSIZE, NULL, NULL},
++ {"Login-LAT-Service", 1, MAX_ATTRSIZE, NULL, NULL},
++ {"Login-LAT-Node", 1, MAX_ATTRSIZE, NULL, NULL},
++ {"Login-LAT-Group", 32, 32, NULL, NULL},
++ {"Framed-AppleTalk-Link", 4, 4, NULL, NULL},
++ {"Framed-AppleTalk-Network", 4, 4, NULL, NULL},
++ {"Framed-AppleTalk-Zone", 1, MAX_ATTRSIZE, NULL, NULL},
++ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
++ {"CHAP-Challenge", 5, MAX_ATTRSIZE, NULL, NULL},
++ {"NAS-Port-Type", 4, 4, NULL, NULL},
++ {"Port-Limit", 4, 4, NULL, NULL},
++ {"Login-LAT-Port", 1, MAX_ATTRSIZE, NULL, NULL},
++};
++
++/* Encode User-Password attribute. */
++static krb5_error_code
++user_password_encode(krb5_context ctx, const char *secret,
++ const unsigned char *auth, const krb5_data *in,
++ unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen)
++{
++ const unsigned char *indx;
++ krb5_error_code retval;
++ unsigned int seclen;
++ krb5_checksum sum;
++ size_t blck, len, i;
++ krb5_data tmp;
++
++ /* Copy the input buffer to the (zero-padded) output buffer. */
++ len = (in->length + BLOCKSIZE - 1) / BLOCKSIZE * BLOCKSIZE;
++ if (len > MAX_ATTRSIZE)
++ return ENOBUFS;
++ memset(outbuf, 0, len);
++ memcpy(outbuf, in->data, in->length);
++
++ /* Create our temporary space for processing each block. */
++ seclen = strlen(secret);
++ retval = alloc_data(&tmp, seclen + BLOCKSIZE);
++ if (retval != 0)
++ return retval;
++
++ memcpy(tmp.data, secret, seclen);
++ for (blck = 0, indx = auth; blck * BLOCKSIZE < len; blck++) {
++ memcpy(tmp.data + seclen, indx, BLOCKSIZE);
++
++ retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &tmp,
++ &sum);
++ if (retval != 0) {
++ zap(tmp.data, tmp.length);
++ zap(outbuf, len);
++ krb5_free_data_contents(ctx, &tmp);
++ return retval;
++ }
++
++ for (i = 0; i < BLOCKSIZE; i++)
++ outbuf[blck * BLOCKSIZE + i] ^= sum.contents[i];
++ krb5_free_checksum_contents(ctx, &sum);
++
++ indx = &outbuf[blck * BLOCKSIZE];
++ }
++
++ zap(tmp.data, tmp.length);
++ krb5_free_data_contents(ctx, &tmp);
++ *outlen = len;
++ return 0;
++}
++
++/* Decode User-Password attribute. */
++static krb5_error_code
++user_password_decode(krb5_context ctx, const char *secret,
++ const unsigned char *auth, const krb5_data *in,
++ unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen)
++{
++ const unsigned char *indx;
++ krb5_error_code retval;
++ unsigned int seclen;
++ krb5_checksum sum;
++ ssize_t blck, i;
++ krb5_data tmp;
++
++ if (in->length % BLOCKSIZE != 0)
++ return EINVAL;
++ if (in->length > MAX_ATTRSIZE)
++ return ENOBUFS;
++
++ /* Create our temporary space for processing each block. */
++ seclen = strlen(secret);
++ retval = alloc_data(&tmp, seclen + BLOCKSIZE);
++ if (retval != 0)
++ return retval;
++
++ memcpy(tmp.data, secret, seclen);
++ for (blck = 0, indx = auth; blck * BLOCKSIZE < in->length; blck++) {
++ memcpy(tmp.data + seclen, indx, BLOCKSIZE);
++
++ retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0,
++ &tmp, &sum);
++ if (retval != 0) {
++ zap(tmp.data, tmp.length);
++ zap(outbuf, in->length);
++ krb5_free_data_contents(ctx, &tmp);
++ return retval;
++ }
++
++ for (i = 0; i < BLOCKSIZE; i++) {
++ outbuf[blck * BLOCKSIZE + i] = in->data[blck * BLOCKSIZE + i] ^
++ sum.contents[i];
++ }
++ krb5_free_checksum_contents(ctx, &sum);
++
++ indx = (const unsigned char *)&in->data[blck * BLOCKSIZE];
++ }
++
++ /* Strip off trailing NULL bytes. */
++ *outlen = in->length;
++ while (*outlen > 0 && outbuf[*outlen - 1] == '\0')
++ (*outlen)--;
++
++ krb5_free_data_contents(ctx, &tmp);
++ return 0;
++}
++
++krb5_error_code
++kr_attr_valid(krad_attr type, const krb5_data *data)
++{
++ const attribute_record *ar;
++
++ if (type == 0)
++ return EINVAL;
++
++ ar = &attributes[type - 1];
++ return (data->length >= ar->minval && data->length <= ar->maxval) ? 0 :
++ EMSGSIZE;
++}
++
++krb5_error_code
++kr_attr_encode(krb5_context ctx, const char *secret,
++ const unsigned char *auth, krad_attr type,
++ const krb5_data *in, unsigned char outbuf[MAX_ATTRSIZE],
++ size_t *outlen)
++{
++ krb5_error_code retval;
++
++ retval = kr_attr_valid(type, in);
++ if (retval != 0)
++ return retval;
++
++ if (attributes[type - 1].encode == NULL) {
++ if (in->length > MAX_ATTRSIZE)
++ return ENOBUFS;
++
++ *outlen = in->length;
++ memcpy(outbuf, in->data, in->length);
++ return 0;
++ }
++
++ return attributes[type - 1].encode(ctx, secret, auth, in, outbuf, outlen);
++}
++
++krb5_error_code
++kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
++ krad_attr type, const krb5_data *in,
++ unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen)
++{
++ krb5_error_code retval;
++
++ retval = kr_attr_valid(type, in);
++ if (retval != 0)
++ return retval;
++
++ if (attributes[type - 1].encode == NULL) {
++ if (in->length > MAX_ATTRSIZE)
++ return ENOBUFS;
++
++ *outlen = in->length;
++ memcpy(outbuf, in->data, in->length);
++ return 0;
++ }
++
++ return attributes[type - 1].decode(ctx, secret, auth, in, outbuf, outlen);
++}
++
++krad_attr
++krad_attr_name2num(const char *name)
++{
++ unsigned char i;
++
++ for (i = 0; i < UCHAR_MAX; i++) {
++ if (attributes[i].name == NULL)
++ continue;
++
++ if (strcmp(attributes[i].name, name) == 0)
++ return i + 1;
++ }
++
++ return 0;
++}
++
++const char *
++krad_attr_num2name(krad_attr type)
++{
++ if (type == 0)
++ return NULL;
++
++ return attributes[type - 1].name;
++}
+diff --git a/src/lib/krad/attrset.c b/src/lib/krad/attrset.c
+new file mode 100644
+index 0000000..fbd0621
+--- /dev/null
++++ b/src/lib/krad/attrset.c
+@@ -0,0 +1,244 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/attrset.c - RADIUS attribute set functions for libkrad */
++/*
++ * Copyright 2013 Red Hat, Inc. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in
++ * the documentation and/or other materials provided with the
++ * distribution.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
++ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
++ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include <k5-int.h>
++#include <k5-queue.h>
++#include "internal.h"
++
++#include <string.h>
++
++TAILQ_HEAD(attr_head, attr_st);
++
++typedef struct attr_st attr;
++struct attr_st {
++ TAILQ_ENTRY(attr_st) list;
++ krad_attr type;
++ krb5_data attr;
++ char buffer[MAX_ATTRSIZE];
++};
++
++struct krad_attrset_st {
++ krb5_context ctx;
++ struct attr_head list;
++};
++
++krb5_error_code
++krad_attrset_new(krb5_context ctx, krad_attrset **set)
++{
++ krad_attrset *tmp;
++
++ tmp = calloc(1, sizeof(krad_attrset));
++ if (tmp == NULL)
++ return ENOMEM;
++ tmp->ctx = ctx;
++ TAILQ_INIT(&tmp->list);
++
++ *set = tmp;
++ return 0;
++}
++
++void
++krad_attrset_free(krad_attrset *set)
++{
++ attr *a;
++
++ if (set == NULL)
++ return;
++
++ while (!TAILQ_EMPTY(&set->list)) {
++ a = TAILQ_FIRST(&set->list);
++ TAILQ_REMOVE(&set->list, a, list);
++ zap(a->buffer, sizeof(a->buffer));
++ free(a);
++ }
++
++ free(set);
++}
++
++krb5_error_code
++krad_attrset_add(krad_attrset *set, krad_attr type, const krb5_data *data)
++{
++ krb5_error_code retval;
++ attr *tmp;
++
++ retval = kr_attr_valid(type, data);
++ if (retval != 0)
++ return retval;
++
++ tmp = calloc(1, sizeof(attr));
++ if (tmp == NULL)
++ return ENOMEM;
++
++ tmp->type = type;
++ tmp->attr = make_data(tmp->buffer, data->length);
++ memcpy(tmp->attr.data, data->data, data->length);
++
++ TAILQ_INSERT_TAIL(&set->list, tmp, list);
++ return 0;
++}
++
++krb5_error_code
++krad_attrset_add_number(krad_attrset *set, krad_attr type, krb5_ui_4 num)
++{
++ krb5_data data;
++
++ num = htonl(num);
++ data = make_data(&num, sizeof(num));
++ return krad_attrset_add(set, type, &data);
++}
++
++void
++krad_attrset_del(krad_attrset *set, krad_attr type, size_t indx)
++{
++ attr *a;
++
++ TAILQ_FOREACH(a, &set->list, list) {
++ if (a->type == type && indx-- == 0) {
++ TAILQ_REMOVE(&set->list, a, list);
++ zap(a->buffer, sizeof(a->buffer));
++ free(a);
++ return;
++ }
++ }
++}
++
++const krb5_data *
++krad_attrset_get(const krad_attrset *set, krad_attr type, size_t indx)
++{
++ attr *a;
++
++ TAILQ_FOREACH(a, &set->list, list) {
++ if (a->type == type && indx-- == 0)
++ return &a->attr;
++ }
++
++ return NULL;
++}
++
++krb5_error_code
++krad_attrset_copy(const krad_attrset *set, krad_attrset **copy)
++{
++ krb5_error_code retval;
++ krad_attrset *tmp;
++ attr *a;
++
++ retval = krad_attrset_new(set->ctx, &tmp);
++ if (retval != 0)
++ return retval;
++
++ TAILQ_FOREACH(a, &set->list, list) {
++ retval = krad_attrset_add(tmp, a->type, &a->attr);
++ if (retval != 0) {
++ krad_attrset_free(tmp);
++ return retval;
++ }
++ }
++
++ *copy = tmp;
++ return 0;
++}
++
++krb5_error_code
++kr_attrset_encode(const krad_attrset *set, const char *secret,
++ const unsigned char *auth,
++ unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen)
++{
++ unsigned char buffer[MAX_ATTRSIZE];
++ krb5_error_code retval;
++ size_t i = 0, attrlen;
++ attr *a;
++
++ if (set == NULL) {
++ *outlen = 0;
++ return 0;
++ }
++
++ TAILQ_FOREACH(a, &set->list, list) {
++ retval = kr_attr_encode(set->ctx, secret, auth, a->type, &a->attr,
++ buffer, &attrlen);
++ if (retval != 0)
++ return retval;
++
++ if (i + attrlen + 2 > MAX_ATTRSETSIZE)
++ return EMSGSIZE;
++
++ outbuf[i++] = a->type;
++ outbuf[i++] = attrlen + 2;
++ memcpy(&outbuf[i], buffer, attrlen);
++ i += attrlen;
++ }
++
++ *outlen = i;
++ return 0;
++}
++
++krb5_error_code
++kr_attrset_decode(krb5_context ctx, const krb5_data *in, const char *secret,
++ const unsigned char *auth, krad_attrset **set_out)
++{
++ unsigned char buffer[MAX_ATTRSIZE];
++ krb5_data tmp;
++ krb5_error_code retval;
++ krad_attr type;
++ krad_attrset *set;
++ size_t i, len;
++
++ *set_out = NULL;
++
++ retval = krad_attrset_new(ctx, &set);
++ if (retval != 0)
++ return retval;
++
++ for (i = 0; i + 2 < in->length; ) {
++ type = in->data[i++];
++ tmp = make_data(&in->data[i + 1], in->data[i] - 2);
++ i += tmp.length + 1;
++
++ retval = (in->length < i) ? EBADMSG : 0;
++ if (retval != 0)
++ goto cleanup;
++
++ retval = kr_attr_decode(ctx, secret, auth, type, &tmp, buffer, &len);
++ if (retval != 0)
++ goto cleanup;
++
++ tmp = make_data(buffer, len);
++ retval = krad_attrset_add(set, type, &tmp);
++ if (retval != 0)
++ goto cleanup;
++ }
++
++ *set_out = set;
++ set = NULL;
++
++cleanup:
++ zap(buffer, sizeof(buffer));
++ krad_attrset_free(set);
++ return retval;
++}
+diff --git a/src/lib/krad/client.c b/src/lib/krad/client.c
+new file mode 100644
+index 0000000..a3cf7bd
+--- /dev/null
++++ b/src/lib/krad/client.c
+@@ -0,0 +1,335 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/client.c - Client request code for libkrad */
++/*
++ * Copyright 2013 Red Hat, Inc. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in
++ * the documentation and/or other materials provided with the
++ * distribution.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
++ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
++ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include <k5-queue.h>
++#include "internal.h"
++
++#include <string.h>
++#include <sys/un.h>
++#include <unistd.h>
++#include <stdio.h>
++#include <limits.h>
++
++LIST_HEAD(server_head, server_st);
++
++typedef struct remote_state_st remote_state;
++typedef struct request_st request;
++typedef struct server_st server;
++
++struct remote_state_st {
++ const krad_packet *packet;
++ krad_remote *remote;
++};
++
++struct request_st {
++ krad_client *rc;
++
++ krad_code code;
++ krad_attrset *attrs;
++ time_t timeout;
++ size_t retries;
++ krad_cb cb;
++ void *data;
++
++ remote_state *remotes;
++ ssize_t current;
++ ssize_t count;
++};
++
++struct server_st {
++ krad_remote *serv;
++ time_t last;
++ LIST_ENTRY(server_st) list;
++};
++
++struct krad_client_st {
++ krb5_context kctx;
++ verto_ctx *vctx;
++ struct server_head servers;
++};
++
++/* Return either a pre-existing server that matches the address info and the
++ * secret, or create a new one. */
++static krb5_error_code
++get_server(krad_client *rc, const struct addrinfo *ai, const char *secret,
++ krad_remote **out)
++{
++ krb5_error_code retval;
++ time_t currtime;
++ server *srv;
++
++ if (time(&currtime) == (time_t) -1)
++ return errno;
++
++ LIST_FOREACH(srv, &rc->servers, list) {
++ if (kr_remote_equals(srv->serv, ai, secret)) {
++ srv->last = currtime;
++ *out = srv->serv;
++ return 0;
++ }
++ }
++
++ srv = calloc(1, sizeof(server));
++ if (srv == NULL)
++ return ENOMEM;
++ srv->last = currtime;
++
++ retval = kr_remote_new(rc->kctx, rc->vctx, ai, secret, &srv->serv);
++ if (retval != 0) {
++ free(srv);
++ return retval;
++ }
++
++ LIST_INSERT_HEAD(&rc->servers, srv, list);
++ *out = srv->serv;
++ return 0;
++}
++
++/* Free a request. */
++static void
++request_free(request *req)
++{
++ krad_attrset_free(req->attrs);
++ free(req->remotes);
++ free(req);
++}
++
++/* Create a request. */
++static krb5_error_code
++request_new(krad_client *rc, krad_code code, const krad_attrset *attrs,
++ const struct addrinfo *ai, const char *secret, time_t timeout,
++ size_t retries, krad_cb cb, void *data, request **req)
++{
++ const struct addrinfo *tmp;
++ krb5_error_code retval;
++ request *rqst;
++ size_t i;
++
++ if (ai == NULL)
++ return EINVAL;
++
++ rqst = calloc(1, sizeof(request));
++ if (rqst == NULL)
++ return ENOMEM;
++
++ for (tmp = ai; tmp != NULL; tmp = tmp->ai_next)
++ rqst->count++;
++
++ rqst->rc = rc;
++ rqst->code = code;
++ rqst->cb = cb;
++ rqst->data = data;
++ rqst->timeout = timeout / rqst->count;
++ rqst->retries = retries;
++
++ retval = krad_attrset_copy(attrs, &rqst->attrs);
++ if (retval != 0) {
++ request_free(rqst);
++ return retval;
++ }
++
++ rqst->remotes = calloc(rqst->count + 1, sizeof(remote_state));
++ if (rqst->remotes == NULL) {
++ request_free(rqst);
++ return ENOMEM;
++ }
++
++ i = 0;
++ for (tmp = ai; tmp != NULL; tmp = tmp->ai_next) {
++ retval = get_server(rc, tmp, secret, &rqst->remotes[i++].remote);
++ if (retval != 0) {
++ request_free(rqst);
++ return retval;
++ }
++ }
++
++ *req = rqst;
++ return 0;
++}
++
++/* Close remotes that haven't been used in a while. */
++static void
++age(struct server_head *head, time_t currtime)
++{
++ server *srv, *tmp;
++
++ LIST_FOREACH_SAFE(srv, head, list, tmp) {
++ if (currtime == (time_t)-1 || currtime - srv->last > 60 * 60) {
++ LIST_REMOVE(srv, list);
++ kr_remote_free(srv->serv);
++ free(srv);
++ }
++ }
++}
++
++/* Handle a response from a server (or related errors). */
++static void
++on_response(krb5_error_code retval, const krad_packet *reqp,
++ const krad_packet *rspp, void *data)
++{
++ request *req = data;
++ time_t currtime;
++ size_t i;
++
++ /* Do nothing if we are already completed. */
++ if (req->count < 0)
++ return;
++
++ /* If we have timed out and have more remotes to try, do so. */
++ if (retval == ETIMEDOUT && req->remotes[++req->current].remote != NULL) {
++ retval = kr_remote_send(req->remotes[req->current].remote, req->code,
++ req->attrs, on_response, req, req->timeout,
++ req->retries,
++ &req->remotes[req->current].packet);
++ if (retval == 0)
++ return;
++ }
++
++ /* Mark the request as complete. */
++ req->count = -1;
++
++ /* Inform the callback. */
++ req->cb(retval, reqp, rspp, req->data);
++
++ /* Cancel the outstanding packets. */
++ for (i = 0; req->remotes[i].remote != NULL; i++)
++ kr_remote_cancel(req->remotes[i].remote, req->remotes[i].packet);
++
++ /* Age out servers that haven't been used in a while. */
++ if (time(&currtime) != (time_t)-1)
++ age(&req->rc->servers, currtime);
++
++ request_free(req);
++}
++
++krb5_error_code
++krad_client_new(krb5_context kctx, verto_ctx *vctx, krad_client **out)
++{
++ krad_client *tmp;
++
++ tmp = calloc(1, sizeof(krad_client));
++ if (tmp == NULL)
++ return ENOMEM;
++
++ tmp->kctx = kctx;
++ tmp->vctx = vctx;
++
++ *out = tmp;
++ return 0;
++}
++
++void
++krad_client_free(krad_client *rc)
++{
++ if (rc == NULL)
++ return;
++
++ age(&rc->servers, -1);
++ free(rc);
++}
++
++static krb5_error_code
++resolve_remote(const char *remote, struct addrinfo **ai)
++{
++ const char *svc = "radius";
++ krb5_error_code retval;
++ struct addrinfo hints;
++ char *sep, *srv;
++
++ /* Isolate the port number if it exists. */
++ srv = strdup(remote);
++ if (srv == NULL)
++ return ENOMEM;
++
++ if (srv[0] == '[') {
++ /* IPv6 */
++ sep = strrchr(srv, ']');
++ if (sep != NULL && sep[1] == ':') {
++ sep[1] = '\0';
++ svc = &sep[2];
++ }
++ } else {
++ /* IPv4 or DNS */
++ sep = strrchr(srv, ':');
++ if (sep != NULL && sep[1] != '\0') {
++ sep[0] = '\0';
++ svc = &sep[1];
++ }
++ }
++
++ /* Perform the lookup. */
++ memset(&hints, 0, sizeof(hints));
++ hints.ai_socktype = SOCK_DGRAM;
++ retval = gai_error_code(getaddrinfo(srv, svc, &hints, ai));
++ free(srv);
++ return retval;
++}
++
++krb5_error_code
++krad_client_send(krad_client *rc, krad_code code, const krad_attrset *attrs,
++ const char *remote, const char *secret, time_t timeout,
++ size_t retries, krad_cb cb, void *data)
++{
++ struct addrinfo usock, *ai = NULL;
++ krb5_error_code retval;
++ struct sockaddr_un ua;
++ request *req;
++
++ if (remote[0] == '/') {
++ ua.sun_family = AF_UNIX;
++ snprintf(ua.sun_path, sizeof(ua.sun_path), "%s", remote);
++ memset(&usock, 0, sizeof(usock));
++ usock.ai_family = AF_UNIX;
++ usock.ai_socktype = SOCK_STREAM;
++ usock.ai_addr = (struct sockaddr *)&ua;
++ usock.ai_addrlen = sizeof(ua);
++
++ retval = request_new(rc, code, attrs, &usock, secret, timeout, retries,
++ cb, data, &req);
++ } else {
++ retval = resolve_remote(remote, &ai);
++ if (retval == 0) {
++ retval = request_new(rc, code, attrs, ai, secret, timeout, retries,
++ cb, data, &req);
++ freeaddrinfo(ai);
++ }
++ }
++ if (retval != 0)
++ return retval;
++
++ retval = kr_remote_send(req->remotes[req->current].remote, req->code,
++ req->attrs, on_response, req, req->timeout,
++ req->retries, &req->remotes[req->current].packet);
++ if (retval != 0) {
++ request_free(req);
++ return retval;
++ }
++
++ return 0;
++}
+diff --git a/src/lib/krad/code.c b/src/lib/krad/code.c
+new file mode 100644
+index 0000000..16871bb
+--- /dev/null
++++ b/src/lib/krad/code.c
+@@ -0,0 +1,111 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/code.c - RADIUS code name table for libkrad */
++/*
++ * Copyright 2013 Red Hat, Inc. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in
++ * the documentation and/or other materials provided with the
++ * distribution.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
++ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
++ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include "internal.h"
++
++#include <string.h>
++
++static const char *codes[UCHAR_MAX] = {
++ "Access-Request",
++ "Access-Accept",
++ "Access-Reject",
++ "Accounting-Request",
++ "Accounting-Response",
++ "Accounting-Status",
++ "Password-Request",
++ "Password-Ack",
++ "Password-Reject",
++ "Accounting-Message",
++ "Access-Challenge",
++ "Status-Server",
++ "Status-Client",
++ NULL,
++ NULL,
++ NULL,
++ NULL,
++ NULL,
++ NULL,
++ NULL,
++ "Resource-Free-Request",
++ "Resource-Free-Response",
++ "Resource-Query-Request",
++ "Resource-Query-Response",
++ "Alternate-Resource-Reclaim-Request",
++ "NAS-Reboot-Request",
++ "NAS-Reboot-Response",
++ NULL,
++ "Next-Passcode",
++ "New-Pin",
++ "Terminate-Session",
++ "Password-Expired",
++ "Event-Request",
++ "Event-Response",
++ NULL,
++ NULL,
++ NULL,
++ NULL,
++ NULL,
++ "Disconnect-Request",
++ "Disconnect-Ack",
++ "Disconnect-Nak",
++ "Change-Filters-Request",
++ "Change-Filters-Ack",
++ "Change-Filters-Nak",
++ NULL,
++ NULL,
++ NULL,
++ NULL,
++ "IP-Address-Allocate",
++ "IP-Address-Release",
++};
++
++krad_code
++krad_code_name2num(const char *name)
++{
++ unsigned char i;
++
++ for (i = 0; i < UCHAR_MAX; i++) {
++ if (codes[i] == NULL)
++ continue;
++
++ if (strcmp(codes[i], name) == 0)
++ return ++i;
++ }
++
++ return 0;
++}
++
++const char *
++krad_code_num2name(krad_code code)
++{
++ if (code == 0)
++ return NULL;
++
++ return codes[code - 1];
++}
+diff --git a/src/lib/krad/deps b/src/lib/krad/deps
+new file mode 100644
+index 0000000..8171f94
+--- /dev/null
++++ b/src/lib/krad/deps
+@@ -0,0 +1,156 @@
++#
++# Generated makefile dependencies follow.
++#
++attr.so attr.po $(OUTPRE)attr.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
++ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
++ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
++ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
++ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
++ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
++ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
++ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krad.h \
++ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
++ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
++ $(top_srcdir)/include/socket-utils.h attr.c internal.h
++attrset.so attrset.po $(OUTPRE)attrset.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
++ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
++ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
++ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
++ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
++ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
++ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-queue.h \
++ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
++ $(top_srcdir)/include/krad.h $(top_srcdir)/include/krb5.h \
++ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
++ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
++ attrset.c internal.h
++client.so client.po $(OUTPRE)client.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
++ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
++ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
++ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
++ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
++ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
++ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-queue.h \
++ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
++ $(top_srcdir)/include/krad.h $(top_srcdir)/include/krb5.h \
++ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
++ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
++ client.c internal.h
++code.so code.po $(OUTPRE)code.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
++ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
++ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
++ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
++ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
++ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
++ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
++ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krad.h \
++ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
++ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
++ $(top_srcdir)/include/socket-utils.h code.c internal.h
++packet.so packet.po $(OUTPRE)packet.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
++ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
++ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
++ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
++ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
++ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
++ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
++ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krad.h \
++ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
++ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
++ $(top_srcdir)/include/socket-utils.h internal.h packet.c
++remote.so remote.po $(OUTPRE)remote.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
++ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
++ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
++ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
++ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
++ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
++ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-queue.h \
++ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
++ $(top_srcdir)/include/krad.h $(top_srcdir)/include/krb5.h \
++ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
++ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
++ internal.h remote.c
++t_attr.so t_attr.po $(OUTPRE)t_attr.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
++ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
++ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
++ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
++ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
++ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
++ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
++ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krad.h \
++ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
++ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
++ $(top_srcdir)/include/socket-utils.h internal.h t_attr.c \
++ t_test.h
++t_attrset.so t_attrset.po $(OUTPRE)t_attrset.$(OBJEXT): \
++ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
++ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
++ $(COM_ERR_DEPS) $(VERTO_DEPS) $(top_srcdir)/include/k5-buf.h \
++ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
++ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
++ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
++ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
++ $(top_srcdir)/include/krad.h $(top_srcdir)/include/krb5.h \
++ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
++ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
++ internal.h t_attrset.c t_test.h
++t_client.so t_client.po $(OUTPRE)t_client.$(OBJEXT): \
++ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
++ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
++ $(COM_ERR_DEPS) $(VERTO_DEPS) $(top_srcdir)/include/k5-buf.h \
++ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
++ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
++ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
++ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
++ $(top_srcdir)/include/krad.h $(top_srcdir)/include/krb5.h \
++ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
++ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
++ internal.h t_client.c t_daemon.h t_test.h
++t_code.so t_code.po $(OUTPRE)t_code.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
++ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
++ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
++ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
++ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
++ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
++ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
++ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krad.h \
++ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
++ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
++ $(top_srcdir)/include/socket-utils.h internal.h t_code.c \
++ t_test.h
++t_packet.so t_packet.po $(OUTPRE)t_packet.$(OBJEXT): \
++ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
++ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
++ $(COM_ERR_DEPS) $(VERTO_DEPS) $(top_srcdir)/include/k5-buf.h \
++ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
++ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
++ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
++ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
++ $(top_srcdir)/include/krad.h $(top_srcdir)/include/krb5.h \
++ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
++ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
++ internal.h t_daemon.h t_packet.c t_test.h
++t_remote.so t_remote.po $(OUTPRE)t_remote.$(OBJEXT): \
++ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
++ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
++ $(COM_ERR_DEPS) $(VERTO_DEPS) $(top_srcdir)/include/k5-buf.h \
++ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
++ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
++ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
++ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
++ $(top_srcdir)/include/krad.h $(top_srcdir)/include/krb5.h \
++ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
++ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
++ internal.h t_daemon.h t_remote.c t_test.h
++t_test.so t_test.po $(OUTPRE)t_test.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
++ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
++ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
++ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
++ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
++ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
++ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
++ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krad.h \
++ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
++ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
++ $(top_srcdir)/include/socket-utils.h internal.h t_test.c \
++ t_test.h
+diff --git a/src/lib/krad/internal.h b/src/lib/krad/internal.h
+new file mode 100644
+index 0000000..49ea682
+--- /dev/null
++++ b/src/lib/krad/internal.h
+@@ -0,0 +1,155 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/internal.h - Internal declarations for libkrad */
++/*
++ * Copyright 2013 Red Hat, Inc. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in
++ * the documentation and/or other materials provided with the
++ * distribution.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
++ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
++ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#ifndef INTERNAL_H_
++#define INTERNAL_H_
++
++#include <k5-int.h>
++#include "krad.h"
++
++#include <errno.h>
++
++#include <sys/types.h>
++#include <sys/socket.h>
++#include <netdb.h>
++
++#ifndef UCHAR_MAX
++#define UCHAR_MAX 255
++#endif
++
++/* RFC 2865 */
++#define MAX_ATTRSIZE (UCHAR_MAX - 2)
++#define MAX_ATTRSETSIZE (KRAD_PACKET_SIZE_MAX - 20)
++
++typedef struct krad_remote_st krad_remote;
++
++/* Validate constraints of an attribute. */
++krb5_error_code
++kr_attr_valid(krad_attr type, const krb5_data *data);
++
++/* Encode an attribute. */
++krb5_error_code
++kr_attr_encode(krb5_context ctx, const char *secret, const unsigned char *auth,
++ krad_attr type, const krb5_data *in,
++ unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
++
++/* Decode an attribute. */
++krb5_error_code
++kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
++ krad_attr type, const krb5_data *in,
++ unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
++
++/* Encode the attributes into the buffer. */
++krb5_error_code
++kr_attrset_encode(const krad_attrset *set, const char *secret,
++ const unsigned char *auth,
++ unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen);
++
++/* Decode attributes from a buffer. */
++krb5_error_code
++kr_attrset_decode(krb5_context ctx, const krb5_data *in, const char *secret,
++ const unsigned char *auth, krad_attrset **set);
++
++/* Create a new remote object which manages a socket and the state of
++ * outstanding requests. */
++krb5_error_code
++kr_remote_new(krb5_context kctx, verto_ctx *vctx, const struct addrinfo *info,
++ const char *secret, krad_remote **rr);
++
++/* Free a remote object. */
++void
++kr_remote_free(krad_remote *rr);
++
++/*
++ * Send the packet to the remote. The cb will be called when a response is
++ * received, the request times out, the request is canceled or an error occurs.
++ *
++ * The timeout parameter is the total timeout across all retries in
++ * milliseconds.
++ *
++ * If the cb is called with a retval of ETIMEDOUT it indicates that the alloted
++ * time has elapsed. However, in the case of a timeout, we continue to listen
++ * for the packet until krad_remote_cancel() is called or a response is
++ * received. This means that cb will always be called twice in the event of a
++ * timeout. This permits you to pursue other remotes while still listening for
++ * a response from the first one.
++ */
++krb5_error_code
++kr_remote_send(krad_remote *rr, krad_code code, krad_attrset *attrs,
++ krad_cb cb, void *data, time_t timeout, size_t retries,
++ const krad_packet **pkt);
++
++/* Remove packet from the queue of requests awaiting responses. */
++void
++kr_remote_cancel(krad_remote *rr, const krad_packet *pkt);
++
++/* Determine if this remote object refers to the remote resource identified
++ * by the addrinfo struct and the secret. */
++krb5_boolean
++kr_remote_equals(const krad_remote *rr, const struct addrinfo *info,
++ const char *secret);
++
++/* Adapted from lib/krb5/os/sendto_kdc.c. */
++static inline krb5_error_code
++gai_error_code(int err)
++{
++ switch (err) {
++ case 0:
++ return 0;
++ case EAI_BADFLAGS:
++ case EAI_FAMILY:
++ case EAI_SOCKTYPE:
++ case EAI_SERVICE:
++#ifdef EAI_ADDRFAMILY
++ case EAI_ADDRFAMILY:
++#endif
++ return EINVAL;
++ case EAI_AGAIN:
++ return EAGAIN;
++ case EAI_MEMORY:
++ return ENOMEM;
++#if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME
++ case EAI_NODATA:
++#endif
++ case EAI_NONAME:
++ return EADDRNOTAVAIL;
++#ifdef EAI_OVERFLOW
++ case EAI_OVERFLOW:
++ return EOVERFLOW;
++#endif
++#ifdef EAI_SYSTEM
++ case EAI_SYSTEM:
++ return errno;
++#endif
++ default:
++ return EINVAL;
++ }
++}
++
++#endif /* INTERNAL_H_ */
+diff --git a/src/lib/krad/libkrad.exports b/src/lib/krad/libkrad.exports
+new file mode 100644
+index 0000000..fe3f159
+--- /dev/null
++++ b/src/lib/krad/libkrad.exports
+@@ -0,0 +1,23 @@
++krad_code_name2num
++krad_code_num2name
++krad_attr_name2num
++krad_attr_num2name
++krad_attrset_new
++krad_attrset_copy
++krad_attrset_free
++krad_attrset_add
++krad_attrset_add_number
++krad_attrset_del
++krad_attrset_get
++krad_packet_bytes_needed
++krad_packet_free
++krad_packet_new_request
++krad_packet_new_response
++krad_packet_decode_request
++krad_packet_decode_response
++krad_packet_encode
++krad_packet_get_code
++krad_packet_get_attr
++krad_client_new
++krad_client_free
++krad_client_send
+diff --git a/src/lib/krad/packet.c b/src/lib/krad/packet.c
+new file mode 100644
+index 0000000..63d5ec8
+--- /dev/null
++++ b/src/lib/krad/packet.c
+@@ -0,0 +1,475 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/packet.c - Packet functions for libkrad */
++/*
++ * Copyright 2013 Red Hat, Inc. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in
++ * the documentation and/or other materials provided with the
++ * distribution.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
++ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
++ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include "internal.h"
++
++#include <string.h>
++
++#include <arpa/inet.h>
++
++typedef unsigned char uchar;
++
++/* RFC 2865 */
++#define OFFSET_CODE 0
++#define OFFSET_ID 1
++#define OFFSET_LENGTH 2
++#define OFFSET_AUTH 4
++#define OFFSET_ATTR 20
++#define AUTH_FIELD_SIZE (OFFSET_ATTR - OFFSET_AUTH)
++
++#define offset(d, o) (&(d)->data[o])
++#define pkt_code_get(p) (*(krad_code *)offset(&(p)->pkt, OFFSET_CODE))
++#define pkt_code_set(p, v) (*(krad_code *)offset(&(p)->pkt, OFFSET_CODE)) = v
++#define pkt_id_get(p) (*(uchar *)offset(&(p)->pkt, OFFSET_ID))
++#define pkt_id_set(p, v) (*(uchar *)offset(&(p)->pkt, OFFSET_ID)) = v
++#define pkt_len_get(p) load_16_be(offset(&(p)->pkt, OFFSET_LENGTH))
++#define pkt_len_set(p, v) store_16_be(v, offset(&(p)->pkt, OFFSET_LENGTH))
++#define pkt_auth(p) ((uchar *)offset(&(p)->pkt, OFFSET_AUTH))
++#define pkt_attr(p) ((unsigned char *)offset(&(p)->pkt, OFFSET_ATTR))
++
++struct krad_packet_st {
++ char buffer[KRAD_PACKET_SIZE_MAX];
++ krad_attrset *attrset;
++ krb5_data pkt;
++};
++
++typedef struct {
++ uchar x[(UCHAR_MAX + 1) / 8];
++} idmap;
++
++/* Ensure the map is empty. */
++static inline void
++idmap_init(idmap *map)
++{
++ memset(map, 0, sizeof(*map));
++}
++
++/* Set an id as already allocated. */
++static inline void
++idmap_set(idmap *map, uchar id)
++{
++ map->x[id / 8] |= 1 << (id % 8);
++}
++
++/* Determine whether or not an id is used. */
++static inline krb5_boolean
++idmap_isset(const idmap *map, uchar id)
++{
++ return (map->x[id / 8] & (1 << (id % 8))) != 0;
++}
++
++/* Find an unused id starting the search at the value specified in id.
++ * NOTE: For optimal security, the initial value of id should be random. */
++static inline krb5_error_code
++idmap_find(const idmap *map, uchar *id)
++{
++ krb5_int16 i;
++
++ for (i = *id; i >= 0 && i <= UCHAR_MAX; *id % 2 == 0 ? i++ : i--) {
++ if (!idmap_isset(map, i))
++ goto success;
++ }
++
++ for (i = *id; i >= 0 && i <= UCHAR_MAX; *id % 2 == 1 ? i++ : i--) {
++ if (!idmap_isset(map, i))
++ goto success;
++ }
++
++ return ERANGE;
++
++success:
++ *id = i;
++ return 0;
++}
++
++/* Generate size bytes of random data into the buffer. */
++static inline krb5_error_code
++randomize(krb5_context ctx, void *buffer, unsigned int size)
++{
++ krb5_data rdata = make_data(buffer, size);
++ return krb5_c_random_make_octets(ctx, &rdata);
++}
++
++/* Generate a radius packet id. */
++static krb5_error_code
++id_generate(krb5_context ctx, krad_packet_iter_cb cb, void *data, uchar *id)
++{
++ krb5_error_code retval;
++ const krad_packet *tmp;
++ idmap used;
++ uchar i;
++
++ retval = randomize(ctx, &i, sizeof(i));
++ if (retval != 0) {
++ if (cb != NULL)
++ (*cb)(data, TRUE);
++ return retval;
++ }
++
++ if (cb != NULL) {
++ idmap_init(&used);
++ for (tmp = (*cb)(data, FALSE); tmp != NULL; tmp = (*cb)(data, FALSE))
++ idmap_set(&used, tmp->pkt.data[1]);
++
++ retval = idmap_find(&used, &i);
++ if (retval != 0)
++ return retval;
++ }
++
++ *id = i;
++ return 0;
++}
++
++/* Generate a random authenticator field. */
++static krb5_error_code
++auth_generate_random(krb5_context ctx, uchar *rauth)
++{
++ krb5_ui_4 trunctime;
++ time_t currtime;
++
++ /* Get the least-significant four bytes of the current time. */
++ currtime = time(NULL);
++ if (currtime == (time_t)-1)
++ return errno;
++ trunctime = (krb5_ui_4)currtime;
++ memcpy(rauth, &trunctime, sizeof(trunctime));
++
++ /* Randomize the rest of the buffer. */
++ return randomize(ctx, rauth + sizeof(trunctime),
++ AUTH_FIELD_SIZE - sizeof(trunctime));
++}
++
++/* Generate a response authenticator field. */
++static krb5_error_code
++auth_generate_response(krb5_context ctx, const char *secret,
++ const krad_packet *response, const uchar *auth,
++ uchar *rauth)
++{
++ krb5_error_code retval;
++ krb5_checksum hash;
++ krb5_data data;
++
++ /* Allocate the temporary buffer. */
++ retval = alloc_data(&data, response->pkt.length + strlen(secret));
++ if (retval != 0)
++ return retval;
++
++ /* Encoded RADIUS packet with the request's
++ * authenticator and the secret at the end. */
++ memcpy(data.data, response->pkt.data, response->pkt.length);
++ memcpy(data.data + OFFSET_AUTH, auth, AUTH_FIELD_SIZE);
++ memcpy(data.data + response->pkt.length, secret, strlen(secret));
++
++ /* Hash it. */
++ retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &data,
++ &hash);
++ free(data.data);
++ if (retval != 0)
++ return retval;
++
++ memcpy(rauth, hash.contents, AUTH_FIELD_SIZE);
++ krb5_free_checksum_contents(ctx, &hash);
++ return 0;
++}
++
++/* Create a new packet. */
++static krad_packet *
++packet_new()
++{
++ krad_packet *pkt;
++
++ pkt = calloc(1, sizeof(krad_packet));
++ if (pkt == NULL)
++ return NULL;
++ pkt->pkt = make_data(pkt->buffer, sizeof(pkt->buffer));
++
++ return pkt;
++}
++
++/* Set the attrset object by decoding the packet. */
++static krb5_error_code
++packet_set_attrset(krb5_context ctx, const char *secret, krad_packet *pkt)
++{
++ krb5_data tmp;
++
++ tmp = make_data(pkt_attr(pkt), pkt->pkt.length - OFFSET_ATTR);
++ return kr_attrset_decode(ctx, &tmp, secret, pkt_auth(pkt), &pkt->attrset);
++}
++
++ssize_t
++krad_packet_bytes_needed(const krb5_data *buffer)
++{
++ size_t len;
++
++ if (buffer->length < OFFSET_AUTH)
++ return OFFSET_AUTH - buffer->length;
++
++ len = load_16_be(offset(buffer, OFFSET_LENGTH));
++ if (len > KRAD_PACKET_SIZE_MAX)
++ return -1;
++
++ return buffer->length > len ? 0 : len - buffer->length;
++}
++
++void
++krad_packet_free(krad_packet *pkt)
++{
++ if (pkt)
++ krad_attrset_free(pkt->attrset);
++ free(pkt);
++}
++
++/* Create a new request packet. */
++krb5_error_code
++krad_packet_new_request(krb5_context ctx, const char *secret, krad_code code,
++ const krad_attrset *set, krad_packet_iter_cb cb,
++ void *data, krad_packet **request)
++{
++ krb5_error_code retval;
++ krad_packet *pkt;
++ uchar id;
++ size_t attrset_len;
++
++ pkt = packet_new();
++ if (pkt == NULL) {
++ if (cb != NULL)
++ (*cb)(data, TRUE);
++ return ENOMEM;
++ }
++
++ /* Generate the ID. */
++ retval = id_generate(ctx, cb, data, &id);
++ if (retval != 0)
++ goto error;
++ pkt_id_set(pkt, id);
++
++ /* Generate the authenticator. */
++ retval = auth_generate_random(ctx, pkt_auth(pkt));
++ if (retval != 0)
++ goto error;
++
++ /* Encode the attributes. */
++ retval = kr_attrset_encode(set, secret, pkt_auth(pkt), pkt_attr(pkt),
++ &attrset_len);
++ if (retval != 0)
++ goto error;
++
++ /* Set the code, ID and length. */
++ pkt->pkt.length = attrset_len + OFFSET_ATTR;
++ pkt_code_set(pkt, code);
++ pkt_len_set(pkt, pkt->pkt.length);
++
++ /* Copy the attrset for future use. */
++ retval = packet_set_attrset(ctx, secret, pkt);
++ if (retval != 0)
++ goto error;
++
++ *request = pkt;
++ return 0;
++
++error:
++ free(pkt);
++ return retval;
++}
++
++/* Create a new request packet. */
++krb5_error_code
++krad_packet_new_response(krb5_context ctx, const char *secret, krad_code code,
++ const krad_attrset *set, const krad_packet *request,
++ krad_packet **response)
++{
++ krb5_error_code retval;
++ krad_packet *pkt;
++ size_t attrset_len;
++
++ pkt = packet_new();
++ if (pkt == NULL)
++ return ENOMEM;
++
++ /* Encode the attributes. */
++ retval = kr_attrset_encode(set, secret, pkt_auth(request), pkt_attr(pkt),
++ &attrset_len);
++ if (retval != 0)
++ goto error;
++
++ /* Set the code, ID and length. */
++ pkt->pkt.length = attrset_len + OFFSET_ATTR;
++ pkt_code_set(pkt, code);
++ pkt_id_set(pkt, pkt_id_get(request));
++ pkt_len_set(pkt, pkt->pkt.length);
++
++ /* Generate the authenticator. */
++ retval = auth_generate_response(ctx, secret, pkt, pkt_auth(request),
++ pkt_auth(pkt));
++ if (retval != 0)
++ goto error;
++
++ /* Copy the attrset for future use. */
++ retval = packet_set_attrset(ctx, secret, pkt);
++ if (retval != 0)
++ goto error;
++
++ *response = pkt;
++ return 0;
++
++error:
++ free(pkt);
++ return retval;
++}
++
++/* Decode a packet. */
++static krb5_error_code
++decode_packet(krb5_context ctx, const char *secret, const krb5_data *buffer,
++ krad_packet **pkt)
++{
++ krb5_error_code retval;
++ krad_packet *tmp;
++ krb5_ui_2 len;
++
++ tmp = packet_new();
++ if (tmp == NULL) {
++ retval = ENOMEM;
++ goto error;
++ }
++
++ /* Ensure a proper message length. */
++ retval = buffer->length < OFFSET_ATTR ? EMSGSIZE : 0;
++ if (retval != 0)
++ goto error;
++ len = load_16_be(offset(buffer, OFFSET_LENGTH));
++ retval = len < OFFSET_ATTR ? EBADMSG : 0;
++ if (retval != 0)
++ goto error;
++ retval = (len > buffer->length || len > tmp->pkt.length) ? EBADMSG : 0;
++ if (retval != 0)
++ goto error;
++
++ /* Copy over the buffer. */
++ tmp->pkt.length = len;
++ memcpy(tmp->pkt.data, buffer->data, len);
++
++ /* Parse the packet to ensure it is well-formed. */
++ retval = packet_set_attrset(ctx, secret, tmp);
++ if (retval != 0)
++ goto error;
++
++ *pkt = tmp;
++ return 0;
++
++error:
++ krad_packet_free(tmp);
++ return retval;
++}
++
++krb5_error_code
++krad_packet_decode_request(krb5_context ctx, const char *secret,
++ const krb5_data *buffer, krad_packet_iter_cb cb,
++ void *data, const krad_packet **duppkt,
++ krad_packet **reqpkt)
++{
++ const krad_packet *tmp = NULL;
++ krb5_error_code retval;
++
++ retval = decode_packet(ctx, secret, buffer, reqpkt);
++ if (cb != NULL && retval == 0) {
++ for (tmp = (*cb)(data, FALSE); tmp != NULL; tmp = (*cb)(data, FALSE)) {
++ if (pkt_id_get(*reqpkt) != pkt_id_get(tmp))
++ continue;
++
++ *duppkt = tmp;
++ break;
++ }
++ }
++
++ if (cb != NULL && (retval != 0 || tmp != NULL))
++ (*cb)(data, TRUE);
++
++ return retval;
++}
++
++krb5_error_code
++krad_packet_decode_response(krb5_context ctx, const char *secret,
++ const krb5_data *buffer, krad_packet_iter_cb cb,
++ void *data, const krad_packet **reqpkt,
++ krad_packet **rsppkt)
++{
++ uchar auth[AUTH_FIELD_SIZE];
++ const krad_packet *tmp = NULL;
++ krb5_error_code retval;
++
++ retval = decode_packet(ctx, secret, buffer, rsppkt);
++ if (cb != NULL && retval == 0) {
++ for (tmp = (*cb)(data, FALSE); tmp != NULL; tmp = (*cb)(data, FALSE)) {
++ if (pkt_id_get(*rsppkt) != pkt_id_get(tmp))
++ continue;
++
++ /* Response */
++ retval = auth_generate_response(ctx, secret, *rsppkt,
++ pkt_auth(tmp), auth);
++ if (retval != 0) {
++ krad_packet_free(*rsppkt);
++ break;
++ }
++
++ /* If the authenticator doesn't match, then the response is
++ * invalid. */
++ if (memcmp(pkt_auth(*rsppkt), auth, sizeof(auth)) != 0)
++ continue;
++
++ *reqpkt = tmp;
++ break;
++ }
++ }
++
++ if (cb != NULL && (retval != 0 || tmp != NULL))
++ (*cb)(data, TRUE);
++
++ return retval;
++}
++
++const krb5_data *
++krad_packet_encode(const krad_packet *pkt)
++{
++ return &pkt->pkt;
++}
++
++krad_code
++krad_packet_get_code(const krad_packet *pkt)
++{
++ if (pkt == NULL)
++ return 0;
++
++ return pkt_code_get(pkt);
++}
++
++const krb5_data *
++krad_packet_get_attr(const krad_packet *pkt, krad_attr type, size_t indx)
++{
++ return krad_attrset_get(pkt->attrset, type, indx);
++}
+diff --git a/src/lib/krad/remote.c b/src/lib/krad/remote.c
+new file mode 100644
+index 0000000..fe3fe83
+--- /dev/null
++++ b/src/lib/krad/remote.c
+@@ -0,0 +1,519 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/remote.c - Protocol code for libkrad */
++/*
++ * Copyright 2013 Red Hat, Inc. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in
++ * the documentation and/or other materials provided with the
++ * distribution.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
++ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
++ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include <k5-int.h>
++#include <k5-queue.h>
++#include "internal.h"
++
++#include <string.h>
++#include <unistd.h>
++
++#include <sys/un.h>
++
++#define FLAGS_READ (VERTO_EV_FLAG_PERSIST | VERTO_EV_FLAG_IO_CLOSE_FD | \
++ VERTO_EV_FLAG_IO_ERROR | VERTO_EV_FLAG_IO_READ)
++#define FLAGS_WRITE (FLAGS_READ | VERTO_EV_FLAG_IO_WRITE)
++
++TAILQ_HEAD(request_head, request_st);
++
++typedef struct request_st request;
++struct request_st {
++ TAILQ_ENTRY(request_st) list;
++ krad_remote *rr;
++ krad_packet *request;
++ krad_cb cb;
++ void *data;
++ verto_ev *timer;
++ time_t timeout;
++ size_t retries;
++ size_t sent;
++};
++
++struct krad_remote_st {
++ krb5_context kctx;
++ verto_ctx *vctx;
++ verto_ev *io;
++ char *secret;
++ struct addrinfo *info;
++ struct request_head list;
++ char buffer_[KRAD_PACKET_SIZE_MAX];
++ krb5_data buffer;
++};
++
++static void
++on_io(verto_ctx *ctx, verto_ev *ev);
++
++/* Iterate over the set of outstanding packets. */
++static const krad_packet *
++iterator(request **out)
++{
++ request *tmp = *out;
++
++ if (tmp == NULL)
++ return NULL;
++
++ *out = TAILQ_NEXT(tmp, list);
++ return tmp->request;
++}
++
++/* Create a new request. */
++static krb5_error_code
++request_new(krad_remote *rr, krad_packet *rqst, time_t timeout, size_t retries,
++ krad_cb cb, void *data, request **out)
++{
++ request *tmp;
++
++ tmp = calloc(1, sizeof(request));
++ if (tmp == NULL)
++ return ENOMEM;
++
++ tmp->rr = rr;
++ tmp->request = rqst;
++ tmp->cb = cb;
++ tmp->data = data;
++ tmp->timeout = timeout;
++ tmp->retries = retries;
++
++ *out = tmp;
++ return 0;
++}
++
++/* Finish a request, calling the callback and freeing it. */
++static inline void
++request_finish(request *req, krb5_error_code retval,
++ const krad_packet *response)
++{
++ if (retval != ETIMEDOUT)
++ TAILQ_REMOVE(&req->rr->list, req, list);
++
++ req->cb(retval, req->request, response, req->data);
++
++ if (retval != ETIMEDOUT) {
++ krad_packet_free(req->request);
++ verto_del(req->timer);
++ free(req);
++ }
++}
++
++/* Handle when packets receive no response within their alloted time. */
++static void
++on_timeout(verto_ctx *ctx, verto_ev *ev)
++{
++ request *req = verto_get_private(ev);
++
++ req->timer = NULL; /* Void the timer event. */
++
++ /* If we have more retries to perform, resend the packet. */
++ if (req->retries-- > 1) {
++ req->sent = 0;
++ verto_set_flags(req->rr->io, FLAGS_WRITE);
++ return;
++ }
++
++ request_finish(req, ETIMEDOUT, NULL);
++}
++
++/* Connect to the remote host. */
++static krb5_error_code
++remote_connect(krad_remote *rr)
++{
++ int i, sock = -1;
++ verto_ev *ev;
++
++ sock = socket(rr->info->ai_family, rr->info->ai_socktype,
++ rr->info->ai_protocol);
++ if (sock < 0)
++ return errno;
++
++ i = connect(sock, rr->info->ai_addr, rr->info->ai_addrlen);
++ if (i < 0) {
++ i = errno;
++ close(sock);
++ return i;
++ }
++
++ ev = verto_add_io(rr->vctx, FLAGS_READ, on_io, sock);
++ if (ev == NULL) {
++ close(sock);
++ return ENOMEM;
++ }
++
++ rr->io = ev;
++ verto_set_private(rr->io, rr, NULL);
++ return 0;
++}
++
++/* Disconnect and reconnect to the remote host. */
++static krb5_error_code
++remote_reconnect(krad_remote *rr, int errnum)
++{
++ krb5_error_code retval;
++ const krb5_data *tmp;
++ request *r;
++
++ verto_del(rr->io);
++ rr->io = NULL;
++ retval = remote_connect(rr);
++ if (retval != 0)
++ return retval;
++
++ TAILQ_FOREACH(r, &rr->list, list) {
++ tmp = krad_packet_encode(r->request);
++
++ if (r->sent == tmp->length) {
++ /* Error out sent requests. */
++ request_finish(r, errnum, NULL);
++ } else {
++ /* Reset partially sent requests. */
++ r->sent = 0;
++ }
++ }
++
++ return 0;
++}
++
++/* Close the connection and call the callbacks of all oustanding requests. */
++static void
++remote_shutdown(krad_remote *rr, int errnum)
++{
++ verto_del(rr->io);
++ rr->io = NULL;
++ while (!TAILQ_EMPTY(&rr->list))
++ request_finish(TAILQ_FIRST(&rr->list), errnum, NULL);
++}
++
++/* Write data to the socket. */
++static void
++on_io_write(krad_remote *rr)
++{
++ const krb5_data *tmp;
++ request *r;
++ int i;
++
++ TAILQ_FOREACH(r, &rr->list, list) {
++ tmp = krad_packet_encode(r->request);
++
++ /* If the packet has already been sent, do nothing. */
++ if (r->sent == tmp->length)
++ continue;
++
++ /* Send the packet. */
++ i = sendto(verto_get_fd(rr->io), tmp->data + r->sent,
++ tmp->length - r->sent, 0, NULL, 0);
++ if (i < 0) {
++ /* Should we try again? */
++ if (errno == EWOULDBLOCK || errno == EAGAIN || errno == ENOBUFS ||
++ errno == EINTR)
++ return;
++
++ /* In this case, we need to re-connect. */
++ i = remote_reconnect(rr, errno);
++ if (i == 0)
++ return;
++
++ /* Do a full reset. */
++ remote_shutdown(rr, i);
++ return;
++ }
++
++ /* SOCK_STREAM permits partial writes. */
++ if (rr->info->ai_socktype == SOCK_STREAM)
++ r->sent += i;
++ else if (i == (int)tmp->length)
++ r->sent = i;
++
++ /* If the packet was completely sent, set a timeout. */
++ if (r->sent == tmp->length) {
++ verto_del(r->timer);
++ r->timer = verto_add_timeout(rr->vctx, VERTO_EV_FLAG_NONE,
++ on_timeout, r->timeout);
++ if (r->timer == NULL)
++ request_finish(r, ENOMEM, NULL);
++ else
++ verto_set_private(r->timer, r, NULL);
++ }
++
++ return;
++ }
++
++ verto_set_flags(rr->io, FLAGS_READ);
++}
++
++/* Read data from the socket. */
++static void
++on_io_read(krad_remote *rr)
++{
++ const krad_packet *req = NULL;
++ krad_packet *rsp = NULL;
++ krb5_error_code retval;
++ ssize_t pktlen;
++ request *tmp, *r;
++ int i;
++
++ pktlen = sizeof(rr->buffer_);
++ if (rr->info->ai_socktype == SOCK_STREAM) {
++ pktlen = krad_packet_bytes_needed(&rr->buffer);
++ if (pktlen < 0) {
++ retval = remote_reconnect(rr, EBADMSG);
++ if (retval != 0)
++ remote_shutdown(rr, retval);
++ return;
++ }
++ }
++
++ /* Read the packet. */
++ i = recv(verto_get_fd(rr->io), rr->buffer.data + rr->buffer.length,
++ pktlen - rr->buffer.length, 0);
++ if (i < 0) {
++ /* Should we try again? */
++ if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR)
++ return;
++
++ if (errno == ECONNREFUSED || errno == ECONNRESET ||
++ errno == ENOTCONN) {
++ /*
++ * When doing UDP against a local socket, the kernel will notify
++ * when the daemon closes. But not against remote sockets. We want
++ * to treat them both the same. Returning here will cause an
++ * eventual timeout.
++ */
++ if (rr->info->ai_socktype != SOCK_STREAM)
++ return;
++ }
++
++ /* In this case, we need to re-connect. */
++ i = remote_reconnect(rr, errno);
++ if (i == 0)
++ return;
++
++ /* Do a full reset. */
++ remote_shutdown(rr, i);
++ return;
++ }
++
++ /* If we have a partial read or just the header, try again. */
++ rr->buffer.length += i;
++ pktlen = krad_packet_bytes_needed(&rr->buffer);
++ if (rr->info->ai_socktype == SOCK_STREAM && pktlen > 0)
++ return;
++
++ /* Decode the packet. */
++ tmp = TAILQ_FIRST(&rr->list);
++ retval = krad_packet_decode_response(rr->kctx, rr->secret, &rr->buffer,
++ (krad_packet_iter_cb)iterator, &tmp,
++ &req, &rsp);
++ rr->buffer.length = 0;
++ if (retval != 0)
++ return;
++
++ /* Match the response with an outstanding request. */
++ if (req != NULL) {
++ TAILQ_FOREACH(r, &rr->list, list) {
++ if (r->request == req &&
++ r->sent == krad_packet_encode(req)->length) {
++ request_finish(r, 0, rsp);
++ break;
++ }
++ }
++ }
++
++ krad_packet_free(rsp);
++}
++
++/* Handle when IO is ready on the socket. */
++static void
++on_io(verto_ctx *ctx, verto_ev *ev)
++{
++ krad_remote *rr;
++
++ rr = verto_get_private(ev);
++
++ if (verto_get_fd_state(ev) & VERTO_EV_FLAG_IO_WRITE)
++ on_io_write(rr);
++ else
++ on_io_read(rr);
++}
++
++krb5_error_code
++kr_remote_new(krb5_context kctx, verto_ctx *vctx, const struct addrinfo *info,
++ const char *secret, krad_remote **rr)
++{
++ krb5_error_code retval = ENOMEM;
++ krad_remote *tmp = NULL;
++
++ tmp = calloc(1, sizeof(krad_remote));
++ if (tmp == NULL)
++ goto error;
++ tmp->kctx = kctx;
++ tmp->vctx = vctx;
++ tmp->buffer = make_data(tmp->buffer_, 0);
++ TAILQ_INIT(&tmp->list);
++
++ tmp->secret = strdup(secret);
++ if (tmp->secret == NULL)
++ goto error;
++
++ tmp->info = k5memdup(info, sizeof(*info), &retval);
++ if (tmp->info == NULL)
++ goto error;
++
++ tmp->info->ai_addr = k5memdup(info->ai_addr, info->ai_addrlen, &retval);
++ if (tmp->info == NULL)
++ goto error;
++ tmp->info->ai_next = NULL;
++ tmp->info->ai_canonname = NULL;
++
++ retval = remote_connect(tmp);
++ if (retval != 0)
++ goto error;
++
++ *rr = tmp;
++ return 0;
++
++error:
++ kr_remote_free(tmp);
++ return retval;
++}
++
++void
++kr_remote_free(krad_remote *rr)
++{
++ if (rr == NULL)
++ return;
++
++ while (!TAILQ_EMPTY(&rr->list))
++ request_finish(TAILQ_FIRST(&rr->list), ECANCELED, NULL);
++
++ free(rr->secret);
++ if (rr->info != NULL)
++ free(rr->info->ai_addr);
++ free(rr->info);
++ verto_del(rr->io);
++ free(rr);
++}
++
++krb5_error_code
++kr_remote_send(krad_remote *rr, krad_code code, krad_attrset *attrs,
++ krad_cb cb, void *data, time_t timeout, size_t retries,
++ const krad_packet **pkt)
++{
++ krad_packet *tmp = NULL;
++ krb5_error_code retval;
++ request *r;
++
++ r = TAILQ_FIRST(&rr->list);
++ retval = krad_packet_new_request(rr->kctx, rr->secret, code, attrs,
++ (krad_packet_iter_cb)iterator, &r, &tmp);
++ if (retval != 0)
++ goto error;
++
++ TAILQ_FOREACH(r, &rr->list, list) {
++ if (r->request == tmp) {
++ retval = EALREADY;
++ goto error;
++ }
++ }
++
++ if (rr->io == NULL) {
++ retval = remote_connect(rr);
++ if (retval != 0)
++ goto error;
++ }
++
++ if (rr->info->ai_socktype == SOCK_STREAM)
++ retries = 0;
++ timeout = timeout / (retries + 1);
++ retval = request_new(rr, tmp, timeout, retries, cb, data, &r);
++ if (retval != 0)
++ goto error;
++
++ if ((verto_get_flags(rr->io) & VERTO_EV_FLAG_IO_WRITE) == 0)
++ verto_set_flags(rr->io, FLAGS_WRITE);
++
++ TAILQ_INSERT_TAIL(&rr->list, r, list);
++ if (pkt != NULL)
++ *pkt = tmp;
++ return 0;
++
++error:
++ krad_packet_free(tmp);
++ return retval;
++}
++
++void
++kr_remote_cancel(krad_remote *rr, const krad_packet *pkt)
++{
++ request *r;
++
++ TAILQ_FOREACH(r, &rr->list, list) {
++ if (r->request == pkt) {
++ request_finish(r, ECANCELED, NULL);
++ return;
++ }
++ }
++}
++
++krb5_boolean
++kr_remote_equals(const krad_remote *rr, const struct addrinfo *info,
++ const char *secret)
++{
++ struct sockaddr_un *a, *b;
++
++ if (strcmp(rr->secret, secret) != 0)
++ return FALSE;
++
++ if (info->ai_addrlen != rr->info->ai_addrlen)
++ return FALSE;
++
++ if (info->ai_family != rr->info->ai_family)
++ return FALSE;
++
++ if (info->ai_socktype != rr->info->ai_socktype)
++ return FALSE;
++
++ if (info->ai_protocol != rr->info->ai_protocol)
++ return FALSE;
++
++ if (info->ai_flags != rr->info->ai_flags)
++ return FALSE;
++
++ if (memcmp(rr->info->ai_addr, info->ai_addr, info->ai_addrlen) != 0) {
++ /* AF_UNIX fails the memcmp() test due to uninitialized bytes after the
++ * socket name. */
++ if (info->ai_family != AF_UNIX)
++ return FALSE;
++
++ a = (struct sockaddr_un *)info->ai_addr;
++ b = (struct sockaddr_un *)rr->info->ai_addr;
++ if (strncmp(a->sun_path, b->sun_path, sizeof(a->sun_path)) != 0)
++ return FALSE;
++ }
++
++ return TRUE;
++}
+diff --git a/src/lib/krad/t_attr.c b/src/lib/krad/t_attr.c
+new file mode 100644
+index 0000000..e80d77b
+--- /dev/null
++++ b/src/lib/krad/t_attr.c
+@@ -0,0 +1,89 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/t_attr.c - Attribute test program */
++/*
++ * Copyright 2013 Red Hat, Inc. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in
++ * the documentation and/or other materials provided with the
++ * distribution.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
++ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
++ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include "t_test.h"
++
++const static char encoded[] = {
++ 0xba, 0xfc, 0xed, 0x50, 0xe1, 0xeb, 0xa6, 0xc3,
++ 0xc1, 0x75, 0x20, 0xe9, 0x10, 0xce, 0xc2, 0xcb
++};
++
++const static unsigned char auth[] = {
++ 0xac, 0x9d, 0xc1, 0x62, 0x08, 0xc4, 0xc7, 0x8b,
++ 0xa1, 0x2f, 0x25, 0x0a, 0xc4, 0x1d, 0x36, 0x41
++};
++
++int
++main()
++{
++ unsigned char outbuf[MAX_ATTRSETSIZE];
++ const char *decoded = "accept";
++ const char *secret = "foo";
++ krb5_error_code retval;
++ krb5_context ctx;
++ const char *tmp;
++ krb5_data in;
++ size_t len;
++
++ noerror(krb5_init_context(&ctx));
++
++ /* Make sure User-Name is 1. */
++ insist(krad_attr_name2num("User-Name") == 1);
++
++ /* Make sure 2 is User-Password. */
++ tmp = krad_attr_num2name(2);
++ insist(tmp != NULL);
++ insist(strcmp(tmp, "User-Password") == 0);
++
++ /* Test decoding. */
++ in = make_data((void *)encoded, sizeof(encoded));
++ noerror(kr_attr_decode(ctx, secret, auth,
++ krad_attr_name2num("User-Password"),
++ &in, outbuf, &len));
++ insist(len == strlen(decoded));
++ insist(memcmp(outbuf, decoded, len) == 0);
++
++ /* Test encoding. */
++ in = string2data((char *)decoded);
++ retval = kr_attr_encode(ctx, secret, auth,
++ krad_attr_name2num("User-Password"),
++ &in, outbuf, &len);
++ insist(retval == 0);
++ insist(len == sizeof(encoded));
++ insist(memcmp(outbuf, encoded, len) == 0);
++
++ /* Test constraint. */
++ in.length = 100;
++ insist(kr_attr_valid(krad_attr_name2num("User-Password"), &in) == 0);
++ in.length = 200;
++ insist(kr_attr_valid(krad_attr_name2num("User-Password"), &in) != 0);
++
++ krb5_free_context(ctx);
++ return 0;
++}
+diff --git a/src/lib/krad/t_attrset.c b/src/lib/krad/t_attrset.c
+new file mode 100644
+index 0000000..afae5e4
+--- /dev/null
++++ b/src/lib/krad/t_attrset.c
+@@ -0,0 +1,98 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/t_attrset.c - Attribute set test program */
++/*
++ * Copyright 2013 Red Hat, Inc. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in
++ * the documentation and/or other materials provided with the
++ * distribution.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
++ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
++ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include "t_test.h"
++
++const static unsigned char auth[] = {
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++};
++
++const static char encpass[] = {
++ 0x58, 0x8d, 0xff, 0xda, 0x37, 0xf9, 0xe4, 0xca,
++ 0x19, 0xae, 0x49, 0xb7, 0x16, 0x6d, 0x58, 0x27
++};
++
++int
++main()
++{
++ unsigned char buffer[KRAD_PACKET_SIZE_MAX], encoded[MAX_ATTRSETSIZE];
++ const char *username = "testUser", *password = "accept";
++ const krb5_data *tmpp;
++ krad_attrset *set;
++ krb5_context ctx;
++ size_t len = 0, encode_len;
++ krb5_data tmp;
++
++ noerror(krb5_init_context(&ctx));
++ noerror(krad_attrset_new(ctx, &set));
++
++ /* Add username. */
++ tmp = string2data((char *)username);
++ noerror(krad_attrset_add(set, krad_attr_name2num("User-Name"), &tmp));
++
++ /* Add password. */
++ tmp = string2data((char *)password);
++ noerror(krad_attrset_add(set, krad_attr_name2num("User-Password"), &tmp));
++
++ /* Encode attrset. */
++ noerror(kr_attrset_encode(set, "foo", auth, buffer, &encode_len));
++ krad_attrset_free(set);
++
++ /* Manually encode User-Name. */
++ encoded[len + 0] = krad_attr_name2num("User-Name");
++ encoded[len + 1] = strlen(username) + 2;
++ memcpy(encoded + len + 2, username, strlen(username));
++ len += encoded[len + 1];
++
++ /* Manually encode User-Password. */
++ encoded[len + 0] = krad_attr_name2num("User-Password");
++ encoded[len + 1] = sizeof(encpass) + 2;
++ memcpy(encoded + len + 2, encpass, sizeof(encpass));
++ len += encoded[len + 1];
++
++ /* Compare output. */
++ insist(len == encode_len);
++ insist(memcmp(encoded, buffer, len) == 0);
++
++ /* Decode output. */
++ tmp = make_data(buffer, len);
++ noerror(kr_attrset_decode(ctx, &tmp, "foo", auth, &set));
++
++ /* Test getting an attribute. */
++ tmp = string2data((char *)username);
++ tmpp = krad_attrset_get(set, krad_attr_name2num("User-Name"), 0);
++ insist(tmpp != NULL);
++ insist(tmpp->length == tmp.length);
++ insist(strncmp(tmpp->data, tmp.data, tmp.length) == 0);
++
++ krad_attrset_free(set);
++ krb5_free_context(ctx);
++ return 0;
++}
+diff --git a/src/lib/krad/t_client.c b/src/lib/krad/t_client.c
+new file mode 100644
+index 0000000..3d0fda9
+--- /dev/null
++++ b/src/lib/krad/t_client.c
+@@ -0,0 +1,126 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/t_client.c - Client request test program */
++/*
++ * Copyright 2013 Red Hat, Inc. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in
++ * the documentation and/or other materials provided with the
++ * distribution.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
++ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
++ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include "t_daemon.h"
++
++#define EVENT_COUNT 4
++
++static struct
++{
++ int count;
++ struct event events[EVENT_COUNT];
++} record;
++
++static verto_ctx *vctx;
++
++static void
++callback(krb5_error_code retval, const krad_packet *request,
++ const krad_packet *response, void *data)
++{
++ struct event *evt;
++
++ evt = &record.events[record.count++];
++ evt->error = retval != 0;
++ if (evt->error)
++ evt->result.retval = retval;
++ else
++ evt->result.code = krad_packet_get_code(response);
++ verto_break(vctx);
++}
++
++int
++main(int argc, const char **argv)
++{
++ krad_attrset *attrs;
++ krad_client *rc;
++ krb5_context kctx;
++ krb5_data tmp;
++
++ if (!daemon_start(argc, argv)) {
++ fprintf(stderr, "Unable to start pyrad daemon, skipping test...\n");
++ return 0;
++ }
++
++ noerror(krb5_init_context(&kctx));
++ vctx = verto_new(NULL, VERTO_EV_TYPE_IO | VERTO_EV_TYPE_TIMEOUT);
++ insist(vctx != NULL);
++ noerror(krad_client_new(kctx, vctx, &rc));
++
++ tmp = string2data("testUser");
++ noerror(krad_attrset_new(kctx, &attrs));
++ noerror(krad_attrset_add(attrs, krad_attr_name2num("User-Name"), &tmp));
++
++ /* Test accept. */
++ tmp = string2data("accept");
++ noerror(krad_attrset_add(attrs, krad_attr_name2num("User-Password"),
++ &tmp));
++ noerror(krad_client_send(rc, krad_code_name2num("Access-Request"), attrs,
++ "localhost", "foo", 1000, 3, callback, NULL));
++ verto_run(vctx);
++
++ /* Test reject. */
++ tmp = string2data("reject");
++ krad_attrset_del(attrs, krad_attr_name2num("User-Password"), 0);
++ noerror(krad_attrset_add(attrs, krad_attr_name2num("User-Password"),
++ &tmp));
++ noerror(krad_client_send(rc, krad_code_name2num("Access-Request"), attrs,
++ "localhost", "foo", 1000, 3, callback, NULL));
++ verto_run(vctx);
++
++ /* Test timeout. */
++ daemon_stop();
++ noerror(krad_client_send(rc, krad_code_name2num("Access-Request"), attrs,
++ "localhost", "foo", 1000, 3, callback, NULL));
++ verto_run(vctx);
++
++ /* Test outstanding packet freeing. */
++ noerror(krad_client_send(rc, krad_code_name2num("Access-Request"), attrs,
++ "localhost", "foo", 1000, 3, callback, NULL));
++ krad_client_free(rc);
++ rc = NULL;
++
++ /* Verify the results. */
++ insist(record.count == EVENT_COUNT);
++ insist(record.events[0].error == FALSE);
++ insist(record.events[0].result.code ==
++ krad_code_name2num("Access-Accept"));
++ insist(record.events[1].error == FALSE);
++ insist(record.events[1].result.code ==
++ krad_code_name2num("Access-Reject"));
++ insist(record.events[2].error == TRUE);
++ insist(record.events[2].result.retval == ETIMEDOUT);
++ insist(record.events[3].error == TRUE);
++ insist(record.events[3].result.retval == ECANCELED);
++
++ krad_attrset_free(attrs);
++ krad_client_free(rc);
++ verto_free(vctx);
++ krb5_free_context(kctx);
++ return 0;
++}
+diff --git a/src/lib/krad/t_code.c b/src/lib/krad/t_code.c
+new file mode 100644
+index 0000000..b245a7e
+--- /dev/null
++++ b/src/lib/krad/t_code.c
+@@ -0,0 +1,54 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/t_code.c - RADIUS code table test program */
++/*
++ * Copyright 2013 Red Hat, Inc. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in
++ * the documentation and/or other materials provided with the
++ * distribution.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
++ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
++ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include "t_test.h"
++
++int
++main()
++{
++ const char *tmp;
++
++ insist(krad_code_name2num("Access-Request") == 1);
++ insist(krad_code_name2num("Access-Accept") == 2);
++ insist(krad_code_name2num("Access-Reject") == 3);
++
++ tmp = krad_code_num2name(1);
++ insist(tmp != NULL);
++ insist(strcmp(tmp, "Access-Request") == 0);
++
++ tmp = krad_code_num2name(2);
++ insist(tmp != NULL);
++ insist(strcmp(tmp, "Access-Accept") == 0);
++
++ tmp = krad_code_num2name(3);
++ insist(tmp != NULL);
++ insist(strcmp(tmp, "Access-Reject") == 0);
++
++ return 0;
++}
+diff --git a/src/lib/krad/t_daemon.h b/src/lib/krad/t_daemon.h
+new file mode 100644
+index 0000000..7c345a6
+--- /dev/null
++++ b/src/lib/krad/t_daemon.h
+@@ -0,0 +1,92 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/t_daemon.h - Daemonization helper for RADIUS test programs */
++/*
++ * Copyright 2013 Red Hat, Inc. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in
++ * the documentation and/or other materials provided with the
++ * distribution.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
++ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
++ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#ifndef T_DAEMON_H_
++#define T_DAEMON_H_
++
++#include "t_test.h"
++#include <libgen.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <fcntl.h>
++
++static pid_t daemon_pid;
++
++static void
++daemon_stop(void)
++{
++ if (daemon_pid == 0)
++ return;
++ kill(daemon_pid, SIGTERM);
++ waitpid(daemon_pid, NULL, 0);
++ daemon_pid = 0;
++}
++
++static krb5_boolean
++daemon_start(int argc, const char **argv)
++{
++ sigset_t set;
++ int sig;
++
++ if (argc != 3 || argv == NULL)
++ return FALSE;
++
++ if (daemon_pid != 0)
++ return TRUE;
++
++ if (sigemptyset(&set) != 0)
++ return FALSE;
++
++ if (sigaddset(&set, SIGUSR1) != 0)
++ return FALSE;
++
++ if (sigaddset(&set, SIGCHLD) != 0)
++ return FALSE;
++
++ if (sigprocmask(SIG_BLOCK, &set, NULL) != 0)
++ return FALSE;
++
++ daemon_pid = fork();
++ if (daemon_pid == 0) {
++ close(STDOUT_FILENO);
++ open("/dev/null", O_WRONLY);
++ exit(execlp(argv[1], argv[1], argv[2], NULL));
++ }
++
++ if (sigwait(&set, &sig) != 0 || sig == SIGCHLD) {
++ daemon_stop();
++ daemon_pid = 0;
++ return FALSE;
++ }
++
++ atexit(daemon_stop);
++ return TRUE;
++}
++
++#endif /* T_DAEMON_H_ */
+diff --git a/src/lib/krad/t_daemon.py b/src/lib/krad/t_daemon.py
+new file mode 100644
+index 0000000..d62bc29
+--- /dev/null
++++ b/src/lib/krad/t_daemon.py
+@@ -0,0 +1,76 @@
++#!/usr/bin/python
++#
++# Copyright 2013 Red Hat, Inc. All rights reserved.
++#
++# Redistribution and use in source and binary forms, with or without
++# modification, are permitted provided that the following conditions are met:
++#
++# 1. Redistributions of source code must retain the above copyright
++# notice, this list of conditions and the following disclaimer.
++#
++# 2. Redistributions in binary form must reproduce the above copyright
++# notice, this list of conditions and the following disclaimer in
++# the documentation and/or other materials provided with the
++# distribution.
++#
++# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
++# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
++# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
++# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
++# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
++# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
++# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++import StringIO
++import os
++import sys
++import signal
++
++try:
++ from pyrad import dictionary, packet, server
++except ImportError:
++ sys.stdout.write("pyrad not found!\n")
++ sys.exit(0)
++
++# We could use a dictionary file, but since we need
++# such few attributes, we'll just include them here
++DICTIONARY = """
++ATTRIBUTE\tUser-Name\t1\tstring
++ATTRIBUTE\tUser-Password\t2\toctets
++ATTRIBUTE\tNAS-Identifier\t32\tstring
++"""
++
++class TestServer(server.Server):
++ def _HandleAuthPacket(self, pkt):
++ server.Server._HandleAuthPacket(self, pkt)
++
++ passwd = []
++
++ print "Request: "
++ for key in pkt.keys():
++ if key == "User-Password":
++ passwd = map(pkt.PwDecrypt, pkt[key])
++ print "\t%s\t%s" % (key, passwd)
++ else:
++ print "\t%s\t%s" % (key, pkt[key])
++
++ reply = self.CreateReplyPacket(pkt)
++ if passwd == ['accept']:
++ reply.code = packet.AccessAccept
++ print "Response: %s" % "Access-Accept"
++ else:
++ reply.code = packet.AccessReject
++ print "Response: %s" % "Access-Reject"
++ print
++ self.SendReplyPacket(pkt.fd, reply)
++
++srv = TestServer(addresses=["localhost"],
++ hosts={"127.0.0.1":
++ server.RemoteHost("127.0.0.1", "foo", "localhost")},
++ dict=dictionary.Dictionary(StringIO.StringIO(DICTIONARY)))
++os.kill(os.getppid(), signal.SIGUSR1)
++srv.Run()
+diff --git a/src/lib/krad/t_packet.c b/src/lib/krad/t_packet.c
+new file mode 100644
+index 0000000..0a92e9c
+--- /dev/null
++++ b/src/lib/krad/t_packet.c
+@@ -0,0 +1,194 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/t_packet.c - RADIUS packet test program */
++/*
++ * Copyright 2013 Red Hat, Inc. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in
++ * the documentation and/or other materials provided with the
++ * distribution.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
++ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
++ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include "t_daemon.h"
++
++#define ACCEPT_PACKET 0
++#define REJECT_PACKET 1
++
++static krad_packet *packets[3];
++
++static const krad_packet *
++iterator(void *data, krb5_boolean cancel)
++{
++ krad_packet *tmp;
++ int *i = data;
++
++ if (cancel || packets[*i] == NULL)
++ return NULL;
++
++ tmp = packets[*i];
++ *i += 1;
++ return tmp;
++}
++
++static krb5_error_code
++make_packet(krb5_context ctx, const krb5_data *username,
++ const krb5_data *password, krad_packet **pkt)
++{
++ krad_attrset *set = NULL;
++ krad_packet *tmp = NULL;
++ krb5_error_code retval;
++ const krb5_data *data;
++ int i = 0;
++
++ retval = krad_attrset_new(ctx, &set);
++ if (retval != 0)
++ goto out;
++
++ retval = krad_attrset_add(set, krad_attr_name2num("User-Name"), username);
++ if (retval != 0)
++ goto out;
++
++ retval = krad_attrset_add(set, krad_attr_name2num("User-Password"),
++ password);
++ if (retval != 0)
++ goto out;
++
++ retval = krad_packet_new_request(ctx, "foo",
++ krad_code_name2num("Access-Request"),
++ set, iterator, &i, &tmp);
++ if (retval != 0)
++ goto out;
++
++ data = krad_packet_get_attr(tmp, krad_attr_name2num("User-Name"), 0);
++ if (data == NULL) {
++ retval = ENOENT;
++ goto out;
++ }
++
++ if (data->length != username->length ||
++ memcmp(data->data, username->data, data->length) != 0) {
++ retval = EINVAL;
++ goto out;
++ }
++
++ *pkt = tmp;
++ tmp = NULL;
++
++out:
++ krad_attrset_free(set);
++ krad_packet_free(tmp);
++ return retval;
++}
++
++static krb5_error_code
++do_auth(krb5_context ctx, struct addrinfo *ai, const char *secret,
++ const krad_packet *rqst, krb5_boolean *auth)
++{
++ const krad_packet *req = NULL;
++ char tmp[KRAD_PACKET_SIZE_MAX];
++ const krb5_data *request;
++ krad_packet *rsp = NULL;
++ krb5_error_code retval;
++ krb5_data response;
++ int sock = -1, i;
++
++ response = make_data(tmp, sizeof(tmp));
++
++ sock = socket(ai->ai_family, ai->ai_socktype, 0);
++ if (sock < 0) {
++ retval = errno;
++ goto out;
++ }
++
++ request = krad_packet_encode(rqst);
++ if (sendto(sock, request->data, request->length, 0, ai->ai_addr,
++ ai->ai_addrlen) < 0) {
++ retval = errno;
++ goto out;
++ }
++
++ i = recv(sock, response.data, sizeof(tmp), 0);
++ if (i < 0) {
++ retval = errno;
++ goto out;
++ }
++ response.length = i;
++
++ i = 0;
++ retval = krad_packet_decode_response(ctx, secret, &response, iterator, &i,
++ &req, &rsp);
++ if (retval != 0)
++ goto out;
++
++ if (req != rqst) {
++ retval = EBADMSG;
++ goto out;
++ }
++
++ *auth = krad_packet_get_code(rsp) == krad_code_name2num("Access-Accept");
++
++out:
++ krad_packet_free(rsp);
++ if (sock >= 0)
++ close(sock);
++ return retval;
++}
++
++int
++main(int argc, const char **argv)
++{
++ struct addrinfo *ai = NULL, hints;
++ krb5_data username, password;
++ krb5_boolean auth = FALSE;
++ krb5_context ctx;
++
++ username = string2data("testUser");
++
++ if (!daemon_start(argc, argv)) {
++ fprintf(stderr, "Unable to start pyrad daemon, skipping test...\n");
++ return 0;
++ }
++
++ noerror(krb5_init_context(&ctx));
++
++ password = string2data("accept");
++ noerror(make_packet(ctx, &username, &password, &packets[ACCEPT_PACKET]));
++
++ password = string2data("reject");
++ noerror(make_packet(ctx, &username, &password, &packets[REJECT_PACKET]));
++
++ memset(&hints, 0, sizeof(hints));
++ hints.ai_family = AF_INET;
++ hints.ai_socktype = SOCK_DGRAM;
++ noerror(gai_error_code(getaddrinfo("127.0.0.1", "radius", &hints, &ai)));
++
++ noerror(do_auth(ctx, ai, "foo", packets[ACCEPT_PACKET], &auth));
++ insist(auth == TRUE);
++
++ noerror(do_auth(ctx, ai, "foo", packets[REJECT_PACKET], &auth));
++ insist(auth == FALSE);
++
++ krad_packet_free(packets[ACCEPT_PACKET]);
++ krad_packet_free(packets[REJECT_PACKET]);
++ krb5_free_context(ctx);
++ freeaddrinfo(ai);
++ return 0;
++}
+diff --git a/src/lib/krad/t_remote.c b/src/lib/krad/t_remote.c
+new file mode 100644
+index 0000000..a521ecb
+--- /dev/null
++++ b/src/lib/krad/t_remote.c
+@@ -0,0 +1,170 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/t_remote.c - Protocol test program */
++/*
++ * Copyright 2013 Red Hat, Inc. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in
++ * the documentation and/or other materials provided with the
++ * distribution.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
++ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
++ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include "t_daemon.h"
++
++#define EVENT_COUNT 6
++
++static struct
++{
++ int count;
++ struct event events[EVENT_COUNT];
++} record;
++
++static krad_attrset *set;
++static krad_remote *rr;
++static verto_ctx *vctx;
++
++static void
++callback(krb5_error_code retval, const krad_packet *request,
++ const krad_packet *response, void *data)
++{
++ struct event *evt;
++
++ evt = &record.events[record.count++];
++ evt->error = retval != 0;
++ if (evt->error)
++ evt->result.retval = retval;
++ else
++ evt->result.code = krad_packet_get_code(response);
++ verto_break(vctx);
++}
++
++static void
++remote_new(krb5_context kctx, krad_remote **remote)
++{
++ struct addrinfo *ai = NULL, hints;
++
++ memset(&hints, 0, sizeof(hints));
++ hints.ai_family = AF_INET;
++ hints.ai_socktype = SOCK_DGRAM;
++ noerror(gai_error_code(getaddrinfo("127.0.0.1", "radius", &hints, &ai)));
++
++ noerror(kr_remote_new(kctx, vctx, ai, "foo", remote));
++ insist(kr_remote_equals(*remote, ai, "foo"));
++ freeaddrinfo(ai);
++}
++
++static krb5_error_code
++do_auth(const char *password, const krad_packet **pkt)
++{
++ const krad_packet *tmppkt;
++ krb5_error_code retval;
++ krb5_data tmp = string2data((char *)password);
++
++ retval = krad_attrset_add(set, krad_attr_name2num("User-Password"), &tmp);
++ if (retval != 0)
++ return retval;
++
++ retval = kr_remote_send(rr, krad_code_name2num("Access-Request"), set,
++ callback, NULL, 1000, 3, &tmppkt);
++ krad_attrset_del(set, krad_attr_name2num("User-Password"), 0);
++ if (retval != 0)
++ return retval;
++
++ if (pkt != NULL)
++ *pkt = tmppkt;
++ return 0;
++}
++
++static void
++test_timeout(verto_ctx *ctx, verto_ev *ev)
++{
++ static const krad_packet *pkt;
++
++ noerror(do_auth("accept", &pkt));
++ kr_remote_cancel(rr, pkt);
++}
++
++int
++main(int argc, const char **argv)
++{
++ krb5_context kctx = NULL;
++ krb5_data tmp;
++
++ if (!daemon_start(argc, argv)) {
++ fprintf(stderr, "Unable to start pyrad daemon, skipping test...\n");
++ return 0;
++ }
++
++ /* Initialize. */
++ noerror(krb5_init_context(&kctx));
++ vctx = verto_new(NULL, VERTO_EV_TYPE_IO | VERTO_EV_TYPE_TIMEOUT);
++ insist(vctx != NULL);
++ remote_new(kctx, &rr);
++
++ /* Create attribute set. */
++ noerror(krad_attrset_new(kctx, &set));
++ tmp = string2data("testUser");
++ noerror(krad_attrset_add(set, krad_attr_name2num("User-Name"), &tmp));
++
++ /* Send accept packet. */
++ noerror(do_auth("accept", NULL));
++ verto_run(vctx);
++
++ /* Send reject packet. */
++ noerror(do_auth("reject", NULL));
++ verto_run(vctx);
++
++ /* Send canceled packet. */
++ insist(verto_add_timeout(vctx, VERTO_EV_FLAG_NONE, test_timeout, 0) !=
++ NULL);
++ verto_run(vctx);
++
++ /* Test timeout. */
++ daemon_stop();
++ noerror(do_auth("accept", NULL));
++ verto_run(vctx);
++
++ /* Test outstanding packet freeing. */
++ noerror(do_auth("accept", NULL));
++ kr_remote_free(rr);
++ krad_attrset_free(set);
++
++ /* Verify the results. */
++ insist(record.count == EVENT_COUNT);
++ insist(record.events[0].error == FALSE);
++ insist(record.events[0].result.code ==
++ krad_code_name2num("Access-Accept"));
++ insist(record.events[1].error == FALSE);
++ insist(record.events[1].result.code ==
++ krad_code_name2num("Access-Reject"));
++ insist(record.events[2].error == TRUE);
++ insist(record.events[2].result.retval == ECANCELED);
++ insist(record.events[3].error == TRUE);
++ insist(record.events[3].result.retval == ETIMEDOUT);
++ insist(record.events[4].error == TRUE);
++ insist(record.events[4].result.retval == ECANCELED);
++ insist(record.events[5].error == TRUE);
++ insist(record.events[5].result.retval == ECANCELED);
++
++ verto_free(vctx);
++ krb5_free_context(kctx);
++ return 0;
++}
+diff --git a/src/lib/krad/t_test.c b/src/lib/krad/t_test.c
+new file mode 100644
+index 0000000..152bc77
+--- /dev/null
++++ b/src/lib/krad/t_test.c
+@@ -0,0 +1,50 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/t_test.c - Utility functions for libkrad tests */
++/*
++ * Copyright 2013 Red Hat, Inc. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in
++ * the documentation and/or other materials provided with the
++ * distribution.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
++ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
++ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include "t_test.h"
++
++void
++noerror_impl(const char *file, int line, const char *cmd, int retval)
++{
++ if (retval == 0)
++ return;
++
++ fprintf(stderr, "%s:%d: %s:\n\t%s\n", file, line, strerror(retval), cmd);
++ exit(1);
++}
++
++void
++insist_impl(const char *file, int line, const char *cmd, krb5_boolean result)
++{
++ if (result)
++ return;
++
++ fprintf(stderr, "%s:%d: insist failed:\n\t%s\n", file, line, cmd);
++ exit(1);
++}
+diff --git a/src/lib/krad/t_test.h b/src/lib/krad/t_test.h
+new file mode 100644
+index 0000000..f44742f
+--- /dev/null
++++ b/src/lib/krad/t_test.h
+@@ -0,0 +1,60 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/* lib/krad/t_test.h - Shared declarations for libkrad test programs */
++/*
++ * Copyright 2013 Red Hat, Inc. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in
++ * the documentation and/or other materials provided with the
++ * distribution.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
++ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
++ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#ifndef T_TEST_H_
++#define T_TEST_H_
++
++#include "internal.h"
++
++#include <sys/wait.h>
++#include <signal.h>
++#include <unistd.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++
++#define insist(x) insist_impl(__FILE__, __LINE__, #x, x)
++#define noerror(x) noerror_impl(__FILE__, __LINE__, #x, x)
++
++struct event {
++ krb5_boolean error;
++ union
++ {
++ krb5_error_code retval;
++ krad_code code;
++ } result;
++};
++
++void
++noerror_impl(const char *file, int line, const char *cmd, int retval);
++
++void
++insist_impl(const char *file, int line, const char *cmd, krb5_boolean result);
++
++#endif /* T_TEST_H_ */
+--
+1.8.2
+
diff --git a/0002-Add-internal-KDC_DIR-macro.patch b/0003-Add-internal-KDC_DIR-macro.patch
index ec87eb2..7603264 100644
--- a/0002-Add-internal-KDC_DIR-macro.patch
+++ b/0003-Add-internal-KDC_DIR-macro.patch
@@ -1,7 +1,7 @@
-From 5caeecbc6753f526ccd620e29daed49973f8e21d Mon Sep 17 00:00:00 2001
+From a4a7a4aeb2fb96e36494faff46243fbcb3c0d78b Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Tue, 15 Jan 2013 11:11:27 -0500
-Subject: [PATCH 2/3] Add internal KDC_DIR macro
+Subject: [PATCH 3/4] Add internal KDC_DIR macro
Define KDC_DIR in osconf.hin and use it for paths within the KDC
directory.
@@ -62,5 +62,5 @@ index c3a33c2..1bca991 100644
/*
* GSS mechglue
--
-1.8.1.4
+1.8.2
diff --git a/0003-add-otp-plugin.patch b/0004-add-otp-plugin.patch
index f957b9c..23bf218 100644
--- a/0003-add-otp-plugin.patch
+++ b/0004-add-otp-plugin.patch
@@ -1,7 +1,7 @@
-From 9c67d6fd21692d8bbfbe880511cbcbc5d9e6a2e5 Mon Sep 17 00:00:00 2001
+From 10e4dcdbb8856c66f9000d0c737e4eb9312aa021 Mon Sep 17 00:00:00 2001
From: Nathaniel McCallum <npmccallum@redhat.com>
-Date: Fri, 8 Mar 2013 10:22:03 -0500
-Subject: [PATCH 3/3] add otp plugin
+Date: Tue, 9 Apr 2013 12:24:47 -0400
+Subject: [PATCH 4/4] add otp plugin
---
src/Makefile.in | 1 +
@@ -9,11 +9,11 @@ Subject: [PATCH 3/3] add otp plugin
src/kdc/kdc_preauth.c | 2 +
src/plugins/preauth/otp/Makefile.in | 45 +++
src/plugins/preauth/otp/deps | 26 ++
- src/plugins/preauth/otp/main.c | 374 +++++++++++++++++++++++
+ src/plugins/preauth/otp/main.c | 374 ++++++++++++++++++++++++
src/plugins/preauth/otp/otp.exports | 1 +
- src/plugins/preauth/otp/otp_state.c | 571 ++++++++++++++++++++++++++++++++++++
+ src/plugins/preauth/otp/otp_state.c | 568 ++++++++++++++++++++++++++++++++++++
src/plugins/preauth/otp/otp_state.h | 58 ++++
- 9 files changed, 1079 insertions(+)
+ 9 files changed, 1076 insertions(+)
create mode 100644 src/plugins/preauth/otp/Makefile.in
create mode 100644 src/plugins/preauth/otp/deps
create mode 100644 src/plugins/preauth/otp/main.c
@@ -34,7 +34,7 @@ index 2c65831..0b9d355 100644
kdc kadmin slave clients appl tests \
config-files man doc @po@
diff --git a/src/configure.in b/src/configure.in
-index 6a9757f..053e7b4 100644
+index d8676e5..520e0c8 100644
--- a/src/configure.in
+++ b/src/configure.in
@@ -1337,6 +1337,7 @@ dnl ccapi ccapi/lib ccapi/lib/unix ccapi/server ccapi/server/unix ccapi/test
@@ -60,7 +60,7 @@ index 42a37a8..afbf1f6 100644
kdcpreauth_encrypted_challenge_initvt);
diff --git a/src/plugins/preauth/otp/Makefile.in b/src/plugins/preauth/otp/Makefile.in
new file mode 100644
-index 0000000..c610be9
+index 0000000..62fa432
--- /dev/null
+++ b/src/plugins/preauth/otp/Makefile.in
@@ -0,0 +1,45 @@
@@ -84,9 +84,9 @@ index 0000000..c610be9
+SHLIB_EXPDEPS = \
+ $(TOPLIBD)/libk5crypto$(SHLIBEXT) \
+ $(TOPLIBD)/libkrb5$(SHLIBEXT) \
-+ $(TOPLIBD)/radius/libk5radius$(SHLIBEXT)
++ $(TOPLIBD)/krad/libkrad$(SHLIBEXT)
+
-+SHLIB_EXPLIBS= -lverto -lk5radius $(KRB5_LIB) $(K5CRYPTO_LIB) $(COM_ERR_LIB) $(SUPPORT_LIB) $(LIBS)
++SHLIB_EXPLIBS= -lverto -lkrad $(KRB5_LIB) $(K5CRYPTO_LIB) $(COM_ERR_LIB) $(SUPPORT_LIB) $(LIBS)
+
+SHLIB_DIRS=-L$(TOPLIBD)
+SHLIB_RDIRS=$(KRB5_LIBDIR)
@@ -111,7 +111,7 @@ index 0000000..c610be9
+
diff --git a/src/plugins/preauth/otp/deps b/src/plugins/preauth/otp/deps
new file mode 100644
-index 0000000..cf5f19f
+index 0000000..3352126
--- /dev/null
+++ b/src/plugins/preauth/otp/deps
@@ -0,0 +1,26 @@
@@ -119,17 +119,17 @@ index 0000000..cf5f19f
+# Generated makefile dependencies follow.
+#
+otp_state.so otp_state.po $(OUTPRE)otp_state.$(OBJEXT): \
-+ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/k5radius.h \
-+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
-+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
-+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
-+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
-+ $(top_srcdir)/include/k5-json.h $(top_srcdir)/include/k5-platform.h \
-+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
-+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
-+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
-+ $(top_srcdir)/include/krb5/preauth_plugin.h $(top_srcdir)/include/port-sockets.h \
-+ $(top_srcdir)/include/socket-utils.h otp_state.c otp_state.h
++ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
++ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
++ $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
++ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
++ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-json.h \
++ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
++ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
++ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
++ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/krb5/preauth_plugin.h \
++ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
++ otp_state.c otp_state.h
+main.so main.po $(OUTPRE)main.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
@@ -143,7 +143,7 @@ index 0000000..cf5f19f
+ $(top_srcdir)/include/socket-utils.h main.c otp_state.h
diff --git a/src/plugins/preauth/otp/main.c b/src/plugins/preauth/otp/main.c
new file mode 100644
-index 0000000..e980666
+index 0000000..8b48e1b
--- /dev/null
+++ b/src/plugins/preauth/otp/main.c
@@ -0,0 +1,374 @@
@@ -325,7 +325,7 @@ index 0000000..e980666
+static int
+otp_flags(krb5_context context, krb5_preauthtype pa_type)
+{
-+ return PA_REPLACES_KEY;
++ return PA_REPLACES_KEY;
+}
+
+static void
@@ -481,45 +481,45 @@ index 0000000..e980666
+ krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata,
+ krb5_kdcpreauth_modreq modreq)
+{
-+ krb5_keyblock *armor_key = NULL;
++ krb5_keyblock *armor_key = NULL;
+
-+ if (!padata || padata->length == 0)
-+ return 0;
++ if (!padata || padata->length == 0)
++ return 0;
+
-+ /* Get the armor key. */
-+ armor_key = cb->fast_armor(context, rock);
-+ if (!armor_key) {
-+ DEBUGMSG(ENOENT, "No armor key found when returning padata.");
-+ return ENOENT;
-+ }
++ /* Get the armor key. */
++ armor_key = cb->fast_armor(context, rock);
++ if (!armor_key) {
++ DEBUGMSG(ENOENT, "No armor key found when returning padata.");
++ return ENOENT;
++ }
+
-+ /* Replace the reply key with the FAST armor key. */
-+ krb5_free_keyblock_contents(context, encrypting_key);
-+ return krb5_copy_keyblock_contents(context, armor_key, encrypting_key);
++ /* Replace the reply key with the FAST armor key. */
++ krb5_free_keyblock_contents(context, encrypting_key);
++ return krb5_copy_keyblock_contents(context, armor_key, encrypting_key);
+}
+
+krb5_error_code
+kdcpreauth_otp_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable)
+{
-+ krb5_kdcpreauth_vtable vt;
++ krb5_kdcpreauth_vtable vt;
+
-+ if (maj_ver != 1)
-+ return KRB5_PLUGIN_VER_NOTSUPP;
++ if (maj_ver != 1)
++ return KRB5_PLUGIN_VER_NOTSUPP;
+
-+ vt = (krb5_kdcpreauth_vtable)vtable;
-+ vt->name = "otp";
-+ vt->pa_type_list = otp_pa_type_list;
-+ vt->init = otp_init;
-+ vt->fini = otp_fini;
-+ vt->flags = otp_flags;
-+ vt->edata = otp_edata;
-+ vt->verify = otp_verify;
-+ vt->return_padata = otp_return_padata;
++ vt = (krb5_kdcpreauth_vtable)vtable;
++ vt->name = "otp";
++ vt->pa_type_list = otp_pa_type_list;
++ vt->init = otp_init;
++ vt->fini = otp_fini;
++ vt->flags = otp_flags;
++ vt->edata = otp_edata;
++ vt->verify = otp_verify;
++ vt->return_padata = otp_return_padata;
+
-+ com_err("otp", 0, "Loaded.");
++ com_err("otp", 0, "Loaded.");
+
-+ return 0;
++ return 0;
+}
diff --git a/src/plugins/preauth/otp/otp.exports b/src/plugins/preauth/otp/otp.exports
new file mode 100644
@@ -530,10 +530,10 @@ index 0000000..26aa19d
+kdcpreauth_otp_initvt
diff --git a/src/plugins/preauth/otp/otp_state.c b/src/plugins/preauth/otp/otp_state.c
new file mode 100644
-index 0000000..a42141c
+index 0000000..caa0752
--- /dev/null
+++ b/src/plugins/preauth/otp/otp_state.c
-@@ -0,0 +1,571 @@
+@@ -0,0 +1,568 @@
+/*
+ * Copyright 2012 Red Hat, Inc. All rights reserved.
+ *
@@ -563,7 +563,7 @@ index 0000000..a42141c
+
+#include "otp_state.h"
+
-+#include <k5radius.h>
++#include <krad.h>
+#include <k5-json.h>
+
+#include <ctype.h>
@@ -593,14 +593,14 @@ index 0000000..a42141c
+ ssize_t index;
+ otp_cb *cb;
+ void *data;
-+ k5_radius_attrset *attrs;
++ krad_attrset *attrs;
+} request;
+
+struct otp_state_ {
+ krb5_context ctx;
+ token_type *types;
-+ k5_radius_client *radius;
-+ k5_radius_attrset *attrs;
++ krad_client *radius;
++ krad_attrset *attrs;
+};
+
+static inline krb5_data
@@ -928,7 +928,7 @@ index 0000000..a42141c
+ if (req == NULL)
+ return;
+
-+ k5_radius_attrset_free(req->attrs);
++ krad_attrset_free(req->attrs);
+ tokens_free(req->tokens);
+ free(req);
+}
@@ -959,20 +959,19 @@ index 0000000..a42141c
+ if (retval != 0)
+ goto error;
+
-+ retval = k5_radius_attrset_new(ctx, &self->attrs);
++ retval = krad_attrset_new(ctx, &self->attrs);
+ if (retval != 0)
+ goto error;
+
+ hndata = make_data(hostname, strlen(hostname));
-+ retval = k5_radius_attrset_add(self->attrs,
-+ k5_radius_attr_name2num("NAS-Identifier"),
-+ &hndata);
++ retval = krad_attrset_add(self->attrs,
++ krad_attr_name2num("NAS-Identifier"), &hndata);
+ if (retval != 0)
+ goto error;
+
-+ retval = k5_radius_attrset_add_number(
-+ self->attrs, k5_radius_attr_name2num("Service-Type"),
-+ K5_RADIUS_SERVICE_TYPE_AUTHENTICATE_ONLY);
++ retval = krad_attrset_add_number(self->attrs,
++ krad_attr_name2num("Service-Type"),
++ KRAD_SERVICE_TYPE_AUTHENTICATE_ONLY);
+ if (retval != 0)
+ goto error;
+
@@ -991,7 +990,7 @@ index 0000000..a42141c
+ if (self == NULL)
+ return;
+
-+ k5_radius_attrset_free(self->attrs);
++ krad_attrset_free(self->attrs);
+ token_types_free(self->types);
+ free(self);
+}
@@ -1000,8 +999,8 @@ index 0000000..a42141c
+request_send(request *req);
+
+static void
-+callback(krb5_error_code retval, const k5_radius_packet *rqst,
-+ const k5_radius_packet *resp, void *data)
++callback(krb5_error_code retval, const krad_packet *rqst,
++ const krad_packet *resp, void *data)
+{
+ request *req = data;
+
@@ -1011,8 +1010,8 @@ index 0000000..a42141c
+ goto error;
+
+ /* If we received an accept packet, success! */
-+ if (k5_radius_packet_get_code(resp) ==
-+ k5_radius_code_name2num("Access-Accept")) {
++ if (krad_packet_get_code(resp) ==
++ krad_code_name2num("Access-Accept")) {
+ (*req->cb)(retval, otp_response_success, req->data);
+ request_free(req);
+ return;
@@ -1035,21 +1034,20 @@ index 0000000..a42141c
+{
+ krb5_error_code retval;
+
-+ retval = k5_radius_attrset_add(req->attrs,
-+ k5_radius_attr_name2num("User-Name"),
++ retval = krad_attrset_add(req->attrs,
++ krad_attr_name2num("User-Name"),
+ &req->tokens[req->index].username);
+ if (retval != 0)
+ goto error;
+
-+ retval = k5_radius_client_send(req->state->radius,
-+ k5_radius_code_name2num("Access-Request"),
-+ req->attrs,
-+ req->tokens[req->index].type->server,
-+ req->tokens[req->index].type->secret,
-+ req->tokens[req->index].type->timeout,
-+ req->tokens[req->index].type->retries,
-+ callback, req);
-+ k5_radius_attrset_del(req->attrs, k5_radius_attr_name2num("User-Name"), 0);
++ retval = krad_client_send(req->state->radius,
++ krad_code_name2num("Access-Request"), req->attrs,
++ req->tokens[req->index].type->server,
++ req->tokens[req->index].type->secret,
++ req->tokens[req->index].type->timeout,
++ req->tokens[req->index].type->retries,
++ callback, req);
++ krad_attrset_del(req->attrs, krad_attr_name2num("User-Name"), 0);
+ if (retval != 0)
+ goto error;
+
@@ -1069,7 +1067,7 @@ index 0000000..a42141c
+ request *rqst = NULL;
+
+ if (state->radius == NULL) {
-+ retval = k5_radius_client_new(state->ctx, ctx, &state->radius);
++ retval = krad_client_new(state->ctx, ctx, &state->radius);
+ if (retval != 0)
+ goto error;
+ }
@@ -1083,13 +1081,12 @@ index 0000000..a42141c
+ rqst->data = data;
+ rqst->cb = cb;
+
-+ retval = k5_radius_attrset_copy(state->attrs, &rqst->attrs);
++ retval = krad_attrset_copy(state->attrs, &rqst->attrs);
+ if (retval != 0)
+ goto error;
+
-+ retval = k5_radius_attrset_add(rqst->attrs,
-+ k5_radius_attr_name2num("User-Password"),
-+ &req->otp_value);
++ retval = krad_attrset_add(rqst->attrs, krad_attr_name2num("User-Password"),
++ &req->otp_value);
+ if (retval != 0)
+ goto error;
+
@@ -1170,5 +1167,5 @@ index 0000000..89a164a
+
+#endif /* OTP_H_ */
--
-1.8.1.4
+1.8.2
diff --git a/krb5.spec b/krb5.spec
index 4ac3615..8129f4b 100644
--- a/krb5.spec
+++ b/krb5.spec
@@ -30,7 +30,7 @@
Summary: The Kerberos network authentication system
Name: krb5
Version: 1.11.1
-Release: 7%{?dist}
+Release: 8%{?dist}
# Maybe we should explode from the now-available-to-everybody tarball instead?
# http://web.mit.edu/kerberos/dist/krb5/1.11/krb5-1.11.1-signed.tar
Source0: krb5-%{version}.tar.gz
@@ -79,9 +79,11 @@ Patch116: http://ausil.fedorapeople.org/aarch64/krb5/krb5-aarch64.patch
Patch117: krb5-1.11-gss-client-keytab.patch
Patch118: krb5-1.11.1-rpcbind.patch
-Patch201: 0001-add-libk5radius.patch
-Patch202: 0002-Add-internal-KDC_DIR-macro.patch
-Patch203: 0003-add-otp-plugin.patch
+# Patch for otp plugin backport
+Patch201: 0001-add-k5memdup.patch
+Patch202: 0002-add-libkrad.patch
+Patch203: 0003-Add-internal-KDC_DIR-macro.patch
+Patch204: 0004-add-otp-plugin.patch
License: MIT
URL: http://web.mit.edu/kerberos/www/
@@ -299,9 +301,10 @@ ln -s NOTICE LICENSE
%patch117 -p1 -b .gss-client-keytab
%patch118 -p1 -b .rpcbind
-%patch201 -p1 -b .add-libk5radius
-%patch202 -p1 -b .add-internal-kdc_dir
-%patch203 -p1 -b .add-otp-plugin
+%patch201 -p1 -b .add-k5memdup
+%patch202 -p1 -b .add-libkrad
+%patch203 -p1 -b .add-internal-kdc_dir
+%patch204 -p1 -b .add-otp-plugin
# Take the execute bit off of documentation.
chmod -x doc/krb5-protocol/*.txt
@@ -747,7 +750,7 @@ exit 0
%{_libdir}/libkadm5clnt_mit.so.*
%{_libdir}/libkadm5srv_mit.so.*
%{_libdir}/libkdb5.so.*
-%{_libdir}/libk5radius.so.*
+%{_libdir}/libkrad.so.*
%if %{separate_usr}
/%{_lib}/libkrb5.so.*
/%{_lib}/libkrb5support.so.*
@@ -798,7 +801,7 @@ exit 0
%{_libdir}/libkadm5srv.so
%{_libdir}/libkadm5srv_mit.so
%{_libdir}/libkdb5.so
-%{_libdir}/libk5radius.so
+%{_libdir}/libkrad.so
%{_libdir}/libkrb5.so
%{_libdir}/libkrb5support.so
@@ -819,6 +822,9 @@ exit 0
%{_sbindir}/uuserver
%changelog
+* Tue Apr 09 2013 Nathaniel McCallum <npmccallum@redhat.com> - 1.11.1-8
+- Update otp backport patches (libk5radius => libkrad)
+
* Wed Apr 3 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.1-7
- when testing the RPC library, treat denials from the local portmapper the
same as a portmapper-not-running situation, to allow other library tests