krb5 commit: Add non-JSON APIs for PKINIT responder items

Greg Hudson ghudson at MIT.EDU
Thu Jul 18 00:58:58 EDT 2013


https://github.com/krb5/krb5/commit/ce02b69e27bcfa21bcab2ed195dfdbaa8040d773
commit ce02b69e27bcfa21bcab2ed195dfdbaa8040d773
Author: Nalin Dahyabhai <nalin at redhat.com>
Date:   Mon Jul 15 13:37:00 2013 -0400

    Add non-JSON APIs for PKINIT responder items
    
    Add wrappers for the JSON-oriented APIs for PKINIT responder items,
    modeled after the API we provide for OTP items:
    
    * krb5_responder_pkinit_get_challenge() returns the list of
      identities for which we need PINs
    * krb5_responder_pkinit_challenge_free() frees the structure that
      was returned by krb5_responder_pkinit_get_challenge()
    * krb5_responder_pkinit_set_answer() sets the answer to the PIN for
      one of the identities
    
    [ghudson at mit.edu: style cleanup; added comment pointing to main body
    of PKINIT module]
    
    ticket: 7680

 doc/appdev/init_creds.rst         |   16 +++
 doc/appdev/refs/api/index.rst     |    3 +
 src/include/krb5/krb5.hin         |   59 +++++++++++
 src/lib/krb5/krb/Makefile.in      |    3 +
 src/lib/krb5/krb/deps             |   12 ++
 src/lib/krb5/krb/preauth_pkinit.c |  204 +++++++++++++++++++++++++++++++++++++
 src/lib/krb5/libkrb5.exports      |    3 +
 src/lib/krb5_32.def               |    3 +
 8 files changed, 303 insertions(+), 0 deletions(-)

diff --git a/doc/appdev/init_creds.rst b/doc/appdev/init_creds.rst
index 63c9d61..07baa4a 100644
--- a/doc/appdev/init_creds.rst
+++ b/doc/appdev/init_creds.rst
@@ -210,6 +210,22 @@ challenge into a krb5_responder_otp_challenge structure.  The
 token information elements from the challenge and supplies the value
 and pin for that token.
 
+PKINIT password or PIN question
+###############################
+
+The :c:macro:`KRB5_RESPONDER_QUESTION_PKINIT` (or ``"pkinit"``) question
+type requests PINs for hardware devices and/or passwords for encrypted
+credentials which are stored on disk, potentially also supplying
+information about the state of the hardware devices.  The challenge and
+answer are JSON-encoded strings, but an application can use convenience
+functions to avoid doing any JSON processing itself.
+
+The :c:func:`krb5_responder_pkinit_get_challenge` function decodes the
+challenges into a krb5_responder_pkinit_challenge structure.  The
+:c:func:`krb5_responder_pkinit_set_answer` function can be used to
+supply the PIN or password for a particular client credential, and can
+be called multiple times.
+
 Example
 #######
 
diff --git a/doc/appdev/refs/api/index.rst b/doc/appdev/refs/api/index.rst
index 7009b30..b1a580a 100644
--- a/doc/appdev/refs/api/index.rst
+++ b/doc/appdev/refs/api/index.rst
@@ -87,6 +87,9 @@ Frequently used public interfaces
    krb5_responder_otp_get_challenge.rst
    krb5_responder_otp_set_answer.rst
    krb5_responder_otp_challenge_free.rst
+   krb5_responder_pkinit_get_challenge.rst
+   krb5_responder_pkinit_set_answer.rst
+   krb5_responder_pkinit_challenge_free.rst
    krb5_set_default_realm.rst
    krb5_set_password.rst
    krb5_set_password_using_ccache.rst
diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index faeabc7..f2ba06f 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -6689,6 +6689,65 @@ krb5_responder_otp_challenge_free(krb5_context ctx,
                                   krb5_responder_context rctx,
                                   krb5_responder_otp_challenge *chl);
 
+typedef struct _krb5_responder_pkinit_identity {
+    char *identity;
+    krb5_int32 token_flags;     /* 0 when not specified or not applicable. */
+} krb5_responder_pkinit_identity;
+
+typedef struct _krb5_responder_pkinit_challenge {
+    krb5_responder_pkinit_identity **identities;
+} krb5_responder_pkinit_challenge;
+
+/**
+ * Decode the KRB5_RESPONDER_QUESTION_PKINIT to a C struct.
+ *
+ * A convenience function which parses the KRB5_RESPONDER_QUESTION_PKINIT
+ * question challenge data, making it available in native C.  The main feature
+ * of this function is the ability to read the challenge without parsing
+ * the JSON.
+ *
+ * The returned value must be passed to krb5_responder_pkinit_challenge_free()
+ * to be freed.
+ *
+ * @param [in]  ctx             Library context
+ * @param [in]  rctx            Responder context
+ * @param [out] chl_out         Challenge structure
+ *
+ * @version New in 1.12
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_responder_pkinit_get_challenge(krb5_context ctx,
+                                    krb5_responder_context rctx,
+                                    krb5_responder_pkinit_challenge **chl_out);
+
+/**
+ * Answer the KRB5_RESPONDER_QUESTION_PKINIT question for one identity.
+ *
+ * @param [in] ctx              Library context
+ * @param [in] rctx             Responder context
+ * @param [in] identity         The identity for which a PIN is being supplied
+ * @param [in] pin              The provided PIN, or NULL for none
+ *
+ * @version New in 1.12
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_responder_pkinit_set_answer(krb5_context ctx, krb5_responder_context rctx,
+                                 const char *identity, const char *pin);
+
+/**
+ * Free the value returned by krb5_responder_pkinit_get_challenge().
+ *
+ * @param [in] ctx              Library context
+ * @param [in] rctx             Responder context
+ * @param [in] chl              The challenge to free
+ *
+ * @version New in 1.12
+ */
+void KRB5_CALLCONV
+krb5_responder_pkinit_challenge_free(krb5_context ctx,
+                                     krb5_responder_context rctx,
+                                     krb5_responder_pkinit_challenge *chl);
+
 /** Store options for @c _krb5_get_init_creds */
 typedef struct _krb5_get_init_creds_opt {
     krb5_flags flags;
diff --git a/src/lib/krb5/krb/Makefile.in b/src/lib/krb5/krb/Makefile.in
index d5db274..3cf857e 100644
--- a/src/lib/krb5/krb/Makefile.in
+++ b/src/lib/krb5/krb/Makefile.in
@@ -77,6 +77,7 @@ STLIBOBJS= \
 	preauth_ec.o	\
 	preauth_encts.o	\
 	preauth_otp.o	\
+	preauth_pkinit.o	\
 	preauth_sam2.o	\
 	gic_opt_set_pa.o	\
 	princ_comp.o	\
@@ -185,6 +186,7 @@ OBJS=	$(OUTPRE)addr_comp.$(OBJEXT)	\
 	$(OUTPRE)preauth_ec.$(OBJEXT)	\
 	$(OUTPRE)preauth_encts.$(OBJEXT)	\
 	$(OUTPRE)preauth_otp.$(OBJEXT)	\
+	$(OUTPRE)preauth_pkinit.$(OBJEXT)	\
 	$(OUTPRE)preauth_sam2.$(OBJEXT)	\
 	$(OUTPRE)gic_opt_set_pa.$(OBJEXT)	\
 	$(OUTPRE)princ_comp.$(OBJEXT)	\
@@ -293,6 +295,7 @@ SRCS=	$(srcdir)/addr_comp.c	\
 	$(srcdir)/preauth_ec.c	\
 	$(srcdir)/preauth_encts.c	\
 	$(srcdir)/preauth_otp.c	\
+	$(srcdir)/preauth_pkinit.c	\
 	$(srcdir)/preauth_sam2.c	\
 	$(srcdir)/gic_opt_set_pa.c	\
 	$(srcdir)/princ_comp.c	\
diff --git a/src/lib/krb5/krb/deps b/src/lib/krb5/krb/deps
index f4bbc69..7984173 100644
--- a/src/lib/krb5/krb/deps
+++ b/src/lib/krb5/krb/deps
@@ -758,6 +758,18 @@ preauth_otp.so preauth_otp.po $(OUTPRE)preauth_otp.$(OBJEXT): \
   $(top_srcdir)/include/krb5/clpreauth_plugin.h $(top_srcdir)/include/krb5/plugin.h \
   $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
   int-proto.h preauth_otp.c
+preauth_pkinit.so preauth_pkinit.po $(OUTPRE)preauth_pkinit.$(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/port-sockets.h \
+  $(top_srcdir)/include/socket-utils.h init_creds_ctx.h \
+  int-proto.h preauth_pkinit.c
 preauth_sam2.so preauth_sam2.po $(OUTPRE)preauth_sam2.$(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/preauth_pkinit.c b/src/lib/krb5/krb/preauth_pkinit.c
new file mode 100644
index 0000000..02810f2
--- /dev/null
+++ b/src/lib/krb5/krb/preauth_pkinit.c
@@ -0,0 +1,204 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/krb/preauth_pkinit.c - PKINIT clpreauth helpers */
+/*
+ * Copyright 2013 Red Hat, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *
+ *    2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This file defines libkrb5 APIs for manipulating PKINIT responder questions
+ * and answers.  The main body of the PKINIT clpreauth module is in the
+ * plugins/preauth/pkinit directory.
+ */
+
+#include "k5-int.h"
+#include "k5-json.h"
+#include "int-proto.h"
+#include "init_creds_ctx.h"
+
+struct get_one_challenge_data {
+    krb5_responder_pkinit_identity **identities;
+    krb5_error_code err;
+};
+
+static void
+get_one_challenge(void *arg, const char *key, k5_json_value val)
+{
+    struct get_one_challenge_data *data;
+    unsigned long token_flags;
+    int i;
+
+    data = arg;
+    if (data->err != 0)
+        return;
+    if (k5_json_get_tid(val) != K5_JSON_TID_NUMBER) {
+        data->err = EINVAL;
+        return;
+    }
+
+    token_flags = k5_json_number_value(val);
+    /* Find the slot for this entry. */
+    for (i = 0; data->identities[i] != NULL; i++)
+        continue;
+    /* Set the identity (a copy of the key) and the token flags. */
+    data->identities[i] = k5alloc(sizeof(*data->identities[i]), &data->err);
+    if (data->identities[i] == NULL)
+        return;
+    data->identities[i]->identity = strdup(key);
+    if (data->identities[i]->identity == NULL) {
+        data->err = ENOMEM;
+        return;
+    }
+    data->identities[i]->token_flags = token_flags;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_responder_pkinit_get_challenge(krb5_context ctx,
+                                    krb5_responder_context rctx,
+                                    krb5_responder_pkinit_challenge **chl_out)
+{
+    const char *challenge;
+    k5_json_value j;
+    struct get_one_challenge_data get_one_challenge_data;
+    krb5_responder_pkinit_challenge *chl = NULL;
+    unsigned int n_ids;
+    krb5_error_code ret;
+
+    *chl_out = NULL;
+    challenge = krb5_responder_get_challenge(ctx, rctx,
+                                             KRB5_RESPONDER_QUESTION_PKINIT);
+    if (challenge == NULL)
+       return 0;
+
+    ret = k5_json_decode(challenge, &j);
+    if (ret != 0)
+        return ret;
+
+    /* Create the returned object. */
+    chl = k5alloc(sizeof(*chl), &ret);
+    if (chl == NULL)
+        goto failed;
+
+    /* Create the list of identities. */
+    n_ids = k5_json_object_count(j);
+    chl->identities = k5calloc(n_ids + 1, sizeof(chl->identities[0]), &ret);
+    if (chl->identities == NULL)
+        goto failed;
+
+    /* Populate the object with identities. */
+    memset(&get_one_challenge_data, 0, sizeof(get_one_challenge_data));
+    get_one_challenge_data.identities = chl->identities;
+    k5_json_object_iterate(j, get_one_challenge, &get_one_challenge_data);
+    if (get_one_challenge_data.err != 0) {
+        ret = get_one_challenge_data.err;
+        goto failed;
+    }
+
+    /* All done. */
+    k5_json_release(j);
+    *chl_out = chl;
+    return 0;
+
+failed:
+    k5_json_release(j);
+    krb5_responder_pkinit_challenge_free(ctx, rctx, chl);
+    return ret;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_responder_pkinit_set_answer(krb5_context ctx, krb5_responder_context rctx,
+                                 const char *identity, const char *pin)
+{
+    char *answer = NULL;
+    const char *old_answer;
+    k5_json_value answers = NULL;
+    k5_json_string jpin = NULL;
+    krb5_error_code ret = ENOMEM;
+
+    /* If there's an answer already set, we're adding/removing a value. */
+    old_answer = k5_response_items_get_answer(rctx->items,
+                                              KRB5_RESPONDER_QUESTION_PKINIT);
+
+    /* If we're removing a value, and we have no values, we're done. */
+    if (old_answer == NULL && pin == NULL)
+        return 0;
+
+    /* Decode the old answers. */
+    if (old_answer == NULL)
+        old_answer = "{}";
+    ret = k5_json_decode(old_answer, &answers);
+    if (ret != 0)
+        goto cleanup;
+
+    if (k5_json_get_tid(answers) != K5_JSON_TID_OBJECT) {
+        ret = EINVAL;
+        goto cleanup;
+    }
+
+    /* Create and add the new pin string, if we're adding a value. */
+    if (pin != NULL) {
+        ret = k5_json_string_create(pin, &jpin);
+        if (ret != 0)
+            goto cleanup;
+        ret = k5_json_object_set(answers, identity, jpin);
+        if (ret != 0)
+            goto cleanup;
+    } else {
+        ret = k5_json_object_set(answers, identity, NULL);
+        if (ret != 0)
+            goto cleanup;
+    }
+
+    /* Encode and we're done. */
+    ret = k5_json_encode(answers, &answer);
+    if (ret != 0)
+        goto cleanup;
+
+    ret = krb5_responder_set_answer(ctx, rctx, KRB5_RESPONDER_QUESTION_PKINIT,
+                                    answer);
+
+cleanup:
+    k5_json_release(jpin);
+    k5_json_release(answers);
+    free(answer);
+    return ret;
+}
+
+void KRB5_CALLCONV
+krb5_responder_pkinit_challenge_free(krb5_context ctx,
+                                     krb5_responder_context rctx,
+                                     krb5_responder_pkinit_challenge *chl)
+{
+   unsigned int i;
+
+   if (chl == NULL)
+       return;
+   for (i = 0; chl->identities != NULL && chl->identities[i] != NULL; i++) {
+       free(chl->identities[i]->identity);
+       free(chl->identities[i]);
+   }
+   free(chl->identities);
+   free(chl);
+}
diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports
index f91e468..471671b 100644
--- a/src/lib/krb5/libkrb5.exports
+++ b/src/lib/krb5/libkrb5.exports
@@ -525,6 +525,9 @@ krb5_responder_set_answer
 krb5_responder_otp_get_challenge
 krb5_responder_otp_set_answer
 krb5_responder_otp_challenge_free
+krb5_responder_pkinit_get_challenge
+krb5_responder_pkinit_set_answer
+krb5_responder_pkinit_challenge_free
 krb5_salttype_to_string
 krb5_sendauth
 krb5_sendto_kdc
diff --git a/src/lib/krb5_32.def b/src/lib/krb5_32.def
index 9bff8a7..cd4c148 100644
--- a/src/lib/krb5_32.def
+++ b/src/lib/krb5_32.def
@@ -447,3 +447,6 @@ EXPORTS
 ; new in 1.12
 	krb5_free_enctypes				@419
 	krb5_kt_dup					@420
+	krb5_responder_pkinit_get_challenge		@421
+	krb5_responder_pkinit_set_answer		@422
+	krb5_responder_pkinit_challenge_free		@423


More information about the cvs-krb5 mailing list