krb5 commit: Add otp client preauth plugin

Greg Hudson ghudson at MIT.EDU
Thu Aug 23 13:32:24 EDT 2012


https://github.com/krb5/krb5/commit/652313bbda5f4fdd6ca2828f0aa61ff1b0178c51
commit 652313bbda5f4fdd6ca2828f0aa61ff1b0178c51
Author: Nathaniel McCallum <npmccallum at redhat.com>
Date:   Mon Aug 20 16:49:54 2012 -0400

    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)

 src/include/k5-int.h           |    6 +
 src/include/krb5/krb5.hin      |    5 +
 src/lib/krb5/krb/Makefile.in   |    3 +
 src/lib/krb5/krb/deps          |   11 +
 src/lib/krb5/krb/int-proto.h   |    4 +
 src/lib/krb5/krb/preauth2.c    |    2 +
 src/lib/krb5/krb/preauth_otp.c |  561 ++++++++++++++++++++++++++++++++++++++++
 7 files changed, 592 insertions(+), 0 deletions(-)

diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index 6a853ce..d119d02 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 fde0d8f..16da726 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 3e48868..8c3b5d1 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 8c4db77..5ca91e9 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 8dc6867..f794f14 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 c47269e..2ed53ab 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 0000000..04eeb72
--- /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;
+}


More information about the cvs-krb5 mailing list