1175 lines
34 KiB
Diff
1175 lines
34 KiB
Diff
From 9c67d6fd21692d8bbfbe880511cbcbc5d9e6a2e5 Mon Sep 17 00:00:00 2001
|
|
From: Nathaniel McCallum <npmccallum@redhat.com>
|
|
Date: Fri, 8 Mar 2013 10:22:03 -0500
|
|
Subject: [PATCH 3/3] add otp plugin
|
|
|
|
---
|
|
src/Makefile.in | 1 +
|
|
src/configure.in | 1 +
|
|
src/kdc/kdc_preauth.c | 2 +
|
|
src/plugins/preauth/otp/Makefile.in | 45 +++
|
|
src/plugins/preauth/otp/deps | 26 ++
|
|
src/plugins/preauth/otp/main.c | 374 +++++++++++++++++++++++
|
|
src/plugins/preauth/otp/otp.exports | 1 +
|
|
src/plugins/preauth/otp/otp_state.c | 571 ++++++++++++++++++++++++++++++++++++
|
|
src/plugins/preauth/otp/otp_state.h | 58 ++++
|
|
9 files changed, 1079 insertions(+)
|
|
create mode 100644 src/plugins/preauth/otp/Makefile.in
|
|
create mode 100644 src/plugins/preauth/otp/deps
|
|
create mode 100644 src/plugins/preauth/otp/main.c
|
|
create mode 100644 src/plugins/preauth/otp/otp.exports
|
|
create mode 100644 src/plugins/preauth/otp/otp_state.c
|
|
create mode 100644 src/plugins/preauth/otp/otp_state.h
|
|
|
|
diff --git a/src/Makefile.in b/src/Makefile.in
|
|
index 2c65831..0b9d355 100644
|
|
--- a/src/Makefile.in
|
|
+++ b/src/Makefile.in
|
|
@@ -12,6 +12,7 @@ SUBDIRS=util include lib \
|
|
plugins/kadm5_hook/test \
|
|
plugins/kdb/db2 \
|
|
@ldap_plugin_dir@ \
|
|
+ plugins/preauth/otp \
|
|
plugins/preauth/pkinit \
|
|
kdc kadmin slave clients appl tests \
|
|
config-files man doc @po@
|
|
diff --git a/src/configure.in b/src/configure.in
|
|
index 6a9757f..053e7b4 100644
|
|
--- a/src/configure.in
|
|
+++ b/src/configure.in
|
|
@@ -1337,6 +1337,7 @@ dnl ccapi ccapi/lib ccapi/lib/unix ccapi/server ccapi/server/unix ccapi/test
|
|
plugins/kdb/db2/libdb2/test
|
|
plugins/kdb/hdb
|
|
plugins/preauth/cksum_body
|
|
+ plugins/preauth/otp
|
|
plugins/preauth/securid_sam2
|
|
plugins/preauth/wpse
|
|
plugins/authdata/greet
|
|
diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c
|
|
index 42a37a8..afbf1f6 100644
|
|
--- a/src/kdc/kdc_preauth.c
|
|
+++ b/src/kdc/kdc_preauth.c
|
|
@@ -238,6 +238,8 @@ get_plugin_vtables(krb5_context context,
|
|
/* Auto-register encrypted challenge and (if possible) pkinit. */
|
|
k5_plugin_register_dyn(context, PLUGIN_INTERFACE_KDCPREAUTH, "pkinit",
|
|
"preauth");
|
|
+ k5_plugin_register_dyn(context, PLUGIN_INTERFACE_KDCPREAUTH, "otp",
|
|
+ "preauth");
|
|
k5_plugin_register(context, PLUGIN_INTERFACE_KDCPREAUTH,
|
|
"encrypted_challenge",
|
|
kdcpreauth_encrypted_challenge_initvt);
|
|
diff --git a/src/plugins/preauth/otp/Makefile.in b/src/plugins/preauth/otp/Makefile.in
|
|
new file mode 100644
|
|
index 0000000..c610be9
|
|
--- /dev/null
|
|
+++ b/src/plugins/preauth/otp/Makefile.in
|
|
@@ -0,0 +1,45 @@
|
|
+mydir=plugins$(S)preauth$(S)otp
|
|
+BUILDTOP=$(REL)..$(S)..$(S)..
|
|
+KRB5_RUN_ENV = @KRB5_RUN_ENV@
|
|
+KRB5_CONFIG_SETUP = KRB5_CONFIG=$(top_srcdir)/config-files/krb5.conf ; export KRB5_CONFIG ;
|
|
+PROG_LIBPATH=-L$(TOPLIBD)
|
|
+PROG_RPATH=$(KRB5_LIBDIR)
|
|
+MODULE_INSTALL_DIR = $(KRB5_PA_MODULE_DIR)
|
|
+DEFS=@DEFS@
|
|
+
|
|
+LOCALINCLUDES = -I../../../include/krb5 -I../../../include/
|
|
+
|
|
+LIBBASE=otp
|
|
+LIBMAJOR=0
|
|
+LIBMINOR=0
|
|
+SO_EXT=.so
|
|
+RELDIR=../plugins/preauth/otp
|
|
+# Depends on libk5crypto and libkrb5
|
|
+SHLIB_EXPDEPS = \
|
|
+ $(TOPLIBD)/libk5crypto$(SHLIBEXT) \
|
|
+ $(TOPLIBD)/libkrb5$(SHLIBEXT) \
|
|
+ $(TOPLIBD)/radius/libk5radius$(SHLIBEXT)
|
|
+
|
|
+SHLIB_EXPLIBS= -lverto -lk5radius $(KRB5_LIB) $(K5CRYPTO_LIB) $(COM_ERR_LIB) $(SUPPORT_LIB) $(LIBS)
|
|
+
|
|
+SHLIB_DIRS=-L$(TOPLIBD)
|
|
+SHLIB_RDIRS=$(KRB5_LIBDIR)
|
|
+STOBJLISTS=OBJS.ST
|
|
+STLIBOBJS = \
|
|
+ otp_state.o \
|
|
+ main.o
|
|
+
|
|
+SRCS = \
|
|
+ $(srcdir)/otp_state.c \
|
|
+ $(srcdir)/main.c
|
|
+
|
|
+all-unix:: $(LIBBASE)$(SO_EXT)
|
|
+install-unix:: install-libs
|
|
+clean-unix:: clean-libs clean-libobjs
|
|
+
|
|
+clean::
|
|
+ $(RM) lib$(LIBBASE)$(SO_EXT)
|
|
+
|
|
+@libnover_frag@
|
|
+@libobj_frag@
|
|
+
|
|
diff --git a/src/plugins/preauth/otp/deps b/src/plugins/preauth/otp/deps
|
|
new file mode 100644
|
|
index 0000000..cf5f19f
|
|
--- /dev/null
|
|
+++ b/src/plugins/preauth/otp/deps
|
|
@@ -0,0 +1,26 @@
|
|
+#
|
|
+# Generated makefile dependencies follow.
|
|
+#
|
|
+otp_state.so otp_state.po $(OUTPRE)otp_state.$(OBJEXT): \
|
|
+ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/k5radius.h \
|
|
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
|
|
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
|
|
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
|
|
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
|
|
+ $(top_srcdir)/include/k5-json.h $(top_srcdir)/include/k5-platform.h \
|
|
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
|
|
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
|
|
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
|
|
+ $(top_srcdir)/include/krb5/preauth_plugin.h $(top_srcdir)/include/port-sockets.h \
|
|
+ $(top_srcdir)/include/socket-utils.h otp_state.c otp_state.h
|
|
+main.so main.po $(OUTPRE)main.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
|
|
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
|
|
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
|
|
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
|
|
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
|
|
+ $(top_srcdir)/include/k5-json.h $(top_srcdir)/include/k5-platform.h \
|
|
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
|
|
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
|
|
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
|
|
+ $(top_srcdir)/include/krb5/preauth_plugin.h $(top_srcdir)/include/port-sockets.h \
|
|
+ $(top_srcdir)/include/socket-utils.h main.c otp_state.h
|
|
diff --git a/src/plugins/preauth/otp/main.c b/src/plugins/preauth/otp/main.c
|
|
new file mode 100644
|
|
index 0000000..e980666
|
|
--- /dev/null
|
|
+++ b/src/plugins/preauth/otp/main.c
|
|
@@ -0,0 +1,374 @@
|
|
+/*
|
|
+ * Copyright 2011 NORDUnet A/S. All rights reserved.
|
|
+ * Copyright 2011 Red Hat, Inc. All rights reserved.
|
|
+ *
|
|
+ * Redistribution and use in source and binary forms, with or without
|
|
+ * modification, are permitted provided that the following conditions are met:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ *
|
|
+ * 2. Redistributions in binary form must reproduce the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer in
|
|
+ * the documentation and/or other materials provided with the
|
|
+ * distribution.
|
|
+ *
|
|
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
|
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
|
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
+ */
|
|
+
|
|
+#include "k5-int.h"
|
|
+#include "k5-json.h"
|
|
+#include <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..a42141c
|
|
--- /dev/null
|
|
+++ b/src/plugins/preauth/otp/otp_state.c
|
|
@@ -0,0 +1,571 @@
|
|
+/*
|
|
+ * Copyright 2012 Red Hat, Inc. All rights reserved.
|
|
+ *
|
|
+ * Redistribution and use in source and binary forms, with or without
|
|
+ * modification, are permitted provided that the following conditions are met:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ *
|
|
+ * 2. Redistributions in binary form must reproduce the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer in
|
|
+ * the documentation and/or other materials provided with the
|
|
+ * distribution.
|
|
+ *
|
|
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
|
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
|
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
+ */
|
|
+
|
|
+#include "otp_state.h"
|
|
+
|
|
+#include <k5radius.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;
|
|
+ k5_radius_attrset *attrs;
|
|
+} request;
|
|
+
|
|
+struct otp_state_ {
|
|
+ krb5_context ctx;
|
|
+ token_type *types;
|
|
+ k5_radius_client *radius;
|
|
+ k5_radius_attrset *attrs;
|
|
+};
|
|
+
|
|
+static inline krb5_data
|
|
+string2data_copy(const char *s)
|
|
+{
|
|
+ char *tmp;
|
|
+
|
|
+ tmp = strdup(s);
|
|
+ return make_data(NULL, tmp == NULL ? 0 : strlen(tmp));
|
|
+}
|
|
+
|
|
+/* Free a NULL-terminated array of strings. */
|
|
+static void
|
|
+stringv_free(char **strv)
|
|
+{
|
|
+ size_t i;
|
|
+
|
|
+ if (strv == NULL)
|
|
+ return;
|
|
+
|
|
+ for (i = 0; strv[i] != NULL; i++)
|
|
+ free(strv[i]);
|
|
+
|
|
+ free(strv);
|
|
+}
|
|
+
|
|
+/* Free the contents of a single token type. */
|
|
+static void
|
|
+token_type_free(token_type *type)
|
|
+{
|
|
+ if (type == NULL)
|
|
+ return;
|
|
+
|
|
+ free(type->name);
|
|
+ free(type->server);
|
|
+ free(type->secret);
|
|
+}
|
|
+
|
|
+/* Decode a single token type from the profile. */
|
|
+static krb5_error_code
|
|
+token_type_decode(profile_t profile, const char *name, token_type *out)
|
|
+{
|
|
+ krb5_error_code retval;
|
|
+ char *defsrv = NULL;
|
|
+ token_type tt;
|
|
+ int tmp;
|
|
+
|
|
+ memset(&tt, 0, sizeof(tt));
|
|
+
|
|
+ /* Set the name. */
|
|
+ tt.name = strdup(name == NULL ? "DEFAULT" : name);
|
|
+ if (tt.name == NULL) {
|
|
+ retval = ENOMEM;
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ /* Set defaults. */
|
|
+ tt.timeout = 5000;
|
|
+ tt.retries = 3;
|
|
+ if (asprintf(&defsrv, "%s/%s.socket", KDC_DIR, tt.name) < 0) {
|
|
+ retval = ENOMEM;
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ /* Set the internal default. */
|
|
+ if (name == NULL) {
|
|
+ retval = ENOMEM;
|
|
+
|
|
+ tt.secret = strdup("");
|
|
+ if (tt.secret == NULL)
|
|
+ goto error;
|
|
+
|
|
+ tt.server = defsrv;
|
|
+ tt.strip_realm = FALSE;
|
|
+
|
|
+ *out = tt;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* Set strip_realm. */
|
|
+ retval = profile_get_boolean(profile, "otp", name, "strip_realm", TRUE,
|
|
+ &tmp);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+ tt.strip_realm = tmp == 0 ? FALSE : TRUE;
|
|
+
|
|
+ /* Set the server. */
|
|
+ retval = profile_get_string(profile, "otp", name, "server",
|
|
+ defsrv, &tt.server);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ /* Set the secret. */
|
|
+ retval = profile_get_string(profile, "otp", name, "secret",
|
|
+ tt.server[0] == '/' ? "" : NULL,
|
|
+ &tt.server);
|
|
+ if (retval != 0) {
|
|
+ goto error;
|
|
+ } else if (tt.secret == NULL) {
|
|
+ DEBUGMSG(EINVAL, "Secret not specified in token type '%s'.", name);
|
|
+ retval = EINVAL;
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ /* Set the timeout. */
|
|
+ retval = profile_get_integer(profile, "otp", name, "timeout",
|
|
+ tt.timeout / 1000, &tmp);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+ tt.timeout = tmp * 1000; /* Convert to milliseconds. */
|
|
+
|
|
+ /* Set the retries. */
|
|
+ retval = profile_get_integer(profile, "otp", name, "retries",
|
|
+ tt.retries, &tmp);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+ tt.retries = tmp;
|
|
+
|
|
+ *out = tt;
|
|
+ free(defsrv);
|
|
+ return 0;
|
|
+
|
|
+error:
|
|
+ token_type_free(&tt);
|
|
+ free(defsrv);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+/* Free an array of token types. */
|
|
+static void
|
|
+token_types_free(token_type *types)
|
|
+{
|
|
+ size_t i;
|
|
+
|
|
+ if (types == NULL)
|
|
+ return;
|
|
+
|
|
+ for (i = 0; types[i].server != NULL; i++)
|
|
+ token_type_free(&types[i]);
|
|
+
|
|
+ free(types);
|
|
+}
|
|
+
|
|
+/* Decode an array of token types from the profile. */
|
|
+static krb5_error_code
|
|
+token_types_decode(profile_t profile, token_type **out)
|
|
+{
|
|
+ const char *tmp[2] = { "otp", NULL };
|
|
+ token_type *types = NULL;
|
|
+ char **names = NULL;
|
|
+ errcode_t retval;
|
|
+ ssize_t i, j;
|
|
+
|
|
+ retval = profile_get_subsection_names(profile, tmp, &names);
|
|
+ if (retval != 0)
|
|
+ return retval;
|
|
+
|
|
+ for (i = 0, j = 0; names[i] != NULL; i++) {
|
|
+ if (strcmp(names[i], "DEFAULT") == 0)
|
|
+ j = 1;
|
|
+ }
|
|
+
|
|
+ types = calloc(i - j + 2, sizeof(token_type));
|
|
+ if (types == NULL) {
|
|
+ retval = ENOMEM;
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ /* If no default has been specified, use our internal default. */
|
|
+ if (j == 0) {
|
|
+ retval = token_type_decode(profile, NULL, &types[j++]);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+ } else {
|
|
+ j = 0;
|
|
+ }
|
|
+
|
|
+ for (i = 0; names[i] != NULL; i++) {
|
|
+ retval = token_type_decode(profile, names[i], &types[j++]);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ stringv_free(names);
|
|
+ *out = types;
|
|
+ return 0;
|
|
+
|
|
+error:
|
|
+ token_types_free(types);
|
|
+ stringv_free(names);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+/* Free the contents of a single token. */
|
|
+static void
|
|
+token_free(token *t)
|
|
+{
|
|
+ if (t == NULL)
|
|
+ return;
|
|
+
|
|
+ free(t->username.data);
|
|
+}
|
|
+
|
|
+/* Decode a single token from a JSON token object. */
|
|
+static krb5_error_code
|
|
+token_decode(krb5_context ctx, krb5_const_principal princ,
|
|
+ const token_type *types, k5_json_object obj, token *out)
|
|
+{
|
|
+ const char *type = NULL;
|
|
+ krb5_error_code retval;
|
|
+ k5_json_value tmp;
|
|
+ size_t i;
|
|
+ token t;
|
|
+
|
|
+ memset(&t, 0, sizeof(t));
|
|
+
|
|
+ tmp = k5_json_object_get(obj, "username");
|
|
+ if (tmp != NULL && k5_json_get_tid(tmp) == K5_JSON_TID_STRING) {
|
|
+ t.username = string2data_copy(k5_json_string_utf8(tmp));
|
|
+ if (t.username.data == NULL)
|
|
+ return ENOMEM;
|
|
+ }
|
|
+
|
|
+ tmp = k5_json_object_get(obj, "type");
|
|
+ if (tmp != NULL && k5_json_get_tid(tmp) == K5_JSON_TID_STRING)
|
|
+ type = k5_json_string_utf8(tmp);
|
|
+
|
|
+ for (i = 0; types[i].server != NULL; i++) {
|
|
+ if (strcmp(type == NULL ? "DEFAULT" : type, types[i].name) == 0)
|
|
+ t.type = &types[i];
|
|
+ }
|
|
+
|
|
+ if (t.username.data == NULL) {
|
|
+ retval = krb5_unparse_name_flags(ctx, princ,
|
|
+ t.type->strip_realm
|
|
+ ? KRB5_PRINCIPAL_UNPARSE_NO_REALM
|
|
+ : 0,
|
|
+ &t.username.data);
|
|
+ if (retval != 0)
|
|
+ return retval;
|
|
+ t.username.length = strlen(t.username.data);
|
|
+ }
|
|
+
|
|
+ *out = t;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Free an array of tokens. */
|
|
+static void
|
|
+tokens_free(token *tokens)
|
|
+{
|
|
+ size_t i;
|
|
+
|
|
+ if (tokens == NULL)
|
|
+ return;
|
|
+
|
|
+ for (i = 0; tokens[i].type != NULL; i++)
|
|
+ token_free(&tokens[i]);
|
|
+
|
|
+ free(tokens);
|
|
+}
|
|
+
|
|
+/* Decode an array of tokens from the configuration string. */
|
|
+static krb5_error_code
|
|
+tokens_decode(krb5_context ctx, krb5_const_principal princ,
|
|
+ const token_type *types, const char *config, token **out)
|
|
+{
|
|
+ krb5_error_code retval;
|
|
+ k5_json_value arr, obj;
|
|
+ token *tokens;
|
|
+ ssize_t len, i, j;
|
|
+
|
|
+ if (config == NULL)
|
|
+ config = "[{}]";
|
|
+
|
|
+ arr = k5_json_decode(config);
|
|
+ if (arr == NULL)
|
|
+ return ENOMEM;
|
|
+
|
|
+ if (k5_json_get_tid(arr) != K5_JSON_TID_ARRAY ||
|
|
+ (len = k5_json_array_length(arr)) == 0) {
|
|
+ k5_json_release(arr);
|
|
+
|
|
+ arr = k5_json_decode("[{}]");
|
|
+ if (arr == NULL)
|
|
+ return ENOMEM;
|
|
+
|
|
+ if (k5_json_get_tid(arr) != K5_JSON_TID_ARRAY) {
|
|
+ k5_json_release(arr);
|
|
+ return ENOMEM;
|
|
+ }
|
|
+
|
|
+ len = k5_json_array_length(arr);
|
|
+ }
|
|
+
|
|
+ tokens = calloc(len + 1, sizeof(token));
|
|
+ if (tokens == NULL) {
|
|
+ k5_json_release(arr);
|
|
+ return ENOMEM;
|
|
+ }
|
|
+
|
|
+ for (i = 0, j = 0; i < len; i++) {
|
|
+ obj = k5_json_array_get(arr, i);
|
|
+ if (k5_json_get_tid(obj) != K5_JSON_TID_OBJECT)
|
|
+ continue;
|
|
+
|
|
+ retval = token_decode(ctx, princ, types, obj, &tokens[j++]);
|
|
+ if (retval != 0) {
|
|
+ k5_json_release(arr);
|
|
+ while (--j > 0)
|
|
+ token_free(&tokens[j]);
|
|
+ free(tokens);
|
|
+ return retval;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ k5_json_release(arr);
|
|
+ *out = tokens;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void
|
|
+request_free(request *req)
|
|
+{
|
|
+ if (req == NULL)
|
|
+ return;
|
|
+
|
|
+ k5_radius_attrset_free(req->attrs);
|
|
+ tokens_free(req->tokens);
|
|
+ free(req);
|
|
+}
|
|
+
|
|
+krb5_error_code
|
|
+otp_state_new(krb5_context ctx, otp_state **out)
|
|
+{
|
|
+ char hostname[HOST_NAME_MAX + 1];
|
|
+ krb5_error_code retval;
|
|
+ profile_t profile;
|
|
+ krb5_data hndata;
|
|
+ otp_state *self;
|
|
+
|
|
+ retval = gethostname(hostname, sizeof(hostname));
|
|
+ if (retval != 0)
|
|
+ return retval;
|
|
+
|
|
+ self = calloc(1, sizeof(otp_state));
|
|
+ if (self == NULL)
|
|
+ return ENOMEM;
|
|
+
|
|
+ retval = krb5_get_profile(ctx, &profile);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ retval = token_types_decode(profile, &self->types);
|
|
+ profile_abandon(profile);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ retval = k5_radius_attrset_new(ctx, &self->attrs);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ hndata = make_data(hostname, strlen(hostname));
|
|
+ retval = k5_radius_attrset_add(self->attrs,
|
|
+ k5_radius_attr_name2num("NAS-Identifier"),
|
|
+ &hndata);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ retval = k5_radius_attrset_add_number(
|
|
+ self->attrs, k5_radius_attr_name2num("Service-Type"),
|
|
+ K5_RADIUS_SERVICE_TYPE_AUTHENTICATE_ONLY);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ self->ctx = ctx;
|
|
+ *out = self;
|
|
+ return 0;
|
|
+
|
|
+error:
|
|
+ otp_state_free(self);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+void
|
|
+otp_state_free(otp_state *self)
|
|
+{
|
|
+ if (self == NULL)
|
|
+ return;
|
|
+
|
|
+ k5_radius_attrset_free(self->attrs);
|
|
+ token_types_free(self->types);
|
|
+ free(self);
|
|
+}
|
|
+
|
|
+static void
|
|
+request_send(request *req);
|
|
+
|
|
+static void
|
|
+callback(krb5_error_code retval, const k5_radius_packet *rqst,
|
|
+ const k5_radius_packet *resp, void *data)
|
|
+{
|
|
+ request *req = data;
|
|
+
|
|
+ req->index++;
|
|
+
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ /* If we received an accept packet, success! */
|
|
+ if (k5_radius_packet_get_code(resp) ==
|
|
+ k5_radius_code_name2num("Access-Accept")) {
|
|
+ (*req->cb)(retval, otp_response_success, req->data);
|
|
+ request_free(req);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* If we have no more tokens to try, failure! */
|
|
+ if (req->tokens[req->index].type == NULL)
|
|
+ goto error;
|
|
+
|
|
+ /* Try the next token. */
|
|
+ request_send(req);
|
|
+
|
|
+error:
|
|
+ (*req->cb)(retval, otp_response_fail, req->data);
|
|
+ request_free(req);
|
|
+}
|
|
+
|
|
+static void
|
|
+request_send(request *req)
|
|
+{
|
|
+ krb5_error_code retval;
|
|
+
|
|
+ retval = k5_radius_attrset_add(req->attrs,
|
|
+ k5_radius_attr_name2num("User-Name"),
|
|
+ &req->tokens[req->index].username);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ retval = k5_radius_client_send(req->state->radius,
|
|
+ k5_radius_code_name2num("Access-Request"),
|
|
+ req->attrs,
|
|
+ req->tokens[req->index].type->server,
|
|
+ req->tokens[req->index].type->secret,
|
|
+ req->tokens[req->index].type->timeout,
|
|
+ req->tokens[req->index].type->retries,
|
|
+ callback, req);
|
|
+ k5_radius_attrset_del(req->attrs, k5_radius_attr_name2num("User-Name"), 0);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ return;
|
|
+
|
|
+error:
|
|
+ (*req->cb)(retval, otp_response_fail, req->data);
|
|
+ request_free(req);
|
|
+}
|
|
+
|
|
+void
|
|
+otp_state_verify(otp_state *state, verto_ctx *ctx, krb5_const_principal princ,
|
|
+ const char *config, const krb5_pa_otp_req *req,
|
|
+ otp_cb *cb, void *data)
|
|
+{
|
|
+ krb5_error_code retval;
|
|
+ request *rqst = NULL;
|
|
+
|
|
+ if (state->radius == NULL) {
|
|
+ retval = k5_radius_client_new(state->ctx, ctx, &state->radius);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ rqst = calloc(1, sizeof(request));
|
|
+ if (rqst == NULL) {
|
|
+ (*cb)(ENOMEM, otp_response_fail, data);
|
|
+ return;
|
|
+ }
|
|
+ rqst->state = state;
|
|
+ rqst->data = data;
|
|
+ rqst->cb = cb;
|
|
+
|
|
+ retval = k5_radius_attrset_copy(state->attrs, &rqst->attrs);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ retval = k5_radius_attrset_add(rqst->attrs,
|
|
+ k5_radius_attr_name2num("User-Password"),
|
|
+ &req->otp_value);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ retval = tokens_decode(state->ctx, princ, state->types, config,
|
|
+ &rqst->tokens);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ request_send(rqst);
|
|
+ return;
|
|
+
|
|
+error:
|
|
+ (*cb)(retval, otp_response_fail, data);
|
|
+ request_free(rqst);
|
|
+}
|
|
diff --git a/src/plugins/preauth/otp/otp_state.h b/src/plugins/preauth/otp/otp_state.h
|
|
new file mode 100644
|
|
index 0000000..89a164a
|
|
--- /dev/null
|
|
+++ b/src/plugins/preauth/otp/otp_state.h
|
|
@@ -0,0 +1,58 @@
|
|
+/*
|
|
+ * Copyright 2012 Red Hat, Inc. All rights reserved.
|
|
+ *
|
|
+ * Redistribution and use in source and binary forms, with or without
|
|
+ * modification, are permitted provided that the following conditions are met:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ *
|
|
+ * 2. Redistributions in binary form must reproduce the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer in
|
|
+ * the documentation and/or other materials provided with the
|
|
+ * distribution.
|
|
+ *
|
|
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
|
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
|
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
+ */
|
|
+
|
|
+#ifndef OTP_H_
|
|
+#define OTP_H_
|
|
+
|
|
+#include <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.1.4
|
|
|