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 -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 -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 -Date: Tue, 9 Apr 2013 12:24:47 -0400 -Subject: [PATCH 4/4] add otp plugin - ---- - src/Makefile.in | 1 + - src/configure.in | 1 + - src/kdc/kdc_preauth.c | 2 + - src/plugins/preauth/otp/Makefile.in | 45 +++ - src/plugins/preauth/otp/deps | 26 ++ - src/plugins/preauth/otp/main.c | 374 ++++++++++++++++++++++++ - src/plugins/preauth/otp/otp.exports | 1 + - src/plugins/preauth/otp/otp_state.c | 568 ++++++++++++++++++++++++++++++++++++ - src/plugins/preauth/otp/otp_state.h | 58 ++++ - 9 files changed, 1076 insertions(+) - create mode 100644 src/plugins/preauth/otp/Makefile.in - create mode 100644 src/plugins/preauth/otp/deps - create mode 100644 src/plugins/preauth/otp/main.c - create mode 100644 src/plugins/preauth/otp/otp.exports - create mode 100644 src/plugins/preauth/otp/otp_state.c - create mode 100644 src/plugins/preauth/otp/otp_state.h - -diff --git a/src/Makefile.in b/src/Makefile.in -index 2c65831..0b9d355 100644 ---- a/src/Makefile.in -+++ b/src/Makefile.in -@@ -12,6 +12,7 @@ SUBDIRS=util include lib \ - plugins/kadm5_hook/test \ - plugins/kdb/db2 \ - @ldap_plugin_dir@ \ -+ plugins/preauth/otp \ - plugins/preauth/pkinit \ - kdc kadmin slave clients appl tests \ - config-files man doc @po@ -diff --git a/src/configure.in b/src/configure.in -index d8676e5..520e0c8 100644 ---- a/src/configure.in -+++ b/src/configure.in -@@ -1337,6 +1337,7 @@ dnl ccapi ccapi/lib ccapi/lib/unix ccapi/server ccapi/server/unix ccapi/test - plugins/kdb/db2/libdb2/test - plugins/kdb/hdb - plugins/preauth/cksum_body -+ plugins/preauth/otp - plugins/preauth/securid_sam2 - plugins/preauth/wpse - plugins/authdata/greet -diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c -index 42a37a8..afbf1f6 100644 ---- a/src/kdc/kdc_preauth.c -+++ b/src/kdc/kdc_preauth.c -@@ -238,6 +238,8 @@ get_plugin_vtables(krb5_context context, - /* Auto-register encrypted challenge and (if possible) pkinit. */ - k5_plugin_register_dyn(context, PLUGIN_INTERFACE_KDCPREAUTH, "pkinit", - "preauth"); -+ k5_plugin_register_dyn(context, PLUGIN_INTERFACE_KDCPREAUTH, "otp", -+ "preauth"); - k5_plugin_register(context, PLUGIN_INTERFACE_KDCPREAUTH, - "encrypted_challenge", - kdcpreauth_encrypted_challenge_initvt); -diff --git a/src/plugins/preauth/otp/Makefile.in b/src/plugins/preauth/otp/Makefile.in -new file mode 100644 -index 0000000..62fa432 ---- /dev/null -+++ b/src/plugins/preauth/otp/Makefile.in -@@ -0,0 +1,45 @@ -+mydir=plugins$(S)preauth$(S)otp -+BUILDTOP=$(REL)..$(S)..$(S).. -+KRB5_RUN_ENV = @KRB5_RUN_ENV@ -+KRB5_CONFIG_SETUP = KRB5_CONFIG=$(top_srcdir)/config-files/krb5.conf ; export KRB5_CONFIG ; -+PROG_LIBPATH=-L$(TOPLIBD) -+PROG_RPATH=$(KRB5_LIBDIR) -+MODULE_INSTALL_DIR = $(KRB5_PA_MODULE_DIR) -+DEFS=@DEFS@ -+ -+LOCALINCLUDES = -I../../../include/krb5 -I../../../include/ -+ -+LIBBASE=otp -+LIBMAJOR=0 -+LIBMINOR=0 -+SO_EXT=.so -+RELDIR=../plugins/preauth/otp -+# Depends on libk5crypto and libkrb5 -+SHLIB_EXPDEPS = \ -+ $(TOPLIBD)/libk5crypto$(SHLIBEXT) \ -+ $(TOPLIBD)/libkrb5$(SHLIBEXT) \ -+ $(TOPLIBD)/krad/libkrad$(SHLIBEXT) -+ -+SHLIB_EXPLIBS= -lverto -lkrad $(KRB5_LIB) $(K5CRYPTO_LIB) $(COM_ERR_LIB) $(SUPPORT_LIB) $(LIBS) -+ -+SHLIB_DIRS=-L$(TOPLIBD) -+SHLIB_RDIRS=$(KRB5_LIBDIR) -+STOBJLISTS=OBJS.ST -+STLIBOBJS = \ -+ otp_state.o \ -+ main.o -+ -+SRCS = \ -+ $(srcdir)/otp_state.c \ -+ $(srcdir)/main.c -+ -+all-unix:: $(LIBBASE)$(SO_EXT) -+install-unix:: install-libs -+clean-unix:: clean-libs clean-libobjs -+ -+clean:: -+ $(RM) lib$(LIBBASE)$(SO_EXT) -+ -+@libnover_frag@ -+@libobj_frag@ -+ -diff --git a/src/plugins/preauth/otp/deps b/src/plugins/preauth/otp/deps -new file mode 100644 -index 0000000..3352126 ---- /dev/null -+++ b/src/plugins/preauth/otp/deps -@@ -0,0 +1,26 @@ -+# -+# Generated makefile dependencies follow. -+# -+otp_state.so otp_state.po $(OUTPRE)otp_state.$(OBJEXT): \ -+ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \ -+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ -+ $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ -+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ -+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-json.h \ -+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ -+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ -+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ -+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/krb5/preauth_plugin.h \ -+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ -+ otp_state.c otp_state.h -+main.so main.po $(OUTPRE)main.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ -+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ -+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \ -+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ -+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ -+ $(top_srcdir)/include/k5-json.h $(top_srcdir)/include/k5-platform.h \ -+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ -+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ -+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ -+ $(top_srcdir)/include/krb5/preauth_plugin.h $(top_srcdir)/include/port-sockets.h \ -+ $(top_srcdir)/include/socket-utils.h main.c otp_state.h -diff --git a/src/plugins/preauth/otp/main.c b/src/plugins/preauth/otp/main.c -new file mode 100644 -index 0000000..8b48e1b ---- /dev/null -+++ b/src/plugins/preauth/otp/main.c -@@ -0,0 +1,374 @@ -+/* -+ * Copyright 2011 NORDUnet A/S. All rights reserved. -+ * Copyright 2011 Red Hat, Inc. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in -+ * the documentation and/or other materials provided with the -+ * distribution. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER -+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include "k5-int.h" -+#include "k5-json.h" -+#include -+#include "otp_state.h" -+ -+#include -+#include -+ -+static krb5_preauthtype otp_pa_type_list[] = -+ { KRB5_PADATA_OTP_REQUEST, 0 }; -+ -+struct request_state { -+ krb5_kdcpreauth_verify_respond_fn respond; -+ void *arg; -+}; -+ -+static krb5_error_code -+decrypt_encdata(krb5_context context, krb5_keyblock *armor_key, -+ krb5_pa_otp_req *req, krb5_data *out) -+{ -+ krb5_error_code retval; -+ krb5_data tmp; -+ -+ if (!req) -+ return EINVAL; -+ -+ tmp.length = req->enc_data.ciphertext.length; -+ tmp.data = calloc(tmp.length, sizeof(char)); -+ if (!tmp.data) -+ return ENOMEM; -+ -+ retval = krb5_c_decrypt(context, armor_key, KRB5_KEYUSAGE_PA_OTP_REQUEST, -+ NULL, &req->enc_data, &tmp); -+ if (retval != 0) { -+ DEBUGMSG(retval, "Unable to decrypt encData in PA-OTP-REQUEST."); -+ free(tmp.data); -+ return retval; -+ } -+ -+ *out = tmp; -+ return 0; -+} -+ -+static krb5_error_code -+nonce_verify(krb5_context ctx, krb5_keyblock *armor_key, -+ const krb5_data *nonce) -+{ -+ krb5_error_code retval = EINVAL; -+ krb5_timestamp ts; -+ krb5_data *er = NULL; -+ -+ if (armor_key == NULL || nonce->data == NULL) -+ goto out; -+ -+ /* Decode the PA-OTP-ENC-REQUEST structure */ -+ retval = decode_krb5_pa_otp_enc_req(nonce, &er); -+ if (retval != 0) -+ goto out; -+ -+ /* Make sure the nonce is exactly the same size as the one generated */ -+ if (er->length != armor_key->length + sizeof(krb5_timestamp)) -+ goto out; -+ -+ /* Check to make sure the timestamp at the beginning is still valid */ -+ ts = ntohl(((krb5_timestamp *)er->data)[0]); -+ retval = krb5_check_clockskew(ctx, ts); -+ -+out: -+ krb5_free_data(ctx, er); -+ return retval; -+} -+ -+static krb5_error_code -+timestamp_verify(krb5_context ctx, const krb5_data *nonce) -+{ -+ krb5_error_code retval = EINVAL; -+ krb5_pa_enc_ts *et = NULL; -+ -+ if (nonce->data == NULL) -+ goto out; -+ -+ /* Decode the PA-ENC-TS-ENC structure */ -+ retval = decode_krb5_pa_enc_ts(nonce, &et); -+ if (retval != 0) -+ goto out; -+ -+ /* Check the clockskew */ -+ retval = krb5_check_clockskew(ctx, et->patimestamp); -+ -+out: -+ krb5_free_pa_enc_ts(ctx, et); -+ return retval; -+} -+ -+static krb5_error_code -+nonce_generate(krb5_context ctx, unsigned int length, krb5_data *nonce) -+{ -+ krb5_data tmp; -+ krb5_error_code retval; -+ krb5_timestamp time; -+ -+ retval = krb5_timeofday(ctx, &time); -+ if (retval != 0) -+ return retval; -+ -+ tmp.length = length + sizeof(time); -+ tmp.data = (char *)malloc(tmp.length); -+ if (!tmp.data) -+ return ENOMEM; -+ -+ retval = krb5_c_random_make_octets(ctx, &tmp); -+ if (retval != 0) { -+ free(tmp.data); -+ return retval; -+ } -+ -+ *((krb5_timestamp *)tmp.data) = htonl(time); -+ *nonce = tmp; -+ return 0; -+} -+ -+static void -+on_response(krb5_error_code retval, otp_response response, void *data) -+{ -+ struct request_state rs = *(struct request_state *)data; -+ -+ free(data); -+ -+ if (retval == 0 && response != otp_response_success) -+ retval = KRB5_PREAUTH_FAILED; -+ -+ (*rs.respond)(rs.arg, retval, NULL, NULL, NULL); -+} -+ -+static krb5_error_code -+otp_init(krb5_context context, krb5_kdcpreauth_moddata *moddata_out, -+ const char **realmnames) -+{ -+ return otp_state_new(context, (otp_state **)moddata_out); -+} -+ -+static void -+otp_fini(krb5_context context, krb5_kdcpreauth_moddata moddata) -+{ -+ otp_state_free((otp_state *)moddata); -+} -+ -+static int -+otp_flags(krb5_context context, krb5_preauthtype pa_type) -+{ -+ return PA_REPLACES_KEY; -+} -+ -+static void -+otp_edata(krb5_context context, krb5_kdc_req *request, -+ krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, -+ krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type, -+ krb5_kdcpreauth_edata_respond_fn respond, void *arg) -+{ -+ krb5_otp_tokeninfo ti, *tis[2] = { &ti, NULL }; -+ krb5_keyblock *armor_key = NULL; -+ krb5_pa_otp_challenge chl; -+ krb5_pa_data *pa = NULL; -+ krb5_error_code retval; -+ krb5_data *tmp = NULL; -+ char *config; -+ -+ /* Determine if otp is enabled for the user. */ -+ retval = cb->get_string(context, rock, "otp", &config); -+ if (retval != 0 || config == NULL) -+ goto out; -+ cb->free_string(context, rock, config); -+ -+ /* Get the armor key. -+ * This indicates the length of random data to use in the nonce. */ -+ armor_key = cb->fast_armor(context, rock); -+ if (armor_key == NULL) { -+ retval = EINVAL; -+ goto out; -+ } -+ -+ /* Build the (mostly empty) challenge. */ -+ memset(&ti, 0, sizeof(ti)); -+ memset(&chl, 0, sizeof(chl)); -+ chl.tokeninfo = tis; -+ ti.format = -1; -+ ti.length = -1; -+ ti.iteration_count = -1; -+ -+ /* Generate the nonce. */ -+ retval = nonce_generate(context, armor_key->length, &chl.nonce); -+ if (retval != 0) -+ goto out; -+ -+ /* Build the output pa data. */ -+ pa = calloc(1, sizeof(krb5_pa_data)); -+ if (pa) { -+ retval = encode_krb5_pa_otp_challenge(&chl, &tmp); -+ if (retval != 0) { -+ DEBUGMSG(ENOMEM, "Unable to encode challenge."); -+ free(pa); -+ pa = NULL; -+ } -+ -+ pa->pa_type = KRB5_PADATA_OTP_CHALLENGE; -+ pa->contents = (krb5_octet *)tmp->data; -+ pa->length = tmp->length; -+ free(tmp); /* Is there a better way to steal the data contents? */ -+ } else { -+ retval = ENOMEM; -+ } -+ -+out: -+ (*respond)(arg, retval, pa); -+ return; -+} -+ -+static void -+otp_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request, -+ krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *data, -+ krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, -+ krb5_kdcpreauth_moddata moddata, -+ krb5_kdcpreauth_verify_respond_fn respond, void *arg) -+{ -+ krb5_keyblock *armor_key = NULL; -+ krb5_pa_otp_req *req = NULL; -+ struct request_state *rs; -+ krb5_error_code retval; -+ krb5_data tmp; -+ char *config; -+ -+ enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH; -+ -+ /* Get the FAST armor key */ -+ armor_key = cb->fast_armor(context, rock); -+ if (armor_key == NULL) { -+ retval = KRB5KDC_ERR_PREAUTH_FAILED; -+ DEBUGMSG(retval, "No armor key found when verifying padata."); -+ goto error; -+ } -+ -+ /* Decode the request */ -+ tmp = make_data(data->contents, data->length); -+ retval = decode_krb5_pa_otp_req(&tmp, &req); -+ if (retval != 0) { -+ DEBUGMSG(retval, "Unable to decode OTP request."); -+ goto error; -+ } -+ -+ /* Decrypt the nonce from the request */ -+ retval = decrypt_encdata(context, armor_key, req, &tmp); -+ if (retval != 0) { -+ DEBUGMSG(retval, "Unable to decrypt encData."); -+ goto error; -+ } -+ -+ /* Verify the nonce or timestamp */ -+ retval = nonce_verify(context, armor_key, &tmp); -+ if (retval != 0) -+ retval = timestamp_verify(context, &tmp); -+ krb5_free_data_contents(context, &tmp); -+ if (retval != 0) { -+ DEBUGMSG(retval, "Unable to verify nonce or timestamp."); -+ goto error; -+ } -+ -+ /* Create the request state. */ -+ rs = malloc(sizeof(struct request_state)); -+ if (rs == NULL) { -+ retval = ENOMEM; -+ goto error; -+ } -+ rs->arg = arg; -+ rs->respond = respond; -+ -+ /* Get the configuration string. */ -+ retval = cb->get_string(context, rock, "otp", &config); -+ if (retval != 0 || config == NULL) { -+ if (config == NULL) -+ retval = KRB5_PREAUTH_FAILED; -+ free(rs); -+ goto error; -+ } -+ -+ /* Send the request. */ -+ otp_state_verify((otp_state *)moddata, -+ (*cb->event_context)(context, rock), -+ request->client, config, req, on_response, rs); -+ cb->free_string(context, rock, config); -+ -+ k5_free_pa_otp_req(context, req); -+ return; -+ -+error: -+ k5_free_pa_otp_req(context, req); -+ (*respond)(arg, retval, NULL, NULL, NULL); -+} -+ -+static krb5_error_code -+otp_return_padata(krb5_context context, krb5_pa_data *padata, -+ krb5_data *req_pkt, krb5_kdc_req *request, -+ krb5_kdc_rep *reply, krb5_keyblock *encrypting_key, -+ krb5_pa_data **send_pa_out, krb5_kdcpreauth_callbacks cb, -+ krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata, -+ krb5_kdcpreauth_modreq modreq) -+{ -+ krb5_keyblock *armor_key = NULL; -+ -+ if (!padata || padata->length == 0) -+ return 0; -+ -+ /* Get the armor key. */ -+ armor_key = cb->fast_armor(context, rock); -+ if (!armor_key) { -+ DEBUGMSG(ENOENT, "No armor key found when returning padata."); -+ return ENOENT; -+ } -+ -+ /* Replace the reply key with the FAST armor key. */ -+ krb5_free_keyblock_contents(context, encrypting_key); -+ return krb5_copy_keyblock_contents(context, armor_key, encrypting_key); -+} -+ -+krb5_error_code -+kdcpreauth_otp_initvt(krb5_context context, int maj_ver, int min_ver, -+ krb5_plugin_vtable vtable) -+{ -+ krb5_kdcpreauth_vtable vt; -+ -+ if (maj_ver != 1) -+ return KRB5_PLUGIN_VER_NOTSUPP; -+ -+ vt = (krb5_kdcpreauth_vtable)vtable; -+ vt->name = "otp"; -+ vt->pa_type_list = otp_pa_type_list; -+ vt->init = otp_init; -+ vt->fini = otp_fini; -+ vt->flags = otp_flags; -+ vt->edata = otp_edata; -+ vt->verify = otp_verify; -+ vt->return_padata = otp_return_padata; -+ -+ com_err("otp", 0, "Loaded."); -+ -+ return 0; -+} -diff --git a/src/plugins/preauth/otp/otp.exports b/src/plugins/preauth/otp/otp.exports -new file mode 100644 -index 0000000..26aa19d ---- /dev/null -+++ b/src/plugins/preauth/otp/otp.exports -@@ -0,0 +1 @@ -+kdcpreauth_otp_initvt -diff --git a/src/plugins/preauth/otp/otp_state.c b/src/plugins/preauth/otp/otp_state.c -new file mode 100644 -index 0000000..caa0752 ---- /dev/null -+++ b/src/plugins/preauth/otp/otp_state.c -@@ -0,0 +1,568 @@ -+/* -+ * Copyright 2012 Red Hat, Inc. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in -+ * the documentation and/or other materials provided with the -+ * distribution. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER -+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include "otp_state.h" -+ -+#include -+#include -+ -+#include -+ -+#ifndef HOST_NAME_MAX -+/* SUSv2 */ -+#define HOST_NAME_MAX 255 -+#endif -+ -+typedef struct token_type_ { -+ char *name; -+ char *server; -+ char *secret; -+ time_t timeout; -+ size_t retries; -+ krb5_boolean strip_realm; -+} token_type; -+ -+typedef struct token_ { -+ const token_type *type; -+ krb5_data username; -+} token; -+ -+typedef struct request_ { -+ otp_state *state; -+ token *tokens; -+ ssize_t index; -+ otp_cb *cb; -+ void *data; -+ krad_attrset *attrs; -+} request; -+ -+struct otp_state_ { -+ krb5_context ctx; -+ token_type *types; -+ krad_client *radius; -+ krad_attrset *attrs; -+}; -+ -+static inline krb5_data -+string2data_copy(const char *s) -+{ -+ char *tmp; -+ -+ tmp = strdup(s); -+ return make_data(NULL, tmp == NULL ? 0 : strlen(tmp)); -+} -+ -+/* Free a NULL-terminated array of strings. */ -+static void -+stringv_free(char **strv) -+{ -+ size_t i; -+ -+ if (strv == NULL) -+ return; -+ -+ for (i = 0; strv[i] != NULL; i++) -+ free(strv[i]); -+ -+ free(strv); -+} -+ -+/* Free the contents of a single token type. */ -+static void -+token_type_free(token_type *type) -+{ -+ if (type == NULL) -+ return; -+ -+ free(type->name); -+ free(type->server); -+ free(type->secret); -+} -+ -+/* Decode a single token type from the profile. */ -+static krb5_error_code -+token_type_decode(profile_t profile, const char *name, token_type *out) -+{ -+ krb5_error_code retval; -+ char *defsrv = NULL; -+ token_type tt; -+ int tmp; -+ -+ memset(&tt, 0, sizeof(tt)); -+ -+ /* Set the name. */ -+ tt.name = strdup(name == NULL ? "DEFAULT" : name); -+ if (tt.name == NULL) { -+ retval = ENOMEM; -+ goto error; -+ } -+ -+ /* Set defaults. */ -+ tt.timeout = 5000; -+ tt.retries = 3; -+ if (asprintf(&defsrv, "%s/%s.socket", KDC_DIR, tt.name) < 0) { -+ retval = ENOMEM; -+ goto error; -+ } -+ -+ /* Set the internal default. */ -+ if (name == NULL) { -+ retval = ENOMEM; -+ -+ tt.secret = strdup(""); -+ if (tt.secret == NULL) -+ goto error; -+ -+ tt.server = defsrv; -+ tt.strip_realm = FALSE; -+ -+ *out = tt; -+ return 0; -+ } -+ -+ /* Set strip_realm. */ -+ retval = profile_get_boolean(profile, "otp", name, "strip_realm", TRUE, -+ &tmp); -+ if (retval != 0) -+ goto error; -+ tt.strip_realm = tmp == 0 ? FALSE : TRUE; -+ -+ /* Set the server. */ -+ retval = profile_get_string(profile, "otp", name, "server", -+ defsrv, &tt.server); -+ if (retval != 0) -+ goto error; -+ -+ /* Set the secret. */ -+ retval = profile_get_string(profile, "otp", name, "secret", -+ tt.server[0] == '/' ? "" : NULL, -+ &tt.server); -+ if (retval != 0) { -+ goto error; -+ } else if (tt.secret == NULL) { -+ DEBUGMSG(EINVAL, "Secret not specified in token type '%s'.", name); -+ retval = EINVAL; -+ goto error; -+ } -+ -+ /* Set the timeout. */ -+ retval = profile_get_integer(profile, "otp", name, "timeout", -+ tt.timeout / 1000, &tmp); -+ if (retval != 0) -+ goto error; -+ tt.timeout = tmp * 1000; /* Convert to milliseconds. */ -+ -+ /* Set the retries. */ -+ retval = profile_get_integer(profile, "otp", name, "retries", -+ tt.retries, &tmp); -+ if (retval != 0) -+ goto error; -+ tt.retries = tmp; -+ -+ *out = tt; -+ free(defsrv); -+ return 0; -+ -+error: -+ token_type_free(&tt); -+ free(defsrv); -+ return retval; -+} -+ -+/* Free an array of token types. */ -+static void -+token_types_free(token_type *types) -+{ -+ size_t i; -+ -+ if (types == NULL) -+ return; -+ -+ for (i = 0; types[i].server != NULL; i++) -+ token_type_free(&types[i]); -+ -+ free(types); -+} -+ -+/* Decode an array of token types from the profile. */ -+static krb5_error_code -+token_types_decode(profile_t profile, token_type **out) -+{ -+ const char *tmp[2] = { "otp", NULL }; -+ token_type *types = NULL; -+ char **names = NULL; -+ errcode_t retval; -+ ssize_t i, j; -+ -+ retval = profile_get_subsection_names(profile, tmp, &names); -+ if (retval != 0) -+ return retval; -+ -+ for (i = 0, j = 0; names[i] != NULL; i++) { -+ if (strcmp(names[i], "DEFAULT") == 0) -+ j = 1; -+ } -+ -+ types = calloc(i - j + 2, sizeof(token_type)); -+ if (types == NULL) { -+ retval = ENOMEM; -+ goto error; -+ } -+ -+ /* If no default has been specified, use our internal default. */ -+ if (j == 0) { -+ retval = token_type_decode(profile, NULL, &types[j++]); -+ if (retval != 0) -+ goto error; -+ } else { -+ j = 0; -+ } -+ -+ for (i = 0; names[i] != NULL; i++) { -+ retval = token_type_decode(profile, names[i], &types[j++]); -+ if (retval != 0) -+ goto error; -+ } -+ -+ stringv_free(names); -+ *out = types; -+ return 0; -+ -+error: -+ token_types_free(types); -+ stringv_free(names); -+ return retval; -+} -+ -+/* Free the contents of a single token. */ -+static void -+token_free(token *t) -+{ -+ if (t == NULL) -+ return; -+ -+ free(t->username.data); -+} -+ -+/* Decode a single token from a JSON token object. */ -+static krb5_error_code -+token_decode(krb5_context ctx, krb5_const_principal princ, -+ const token_type *types, k5_json_object obj, token *out) -+{ -+ const char *type = NULL; -+ krb5_error_code retval; -+ k5_json_value tmp; -+ size_t i; -+ token t; -+ -+ memset(&t, 0, sizeof(t)); -+ -+ tmp = k5_json_object_get(obj, "username"); -+ if (tmp != NULL && k5_json_get_tid(tmp) == K5_JSON_TID_STRING) { -+ t.username = string2data_copy(k5_json_string_utf8(tmp)); -+ if (t.username.data == NULL) -+ return ENOMEM; -+ } -+ -+ tmp = k5_json_object_get(obj, "type"); -+ if (tmp != NULL && k5_json_get_tid(tmp) == K5_JSON_TID_STRING) -+ type = k5_json_string_utf8(tmp); -+ -+ for (i = 0; types[i].server != NULL; i++) { -+ if (strcmp(type == NULL ? "DEFAULT" : type, types[i].name) == 0) -+ t.type = &types[i]; -+ } -+ -+ if (t.username.data == NULL) { -+ retval = krb5_unparse_name_flags(ctx, princ, -+ t.type->strip_realm -+ ? KRB5_PRINCIPAL_UNPARSE_NO_REALM -+ : 0, -+ &t.username.data); -+ if (retval != 0) -+ return retval; -+ t.username.length = strlen(t.username.data); -+ } -+ -+ *out = t; -+ return 0; -+} -+ -+/* Free an array of tokens. */ -+static void -+tokens_free(token *tokens) -+{ -+ size_t i; -+ -+ if (tokens == NULL) -+ return; -+ -+ for (i = 0; tokens[i].type != NULL; i++) -+ token_free(&tokens[i]); -+ -+ free(tokens); -+} -+ -+/* Decode an array of tokens from the configuration string. */ -+static krb5_error_code -+tokens_decode(krb5_context ctx, krb5_const_principal princ, -+ const token_type *types, const char *config, token **out) -+{ -+ krb5_error_code retval; -+ k5_json_value arr, obj; -+ token *tokens; -+ ssize_t len, i, j; -+ -+ if (config == NULL) -+ config = "[{}]"; -+ -+ arr = k5_json_decode(config); -+ if (arr == NULL) -+ return ENOMEM; -+ -+ if (k5_json_get_tid(arr) != K5_JSON_TID_ARRAY || -+ (len = k5_json_array_length(arr)) == 0) { -+ k5_json_release(arr); -+ -+ arr = k5_json_decode("[{}]"); -+ if (arr == NULL) -+ return ENOMEM; -+ -+ if (k5_json_get_tid(arr) != K5_JSON_TID_ARRAY) { -+ k5_json_release(arr); -+ return ENOMEM; -+ } -+ -+ len = k5_json_array_length(arr); -+ } -+ -+ tokens = calloc(len + 1, sizeof(token)); -+ if (tokens == NULL) { -+ k5_json_release(arr); -+ return ENOMEM; -+ } -+ -+ for (i = 0, j = 0; i < len; i++) { -+ obj = k5_json_array_get(arr, i); -+ if (k5_json_get_tid(obj) != K5_JSON_TID_OBJECT) -+ continue; -+ -+ retval = token_decode(ctx, princ, types, obj, &tokens[j++]); -+ if (retval != 0) { -+ k5_json_release(arr); -+ while (--j > 0) -+ token_free(&tokens[j]); -+ free(tokens); -+ return retval; -+ } -+ } -+ -+ k5_json_release(arr); -+ *out = tokens; -+ return 0; -+} -+ -+static void -+request_free(request *req) -+{ -+ if (req == NULL) -+ return; -+ -+ krad_attrset_free(req->attrs); -+ tokens_free(req->tokens); -+ free(req); -+} -+ -+krb5_error_code -+otp_state_new(krb5_context ctx, otp_state **out) -+{ -+ char hostname[HOST_NAME_MAX + 1]; -+ krb5_error_code retval; -+ profile_t profile; -+ krb5_data hndata; -+ otp_state *self; -+ -+ retval = gethostname(hostname, sizeof(hostname)); -+ if (retval != 0) -+ return retval; -+ -+ self = calloc(1, sizeof(otp_state)); -+ if (self == NULL) -+ return ENOMEM; -+ -+ retval = krb5_get_profile(ctx, &profile); -+ if (retval != 0) -+ goto error; -+ -+ retval = token_types_decode(profile, &self->types); -+ profile_abandon(profile); -+ if (retval != 0) -+ goto error; -+ -+ retval = krad_attrset_new(ctx, &self->attrs); -+ if (retval != 0) -+ goto error; -+ -+ hndata = make_data(hostname, strlen(hostname)); -+ retval = krad_attrset_add(self->attrs, -+ krad_attr_name2num("NAS-Identifier"), &hndata); -+ if (retval != 0) -+ goto error; -+ -+ retval = krad_attrset_add_number(self->attrs, -+ krad_attr_name2num("Service-Type"), -+ KRAD_SERVICE_TYPE_AUTHENTICATE_ONLY); -+ if (retval != 0) -+ goto error; -+ -+ self->ctx = ctx; -+ *out = self; -+ return 0; -+ -+error: -+ otp_state_free(self); -+ return retval; -+} -+ -+void -+otp_state_free(otp_state *self) -+{ -+ if (self == NULL) -+ return; -+ -+ krad_attrset_free(self->attrs); -+ token_types_free(self->types); -+ free(self); -+} -+ -+static void -+request_send(request *req); -+ -+static void -+callback(krb5_error_code retval, const krad_packet *rqst, -+ const krad_packet *resp, void *data) -+{ -+ request *req = data; -+ -+ req->index++; -+ -+ if (retval != 0) -+ goto error; -+ -+ /* If we received an accept packet, success! */ -+ if (krad_packet_get_code(resp) == -+ krad_code_name2num("Access-Accept")) { -+ (*req->cb)(retval, otp_response_success, req->data); -+ request_free(req); -+ return; -+ } -+ -+ /* If we have no more tokens to try, failure! */ -+ if (req->tokens[req->index].type == NULL) -+ goto error; -+ -+ /* Try the next token. */ -+ request_send(req); -+ -+error: -+ (*req->cb)(retval, otp_response_fail, req->data); -+ request_free(req); -+} -+ -+static void -+request_send(request *req) -+{ -+ krb5_error_code retval; -+ -+ retval = krad_attrset_add(req->attrs, -+ krad_attr_name2num("User-Name"), -+ &req->tokens[req->index].username); -+ if (retval != 0) -+ goto error; -+ -+ retval = krad_client_send(req->state->radius, -+ krad_code_name2num("Access-Request"), req->attrs, -+ req->tokens[req->index].type->server, -+ req->tokens[req->index].type->secret, -+ req->tokens[req->index].type->timeout, -+ req->tokens[req->index].type->retries, -+ callback, req); -+ krad_attrset_del(req->attrs, krad_attr_name2num("User-Name"), 0); -+ if (retval != 0) -+ goto error; -+ -+ return; -+ -+error: -+ (*req->cb)(retval, otp_response_fail, req->data); -+ request_free(req); -+} -+ -+void -+otp_state_verify(otp_state *state, verto_ctx *ctx, krb5_const_principal princ, -+ const char *config, const krb5_pa_otp_req *req, -+ otp_cb *cb, void *data) -+{ -+ krb5_error_code retval; -+ request *rqst = NULL; -+ -+ if (state->radius == NULL) { -+ retval = krad_client_new(state->ctx, ctx, &state->radius); -+ if (retval != 0) -+ goto error; -+ } -+ -+ rqst = calloc(1, sizeof(request)); -+ if (rqst == NULL) { -+ (*cb)(ENOMEM, otp_response_fail, data); -+ return; -+ } -+ rqst->state = state; -+ rqst->data = data; -+ rqst->cb = cb; -+ -+ retval = krad_attrset_copy(state->attrs, &rqst->attrs); -+ if (retval != 0) -+ goto error; -+ -+ retval = krad_attrset_add(rqst->attrs, krad_attr_name2num("User-Password"), -+ &req->otp_value); -+ if (retval != 0) -+ goto error; -+ -+ retval = tokens_decode(state->ctx, princ, state->types, config, -+ &rqst->tokens); -+ if (retval != 0) -+ goto error; -+ -+ request_send(rqst); -+ return; -+ -+error: -+ (*cb)(retval, otp_response_fail, data); -+ request_free(rqst); -+} -diff --git a/src/plugins/preauth/otp/otp_state.h b/src/plugins/preauth/otp/otp_state.h -new file mode 100644 -index 0000000..89a164a ---- /dev/null -+++ b/src/plugins/preauth/otp/otp_state.h -@@ -0,0 +1,58 @@ -+/* -+ * Copyright 2012 Red Hat, Inc. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in -+ * the documentation and/or other materials provided with the -+ * distribution. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER -+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef OTP_H_ -+#define OTP_H_ -+ -+#include -+#include -+ -+#include -+#define DEBUGMSG(code, ...) com_err("otp", code, __VA_ARGS__) -+ -+typedef enum otp_response_ { -+ otp_response_fail = 0, -+ otp_response_success -+ /* Other values reserved for responses like next token or new pin. */ -+} otp_response; -+ -+typedef struct otp_state_ otp_state; -+typedef void -+(otp_cb)(krb5_error_code retval, otp_response response, void *data); -+ -+krb5_error_code -+otp_state_new(krb5_context ctx, otp_state **self); -+ -+void -+otp_state_free(otp_state *self); -+ -+void -+otp_state_verify(otp_state *state, verto_ctx *ctx, krb5_const_principal princ, -+ const char *config, const krb5_pa_otp_req *request, -+ otp_cb *cb, void *data); -+ -+#endif /* OTP_H_ */ --- -1.8.2 - diff --git a/krb5-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 +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 +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 +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 similarity index 76% rename from 0002-add-libkrad.patch rename to 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 +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 +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 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 +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 ++#include "otp_state.h" ++ ++#include ++#include ++ ++static krb5_preauthtype otp_pa_type_list[] = ++ { KRB5_PADATA_OTP_REQUEST, 0 }; ++ ++struct request_state { ++ krb5_kdcpreauth_verify_respond_fn respond; ++ void *arg; ++}; ++ ++static krb5_error_code ++decrypt_encdata(krb5_context context, krb5_keyblock *armor_key, ++ krb5_pa_otp_req *req, krb5_data *out) ++{ ++ krb5_error_code retval; ++ krb5_data 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 ++#include ++ ++#include ++ ++#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 ++#include ++ ++#include ++ ++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 1.11.2-4 +- Update otp patches +- Merge otp patches into a single patch +- Add keycheck patch + * Tue Apr 23 2013 Nalin Dahyabhai 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