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