summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNalin Dahyabhai <nalin@dahyabhai.net>2013-10-15 17:37:07 -0400
committerNalin Dahyabhai <nalin@dahyabhai.net>2013-11-19 17:38:54 -0500
commitf619caa9c979fc03ddcd83c7938d671596ac55c9 (patch)
tree0f064f09b7d006327a9637f67216375d46120102
parent7448cea67e8682c825b3172dfb2787e18de6af3e (diff)
downloadkrb5-f619caa9c979fc03ddcd83c7938d671596ac55c9.tar.gz
krb5-f619caa9c979fc03ddcd83c7938d671596ac55c9.tar.xz
krb5-f619caa9c979fc03ddcd83c7938d671596ac55c9.zip
Drop OTP backport
-rw-r--r--krb5-1.11.2-keycheck.patch230
-rw-r--r--krb5-1.11.2-otp.patch5163
-rw-r--r--krb5.spec8
3 files changed, 1 insertions, 5400 deletions
diff --git a/krb5-1.11.2-keycheck.patch b/krb5-1.11.2-keycheck.patch
deleted file mode 100644
index 4b4bd08..0000000
--- a/krb5-1.11.2-keycheck.patch
+++ /dev/null
@@ -1,230 +0,0 @@
-From c7047421487c0e616b881c0922937bc759114233 Mon Sep 17 00:00:00 2001
-From: Nathaniel McCallum <npmccallum@redhat.com>
-Date: Fri, 3 May 2013 15:44:44 -0400
-Subject: [PATCH 1/3] Check for keys in encrypted timestamp/challenge
-
-Encrypted timestamp and encrypted challenge cannot succeed if the
-client has no long-term key matching the request enctypes, so do not
-offer them in that case.
-
-ticket: 7630
-
-NPM - This is a backport from the upstream fix. See upstream commits:
- https://github.com/krb5/krb5/commit/e50482720a805ecd8c160e4a8f4a846e6327dca2
- https://github.com/krb5/krb5/commit/9593d1311fa5e6e841c429653ad35a63d17c2fdd
----
- src/kdc/kdc_preauth_ec.c | 23 +++++++++++++++++++++--
- src/kdc/kdc_preauth_encts.c | 22 ++++++++++++++++++++--
- 2 files changed, 41 insertions(+), 4 deletions(-)
-
-diff --git a/src/kdc/kdc_preauth_ec.c b/src/kdc/kdc_preauth_ec.c
-index 9d7236c..db3ad07 100644
---- a/src/kdc/kdc_preauth_ec.c
-+++ b/src/kdc/kdc_preauth_ec.c
-@@ -39,8 +39,27 @@ ec_edata(krb5_context context, krb5_kdc_req *request,
- krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type,
- krb5_kdcpreauth_edata_respond_fn respond, void *arg)
- {
-- krb5_keyblock *armor_key = cb->fast_armor(context, rock);
-- (*respond)(arg, (armor_key == NULL) ? ENOENT : 0, NULL);
-+ krb5_boolean have_client_keys = FALSE;
-+ krb5_keyblock *armor_key;
-+ krb5_key_data *kd;
-+ int i;
-+
-+ armor_key = cb->fast_armor(context, rock);
-+
-+ for (i = 0; i < rock->request->nktypes; i++) {
-+ if (krb5_dbe_find_enctype(context, rock->client,
-+ rock->request->ktype[i],
-+ -1, 0, &kd) == 0) {
-+ have_client_keys = TRUE;
-+ break;
-+ }
-+ }
-+
-+ /* Encrypted challenge only works with FAST, and requires a client key. */
-+ if (armor_key == NULL || !have_client_keys)
-+ (*respond)(arg, ENOENT, NULL);
-+ else
-+ (*respond)(arg, 0, NULL);
- }
-
- static void
-diff --git a/src/kdc/kdc_preauth_encts.c b/src/kdc/kdc_preauth_encts.c
-index d708061..adde6e2 100644
---- a/src/kdc/kdc_preauth_encts.c
-+++ b/src/kdc/kdc_preauth_encts.c
-@@ -34,9 +34,27 @@ enc_ts_get(krb5_context context, krb5_kdc_req *request,
- krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type,
- krb5_kdcpreauth_edata_respond_fn respond, void *arg)
- {
-- krb5_keyblock *armor_key = cb->fast_armor(context, rock);
-+ krb5_boolean have_client_keys = FALSE;
-+ krb5_keyblock *armor_key;
-+ krb5_key_data *kd;
-+ int i;
-+
-+ armor_key = cb->fast_armor(context, rock);
-+
-+ for (i = 0; i < rock->request->nktypes; i++) {
-+ if (krb5_dbe_find_enctype(context, rock->client,
-+ rock->request->ktype[i],
-+ -1, 0, &kd) == 0) {
-+ have_client_keys = TRUE;
-+ break;
-+ }
-+ }
-
-- (*respond)(arg, (armor_key != NULL) ? ENOENT : 0, NULL);
-+ /* Encrypted timestamp must not be used with FAST, and requires a key. */
-+ if (armor_key != NULL || !have_client_keys)
-+ (*respond)(arg, ENOENT, NULL);
-+ else
-+ (*respond)(arg, 0, NULL);
- }
-
- static void
---
-1.8.2.1
-
-
-From 4d19790527e2c9d52bb05abd6048a63a1a8c222c Mon Sep 17 00:00:00 2001
-From: Greg Hudson <ghudson@mit.edu>
-Date: Mon, 29 Apr 2013 14:55:31 -0400
-Subject: [PATCH 2/3] Don't send empty etype info from KDC
-
-RFC 4120 prohibits empty ETYPE-INFO2 sequences (though not ETYPE-INFO
-sequences), and our client errors out if it sees an empty sequence of
-either.
-
-ticket: 7630
----
- src/kdc/kdc_preauth.c | 5 +++++
- 1 file changed, 5 insertions(+)
-
-diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c
-index 42a37a8..5c3b615 100644
---- a/src/kdc/kdc_preauth.c
-+++ b/src/kdc/kdc_preauth.c
-@@ -1404,6 +1404,11 @@ etype_info_helper(krb5_context context, krb5_kdc_req *request,
- seen_des++;
- }
- }
-+
-+ /* If the list is empty, don't send it at all. */
-+ if (i == 0)
-+ goto cleanup;
-+
- if (etype_info2)
- retval = encode_krb5_etype_info2(entry, &scratch);
- else
---
-1.8.2.1
-
-
-From a04cf2e89f4101eb6c2ec44ef1948976fe5fe9d3 Mon Sep 17 00:00:00 2001
-From: Greg Hudson <ghudson@mit.edu>
-Date: Thu, 2 May 2013 16:15:32 -0400
-Subject: [PATCH 3/3] Make AS requests work with no client key
-
-If we cannot find a client key while preparing an AS reply, give
-preauth mechanisms a chance to replace the reply key before erroring
-out.
-
-ticket: 7630
----
- src/kdc/do_as_req.c | 36 ++++++++++++++++++++----------------
- src/kdc/kdc_preauth.c | 6 ++++++
- 2 files changed, 26 insertions(+), 16 deletions(-)
-
-diff --git a/src/kdc/do_as_req.c b/src/kdc/do_as_req.c
-index 79da300..cb91803 100644
---- a/src/kdc/do_as_req.c
-+++ b/src/kdc/do_as_req.c
-@@ -195,23 +195,18 @@ finish_process_as_req(struct as_req_state *state, krb5_error_code errcode)
- useenctype, -1, 0, &client_key))
- break;
- }
-- if (!(client_key)) {
-- /* Cannot find an appropriate key */
-- state->status = "CANT_FIND_CLIENT_KEY";
-- errcode = KRB5KDC_ERR_ETYPE_NOSUPP;
-- goto egress;
-- }
-- state->rock.client_key = client_key;
-
-- /* convert client.key_data into a real key */
-- if ((errcode = krb5_dbe_decrypt_key_data(kdc_context, NULL,
-- client_key,
-- &state->client_keyblock,
-- NULL))) {
-- state->status = "DECRYPT_CLIENT_KEY";
-- goto egress;
-+ if (client_key != NULL) {
-+ /* Decrypt the client key data entry to get the real reply key. */
-+ errcode = krb5_dbe_decrypt_key_data(kdc_context, NULL, client_key,
-+ &state->client_keyblock, NULL);
-+ if (errcode) {
-+ state->status = "DECRYPT_CLIENT_KEY";
-+ goto egress;
-+ }
-+ state->client_keyblock.enctype = useenctype;
-+ state->rock.client_key = client_key;
- }
-- state->client_keyblock.enctype = useenctype;
-
- /* Start assembling the response */
- state->reply.msg_type = KRB5_AS_REP;
-@@ -248,6 +243,14 @@ finish_process_as_req(struct as_req_state *state, krb5_error_code errcode)
- goto egress;
- }
-
-+ /* If we didn't find a client long-term key and no preauth mechanism
-+ * replaced the reply key, error out now. */
-+ if (state->client_keyblock.enctype == ENCTYPE_NULL) {
-+ state->status = "CANT_FIND_CLIENT_KEY";
-+ errcode = KRB5KDC_ERR_ETYPE_NOSUPP;
-+ goto egress;
-+ }
-+
- errcode = handle_authdata(kdc_context,
- state->c_flags,
- state->client,
-@@ -306,7 +309,8 @@ finish_process_as_req(struct as_req_state *state, krb5_error_code errcode)
- &state->reply_encpart, 0,
- as_encrypting_key,
- &state->reply, &response);
-- state->reply.enc_part.kvno = client_key->key_data_kvno;
-+ if (client_key != NULL)
-+ state->reply.enc_part.kvno = client_key->key_data_kvno;
- if (errcode) {
- state->status = "ENCODE_KDC_REP";
- goto egress;
-diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c
-index 5c3b615..5d12346 100644
---- a/src/kdc/kdc_preauth.c
-+++ b/src/kdc/kdc_preauth.c
-@@ -1473,6 +1473,9 @@ etype_info_as_rep_helper(krb5_context context, krb5_pa_data * padata,
- krb5_etype_info_entry **entry = NULL;
- krb5_data *scratch = NULL;
-
-+ if (client_key == NULL)
-+ return 0;
-+
- /*
- * Skip PA-ETYPE-INFO completely if AS-REQ lists any "newer"
- * enctypes.
-@@ -1576,6 +1579,9 @@ return_pw_salt(krb5_context context, krb5_pa_data *in_padata,
- krb5_key_data * client_key = rock->client_key;
- int i;
-
-+ if (client_key == NULL)
-+ return 0;
-+
- for (i = 0; i < request->nktypes; i++) {
- if (enctype_requires_etype_info_2(request->ktype[i]))
- return 0;
---
-1.8.2.1
-
diff --git a/krb5-1.11.2-otp.patch b/krb5-1.11.2-otp.patch
deleted file mode 100644
index eab913e..0000000
--- a/krb5-1.11.2-otp.patch
+++ /dev/null
@@ -1,5163 +0,0 @@
-From 9a0bb5ada335017c5da35cb41333632ae5577a93 Mon Sep 17 00:00:00 2001
-From: Greg Hudson <ghudson@mit.edu>
-Date: Tue, 15 Jan 2013 11:11:27 -0500
-Subject: [PATCH 1/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..90ab86d 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 "/kadm_old.acl"
-
- /* Location of KDC profile */
--#define DEFAULT_KDC_PROFILE "@LOCALSTATEDIR/krb5kdc/kdc.conf"
-+#define DEFAULT_KDC_PROFILE KDC_DIR "/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.1
-
-
-From 4ba748915908a45564d2e8a098cf107e39de3315 Mon Sep 17 00:00:00 2001
-From: Nathaniel McCallum <npmccallum@redhat.com>
-Date: Tue, 9 Apr 2013 11:17:04 -0400
-Subject: [PATCH 2/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.1
-
-
-From f98e3f2569996d84c968852b50f76173203e568f Mon Sep 17 00:00:00 2001
-From: Nathaniel McCallum <npmccallum@redhat.com>
-Date: Thu, 4 Apr 2013 13:39:21 -0400
-Subject: [PATCH 3/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 | 470 +++++++++++++++++++++++++++++++++++++++
- 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, 3683 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..e6d5766
---- /dev/null
-+++ b/src/include/krad.h
-@@ -0,0 +1,267 @@
-+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-+/*
-+ * Copyright 2013 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+/*
-+ * This API is not considered as stable as the main krb5 API.
-+ *
-+ * - We may make arbitrary incompatible changes between feature releases
-+ * (e.g. from 1.12 to 1.13).
-+ * - We will make some effort to avoid making incompatible changes for
-+ * bugfix releases, but will make them if necessary.
-+ */
-+
-+#ifndef KRAD_H_
-+#define KRAD_H_
-+
-+#include <krb5.h>
-+#include <verto.h>
-+#include <stddef.h>
-+#include <stdio.h>
-+
-+#define KRAD_PACKET_SIZE_MAX 4096
-+
-+#define KRAD_SERVICE_TYPE_LOGIN 1
-+#define KRAD_SERVICE_TYPE_FRAMED 2
-+#define KRAD_SERVICE_TYPE_CALLBACK_LOGIN 3
-+#define KRAD_SERVICE_TYPE_CALLBACK_FRAMED 4
-+#define KRAD_SERVICE_TYPE_OUTBOUND 5
-+#define KRAD_SERVICE_TYPE_ADMINISTRATIVE 6
-+#define KRAD_SERVICE_TYPE_NAS_PROMPT 7
-+#define KRAD_SERVICE_TYPE_AUTHENTICATE_ONLY 8
-+#define KRAD_SERVICE_TYPE_CALLBACK_NAS_PROMPT 9
-+#define KRAD_SERVICE_TYPE_CALL_CHECK 10
-+#define KRAD_SERVICE_TYPE_CALLBACK_ADMINISTRATIVE 11
-+
-+typedef struct krad_attrset_st krad_attrset;
-+typedef struct krad_packet_st krad_packet;
-+typedef struct krad_client_st krad_client;
-+typedef unsigned char krad_code;
-+typedef unsigned char krad_attr;
-+
-+/* Called when a response is received or the request times out. */
-+typedef void
-+(*krad_cb)(krb5_error_code retval, const krad_packet *request,
-+ const krad_packet *response, void *data);
-+
-+/*
-+ * Called to iterate over a set of requests. Either the callback will be
-+ * called until it returns NULL, or it will be called with cancel = TRUE to
-+ * terminate in the middle of an iteration.
-+ */
-+typedef const krad_packet *
-+(*krad_packet_iter_cb)(void *data, krb5_boolean cancel);
-+
-+/*
-+ * Code
-+ */
-+
-+/* Convert a code name to its number. Only works for codes defined
-+ * by RFC 2875 or 2882. Returns 0 if the name was not found. */
-+krad_code
-+krad_code_name2num(const char *name);
-+
-+/* Convert a code number to its name. Only works for attributes defined
-+ * by RFC 2865 or 2882. Returns NULL if the name was not found. */
-+const char *
-+krad_code_num2name(krad_code code);
-+
-+/*
-+ * Attribute
-+ */
-+
-+/* Convert an attribute name to its number. Only works for attributes defined
-+ * by RFC 2865. Returns 0 if the name was not found. */
-+krad_attr
-+krad_attr_name2num(const char *name);
-+
-+/* Convert an attribute number to its name. Only works for attributes defined
-+ * by RFC 2865. Returns NULL if the name was not found. */
-+const char *
-+krad_attr_num2name(krad_attr type);
-+
-+/*
-+ * Attribute set
-+ */
-+
-+/* Create a new attribute set. */
-+krb5_error_code
-+krad_attrset_new(krb5_context ctx, krad_attrset **set);
-+
-+/* Create a deep copy of an attribute set. */
-+krb5_error_code
-+krad_attrset_copy(const krad_attrset *set, krad_attrset **copy);
-+
-+/* Free an attribute set. */
-+void
-+krad_attrset_free(krad_attrset *set);
-+
-+/* Add an attribute to a set. */
-+krb5_error_code
-+krad_attrset_add(krad_attrset *set, krad_attr type, const krb5_data *data);
-+
-+/* Add a four-octet unsigned number attribute to the given set. */
-+krb5_error_code
-+krad_attrset_add_number(krad_attrset *set, krad_attr type, krb5_ui_4 num);
-+
-+/* Delete the specified attribute. */
-+void
-+krad_attrset_del(krad_attrset *set, krad_attr type, size_t indx);
-+
-+/* Get the specified attribute. */
-+const krb5_data *
-+krad_attrset_get(const krad_attrset *set, krad_attr type, size_t indx);
-+
-+/*
-+ * Packet
-+ */
-+
-+/* Determine the bytes needed from the socket to get the whole packet. Don't
-+ * cache the return value as it can change! Returns -1 on EBADMSG. */
-+ssize_t
-+krad_packet_bytes_needed(const krb5_data *buffer);
-+
-+/* Free a packet. */
-+void
-+krad_packet_free(krad_packet *pkt);
-+
-+/*
-+ * Create a new request packet.
-+ *
-+ * This function takes the attributes specified in set and converts them into a
-+ * radius packet. The packet will have a randomized id. If cb is not NULL, it
-+ * will be called passing data as the argument to iterate over a set of
-+ * outstanding requests. In this case, the id will be both random and unique
-+ * across the set of requests.
-+ */
-+krb5_error_code
-+krad_packet_new_request(krb5_context ctx, const char *secret, krad_code code,
-+ const krad_attrset *set, krad_packet_iter_cb cb,
-+ void *data, krad_packet **request);
-+
-+/*
-+ * Create a new response packet.
-+ *
-+ * This function is similar to krad_packet_new_requst() except that it crafts a
-+ * packet in response to a request packet. This new packet will borrow values
-+ * from the request such as the id and the authenticator.
-+ */
-+krb5_error_code
-+krad_packet_new_response(krb5_context ctx, const char *secret, krad_code code,
-+ const krad_attrset *set, const krad_packet *request,
-+ krad_packet **response);
-+
-+/*
-+ * Decode a request radius packet from krb5_data.
-+ *
-+ * The resulting decoded packet will be a request packet stored in *reqpkt.
-+ *
-+ * If cb is NULL, *duppkt will always be NULL.
-+ *
-+ * If cb is not NULL, it will be called (with the data argument) to iterate
-+ * over a set of requests currently being processed. In this case, if the
-+ * packet is a duplicate of an already received request, the original request
-+ * will be set in *duppkt.
-+ */
-+krb5_error_code
-+krad_packet_decode_request(krb5_context ctx, const char *secret,
-+ const krb5_data *buffer, krad_packet_iter_cb cb,
-+ void *data, const krad_packet **duppkt,
-+ krad_packet **reqpkt);
-+
-+/*
-+ * Decode a response radius packet from krb5_data.
-+ *
-+ * The resulting decoded packet will be a response packet stored in *rsppkt.
-+ *
-+ * If cb is NULL, *reqpkt will always be NULL.
-+ *
-+ * If cb is not NULL, it will be called (with the data argument) to iterate
-+ * over a set of requests awaiting responses. In this case, if the response
-+ * packet matches one of these requests, the original request will be set in
-+ * *reqpkt.
-+ */
-+krb5_error_code
-+krad_packet_decode_response(krb5_context ctx, const char *secret,
-+ const krb5_data *buffer, krad_packet_iter_cb cb,
-+ void *data, const krad_packet **reqpkt,
-+ krad_packet **rsppkt);
-+
-+/* Encode packet. */
-+const krb5_data *
-+krad_packet_encode(const krad_packet *pkt);
-+
-+/* Get the code for the given packet. */
-+krad_code
-+krad_packet_get_code(const krad_packet *pkt);
-+
-+/* Get the specified attribute. */
-+const krb5_data *
-+krad_packet_get_attr(const krad_packet *pkt, krad_attr type, size_t indx);
-+
-+/*
-+ * Client
-+ */
-+
-+/* Create a new client. */
-+krb5_error_code
-+krad_client_new(krb5_context kctx, verto_ctx *vctx, krad_client **client);
-+
-+/* Free the client. */
-+void
-+krad_client_free(krad_client *client);
-+
-+/*
-+ * Send a request to a radius server.
-+ *
-+ * The remote host may be specified by one of the following formats:
-+ * - /path/to/unix.socket
-+ * - IPv4
-+ * - IPv4:port
-+ * - IPv4:service
-+ * - [IPv6]
-+ * - [IPv6]:port
-+ * - [IPv6]:service
-+ * - hostname
-+ * - hostname:port
-+ * - hostname:service
-+ *
-+ * The timeout parameter (milliseconds) is the total timeout across all remote
-+ * hosts (when DNS returns multiple entries) and all retries.
-+ *
-+ * The cb function will be called with the data argument when either a response
-+ * is received or the request times out on all possible remote hosts.
-+ *
-+ * If the remote host is a unix domain socket, retries is ignored due to
-+ * guaranteed delivery.
-+ */
-+krb5_error_code
-+krad_client_send(krad_client *rc, krad_code code, const krad_attrset *attrs,
-+ const char *remote, const char *secret, int timeout,
-+ size_t retries, krad_cb cb, void *data);
-+
-+#endif /* KRAD_H_ */
-diff --git a/src/lib/Makefile.in b/src/lib/Makefile.in
-index 485db40..4dde514 100644
---- a/src/lib/Makefile.in
-+++ b/src/lib/Makefile.in
-@@ -1,5 +1,5 @@
- mydir=lib
--SUBDIRS=crypto krb5 gssapi rpc kdb kadm5 apputils
-+SUBDIRS=crypto krb5 gssapi rpc kdb kadm5 apputils krad
- WINSUBDIRS=crypto krb5 gssapi
- BUILDTOP=$(REL)..
-
-diff --git a/src/lib/krad/Makefile.in b/src/lib/krad/Makefile.in
-new file mode 100644
-index 0000000..75431a0
---- /dev/null
-+++ b/src/lib/krad/Makefile.in
-@@ -0,0 +1,74 @@
-+mydir=lib$(S)krad
-+BUILDTOP=$(REL)..$(S)..
-+RELDIR=krad
-+
-+RUN_SETUP=@KRB5_RUN_ENV@
-+PROG_LIBPATH=-L$(TOPLIBD)
-+PROG_RPATH=$(KRB5_LIBDIR)
-+
-+SHLIB_EXPLIBS=$(KRB5_BASE_LIBS) $(VERTO_LIBS)
-+SHLIB_EXPDEPLIBS=$(KRB5_BASE_DEPLIBS) $(VERTO_DEPLIB)
-+SHLIB_DIRS=-L$(TOPLIBD)
-+SHLIB_RDIRS=$(KRB5_LIBDIR)
-+
-+LIBBASE=krad
-+LIBMAJOR=0
-+LIBMINOR=0
-+
-+STLIBOBJS=attr.o attrset.o client.o code.o packet.o remote.o
-+LIBOBJS=$(OUTPRE)attr.$(OBJEXT) \
-+ $(OUTPRE)attrset.$(OBJEXT) \
-+ $(OUTPRE)client.$(OBJEXT) \
-+ $(OUTPRE)code.$(OBJEXT) \
-+ $(OUTPRE)packet.$(OBJEXT) \
-+ $(OUTPRE)remote.$(OBJEXT)
-+SRCS=attr.c attrset.c client.c code.c packet.c remote.c \
-+ t_attr.c t_attrset.c t_client.c t_code.c t_packet.c t_remote.c t_test.c
-+
-+STOBJLISTS=OBJS.ST
-+
-+all-unix:: all-liblinks
-+install-unix:: install-libs
-+
-+clean-unix:: clean-liblinks clean-libs clean-libobjs
-+
-+check-unix:: t_attr t_attrset t_code t_packet t_remote t_client
-+ $(RUN_SETUP) $(VALGRIND) ./t_attr
-+ $(RUN_SETUP) $(VALGRIND) ./t_attrset
-+ $(RUN_SETUP) $(VALGRIND) ./t_code
-+ $(RUN_SETUP) $(VALGRIND) ./t_packet $(PYTHON) $(srcdir)/t_daemon.py
-+ $(RUN_SETUP) $(VALGRIND) ./t_remote $(PYTHON) $(srcdir)/t_daemon.py
-+ $(RUN_SETUP) $(VALGRIND) ./t_client $(PYTHON) $(srcdir)/t_daemon.py
-+
-+TESTDEPS=t_test.o $(KRB5_BASE_DEPLIBS)
-+TESTLIBS=t_test.o $(KRB5_BASE_LIBS)
-+
-+T_ATTR_OBJS=attr.o t_attr.o
-+t_attr: $(T_ATTR_OBJS) $(TESTDEPS)
-+ $(CC_LINK) -o $@ $(T_ATTR_OBJS) $(TESTLIBS)
-+
-+T_ATTRSET_OBJS=attr.o attrset.o t_attrset.o
-+t_attrset: $(T_ATTRSET_OBJS) $(TESTDEPS)
-+ $(CC_LINK) -o $@ $(T_ATTRSET_OBJS) $(TESTLIBS)
-+
-+T_CODE_OBJS=code.o t_code.o
-+t_code: $(T_CODE_OBJS) $(TESTDEPS)
-+ $(CC_LINK) -o $@ $(T_CODE_OBJS) $(TESTLIBS)
-+
-+T_PACKET_OBJS=attr.o attrset.o code.o packet.o t_packet.o
-+t_packet: $(T_PACKET_OBJS) $(TESTDEPS)
-+ $(CC_LINK) -o $@ $(T_PACKET_OBJS) $(TESTLIBS)
-+
-+T_REMOTE_OBJS=attr.o attrset.o code.o packet.o remote.o t_remote.o
-+t_remote: $(T_REMOTE_OBJS) $(TESTDEPS) $(VERTO_DEPLIB)
-+ $(CC_LINK) -o $@ $(T_REMOTE_OBJS) $(TESTLIBS) $(VERTO_LIBS)
-+
-+T_CLIENT_OBJS=attr.o attrset.o code.o packet.o remote.o client.o t_client.o
-+t_client: $(T_CLIENT_OBJS) $(TESTDEPS) $(VERTO_DEPLIB)
-+ $(CC_LINK) -o $@ $(T_CLIENT_OBJS) $(TESTLIBS) $(VERTO_LIBS)
-+
-+clean-unix:: clean-libobjs
-+ $(RM) *.o t_attr t_attrset t_code t_packet t_remote t_client
-+
-+@lib_frag@
-+@libobj_frag@
-diff --git a/src/lib/krad/attr.c b/src/lib/krad/attr.c
-new file mode 100644
-index 0000000..9c13d9d
---- /dev/null
-+++ b/src/lib/krad/attr.c
-@@ -0,0 +1,317 @@
-+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-+/* lib/krad/attr.c - RADIUS attribute functions for libkrad */
-+/*
-+ * Copyright 2013 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include <k5-int.h>
-+#include "internal.h"
-+
-+#include <string.h>
-+
-+/* RFC 2865 */
-+#define BLOCKSIZE 16
-+
-+typedef krb5_error_code
-+(*attribute_transform_fn)(krb5_context ctx, const char *secret,
-+ const unsigned char *auth, const krb5_data *in,
-+ unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
-+
-+typedef struct {
-+ const char *name;
-+ unsigned char minval;
-+ unsigned char maxval;
-+ attribute_transform_fn encode;
-+ attribute_transform_fn decode;
-+} attribute_record;
-+
-+static krb5_error_code
-+user_password_encode(krb5_context ctx, const char *secret,
-+ const unsigned char *auth, const krb5_data *in,
-+ unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
-+
-+static krb5_error_code
-+user_password_decode(krb5_context ctx, const char *secret,
-+ const unsigned char *auth, const krb5_data *in,
-+ unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
-+
-+static const attribute_record attributes[UCHAR_MAX] = {
-+ {"User-Name", 1, MAX_ATTRSIZE, NULL, NULL},
-+ {"User-Password", 1, 128, user_password_encode, user_password_decode},
-+ {"CHAP-Password", 17, 17, NULL, NULL},
-+ {"NAS-IP-Address", 4, 4, NULL, NULL},
-+ {"NAS-Port", 4, 4, NULL, NULL},
-+ {"Service-Type", 4, 4, NULL, NULL},
-+ {"Framed-Protocol", 4, 4, NULL, NULL},
-+ {"Framed-IP-Address", 4, 4, NULL, NULL},
-+ {"Framed-IP-Netmask", 4, 4, NULL, NULL},
-+ {"Framed-Routing", 4, 4, NULL, NULL},
-+ {"Filter-Id", 1, MAX_ATTRSIZE, NULL, NULL},
-+ {"Framed-MTU", 4, 4, NULL, NULL},
-+ {"Framed-Compression", 4, 4, NULL, NULL},
-+ {"Login-IP-Host", 4, 4, NULL, NULL},
-+ {"Login-Service", 4, 4, NULL, NULL},
-+ {"Login-TCP-Port", 4, 4, NULL, NULL},
-+ {NULL, 0, 0, NULL, NULL}, /* Unassigned */
-+ {"Reply-Message", 1, MAX_ATTRSIZE, NULL, NULL},
-+ {"Callback-Number", 1, MAX_ATTRSIZE, NULL, NULL},
-+ {"Callback-Id", 1, MAX_ATTRSIZE, NULL, NULL},
-+ {NULL, 0, 0, NULL, NULL}, /* Unassigned */
-+ {"Framed-Route", 1, MAX_ATTRSIZE, NULL, NULL},
-+ {"Framed-IPX-Network", 4, 4, NULL, NULL},
-+ {"State", 1, MAX_ATTRSIZE, NULL, NULL},
-+ {"Class", 1, MAX_ATTRSIZE, NULL, NULL},
-+ {"Vendor-Specific", 5, MAX_ATTRSIZE, NULL, NULL},
-+ {"Session-Timeout", 4, 4, NULL, NULL},
-+ {"Idle-Timeout", 4, 4, NULL, NULL},
-+ {"Termination-Action", 4, 4, NULL, NULL},
-+ {"Called-Station-Id", 1, MAX_ATTRSIZE, NULL, NULL},
-+ {"Calling-Station-Id", 1, MAX_ATTRSIZE, NULL, NULL},
-+ {"NAS-Identifier", 1, MAX_ATTRSIZE, NULL, NULL},
-+ {"Proxy-State", 1, MAX_ATTRSIZE, NULL, NULL},
-+ {"Login-LAT-Service", 1, MAX_ATTRSIZE, NULL, NULL},
-+ {"Login-LAT-Node", 1, MAX_ATTRSIZE, NULL, NULL},
-+ {"Login-LAT-Group", 32, 32, NULL, NULL},
-+ {"Framed-AppleTalk-Link", 4, 4, NULL, NULL},
-+ {"Framed-AppleTalk-Network", 4, 4, NULL, NULL},
-+ {"Framed-AppleTalk-Zone", 1, MAX_ATTRSIZE, NULL, NULL},
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
-+ {"CHAP-Challenge", 5, MAX_ATTRSIZE, NULL, NULL},
-+ {"NAS-Port-Type", 4, 4, NULL, NULL},
-+ {"Port-Limit", 4, 4, NULL, NULL},
-+ {"Login-LAT-Port", 1, MAX_ATTRSIZE, NULL, NULL},
-+};
-+
-+/* Encode User-Password attribute. */
-+static krb5_error_code
-+user_password_encode(krb5_context ctx, const char *secret,
-+ const unsigned char *auth, const krb5_data *in,
-+ unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen)
-+{
-+ const unsigned char *indx;
-+ krb5_error_code retval;
-+ unsigned int seclen;
-+ krb5_checksum sum;
-+ size_t blck, len, i;
-+ krb5_data tmp;
-+
-+ /* Copy the input buffer to the (zero-padded) output buffer. */
-+ len = (in->length + BLOCKSIZE - 1) / BLOCKSIZE * BLOCKSIZE;
-+ if (len > MAX_ATTRSIZE)
-+ return ENOBUFS;
-+ memset(outbuf, 0, len);
-+ memcpy(outbuf, in->data, in->length);
-+
-+ /* Create our temporary space for processing each block. */
-+ seclen = strlen(secret);
-+ retval = alloc_data(&tmp, seclen + BLOCKSIZE);
-+ if (retval != 0)
-+ return retval;
-+
-+ memcpy(tmp.data, secret, seclen);
-+ for (blck = 0, indx = auth; blck * BLOCKSIZE < len; blck++) {
-+ memcpy(tmp.data + seclen, indx, BLOCKSIZE);
-+
-+ retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &tmp,
-+ &sum);
-+ if (retval != 0) {
-+ zap(tmp.data, tmp.length);
-+ zap(outbuf, len);
-+ krb5_free_data_contents(ctx, &tmp);
-+ return retval;
-+ }
-+
-+ for (i = 0; i < BLOCKSIZE; i++)
-+ outbuf[blck * BLOCKSIZE + i] ^= sum.contents[i];
-+ krb5_free_checksum_contents(ctx, &sum);
-+
-+ indx = &outbuf[blck * BLOCKSIZE];
-+ }
-+
-+ zap(tmp.data, tmp.length);
-+ krb5_free_data_contents(ctx, &tmp);
-+ *outlen = len;
-+ return 0;
-+}
-+
-+/* Decode User-Password attribute. */
-+static krb5_error_code
-+user_password_decode(krb5_context ctx, const char *secret,
-+ const unsigned char *auth, const krb5_data *in,
-+ unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen)
-+{
-+ const unsigned char *indx;
-+ krb5_error_code retval;
-+ unsigned int seclen;
-+ krb5_checksum sum;
-+ ssize_t blck, i;
-+ krb5_data tmp;
-+
-+ if (in->length % BLOCKSIZE != 0)
-+ return EINVAL;
-+ if (in->length > MAX_ATTRSIZE)
-+ return ENOBUFS;
-+
-+ /* Create our temporary space for processing each block. */
-+ seclen = strlen(secret);
-+ retval = alloc_data(&tmp, seclen + BLOCKSIZE);
-+ if (retval != 0)
-+ return retval;
-+
-+ memcpy(tmp.data, secret, seclen);
-+ for (blck = 0, indx = auth; blck * BLOCKSIZE < in->length; blck++) {
-+ memcpy(tmp.data + seclen, indx, BLOCKSIZE);
-+
-+ retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0,
-+ &tmp, &sum);
-+ if (retval != 0) {
-+ zap(tmp.data, tmp.length);
-+ zap(outbuf, in->length);
-+ krb5_free_data_contents(ctx, &tmp);
-+ return retval;
-+ }
-+
-+ for (i = 0; i < BLOCKSIZE; i++) {
-+ outbuf[blck * BLOCKSIZE + i] = in->data[blck * BLOCKSIZE + i] ^
-+ sum.contents[i];
-+ }
-+ krb5_free_checksum_contents(ctx, &sum);
-+
-+ indx = (const unsigned char *)&in->data[blck * BLOCKSIZE];
-+ }
-+
-+ /* Strip off trailing NULL bytes. */
-+ *outlen = in->length;
-+ while (*outlen > 0 && outbuf[*outlen - 1] == '\0')
-+ (*outlen)--;
-+
-+ krb5_free_data_contents(ctx, &tmp);
-+ return 0;
-+}
-+
-+krb5_error_code
-+kr_attr_valid(krad_attr type, const krb5_data *data)
-+{
-+ const attribute_record *ar;
-+
-+ if (type == 0)
-+ return EINVAL;
-+
-+ ar = &attributes[type - 1];
-+ return (data->length >= ar->minval && data->length <= ar->maxval) ? 0 :
-+ EMSGSIZE;
-+}
-+
-+krb5_error_code
-+kr_attr_encode(krb5_context ctx, const char *secret,
-+ const unsigned char *auth, krad_attr type,
-+ const krb5_data *in, unsigned char outbuf[MAX_ATTRSIZE],
-+ size_t *outlen)
-+{
-+ krb5_error_code retval;
-+
-+ retval = kr_attr_valid(type, in);
-+ if (retval != 0)
-+ return retval;
-+
-+ if (attributes[type - 1].encode == NULL) {
-+ if (in->length > MAX_ATTRSIZE)
-+ return ENOBUFS;
-+
-+ *outlen = in->length;
-+ memcpy(outbuf, in->data, in->length);
-+ return 0;
-+ }
-+
-+ return attributes[type - 1].encode(ctx, secret, auth, in, outbuf, outlen);
-+}
-+
-+krb5_error_code
-+kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
-+ krad_attr type, const krb5_data *in,
-+ unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen)
-+{
-+ krb5_error_code retval;
-+
-+ retval = kr_attr_valid(type, in);
-+ if (retval != 0)
-+ return retval;
-+
-+ if (attributes[type - 1].encode == NULL) {
-+ if (in->length > MAX_ATTRSIZE)
-+ return ENOBUFS;
-+
-+ *outlen = in->length;
-+ memcpy(outbuf, in->data, in->length);
-+ return 0;
-+ }
-+
-+ return attributes[type - 1].decode(ctx, secret, auth, in, outbuf, outlen);
-+}
-+
-+krad_attr
-+krad_attr_name2num(const char *name)
-+{
-+ unsigned char i;
-+
-+ for (i = 0; i < UCHAR_MAX; i++) {
-+ if (attributes[i].name == NULL)
-+ continue;
-+
-+ if (strcmp(attributes[i].name, name) == 0)
-+ return i + 1;
-+ }
-+
-+ return 0;
-+}
-+
-+const char *
-+krad_attr_num2name(krad_attr type)
-+{
-+ if (type == 0)
-+ return NULL;
-+
-+ return attributes[type - 1].name;
-+}
-diff --git a/src/lib/krad/attrset.c b/src/lib/krad/attrset.c
-new file mode 100644
-index 0000000..fbd0621
---- /dev/null
-+++ b/src/lib/krad/attrset.c
-@@ -0,0 +1,244 @@
-+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-+/* lib/krad/attrset.c - RADIUS attribute set functions for libkrad */
-+/*
-+ * Copyright 2013 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include <k5-int.h>
-+#include <k5-queue.h>
-+#include "internal.h"
-+
-+#include <string.h>
-+
-+TAILQ_HEAD(attr_head, attr_st);
-+
-+typedef struct attr_st attr;
-+struct attr_st {
-+ TAILQ_ENTRY(attr_st) list;
-+ krad_attr type;
-+ krb5_data attr;
-+ char buffer[MAX_ATTRSIZE];
-+};
-+
-+struct krad_attrset_st {
-+ krb5_context ctx;
-+ struct attr_head list;
-+};
-+
-+krb5_error_code
-+krad_attrset_new(krb5_context ctx, krad_attrset **set)
-+{
-+ krad_attrset *tmp;
-+
-+ tmp = calloc(1, sizeof(krad_attrset));
-+ if (tmp == NULL)
-+ return ENOMEM;
-+ tmp->ctx = ctx;
-+ TAILQ_INIT(&tmp->list);
-+
-+ *set = tmp;
-+ return 0;
-+}
-+
-+void
-+krad_attrset_free(krad_attrset *set)
-+{
-+ attr *a;
-+
-+ if (set == NULL)
-+ return;
-+
-+ while (!TAILQ_EMPTY(&set->list)) {
-+ a = TAILQ_FIRST(&set->list);
-+ TAILQ_REMOVE(&set->list, a, list);
-+ zap(a->buffer, sizeof(a->buffer));
-+ free(a);
-+ }
-+
-+ free(set);
-+}
-+
-+krb5_error_code
-+krad_attrset_add(krad_attrset *set, krad_attr type, const krb5_data *data)
-+{
-+ krb5_error_code retval;
-+ attr *tmp;
-+
-+ retval = kr_attr_valid(type, data);
-+ if (retval != 0)
-+ return retval;
-+
-+ tmp = calloc(1, sizeof(attr));
-+ if (tmp == NULL)
-+ return ENOMEM;
-+
-+ tmp->type = type;
-+ tmp->attr = make_data(tmp->buffer, data->length);
-+ memcpy(tmp->attr.data, data->data, data->length);
-+
-+ TAILQ_INSERT_TAIL(&set->list, tmp, list);
-+ return 0;
-+}
-+
-+krb5_error_code
-+krad_attrset_add_number(krad_attrset *set, krad_attr type, krb5_ui_4 num)
-+{
-+ krb5_data data;
-+
-+ num = htonl(num);
-+ data = make_data(&num, sizeof(num));
-+ return krad_attrset_add(set, type, &data);
-+}
-+
-+void
-+krad_attrset_del(krad_attrset *set, krad_attr type, size_t indx)
-+{
-+ attr *a;
-+
-+ TAILQ_FOREACH(a, &set->list, list) {
-+ if (a->type == type && indx-- == 0) {
-+ TAILQ_REMOVE(&set->list, a, list);
-+ zap(a->buffer, sizeof(a->buffer));
-+ free(a);
-+ return;
-+ }
-+ }
-+}
-+
-+const krb5_data *
-+krad_attrset_get(const krad_attrset *set, krad_attr type, size_t indx)
-+{
-+ attr *a;
-+
-+ TAILQ_FOREACH(a, &set->list, list) {
-+ if (a->type == type && indx-- == 0)
-+ return &a->attr;
-+ }
-+
-+ return NULL;
-+}
-+
-+krb5_error_code
-+krad_attrset_copy(const krad_attrset *set, krad_attrset **copy)
-+{
-+ krb5_error_code retval;
-+ krad_attrset *tmp;
-+ attr *a;
-+
-+ retval = krad_attrset_new(set->ctx, &tmp);
-+ if (retval != 0)
-+ return retval;
-+
-+ TAILQ_FOREACH(a, &set->list, list) {
-+ retval = krad_attrset_add(tmp, a->type, &a->attr);
-+ if (retval != 0) {
-+ krad_attrset_free(tmp);
-+ return retval;
-+ }
-+ }
-+
-+ *copy = tmp;
-+ return 0;
-+}
-+
-+krb5_error_code
-+kr_attrset_encode(const krad_attrset *set, const char *secret,
-+ const unsigned char *auth,
-+ unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen)
-+{
-+ unsigned char buffer[MAX_ATTRSIZE];
-+ krb5_error_code retval;
-+ size_t i = 0, attrlen;
-+ attr *a;
-+
-+ if (set == NULL) {
-+ *outlen = 0;
-+ return 0;
-+ }
-+
-+ TAILQ_FOREACH(a, &set->list, list) {
-+ retval = kr_attr_encode(set->ctx, secret, auth, a->type, &a->attr,
-+ buffer, &attrlen);
-+ if (retval != 0)
-+ return retval;
-+
-+ if (i + attrlen + 2 > MAX_ATTRSETSIZE)
-+ return EMSGSIZE;
-+
-+ outbuf[i++] = a->type;
-+ outbuf[i++] = attrlen + 2;
-+ memcpy(&outbuf[i], buffer, attrlen);
-+ i += attrlen;
-+ }
-+
-+ *outlen = i;
-+ return 0;
-+}
-+
-+krb5_error_code
-+kr_attrset_decode(krb5_context ctx, const krb5_data *in, const char *secret,
-+ const unsigned char *auth, krad_attrset **set_out)
-+{
-+ unsigned char buffer[MAX_ATTRSIZE];
-+ krb5_data tmp;
-+ krb5_error_code retval;
-+ krad_attr type;
-+ krad_attrset *set;
-+ size_t i, len;
-+
-+ *set_out = NULL;
-+
-+ retval = krad_attrset_new(ctx, &set);
-+ if (retval != 0)
-+ return retval;
-+
-+ for (i = 0; i + 2 < in->length; ) {
-+ type = in->data[i++];
-+ tmp = make_data(&in->data[i + 1], in->data[i] - 2);
-+ i += tmp.length + 1;
-+
-+ retval = (in->length < i) ? EBADMSG : 0;
-+ if (retval != 0)
-+ goto cleanup;
-+
-+ retval = kr_attr_decode(ctx, secret, auth, type, &tmp, buffer, &len);
-+ if (retval != 0)
-+ goto cleanup;
-+
-+ tmp = make_data(buffer, len);
-+ retval = krad_attrset_add(set, type, &tmp);
-+ if (retval != 0)
-+ goto cleanup;
-+ }
-+
-+ *set_out = set;
-+ set = NULL;
-+
-+cleanup:
-+ zap(buffer, sizeof(buffer));
-+ krad_attrset_free(set);
-+ return retval;
-+}
-diff --git a/src/lib/krad/client.c b/src/lib/krad/client.c
-new file mode 100644
-index 0000000..0c37680
---- /dev/null
-+++ b/src/lib/krad/client.c
-@@ -0,0 +1,335 @@
-+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-+/* lib/krad/client.c - Client request code for libkrad */
-+/*
-+ * Copyright 2013 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include <k5-queue.h>
-+#include "internal.h"
-+
-+#include <string.h>
-+#include <sys/un.h>
-+#include <unistd.h>
-+#include <stdio.h>
-+#include <limits.h>
-+
-+LIST_HEAD(server_head, server_st);
-+
-+typedef struct remote_state_st remote_state;
-+typedef struct request_st request;
-+typedef struct server_st server;
-+
-+struct remote_state_st {
-+ const krad_packet *packet;
-+ krad_remote *remote;
-+};
-+
-+struct request_st {
-+ krad_client *rc;
-+
-+ krad_code code;
-+ krad_attrset *attrs;
-+ int 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, int 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, int timeout,
-+ size_t retries, krad_cb cb, void *data)
-+{
-+ struct addrinfo usock, *ai = NULL;
-+ krb5_error_code retval;
-+ struct sockaddr_un ua;
-+ request *req;
-+
-+ if (remote[0] == '/') {
-+ ua.sun_family = AF_UNIX;
-+ snprintf(ua.sun_path, sizeof(ua.sun_path), "%s", remote);
-+ memset(&usock, 0, sizeof(usock));
-+ usock.ai_family = AF_UNIX;
-+ usock.ai_socktype = SOCK_STREAM;
-+ usock.ai_addr = (struct sockaddr *)&ua;
-+ usock.ai_addrlen = sizeof(ua);
-+
-+ retval = request_new(rc, code, attrs, &usock, secret, timeout, retries,
-+ cb, data, &req);
-+ } else {
-+ retval = resolve_remote(remote, &ai);
-+ if (retval == 0) {
-+ retval = request_new(rc, code, attrs, ai, secret, timeout, retries,
-+ cb, data, &req);
-+ freeaddrinfo(ai);
-+ }
-+ }
-+ if (retval != 0)
-+ return retval;
-+
-+ retval = kr_remote_send(req->remotes[req->current].remote, req->code,
-+ req->attrs, on_response, req, req->timeout,
-+ req->retries, &req->remotes[req->current].packet);
-+ if (retval != 0) {
-+ request_free(req);
-+ return retval;
-+ }
-+
-+ return 0;
-+}
-diff --git a/src/lib/krad/code.c b/src/lib/krad/code.c
-new file mode 100644
-index 0000000..16871bb
---- /dev/null
-+++ b/src/lib/krad/code.c
-@@ -0,0 +1,111 @@
-+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-+/* lib/krad/code.c - RADIUS code name table for libkrad */
-+/*
-+ * Copyright 2013 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include "internal.h"
-+
-+#include <string.h>
-+
-+static const char *codes[UCHAR_MAX] = {
-+ "Access-Request",
-+ "Access-Accept",
-+ "Access-Reject",
-+ "Accounting-Request",
-+ "Accounting-Response",
-+ "Accounting-Status",
-+ "Password-Request",
-+ "Password-Ack",
-+ "Password-Reject",
-+ "Accounting-Message",
-+ "Access-Challenge",
-+ "Status-Server",
-+ "Status-Client",
-+ NULL,
-+ NULL,
-+ NULL,
-+ NULL,
-+ NULL,
-+ NULL,
-+ NULL,
-+ "Resource-Free-Request",
-+ "Resource-Free-Response",
-+ "Resource-Query-Request",
-+ "Resource-Query-Response",
-+ "Alternate-Resource-Reclaim-Request",
-+ "NAS-Reboot-Request",
-+ "NAS-Reboot-Response",
-+ NULL,
-+ "Next-Passcode",
-+ "New-Pin",
-+ "Terminate-Session",
-+ "Password-Expired",
-+ "Event-Request",
-+ "Event-Response",
-+ NULL,
-+ NULL,
-+ NULL,
-+ NULL,
-+ NULL,
-+ "Disconnect-Request",
-+ "Disconnect-Ack",
-+ "Disconnect-Nak",
-+ "Change-Filters-Request",
-+ "Change-Filters-Ack",
-+ "Change-Filters-Nak",
-+ NULL,
-+ NULL,
-+ NULL,
-+ NULL,
-+ "IP-Address-Allocate",
-+ "IP-Address-Release",
-+};
-+
-+krad_code
-+krad_code_name2num(const char *name)
-+{
-+ unsigned char i;
-+
-+ for (i = 0; i < UCHAR_MAX; i++) {
-+ if (codes[i] == NULL)
-+ continue;
-+
-+ if (strcmp(codes[i], name) == 0)
-+ return ++i;
-+ }
-+
-+ return 0;
-+}
-+
-+const char *
-+krad_code_num2name(krad_code code)
-+{
-+ if (code == 0)
-+ return NULL;
-+
-+ return codes[code - 1];
-+}
-diff --git a/src/lib/krad/deps b/src/lib/krad/deps
-new file mode 100644
-index 0000000..8171f94
---- /dev/null
-+++ b/src/lib/krad/deps
-@@ -0,0 +1,156 @@
-+#
-+# Generated makefile dependencies follow.
-+#
-+attr.so attr.po $(OUTPRE)attr.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
-+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
-+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
-+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
-+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
-+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
-+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
-+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krad.h \
-+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
-+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
-+ $(top_srcdir)/include/socket-utils.h attr.c internal.h
-+attrset.so attrset.po $(OUTPRE)attrset.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
-+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
-+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
-+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
-+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
-+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
-+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-queue.h \
-+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
-+ $(top_srcdir)/include/krad.h $(top_srcdir)/include/krb5.h \
-+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
-+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
-+ attrset.c internal.h
-+client.so client.po $(OUTPRE)client.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
-+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
-+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
-+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
-+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
-+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
-+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-queue.h \
-+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
-+ $(top_srcdir)/include/krad.h $(top_srcdir)/include/krb5.h \
-+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
-+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
-+ client.c internal.h
-+code.so code.po $(OUTPRE)code.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
-+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
-+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
-+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
-+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
-+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
-+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
-+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krad.h \
-+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
-+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
-+ $(top_srcdir)/include/socket-utils.h code.c internal.h
-+packet.so packet.po $(OUTPRE)packet.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
-+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
-+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
-+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
-+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
-+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
-+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
-+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krad.h \
-+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
-+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
-+ $(top_srcdir)/include/socket-utils.h internal.h packet.c
-+remote.so remote.po $(OUTPRE)remote.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
-+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
-+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
-+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
-+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
-+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
-+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-queue.h \
-+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
-+ $(top_srcdir)/include/krad.h $(top_srcdir)/include/krb5.h \
-+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
-+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
-+ internal.h remote.c
-+t_attr.so t_attr.po $(OUTPRE)t_attr.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
-+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
-+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
-+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
-+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
-+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
-+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
-+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krad.h \
-+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
-+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
-+ $(top_srcdir)/include/socket-utils.h internal.h t_attr.c \
-+ t_test.h
-+t_attrset.so t_attrset.po $(OUTPRE)t_attrset.$(OBJEXT): \
-+ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
-+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
-+ $(COM_ERR_DEPS) $(VERTO_DEPS) $(top_srcdir)/include/k5-buf.h \
-+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
-+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
-+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
-+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
-+ $(top_srcdir)/include/krad.h $(top_srcdir)/include/krb5.h \
-+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
-+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
-+ internal.h t_attrset.c t_test.h
-+t_client.so t_client.po $(OUTPRE)t_client.$(OBJEXT): \
-+ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
-+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
-+ $(COM_ERR_DEPS) $(VERTO_DEPS) $(top_srcdir)/include/k5-buf.h \
-+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
-+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
-+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
-+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
-+ $(top_srcdir)/include/krad.h $(top_srcdir)/include/krb5.h \
-+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
-+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
-+ internal.h t_client.c t_daemon.h t_test.h
-+t_code.so t_code.po $(OUTPRE)t_code.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
-+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
-+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
-+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
-+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
-+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
-+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
-+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krad.h \
-+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
-+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
-+ $(top_srcdir)/include/socket-utils.h internal.h t_code.c \
-+ t_test.h
-+t_packet.so t_packet.po $(OUTPRE)t_packet.$(OBJEXT): \
-+ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
-+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
-+ $(COM_ERR_DEPS) $(VERTO_DEPS) $(top_srcdir)/include/k5-buf.h \
-+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
-+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
-+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
-+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
-+ $(top_srcdir)/include/krad.h $(top_srcdir)/include/krb5.h \
-+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
-+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
-+ internal.h t_daemon.h t_packet.c t_test.h
-+t_remote.so t_remote.po $(OUTPRE)t_remote.$(OBJEXT): \
-+ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
-+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
-+ $(COM_ERR_DEPS) $(VERTO_DEPS) $(top_srcdir)/include/k5-buf.h \
-+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
-+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
-+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
-+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
-+ $(top_srcdir)/include/krad.h $(top_srcdir)/include/krb5.h \
-+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
-+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
-+ internal.h t_daemon.h t_remote.c t_test.h
-+t_test.so t_test.po $(OUTPRE)t_test.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
-+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
-+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
-+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
-+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
-+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
-+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
-+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krad.h \
-+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
-+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
-+ $(top_srcdir)/include/socket-utils.h internal.h t_test.c \
-+ t_test.h
-diff --git a/src/lib/krad/internal.h b/src/lib/krad/internal.h
-new file mode 100644
-index 0000000..996a893
---- /dev/null
-+++ b/src/lib/krad/internal.h
-@@ -0,0 +1,155 @@
-+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-+/* lib/krad/internal.h - Internal declarations for libkrad */
-+/*
-+ * Copyright 2013 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#ifndef INTERNAL_H_
-+#define INTERNAL_H_
-+
-+#include <k5-int.h>
-+#include "krad.h"
-+
-+#include <errno.h>
-+
-+#include <sys/types.h>
-+#include <sys/socket.h>
-+#include <netdb.h>
-+
-+#ifndef UCHAR_MAX
-+#define UCHAR_MAX 255
-+#endif
-+
-+/* RFC 2865 */
-+#define MAX_ATTRSIZE (UCHAR_MAX - 2)
-+#define MAX_ATTRSETSIZE (KRAD_PACKET_SIZE_MAX - 20)
-+
-+typedef struct krad_remote_st krad_remote;
-+
-+/* Validate constraints of an attribute. */
-+krb5_error_code
-+kr_attr_valid(krad_attr type, const krb5_data *data);
-+
-+/* Encode an attribute. */
-+krb5_error_code
-+kr_attr_encode(krb5_context ctx, const char *secret, const unsigned char *auth,
-+ krad_attr type, const krb5_data *in,
-+ unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
-+
-+/* Decode an attribute. */
-+krb5_error_code
-+kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
-+ krad_attr type, const krb5_data *in,
-+ unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
-+
-+/* Encode the attributes into the buffer. */
-+krb5_error_code
-+kr_attrset_encode(const krad_attrset *set, const char *secret,
-+ const unsigned char *auth,
-+ unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen);
-+
-+/* Decode attributes from a buffer. */
-+krb5_error_code
-+kr_attrset_decode(krb5_context ctx, const krb5_data *in, const char *secret,
-+ const unsigned char *auth, krad_attrset **set);
-+
-+/* Create a new remote object which manages a socket and the state of
-+ * outstanding requests. */
-+krb5_error_code
-+kr_remote_new(krb5_context kctx, verto_ctx *vctx, const struct addrinfo *info,
-+ const char *secret, krad_remote **rr);
-+
-+/* Free a remote object. */
-+void
-+kr_remote_free(krad_remote *rr);
-+
-+/*
-+ * Send the packet to the remote. The cb will be called when a response is
-+ * received, the request times out, the request is canceled or an error occurs.
-+ *
-+ * The timeout parameter is the total timeout across all retries in
-+ * milliseconds.
-+ *
-+ * If the cb is called with a retval of ETIMEDOUT it indicates that the alloted
-+ * time has elapsed. However, in the case of a timeout, we continue to listen
-+ * for the packet until krad_remote_cancel() is called or a response is
-+ * received. This means that cb will always be called twice in the event of a
-+ * timeout. This permits you to pursue other remotes while still listening for
-+ * a response from the first one.
-+ */
-+krb5_error_code
-+kr_remote_send(krad_remote *rr, krad_code code, krad_attrset *attrs,
-+ krad_cb cb, void *data, int 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..6e6b27e
---- /dev/null
-+++ b/src/lib/krad/packet.c
-@@ -0,0 +1,470 @@
-+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-+/* lib/krad/packet.c - Packet functions for libkrad */
-+/*
-+ * Copyright 2013 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include "internal.h"
-+
-+#include <string.h>
-+
-+#include <arpa/inet.h>
-+
-+typedef unsigned char uchar;
-+
-+/* RFC 2865 */
-+#define OFFSET_CODE 0
-+#define OFFSET_ID 1
-+#define OFFSET_LENGTH 2
-+#define OFFSET_AUTH 4
-+#define OFFSET_ATTR 20
-+#define AUTH_FIELD_SIZE (OFFSET_ATTR - OFFSET_AUTH)
-+
-+#define offset(d, o) (&(d)->data[o])
-+#define pkt_code_get(p) (*(krad_code *)offset(&(p)->pkt, OFFSET_CODE))
-+#define pkt_code_set(p, v) (*(krad_code *)offset(&(p)->pkt, OFFSET_CODE)) = v
-+#define pkt_id_get(p) (*(uchar *)offset(&(p)->pkt, OFFSET_ID))
-+#define pkt_id_set(p, v) (*(uchar *)offset(&(p)->pkt, OFFSET_ID)) = v
-+#define pkt_len_get(p) load_16_be(offset(&(p)->pkt, OFFSET_LENGTH))
-+#define pkt_len_set(p, v) store_16_be(v, offset(&(p)->pkt, OFFSET_LENGTH))
-+#define pkt_auth(p) ((uchar *)offset(&(p)->pkt, OFFSET_AUTH))
-+#define pkt_attr(p) ((unsigned char *)offset(&(p)->pkt, OFFSET_ATTR))
-+
-+struct krad_packet_st {
-+ char buffer[KRAD_PACKET_SIZE_MAX];
-+ krad_attrset *attrset;
-+ krb5_data pkt;
-+};
-+
-+typedef struct {
-+ uchar x[(UCHAR_MAX + 1) / 8];
-+} idmap;
-+
-+/* Ensure the map is empty. */
-+static inline void
-+idmap_init(idmap *map)
-+{
-+ memset(map, 0, sizeof(*map));
-+}
-+
-+/* Set an id as already allocated. */
-+static inline void
-+idmap_set(idmap *map, uchar id)
-+{
-+ map->x[id / 8] |= 1 << (id % 8);
-+}
-+
-+/* Determine whether or not an id is used. */
-+static inline krb5_boolean
-+idmap_isset(const idmap *map, uchar id)
-+{
-+ return (map->x[id / 8] & (1 << (id % 8))) != 0;
-+}
-+
-+/* Find an unused id starting the search at the value specified in id.
-+ * NOTE: For optimal security, the initial value of id should be random. */
-+static inline krb5_error_code
-+idmap_find(const idmap *map, uchar *id)
-+{
-+ krb5_int16 i;
-+
-+ for (i = *id; i >= 0 && i <= UCHAR_MAX; *id % 2 == 0 ? i++ : i--) {
-+ if (!idmap_isset(map, i))
-+ goto success;
-+ }
-+
-+ for (i = *id; i >= 0 && i <= UCHAR_MAX; *id % 2 == 1 ? i++ : i--) {
-+ if (!idmap_isset(map, i))
-+ goto success;
-+ }
-+
-+ return ERANGE;
-+
-+success:
-+ *id = i;
-+ return 0;
-+}
-+
-+/* Generate size bytes of random data into the buffer. */
-+static inline krb5_error_code
-+randomize(krb5_context ctx, void *buffer, unsigned int size)
-+{
-+ krb5_data rdata = make_data(buffer, size);
-+ return krb5_c_random_make_octets(ctx, &rdata);
-+}
-+
-+/* Generate a radius packet id. */
-+static krb5_error_code
-+id_generate(krb5_context ctx, krad_packet_iter_cb cb, void *data, uchar *id)
-+{
-+ krb5_error_code retval;
-+ const krad_packet *tmp;
-+ idmap used;
-+ uchar i;
-+
-+ retval = randomize(ctx, &i, sizeof(i));
-+ if (retval != 0) {
-+ if (cb != NULL)
-+ (*cb)(data, TRUE);
-+ return retval;
-+ }
-+
-+ if (cb != NULL) {
-+ idmap_init(&used);
-+ for (tmp = (*cb)(data, FALSE); tmp != NULL; tmp = (*cb)(data, FALSE))
-+ idmap_set(&used, tmp->pkt.data[1]);
-+
-+ retval = idmap_find(&used, &i);
-+ if (retval != 0)
-+ return retval;
-+ }
-+
-+ *id = i;
-+ return 0;
-+}
-+
-+/* Generate a random authenticator field. */
-+static krb5_error_code
-+auth_generate_random(krb5_context ctx, uchar *rauth)
-+{
-+ krb5_ui_4 trunctime;
-+ time_t currtime;
-+
-+ /* Get the least-significant four bytes of the current time. */
-+ currtime = time(NULL);
-+ if (currtime == (time_t)-1)
-+ return errno;
-+ trunctime = (krb5_ui_4)currtime;
-+ memcpy(rauth, &trunctime, sizeof(trunctime));
-+
-+ /* Randomize the rest of the buffer. */
-+ return randomize(ctx, rauth + sizeof(trunctime),
-+ AUTH_FIELD_SIZE - sizeof(trunctime));
-+}
-+
-+/* Generate a response authenticator field. */
-+static krb5_error_code
-+auth_generate_response(krb5_context ctx, const char *secret,
-+ const krad_packet *response, const uchar *auth,
-+ uchar *rauth)
-+{
-+ krb5_error_code retval;
-+ krb5_checksum hash;
-+ krb5_data data;
-+
-+ /* Allocate the temporary buffer. */
-+ retval = alloc_data(&data, response->pkt.length + strlen(secret));
-+ if (retval != 0)
-+ return retval;
-+
-+ /* Encoded RADIUS packet with the request's
-+ * authenticator and the secret at the end. */
-+ memcpy(data.data, response->pkt.data, response->pkt.length);
-+ memcpy(data.data + OFFSET_AUTH, auth, AUTH_FIELD_SIZE);
-+ memcpy(data.data + response->pkt.length, secret, strlen(secret));
-+
-+ /* Hash it. */
-+ retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &data,
-+ &hash);
-+ free(data.data);
-+ if (retval != 0)
-+ return retval;
-+
-+ memcpy(rauth, hash.contents, AUTH_FIELD_SIZE);
-+ krb5_free_checksum_contents(ctx, &hash);
-+ return 0;
-+}
-+
-+/* Create a new packet. */
-+static krad_packet *
-+packet_new()
-+{
-+ krad_packet *pkt;
-+
-+ pkt = calloc(1, sizeof(krad_packet));
-+ if (pkt == NULL)
-+ return NULL;
-+ pkt->pkt = make_data(pkt->buffer, sizeof(pkt->buffer));
-+
-+ return pkt;
-+}
-+
-+/* Set the attrset object by decoding the packet. */
-+static krb5_error_code
-+packet_set_attrset(krb5_context ctx, const char *secret, krad_packet *pkt)
-+{
-+ krb5_data tmp;
-+
-+ tmp = make_data(pkt_attr(pkt), pkt->pkt.length - OFFSET_ATTR);
-+ return kr_attrset_decode(ctx, &tmp, secret, pkt_auth(pkt), &pkt->attrset);
-+}
-+
-+ssize_t
-+krad_packet_bytes_needed(const krb5_data *buffer)
-+{
-+ size_t len;
-+
-+ if (buffer->length < OFFSET_AUTH)
-+ return OFFSET_AUTH - buffer->length;
-+
-+ len = load_16_be(offset(buffer, OFFSET_LENGTH));
-+ if (len > KRAD_PACKET_SIZE_MAX)
-+ return -1;
-+
-+ return buffer->length > len ? 0 : len - buffer->length;
-+}
-+
-+void
-+krad_packet_free(krad_packet *pkt)
-+{
-+ if (pkt)
-+ krad_attrset_free(pkt->attrset);
-+ free(pkt);
-+}
-+
-+/* Create a new request packet. */
-+krb5_error_code
-+krad_packet_new_request(krb5_context ctx, const char *secret, krad_code code,
-+ const krad_attrset *set, krad_packet_iter_cb cb,
-+ void *data, krad_packet **request)
-+{
-+ krb5_error_code retval;
-+ krad_packet *pkt;
-+ uchar id;
-+ size_t attrset_len;
-+
-+ pkt = packet_new();
-+ if (pkt == NULL) {
-+ if (cb != NULL)
-+ (*cb)(data, TRUE);
-+ return ENOMEM;
-+ }
-+
-+ /* Generate the ID. */
-+ retval = id_generate(ctx, cb, data, &id);
-+ if (retval != 0)
-+ goto error;
-+ pkt_id_set(pkt, id);
-+
-+ /* Generate the authenticator. */
-+ retval = auth_generate_random(ctx, pkt_auth(pkt));
-+ if (retval != 0)
-+ goto error;
-+
-+ /* Encode the attributes. */
-+ retval = kr_attrset_encode(set, secret, pkt_auth(pkt), pkt_attr(pkt),
-+ &attrset_len);
-+ if (retval != 0)
-+ goto error;
-+
-+ /* Set the code, ID and length. */
-+ pkt->pkt.length = attrset_len + OFFSET_ATTR;
-+ pkt_code_set(pkt, code);
-+ pkt_len_set(pkt, pkt->pkt.length);
-+
-+ /* Copy the attrset for future use. */
-+ retval = packet_set_attrset(ctx, secret, pkt);
-+ if (retval != 0)
-+ goto error;
-+
-+ *request = pkt;
-+ return 0;
-+
-+error:
-+ free(pkt);
-+ return retval;
-+}
-+
-+/* Create a new request packet. */
-+krb5_error_code
-+krad_packet_new_response(krb5_context ctx, const char *secret, krad_code code,
-+ const krad_attrset *set, const krad_packet *request,
-+ krad_packet **response)
-+{
-+ krb5_error_code retval;
-+ krad_packet *pkt;
-+ size_t attrset_len;
-+
-+ pkt = packet_new();
-+ if (pkt == NULL)
-+ return ENOMEM;
-+
-+ /* Encode the attributes. */
-+ retval = kr_attrset_encode(set, secret, pkt_auth(request), pkt_attr(pkt),
-+ &attrset_len);
-+ if (retval != 0)
-+ goto error;
-+
-+ /* Set the code, ID and length. */
-+ pkt->pkt.length = attrset_len + OFFSET_ATTR;
-+ pkt_code_set(pkt, code);
-+ pkt_id_set(pkt, pkt_id_get(request));
-+ pkt_len_set(pkt, pkt->pkt.length);
-+
-+ /* Generate the authenticator. */
-+ retval = auth_generate_response(ctx, secret, pkt, pkt_auth(request),
-+ pkt_auth(pkt));
-+ if (retval != 0)
-+ goto error;
-+
-+ /* Copy the attrset for future use. */
-+ retval = packet_set_attrset(ctx, secret, pkt);
-+ if (retval != 0)
-+ goto error;
-+
-+ *response = pkt;
-+ return 0;
-+
-+error:
-+ free(pkt);
-+ return retval;
-+}
-+
-+/* Decode a packet. */
-+static krb5_error_code
-+decode_packet(krb5_context ctx, const char *secret, const krb5_data *buffer,
-+ krad_packet **pkt)
-+{
-+ krb5_error_code retval;
-+ krad_packet *tmp;
-+ krb5_ui_2 len;
-+
-+ tmp = packet_new();
-+ if (tmp == NULL) {
-+ retval = ENOMEM;
-+ goto error;
-+ }
-+
-+ /* Ensure a proper message length. */
-+ retval = buffer->length < OFFSET_ATTR ? EMSGSIZE : 0;
-+ if (retval != 0)
-+ goto error;
-+ len = load_16_be(offset(buffer, OFFSET_LENGTH));
-+ retval = len < OFFSET_ATTR ? EBADMSG : 0;
-+ if (retval != 0)
-+ goto error;
-+ retval = (len > buffer->length || len > tmp->pkt.length) ? EBADMSG : 0;
-+ if (retval != 0)
-+ goto error;
-+
-+ /* Copy over the buffer. */
-+ tmp->pkt.length = len;
-+ memcpy(tmp->pkt.data, buffer->data, len);
-+
-+ /* Parse the packet to ensure it is well-formed. */
-+ retval = packet_set_attrset(ctx, secret, tmp);
-+ if (retval != 0)
-+ goto error;
-+
-+ *pkt = tmp;
-+ return 0;
-+
-+error:
-+ krad_packet_free(tmp);
-+ return retval;
-+}
-+
-+krb5_error_code
-+krad_packet_decode_request(krb5_context ctx, const char *secret,
-+ const krb5_data *buffer, krad_packet_iter_cb cb,
-+ void *data, const krad_packet **duppkt,
-+ krad_packet **reqpkt)
-+{
-+ const krad_packet *tmp = NULL;
-+ krb5_error_code retval;
-+
-+ retval = decode_packet(ctx, secret, buffer, reqpkt);
-+ if (cb != NULL && retval == 0) {
-+ for (tmp = (*cb)(data, FALSE); tmp != NULL; tmp = (*cb)(data, FALSE)) {
-+ if (pkt_id_get(*reqpkt) == pkt_id_get(tmp))
-+ break;
-+ }
-+ }
-+
-+ if (cb != NULL && (retval != 0 || tmp != NULL))
-+ (*cb)(data, TRUE);
-+
-+ *duppkt = tmp;
-+ 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 matches, then the response is valid. */
-+ if (memcmp(pkt_auth(*rsppkt), auth, sizeof(auth)) == 0)
-+ break;
-+ }
-+ }
-+
-+ if (cb != NULL && (retval != 0 || tmp != NULL))
-+ (*cb)(data, TRUE);
-+
-+ *reqpkt = tmp;
-+ 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..74d9865
---- /dev/null
-+++ b/src/lib/krad/remote.c
-@@ -0,0 +1,519 @@
-+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-+/* lib/krad/remote.c - Protocol code for libkrad */
-+/*
-+ * Copyright 2013 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include <k5-int.h>
-+#include <k5-queue.h>
-+#include "internal.h"
-+
-+#include <string.h>
-+#include <unistd.h>
-+
-+#include <sys/un.h>
-+
-+#define FLAGS_READ (VERTO_EV_FLAG_PERSIST | VERTO_EV_FLAG_IO_CLOSE_FD | \
-+ VERTO_EV_FLAG_IO_ERROR | VERTO_EV_FLAG_IO_READ)
-+#define FLAGS_WRITE (FLAGS_READ | VERTO_EV_FLAG_IO_WRITE)
-+
-+TAILQ_HEAD(request_head, request_st);
-+
-+typedef struct request_st request;
-+struct request_st {
-+ TAILQ_ENTRY(request_st) list;
-+ krad_remote *rr;
-+ krad_packet *request;
-+ krad_cb cb;
-+ void *data;
-+ verto_ev *timer;
-+ int 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, int 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, int timeout, size_t retries,
-+ const krad_packet **pkt)
-+{
-+ krad_packet *tmp = NULL;
-+ krb5_error_code retval;
-+ request *r;
-+
-+ r = TAILQ_FIRST(&rr->list);
-+ retval = krad_packet_new_request(rr->kctx, rr->secret, code, attrs,
-+ (krad_packet_iter_cb)iterator, &r, &tmp);
-+ if (retval != 0)
-+ goto error;
-+
-+ TAILQ_FOREACH(r, &rr->list, list) {
-+ if (r->request == tmp) {
-+ retval = EALREADY;
-+ goto error;
-+ }
-+ }
-+
-+ if (rr->io == NULL) {
-+ retval = remote_connect(rr);
-+ if (retval != 0)
-+ goto error;
-+ }
-+
-+ if (rr->info->ai_socktype == SOCK_STREAM)
-+ retries = 0;
-+ timeout = timeout / (retries + 1);
-+ retval = request_new(rr, tmp, timeout, retries, cb, data, &r);
-+ if (retval != 0)
-+ goto error;
-+
-+ if ((verto_get_flags(rr->io) & VERTO_EV_FLAG_IO_WRITE) == 0)
-+ verto_set_flags(rr->io, FLAGS_WRITE);
-+
-+ TAILQ_INSERT_TAIL(&rr->list, r, list);
-+ if (pkt != NULL)
-+ *pkt = tmp;
-+ return 0;
-+
-+error:
-+ krad_packet_free(tmp);
-+ return retval;
-+}
-+
-+void
-+kr_remote_cancel(krad_remote *rr, const krad_packet *pkt)
-+{
-+ request *r;
-+
-+ TAILQ_FOREACH(r, &rr->list, list) {
-+ if (r->request == pkt) {
-+ request_finish(r, ECANCELED, NULL);
-+ return;
-+ }
-+ }
-+}
-+
-+krb5_boolean
-+kr_remote_equals(const krad_remote *rr, const struct addrinfo *info,
-+ const char *secret)
-+{
-+ struct sockaddr_un *a, *b;
-+
-+ if (strcmp(rr->secret, secret) != 0)
-+ return FALSE;
-+
-+ if (info->ai_addrlen != rr->info->ai_addrlen)
-+ return FALSE;
-+
-+ if (info->ai_family != rr->info->ai_family)
-+ return FALSE;
-+
-+ if (info->ai_socktype != rr->info->ai_socktype)
-+ return FALSE;
-+
-+ if (info->ai_protocol != rr->info->ai_protocol)
-+ return FALSE;
-+
-+ if (info->ai_flags != rr->info->ai_flags)
-+ return FALSE;
-+
-+ if (memcmp(rr->info->ai_addr, info->ai_addr, info->ai_addrlen) != 0) {
-+ /* AF_UNIX fails the memcmp() test due to uninitialized bytes after the
-+ * socket name. */
-+ if (info->ai_family != AF_UNIX)
-+ return FALSE;
-+
-+ a = (struct sockaddr_un *)info->ai_addr;
-+ b = (struct sockaddr_un *)rr->info->ai_addr;
-+ if (strncmp(a->sun_path, b->sun_path, sizeof(a->sun_path)) != 0)
-+ return FALSE;
-+ }
-+
-+ return TRUE;
-+}
-diff --git a/src/lib/krad/t_attr.c b/src/lib/krad/t_attr.c
-new file mode 100644
-index 0000000..e80d77b
---- /dev/null
-+++ b/src/lib/krad/t_attr.c
-@@ -0,0 +1,89 @@
-+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-+/* lib/krad/t_attr.c - Attribute test program */
-+/*
-+ * Copyright 2013 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include "t_test.h"
-+
-+const static char encoded[] = {
-+ 0xba, 0xfc, 0xed, 0x50, 0xe1, 0xeb, 0xa6, 0xc3,
-+ 0xc1, 0x75, 0x20, 0xe9, 0x10, 0xce, 0xc2, 0xcb
-+};
-+
-+const static unsigned char auth[] = {
-+ 0xac, 0x9d, 0xc1, 0x62, 0x08, 0xc4, 0xc7, 0x8b,
-+ 0xa1, 0x2f, 0x25, 0x0a, 0xc4, 0x1d, 0x36, 0x41
-+};
-+
-+int
-+main()
-+{
-+ unsigned char outbuf[MAX_ATTRSETSIZE];
-+ const char *decoded = "accept";
-+ const char *secret = "foo";
-+ krb5_error_code retval;
-+ krb5_context ctx;
-+ const char *tmp;
-+ krb5_data in;
-+ size_t len;
-+
-+ noerror(krb5_init_context(&ctx));
-+
-+ /* Make sure User-Name is 1. */
-+ insist(krad_attr_name2num("User-Name") == 1);
-+
-+ /* Make sure 2 is User-Password. */
-+ tmp = krad_attr_num2name(2);
-+ insist(tmp != NULL);
-+ insist(strcmp(tmp, "User-Password") == 0);
-+
-+ /* Test decoding. */
-+ in = make_data((void *)encoded, sizeof(encoded));
-+ noerror(kr_attr_decode(ctx, secret, auth,
-+ krad_attr_name2num("User-Password"),
-+ &in, outbuf, &len));
-+ insist(len == strlen(decoded));
-+ insist(memcmp(outbuf, decoded, len) == 0);
-+
-+ /* Test encoding. */
-+ in = string2data((char *)decoded);
-+ retval = kr_attr_encode(ctx, secret, auth,
-+ krad_attr_name2num("User-Password"),
-+ &in, outbuf, &len);
-+ insist(retval == 0);
-+ insist(len == sizeof(encoded));
-+ insist(memcmp(outbuf, encoded, len) == 0);
-+
-+ /* Test constraint. */
-+ in.length = 100;
-+ insist(kr_attr_valid(krad_attr_name2num("User-Password"), &in) == 0);
-+ in.length = 200;
-+ insist(kr_attr_valid(krad_attr_name2num("User-Password"), &in) != 0);
-+
-+ krb5_free_context(ctx);
-+ return 0;
-+}
-diff --git a/src/lib/krad/t_attrset.c b/src/lib/krad/t_attrset.c
-new file mode 100644
-index 0000000..afae5e4
---- /dev/null
-+++ b/src/lib/krad/t_attrset.c
-@@ -0,0 +1,98 @@
-+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-+/* lib/krad/t_attrset.c - Attribute set test program */
-+/*
-+ * Copyright 2013 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include "t_test.h"
-+
-+const static unsigned char auth[] = {
-+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-+};
-+
-+const static char encpass[] = {
-+ 0x58, 0x8d, 0xff, 0xda, 0x37, 0xf9, 0xe4, 0xca,
-+ 0x19, 0xae, 0x49, 0xb7, 0x16, 0x6d, 0x58, 0x27
-+};
-+
-+int
-+main()
-+{
-+ unsigned char buffer[KRAD_PACKET_SIZE_MAX], encoded[MAX_ATTRSETSIZE];
-+ const char *username = "testUser", *password = "accept";
-+ const krb5_data *tmpp;
-+ krad_attrset *set;
-+ krb5_context ctx;
-+ size_t len = 0, encode_len;
-+ krb5_data tmp;
-+
-+ noerror(krb5_init_context(&ctx));
-+ noerror(krad_attrset_new(ctx, &set));
-+
-+ /* Add username. */
-+ tmp = string2data((char *)username);
-+ noerror(krad_attrset_add(set, krad_attr_name2num("User-Name"), &tmp));
-+
-+ /* Add password. */
-+ tmp = string2data((char *)password);
-+ noerror(krad_attrset_add(set, krad_attr_name2num("User-Password"), &tmp));
-+
-+ /* Encode attrset. */
-+ noerror(kr_attrset_encode(set, "foo", auth, buffer, &encode_len));
-+ krad_attrset_free(set);
-+
-+ /* Manually encode User-Name. */
-+ encoded[len + 0] = krad_attr_name2num("User-Name");
-+ encoded[len + 1] = strlen(username) + 2;
-+ memcpy(encoded + len + 2, username, strlen(username));
-+ len += encoded[len + 1];
-+
-+ /* Manually encode User-Password. */
-+ encoded[len + 0] = krad_attr_name2num("User-Password");
-+ encoded[len + 1] = sizeof(encpass) + 2;
-+ memcpy(encoded + len + 2, encpass, sizeof(encpass));
-+ len += encoded[len + 1];
-+
-+ /* Compare output. */
-+ insist(len == encode_len);
-+ insist(memcmp(encoded, buffer, len) == 0);
-+
-+ /* Decode output. */
-+ tmp = make_data(buffer, len);
-+ noerror(kr_attrset_decode(ctx, &tmp, "foo", auth, &set));
-+
-+ /* Test getting an attribute. */
-+ tmp = string2data((char *)username);
-+ tmpp = krad_attrset_get(set, krad_attr_name2num("User-Name"), 0);
-+ insist(tmpp != NULL);
-+ insist(tmpp->length == tmp.length);
-+ insist(strncmp(tmpp->data, tmp.data, tmp.length) == 0);
-+
-+ krad_attrset_free(set);
-+ krb5_free_context(ctx);
-+ return 0;
-+}
-diff --git a/src/lib/krad/t_client.c b/src/lib/krad/t_client.c
-new file mode 100644
-index 0000000..3d0fda9
---- /dev/null
-+++ b/src/lib/krad/t_client.c
-@@ -0,0 +1,126 @@
-+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-+/* lib/krad/t_client.c - Client request test program */
-+/*
-+ * Copyright 2013 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include "t_daemon.h"
-+
-+#define EVENT_COUNT 4
-+
-+static struct
-+{
-+ int count;
-+ struct event events[EVENT_COUNT];
-+} record;
-+
-+static verto_ctx *vctx;
-+
-+static void
-+callback(krb5_error_code retval, const krad_packet *request,
-+ const krad_packet *response, void *data)
-+{
-+ struct event *evt;
-+
-+ evt = &record.events[record.count++];
-+ evt->error = retval != 0;
-+ if (evt->error)
-+ evt->result.retval = retval;
-+ else
-+ evt->result.code = krad_packet_get_code(response);
-+ verto_break(vctx);
-+}
-+
-+int
-+main(int argc, const char **argv)
-+{
-+ krad_attrset *attrs;
-+ krad_client *rc;
-+ krb5_context kctx;
-+ krb5_data tmp;
-+
-+ if (!daemon_start(argc, argv)) {
-+ fprintf(stderr, "Unable to start pyrad daemon, skipping test...\n");
-+ return 0;
-+ }
-+
-+ noerror(krb5_init_context(&kctx));
-+ vctx = verto_new(NULL, VERTO_EV_TYPE_IO | VERTO_EV_TYPE_TIMEOUT);
-+ insist(vctx != NULL);
-+ noerror(krad_client_new(kctx, vctx, &rc));
-+
-+ tmp = string2data("testUser");
-+ noerror(krad_attrset_new(kctx, &attrs));
-+ noerror(krad_attrset_add(attrs, krad_attr_name2num("User-Name"), &tmp));
-+
-+ /* Test accept. */
-+ tmp = string2data("accept");
-+ noerror(krad_attrset_add(attrs, krad_attr_name2num("User-Password"),
-+ &tmp));
-+ noerror(krad_client_send(rc, krad_code_name2num("Access-Request"), attrs,
-+ "localhost", "foo", 1000, 3, callback, NULL));
-+ verto_run(vctx);
-+
-+ /* Test reject. */
-+ tmp = string2data("reject");
-+ krad_attrset_del(attrs, krad_attr_name2num("User-Password"), 0);
-+ noerror(krad_attrset_add(attrs, krad_attr_name2num("User-Password"),
-+ &tmp));
-+ noerror(krad_client_send(rc, krad_code_name2num("Access-Request"), attrs,
-+ "localhost", "foo", 1000, 3, callback, NULL));
-+ verto_run(vctx);
-+
-+ /* Test timeout. */
-+ daemon_stop();
-+ noerror(krad_client_send(rc, krad_code_name2num("Access-Request"), attrs,
-+ "localhost", "foo", 1000, 3, callback, NULL));
-+ verto_run(vctx);
-+
-+ /* Test outstanding packet freeing. */
-+ noerror(krad_client_send(rc, krad_code_name2num("Access-Request"), attrs,
-+ "localhost", "foo", 1000, 3, callback, NULL));
-+ krad_client_free(rc);
-+ rc = NULL;
-+
-+ /* Verify the results. */
-+ insist(record.count == EVENT_COUNT);
-+ insist(record.events[0].error == FALSE);
-+ insist(record.events[0].result.code ==
-+ krad_code_name2num("Access-Accept"));
-+ insist(record.events[1].error == FALSE);
-+ insist(record.events[1].result.code ==
-+ krad_code_name2num("Access-Reject"));
-+ insist(record.events[2].error == TRUE);
-+ insist(record.events[2].result.retval == ETIMEDOUT);
-+ insist(record.events[3].error == TRUE);
-+ insist(record.events[3].result.retval == ECANCELED);
-+
-+ krad_attrset_free(attrs);
-+ krad_client_free(rc);
-+ verto_free(vctx);
-+ krb5_free_context(kctx);
-+ return 0;
-+}
-diff --git a/src/lib/krad/t_code.c b/src/lib/krad/t_code.c
-new file mode 100644
-index 0000000..b245a7e
---- /dev/null
-+++ b/src/lib/krad/t_code.c
-@@ -0,0 +1,54 @@
-+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-+/* lib/krad/t_code.c - RADIUS code table test program */
-+/*
-+ * Copyright 2013 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include "t_test.h"
-+
-+int
-+main()
-+{
-+ const char *tmp;
-+
-+ insist(krad_code_name2num("Access-Request") == 1);
-+ insist(krad_code_name2num("Access-Accept") == 2);
-+ insist(krad_code_name2num("Access-Reject") == 3);
-+
-+ tmp = krad_code_num2name(1);
-+ insist(tmp != NULL);
-+ insist(strcmp(tmp, "Access-Request") == 0);
-+
-+ tmp = krad_code_num2name(2);
-+ insist(tmp != NULL);
-+ insist(strcmp(tmp, "Access-Accept") == 0);
-+
-+ tmp = krad_code_num2name(3);
-+ insist(tmp != NULL);
-+ insist(strcmp(tmp, "Access-Reject") == 0);
-+
-+ return 0;
-+}
-diff --git a/src/lib/krad/t_daemon.h b/src/lib/krad/t_daemon.h
-new file mode 100644
-index 0000000..7c345a6
---- /dev/null
-+++ b/src/lib/krad/t_daemon.h
-@@ -0,0 +1,92 @@
-+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-+/* lib/krad/t_daemon.h - Daemonization helper for RADIUS test programs */
-+/*
-+ * Copyright 2013 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#ifndef T_DAEMON_H_
-+#define T_DAEMON_H_
-+
-+#include "t_test.h"
-+#include <libgen.h>
-+#include <sys/types.h>
-+#include <sys/stat.h>
-+#include <fcntl.h>
-+
-+static pid_t daemon_pid;
-+
-+static void
-+daemon_stop(void)
-+{
-+ if (daemon_pid == 0)
-+ return;
-+ kill(daemon_pid, SIGTERM);
-+ waitpid(daemon_pid, NULL, 0);
-+ daemon_pid = 0;
-+}
-+
-+static krb5_boolean
-+daemon_start(int argc, const char **argv)
-+{
-+ sigset_t set;
-+ int sig;
-+
-+ if (argc != 3 || argv == NULL)
-+ return FALSE;
-+
-+ if (daemon_pid != 0)
-+ return TRUE;
-+
-+ if (sigemptyset(&set) != 0)
-+ return FALSE;
-+
-+ if (sigaddset(&set, SIGUSR1) != 0)
-+ return FALSE;
-+
-+ if (sigaddset(&set, SIGCHLD) != 0)
-+ return FALSE;
-+
-+ if (sigprocmask(SIG_BLOCK, &set, NULL) != 0)
-+ return FALSE;
-+
-+ daemon_pid = fork();
-+ if (daemon_pid == 0) {
-+ close(STDOUT_FILENO);
-+ open("/dev/null", O_WRONLY);
-+ exit(execlp(argv[1], argv[1], argv[2], NULL));
-+ }
-+
-+ if (sigwait(&set, &sig) != 0 || sig == SIGCHLD) {
-+ daemon_stop();
-+ daemon_pid = 0;
-+ return FALSE;
-+ }
-+
-+ atexit(daemon_stop);
-+ return TRUE;
-+}
-+
-+#endif /* T_DAEMON_H_ */
-diff --git a/src/lib/krad/t_daemon.py b/src/lib/krad/t_daemon.py
-new file mode 100644
-index 0000000..d62bc29
---- /dev/null
-+++ b/src/lib/krad/t_daemon.py
-@@ -0,0 +1,76 @@
-+#!/usr/bin/python
-+#
-+# Copyright 2013 Red Hat, Inc. All rights reserved.
-+#
-+# Redistribution and use in source and binary forms, with or without
-+# modification, are permitted provided that the following conditions are met:
-+#
-+# 1. Redistributions of source code must retain the above copyright
-+# notice, this list of conditions and the following disclaimer.
-+#
-+# 2. Redistributions in binary form must reproduce the above copyright
-+# notice, this list of conditions and the following disclaimer in
-+# the documentation and/or other materials provided with the
-+# distribution.
-+#
-+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+
-+import StringIO
-+import os
-+import sys
-+import signal
-+
-+try:
-+ from pyrad import dictionary, packet, server
-+except ImportError:
-+ sys.stdout.write("pyrad not found!\n")
-+ sys.exit(0)
-+
-+# We could use a dictionary file, but since we need
-+# such few attributes, we'll just include them here
-+DICTIONARY = """
-+ATTRIBUTE\tUser-Name\t1\tstring
-+ATTRIBUTE\tUser-Password\t2\toctets
-+ATTRIBUTE\tNAS-Identifier\t32\tstring
-+"""
-+
-+class TestServer(server.Server):
-+ def _HandleAuthPacket(self, pkt):
-+ server.Server._HandleAuthPacket(self, pkt)
-+
-+ passwd = []
-+
-+ print "Request: "
-+ for key in pkt.keys():
-+ if key == "User-Password":
-+ passwd = map(pkt.PwDecrypt, pkt[key])
-+ print "\t%s\t%s" % (key, passwd)
-+ else:
-+ print "\t%s\t%s" % (key, pkt[key])
-+
-+ reply = self.CreateReplyPacket(pkt)
-+ if passwd == ['accept']:
-+ reply.code = packet.AccessAccept
-+ print "Response: %s" % "Access-Accept"
-+ else:
-+ reply.code = packet.AccessReject
-+ print "Response: %s" % "Access-Reject"
-+ print
-+ self.SendReplyPacket(pkt.fd, reply)
-+
-+srv = TestServer(addresses=["localhost"],
-+ hosts={"127.0.0.1":
-+ server.RemoteHost("127.0.0.1", "foo", "localhost")},
-+ dict=dictionary.Dictionary(StringIO.StringIO(DICTIONARY)))
-+os.kill(os.getppid(), signal.SIGUSR1)
-+srv.Run()
-diff --git a/src/lib/krad/t_packet.c b/src/lib/krad/t_packet.c
-new file mode 100644
-index 0000000..0a92e9c
---- /dev/null
-+++ b/src/lib/krad/t_packet.c
-@@ -0,0 +1,194 @@
-+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-+/* lib/krad/t_packet.c - RADIUS packet test program */
-+/*
-+ * Copyright 2013 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include "t_daemon.h"
-+
-+#define ACCEPT_PACKET 0
-+#define REJECT_PACKET 1
-+
-+static krad_packet *packets[3];
-+
-+static const krad_packet *
-+iterator(void *data, krb5_boolean cancel)
-+{
-+ krad_packet *tmp;
-+ int *i = data;
-+
-+ if (cancel || packets[*i] == NULL)
-+ return NULL;
-+
-+ tmp = packets[*i];
-+ *i += 1;
-+ return tmp;
-+}
-+
-+static krb5_error_code
-+make_packet(krb5_context ctx, const krb5_data *username,
-+ const krb5_data *password, krad_packet **pkt)
-+{
-+ krad_attrset *set = NULL;
-+ krad_packet *tmp = NULL;
-+ krb5_error_code retval;
-+ const krb5_data *data;
-+ int i = 0;
-+
-+ retval = krad_attrset_new(ctx, &set);
-+ if (retval != 0)
-+ goto out;
-+
-+ retval = krad_attrset_add(set, krad_attr_name2num("User-Name"), username);
-+ if (retval != 0)
-+ goto out;
-+
-+ retval = krad_attrset_add(set, krad_attr_name2num("User-Password"),
-+ password);
-+ if (retval != 0)
-+ goto out;
-+
-+ retval = krad_packet_new_request(ctx, "foo",
-+ krad_code_name2num("Access-Request"),
-+ set, iterator, &i, &tmp);
-+ if (retval != 0)
-+ goto out;
-+
-+ data = krad_packet_get_attr(tmp, krad_attr_name2num("User-Name"), 0);
-+ if (data == NULL) {
-+ retval = ENOENT;
-+ goto out;
-+ }
-+
-+ if (data->length != username->length ||
-+ memcmp(data->data, username->data, data->length) != 0) {
-+ retval = EINVAL;
-+ goto out;
-+ }
-+
-+ *pkt = tmp;
-+ tmp = NULL;
-+
-+out:
-+ krad_attrset_free(set);
-+ krad_packet_free(tmp);
-+ return retval;
-+}
-+
-+static krb5_error_code
-+do_auth(krb5_context ctx, struct addrinfo *ai, const char *secret,
-+ const krad_packet *rqst, krb5_boolean *auth)
-+{
-+ const krad_packet *req = NULL;
-+ char tmp[KRAD_PACKET_SIZE_MAX];
-+ const krb5_data *request;
-+ krad_packet *rsp = NULL;
-+ krb5_error_code retval;
-+ krb5_data response;
-+ int sock = -1, i;
-+
-+ response = make_data(tmp, sizeof(tmp));
-+
-+ sock = socket(ai->ai_family, ai->ai_socktype, 0);
-+ if (sock < 0) {
-+ retval = errno;
-+ goto out;
-+ }
-+
-+ request = krad_packet_encode(rqst);
-+ if (sendto(sock, request->data, request->length, 0, ai->ai_addr,
-+ ai->ai_addrlen) < 0) {
-+ retval = errno;
-+ goto out;
-+ }
-+
-+ i = recv(sock, response.data, sizeof(tmp), 0);
-+ if (i < 0) {
-+ retval = errno;
-+ goto out;
-+ }
-+ response.length = i;
-+
-+ i = 0;
-+ retval = krad_packet_decode_response(ctx, secret, &response, iterator, &i,
-+ &req, &rsp);
-+ if (retval != 0)
-+ goto out;
-+
-+ if (req != rqst) {
-+ retval = EBADMSG;
-+ goto out;
-+ }
-+
-+ *auth = krad_packet_get_code(rsp) == krad_code_name2num("Access-Accept");
-+
-+out:
-+ krad_packet_free(rsp);
-+ if (sock >= 0)
-+ close(sock);
-+ return retval;
-+}
-+
-+int
-+main(int argc, const char **argv)
-+{
-+ struct addrinfo *ai = NULL, hints;
-+ krb5_data username, password;
-+ krb5_boolean auth = FALSE;
-+ krb5_context ctx;
-+
-+ username = string2data("testUser");
-+
-+ if (!daemon_start(argc, argv)) {
-+ fprintf(stderr, "Unable to start pyrad daemon, skipping test...\n");
-+ return 0;
-+ }
-+
-+ noerror(krb5_init_context(&ctx));
-+
-+ password = string2data("accept");
-+ noerror(make_packet(ctx, &username, &password, &packets[ACCEPT_PACKET]));
-+
-+ password = string2data("reject");
-+ noerror(make_packet(ctx, &username, &password, &packets[REJECT_PACKET]));
-+
-+ memset(&hints, 0, sizeof(hints));
-+ hints.ai_family = AF_INET;
-+ hints.ai_socktype = SOCK_DGRAM;
-+ noerror(gai_error_code(getaddrinfo("127.0.0.1", "radius", &hints, &ai)));
-+
-+ noerror(do_auth(ctx, ai, "foo", packets[ACCEPT_PACKET], &auth));
-+ insist(auth == TRUE);
-+
-+ noerror(do_auth(ctx, ai, "foo", packets[REJECT_PACKET], &auth));
-+ insist(auth == FALSE);
-+
-+ krad_packet_free(packets[ACCEPT_PACKET]);
-+ krad_packet_free(packets[REJECT_PACKET]);
-+ krb5_free_context(ctx);
-+ freeaddrinfo(ai);
-+ return 0;
-+}
-diff --git a/src/lib/krad/t_remote.c b/src/lib/krad/t_remote.c
-new file mode 100644
-index 0000000..a521ecb
---- /dev/null
-+++ b/src/lib/krad/t_remote.c
-@@ -0,0 +1,170 @@
-+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-+/* lib/krad/t_remote.c - Protocol test program */
-+/*
-+ * Copyright 2013 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include "t_daemon.h"
-+
-+#define EVENT_COUNT 6
-+
-+static struct
-+{
-+ int count;
-+ struct event events[EVENT_COUNT];
-+} record;
-+
-+static krad_attrset *set;
-+static krad_remote *rr;
-+static verto_ctx *vctx;
-+
-+static void
-+callback(krb5_error_code retval, const krad_packet *request,
-+ const krad_packet *response, void *data)
-+{
-+ struct event *evt;
-+
-+ evt = &record.events[record.count++];
-+ evt->error = retval != 0;
-+ if (evt->error)
-+ evt->result.retval = retval;
-+ else
-+ evt->result.code = krad_packet_get_code(response);
-+ verto_break(vctx);
-+}
-+
-+static void
-+remote_new(krb5_context kctx, krad_remote **remote)
-+{
-+ struct addrinfo *ai = NULL, hints;
-+
-+ memset(&hints, 0, sizeof(hints));
-+ hints.ai_family = AF_INET;
-+ hints.ai_socktype = SOCK_DGRAM;
-+ noerror(gai_error_code(getaddrinfo("127.0.0.1", "radius", &hints, &ai)));
-+
-+ noerror(kr_remote_new(kctx, vctx, ai, "foo", remote));
-+ insist(kr_remote_equals(*remote, ai, "foo"));
-+ freeaddrinfo(ai);
-+}
-+
-+static krb5_error_code
-+do_auth(const char *password, const krad_packet **pkt)
-+{
-+ const krad_packet *tmppkt;
-+ krb5_error_code retval;
-+ krb5_data tmp = string2data((char *)password);
-+
-+ retval = krad_attrset_add(set, krad_attr_name2num("User-Password"), &tmp);
-+ if (retval != 0)
-+ return retval;
-+
-+ retval = kr_remote_send(rr, krad_code_name2num("Access-Request"), set,
-+ callback, NULL, 1000, 3, &tmppkt);
-+ krad_attrset_del(set, krad_attr_name2num("User-Password"), 0);
-+ if (retval != 0)
-+ return retval;
-+
-+ if (pkt != NULL)
-+ *pkt = tmppkt;
-+ return 0;
-+}
-+
-+static void
-+test_timeout(verto_ctx *ctx, verto_ev *ev)
-+{
-+ static const krad_packet *pkt;
-+
-+ noerror(do_auth("accept", &pkt));
-+ kr_remote_cancel(rr, pkt);
-+}
-+
-+int
-+main(int argc, const char **argv)
-+{
-+ krb5_context kctx = NULL;
-+ krb5_data tmp;
-+
-+ if (!daemon_start(argc, argv)) {
-+ fprintf(stderr, "Unable to start pyrad daemon, skipping test...\n");
-+ return 0;
-+ }
-+
-+ /* Initialize. */
-+ noerror(krb5_init_context(&kctx));
-+ vctx = verto_new(NULL, VERTO_EV_TYPE_IO | VERTO_EV_TYPE_TIMEOUT);
-+ insist(vctx != NULL);
-+ remote_new(kctx, &rr);
-+
-+ /* Create attribute set. */
-+ noerror(krad_attrset_new(kctx, &set));
-+ tmp = string2data("testUser");
-+ noerror(krad_attrset_add(set, krad_attr_name2num("User-Name"), &tmp));
-+
-+ /* Send accept packet. */
-+ noerror(do_auth("accept", NULL));
-+ verto_run(vctx);
-+
-+ /* Send reject packet. */
-+ noerror(do_auth("reject", NULL));
-+ verto_run(vctx);
-+
-+ /* Send canceled packet. */
-+ insist(verto_add_timeout(vctx, VERTO_EV_FLAG_NONE, test_timeout, 0) !=
-+ NULL);
-+ verto_run(vctx);
-+
-+ /* Test timeout. */
-+ daemon_stop();
-+ noerror(do_auth("accept", NULL));
-+ verto_run(vctx);
-+
-+ /* Test outstanding packet freeing. */
-+ noerror(do_auth("accept", NULL));
-+ kr_remote_free(rr);
-+ krad_attrset_free(set);
-+
-+ /* Verify the results. */
-+ insist(record.count == EVENT_COUNT);
-+ insist(record.events[0].error == FALSE);
-+ insist(record.events[0].result.code ==
-+ krad_code_name2num("Access-Accept"));
-+ insist(record.events[1].error == FALSE);
-+ insist(record.events[1].result.code ==
-+ krad_code_name2num("Access-Reject"));
-+ insist(record.events[2].error == TRUE);
-+ insist(record.events[2].result.retval == ECANCELED);
-+ insist(record.events[3].error == TRUE);
-+ insist(record.events[3].result.retval == ETIMEDOUT);
-+ insist(record.events[4].error == TRUE);
-+ insist(record.events[4].result.retval == ECANCELED);
-+ insist(record.events[5].error == TRUE);
-+ insist(record.events[5].result.retval == ECANCELED);
-+
-+ verto_free(vctx);
-+ krb5_free_context(kctx);
-+ return 0;
-+}
-diff --git a/src/lib/krad/t_test.c b/src/lib/krad/t_test.c
-new file mode 100644
-index 0000000..152bc77
---- /dev/null
-+++ b/src/lib/krad/t_test.c
-@@ -0,0 +1,50 @@
-+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-+/* lib/krad/t_test.c - Utility functions for libkrad tests */
-+/*
-+ * Copyright 2013 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include "t_test.h"
-+
-+void
-+noerror_impl(const char *file, int line, const char *cmd, int retval)
-+{
-+ if (retval == 0)
-+ return;
-+
-+ fprintf(stderr, "%s:%d: %s:\n\t%s\n", file, line, strerror(retval), cmd);
-+ exit(1);
-+}
-+
-+void
-+insist_impl(const char *file, int line, const char *cmd, krb5_boolean result)
-+{
-+ if (result)
-+ return;
-+
-+ fprintf(stderr, "%s:%d: insist failed:\n\t%s\n", file, line, cmd);
-+ exit(1);
-+}
-diff --git a/src/lib/krad/t_test.h b/src/lib/krad/t_test.h
-new file mode 100644
-index 0000000..f44742f
---- /dev/null
-+++ b/src/lib/krad/t_test.h
-@@ -0,0 +1,60 @@
-+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-+/* lib/krad/t_test.h - Shared declarations for libkrad test programs */
-+/*
-+ * Copyright 2013 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#ifndef T_TEST_H_
-+#define T_TEST_H_
-+
-+#include "internal.h"
-+
-+#include <sys/wait.h>
-+#include <signal.h>
-+#include <unistd.h>
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <string.h>
-+
-+#define insist(x) insist_impl(__FILE__, __LINE__, #x, x)
-+#define noerror(x) noerror_impl(__FILE__, __LINE__, #x, x)
-+
-+struct event {
-+ krb5_boolean error;
-+ union
-+ {
-+ krb5_error_code retval;
-+ krad_code code;
-+ } result;
-+};
-+
-+void
-+noerror_impl(const char *file, int line, const char *cmd, int retval);
-+
-+void
-+insist_impl(const char *file, int line, const char *cmd, krb5_boolean result);
-+
-+#endif /* T_TEST_H_ */
---
-1.8.2.1
-
-
-From 01c7bae6c9a962ffd38f9d5f70fdde7b9e6eb929 Mon Sep 17 00:00:00 2001
-From: Nathaniel McCallum <npmccallum@redhat.com>
-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 | 39 +++
- src/plugins/preauth/otp/deps | 26 ++
- src/plugins/preauth/otp/main.c | 379 ++++++++++++++++++++++++
- src/plugins/preauth/otp/otp.exports | 1 +
- src/plugins/preauth/otp/otp_state.c | 560 ++++++++++++++++++++++++++++++++++++
- src/plugins/preauth/otp/otp_state.h | 59 ++++
- 9 files changed, 1068 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..43bd2ba
---- /dev/null
-+++ b/src/plugins/preauth/otp/Makefile.in
-@@ -0,0 +1,39 @@
-+mydir=plugins$(S)preauth$(S)otp
-+BUILDTOP=$(REL)..$(S)..$(S)..
-+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@
-+
-+LIBBASE=otp
-+LIBMAJOR=0
-+LIBMINOR=0
-+SO_EXT=.so
-+RELDIR=../plugins/preauth/otp
-+
-+SHLIB_EXPDEPS = $(VERTO_DEPLIBS) $(KRB5_BASE_DEPLIBS) \
-+ $(TOPLIBD)/libkrad$(SHLIBEXT)
-+
-+SHLIB_EXPLIBS= -lkrad $(VERTO_LIBS) $(KRB5_BASE_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:: all-liblinks
-+install-unix:: install-libs
-+clean-unix:: clean-liblinks 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..2f7470e
---- /dev/null
-+++ b/src/plugins/preauth/otp/main.c
-@@ -0,0 +1,379 @@
-+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-+/* plugins/preauth/otp/main.c - OTP kdcpreauth module definition */
-+/*
-+ * Copyright 2011 NORDUnet A/S. All rights reserved.
-+ * Copyright 2013 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include "k5-int.h"
-+#include "k5-json.h"
-+#include <krb5/preauth_plugin.h>
-+#include "otp_state.h"
-+
-+#include <errno.h>
-+#include <ctype.h>
-+
-+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 plaintext;
-+
-+ if (req == NULL)
-+ return EINVAL;
-+
-+ retval = alloc_data(&plaintext, req->enc_data.ciphertext.length);
-+ if (retval)
-+ return retval;
-+
-+ retval = krb5_c_decrypt(context, armor_key, KRB5_KEYUSAGE_PA_OTP_REQUEST,
-+ NULL, &req->enc_data, &plaintext);
-+ if (retval != 0) {
-+ com_err("otp", retval, "Unable to decrypt encData in PA-OTP-REQUEST");
-+ free(plaintext.data);
-+ return retval;
-+ }
-+
-+ *out = plaintext;
-+ return 0;
-+}
-+
-+static krb5_error_code
-+nonce_verify(krb5_context ctx, krb5_keyblock *armor_key,
-+ const krb5_data *nonce)
-+{
-+ krb5_error_code retval;
-+ krb5_timestamp ts;
-+ krb5_data *er = NULL;
-+
-+ if (armor_key == NULL || nonce->data == NULL) {
-+ retval = EINVAL;
-+ 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 = load_32_be(er->data);
-+ 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_out)
-+{
-+ krb5_data nonce;
-+ krb5_error_code retval;
-+ krb5_timestamp now;
-+
-+ retval = krb5_timeofday(ctx, &now);
-+ if (retval != 0)
-+ return retval;
-+
-+ retval = alloc_data(&nonce, sizeof(now) + length);
-+ if (retval != 0)
-+ return retval;
-+
-+ retval = krb5_c_random_make_octets(ctx, &nonce);
-+ if (retval != 0) {
-+ free(nonce.data);
-+ return retval;
-+ }
-+
-+ store_32_be(now, nonce.data);
-+ *nonce_out = nonce;
-+ return 0;
-+}
-+
-+static void
-+on_response(void *data, krb5_error_code retval, otp_response response)
-+{
-+ 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)
-+{
-+ krb5_error_code retval;
-+ otp_state *state;
-+
-+ retval = otp_state_new(context, &state);
-+ if (retval)
-+ return retval;
-+ *moddata_out = (krb5_kdcpreauth_moddata)state;
-+ return 0;
-+}
-+
-+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 *encoding;
-+ 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 = ENOENT;
-+ 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. */
-+ retval = encode_krb5_pa_otp_challenge(&chl, &encoding);
-+ if (retval != 0)
-+ goto out;
-+ pa = k5alloc(sizeof(krb5_pa_data), &retval);
-+ if (pa == NULL) {
-+ krb5_free_data(context, encoding);
-+ goto out;
-+ }
-+ pa->pa_type = KRB5_PADATA_OTP_CHALLENGE;
-+ pa->contents = (krb5_octet *)encoding->data;
-+ pa->length = encoding->length;
-+ free(encoding);
-+
-+out:
-+ (*respond)(arg, retval, pa);
-+}
-+
-+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 *pa,
-+ 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 d, plaintext;
-+ 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;
-+ com_err("otp", retval, "No armor key found when verifying padata");
-+ goto error;
-+ }
-+
-+ /* Decode the request. */
-+ d = make_data(pa->contents, pa->length);
-+ retval = decode_krb5_pa_otp_req(&d, &req);
-+ if (retval != 0) {
-+ com_err("otp", retval, "Unable to decode OTP request");
-+ goto error;
-+ }
-+
-+ /* Decrypt the nonce from the request. */
-+ retval = decrypt_encdata(context, armor_key, req, &plaintext);
-+ if (retval != 0) {
-+ com_err("otp", retval, "Unable to decrypt nonce");
-+ goto error;
-+ }
-+
-+ /* Verify the nonce or timestamp. */
-+ retval = nonce_verify(context, armor_key, &plaintext);
-+ if (retval != 0)
-+ retval = timestamp_verify(context, &plaintext);
-+ krb5_free_data_contents(context, &plaintext);
-+ if (retval != 0) {
-+ com_err("otp", retval, "Unable to verify nonce or timestamp");
-+ goto error;
-+ }
-+
-+ /* Create the request state. */
-+ rs = k5alloc(sizeof(struct request_state), &retval);
-+ if (rs == NULL)
-+ goto error;
-+ rs->arg = arg;
-+ rs->respond = respond;
-+
-+ /* Get the principal's OTP configuration string. */
-+ retval = cb->get_string(context, rock, "otp", &config);
-+ if (config == NULL)
-+ retval = KRB5_PREAUTH_FAILED;
-+ if (retval != 0) {
-+ 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->length == 0)
-+ return 0;
-+
-+ /* Get the armor key. */
-+ armor_key = cb->fast_armor(context, rock);
-+ if (!armor_key) {
-+ com_err("otp", 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_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..95f88e0
---- /dev/null
-+++ b/src/plugins/preauth/otp/otp_state.c
-@@ -0,0 +1,560 @@
-+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-+/* plugins/preauth/otp/otp_state.c - Verify OTP token values using RADIUS */
-+/*
-+ * 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 "otp_state.h"
-+
-+#include <krad.h>
-+#include <k5-json.h>
-+
-+#include <ctype.h>
-+
-+#ifndef HOST_NAME_MAX
-+/* SUSv2 */
-+#define HOST_NAME_MAX 255
-+#endif
-+
-+#define DEFAULT_TYPE_NAME "DEFAULT"
-+#define DEFAULT_SOCKET_FMT KDC_DIR "/%s.socket"
-+#define DEFAULT_TIMEOUT 5
-+#define DEFAULT_RETRIES 3
-+
-+typedef struct token_type_st {
-+ char *name;
-+ char *server;
-+ char *secret;
-+ int timeout;
-+ size_t retries;
-+ krb5_boolean strip_realm;
-+} token_type;
-+
-+typedef struct token_st {
-+ const token_type *type;
-+ krb5_data username;
-+} token;
-+
-+typedef struct request_st {
-+ otp_state *state;
-+ token *tokens;
-+ ssize_t index;
-+ otp_cb cb;
-+ void *data;
-+ krad_attrset *attrs;
-+} request;
-+
-+struct otp_state_st {
-+ krb5_context ctx;
-+ token_type *types;
-+ krad_client *radius;
-+ krad_attrset *attrs;
-+};
-+
-+static void request_send(request *req);
-+
-+/* 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);
-+}
-+
-+/* Construct the internal default token type. */
-+static krb5_error_code
-+token_type_default(token_type *out)
-+{
-+ char *name = NULL, *server = NULL, *secret = NULL;
-+
-+ memset(out, 0, sizeof(*out));
-+
-+ name = strdup(DEFAULT_TYPE_NAME);
-+ if (name == NULL)
-+ goto oom;
-+ if (asprintf(&server, DEFAULT_SOCKET_FMT, name) < 0)
-+ goto oom;
-+ secret = strdup("");
-+ if (secret == NULL)
-+ goto oom;
-+
-+ out->name = name;
-+ out->server = server;
-+ out->secret = secret;
-+ out->timeout = DEFAULT_TIMEOUT * 1000;
-+ out->retries = DEFAULT_RETRIES;
-+ out->strip_realm = FALSE;
-+ return 0;
-+
-+oom:
-+ free(name);
-+ free(server);
-+ free(secret);
-+ return ENOMEM;
-+}
-+
-+/* 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 *server = NULL, *name_copy = NULL, *secret = NULL;
-+ const char *default_secret;
-+ int strip_realm, timeout, retries;
-+
-+ memset(out, 0, sizeof(*out));
-+
-+ /* Set the name. */
-+ name_copy = strdup(name);
-+ if (name_copy == NULL)
-+ return ENOMEM;
-+
-+ /* Set strip_realm. */
-+ retval = profile_get_boolean(profile, "otp", name, "strip_realm", TRUE,
-+ &strip_realm);
-+ if (retval != 0)
-+ goto cleanup;
-+
-+ /* Set the server. */
-+ retval = profile_get_string(profile, "otp", name, "server", NULL, &server);
-+ if (retval != 0)
-+ goto cleanup;
-+ if (server == NULL && asprintf(&server, DEFAULT_SOCKET_FMT, name) < 0) {
-+ retval = ENOMEM;
-+ goto cleanup;
-+ }
-+
-+ /* Get the secret (optional for Unix-domain sockets). */
-+ default_secret = (*server == '/') ? "" : NULL;
-+ retval = profile_get_string(profile, "otp", name, "secret", default_secret,
-+ &secret);
-+ if (retval != 0)
-+ goto cleanup;
-+ if (secret == NULL) {
-+ com_err("otp", EINVAL, "Secret not specified in token type '%s'",
-+ name);
-+ retval = EINVAL;
-+ goto cleanup;
-+ }
-+
-+ /* Get the timeout (profile value in seconds, result in milliseconds). */
-+ retval = profile_get_integer(profile, "otp", name, "timeout",
-+ DEFAULT_TIMEOUT, &timeout);
-+ if (retval != 0)
-+ goto cleanup;
-+ timeout *= 1000;
-+
-+ /* Get the number of retries. */
-+ retval = profile_get_integer(profile, "otp", name, "retries",
-+ DEFAULT_RETRIES, &retries);
-+ if (retval != 0)
-+ goto cleanup;
-+
-+ out->name = name_copy;
-+ out->server = server;
-+ out->secret = secret;
-+ out->timeout = timeout;
-+ out->retries = retries;
-+ out->strip_realm = strip_realm;
-+ name_copy = server = secret = NULL;
-+
-+cleanup:
-+ free(name_copy);
-+ free(server);
-+ free(secret);
-+ 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 *hier[2] = { "otp", NULL };
-+ token_type *types = NULL;
-+ char **names = NULL;
-+ krb5_error_code retval;
-+ size_t i, pos;
-+ krb5_boolean have_default = FALSE;
-+
-+ retval = profile_get_subsection_names(profile, hier, &names);
-+ if (retval != 0)
-+ return retval;
-+
-+ /* Check if any of the profile subsections overrides the default. */
-+ for (i = 0; names[i] != NULL; i++) {
-+ if (strcmp(names[i], DEFAULT_TYPE_NAME) == 0)
-+ have_default = TRUE;
-+ }
-+
-+ /* Leave space for the default (possibly) and the terminator. */
-+ types = k5alloc((i + 2) * sizeof(token_type), &retval);
-+ if (types == NULL)
-+ goto cleanup;
-+
-+ /* If no default has been specified, use our internal default. */
-+ pos = 0;
-+ if (!have_default) {
-+ retval = token_type_default(&types[pos++]);
-+ if (retval != 0)
-+ goto cleanup;
-+ }
-+
-+ /* Decode each profile section into a token type element. */
-+ for (i = 0; names[i] != NULL; i++) {
-+ retval = token_type_decode(profile, names[i], &types[pos++]);
-+ if (retval != 0)
-+ goto cleanup;
-+ }
-+
-+ *out = types;
-+ types = NULL;
-+
-+cleanup:
-+ profile_free_list(names);
-+ token_types_free(types);
-+ return retval;
-+}
-+
-+/* Free the contents of a single token. */
-+static void
-+token_free_contents(token *t)
-+{
-+ if (t != NULL)
-+ 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 *typename = DEFAULT_TYPE_NAME;
-+ const token_type *type = NULL;
-+ char *username = NULL;
-+ krb5_error_code retval;
-+ k5_json_value val;
-+ size_t i;
-+ int flags;
-+
-+ memset(out, 0, sizeof(*out));
-+
-+ /* Find the token type. */
-+ val = k5_json_object_get(obj, "type");
-+ if (val != NULL && k5_json_get_tid(val) == K5_JSON_TID_STRING)
-+ typename = k5_json_string_utf8(val);
-+ for (i = 0; types[i].server != NULL; i++) {
-+ if (strcmp(typename, types[i].name) == 0)
-+ type = &types[i];
-+ }
-+ if (type == NULL)
-+ return EINVAL;
-+
-+ /* Get the username, either from obj or from unparsing the principal. */
-+ val = k5_json_object_get(obj, "username");
-+ if (val != NULL && k5_json_get_tid(val) == K5_JSON_TID_STRING) {
-+ username = strdup(k5_json_string_utf8(val));
-+ if (username == NULL)
-+ return ENOMEM;
-+ } else {
-+ flags = type->strip_realm ? KRB5_PRINCIPAL_UNPARSE_NO_REALM : 0;
-+ retval = krb5_unparse_name_flags(ctx, princ, flags, &username);
-+ if (retval != 0)
-+ return retval;
-+ }
-+
-+ out->type = type;
-+ out->username = string2data(username);
-+ 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_contents(&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_contents(&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
-+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(req->data, retval, otp_response_success);
-+ 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(req->data, retval, otp_response_fail);
-+ request_free(req);
-+}
-+
-+static void
-+request_send(request *req)
-+{
-+ krb5_error_code retval;
-+ token *tok = &req->tokens[req->index];
-+ const token_type *t = tok->type;
-+
-+ retval = krad_attrset_add(req->attrs, krad_attr_name2num("User-Name"),
-+ &tok->username);
-+ if (retval != 0)
-+ goto error;
-+
-+ retval = krad_client_send(req->state->radius,
-+ krad_code_name2num("Access-Request"), req->attrs,
-+ t->server, t->secret, t->timeout, t->retries,
-+ callback, req);
-+ krad_attrset_del(req->attrs, krad_attr_name2num("User-Name"), 0);
-+ if (retval != 0)
-+ goto error;
-+
-+ return;
-+
-+error:
-+ req->cb(req->data, retval, otp_response_fail);
-+ 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)(data, ENOMEM, otp_response_fail);
-+ 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)(data, retval, otp_response_fail);
-+ 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..4247d0b
---- /dev/null
-+++ b/src/plugins/preauth/otp/otp_state.h
-@@ -0,0 +1,59 @@
-+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-+/* plugins/preauth/otp/otp_state.h - Internal declarations for OTP module */
-+/*
-+ * 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 OTP_H_
-+#define OTP_H_
-+
-+#include <k5-int.h>
-+#include <verto.h>
-+
-+#include <com_err.h>
-+
-+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_st otp_state;
-+typedef void
-+(*otp_cb)(void *data, krb5_error_code retval, otp_response response);
-+
-+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.1
-
diff --git a/krb5.spec b/krb5.spec
index 8c31f5e..d4eb3f7 100644
--- a/krb5.spec
+++ b/krb5.spec
@@ -97,10 +97,6 @@ Patch138: krb5-master-keyring-offsets.patch
Patch139: krb5-master-keyring-expiration.patch
Patch140: krb5-1.12-alpha-gss-ccache-import.patch
-# Patches for otp plugin backport
-Patch201: krb5-1.11.2-keycheck.patch
-Patch202: krb5-1.11.2-otp.patch
-
License: MIT
URL: http://web.mit.edu/kerberos/www/
Group: System Environment/Libraries
@@ -323,9 +319,6 @@ ln -s NOTICE LICENSE
%patch139 -p1 -b .keyring-expiration
%patch140 -p1 -b .gss-ccache-import
-%patch201 -p1 -b .keycheck
-%patch202 -p1 -b .otp
-
# Take the execute bit off of documentation.
chmod -x doc/krb5-protocol/*.txt
@@ -1022,6 +1015,7 @@ exit 0
- drop backports for RT#7682
- drop backport for RT#7709
- drop backport for RT#7590 and partial backport for RT#7680
+ - drop OTP backport
* Wed Oct 16 2013 Nalin Dahyabhai <nalin@redhat.com> - 1.11.3-26
- create and own /etc/gss (#1019937)