summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNathaniel McCallum <npmccallum@redhat.com>2013-05-03 17:04:40 -0400
committerNathaniel McCallum <npmccallum@redhat.com>2013-05-03 17:04:40 -0400
commitc0d2f3b96d0d2eee428f588872e97d6a97ad82ec (patch)
treebde9e3300c47341a807d63f28a3a91d7633f43c0
parentfcc98d54031f92cd4e232d98bd7e8e11349b34ad (diff)
downloadkrb5-c0d2f3b96d0d2eee428f588872e97d6a97ad82ec.tar.gz
krb5-c0d2f3b96d0d2eee428f588872e97d6a97ad82ec.tar.xz
krb5-c0d2f3b96d0d2eee428f588872e97d6a97ad82ec.zip
Update otp patch; add keycheck patch
-rw-r--r--0001-add-k5memdup.patch34
-rw-r--r--0003-Add-internal-KDC_DIR-macro.patch66
-rw-r--r--0004-add-otp-plugin.patch1171
-rw-r--r--krb5-1.11.2-keycheck.patch230
-rw-r--r--krb5-1.11.2-otp.patch (renamed from 0002-add-libkrad.patch)1325
-rw-r--r--krb5.spec23
6 files changed, 1534 insertions, 1315 deletions
diff --git a/0001-add-k5memdup.patch b/0001-add-k5memdup.patch
deleted file mode 100644
index e3b7cbb..0000000
--- a/0001-add-k5memdup.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From 5f7844ece4f81ce06f861c65a48c4e9dbeaa215e Mon Sep 17 00:00:00 2001
-From: Nathaniel McCallum <npmccallum@redhat.com>
-Date: Tue, 9 Apr 2013 11:17:04 -0400
-Subject: [PATCH 1/4] add k5memdup()
-
----
- src/include/k5-int.h | 11 +++++++++++
- 1 file changed, 11 insertions(+)
-
-diff --git a/src/include/k5-int.h b/src/include/k5-int.h
-index 75e6783..7b5ab2c 100644
---- a/src/include/k5-int.h
-+++ b/src/include/k5-int.h
-@@ -2600,6 +2600,17 @@ k5alloc(size_t len, krb5_error_code *code)
- return ptr;
- }
-
-+/* Return a copy of the len bytes of memory at in; set *code to 0 or ENOMEM. */
-+static inline void *
-+k5memdup(const void *in, size_t len, krb5_error_code *code)
-+{
-+ void *ptr = k5alloc(len, code);
-+
-+ if (ptr != NULL)
-+ memcpy(ptr, in, len);
-+ return ptr;
-+}
-+
- krb5_error_code KRB5_CALLCONV
- krb5_get_credentials_for_user(krb5_context context, krb5_flags options,
- krb5_ccache ccache,
---
-1.8.2
-
diff --git a/0003-Add-internal-KDC_DIR-macro.patch b/0003-Add-internal-KDC_DIR-macro.patch
deleted file mode 100644
index 7603264..0000000
--- a/0003-Add-internal-KDC_DIR-macro.patch
+++ /dev/null
@@ -1,66 +0,0 @@
-From a4a7a4aeb2fb96e36494faff46243fbcb3c0d78b Mon Sep 17 00:00:00 2001
-From: Greg Hudson <ghudson@mit.edu>
-Date: Tue, 15 Jan 2013 11:11:27 -0500
-Subject: [PATCH 3/4] Add internal KDC_DIR macro
-
-Define KDC_DIR in osconf.hin and use it for paths within the KDC
-directory.
----
- src/include/osconf.hin | 21 +++++++++++----------
- 1 file changed, 11 insertions(+), 10 deletions(-)
-
-diff --git a/src/include/osconf.hin b/src/include/osconf.hin
-index c3a33c2..1bca991 100644
---- a/src/include/osconf.hin
-+++ b/src/include/osconf.hin
-@@ -58,14 +58,15 @@
- #define DEFAULT_PLUGIN_BASE_DIR "@LIBDIR/krb5/plugins"
- #define PLUGIN_EXT "@DYNOBJEXT"
-
--#define DEFAULT_KDB_FILE "@LOCALSTATEDIR/krb5kdc/principal"
--#define DEFAULT_KEYFILE_STUB "@LOCALSTATEDIR/krb5kdc/.k5."
--#define KRB5_DEFAULT_ADMIN_ACL "@LOCALSTATEDIR/krb5kdc/krb5_adm.acl"
-+#define KDC_DIR "@LOCALSTATEDIR/krb5kdc"
-+#define DEFAULT_KDB_FILE KDC_DIR "/principal"
-+#define DEFAULT_KEYFILE_STUB KDC_DIR "/.k5."
-+#define KRB5_DEFAULT_ADMIN_ACL KDC_DIR "/krb5_adm.acl"
- /* Used by old admin server */
--#define DEFAULT_ADMIN_ACL "@LOCALSTATEDIR/krb5kdc/kadm_old.acl"
-+#define DEFAULT_ADMIN_ACL KDC_DIR "/krb5kdc/kadm_old.acl"
-
- /* Location of KDC profile */
--#define DEFAULT_KDC_PROFILE "@LOCALSTATEDIR/krb5kdc/kdc.conf"
-+#define DEFAULT_KDC_PROFILE KDC_DIR "/krb5kdc/kdc.conf"
- #define KDC_PROFILE_ENV "KRB5_KDC_PROFILE"
-
- #if TARGET_OS_MAC
-@@ -93,8 +94,8 @@
- /*
- * Defaults for the KADM5 admin system.
- */
--#define DEFAULT_KADM5_KEYTAB "@LOCALSTATEDIR/krb5kdc/kadm5.keytab"
--#define DEFAULT_KADM5_ACL_FILE "@LOCALSTATEDIR/krb5kdc/kadm5.acl"
-+#define DEFAULT_KADM5_KEYTAB KDC_DIR "/kadm5.keytab"
-+#define DEFAULT_KADM5_ACL_FILE KDC_DIR "/kadm5.acl"
- #define DEFAULT_KADM5_PORT 749 /* assigned by IANA */
-
- #define KRB5_DEFAULT_SUPPORTED_ENCTYPES \
-@@ -116,12 +117,12 @@
- * krb5 slave support follows
- */
-
--#define KPROP_DEFAULT_FILE "@LOCALSTATEDIR/krb5kdc/slave_datatrans"
--#define KPROPD_DEFAULT_FILE "@LOCALSTATEDIR/krb5kdc/from_master"
-+#define KPROP_DEFAULT_FILE KDC_DIR "/slave_datatrans"
-+#define KPROPD_DEFAULT_FILE KDC_DIR "/from_master"
- #define KPROPD_DEFAULT_KDB5_UTIL "@SBINDIR/kdb5_util"
- #define KPROPD_DEFAULT_KPROP "@SBINDIR/kprop"
- #define KPROPD_DEFAULT_KRB_DB DEFAULT_KDB_FILE
--#define KPROPD_ACL_FILE "@LOCALSTATEDIR/krb5kdc/kpropd.acl"
-+#define KPROPD_ACL_FILE KDC_DIR "/kpropd.acl"
-
- /*
- * GSS mechglue
---
-1.8.2
-
diff --git a/0004-add-otp-plugin.patch b/0004-add-otp-plugin.patch
deleted file mode 100644
index 23bf218..0000000
--- a/0004-add-otp-plugin.patch
+++ /dev/null
@@ -1,1171 +0,0 @@
-From 10e4dcdbb8856c66f9000d0c737e4eb9312aa021 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 | 45 +++
- src/plugins/preauth/otp/deps | 26 ++
- src/plugins/preauth/otp/main.c | 374 ++++++++++++++++++++++++
- src/plugins/preauth/otp/otp.exports | 1 +
- src/plugins/preauth/otp/otp_state.c | 568 ++++++++++++++++++++++++++++++++++++
- src/plugins/preauth/otp/otp_state.h | 58 ++++
- 9 files changed, 1076 insertions(+)
- create mode 100644 src/plugins/preauth/otp/Makefile.in
- create mode 100644 src/plugins/preauth/otp/deps
- create mode 100644 src/plugins/preauth/otp/main.c
- create mode 100644 src/plugins/preauth/otp/otp.exports
- create mode 100644 src/plugins/preauth/otp/otp_state.c
- create mode 100644 src/plugins/preauth/otp/otp_state.h
-
-diff --git a/src/Makefile.in b/src/Makefile.in
-index 2c65831..0b9d355 100644
---- a/src/Makefile.in
-+++ b/src/Makefile.in
-@@ -12,6 +12,7 @@ SUBDIRS=util include lib \
- plugins/kadm5_hook/test \
- plugins/kdb/db2 \
- @ldap_plugin_dir@ \
-+ plugins/preauth/otp \
- plugins/preauth/pkinit \
- kdc kadmin slave clients appl tests \
- config-files man doc @po@
-diff --git a/src/configure.in b/src/configure.in
-index d8676e5..520e0c8 100644
---- a/src/configure.in
-+++ b/src/configure.in
-@@ -1337,6 +1337,7 @@ dnl ccapi ccapi/lib ccapi/lib/unix ccapi/server ccapi/server/unix ccapi/test
- plugins/kdb/db2/libdb2/test
- plugins/kdb/hdb
- plugins/preauth/cksum_body
-+ plugins/preauth/otp
- plugins/preauth/securid_sam2
- plugins/preauth/wpse
- plugins/authdata/greet
-diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c
-index 42a37a8..afbf1f6 100644
---- a/src/kdc/kdc_preauth.c
-+++ b/src/kdc/kdc_preauth.c
-@@ -238,6 +238,8 @@ get_plugin_vtables(krb5_context context,
- /* Auto-register encrypted challenge and (if possible) pkinit. */
- k5_plugin_register_dyn(context, PLUGIN_INTERFACE_KDCPREAUTH, "pkinit",
- "preauth");
-+ k5_plugin_register_dyn(context, PLUGIN_INTERFACE_KDCPREAUTH, "otp",
-+ "preauth");
- k5_plugin_register(context, PLUGIN_INTERFACE_KDCPREAUTH,
- "encrypted_challenge",
- kdcpreauth_encrypted_challenge_initvt);
-diff --git a/src/plugins/preauth/otp/Makefile.in b/src/plugins/preauth/otp/Makefile.in
-new file mode 100644
-index 0000000..62fa432
---- /dev/null
-+++ b/src/plugins/preauth/otp/Makefile.in
-@@ -0,0 +1,45 @@
-+mydir=plugins$(S)preauth$(S)otp
-+BUILDTOP=$(REL)..$(S)..$(S)..
-+KRB5_RUN_ENV = @KRB5_RUN_ENV@
-+KRB5_CONFIG_SETUP = KRB5_CONFIG=$(top_srcdir)/config-files/krb5.conf ; export KRB5_CONFIG ;
-+PROG_LIBPATH=-L$(TOPLIBD)
-+PROG_RPATH=$(KRB5_LIBDIR)
-+MODULE_INSTALL_DIR = $(KRB5_PA_MODULE_DIR)
-+DEFS=@DEFS@
-+
-+LOCALINCLUDES = -I../../../include/krb5 -I../../../include/
-+
-+LIBBASE=otp
-+LIBMAJOR=0
-+LIBMINOR=0
-+SO_EXT=.so
-+RELDIR=../plugins/preauth/otp
-+# Depends on libk5crypto and libkrb5
-+SHLIB_EXPDEPS = \
-+ $(TOPLIBD)/libk5crypto$(SHLIBEXT) \
-+ $(TOPLIBD)/libkrb5$(SHLIBEXT) \
-+ $(TOPLIBD)/krad/libkrad$(SHLIBEXT)
-+
-+SHLIB_EXPLIBS= -lverto -lkrad $(KRB5_LIB) $(K5CRYPTO_LIB) $(COM_ERR_LIB) $(SUPPORT_LIB) $(LIBS)
-+
-+SHLIB_DIRS=-L$(TOPLIBD)
-+SHLIB_RDIRS=$(KRB5_LIBDIR)
-+STOBJLISTS=OBJS.ST
-+STLIBOBJS = \
-+ otp_state.o \
-+ main.o
-+
-+SRCS = \
-+ $(srcdir)/otp_state.c \
-+ $(srcdir)/main.c
-+
-+all-unix:: $(LIBBASE)$(SO_EXT)
-+install-unix:: install-libs
-+clean-unix:: clean-libs clean-libobjs
-+
-+clean::
-+ $(RM) lib$(LIBBASE)$(SO_EXT)
-+
-+@libnover_frag@
-+@libobj_frag@
-+
-diff --git a/src/plugins/preauth/otp/deps b/src/plugins/preauth/otp/deps
-new file mode 100644
-index 0000000..3352126
---- /dev/null
-+++ b/src/plugins/preauth/otp/deps
-@@ -0,0 +1,26 @@
-+#
-+# Generated makefile dependencies follow.
-+#
-+otp_state.so otp_state.po $(OUTPRE)otp_state.$(OBJEXT): \
-+ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
-+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
-+ $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
-+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
-+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-json.h \
-+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
-+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
-+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
-+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/krb5/preauth_plugin.h \
-+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
-+ otp_state.c otp_state.h
-+main.so main.po $(OUTPRE)main.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
-+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
-+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
-+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
-+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
-+ $(top_srcdir)/include/k5-json.h $(top_srcdir)/include/k5-platform.h \
-+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
-+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
-+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
-+ $(top_srcdir)/include/krb5/preauth_plugin.h $(top_srcdir)/include/port-sockets.h \
-+ $(top_srcdir)/include/socket-utils.h main.c otp_state.h
-diff --git a/src/plugins/preauth/otp/main.c b/src/plugins/preauth/otp/main.c
-new file mode 100644
-index 0000000..8b48e1b
---- /dev/null
-+++ b/src/plugins/preauth/otp/main.c
-@@ -0,0 +1,374 @@
-+/*
-+ * Copyright 2011 NORDUnet A/S. All rights reserved.
-+ * Copyright 2011 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include "k5-int.h"
-+#include "k5-json.h"
-+#include <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 tmp;
-+
-+ if (!req)
-+ return EINVAL;
-+
-+ tmp.length = req->enc_data.ciphertext.length;
-+ tmp.data = calloc(tmp.length, sizeof(char));
-+ if (!tmp.data)
-+ return ENOMEM;
-+
-+ retval = krb5_c_decrypt(context, armor_key, KRB5_KEYUSAGE_PA_OTP_REQUEST,
-+ NULL, &req->enc_data, &tmp);
-+ if (retval != 0) {
-+ DEBUGMSG(retval, "Unable to decrypt encData in PA-OTP-REQUEST.");
-+ free(tmp.data);
-+ return retval;
-+ }
-+
-+ *out = tmp;
-+ return 0;
-+}
-+
-+static krb5_error_code
-+nonce_verify(krb5_context ctx, krb5_keyblock *armor_key,
-+ const krb5_data *nonce)
-+{
-+ krb5_error_code retval = EINVAL;
-+ krb5_timestamp ts;
-+ krb5_data *er = NULL;
-+
-+ if (armor_key == NULL || nonce->data == NULL)
-+ goto out;
-+
-+ /* Decode the PA-OTP-ENC-REQUEST structure */
-+ retval = decode_krb5_pa_otp_enc_req(nonce, &er);
-+ if (retval != 0)
-+ goto out;
-+
-+ /* Make sure the nonce is exactly the same size as the one generated */
-+ if (er->length != armor_key->length + sizeof(krb5_timestamp))
-+ goto out;
-+
-+ /* Check to make sure the timestamp at the beginning is still valid */
-+ ts = ntohl(((krb5_timestamp *)er->data)[0]);
-+ retval = krb5_check_clockskew(ctx, ts);
-+
-+out:
-+ krb5_free_data(ctx, er);
-+ return retval;
-+}
-+
-+static krb5_error_code
-+timestamp_verify(krb5_context ctx, const krb5_data *nonce)
-+{
-+ krb5_error_code retval = EINVAL;
-+ krb5_pa_enc_ts *et = NULL;
-+
-+ if (nonce->data == NULL)
-+ goto out;
-+
-+ /* Decode the PA-ENC-TS-ENC structure */
-+ retval = decode_krb5_pa_enc_ts(nonce, &et);
-+ if (retval != 0)
-+ goto out;
-+
-+ /* Check the clockskew */
-+ retval = krb5_check_clockskew(ctx, et->patimestamp);
-+
-+out:
-+ krb5_free_pa_enc_ts(ctx, et);
-+ return retval;
-+}
-+
-+static krb5_error_code
-+nonce_generate(krb5_context ctx, unsigned int length, krb5_data *nonce)
-+{
-+ krb5_data tmp;
-+ krb5_error_code retval;
-+ krb5_timestamp time;
-+
-+ retval = krb5_timeofday(ctx, &time);
-+ if (retval != 0)
-+ return retval;
-+
-+ tmp.length = length + sizeof(time);
-+ tmp.data = (char *)malloc(tmp.length);
-+ if (!tmp.data)
-+ return ENOMEM;
-+
-+ retval = krb5_c_random_make_octets(ctx, &tmp);
-+ if (retval != 0) {
-+ free(tmp.data);
-+ return retval;
-+ }
-+
-+ *((krb5_timestamp *)tmp.data) = htonl(time);
-+ *nonce = tmp;
-+ return 0;
-+}
-+
-+static void
-+on_response(krb5_error_code retval, otp_response response, void *data)
-+{
-+ struct request_state rs = *(struct request_state *)data;
-+
-+ free(data);
-+
-+ if (retval == 0 && response != otp_response_success)
-+ retval = KRB5_PREAUTH_FAILED;
-+
-+ (*rs.respond)(rs.arg, retval, NULL, NULL, NULL);
-+}
-+
-+static krb5_error_code
-+otp_init(krb5_context context, krb5_kdcpreauth_moddata *moddata_out,
-+ const char **realmnames)
-+{
-+ return otp_state_new(context, (otp_state **)moddata_out);
-+}
-+
-+static void
-+otp_fini(krb5_context context, krb5_kdcpreauth_moddata moddata)
-+{
-+ otp_state_free((otp_state *)moddata);
-+}
-+
-+static int
-+otp_flags(krb5_context context, krb5_preauthtype pa_type)
-+{
-+ return PA_REPLACES_KEY;
-+}
-+
-+static void
-+otp_edata(krb5_context context, krb5_kdc_req *request,
-+ krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
-+ krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type,
-+ krb5_kdcpreauth_edata_respond_fn respond, void *arg)
-+{
-+ krb5_otp_tokeninfo ti, *tis[2] = { &ti, NULL };
-+ krb5_keyblock *armor_key = NULL;
-+ krb5_pa_otp_challenge chl;
-+ krb5_pa_data *pa = NULL;
-+ krb5_error_code retval;
-+ krb5_data *tmp = NULL;
-+ char *config;
-+
-+ /* Determine if otp is enabled for the user. */
-+ retval = cb->get_string(context, rock, "otp", &config);
-+ if (retval != 0 || config == NULL)
-+ goto out;
-+ cb->free_string(context, rock, config);
-+
-+ /* Get the armor key.
-+ * This indicates the length of random data to use in the nonce. */
-+ armor_key = cb->fast_armor(context, rock);
-+ if (armor_key == NULL) {
-+ retval = EINVAL;
-+ goto out;
-+ }
-+
-+ /* Build the (mostly empty) challenge. */
-+ memset(&ti, 0, sizeof(ti));
-+ memset(&chl, 0, sizeof(chl));
-+ chl.tokeninfo = tis;
-+ ti.format = -1;
-+ ti.length = -1;
-+ ti.iteration_count = -1;
-+
-+ /* Generate the nonce. */
-+ retval = nonce_generate(context, armor_key->length, &chl.nonce);
-+ if (retval != 0)
-+ goto out;
-+
-+ /* Build the output pa data. */
-+ pa = calloc(1, sizeof(krb5_pa_data));
-+ if (pa) {
-+ retval = encode_krb5_pa_otp_challenge(&chl, &tmp);
-+ if (retval != 0) {
-+ DEBUGMSG(ENOMEM, "Unable to encode challenge.");
-+ free(pa);
-+ pa = NULL;
-+ }
-+
-+ pa->pa_type = KRB5_PADATA_OTP_CHALLENGE;
-+ pa->contents = (krb5_octet *)tmp->data;
-+ pa->length = tmp->length;
-+ free(tmp); /* Is there a better way to steal the data contents? */
-+ } else {
-+ retval = ENOMEM;
-+ }
-+
-+out:
-+ (*respond)(arg, retval, pa);
-+ return;
-+}
-+
-+static void
-+otp_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
-+ krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *data,
-+ krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
-+ krb5_kdcpreauth_moddata moddata,
-+ krb5_kdcpreauth_verify_respond_fn respond, void *arg)
-+{
-+ krb5_keyblock *armor_key = NULL;
-+ krb5_pa_otp_req *req = NULL;
-+ struct request_state *rs;
-+ krb5_error_code retval;
-+ krb5_data tmp;
-+ char *config;
-+
-+ enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
-+
-+ /* Get the FAST armor key */
-+ armor_key = cb->fast_armor(context, rock);
-+ if (armor_key == NULL) {
-+ retval = KRB5KDC_ERR_PREAUTH_FAILED;
-+ DEBUGMSG(retval, "No armor key found when verifying padata.");
-+ goto error;
-+ }
-+
-+ /* Decode the request */
-+ tmp = make_data(data->contents, data->length);
-+ retval = decode_krb5_pa_otp_req(&tmp, &req);
-+ if (retval != 0) {
-+ DEBUGMSG(retval, "Unable to decode OTP request.");
-+ goto error;
-+ }
-+
-+ /* Decrypt the nonce from the request */
-+ retval = decrypt_encdata(context, armor_key, req, &tmp);
-+ if (retval != 0) {
-+ DEBUGMSG(retval, "Unable to decrypt encData.");
-+ goto error;
-+ }
-+
-+ /* Verify the nonce or timestamp */
-+ retval = nonce_verify(context, armor_key, &tmp);
-+ if (retval != 0)
-+ retval = timestamp_verify(context, &tmp);
-+ krb5_free_data_contents(context, &tmp);
-+ if (retval != 0) {
-+ DEBUGMSG(retval, "Unable to verify nonce or timestamp.");
-+ goto error;
-+ }
-+
-+ /* Create the request state. */
-+ rs = malloc(sizeof(struct request_state));
-+ if (rs == NULL) {
-+ retval = ENOMEM;
-+ goto error;
-+ }
-+ rs->arg = arg;
-+ rs->respond = respond;
-+
-+ /* Get the configuration string. */
-+ retval = cb->get_string(context, rock, "otp", &config);
-+ if (retval != 0 || config == NULL) {
-+ if (config == NULL)
-+ retval = KRB5_PREAUTH_FAILED;
-+ free(rs);
-+ goto error;
-+ }
-+
-+ /* Send the request. */
-+ otp_state_verify((otp_state *)moddata,
-+ (*cb->event_context)(context, rock),
-+ request->client, config, req, on_response, rs);
-+ cb->free_string(context, rock, config);
-+
-+ k5_free_pa_otp_req(context, req);
-+ return;
-+
-+error:
-+ k5_free_pa_otp_req(context, req);
-+ (*respond)(arg, retval, NULL, NULL, NULL);
-+}
-+
-+static krb5_error_code
-+otp_return_padata(krb5_context context, krb5_pa_data *padata,
-+ krb5_data *req_pkt, krb5_kdc_req *request,
-+ krb5_kdc_rep *reply, krb5_keyblock *encrypting_key,
-+ krb5_pa_data **send_pa_out, krb5_kdcpreauth_callbacks cb,
-+ krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata,
-+ krb5_kdcpreauth_modreq modreq)
-+{
-+ krb5_keyblock *armor_key = NULL;
-+
-+ if (!padata || padata->length == 0)
-+ return 0;
-+
-+ /* Get the armor key. */
-+ armor_key = cb->fast_armor(context, rock);
-+ if (!armor_key) {
-+ DEBUGMSG(ENOENT, "No armor key found when returning padata.");
-+ return ENOENT;
-+ }
-+
-+ /* Replace the reply key with the FAST armor key. */
-+ krb5_free_keyblock_contents(context, encrypting_key);
-+ return krb5_copy_keyblock_contents(context, armor_key, encrypting_key);
-+}
-+
-+krb5_error_code
-+kdcpreauth_otp_initvt(krb5_context context, int maj_ver, int min_ver,
-+ krb5_plugin_vtable vtable)
-+{
-+ krb5_kdcpreauth_vtable vt;
-+
-+ if (maj_ver != 1)
-+ return KRB5_PLUGIN_VER_NOTSUPP;
-+
-+ vt = (krb5_kdcpreauth_vtable)vtable;
-+ vt->name = "otp";
-+ vt->pa_type_list = otp_pa_type_list;
-+ vt->init = otp_init;
-+ vt->fini = otp_fini;
-+ vt->flags = otp_flags;
-+ vt->edata = otp_edata;
-+ vt->verify = otp_verify;
-+ vt->return_padata = otp_return_padata;
-+
-+ com_err("otp", 0, "Loaded.");
-+
-+ return 0;
-+}
-diff --git a/src/plugins/preauth/otp/otp.exports b/src/plugins/preauth/otp/otp.exports
-new file mode 100644
-index 0000000..26aa19d
---- /dev/null
-+++ b/src/plugins/preauth/otp/otp.exports
-@@ -0,0 +1 @@
-+kdcpreauth_otp_initvt
-diff --git a/src/plugins/preauth/otp/otp_state.c b/src/plugins/preauth/otp/otp_state.c
-new file mode 100644
-index 0000000..caa0752
---- /dev/null
-+++ b/src/plugins/preauth/otp/otp_state.c
-@@ -0,0 +1,568 @@
-+/*
-+ * Copyright 2012 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#include "otp_state.h"
-+
-+#include <krad.h>
-+#include <k5-json.h>
-+
-+#include <ctype.h>
-+
-+#ifndef HOST_NAME_MAX
-+/* SUSv2 */
-+#define HOST_NAME_MAX 255
-+#endif
-+
-+typedef struct token_type_ {
-+ char *name;
-+ char *server;
-+ char *secret;
-+ time_t timeout;
-+ size_t retries;
-+ krb5_boolean strip_realm;
-+} token_type;
-+
-+typedef struct token_ {
-+ const token_type *type;
-+ krb5_data username;
-+} token;
-+
-+typedef struct request_ {
-+ otp_state *state;
-+ token *tokens;
-+ ssize_t index;
-+ otp_cb *cb;
-+ void *data;
-+ krad_attrset *attrs;
-+} request;
-+
-+struct otp_state_ {
-+ krb5_context ctx;
-+ token_type *types;
-+ krad_client *radius;
-+ krad_attrset *attrs;
-+};
-+
-+static inline krb5_data
-+string2data_copy(const char *s)
-+{
-+ char *tmp;
-+
-+ tmp = strdup(s);
-+ return make_data(NULL, tmp == NULL ? 0 : strlen(tmp));
-+}
-+
-+/* Free a NULL-terminated array of strings. */
-+static void
-+stringv_free(char **strv)
-+{
-+ size_t i;
-+
-+ if (strv == NULL)
-+ return;
-+
-+ for (i = 0; strv[i] != NULL; i++)
-+ free(strv[i]);
-+
-+ free(strv);
-+}
-+
-+/* Free the contents of a single token type. */
-+static void
-+token_type_free(token_type *type)
-+{
-+ if (type == NULL)
-+ return;
-+
-+ free(type->name);
-+ free(type->server);
-+ free(type->secret);
-+}
-+
-+/* Decode a single token type from the profile. */
-+static krb5_error_code
-+token_type_decode(profile_t profile, const char *name, token_type *out)
-+{
-+ krb5_error_code retval;
-+ char *defsrv = NULL;
-+ token_type tt;
-+ int tmp;
-+
-+ memset(&tt, 0, sizeof(tt));
-+
-+ /* Set the name. */
-+ tt.name = strdup(name == NULL ? "DEFAULT" : name);
-+ if (tt.name == NULL) {
-+ retval = ENOMEM;
-+ goto error;
-+ }
-+
-+ /* Set defaults. */
-+ tt.timeout = 5000;
-+ tt.retries = 3;
-+ if (asprintf(&defsrv, "%s/%s.socket", KDC_DIR, tt.name) < 0) {
-+ retval = ENOMEM;
-+ goto error;
-+ }
-+
-+ /* Set the internal default. */
-+ if (name == NULL) {
-+ retval = ENOMEM;
-+
-+ tt.secret = strdup("");
-+ if (tt.secret == NULL)
-+ goto error;
-+
-+ tt.server = defsrv;
-+ tt.strip_realm = FALSE;
-+
-+ *out = tt;
-+ return 0;
-+ }
-+
-+ /* Set strip_realm. */
-+ retval = profile_get_boolean(profile, "otp", name, "strip_realm", TRUE,
-+ &tmp);
-+ if (retval != 0)
-+ goto error;
-+ tt.strip_realm = tmp == 0 ? FALSE : TRUE;
-+
-+ /* Set the server. */
-+ retval = profile_get_string(profile, "otp", name, "server",
-+ defsrv, &tt.server);
-+ if (retval != 0)
-+ goto error;
-+
-+ /* Set the secret. */
-+ retval = profile_get_string(profile, "otp", name, "secret",
-+ tt.server[0] == '/' ? "" : NULL,
-+ &tt.server);
-+ if (retval != 0) {
-+ goto error;
-+ } else if (tt.secret == NULL) {
-+ DEBUGMSG(EINVAL, "Secret not specified in token type '%s'.", name);
-+ retval = EINVAL;
-+ goto error;
-+ }
-+
-+ /* Set the timeout. */
-+ retval = profile_get_integer(profile, "otp", name, "timeout",
-+ tt.timeout / 1000, &tmp);
-+ if (retval != 0)
-+ goto error;
-+ tt.timeout = tmp * 1000; /* Convert to milliseconds. */
-+
-+ /* Set the retries. */
-+ retval = profile_get_integer(profile, "otp", name, "retries",
-+ tt.retries, &tmp);
-+ if (retval != 0)
-+ goto error;
-+ tt.retries = tmp;
-+
-+ *out = tt;
-+ free(defsrv);
-+ return 0;
-+
-+error:
-+ token_type_free(&tt);
-+ free(defsrv);
-+ return retval;
-+}
-+
-+/* Free an array of token types. */
-+static void
-+token_types_free(token_type *types)
-+{
-+ size_t i;
-+
-+ if (types == NULL)
-+ return;
-+
-+ for (i = 0; types[i].server != NULL; i++)
-+ token_type_free(&types[i]);
-+
-+ free(types);
-+}
-+
-+/* Decode an array of token types from the profile. */
-+static krb5_error_code
-+token_types_decode(profile_t profile, token_type **out)
-+{
-+ const char *tmp[2] = { "otp", NULL };
-+ token_type *types = NULL;
-+ char **names = NULL;
-+ errcode_t retval;
-+ ssize_t i, j;
-+
-+ retval = profile_get_subsection_names(profile, tmp, &names);
-+ if (retval != 0)
-+ return retval;
-+
-+ for (i = 0, j = 0; names[i] != NULL; i++) {
-+ if (strcmp(names[i], "DEFAULT") == 0)
-+ j = 1;
-+ }
-+
-+ types = calloc(i - j + 2, sizeof(token_type));
-+ if (types == NULL) {
-+ retval = ENOMEM;
-+ goto error;
-+ }
-+
-+ /* If no default has been specified, use our internal default. */
-+ if (j == 0) {
-+ retval = token_type_decode(profile, NULL, &types[j++]);
-+ if (retval != 0)
-+ goto error;
-+ } else {
-+ j = 0;
-+ }
-+
-+ for (i = 0; names[i] != NULL; i++) {
-+ retval = token_type_decode(profile, names[i], &types[j++]);
-+ if (retval != 0)
-+ goto error;
-+ }
-+
-+ stringv_free(names);
-+ *out = types;
-+ return 0;
-+
-+error:
-+ token_types_free(types);
-+ stringv_free(names);
-+ return retval;
-+}
-+
-+/* Free the contents of a single token. */
-+static void
-+token_free(token *t)
-+{
-+ if (t == NULL)
-+ return;
-+
-+ free(t->username.data);
-+}
-+
-+/* Decode a single token from a JSON token object. */
-+static krb5_error_code
-+token_decode(krb5_context ctx, krb5_const_principal princ,
-+ const token_type *types, k5_json_object obj, token *out)
-+{
-+ const char *type = NULL;
-+ krb5_error_code retval;
-+ k5_json_value tmp;
-+ size_t i;
-+ token t;
-+
-+ memset(&t, 0, sizeof(t));
-+
-+ tmp = k5_json_object_get(obj, "username");
-+ if (tmp != NULL && k5_json_get_tid(tmp) == K5_JSON_TID_STRING) {
-+ t.username = string2data_copy(k5_json_string_utf8(tmp));
-+ if (t.username.data == NULL)
-+ return ENOMEM;
-+ }
-+
-+ tmp = k5_json_object_get(obj, "type");
-+ if (tmp != NULL && k5_json_get_tid(tmp) == K5_JSON_TID_STRING)
-+ type = k5_json_string_utf8(tmp);
-+
-+ for (i = 0; types[i].server != NULL; i++) {
-+ if (strcmp(type == NULL ? "DEFAULT" : type, types[i].name) == 0)
-+ t.type = &types[i];
-+ }
-+
-+ if (t.username.data == NULL) {
-+ retval = krb5_unparse_name_flags(ctx, princ,
-+ t.type->strip_realm
-+ ? KRB5_PRINCIPAL_UNPARSE_NO_REALM
-+ : 0,
-+ &t.username.data);
-+ if (retval != 0)
-+ return retval;
-+ t.username.length = strlen(t.username.data);
-+ }
-+
-+ *out = t;
-+ return 0;
-+}
-+
-+/* Free an array of tokens. */
-+static void
-+tokens_free(token *tokens)
-+{
-+ size_t i;
-+
-+ if (tokens == NULL)
-+ return;
-+
-+ for (i = 0; tokens[i].type != NULL; i++)
-+ token_free(&tokens[i]);
-+
-+ free(tokens);
-+}
-+
-+/* Decode an array of tokens from the configuration string. */
-+static krb5_error_code
-+tokens_decode(krb5_context ctx, krb5_const_principal princ,
-+ const token_type *types, const char *config, token **out)
-+{
-+ krb5_error_code retval;
-+ k5_json_value arr, obj;
-+ token *tokens;
-+ ssize_t len, i, j;
-+
-+ if (config == NULL)
-+ config = "[{}]";
-+
-+ arr = k5_json_decode(config);
-+ if (arr == NULL)
-+ return ENOMEM;
-+
-+ if (k5_json_get_tid(arr) != K5_JSON_TID_ARRAY ||
-+ (len = k5_json_array_length(arr)) == 0) {
-+ k5_json_release(arr);
-+
-+ arr = k5_json_decode("[{}]");
-+ if (arr == NULL)
-+ return ENOMEM;
-+
-+ if (k5_json_get_tid(arr) != K5_JSON_TID_ARRAY) {
-+ k5_json_release(arr);
-+ return ENOMEM;
-+ }
-+
-+ len = k5_json_array_length(arr);
-+ }
-+
-+ tokens = calloc(len + 1, sizeof(token));
-+ if (tokens == NULL) {
-+ k5_json_release(arr);
-+ return ENOMEM;
-+ }
-+
-+ for (i = 0, j = 0; i < len; i++) {
-+ obj = k5_json_array_get(arr, i);
-+ if (k5_json_get_tid(obj) != K5_JSON_TID_OBJECT)
-+ continue;
-+
-+ retval = token_decode(ctx, princ, types, obj, &tokens[j++]);
-+ if (retval != 0) {
-+ k5_json_release(arr);
-+ while (--j > 0)
-+ token_free(&tokens[j]);
-+ free(tokens);
-+ return retval;
-+ }
-+ }
-+
-+ k5_json_release(arr);
-+ *out = tokens;
-+ return 0;
-+}
-+
-+static void
-+request_free(request *req)
-+{
-+ if (req == NULL)
-+ return;
-+
-+ krad_attrset_free(req->attrs);
-+ tokens_free(req->tokens);
-+ free(req);
-+}
-+
-+krb5_error_code
-+otp_state_new(krb5_context ctx, otp_state **out)
-+{
-+ char hostname[HOST_NAME_MAX + 1];
-+ krb5_error_code retval;
-+ profile_t profile;
-+ krb5_data hndata;
-+ otp_state *self;
-+
-+ retval = gethostname(hostname, sizeof(hostname));
-+ if (retval != 0)
-+ return retval;
-+
-+ self = calloc(1, sizeof(otp_state));
-+ if (self == NULL)
-+ return ENOMEM;
-+
-+ retval = krb5_get_profile(ctx, &profile);
-+ if (retval != 0)
-+ goto error;
-+
-+ retval = token_types_decode(profile, &self->types);
-+ profile_abandon(profile);
-+ if (retval != 0)
-+ goto error;
-+
-+ retval = krad_attrset_new(ctx, &self->attrs);
-+ if (retval != 0)
-+ goto error;
-+
-+ hndata = make_data(hostname, strlen(hostname));
-+ retval = krad_attrset_add(self->attrs,
-+ krad_attr_name2num("NAS-Identifier"), &hndata);
-+ if (retval != 0)
-+ goto error;
-+
-+ retval = krad_attrset_add_number(self->attrs,
-+ krad_attr_name2num("Service-Type"),
-+ KRAD_SERVICE_TYPE_AUTHENTICATE_ONLY);
-+ if (retval != 0)
-+ goto error;
-+
-+ self->ctx = ctx;
-+ *out = self;
-+ return 0;
-+
-+error:
-+ otp_state_free(self);
-+ return retval;
-+}
-+
-+void
-+otp_state_free(otp_state *self)
-+{
-+ if (self == NULL)
-+ return;
-+
-+ krad_attrset_free(self->attrs);
-+ token_types_free(self->types);
-+ free(self);
-+}
-+
-+static void
-+request_send(request *req);
-+
-+static void
-+callback(krb5_error_code retval, const krad_packet *rqst,
-+ const krad_packet *resp, void *data)
-+{
-+ request *req = data;
-+
-+ req->index++;
-+
-+ if (retval != 0)
-+ goto error;
-+
-+ /* If we received an accept packet, success! */
-+ if (krad_packet_get_code(resp) ==
-+ krad_code_name2num("Access-Accept")) {
-+ (*req->cb)(retval, otp_response_success, req->data);
-+ request_free(req);
-+ return;
-+ }
-+
-+ /* If we have no more tokens to try, failure! */
-+ if (req->tokens[req->index].type == NULL)
-+ goto error;
-+
-+ /* Try the next token. */
-+ request_send(req);
-+
-+error:
-+ (*req->cb)(retval, otp_response_fail, req->data);
-+ request_free(req);
-+}
-+
-+static void
-+request_send(request *req)
-+{
-+ krb5_error_code retval;
-+
-+ retval = krad_attrset_add(req->attrs,
-+ krad_attr_name2num("User-Name"),
-+ &req->tokens[req->index].username);
-+ if (retval != 0)
-+ goto error;
-+
-+ retval = krad_client_send(req->state->radius,
-+ krad_code_name2num("Access-Request"), req->attrs,
-+ req->tokens[req->index].type->server,
-+ req->tokens[req->index].type->secret,
-+ req->tokens[req->index].type->timeout,
-+ req->tokens[req->index].type->retries,
-+ callback, req);
-+ krad_attrset_del(req->attrs, krad_attr_name2num("User-Name"), 0);
-+ if (retval != 0)
-+ goto error;
-+
-+ return;
-+
-+error:
-+ (*req->cb)(retval, otp_response_fail, req->data);
-+ request_free(req);
-+}
-+
-+void
-+otp_state_verify(otp_state *state, verto_ctx *ctx, krb5_const_principal princ,
-+ const char *config, const krb5_pa_otp_req *req,
-+ otp_cb *cb, void *data)
-+{
-+ krb5_error_code retval;
-+ request *rqst = NULL;
-+
-+ if (state->radius == NULL) {
-+ retval = krad_client_new(state->ctx, ctx, &state->radius);
-+ if (retval != 0)
-+ goto error;
-+ }
-+
-+ rqst = calloc(1, sizeof(request));
-+ if (rqst == NULL) {
-+ (*cb)(ENOMEM, otp_response_fail, data);
-+ return;
-+ }
-+ rqst->state = state;
-+ rqst->data = data;
-+ rqst->cb = cb;
-+
-+ retval = krad_attrset_copy(state->attrs, &rqst->attrs);
-+ if (retval != 0)
-+ goto error;
-+
-+ retval = krad_attrset_add(rqst->attrs, krad_attr_name2num("User-Password"),
-+ &req->otp_value);
-+ if (retval != 0)
-+ goto error;
-+
-+ retval = tokens_decode(state->ctx, princ, state->types, config,
-+ &rqst->tokens);
-+ if (retval != 0)
-+ goto error;
-+
-+ request_send(rqst);
-+ return;
-+
-+error:
-+ (*cb)(retval, otp_response_fail, data);
-+ request_free(rqst);
-+}
-diff --git a/src/plugins/preauth/otp/otp_state.h b/src/plugins/preauth/otp/otp_state.h
-new file mode 100644
-index 0000000..89a164a
---- /dev/null
-+++ b/src/plugins/preauth/otp/otp_state.h
-@@ -0,0 +1,58 @@
-+/*
-+ * Copyright 2012 Red Hat, Inc. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ *
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ *
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in
-+ * the documentation and/or other materials provided with the
-+ * distribution.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+#ifndef OTP_H_
-+#define OTP_H_
-+
-+#include <k5-int.h>
-+#include <verto.h>
-+
-+#include <com_err.h>
-+#define DEBUGMSG(code, ...) com_err("otp", code, __VA_ARGS__)
-+
-+typedef enum otp_response_ {
-+ otp_response_fail = 0,
-+ otp_response_success
-+ /* Other values reserved for responses like next token or new pin. */
-+} otp_response;
-+
-+typedef struct otp_state_ otp_state;
-+typedef void
-+(otp_cb)(krb5_error_code retval, otp_response response, void *data);
-+
-+krb5_error_code
-+otp_state_new(krb5_context ctx, otp_state **self);
-+
-+void
-+otp_state_free(otp_state *self);
-+
-+void
-+otp_state_verify(otp_state *state, verto_ctx *ctx, krb5_const_principal princ,
-+ const char *config, const krb5_pa_otp_req *request,
-+ otp_cb *cb, void *data);
-+
-+#endif /* OTP_H_ */
---
-1.8.2
-
diff --git a/krb5-1.11.2-keycheck.patch b/krb5-1.11.2-keycheck.patch
new file mode 100644
index 0000000..4b4bd08
--- /dev/null
+++ b/krb5-1.11.2-keycheck.patch
@@ -0,0 +1,230 @@
+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/0002-add-libkrad.patch b/krb5-1.11.2-otp.patch
index 219a3da..eab913e 100644
--- a/0002-add-libkrad.patch
+++ b/krb5-1.11.2-otp.patch
@@ -1,7 +1,109 @@
-From b15c58d7417c7fdd53994fb406213cfdaa8069ec Mon Sep 17 00:00:00 2001
+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 2/4] add libkrad
+Subject: [PATCH 3/4] add libkrad
---
src/configure.in | 2 +-
@@ -16,7 +118,7 @@ Subject: [PATCH 2/4] add libkrad
src/lib/krad/deps | 156 +++++++++++++
src/lib/krad/internal.h | 155 +++++++++++++
src/lib/krad/libkrad.exports | 23 ++
- src/lib/krad/packet.c | 475 +++++++++++++++++++++++++++++++++++++++
+ src/lib/krad/packet.c | 470 +++++++++++++++++++++++++++++++++++++++
src/lib/krad/remote.c | 519 +++++++++++++++++++++++++++++++++++++++++++
src/lib/krad/t_attr.c | 89 ++++++++
src/lib/krad/t_attrset.c | 98 ++++++++
@@ -28,7 +130,7 @@ Subject: [PATCH 2/4] add libkrad
src/lib/krad/t_remote.c | 170 ++++++++++++++
src/lib/krad/t_test.c | 50 +++++
src/lib/krad/t_test.h | 60 +++++
- 24 files changed, 3688 insertions(+), 2 deletions(-)
+ 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
@@ -77,7 +179,7 @@ index c69b809..869b04b 100644
depend:: krb5/krb5.h $(BUILT_HEADERS)
diff --git a/src/include/krad.h b/src/include/krad.h
new file mode 100644
-index 0000000..d88b543
+index 0000000..e6d5766
--- /dev/null
+++ b/src/include/krad.h
@@ -0,0 +1,267 @@
@@ -344,7 +446,7 @@ index 0000000..d88b543
+ */
+krb5_error_code
+krad_client_send(krad_client *rc, krad_code code, const krad_attrset *attrs,
-+ const char *remote, const char *secret, time_t timeout,
++ const char *remote, const char *secret, int timeout,
+ size_t retries, krad_cb cb, void *data);
+
+#endif /* KRAD_H_ */
@@ -1014,7 +1116,7 @@ index 0000000..fbd0621
+}
diff --git a/src/lib/krad/client.c b/src/lib/krad/client.c
new file mode 100644
-index 0000000..a3cf7bd
+index 0000000..0c37680
--- /dev/null
+++ b/src/lib/krad/client.c
@@ -0,0 +1,335 @@
@@ -1072,7 +1174,7 @@ index 0000000..a3cf7bd
+
+ krad_code code;
+ krad_attrset *attrs;
-+ time_t timeout;
++ int timeout;
+ size_t retries;
+ krad_cb cb;
+ void *data;
@@ -1104,7 +1206,7 @@ index 0000000..a3cf7bd
+ time_t currtime;
+ server *srv;
+
-+ if (time(&currtime) == (time_t) -1)
++ if (time(&currtime) == (time_t)-1)
+ return errno;
+
+ LIST_FOREACH(srv, &rc->servers, list) {
@@ -1143,7 +1245,7 @@ index 0000000..a3cf7bd
+/* Create a request. */
+static krb5_error_code
+request_new(krad_client *rc, krad_code code, const krad_attrset *attrs,
-+ const struct addrinfo *ai, const char *secret, time_t timeout,
++ const struct addrinfo *ai, const char *secret, int timeout,
+ size_t retries, krad_cb cb, void *data, request **req)
+{
+ const struct addrinfo *tmp;
@@ -1313,7 +1415,7 @@ index 0000000..a3cf7bd
+
+krb5_error_code
+krad_client_send(krad_client *rc, krad_code code, const krad_attrset *attrs,
-+ const char *remote, const char *secret, time_t timeout,
++ const char *remote, const char *secret, int timeout,
+ size_t retries, krad_cb cb, void *data)
+{
+ struct addrinfo usock, *ai = NULL;
@@ -1634,7 +1736,7 @@ index 0000000..8171f94
+ t_test.h
diff --git a/src/lib/krad/internal.h b/src/lib/krad/internal.h
new file mode 100644
-index 0000000..49ea682
+index 0000000..996a893
--- /dev/null
+++ b/src/lib/krad/internal.h
@@ -0,0 +1,155 @@
@@ -1742,7 +1844,7 @@ index 0000000..49ea682
+ */
+krb5_error_code
+kr_remote_send(krad_remote *rr, krad_code code, krad_attrset *attrs,
-+ krad_cb cb, void *data, time_t timeout, size_t retries,
++ krad_cb cb, void *data, int timeout, size_t retries,
+ const krad_packet **pkt);
+
+/* Remove packet from the queue of requests awaiting responses. */
@@ -1824,10 +1926,10 @@ index 0000000..fe3f159
+krad_client_send
diff --git a/src/lib/krad/packet.c b/src/lib/krad/packet.c
new file mode 100644
-index 0000000..63d5ec8
+index 0000000..6e6b27e
--- /dev/null
+++ b/src/lib/krad/packet.c
-@@ -0,0 +1,475 @@
+@@ -0,0 +1,470 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krad/packet.c - Packet functions for libkrad */
+/*
@@ -2229,17 +2331,15 @@ index 0000000..63d5ec8
+ retval = decode_packet(ctx, secret, buffer, reqpkt);
+ if (cb != NULL && retval == 0) {
+ for (tmp = (*cb)(data, FALSE); tmp != NULL; tmp = (*cb)(data, FALSE)) {
-+ if (pkt_id_get(*reqpkt) != pkt_id_get(tmp))
-+ continue;
-+
-+ *duppkt = tmp;
-+ break;
++ if (pkt_id_get(*reqpkt) == pkt_id_get(tmp))
++ break;
+ }
+ }
+
+ if (cb != NULL && (retval != 0 || tmp != NULL))
+ (*cb)(data, TRUE);
+
++ *duppkt = tmp;
+ return retval;
+}
+
@@ -2267,19 +2367,16 @@ index 0000000..63d5ec8
+ break;
+ }
+
-+ /* If the authenticator doesn't match, then the response is
-+ * invalid. */
-+ if (memcmp(pkt_auth(*rsppkt), auth, sizeof(auth)) != 0)
-+ continue;
-+
-+ *reqpkt = tmp;
-+ break;
++ /* If 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;
+}
+
@@ -2305,7 +2402,7 @@ index 0000000..63d5ec8
+}
diff --git a/src/lib/krad/remote.c b/src/lib/krad/remote.c
new file mode 100644
-index 0000000..fe3fe83
+index 0000000..74d9865
--- /dev/null
+++ b/src/lib/krad/remote.c
@@ -0,0 +1,519 @@
@@ -2361,7 +2458,7 @@ index 0000000..fe3fe83
+ krad_cb cb;
+ void *data;
+ verto_ev *timer;
-+ time_t timeout;
++ int timeout;
+ size_t retries;
+ size_t sent;
+};
@@ -2395,7 +2492,7 @@ index 0000000..fe3fe83
+
+/* Create a new request. */
+static krb5_error_code
-+request_new(krad_remote *rr, krad_packet *rqst, time_t timeout, size_t retries,
++request_new(krad_remote *rr, krad_packet *rqst, int timeout, size_t retries,
+ krad_cb cb, void *data, request **out)
+{
+ request *tmp;
@@ -2731,7 +2828,7 @@ index 0000000..fe3fe83
+
+krb5_error_code
+kr_remote_send(krad_remote *rr, krad_code code, krad_attrset *attrs,
-+ krad_cb cb, void *data, time_t timeout, size_t retries,
++ krad_cb cb, void *data, int timeout, size_t retries,
+ const krad_packet **pkt)
+{
+ krad_packet *tmp = NULL;
@@ -3898,5 +3995,1169 @@ index 0000000..f44742f
+
+#endif /* T_TEST_H_ */
--
-1.8.2
+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 7b978e1..a600b79 100644
--- a/krb5.spec
+++ b/krb5.spec
@@ -30,7 +30,7 @@
Summary: The Kerberos network authentication system
Name: krb5
Version: 1.11.2
-Release: 3%{?dist}
+Release: 4%{?dist}
# Maybe we should explode from the now-available-to-everybody tarball instead?
# http://web.mit.edu/kerberos/dist/krb5/1.11/krb5-1.11.2-signed.tar
Source0: krb5-%{version}.tar.gz
@@ -78,12 +78,9 @@ Patch117: krb5-1.11-gss-client-keytab.patch
Patch118: krb5-1.11.1-rpcbind.patch
Patch119: krb5-fast-msg_type.patch
-# Patch for otp plugin backport
-Patch201: 0001-add-k5memdup.patch
-Patch202: 0002-add-libkrad.patch
-Patch203: 0003-Add-internal-KDC_DIR-macro.patch
-Patch204: 0004-add-otp-plugin.patch
-Patch205: krb5-kdcdir2.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/
@@ -300,11 +297,8 @@ ln -s NOTICE LICENSE
%patch118 -p1 -b .rpcbind
%patch119 -p1 -b .fast-msg_type
-%patch201 -p1 -b .add-k5memdup
-%patch202 -p1 -b .add-libkrad
-%patch203 -p1 -b .add-internal-kdc_dir
-%patch204 -p1 -b .add-otp-plugin
-%patch205 -p1 -b .kdcdir2
+%patch201 -p1 -b .keycheck
+%patch202 -p1 -b .otp
# Take the execute bit off of documentation.
chmod -x doc/krb5-protocol/*.txt
@@ -827,6 +821,11 @@ exit 0
%{_sbindir}/uuserver
%changelog
+* Mon Apr 29 2013 Nathaniel McCallum <npmccallum@redhat.com> 1.11.2-4
+- Update otp patches
+- Merge otp patches into a single patch
+- Add keycheck patch
+
* Tue Apr 23 2013 Nalin Dahyabhai <nalin@redhat.com> 1.11.2-3
- pull the changing of the compiled-in default ccache location to
DIR:/run/user/%%{uid}/krb5cc back into F19, in line with SSSD and