summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNathaniel McCallum <npmccallum@redhat.com>2012-08-20 16:49:54 -0400
committerGreg Hudson <ghudson@mit.edu>2012-08-23 13:30:15 -0400
commit652313bbda5f4fdd6ca2828f0aa61ff1b0178c51 (patch)
tree14a3147191965cba7e86d8ef818fcf5880f68fb2
parenta7dc565cafbaa6c18d5a76ea3cc823c7159a0d6b (diff)
Add otp client preauth plugin
Implements the client side of RFC 6560. Not all features are implemented, but it should work for the most common cases. ticket: 7242 (new)
-rw-r--r--src/include/k5-int.h6
-rw-r--r--src/include/krb5/krb5.hin5
-rw-r--r--src/lib/krb5/krb/Makefile.in3
-rw-r--r--src/lib/krb5/krb/deps11
-rw-r--r--src/lib/krb5/krb/int-proto.h4
-rw-r--r--src/lib/krb5/krb/preauth2.c2
-rw-r--r--src/lib/krb5/krb/preauth_otp.c561
7 files changed, 592 insertions, 0 deletions
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index 6a853ce798..d119d02be4 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -479,6 +479,12 @@ typedef struct _krb5_enc_sam_response_enc_2 {
#define KRB5_OTP_FLAG_SEPARATE_PIN 0x02000000
#define KRB5_OTP_FLAG_CHECK_DIGIT 0x01000000
+#define KRB5_OTP_FORMAT_DECIMAL 0x00000000
+#define KRB5_OTP_FORMAT_HEXADECIMAL 0x00000001
+#define KRB5_OTP_FORMAT_ALPHANUMERIC 0x00000002
+#define KRB5_OTP_FORMAT_BINARY 0x00000003
+#define KRB5_OTP_FORMAT_BASE64 0x00000004
+
typedef struct _krb5_otp_tokeninfo {
krb5_flags flags;
krb5_data vendor;
diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index fde0d8ff44..16da726663 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -970,6 +970,7 @@ krb5_c_keyed_checksum_types(krb5_context context, krb5_enctype enctype,
#define KRB5_KEYUSAGE_AD_SIGNEDPATH -21
#define KRB5_KEYUSAGE_IAKERB_FINISHED 42
#define KRB5_KEYUSAGE_PA_PKINIT_KX 44
+#define KRB5_KEYUSAGE_PA_OTP_REQUEST 45
/* define in draft-ietf-krb-wg-preauth-framework*/
#define KRB5_KEYUSAGE_FAST_REQ_CHKSUM 50
#define KRB5_KEYUSAGE_FAST_ENC 51
@@ -1812,6 +1813,10 @@ krb5_verify_checksum(krb5_context context, krb5_cksumtype ctype,
#define KRB5_PADATA_FX_FAST 136
#define KRB5_PADATA_FX_ERROR 137
#define KRB5_PADATA_ENCRYPTED_CHALLENGE 138
+#define KRB5_PADATA_OTP_CHALLENGE 141
+#define KRB5_PADATA_OTP_REQUEST 142
+#define KRB5_PADATA_OTP_CONFIRM 143
+#define KRB5_PADATA_OTP_PIN_CHANGE 144
#define KRB5_PADATA_PKINIT_KX 147
#define KRB5_ENCPADATA_REQ_ENC_PA_REP 149
diff --git a/src/lib/krb5/krb/Makefile.in b/src/lib/krb5/krb/Makefile.in
index 3e48868a4c..8c3b5d1719 100644
--- a/src/lib/krb5/krb/Makefile.in
+++ b/src/lib/krb5/krb/Makefile.in
@@ -79,6 +79,7 @@ STLIBOBJS= \
preauth2.o \
preauth_ec.o \
preauth_encts.o \
+ preauth_otp.o \
preauth_sam2.o \
gic_opt_set_pa.o \
princ_comp.o \
@@ -185,6 +186,7 @@ OBJS= $(OUTPRE)addr_comp.$(OBJEXT) \
$(OUTPRE)preauth2.$(OBJEXT) \
$(OUTPRE)preauth_ec.$(OBJEXT) \
$(OUTPRE)preauth_encts.$(OBJEXT) \
+ $(OUTPRE)preauth_otp.$(OBJEXT) \
$(OUTPRE)preauth_sam2.$(OBJEXT) \
$(OUTPRE)gic_opt_set_pa.$(OBJEXT) \
$(OUTPRE)princ_comp.$(OBJEXT) \
@@ -291,6 +293,7 @@ SRCS= $(srcdir)/addr_comp.c \
$(srcdir)/preauth2.c \
$(srcdir)/preauth_ec.c \
$(srcdir)/preauth_encts.c \
+ $(srcdir)/preauth_otp.c \
$(srcdir)/gic_opt_set_pa.c \
$(srcdir)/princ_comp.c \
$(srcdir)/privsafe.c \
diff --git a/src/lib/krb5/krb/deps b/src/lib/krb5/krb/deps
index 8c4db775f2..5ca91e921a 100644
--- a/src/lib/krb5/krb/deps
+++ b/src/lib/krb5/krb/deps
@@ -755,6 +755,17 @@ preauth_encts.so preauth_encts.po $(OUTPRE)preauth_encts.$(OBJEXT): \
$(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 int-proto.h preauth_encts.c
+preauth_otp.so preauth_otp.po $(OUTPRE)preauth_otp.$(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-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 int-proto.h preauth_otp.c
gic_opt_set_pa.so gic_opt_set_pa.po $(OUTPRE)gic_opt_set_pa.$(OBJEXT): \
$(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
$(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
diff --git a/src/lib/krb5/krb/int-proto.h b/src/lib/krb5/krb/int-proto.h
index 8dc6867cdb..f794f143e6 100644
--- a/src/lib/krb5/krb/int-proto.h
+++ b/src/lib/krb5/krb/int-proto.h
@@ -68,6 +68,10 @@ clpreauth_sam2_initvt(krb5_context context, int maj_ver, int min_ver,
krb5_plugin_vtable vtable);
krb5_error_code
+clpreauth_otp_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable);
+
+krb5_error_code
krb5int_construct_matching_creds(krb5_context context, krb5_flags options,
krb5_creds *in_creds, krb5_creds *mcreds,
krb5_flags *fields);
diff --git a/src/lib/krb5/krb/preauth2.c b/src/lib/krb5/krb/preauth2.c
index c47269e291..2ed53ab08c 100644
--- a/src/lib/krb5/krb/preauth2.c
+++ b/src/lib/krb5/krb/preauth2.c
@@ -127,6 +127,8 @@ krb5_init_preauth_context(krb5_context kcontext)
clpreauth_encrypted_timestamp_initvt);
k5_plugin_register(kcontext, PLUGIN_INTERFACE_CLPREAUTH, "sam2",
clpreauth_sam2_initvt);
+ k5_plugin_register(kcontext, PLUGIN_INTERFACE_CLPREAUTH, "otp",
+ clpreauth_otp_initvt);
/* Get all available clpreauth vtables. */
if (k5_plugin_load_all(kcontext, PLUGIN_INTERFACE_CLPREAUTH, &plugins))
diff --git a/src/lib/krb5/krb/preauth_otp.c b/src/lib/krb5/krb/preauth_otp.c
new file mode 100644
index 0000000000..04eeb72ea1
--- /dev/null
+++ b/src/lib/krb5/krb/preauth_otp.c
@@ -0,0 +1,561 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/krb/preauth_otp.c - OTP clpreauth module */
+/*
+ * 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 "int-proto.h"
+
+#include <krb5/preauth_plugin.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+
+static krb5_preauthtype otp_client_supported_pa_types[] =
+ { KRB5_PADATA_OTP_CHALLENGE, 0 };
+
+/* Tests krb5_data to see if it is printable. */
+static krb5_boolean
+is_printable_string(const krb5_data *data)
+{
+ unsigned int i;
+
+ if (data == NULL)
+ return FALSE;
+
+ for (i = 0; i < data->length; i++) {
+ if (!isprint((unsigned char)data->data[i]))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Takes the nonce from the challenge and encrypts it into the request. */
+static krb5_error_code
+encrypt_nonce(krb5_context ctx, krb5_keyblock *key,
+ const krb5_pa_otp_challenge *chl, krb5_pa_otp_req *req)
+{
+ krb5_error_code retval;
+ krb5_enc_data encdata;
+ krb5_data *er;
+
+ /* Encode the nonce. */
+ retval = encode_krb5_pa_otp_enc_req(&chl->nonce, &er);
+ if (retval != 0)
+ return retval;
+
+ /* Do the encryption. */
+ retval = krb5_encrypt_helper(ctx, key, KRB5_KEYUSAGE_PA_OTP_REQUEST,
+ er, &encdata);
+ krb5_free_data(ctx, er);
+ if (retval != 0)
+ return retval;
+
+ free(req->enc_data.ciphertext.data);
+ req->enc_data = encdata;
+
+ return 0;
+}
+
+/* Checks to see if the user-supplied otp value matches the length and format
+ * of the supplied tokeninfo. */
+static int
+otpvalue_matches_tokeninfo(const char *otpvalue, krb5_otp_tokeninfo *ti)
+{
+ int (*table[])(int c) = { isdigit, isxdigit, isalnum };
+
+ if (otpvalue == NULL || ti == NULL)
+ return 0;
+
+ if (ti->length >= 0 && strlen(otpvalue) != (size_t)ti->length)
+ return 0;
+
+ if (ti->format >= 0 && ti->format < 3) {
+ while (*otpvalue) {
+ if (!(*table[ti->format])((unsigned char)*otpvalue++))
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* Removes the indexed tokeninfo from the array. */
+static void
+remove_tokeninfo(krb5_context ctx, krb5_otp_tokeninfo **tis, unsigned int i)
+{
+ unsigned int j = 0;
+
+ if (tis == NULL)
+ return;
+
+ for (j = 0; tis[j]; j++) {
+ if (j == i)
+ k5_free_otp_tokeninfo(ctx, tis[j]);
+ if (j >= i)
+ tis[j] = tis[j+1];
+ }
+}
+
+/* Performs a prompt and saves the response in the out parameter. */
+static krb5_error_code
+doprompt(krb5_context context, krb5_prompter_fct prompter, void *prompter_data,
+ const char *banner, const char *prompttxt, char *out, size_t len)
+{
+ krb5_prompt prompt;
+ krb5_data prompt_reply;
+ krb5_error_code retval;
+
+ if (prompttxt == NULL || out == NULL)
+ return EINVAL;
+
+ memset(out, 0, len);
+
+ prompt_reply = make_data(out, len);
+ prompt.reply = &prompt_reply;
+ prompt.prompt = (char *)prompttxt;
+ prompt.hidden = 1;
+
+ retval = (*prompter)(context, prompter_data, NULL, banner, 1, &prompt);
+ if (retval != 0)
+ return retval;
+
+ return 0;
+}
+
+/* Forces the user to choose a single tokeninfo via prompting.
+ * Removes all other tokeninfos from the array. */
+static krb5_error_code
+choose_token(krb5_context context, krb5_prompter_fct prompter,
+ void *prompter_data, krb5_otp_tokeninfo **tis)
+{
+ char *banner = NULL, *tmp, response[1024];
+ krb5_otp_tokeninfo *ti = NULL;
+ krb5_error_code retval = 0;
+ int i = 0, j = 0;
+
+ for (i = 0; tis[i] != NULL; i++) {
+ if (asprintf(&tmp, "%s\t%d. %s %.*s\n",
+ banner ? banner :
+ _("Please choose from the following:\n"),
+ i + 1, tis[i]->vendor.length,
+ _("Vendor:"),
+ tis[i]->vendor.data) < 0) {
+ free(banner);
+ return ENOMEM;
+ }
+
+ free(banner);
+ banner = tmp;
+ }
+
+ do {
+ retval = doprompt(context, prompter, prompter_data,
+ banner, _("Enter #"), response, sizeof(response));
+ if (retval != 0) {
+ free(banner);
+ return retval;
+ }
+
+ errno = 0;
+ j = strtol(response, NULL, 0);
+ if (errno != 0) {
+ free(banner);
+ return errno;
+ }
+ if (j < 1 || j > i)
+ continue;
+
+ ti = tis[--j];
+ for (i = 0; tis[i] != NULL; i++) {
+ if (tis[i] != ti)
+ remove_tokeninfo(context, tis, i--);
+ }
+ } while (ti == NULL);
+
+ free(banner);
+ return 0;
+}
+
+/* Like asprintf() but saves output into a krb5_data structure. */
+static krb5_error_code
+data_printf(krb5_data *data, const char *fmt, ...)
+{
+ va_list ap;
+ char *tmp = NULL;
+
+ if (data == NULL)
+ return EINVAL;
+
+ va_start(ap, fmt);
+ if (vasprintf(&tmp, fmt, ap) < 0) {
+ va_end(ap);
+ return errno;
+ }
+ va_end(ap);
+
+ free(data->data);
+ data->data = tmp;
+ data->length = strlen(tmp);
+
+ return 0;
+}
+
+/* Takes the otp value in the request and base64 encodes it. */
+static krb5_error_code
+base64_encode_request(krb5_pa_otp_req *req)
+{
+ return ENOTSUP;
+}
+
+static int
+is_tokeninfo_supported(krb5_otp_tokeninfo *ti)
+{
+ krb5_flags supported_flags = KRB5_OTP_FLAG_COLLECT_PIN |
+ KRB5_OTP_FLAG_NO_COLLECT_PIN |
+ KRB5_OTP_FLAG_SEPARATE_PIN;
+
+ /* Flags we don't support... */
+ if (ti->flags & ~supported_flags)
+ return 0;
+
+ /* We don't currently support hashing. */
+ if (ti->supported_hash_alg != NULL || ti->iteration_count >= 0)
+ return 0;
+
+ /* Remove tokeninfos with invalid vendor strings. */
+ if (!is_printable_string(&ti->vendor))
+ return 0;
+
+ /* We don't currently support base64. */
+ if (ti->format == KRB5_OTP_FORMAT_BASE64)
+ return 0;
+
+ return 1;
+}
+
+/* Builds a challenge string from the given tokeninfo. */
+static krb5_error_code
+make_challenge(const krb5_otp_tokeninfo *ti, char **challenge)
+{
+ if (challenge == NULL)
+ return EINVAL;
+
+ *challenge = NULL;
+
+ if (ti == NULL || ti->challenge.data == NULL)
+ return 0;
+
+ /*
+ * If the challenge isn't printable, then we have some kind of binary
+ * challenge which we cannot properly handle. So error out. Ideally there
+ * would be some mechanism to handle binary challenges to hardware tokens.
+ */
+ if (!is_printable_string(&ti->challenge))
+ return KRB5_PREAUTH_FAILED;
+
+ if (asprintf(challenge, "%s %.*s\n",
+ _("OTP Challenge:"),
+ ti->challenge.length,
+ ti->challenge.data) < 0)
+ return ENOMEM;
+
+ return 0;
+}
+
+/* Sets the otp value into the request. Similarly, collects and sets
+ * the pin if necessary. */
+static krb5_error_code
+set_value_and_collect_pin(krb5_context context, krb5_prompter_fct prompter,
+ void *prompter_data, const krb5_otp_tokeninfo *ti,
+ const char *otpvalue, krb5_pa_otp_req *req)
+{
+ krb5_error_code retval;
+ char otppin[1024];
+ krb5_flags pin;
+
+ pin = ti->flags & (KRB5_OTP_FLAG_COLLECT_PIN | KRB5_OTP_FLAG_SEPARATE_PIN);
+
+ /* If no PIN will be collected, just set the otp value. */
+ if (pin == 0) {
+ retval = data_printf(&req->otp_value, "%s", otpvalue);
+ if (retval != 0)
+ return retval;
+
+ req->pin = empty_data();
+ return 0;
+ }
+
+ /* Collect the PIN. */
+ retval = doprompt(context, prompter, prompter_data, NULL,
+ _("OTP Token PIN"), otppin, sizeof(otppin));
+ if (retval != 0)
+ return retval;
+
+ /* Set the separate PIN and Value fields. */
+ if (pin & KRB5_OTP_FLAG_SEPARATE_PIN) {
+ retval = data_printf(&req->otp_value, "%s", otpvalue);
+ if (retval != 0)
+ return retval;
+
+ retval = data_printf(&req->pin, "%s", otppin);
+ if (retval != 0)
+ return retval;
+
+ /* Prepend PIN to the Value field. */
+ } else {
+ retval = data_printf(&req->otp_value, "%s%s", otppin, otpvalue);
+ if (retval != 0)
+ return retval;
+
+ req->pin = empty_data();
+ }
+
+ return 0;
+}
+
+/* Builds a request object to send to the KDC, prompting the user if
+ * necessary. */
+static krb5_error_code
+make_request(krb5_context context, krb5_prompter_fct prompter,
+ void *prompter_data, krb5_otp_tokeninfo **tis,
+ krb5_pa_otp_req **request)
+{
+ krb5_error_code retval;
+ int i, challengers = 0;
+ char *challenge = NULL;
+ char otpvalue[1024];
+ krb5_pa_otp_req *req;
+
+ memset(otpvalue, 0, sizeof(otpvalue));
+
+ if (request == NULL || tis == NULL || tis[0] == NULL)
+ return EINVAL;
+
+ /* Filter out any tokeninfos we don't support. */
+ for (i = 0; tis[i] != NULL; i++) {
+ if (!is_tokeninfo_supported(tis[i])) {
+ remove_tokeninfo(context, tis, i--);
+ continue;
+ }
+
+ if (tis[i]->challenge.data != NULL)
+ challengers++; /* Count how many challenges we have. */
+ }
+ if (tis[0] == NULL) {
+ krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
+ _("No supported tokens"));
+ return KRB5_PREAUTH_FAILED; /* We have no supported tokeninfos. */
+ }
+
+ /* Setup our challenge, if present. */
+ if (challengers > 0) {
+ /* If we have multiple tokeninfos still, choose now. */
+ if (tis[1] != NULL) {
+ retval = choose_token(context, prompter, prompter_data, tis);
+ if (retval != 0)
+ return retval;
+ }
+
+ /* Create the challenge prompt. */
+ retval = make_challenge(tis[0], &challenge);
+ if (retval != 0)
+ return retval;
+ }
+
+ /* Prompt for token value. */
+ retval = doprompt(context, prompter, prompter_data, challenge,
+ _("Enter OTP Token Value"), otpvalue, sizeof(otpvalue));
+ free(challenge);
+ if (retval != 0)
+ return retval;
+
+ /* Filter out tokeninfos that don't match our token value. */
+ for (i = 0; tis[i] != NULL; i++) {
+ if (!otpvalue_matches_tokeninfo(otpvalue, tis[i]))
+ remove_tokeninfo(context, tis, i--);
+ }
+
+ /* If we still have multiple tokeninfos, choose now. */
+ if (tis[0] != NULL && tis[1] != NULL) {
+ retval = choose_token(context, prompter, prompter_data, tis);
+ if (retval != 0)
+ return retval;
+ }
+ if (tis == NULL || tis[0] == NULL) {
+ krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
+ _("OTP value doesn't match any token formats"));
+ return KRB5_PREAUTH_FAILED; /* We have no supported tokeninfos. */
+ }
+
+ /* Create the request. */
+ req = calloc(1, sizeof(krb5_pa_otp_req));
+ if (req == NULL)
+ return ENOMEM;
+
+ /* Collect the PIN, if necessary. */
+ retval = set_value_and_collect_pin(context, prompter, prompter_data,
+ tis[0], otpvalue, req);
+ if (retval != 0) {
+ k5_free_pa_otp_req(context, req);
+ return retval;
+ }
+
+ /* Do Base64 encoding, if necessary. */
+ if (tis[0]->format == KRB5_OTP_FORMAT_BASE64) {
+ retval = base64_encode_request(req);
+ if (retval != 0) {
+ k5_free_pa_otp_req(context, req);
+ return retval;
+ }
+ }
+
+ /* Steal values from the tokeninfo. */
+ req->flags = tis[0]->flags;
+ req->alg_id = tis[0]->alg_id;
+ req->format = tis[0]->format;
+ req->token_id = tis[0]->token_id;
+ req->vendor = tis[0]->vendor;
+ tis[0]->alg_id = empty_data();
+ tis[0]->token_id = empty_data();
+ tis[0]->vendor = empty_data();
+
+ *request = req;
+ return 0;
+}
+
+static int
+otp_client_get_flags(krb5_context context, krb5_preauthtype pa_type)
+{
+ return PA_REAL;
+}
+
+static krb5_error_code
+otp_client_process(krb5_context context, krb5_clpreauth_moddata moddata,
+ krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt,
+ krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock,
+ krb5_kdc_req *request, krb5_data *encoded_request_body,
+ krb5_data *encoded_previous_request, krb5_pa_data *pa_data,
+ krb5_prompter_fct prompter, void *prompter_data,
+ krb5_pa_data ***pa_data_out)
+{
+ krb5_pa_otp_challenge *chl = NULL;
+ krb5_pa_data **out_data = NULL;
+ krb5_keyblock *as_key = NULL;
+ krb5_pa_otp_req *req = NULL;
+ krb5_error_code retval = 0;
+ krb5_data tmp, *tmpp;
+
+ *pa_data_out = NULL;
+
+ /* Get FAST armor key. */
+ as_key = cb->fast_armor(context, rock);
+ if (as_key == NULL)
+ return ENOENT;
+
+ /* Use FAST armor key as response key. */
+ retval = cb->set_as_key(context, rock, as_key);
+ if (retval != 0)
+ return retval;
+
+ /* Decode the challenge. */
+ tmp = make_data(pa_data->contents, pa_data->length);
+ retval = decode_krb5_pa_otp_challenge(&tmp, &chl);
+ if (retval != 0)
+ return retval;
+
+ /* Fill in the request info from the TokenInfo structs .*/
+ retval = make_request(context, prompter, prompter_data,
+ chl->tokeninfo, &req);
+ if (retval != 0) {
+ k5_free_pa_otp_challenge(context, chl);
+ return retval;
+ }
+
+ /* Encrypt the challenge's nonce and set it in the request. */
+ retval = encrypt_nonce(context, as_key, chl, req);
+ k5_free_pa_otp_challenge(context, chl);
+ if (retval != 0) {
+ k5_free_pa_otp_req(context, req);
+ return retval;
+ }
+
+ /* Allocate the preauth data array and one item. */
+ out_data = calloc(2, sizeof(krb5_pa_data *));
+ if (out_data == NULL) {
+ k5_free_pa_otp_req(context, req);
+ return ENOMEM;
+ }
+ out_data[0] = calloc(1, sizeof(krb5_pa_data));
+ out_data[1] = NULL;
+ if (out_data[0] == NULL) {
+ free(out_data);
+ k5_free_pa_otp_req(context, req);
+ return ENOMEM;
+ }
+
+ /* Encode our request into the preauth data item. */
+ memset(out_data[0], 0, sizeof(krb5_pa_data));
+ out_data[0]->pa_type = KRB5_PADATA_OTP_REQUEST;
+ retval = encode_krb5_pa_otp_req(req, &tmpp);
+ k5_free_pa_otp_req(context, req);
+ if (retval != 0) {
+ free(out_data[0]);
+ free(out_data);
+ return ENOMEM;
+ }
+ out_data[0]->contents = (krb5_octet*)tmpp->data;
+ out_data[0]->length = tmpp->length;
+
+ *pa_data_out = out_data;
+ return 0;
+}
+
+krb5_error_code
+clpreauth_otp_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable)
+{
+ krb5_clpreauth_vtable vt;
+
+ if (maj_ver != 1)
+ return KRB5_PLUGIN_VER_NOTSUPP;
+
+ vt = (krb5_clpreauth_vtable)vtable;
+ vt->name = "otp";
+ vt->pa_type_list = otp_client_supported_pa_types;
+ vt->flags = otp_client_get_flags;
+ vt->process = otp_client_process;
+ vt->gic_opts = NULL;
+
+ return 0;
+}