From 8d291c8c0a03c8e87fe437169336d8603dd717a7 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Apr 09 2013 18:06:33 +0000 Subject: Update otp plugin backport patches --- 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 +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 -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 -+ -+#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 -+ -+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 *)# -+ 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 -+#include -+#include -+#include -+#include -+ -+#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 -+ -+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 -+ -+#include -+#include -+#include -+ -+/* 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 -+#include -+#include -+#include -+#include -+ -+#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 -+ -+#include -+ -+#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 -+ -+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 -+#include -+ -+#include -+ -+#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 -+ -+#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 -+ -+#include -+#include -+#include -+ -+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 -+#include -+#include -+#include -+#include -+#include -+ -+#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-internal-KDC_DIR-macro.patch b/0002-Add-internal-KDC_DIR-macro.patch deleted file mode 100644 index ec87eb2..0000000 --- a/0002-Add-internal-KDC_DIR-macro.patch +++ /dev/null @@ -1,66 +0,0 @@ -From 5caeecbc6753f526ccd620e29daed49973f8e21d Mon Sep 17 00:00:00 2001 -From: Greg Hudson -Date: Tue, 15 Jan 2013 11:11:27 -0500 -Subject: [PATCH 2/3] Add internal KDC_DIR macro - -Define KDC_DIR in osconf.hin and use it for paths within the KDC -directory. ---- - src/include/osconf.hin | 21 +++++++++++---------- - 1 file changed, 11 insertions(+), 10 deletions(-) - -diff --git a/src/include/osconf.hin b/src/include/osconf.hin -index c3a33c2..1bca991 100644 ---- a/src/include/osconf.hin -+++ b/src/include/osconf.hin -@@ -58,14 +58,15 @@ - #define DEFAULT_PLUGIN_BASE_DIR "@LIBDIR/krb5/plugins" - #define PLUGIN_EXT "@DYNOBJEXT" - --#define DEFAULT_KDB_FILE "@LOCALSTATEDIR/krb5kdc/principal" --#define DEFAULT_KEYFILE_STUB "@LOCALSTATEDIR/krb5kdc/.k5." --#define KRB5_DEFAULT_ADMIN_ACL "@LOCALSTATEDIR/krb5kdc/krb5_adm.acl" -+#define KDC_DIR "@LOCALSTATEDIR/krb5kdc" -+#define DEFAULT_KDB_FILE KDC_DIR "/principal" -+#define DEFAULT_KEYFILE_STUB KDC_DIR "/.k5." -+#define KRB5_DEFAULT_ADMIN_ACL KDC_DIR "/krb5_adm.acl" - /* Used by old admin server */ --#define DEFAULT_ADMIN_ACL "@LOCALSTATEDIR/krb5kdc/kadm_old.acl" -+#define DEFAULT_ADMIN_ACL KDC_DIR "/krb5kdc/kadm_old.acl" - - /* Location of KDC profile */ --#define DEFAULT_KDC_PROFILE "@LOCALSTATEDIR/krb5kdc/kdc.conf" -+#define DEFAULT_KDC_PROFILE KDC_DIR "/krb5kdc/kdc.conf" - #define KDC_PROFILE_ENV "KRB5_KDC_PROFILE" - - #if TARGET_OS_MAC -@@ -93,8 +94,8 @@ - /* - * Defaults for the KADM5 admin system. - */ --#define DEFAULT_KADM5_KEYTAB "@LOCALSTATEDIR/krb5kdc/kadm5.keytab" --#define DEFAULT_KADM5_ACL_FILE "@LOCALSTATEDIR/krb5kdc/kadm5.acl" -+#define DEFAULT_KADM5_KEYTAB KDC_DIR "/kadm5.keytab" -+#define DEFAULT_KADM5_ACL_FILE KDC_DIR "/kadm5.acl" - #define DEFAULT_KADM5_PORT 749 /* assigned by IANA */ - - #define KRB5_DEFAULT_SUPPORTED_ENCTYPES \ -@@ -116,12 +117,12 @@ - * krb5 slave support follows - */ - --#define KPROP_DEFAULT_FILE "@LOCALSTATEDIR/krb5kdc/slave_datatrans" --#define KPROPD_DEFAULT_FILE "@LOCALSTATEDIR/krb5kdc/from_master" -+#define KPROP_DEFAULT_FILE KDC_DIR "/slave_datatrans" -+#define KPROPD_DEFAULT_FILE KDC_DIR "/from_master" - #define KPROPD_DEFAULT_KDB5_UTIL "@SBINDIR/kdb5_util" - #define KPROPD_DEFAULT_KPROP "@SBINDIR/kprop" - #define KPROPD_DEFAULT_KRB_DB DEFAULT_KDB_FILE --#define KPROPD_ACL_FILE "@LOCALSTATEDIR/krb5kdc/kpropd.acl" -+#define KPROPD_ACL_FILE KDC_DIR "/kpropd.acl" - - /* - * GSS mechglue --- -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 +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 ++#include ++#include ++#include ++ ++#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 ++#include "internal.h" ++ ++#include ++ ++/* 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 ++#include ++#include "internal.h" ++ ++#include ++ ++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 ++#include "internal.h" ++ ++#include ++#include ++#include ++#include ++#include ++ ++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 ++ ++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 ++#include "krad.h" ++ ++#include ++ ++#include ++#include ++#include ++ ++#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 ++ ++#include ++ ++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 ++#include ++#include "internal.h" ++ ++#include ++#include ++ ++#include ++ ++#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 ++#include ++#include ++#include ++ ++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 ++#include ++#include ++#include ++#include ++#include ++ ++#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/0003-Add-internal-KDC_DIR-macro.patch b/0003-Add-internal-KDC_DIR-macro.patch new file mode 100644 index 0000000..7603264 --- /dev/null +++ b/0003-Add-internal-KDC_DIR-macro.patch @@ -0,0 +1,66 @@ +From a4a7a4aeb2fb96e36494faff46243fbcb3c0d78b Mon Sep 17 00:00:00 2001 +From: Greg Hudson +Date: Tue, 15 Jan 2013 11:11:27 -0500 +Subject: [PATCH 3/4] Add internal KDC_DIR macro + +Define KDC_DIR in osconf.hin and use it for paths within the KDC +directory. +--- + src/include/osconf.hin | 21 +++++++++++---------- + 1 file changed, 11 insertions(+), 10 deletions(-) + +diff --git a/src/include/osconf.hin b/src/include/osconf.hin +index c3a33c2..1bca991 100644 +--- a/src/include/osconf.hin ++++ b/src/include/osconf.hin +@@ -58,14 +58,15 @@ + #define DEFAULT_PLUGIN_BASE_DIR "@LIBDIR/krb5/plugins" + #define PLUGIN_EXT "@DYNOBJEXT" + +-#define DEFAULT_KDB_FILE "@LOCALSTATEDIR/krb5kdc/principal" +-#define DEFAULT_KEYFILE_STUB "@LOCALSTATEDIR/krb5kdc/.k5." +-#define KRB5_DEFAULT_ADMIN_ACL "@LOCALSTATEDIR/krb5kdc/krb5_adm.acl" ++#define KDC_DIR "@LOCALSTATEDIR/krb5kdc" ++#define DEFAULT_KDB_FILE KDC_DIR "/principal" ++#define DEFAULT_KEYFILE_STUB KDC_DIR "/.k5." ++#define KRB5_DEFAULT_ADMIN_ACL KDC_DIR "/krb5_adm.acl" + /* Used by old admin server */ +-#define DEFAULT_ADMIN_ACL "@LOCALSTATEDIR/krb5kdc/kadm_old.acl" ++#define DEFAULT_ADMIN_ACL KDC_DIR "/krb5kdc/kadm_old.acl" + + /* Location of KDC profile */ +-#define DEFAULT_KDC_PROFILE "@LOCALSTATEDIR/krb5kdc/kdc.conf" ++#define DEFAULT_KDC_PROFILE KDC_DIR "/krb5kdc/kdc.conf" + #define KDC_PROFILE_ENV "KRB5_KDC_PROFILE" + + #if TARGET_OS_MAC +@@ -93,8 +94,8 @@ + /* + * Defaults for the KADM5 admin system. + */ +-#define DEFAULT_KADM5_KEYTAB "@LOCALSTATEDIR/krb5kdc/kadm5.keytab" +-#define DEFAULT_KADM5_ACL_FILE "@LOCALSTATEDIR/krb5kdc/kadm5.acl" ++#define DEFAULT_KADM5_KEYTAB KDC_DIR "/kadm5.keytab" ++#define DEFAULT_KADM5_ACL_FILE KDC_DIR "/kadm5.acl" + #define DEFAULT_KADM5_PORT 749 /* assigned by IANA */ + + #define KRB5_DEFAULT_SUPPORTED_ENCTYPES \ +@@ -116,12 +117,12 @@ + * krb5 slave support follows + */ + +-#define KPROP_DEFAULT_FILE "@LOCALSTATEDIR/krb5kdc/slave_datatrans" +-#define KPROPD_DEFAULT_FILE "@LOCALSTATEDIR/krb5kdc/from_master" ++#define KPROP_DEFAULT_FILE KDC_DIR "/slave_datatrans" ++#define KPROPD_DEFAULT_FILE KDC_DIR "/from_master" + #define KPROPD_DEFAULT_KDB5_UTIL "@SBINDIR/kdb5_util" + #define KPROPD_DEFAULT_KPROP "@SBINDIR/kprop" + #define KPROPD_DEFAULT_KRB_DB DEFAULT_KDB_FILE +-#define KPROPD_ACL_FILE "@LOCALSTATEDIR/krb5kdc/kpropd.acl" ++#define KPROPD_ACL_FILE KDC_DIR "/kpropd.acl" + + /* + * GSS mechglue +-- +1.8.2 + diff --git a/0003-add-otp-plugin.patch b/0003-add-otp-plugin.patch deleted file mode 100644 index f957b9c..0000000 --- a/0003-add-otp-plugin.patch +++ /dev/null @@ -1,1174 +0,0 @@ -From 9c67d6fd21692d8bbfbe880511cbcbc5d9e6a2e5 Mon Sep 17 00:00:00 2001 -From: Nathaniel McCallum -Date: Fri, 8 Mar 2013 10:22:03 -0500 -Subject: [PATCH 3/3] add otp plugin - ---- - src/Makefile.in | 1 + - src/configure.in | 1 + - 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/otp.exports | 1 + - src/plugins/preauth/otp/otp_state.c | 571 ++++++++++++++++++++++++++++++++++++ - src/plugins/preauth/otp/otp_state.h | 58 ++++ - 9 files changed, 1079 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 - create mode 100644 src/plugins/preauth/otp/otp.exports - create mode 100644 src/plugins/preauth/otp/otp_state.c - create mode 100644 src/plugins/preauth/otp/otp_state.h - -diff --git a/src/Makefile.in b/src/Makefile.in -index 2c65831..0b9d355 100644 ---- a/src/Makefile.in -+++ b/src/Makefile.in -@@ -12,6 +12,7 @@ SUBDIRS=util include lib \ - plugins/kadm5_hook/test \ - plugins/kdb/db2 \ - @ldap_plugin_dir@ \ -+ plugins/preauth/otp \ - plugins/preauth/pkinit \ - 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 ---- 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 - plugins/kdb/db2/libdb2/test - plugins/kdb/hdb - plugins/preauth/cksum_body -+ plugins/preauth/otp - plugins/preauth/securid_sam2 - plugins/preauth/wpse - plugins/authdata/greet -diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c -index 42a37a8..afbf1f6 100644 ---- a/src/kdc/kdc_preauth.c -+++ b/src/kdc/kdc_preauth.c -@@ -238,6 +238,8 @@ get_plugin_vtables(krb5_context context, - /* Auto-register encrypted challenge and (if possible) pkinit. */ - k5_plugin_register_dyn(context, PLUGIN_INTERFACE_KDCPREAUTH, "pkinit", - "preauth"); -+ k5_plugin_register_dyn(context, PLUGIN_INTERFACE_KDCPREAUTH, "otp", -+ "preauth"); - k5_plugin_register(context, PLUGIN_INTERFACE_KDCPREAUTH, - "encrypted_challenge", - 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 ---- /dev/null -+++ b/src/plugins/preauth/otp/Makefile.in -@@ -0,0 +1,45 @@ -+mydir=plugins$(S)preauth$(S)otp -+BUILDTOP=$(REL)..$(S)..$(S).. -+KRB5_RUN_ENV = @KRB5_RUN_ENV@ -+KRB5_CONFIG_SETUP = KRB5_CONFIG=$(top_srcdir)/config-files/krb5.conf ; export KRB5_CONFIG ; -+PROG_LIBPATH=-L$(TOPLIBD) -+PROG_RPATH=$(KRB5_LIBDIR) -+MODULE_INSTALL_DIR = $(KRB5_PA_MODULE_DIR) -+DEFS=@DEFS@ -+ -+LOCALINCLUDES = -I../../../include/krb5 -I../../../include/ -+ -+LIBBASE=otp -+LIBMAJOR=0 -+LIBMINOR=0 -+SO_EXT=.so -+RELDIR=../plugins/preauth/otp -+# Depends on libk5crypto and libkrb5 -+SHLIB_EXPDEPS = \ -+ $(TOPLIBD)/libk5crypto$(SHLIBEXT) \ -+ $(TOPLIBD)/libkrb5$(SHLIBEXT) \ -+ $(TOPLIBD)/radius/libk5radius$(SHLIBEXT) -+ -+SHLIB_EXPLIBS= -lverto -lk5radius $(KRB5_LIB) $(K5CRYPTO_LIB) $(COM_ERR_LIB) $(SUPPORT_LIB) $(LIBS) -+ -+SHLIB_DIRS=-L$(TOPLIBD) -+SHLIB_RDIRS=$(KRB5_LIBDIR) -+STOBJLISTS=OBJS.ST -+STLIBOBJS = \ -+ otp_state.o \ -+ main.o -+ -+SRCS = \ -+ $(srcdir)/otp_state.c \ -+ $(srcdir)/main.c -+ -+all-unix:: $(LIBBASE)$(SO_EXT) -+install-unix:: install-libs -+clean-unix:: clean-libs clean-libobjs -+ -+clean:: -+ $(RM) lib$(LIBBASE)$(SO_EXT) -+ -+@libnover_frag@ -+@libobj_frag@ -+ -diff --git a/src/plugins/preauth/otp/deps b/src/plugins/preauth/otp/deps -new file mode 100644 -index 0000000..cf5f19f ---- /dev/null -+++ b/src/plugins/preauth/otp/deps -@@ -0,0 +1,26 @@ -+# -+# 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 -+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 \ -+ $(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 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 ---- /dev/null -+++ b/src/plugins/preauth/otp/main.c -@@ -0,0 +1,374 @@ -+/* -+ * Copyright 2011 NORDUnet A/S. All rights reserved. -+ * Copyright 2011 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-json.h" -+#include -+#include "otp_state.h" -+ -+#include -+#include -+ -+static krb5_preauthtype otp_pa_type_list[] = -+ { KRB5_PADATA_OTP_REQUEST, 0 }; -+ -+struct request_state { -+ krb5_kdcpreauth_verify_respond_fn respond; -+ void *arg; -+}; -+ -+static krb5_error_code -+decrypt_encdata(krb5_context context, krb5_keyblock *armor_key, -+ krb5_pa_otp_req *req, krb5_data *out) -+{ -+ krb5_error_code retval; -+ krb5_data tmp; -+ -+ if (!req) -+ return EINVAL; -+ -+ tmp.length = req->enc_data.ciphertext.length; -+ tmp.data = calloc(tmp.length, sizeof(char)); -+ if (!tmp.data) -+ return ENOMEM; -+ -+ retval = krb5_c_decrypt(context, armor_key, KRB5_KEYUSAGE_PA_OTP_REQUEST, -+ NULL, &req->enc_data, &tmp); -+ if (retval != 0) { -+ DEBUGMSG(retval, "Unable to decrypt encData in PA-OTP-REQUEST."); -+ free(tmp.data); -+ return retval; -+ } -+ -+ *out = tmp; -+ return 0; -+} -+ -+static krb5_error_code -+nonce_verify(krb5_context ctx, krb5_keyblock *armor_key, -+ const krb5_data *nonce) -+{ -+ krb5_error_code retval = EINVAL; -+ krb5_timestamp ts; -+ krb5_data *er = NULL; -+ -+ if (armor_key == NULL || nonce->data == NULL) -+ goto out; -+ -+ /* Decode the PA-OTP-ENC-REQUEST structure */ -+ retval = decode_krb5_pa_otp_enc_req(nonce, &er); -+ if (retval != 0) -+ goto out; -+ -+ /* Make sure the nonce is exactly the same size as the one generated */ -+ if (er->length != armor_key->length + sizeof(krb5_timestamp)) -+ goto out; -+ -+ /* Check to make sure the timestamp at the beginning is still valid */ -+ ts = ntohl(((krb5_timestamp *)er->data)[0]); -+ retval = krb5_check_clockskew(ctx, ts); -+ -+out: -+ krb5_free_data(ctx, er); -+ return retval; -+} -+ -+static krb5_error_code -+timestamp_verify(krb5_context ctx, const krb5_data *nonce) -+{ -+ krb5_error_code retval = EINVAL; -+ krb5_pa_enc_ts *et = NULL; -+ -+ if (nonce->data == NULL) -+ goto out; -+ -+ /* Decode the PA-ENC-TS-ENC structure */ -+ retval = decode_krb5_pa_enc_ts(nonce, &et); -+ if (retval != 0) -+ goto out; -+ -+ /* Check the clockskew */ -+ retval = krb5_check_clockskew(ctx, et->patimestamp); -+ -+out: -+ krb5_free_pa_enc_ts(ctx, et); -+ return retval; -+} -+ -+static krb5_error_code -+nonce_generate(krb5_context ctx, unsigned int length, krb5_data *nonce) -+{ -+ krb5_data tmp; -+ krb5_error_code retval; -+ krb5_timestamp time; -+ -+ retval = krb5_timeofday(ctx, &time); -+ if (retval != 0) -+ return retval; -+ -+ tmp.length = length + sizeof(time); -+ tmp.data = (char *)malloc(tmp.length); -+ if (!tmp.data) -+ return ENOMEM; -+ -+ retval = krb5_c_random_make_octets(ctx, &tmp); -+ if (retval != 0) { -+ free(tmp.data); -+ return retval; -+ } -+ -+ *((krb5_timestamp *)tmp.data) = htonl(time); -+ *nonce = tmp; -+ return 0; -+} -+ -+static void -+on_response(krb5_error_code retval, otp_response response, void *data) -+{ -+ struct request_state rs = *(struct request_state *)data; -+ -+ free(data); -+ -+ if (retval == 0 && response != otp_response_success) -+ retval = KRB5_PREAUTH_FAILED; -+ -+ (*rs.respond)(rs.arg, retval, NULL, NULL, NULL); -+} -+ -+static krb5_error_code -+otp_init(krb5_context context, krb5_kdcpreauth_moddata *moddata_out, -+ const char **realmnames) -+{ -+ return otp_state_new(context, (otp_state **)moddata_out); -+} -+ -+static void -+otp_fini(krb5_context context, krb5_kdcpreauth_moddata moddata) -+{ -+ otp_state_free((otp_state *)moddata); -+} -+ -+static int -+otp_flags(krb5_context context, krb5_preauthtype pa_type) -+{ -+ return PA_REPLACES_KEY; -+} -+ -+static void -+otp_edata(krb5_context context, krb5_kdc_req *request, -+ krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, -+ krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type, -+ krb5_kdcpreauth_edata_respond_fn respond, void *arg) -+{ -+ krb5_otp_tokeninfo ti, *tis[2] = { &ti, NULL }; -+ krb5_keyblock *armor_key = NULL; -+ krb5_pa_otp_challenge chl; -+ krb5_pa_data *pa = NULL; -+ krb5_error_code retval; -+ krb5_data *tmp = NULL; -+ char *config; -+ -+ /* Determine if otp is enabled for the user. */ -+ retval = cb->get_string(context, rock, "otp", &config); -+ if (retval != 0 || config == NULL) -+ goto out; -+ cb->free_string(context, rock, config); -+ -+ /* Get the armor key. -+ * This indicates the length of random data to use in the nonce. */ -+ armor_key = cb->fast_armor(context, rock); -+ if (armor_key == NULL) { -+ retval = EINVAL; -+ goto out; -+ } -+ -+ /* Build the (mostly empty) challenge. */ -+ memset(&ti, 0, sizeof(ti)); -+ memset(&chl, 0, sizeof(chl)); -+ chl.tokeninfo = tis; -+ ti.format = -1; -+ ti.length = -1; -+ ti.iteration_count = -1; -+ -+ /* Generate the nonce. */ -+ retval = nonce_generate(context, armor_key->length, &chl.nonce); -+ if (retval != 0) -+ goto out; -+ -+ /* Build the output pa data. */ -+ pa = calloc(1, sizeof(krb5_pa_data)); -+ if (pa) { -+ retval = encode_krb5_pa_otp_challenge(&chl, &tmp); -+ if (retval != 0) { -+ DEBUGMSG(ENOMEM, "Unable to encode challenge."); -+ free(pa); -+ pa = NULL; -+ } -+ -+ pa->pa_type = KRB5_PADATA_OTP_CHALLENGE; -+ pa->contents = (krb5_octet *)tmp->data; -+ pa->length = tmp->length; -+ free(tmp); /* Is there a better way to steal the data contents? */ -+ } else { -+ retval = ENOMEM; -+ } -+ -+out: -+ (*respond)(arg, retval, pa); -+ return; -+} -+ -+static void -+otp_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request, -+ krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *data, -+ krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, -+ krb5_kdcpreauth_moddata moddata, -+ krb5_kdcpreauth_verify_respond_fn respond, void *arg) -+{ -+ krb5_keyblock *armor_key = NULL; -+ krb5_pa_otp_req *req = NULL; -+ struct request_state *rs; -+ krb5_error_code retval; -+ krb5_data tmp; -+ char *config; -+ -+ enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH; -+ -+ /* Get the FAST armor key */ -+ armor_key = cb->fast_armor(context, rock); -+ if (armor_key == NULL) { -+ retval = KRB5KDC_ERR_PREAUTH_FAILED; -+ DEBUGMSG(retval, "No armor key found when verifying padata."); -+ goto error; -+ } -+ -+ /* Decode the request */ -+ tmp = make_data(data->contents, data->length); -+ retval = decode_krb5_pa_otp_req(&tmp, &req); -+ if (retval != 0) { -+ DEBUGMSG(retval, "Unable to decode OTP request."); -+ goto error; -+ } -+ -+ /* Decrypt the nonce from the request */ -+ retval = decrypt_encdata(context, armor_key, req, &tmp); -+ if (retval != 0) { -+ DEBUGMSG(retval, "Unable to decrypt encData."); -+ goto error; -+ } -+ -+ /* Verify the nonce or timestamp */ -+ retval = nonce_verify(context, armor_key, &tmp); -+ if (retval != 0) -+ retval = timestamp_verify(context, &tmp); -+ krb5_free_data_contents(context, &tmp); -+ if (retval != 0) { -+ DEBUGMSG(retval, "Unable to verify nonce or timestamp."); -+ goto error; -+ } -+ -+ /* Create the request state. */ -+ rs = malloc(sizeof(struct request_state)); -+ if (rs == NULL) { -+ retval = ENOMEM; -+ goto error; -+ } -+ rs->arg = arg; -+ rs->respond = respond; -+ -+ /* Get the configuration string. */ -+ retval = cb->get_string(context, rock, "otp", &config); -+ if (retval != 0 || config == NULL) { -+ if (config == NULL) -+ retval = KRB5_PREAUTH_FAILED; -+ free(rs); -+ goto error; -+ } -+ -+ /* Send the request. */ -+ otp_state_verify((otp_state *)moddata, -+ (*cb->event_context)(context, rock), -+ request->client, config, req, on_response, rs); -+ cb->free_string(context, rock, config); -+ -+ k5_free_pa_otp_req(context, req); -+ return; -+ -+error: -+ k5_free_pa_otp_req(context, req); -+ (*respond)(arg, retval, NULL, NULL, NULL); -+} -+ -+static krb5_error_code -+otp_return_padata(krb5_context context, krb5_pa_data *padata, -+ krb5_data *req_pkt, krb5_kdc_req *request, -+ krb5_kdc_rep *reply, krb5_keyblock *encrypting_key, -+ krb5_pa_data **send_pa_out, krb5_kdcpreauth_callbacks cb, -+ krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata, -+ krb5_kdcpreauth_modreq modreq) -+{ -+ krb5_keyblock *armor_key = NULL; -+ -+ 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; -+ } -+ -+ /* 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; -+ -+ 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; -+ -+ com_err("otp", 0, "Loaded."); -+ -+ return 0; -+} -diff --git a/src/plugins/preauth/otp/otp.exports b/src/plugins/preauth/otp/otp.exports -new file mode 100644 -index 0000000..26aa19d ---- /dev/null -+++ b/src/plugins/preauth/otp/otp.exports -@@ -0,0 +1 @@ -+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 ---- /dev/null -+++ b/src/plugins/preauth/otp/otp_state.c -@@ -0,0 +1,571 @@ -+/* -+ * 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 "otp_state.h" -+ -+#include -+#include -+ -+#include -+ -+#ifndef HOST_NAME_MAX -+/* SUSv2 */ -+#define HOST_NAME_MAX 255 -+#endif -+ -+typedef struct token_type_ { -+ char *name; -+ char *server; -+ char *secret; -+ time_t timeout; -+ size_t retries; -+ krb5_boolean strip_realm; -+} token_type; -+ -+typedef struct token_ { -+ const token_type *type; -+ krb5_data username; -+} token; -+ -+typedef struct request_ { -+ otp_state *state; -+ token *tokens; -+ ssize_t index; -+ otp_cb *cb; -+ void *data; -+ k5_radius_attrset *attrs; -+} request; -+ -+struct otp_state_ { -+ krb5_context ctx; -+ token_type *types; -+ k5_radius_client *radius; -+ k5_radius_attrset *attrs; -+}; -+ -+static inline krb5_data -+string2data_copy(const char *s) -+{ -+ char *tmp; -+ -+ tmp = strdup(s); -+ return make_data(NULL, tmp == NULL ? 0 : strlen(tmp)); -+} -+ -+/* Free a NULL-terminated array of strings. */ -+static void -+stringv_free(char **strv) -+{ -+ size_t i; -+ -+ if (strv == NULL) -+ return; -+ -+ for (i = 0; strv[i] != NULL; i++) -+ free(strv[i]); -+ -+ free(strv); -+} -+ -+/* Free the contents of a single token type. */ -+static void -+token_type_free(token_type *type) -+{ -+ if (type == NULL) -+ return; -+ -+ free(type->name); -+ free(type->server); -+ free(type->secret); -+} -+ -+/* Decode a single token type from the profile. */ -+static krb5_error_code -+token_type_decode(profile_t profile, const char *name, token_type *out) -+{ -+ krb5_error_code retval; -+ char *defsrv = NULL; -+ token_type tt; -+ int tmp; -+ -+ memset(&tt, 0, sizeof(tt)); -+ -+ /* Set the name. */ -+ tt.name = strdup(name == NULL ? "DEFAULT" : name); -+ if (tt.name == NULL) { -+ retval = ENOMEM; -+ goto error; -+ } -+ -+ /* Set defaults. */ -+ tt.timeout = 5000; -+ tt.retries = 3; -+ if (asprintf(&defsrv, "%s/%s.socket", KDC_DIR, tt.name) < 0) { -+ retval = ENOMEM; -+ goto error; -+ } -+ -+ /* Set the internal default. */ -+ if (name == NULL) { -+ retval = ENOMEM; -+ -+ tt.secret = strdup(""); -+ if (tt.secret == NULL) -+ goto error; -+ -+ tt.server = defsrv; -+ tt.strip_realm = FALSE; -+ -+ *out = tt; -+ return 0; -+ } -+ -+ /* Set strip_realm. */ -+ retval = profile_get_boolean(profile, "otp", name, "strip_realm", TRUE, -+ &tmp); -+ if (retval != 0) -+ goto error; -+ tt.strip_realm = tmp == 0 ? FALSE : TRUE; -+ -+ /* Set the server. */ -+ retval = profile_get_string(profile, "otp", name, "server", -+ defsrv, &tt.server); -+ if (retval != 0) -+ goto error; -+ -+ /* Set the secret. */ -+ retval = profile_get_string(profile, "otp", name, "secret", -+ tt.server[0] == '/' ? "" : NULL, -+ &tt.server); -+ if (retval != 0) { -+ goto error; -+ } else if (tt.secret == NULL) { -+ DEBUGMSG(EINVAL, "Secret not specified in token type '%s'.", name); -+ retval = EINVAL; -+ goto error; -+ } -+ -+ /* Set the timeout. */ -+ retval = profile_get_integer(profile, "otp", name, "timeout", -+ tt.timeout / 1000, &tmp); -+ if (retval != 0) -+ goto error; -+ tt.timeout = tmp * 1000; /* Convert to milliseconds. */ -+ -+ /* Set the retries. */ -+ retval = profile_get_integer(profile, "otp", name, "retries", -+ tt.retries, &tmp); -+ if (retval != 0) -+ goto error; -+ tt.retries = tmp; -+ -+ *out = tt; -+ free(defsrv); -+ return 0; -+ -+error: -+ token_type_free(&tt); -+ free(defsrv); -+ return retval; -+} -+ -+/* Free an array of token types. */ -+static void -+token_types_free(token_type *types) -+{ -+ size_t i; -+ -+ if (types == NULL) -+ return; -+ -+ for (i = 0; types[i].server != NULL; i++) -+ token_type_free(&types[i]); -+ -+ free(types); -+} -+ -+/* Decode an array of token types from the profile. */ -+static krb5_error_code -+token_types_decode(profile_t profile, token_type **out) -+{ -+ const char *tmp[2] = { "otp", NULL }; -+ token_type *types = NULL; -+ char **names = NULL; -+ errcode_t retval; -+ ssize_t i, j; -+ -+ retval = profile_get_subsection_names(profile, tmp, &names); -+ if (retval != 0) -+ return retval; -+ -+ for (i = 0, j = 0; names[i] != NULL; i++) { -+ if (strcmp(names[i], "DEFAULT") == 0) -+ j = 1; -+ } -+ -+ types = calloc(i - j + 2, sizeof(token_type)); -+ if (types == NULL) { -+ retval = ENOMEM; -+ goto error; -+ } -+ -+ /* If no default has been specified, use our internal default. */ -+ if (j == 0) { -+ retval = token_type_decode(profile, NULL, &types[j++]); -+ if (retval != 0) -+ goto error; -+ } else { -+ j = 0; -+ } -+ -+ for (i = 0; names[i] != NULL; i++) { -+ retval = token_type_decode(profile, names[i], &types[j++]); -+ if (retval != 0) -+ goto error; -+ } -+ -+ stringv_free(names); -+ *out = types; -+ return 0; -+ -+error: -+ token_types_free(types); -+ stringv_free(names); -+ return retval; -+} -+ -+/* Free the contents of a single token. */ -+static void -+token_free(token *t) -+{ -+ if (t == NULL) -+ return; -+ -+ free(t->username.data); -+} -+ -+/* Decode a single token from a JSON token object. */ -+static krb5_error_code -+token_decode(krb5_context ctx, krb5_const_principal princ, -+ const token_type *types, k5_json_object obj, token *out) -+{ -+ const char *type = NULL; -+ krb5_error_code retval; -+ k5_json_value tmp; -+ size_t i; -+ token t; -+ -+ memset(&t, 0, sizeof(t)); -+ -+ tmp = k5_json_object_get(obj, "username"); -+ if (tmp != NULL && k5_json_get_tid(tmp) == K5_JSON_TID_STRING) { -+ t.username = string2data_copy(k5_json_string_utf8(tmp)); -+ if (t.username.data == NULL) -+ return ENOMEM; -+ } -+ -+ tmp = k5_json_object_get(obj, "type"); -+ if (tmp != NULL && k5_json_get_tid(tmp) == K5_JSON_TID_STRING) -+ type = k5_json_string_utf8(tmp); -+ -+ for (i = 0; types[i].server != NULL; i++) { -+ if (strcmp(type == NULL ? "DEFAULT" : type, types[i].name) == 0) -+ t.type = &types[i]; -+ } -+ -+ if (t.username.data == NULL) { -+ retval = krb5_unparse_name_flags(ctx, princ, -+ t.type->strip_realm -+ ? KRB5_PRINCIPAL_UNPARSE_NO_REALM -+ : 0, -+ &t.username.data); -+ if (retval != 0) -+ return retval; -+ t.username.length = strlen(t.username.data); -+ } -+ -+ *out = t; -+ return 0; -+} -+ -+/* Free an array of tokens. */ -+static void -+tokens_free(token *tokens) -+{ -+ size_t i; -+ -+ if (tokens == NULL) -+ return; -+ -+ for (i = 0; tokens[i].type != NULL; i++) -+ token_free(&tokens[i]); -+ -+ free(tokens); -+} -+ -+/* Decode an array of tokens from the configuration string. */ -+static krb5_error_code -+tokens_decode(krb5_context ctx, krb5_const_principal princ, -+ const token_type *types, const char *config, token **out) -+{ -+ krb5_error_code retval; -+ k5_json_value arr, obj; -+ token *tokens; -+ ssize_t len, i, j; -+ -+ if (config == NULL) -+ config = "[{}]"; -+ -+ arr = k5_json_decode(config); -+ if (arr == NULL) -+ return ENOMEM; -+ -+ if (k5_json_get_tid(arr) != K5_JSON_TID_ARRAY || -+ (len = k5_json_array_length(arr)) == 0) { -+ k5_json_release(arr); -+ -+ arr = k5_json_decode("[{}]"); -+ if (arr == NULL) -+ return ENOMEM; -+ -+ if (k5_json_get_tid(arr) != K5_JSON_TID_ARRAY) { -+ k5_json_release(arr); -+ return ENOMEM; -+ } -+ -+ len = k5_json_array_length(arr); -+ } -+ -+ tokens = calloc(len + 1, sizeof(token)); -+ if (tokens == NULL) { -+ k5_json_release(arr); -+ return ENOMEM; -+ } -+ -+ for (i = 0, j = 0; i < len; i++) { -+ obj = k5_json_array_get(arr, i); -+ if (k5_json_get_tid(obj) != K5_JSON_TID_OBJECT) -+ continue; -+ -+ retval = token_decode(ctx, princ, types, obj, &tokens[j++]); -+ if (retval != 0) { -+ k5_json_release(arr); -+ while (--j > 0) -+ token_free(&tokens[j]); -+ free(tokens); -+ return retval; -+ } -+ } -+ -+ k5_json_release(arr); -+ *out = tokens; -+ return 0; -+} -+ -+static void -+request_free(request *req) -+{ -+ if (req == NULL) -+ return; -+ -+ k5_radius_attrset_free(req->attrs); -+ tokens_free(req->tokens); -+ free(req); -+} -+ -+krb5_error_code -+otp_state_new(krb5_context ctx, otp_state **out) -+{ -+ char hostname[HOST_NAME_MAX + 1]; -+ krb5_error_code retval; -+ profile_t profile; -+ krb5_data hndata; -+ otp_state *self; -+ -+ retval = gethostname(hostname, sizeof(hostname)); -+ if (retval != 0) -+ return retval; -+ -+ self = calloc(1, sizeof(otp_state)); -+ if (self == NULL) -+ return ENOMEM; -+ -+ retval = krb5_get_profile(ctx, &profile); -+ if (retval != 0) -+ goto error; -+ -+ retval = token_types_decode(profile, &self->types); -+ profile_abandon(profile); -+ if (retval != 0) -+ goto error; -+ -+ retval = k5_radius_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); -+ 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); -+ if (retval != 0) -+ goto error; -+ -+ self->ctx = ctx; -+ *out = self; -+ return 0; -+ -+error: -+ otp_state_free(self); -+ return retval; -+} -+ -+void -+otp_state_free(otp_state *self) -+{ -+ if (self == NULL) -+ return; -+ -+ k5_radius_attrset_free(self->attrs); -+ token_types_free(self->types); -+ free(self); -+} -+ -+static void -+request_send(request *req); -+ -+static void -+callback(krb5_error_code retval, const k5_radius_packet *rqst, -+ const k5_radius_packet *resp, void *data) -+{ -+ request *req = data; -+ -+ req->index++; -+ -+ if (retval != 0) -+ goto error; -+ -+ /* If we received an accept packet, success! */ -+ if (k5_radius_packet_get_code(resp) == -+ k5_radius_code_name2num("Access-Accept")) { -+ (*req->cb)(retval, otp_response_success, req->data); -+ request_free(req); -+ return; -+ } -+ -+ /* If we have no more tokens to try, failure! */ -+ if (req->tokens[req->index].type == NULL) -+ goto error; -+ -+ /* Try the next token. */ -+ request_send(req); -+ -+error: -+ (*req->cb)(retval, otp_response_fail, req->data); -+ request_free(req); -+} -+ -+static void -+request_send(request *req) -+{ -+ krb5_error_code retval; -+ -+ retval = k5_radius_attrset_add(req->attrs, -+ k5_radius_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); -+ if (retval != 0) -+ goto error; -+ -+ return; -+ -+error: -+ (*req->cb)(retval, otp_response_fail, req->data); -+ request_free(req); -+} -+ -+void -+otp_state_verify(otp_state *state, verto_ctx *ctx, krb5_const_principal princ, -+ const char *config, const krb5_pa_otp_req *req, -+ otp_cb *cb, void *data) -+{ -+ krb5_error_code retval; -+ request *rqst = NULL; -+ -+ if (state->radius == NULL) { -+ retval = k5_radius_client_new(state->ctx, ctx, &state->radius); -+ if (retval != 0) -+ goto error; -+ } -+ -+ rqst = calloc(1, sizeof(request)); -+ if (rqst == NULL) { -+ (*cb)(ENOMEM, otp_response_fail, data); -+ return; -+ } -+ rqst->state = state; -+ rqst->data = data; -+ rqst->cb = cb; -+ -+ retval = k5_radius_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); -+ if (retval != 0) -+ goto error; -+ -+ retval = tokens_decode(state->ctx, princ, state->types, config, -+ &rqst->tokens); -+ if (retval != 0) -+ goto error; -+ -+ request_send(rqst); -+ return; -+ -+error: -+ (*cb)(retval, otp_response_fail, data); -+ request_free(rqst); -+} -diff --git a/src/plugins/preauth/otp/otp_state.h b/src/plugins/preauth/otp/otp_state.h -new file mode 100644 -index 0000000..89a164a ---- /dev/null -+++ b/src/plugins/preauth/otp/otp_state.h -@@ -0,0 +1,58 @@ -+/* -+ * 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 OTP_H_ -+#define OTP_H_ -+ -+#include -+#include -+ -+#include -+#define DEBUGMSG(code, ...) com_err("otp", code, __VA_ARGS__) -+ -+typedef enum otp_response_ { -+ otp_response_fail = 0, -+ otp_response_success -+ /* Other values reserved for responses like next token or new pin. */ -+} otp_response; -+ -+typedef struct otp_state_ otp_state; -+typedef void -+(otp_cb)(krb5_error_code retval, otp_response response, void *data); -+ -+krb5_error_code -+otp_state_new(krb5_context ctx, otp_state **self); -+ -+void -+otp_state_free(otp_state *self); -+ -+void -+otp_state_verify(otp_state *state, verto_ctx *ctx, krb5_const_principal princ, -+ const char *config, const krb5_pa_otp_req *request, -+ otp_cb *cb, void *data); -+ -+#endif /* OTP_H_ */ --- -1.8.1.4 - diff --git a/0004-add-otp-plugin.patch b/0004-add-otp-plugin.patch new file mode 100644 index 0000000..23bf218 --- /dev/null +++ b/0004-add-otp-plugin.patch @@ -0,0 +1,1171 @@ +From 10e4dcdbb8856c66f9000d0c737e4eb9312aa021 Mon Sep 17 00:00:00 2001 +From: Nathaniel McCallum +Date: Tue, 9 Apr 2013 12:24:47 -0400 +Subject: [PATCH 4/4] add otp plugin + +--- + src/Makefile.in | 1 + + src/configure.in | 1 + + 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/otp.exports | 1 + + src/plugins/preauth/otp/otp_state.c | 568 ++++++++++++++++++++++++++++++++++++ + src/plugins/preauth/otp/otp_state.h | 58 ++++ + 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 + create mode 100644 src/plugins/preauth/otp/otp.exports + create mode 100644 src/plugins/preauth/otp/otp_state.c + create mode 100644 src/plugins/preauth/otp/otp_state.h + +diff --git a/src/Makefile.in b/src/Makefile.in +index 2c65831..0b9d355 100644 +--- a/src/Makefile.in ++++ b/src/Makefile.in +@@ -12,6 +12,7 @@ SUBDIRS=util include lib \ + plugins/kadm5_hook/test \ + plugins/kdb/db2 \ + @ldap_plugin_dir@ \ ++ plugins/preauth/otp \ + plugins/preauth/pkinit \ + kdc kadmin slave clients appl tests \ + config-files man doc @po@ +diff --git a/src/configure.in b/src/configure.in +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 + plugins/kdb/db2/libdb2/test + plugins/kdb/hdb + plugins/preauth/cksum_body ++ plugins/preauth/otp + plugins/preauth/securid_sam2 + plugins/preauth/wpse + plugins/authdata/greet +diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c +index 42a37a8..afbf1f6 100644 +--- a/src/kdc/kdc_preauth.c ++++ b/src/kdc/kdc_preauth.c +@@ -238,6 +238,8 @@ get_plugin_vtables(krb5_context context, + /* Auto-register encrypted challenge and (if possible) pkinit. */ + k5_plugin_register_dyn(context, PLUGIN_INTERFACE_KDCPREAUTH, "pkinit", + "preauth"); ++ k5_plugin_register_dyn(context, PLUGIN_INTERFACE_KDCPREAUTH, "otp", ++ "preauth"); + k5_plugin_register(context, PLUGIN_INTERFACE_KDCPREAUTH, + "encrypted_challenge", + 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..62fa432 +--- /dev/null ++++ b/src/plugins/preauth/otp/Makefile.in +@@ -0,0 +1,45 @@ ++mydir=plugins$(S)preauth$(S)otp ++BUILDTOP=$(REL)..$(S)..$(S).. ++KRB5_RUN_ENV = @KRB5_RUN_ENV@ ++KRB5_CONFIG_SETUP = KRB5_CONFIG=$(top_srcdir)/config-files/krb5.conf ; export KRB5_CONFIG ; ++PROG_LIBPATH=-L$(TOPLIBD) ++PROG_RPATH=$(KRB5_LIBDIR) ++MODULE_INSTALL_DIR = $(KRB5_PA_MODULE_DIR) ++DEFS=@DEFS@ ++ ++LOCALINCLUDES = -I../../../include/krb5 -I../../../include/ ++ ++LIBBASE=otp ++LIBMAJOR=0 ++LIBMINOR=0 ++SO_EXT=.so ++RELDIR=../plugins/preauth/otp ++# Depends on libk5crypto and libkrb5 ++SHLIB_EXPDEPS = \ ++ $(TOPLIBD)/libk5crypto$(SHLIBEXT) \ ++ $(TOPLIBD)/libkrb5$(SHLIBEXT) \ ++ $(TOPLIBD)/krad/libkrad$(SHLIBEXT) ++ ++SHLIB_EXPLIBS= -lverto -lkrad $(KRB5_LIB) $(K5CRYPTO_LIB) $(COM_ERR_LIB) $(SUPPORT_LIB) $(LIBS) ++ ++SHLIB_DIRS=-L$(TOPLIBD) ++SHLIB_RDIRS=$(KRB5_LIBDIR) ++STOBJLISTS=OBJS.ST ++STLIBOBJS = \ ++ otp_state.o \ ++ main.o ++ ++SRCS = \ ++ $(srcdir)/otp_state.c \ ++ $(srcdir)/main.c ++ ++all-unix:: $(LIBBASE)$(SO_EXT) ++install-unix:: install-libs ++clean-unix:: clean-libs clean-libobjs ++ ++clean:: ++ $(RM) lib$(LIBBASE)$(SO_EXT) ++ ++@libnover_frag@ ++@libobj_frag@ ++ +diff --git a/src/plugins/preauth/otp/deps b/src/plugins/preauth/otp/deps +new file mode 100644 +index 0000000..3352126 +--- /dev/null ++++ b/src/plugins/preauth/otp/deps +@@ -0,0 +1,26 @@ ++# ++# Generated makefile dependencies follow. ++# ++otp_state.so otp_state.po $(OUTPRE)otp_state.$(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 $(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 \ ++ $(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 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..8b48e1b +--- /dev/null ++++ b/src/plugins/preauth/otp/main.c +@@ -0,0 +1,374 @@ ++/* ++ * Copyright 2011 NORDUnet A/S. All rights reserved. ++ * Copyright 2011 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-json.h" ++#include ++#include "otp_state.h" ++ ++#include ++#include ++ ++static krb5_preauthtype otp_pa_type_list[] = ++ { KRB5_PADATA_OTP_REQUEST, 0 }; ++ ++struct request_state { ++ krb5_kdcpreauth_verify_respond_fn respond; ++ void *arg; ++}; ++ ++static krb5_error_code ++decrypt_encdata(krb5_context context, krb5_keyblock *armor_key, ++ krb5_pa_otp_req *req, krb5_data *out) ++{ ++ krb5_error_code retval; ++ krb5_data tmp; ++ ++ if (!req) ++ return EINVAL; ++ ++ tmp.length = req->enc_data.ciphertext.length; ++ tmp.data = calloc(tmp.length, sizeof(char)); ++ if (!tmp.data) ++ return ENOMEM; ++ ++ retval = krb5_c_decrypt(context, armor_key, KRB5_KEYUSAGE_PA_OTP_REQUEST, ++ NULL, &req->enc_data, &tmp); ++ if (retval != 0) { ++ DEBUGMSG(retval, "Unable to decrypt encData in PA-OTP-REQUEST."); ++ free(tmp.data); ++ return retval; ++ } ++ ++ *out = tmp; ++ return 0; ++} ++ ++static krb5_error_code ++nonce_verify(krb5_context ctx, krb5_keyblock *armor_key, ++ const krb5_data *nonce) ++{ ++ krb5_error_code retval = EINVAL; ++ krb5_timestamp ts; ++ krb5_data *er = NULL; ++ ++ if (armor_key == NULL || nonce->data == NULL) ++ goto out; ++ ++ /* Decode the PA-OTP-ENC-REQUEST structure */ ++ retval = decode_krb5_pa_otp_enc_req(nonce, &er); ++ if (retval != 0) ++ goto out; ++ ++ /* Make sure the nonce is exactly the same size as the one generated */ ++ if (er->length != armor_key->length + sizeof(krb5_timestamp)) ++ goto out; ++ ++ /* Check to make sure the timestamp at the beginning is still valid */ ++ ts = ntohl(((krb5_timestamp *)er->data)[0]); ++ retval = krb5_check_clockskew(ctx, ts); ++ ++out: ++ krb5_free_data(ctx, er); ++ return retval; ++} ++ ++static krb5_error_code ++timestamp_verify(krb5_context ctx, const krb5_data *nonce) ++{ ++ krb5_error_code retval = EINVAL; ++ krb5_pa_enc_ts *et = NULL; ++ ++ if (nonce->data == NULL) ++ goto out; ++ ++ /* Decode the PA-ENC-TS-ENC structure */ ++ retval = decode_krb5_pa_enc_ts(nonce, &et); ++ if (retval != 0) ++ goto out; ++ ++ /* Check the clockskew */ ++ retval = krb5_check_clockskew(ctx, et->patimestamp); ++ ++out: ++ krb5_free_pa_enc_ts(ctx, et); ++ return retval; ++} ++ ++static krb5_error_code ++nonce_generate(krb5_context ctx, unsigned int length, krb5_data *nonce) ++{ ++ krb5_data tmp; ++ krb5_error_code retval; ++ krb5_timestamp time; ++ ++ retval = krb5_timeofday(ctx, &time); ++ if (retval != 0) ++ return retval; ++ ++ tmp.length = length + sizeof(time); ++ tmp.data = (char *)malloc(tmp.length); ++ if (!tmp.data) ++ return ENOMEM; ++ ++ retval = krb5_c_random_make_octets(ctx, &tmp); ++ if (retval != 0) { ++ free(tmp.data); ++ return retval; ++ } ++ ++ *((krb5_timestamp *)tmp.data) = htonl(time); ++ *nonce = tmp; ++ return 0; ++} ++ ++static void ++on_response(krb5_error_code retval, otp_response response, void *data) ++{ ++ struct request_state rs = *(struct request_state *)data; ++ ++ free(data); ++ ++ if (retval == 0 && response != otp_response_success) ++ retval = KRB5_PREAUTH_FAILED; ++ ++ (*rs.respond)(rs.arg, retval, NULL, NULL, NULL); ++} ++ ++static krb5_error_code ++otp_init(krb5_context context, krb5_kdcpreauth_moddata *moddata_out, ++ const char **realmnames) ++{ ++ return otp_state_new(context, (otp_state **)moddata_out); ++} ++ ++static void ++otp_fini(krb5_context context, krb5_kdcpreauth_moddata moddata) ++{ ++ otp_state_free((otp_state *)moddata); ++} ++ ++static int ++otp_flags(krb5_context context, krb5_preauthtype pa_type) ++{ ++ return PA_REPLACES_KEY; ++} ++ ++static void ++otp_edata(krb5_context context, krb5_kdc_req *request, ++ krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, ++ krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type, ++ krb5_kdcpreauth_edata_respond_fn respond, void *arg) ++{ ++ krb5_otp_tokeninfo ti, *tis[2] = { &ti, NULL }; ++ krb5_keyblock *armor_key = NULL; ++ krb5_pa_otp_challenge chl; ++ krb5_pa_data *pa = NULL; ++ krb5_error_code retval; ++ krb5_data *tmp = NULL; ++ char *config; ++ ++ /* Determine if otp is enabled for the user. */ ++ retval = cb->get_string(context, rock, "otp", &config); ++ if (retval != 0 || config == NULL) ++ goto out; ++ cb->free_string(context, rock, config); ++ ++ /* Get the armor key. ++ * This indicates the length of random data to use in the nonce. */ ++ armor_key = cb->fast_armor(context, rock); ++ if (armor_key == NULL) { ++ retval = EINVAL; ++ goto out; ++ } ++ ++ /* Build the (mostly empty) challenge. */ ++ memset(&ti, 0, sizeof(ti)); ++ memset(&chl, 0, sizeof(chl)); ++ chl.tokeninfo = tis; ++ ti.format = -1; ++ ti.length = -1; ++ ti.iteration_count = -1; ++ ++ /* Generate the nonce. */ ++ retval = nonce_generate(context, armor_key->length, &chl.nonce); ++ if (retval != 0) ++ goto out; ++ ++ /* Build the output pa data. */ ++ pa = calloc(1, sizeof(krb5_pa_data)); ++ if (pa) { ++ retval = encode_krb5_pa_otp_challenge(&chl, &tmp); ++ if (retval != 0) { ++ DEBUGMSG(ENOMEM, "Unable to encode challenge."); ++ free(pa); ++ pa = NULL; ++ } ++ ++ pa->pa_type = KRB5_PADATA_OTP_CHALLENGE; ++ pa->contents = (krb5_octet *)tmp->data; ++ pa->length = tmp->length; ++ free(tmp); /* Is there a better way to steal the data contents? */ ++ } else { ++ retval = ENOMEM; ++ } ++ ++out: ++ (*respond)(arg, retval, pa); ++ return; ++} ++ ++static void ++otp_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request, ++ krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *data, ++ krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, ++ krb5_kdcpreauth_moddata moddata, ++ krb5_kdcpreauth_verify_respond_fn respond, void *arg) ++{ ++ krb5_keyblock *armor_key = NULL; ++ krb5_pa_otp_req *req = NULL; ++ struct request_state *rs; ++ krb5_error_code retval; ++ krb5_data tmp; ++ char *config; ++ ++ enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH; ++ ++ /* Get the FAST armor key */ ++ armor_key = cb->fast_armor(context, rock); ++ if (armor_key == NULL) { ++ retval = KRB5KDC_ERR_PREAUTH_FAILED; ++ DEBUGMSG(retval, "No armor key found when verifying padata."); ++ goto error; ++ } ++ ++ /* Decode the request */ ++ tmp = make_data(data->contents, data->length); ++ retval = decode_krb5_pa_otp_req(&tmp, &req); ++ if (retval != 0) { ++ DEBUGMSG(retval, "Unable to decode OTP request."); ++ goto error; ++ } ++ ++ /* Decrypt the nonce from the request */ ++ retval = decrypt_encdata(context, armor_key, req, &tmp); ++ if (retval != 0) { ++ DEBUGMSG(retval, "Unable to decrypt encData."); ++ goto error; ++ } ++ ++ /* Verify the nonce or timestamp */ ++ retval = nonce_verify(context, armor_key, &tmp); ++ if (retval != 0) ++ retval = timestamp_verify(context, &tmp); ++ krb5_free_data_contents(context, &tmp); ++ if (retval != 0) { ++ DEBUGMSG(retval, "Unable to verify nonce or timestamp."); ++ goto error; ++ } ++ ++ /* Create the request state. */ ++ rs = malloc(sizeof(struct request_state)); ++ if (rs == NULL) { ++ retval = ENOMEM; ++ goto error; ++ } ++ rs->arg = arg; ++ rs->respond = respond; ++ ++ /* Get the configuration string. */ ++ retval = cb->get_string(context, rock, "otp", &config); ++ if (retval != 0 || config == NULL) { ++ if (config == NULL) ++ retval = KRB5_PREAUTH_FAILED; ++ free(rs); ++ goto error; ++ } ++ ++ /* Send the request. */ ++ otp_state_verify((otp_state *)moddata, ++ (*cb->event_context)(context, rock), ++ request->client, config, req, on_response, rs); ++ cb->free_string(context, rock, config); ++ ++ k5_free_pa_otp_req(context, req); ++ return; ++ ++error: ++ k5_free_pa_otp_req(context, req); ++ (*respond)(arg, retval, NULL, NULL, NULL); ++} ++ ++static krb5_error_code ++otp_return_padata(krb5_context context, krb5_pa_data *padata, ++ krb5_data *req_pkt, krb5_kdc_req *request, ++ krb5_kdc_rep *reply, krb5_keyblock *encrypting_key, ++ krb5_pa_data **send_pa_out, krb5_kdcpreauth_callbacks cb, ++ krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata, ++ krb5_kdcpreauth_modreq modreq) ++{ ++ krb5_keyblock *armor_key = NULL; ++ ++ 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; ++ } ++ ++ /* 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; ++ ++ 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; ++ ++ com_err("otp", 0, "Loaded."); ++ ++ return 0; ++} +diff --git a/src/plugins/preauth/otp/otp.exports b/src/plugins/preauth/otp/otp.exports +new file mode 100644 +index 0000000..26aa19d +--- /dev/null ++++ b/src/plugins/preauth/otp/otp.exports +@@ -0,0 +1 @@ ++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..caa0752 +--- /dev/null ++++ b/src/plugins/preauth/otp/otp_state.c +@@ -0,0 +1,568 @@ ++/* ++ * 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 "otp_state.h" ++ ++#include ++#include ++ ++#include ++ ++#ifndef HOST_NAME_MAX ++/* SUSv2 */ ++#define HOST_NAME_MAX 255 ++#endif ++ ++typedef struct token_type_ { ++ char *name; ++ char *server; ++ char *secret; ++ time_t timeout; ++ size_t retries; ++ krb5_boolean strip_realm; ++} token_type; ++ ++typedef struct token_ { ++ const token_type *type; ++ krb5_data username; ++} token; ++ ++typedef struct request_ { ++ otp_state *state; ++ token *tokens; ++ ssize_t index; ++ otp_cb *cb; ++ void *data; ++ krad_attrset *attrs; ++} request; ++ ++struct otp_state_ { ++ krb5_context ctx; ++ token_type *types; ++ krad_client *radius; ++ krad_attrset *attrs; ++}; ++ ++static inline krb5_data ++string2data_copy(const char *s) ++{ ++ char *tmp; ++ ++ tmp = strdup(s); ++ return make_data(NULL, tmp == NULL ? 0 : strlen(tmp)); ++} ++ ++/* Free a NULL-terminated array of strings. */ ++static void ++stringv_free(char **strv) ++{ ++ size_t i; ++ ++ if (strv == NULL) ++ return; ++ ++ for (i = 0; strv[i] != NULL; i++) ++ free(strv[i]); ++ ++ free(strv); ++} ++ ++/* Free the contents of a single token type. */ ++static void ++token_type_free(token_type *type) ++{ ++ if (type == NULL) ++ return; ++ ++ free(type->name); ++ free(type->server); ++ free(type->secret); ++} ++ ++/* Decode a single token type from the profile. */ ++static krb5_error_code ++token_type_decode(profile_t profile, const char *name, token_type *out) ++{ ++ krb5_error_code retval; ++ char *defsrv = NULL; ++ token_type tt; ++ int tmp; ++ ++ memset(&tt, 0, sizeof(tt)); ++ ++ /* Set the name. */ ++ tt.name = strdup(name == NULL ? "DEFAULT" : name); ++ if (tt.name == NULL) { ++ retval = ENOMEM; ++ goto error; ++ } ++ ++ /* Set defaults. */ ++ tt.timeout = 5000; ++ tt.retries = 3; ++ if (asprintf(&defsrv, "%s/%s.socket", KDC_DIR, tt.name) < 0) { ++ retval = ENOMEM; ++ goto error; ++ } ++ ++ /* Set the internal default. */ ++ if (name == NULL) { ++ retval = ENOMEM; ++ ++ tt.secret = strdup(""); ++ if (tt.secret == NULL) ++ goto error; ++ ++ tt.server = defsrv; ++ tt.strip_realm = FALSE; ++ ++ *out = tt; ++ return 0; ++ } ++ ++ /* Set strip_realm. */ ++ retval = profile_get_boolean(profile, "otp", name, "strip_realm", TRUE, ++ &tmp); ++ if (retval != 0) ++ goto error; ++ tt.strip_realm = tmp == 0 ? FALSE : TRUE; ++ ++ /* Set the server. */ ++ retval = profile_get_string(profile, "otp", name, "server", ++ defsrv, &tt.server); ++ if (retval != 0) ++ goto error; ++ ++ /* Set the secret. */ ++ retval = profile_get_string(profile, "otp", name, "secret", ++ tt.server[0] == '/' ? "" : NULL, ++ &tt.server); ++ if (retval != 0) { ++ goto error; ++ } else if (tt.secret == NULL) { ++ DEBUGMSG(EINVAL, "Secret not specified in token type '%s'.", name); ++ retval = EINVAL; ++ goto error; ++ } ++ ++ /* Set the timeout. */ ++ retval = profile_get_integer(profile, "otp", name, "timeout", ++ tt.timeout / 1000, &tmp); ++ if (retval != 0) ++ goto error; ++ tt.timeout = tmp * 1000; /* Convert to milliseconds. */ ++ ++ /* Set the retries. */ ++ retval = profile_get_integer(profile, "otp", name, "retries", ++ tt.retries, &tmp); ++ if (retval != 0) ++ goto error; ++ tt.retries = tmp; ++ ++ *out = tt; ++ free(defsrv); ++ return 0; ++ ++error: ++ token_type_free(&tt); ++ free(defsrv); ++ return retval; ++} ++ ++/* Free an array of token types. */ ++static void ++token_types_free(token_type *types) ++{ ++ size_t i; ++ ++ if (types == NULL) ++ return; ++ ++ for (i = 0; types[i].server != NULL; i++) ++ token_type_free(&types[i]); ++ ++ free(types); ++} ++ ++/* Decode an array of token types from the profile. */ ++static krb5_error_code ++token_types_decode(profile_t profile, token_type **out) ++{ ++ const char *tmp[2] = { "otp", NULL }; ++ token_type *types = NULL; ++ char **names = NULL; ++ errcode_t retval; ++ ssize_t i, j; ++ ++ retval = profile_get_subsection_names(profile, tmp, &names); ++ if (retval != 0) ++ return retval; ++ ++ for (i = 0, j = 0; names[i] != NULL; i++) { ++ if (strcmp(names[i], "DEFAULT") == 0) ++ j = 1; ++ } ++ ++ types = calloc(i - j + 2, sizeof(token_type)); ++ if (types == NULL) { ++ retval = ENOMEM; ++ goto error; ++ } ++ ++ /* If no default has been specified, use our internal default. */ ++ if (j == 0) { ++ retval = token_type_decode(profile, NULL, &types[j++]); ++ if (retval != 0) ++ goto error; ++ } else { ++ j = 0; ++ } ++ ++ for (i = 0; names[i] != NULL; i++) { ++ retval = token_type_decode(profile, names[i], &types[j++]); ++ if (retval != 0) ++ goto error; ++ } ++ ++ stringv_free(names); ++ *out = types; ++ return 0; ++ ++error: ++ token_types_free(types); ++ stringv_free(names); ++ return retval; ++} ++ ++/* Free the contents of a single token. */ ++static void ++token_free(token *t) ++{ ++ if (t == NULL) ++ return; ++ ++ free(t->username.data); ++} ++ ++/* Decode a single token from a JSON token object. */ ++static krb5_error_code ++token_decode(krb5_context ctx, krb5_const_principal princ, ++ const token_type *types, k5_json_object obj, token *out) ++{ ++ const char *type = NULL; ++ krb5_error_code retval; ++ k5_json_value tmp; ++ size_t i; ++ token t; ++ ++ memset(&t, 0, sizeof(t)); ++ ++ tmp = k5_json_object_get(obj, "username"); ++ if (tmp != NULL && k5_json_get_tid(tmp) == K5_JSON_TID_STRING) { ++ t.username = string2data_copy(k5_json_string_utf8(tmp)); ++ if (t.username.data == NULL) ++ return ENOMEM; ++ } ++ ++ tmp = k5_json_object_get(obj, "type"); ++ if (tmp != NULL && k5_json_get_tid(tmp) == K5_JSON_TID_STRING) ++ type = k5_json_string_utf8(tmp); ++ ++ for (i = 0; types[i].server != NULL; i++) { ++ if (strcmp(type == NULL ? "DEFAULT" : type, types[i].name) == 0) ++ t.type = &types[i]; ++ } ++ ++ if (t.username.data == NULL) { ++ retval = krb5_unparse_name_flags(ctx, princ, ++ t.type->strip_realm ++ ? KRB5_PRINCIPAL_UNPARSE_NO_REALM ++ : 0, ++ &t.username.data); ++ if (retval != 0) ++ return retval; ++ t.username.length = strlen(t.username.data); ++ } ++ ++ *out = t; ++ return 0; ++} ++ ++/* Free an array of tokens. */ ++static void ++tokens_free(token *tokens) ++{ ++ size_t i; ++ ++ if (tokens == NULL) ++ return; ++ ++ for (i = 0; tokens[i].type != NULL; i++) ++ token_free(&tokens[i]); ++ ++ free(tokens); ++} ++ ++/* Decode an array of tokens from the configuration string. */ ++static krb5_error_code ++tokens_decode(krb5_context ctx, krb5_const_principal princ, ++ const token_type *types, const char *config, token **out) ++{ ++ krb5_error_code retval; ++ k5_json_value arr, obj; ++ token *tokens; ++ ssize_t len, i, j; ++ ++ if (config == NULL) ++ config = "[{}]"; ++ ++ arr = k5_json_decode(config); ++ if (arr == NULL) ++ return ENOMEM; ++ ++ if (k5_json_get_tid(arr) != K5_JSON_TID_ARRAY || ++ (len = k5_json_array_length(arr)) == 0) { ++ k5_json_release(arr); ++ ++ arr = k5_json_decode("[{}]"); ++ if (arr == NULL) ++ return ENOMEM; ++ ++ if (k5_json_get_tid(arr) != K5_JSON_TID_ARRAY) { ++ k5_json_release(arr); ++ return ENOMEM; ++ } ++ ++ len = k5_json_array_length(arr); ++ } ++ ++ tokens = calloc(len + 1, sizeof(token)); ++ if (tokens == NULL) { ++ k5_json_release(arr); ++ return ENOMEM; ++ } ++ ++ for (i = 0, j = 0; i < len; i++) { ++ obj = k5_json_array_get(arr, i); ++ if (k5_json_get_tid(obj) != K5_JSON_TID_OBJECT) ++ continue; ++ ++ retval = token_decode(ctx, princ, types, obj, &tokens[j++]); ++ if (retval != 0) { ++ k5_json_release(arr); ++ while (--j > 0) ++ token_free(&tokens[j]); ++ free(tokens); ++ return retval; ++ } ++ } ++ ++ k5_json_release(arr); ++ *out = tokens; ++ return 0; ++} ++ ++static void ++request_free(request *req) ++{ ++ if (req == NULL) ++ return; ++ ++ krad_attrset_free(req->attrs); ++ tokens_free(req->tokens); ++ free(req); ++} ++ ++krb5_error_code ++otp_state_new(krb5_context ctx, otp_state **out) ++{ ++ char hostname[HOST_NAME_MAX + 1]; ++ krb5_error_code retval; ++ profile_t profile; ++ krb5_data hndata; ++ otp_state *self; ++ ++ retval = gethostname(hostname, sizeof(hostname)); ++ if (retval != 0) ++ return retval; ++ ++ self = calloc(1, sizeof(otp_state)); ++ if (self == NULL) ++ return ENOMEM; ++ ++ retval = krb5_get_profile(ctx, &profile); ++ if (retval != 0) ++ goto error; ++ ++ retval = token_types_decode(profile, &self->types); ++ profile_abandon(profile); ++ if (retval != 0) ++ goto error; ++ ++ retval = krad_attrset_new(ctx, &self->attrs); ++ if (retval != 0) ++ goto error; ++ ++ hndata = make_data(hostname, strlen(hostname)); ++ retval = krad_attrset_add(self->attrs, ++ krad_attr_name2num("NAS-Identifier"), &hndata); ++ if (retval != 0) ++ goto error; ++ ++ retval = krad_attrset_add_number(self->attrs, ++ krad_attr_name2num("Service-Type"), ++ KRAD_SERVICE_TYPE_AUTHENTICATE_ONLY); ++ if (retval != 0) ++ goto error; ++ ++ self->ctx = ctx; ++ *out = self; ++ return 0; ++ ++error: ++ otp_state_free(self); ++ return retval; ++} ++ ++void ++otp_state_free(otp_state *self) ++{ ++ if (self == NULL) ++ return; ++ ++ krad_attrset_free(self->attrs); ++ token_types_free(self->types); ++ free(self); ++} ++ ++static void ++request_send(request *req); ++ ++static void ++callback(krb5_error_code retval, const krad_packet *rqst, ++ const krad_packet *resp, void *data) ++{ ++ request *req = data; ++ ++ req->index++; ++ ++ if (retval != 0) ++ goto error; ++ ++ /* If we received an accept packet, success! */ ++ if (krad_packet_get_code(resp) == ++ krad_code_name2num("Access-Accept")) { ++ (*req->cb)(retval, otp_response_success, req->data); ++ request_free(req); ++ return; ++ } ++ ++ /* If we have no more tokens to try, failure! */ ++ if (req->tokens[req->index].type == NULL) ++ goto error; ++ ++ /* Try the next token. */ ++ request_send(req); ++ ++error: ++ (*req->cb)(retval, otp_response_fail, req->data); ++ request_free(req); ++} ++ ++static void ++request_send(request *req) ++{ ++ krb5_error_code retval; ++ ++ retval = krad_attrset_add(req->attrs, ++ krad_attr_name2num("User-Name"), ++ &req->tokens[req->index].username); ++ if (retval != 0) ++ goto error; ++ ++ 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; ++ ++ return; ++ ++error: ++ (*req->cb)(retval, otp_response_fail, req->data); ++ request_free(req); ++} ++ ++void ++otp_state_verify(otp_state *state, verto_ctx *ctx, krb5_const_principal princ, ++ const char *config, const krb5_pa_otp_req *req, ++ otp_cb *cb, void *data) ++{ ++ krb5_error_code retval; ++ request *rqst = NULL; ++ ++ if (state->radius == NULL) { ++ retval = krad_client_new(state->ctx, ctx, &state->radius); ++ if (retval != 0) ++ goto error; ++ } ++ ++ rqst = calloc(1, sizeof(request)); ++ if (rqst == NULL) { ++ (*cb)(ENOMEM, otp_response_fail, data); ++ return; ++ } ++ rqst->state = state; ++ rqst->data = data; ++ rqst->cb = cb; ++ ++ retval = krad_attrset_copy(state->attrs, &rqst->attrs); ++ if (retval != 0) ++ goto error; ++ ++ retval = krad_attrset_add(rqst->attrs, krad_attr_name2num("User-Password"), ++ &req->otp_value); ++ if (retval != 0) ++ goto error; ++ ++ retval = tokens_decode(state->ctx, princ, state->types, config, ++ &rqst->tokens); ++ if (retval != 0) ++ goto error; ++ ++ request_send(rqst); ++ return; ++ ++error: ++ (*cb)(retval, otp_response_fail, data); ++ request_free(rqst); ++} +diff --git a/src/plugins/preauth/otp/otp_state.h b/src/plugins/preauth/otp/otp_state.h +new file mode 100644 +index 0000000..89a164a +--- /dev/null ++++ b/src/plugins/preauth/otp/otp_state.h +@@ -0,0 +1,58 @@ ++/* ++ * 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 OTP_H_ ++#define OTP_H_ ++ ++#include ++#include ++ ++#include ++#define DEBUGMSG(code, ...) com_err("otp", code, __VA_ARGS__) ++ ++typedef enum otp_response_ { ++ otp_response_fail = 0, ++ otp_response_success ++ /* Other values reserved for responses like next token or new pin. */ ++} otp_response; ++ ++typedef struct otp_state_ otp_state; ++typedef void ++(otp_cb)(krb5_error_code retval, otp_response response, void *data); ++ ++krb5_error_code ++otp_state_new(krb5_context ctx, otp_state **self); ++ ++void ++otp_state_free(otp_state *self); ++ ++void ++otp_state_verify(otp_state *state, verto_ctx *ctx, krb5_const_principal princ, ++ const char *config, const krb5_pa_otp_req *request, ++ otp_cb *cb, void *data); ++ ++#endif /* OTP_H_ */ +-- +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 - 1.11.1-8 +- Update otp backport patches (libk5radius => libkrad) + * Wed Apr 3 2013 Nalin Dahyabhai 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