krb5 commit: Add support for PKINIT deferring identity prompts

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


https://github.com/krb5/krb5/commit/805cd6078b5970750b979bd97b4b9f6147e1fd0d
commit 805cd6078b5970750b979bd97b4b9f6147e1fd0d
Author: Nalin Dahyabhai <nalin at redhat.com>
Date:   Fri Jun 28 17:12:39 2013 -0400

    Add support for PKINIT deferring identity prompts
    
    Learn to manage a list of deferred identities, for which we want to
    prompt for passwords or PINs, in pkinit_identity_crypto_context
    structures, along with their associated token flags.  These are opaque
    outside of pkinit_crypto_openssl and pkinit_crypto_nss, so both
    implementations need to provide wrapper functions that can be called
    from elsewhere in the module to populate and query the lists.
    
    ticket: 7680

 src/plugins/preauth/pkinit/pkinit.h                |   21 ++++
 src/plugins/preauth/pkinit/pkinit_crypto.h         |   11 ++
 src/plugins/preauth/pkinit/pkinit_crypto_nss.c     |   40 +++++++
 src/plugins/preauth/pkinit/pkinit_crypto_openssl.c |   38 +++++++
 src/plugins/preauth/pkinit/pkinit_crypto_openssl.h |    2 +
 src/plugins/preauth/pkinit/pkinit_identity.c       |  114 ++++++++++++++++++++
 6 files changed, 226 insertions(+), 0 deletions(-)

diff --git a/src/plugins/preauth/pkinit/pkinit.h b/src/plugins/preauth/pkinit/pkinit.h
index b44dfe7..38a43f5 100644
--- a/src/plugins/preauth/pkinit/pkinit.h
+++ b/src/plugins/preauth/pkinit/pkinit.h
@@ -71,6 +71,7 @@ extern int longhorn;	    /* XXX Talking to a Longhorn server? */
 
 #define PKINIT_CTX_MAGIC	0x05551212
 #define PKINIT_REQ_CTX_MAGIC	0xdeadbeef
+#define PKINIT_DEFERRED_ID_MAGIC    0x3ca20d21
 
 #define PKINIT_DEFAULT_DH_MIN_BITS  2048
 #define PKINIT_DH_MIN_CONFIG_BITS   1024
@@ -309,6 +310,26 @@ krb5_error_code pkinit_cert_matching
 	krb5_principal princ);
 
 /*
+ * Client's list of identities for which it needs PINs or passwords
+ */
+struct _pkinit_deferred_id {
+    int magic;
+    char *identity;
+    unsigned long ck_flags;
+    char *password;
+};
+typedef struct _pkinit_deferred_id *pkinit_deferred_id;
+
+krb5_error_code pkinit_set_deferred_id
+	(pkinit_deferred_id **identities, const char *identity,
+	 unsigned long ck_flags, const char *password);
+const char * pkinit_find_deferred_id
+	(pkinit_deferred_id *identities, const char *identity);
+unsigned long pkinit_get_deferred_id_flags
+	(pkinit_deferred_id *identities, const char *identity);
+void pkinit_free_deferred_ids(pkinit_deferred_id *identities);
+
+/*
  * initialization and free functions
  */
 void init_krb5_pa_pk_as_req(krb5_pa_pk_as_req **in);
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto.h b/src/plugins/preauth/pkinit/pkinit_crypto.h
index 8c2b006..b483aff 100644
--- a/src/plugins/preauth/pkinit/pkinit_crypto.h
+++ b/src/plugins/preauth/pkinit/pkinit_crypto.h
@@ -424,6 +424,17 @@ krb5_error_code create_issuerAndSerial
 		    receives length of encoded kdcPKId */
 
 /*
+ * These functions manipulate the deferred-identities list in the identity
+ * context, which is opaque outside of the crypto-specific bits.
+ */
+const pkinit_deferred_id * crypto_get_deferred_ids
+	(krb5_context context, pkinit_identity_crypto_context id_cryptoctx);
+krb5_error_code crypto_set_deferred_id
+	(krb5_context context,
+	 pkinit_identity_crypto_context id_cryptoctx,
+	 const char *identity, const char *password);
+
+/*
  * process the values from idopts and obtain the cert(s)
  * specified by those options, populating the id_cryptoctx.
  */
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_nss.c b/src/plugins/preauth/pkinit/pkinit_crypto_nss.c
index ef0e94d..3c6a87d 100644
--- a/src/plugins/preauth/pkinit/pkinit_crypto_nss.c
+++ b/src/plugins/preauth/pkinit/pkinit_crypto_nss.c
@@ -149,6 +149,8 @@ struct _pkinit_identity_crypto_context {
         krb5_prompter_fct prompter;
         void *prompter_data;
     } pwcb_args;
+    krb5_boolean defer_id_prompt;
+    pkinit_deferred_id *deferred_ids;
 };
 
 struct _pkinit_cert_info {      /* aka _pkinit_cert_handle */
@@ -785,6 +787,8 @@ pkinit_fini_identity_crypto(pkinit_identity_crypto_context id_cryptoctx)
     pkiDebug("%s\n", __FUNCTION__);
     /* The order of cleanup here is intended to ensure that nothing gets
      * freed before anything that might have a reference to it. */
+    if (id_cryptoctx->deferred_ids != NULL)
+        pkinit_free_deferred_ids(id_cryptoctx->deferred_ids);
     if (id_cryptoctx->id_cert != NULL)
         CERT_DestroyCertificate(id_cryptoctx->id_cert);
     CERT_DestroyCertList(id_cryptoctx->ca_certs);
@@ -2895,6 +2899,8 @@ crypto_load_certs(krb5_context context,
 {
     SECStatus status;
 
+    id_cryptoctx->defer_id_prompt = defer_id_prompts;
+
     switch (idopts->idtype) {
     case IDTYPE_FILE:
         status = crypto_load_files(context,
@@ -5550,3 +5556,37 @@ cms_signeddata_verify(krb5_context context,
 
     return 0;
 }
+
+/*
+ * Add an item to the pkinit_identity_crypto_context's list of deferred
+ * identities.
+ */
+krb5_error_code
+crypto_set_deferred_id(krb5_context context,
+                       pkinit_identity_crypto_context id_cryptoctx,
+                       const char *identity, const char *password)
+{
+    unsigned long ck_flags;
+
+    ck_flags = pkinit_get_deferred_id_flags(id_cryptoctx->deferred_ids,
+                                            identity);
+    return pkinit_set_deferred_id(&id_cryptoctx->deferred_ids,
+                                  identity, ck_flags, password);
+}
+
+/*
+ * Retrieve a read-only copy of the pkinit_identity_crypto_context's list of
+ * deferred identities, sure to be valid only until the next time someone calls
+ * either pkinit_set_deferred_id() or crypto_set_deferred_id().
+ */
+const pkinit_deferred_id *
+crypto_get_deferred_ids(krb5_context context,
+                        pkinit_identity_crypto_context id_cryptoctx)
+{
+    pkinit_deferred_id *deferred;
+    const pkinit_deferred_id *ret;
+
+    deferred = id_cryptoctx->deferred_ids;
+    ret = (const pkinit_deferred_id *)deferred;
+    return ret;
+}
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
index a780e71..1312555 100644
--- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
+++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
@@ -510,6 +510,8 @@ pkinit_fini_identity_crypto(pkinit_identity_crypto_context idctx)
         return;
 
     pkiDebug("%s: freeing ctx at %p\n", __FUNCTION__, idctx);
+    if (idctx->deferred_ids != NULL)
+        pkinit_free_deferred_ids(idctx->deferred_ids);
     free(idctx->identity);
     pkinit_fini_certs(idctx);
     pkinit_fini_pkcs11(idctx);
@@ -4797,6 +4799,8 @@ crypto_load_certs(krb5_context context,
 {
     krb5_error_code retval;
 
+    id_cryptoctx->defer_id_prompt = defer_id_prompts;
+
     switch(idopts->idtype) {
     case IDTYPE_FILE:
         retval = pkinit_get_certs_fs(context, plg_cryptoctx,
@@ -6078,3 +6082,37 @@ pkinit_pkcs11_code_to_text(int err)
     snprintf(uc, sizeof(uc), _("unknown code 0x%x"), err);
     return (uc);
 }
+
+/*
+ * Add an item to the pkinit_identity_crypto_context's list of deferred
+ * identities.
+ */
+krb5_error_code
+crypto_set_deferred_id(krb5_context context,
+                       pkinit_identity_crypto_context id_cryptoctx,
+                       const char *identity, const char *password)
+{
+    unsigned long flags;
+
+    flags = pkinit_get_deferred_id_flags(id_cryptoctx->deferred_ids,
+                                         identity);
+    return pkinit_set_deferred_id(&id_cryptoctx->deferred_ids,
+                                  identity, flags, password);
+}
+
+/*
+ * Retrieve a read-only copy of the pkinit_identity_crypto_context's list of
+ * deferred identities, sure to be valid only until the next time someone calls
+ * either pkinit_set_deferred_id() or crypto_set_deferred_id().
+ */
+const pkinit_deferred_id *
+crypto_get_deferred_ids(krb5_context context,
+                        pkinit_identity_crypto_context id_cryptoctx)
+{
+    pkinit_deferred_id *deferred;
+    const pkinit_deferred_id *ret;
+
+    deferred = id_cryptoctx->deferred_ids;
+    ret = (const pkinit_deferred_id *)deferred;
+    return ret;
+}
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h
index 0c14e00..3c73394 100644
--- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h
+++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.h
@@ -86,6 +86,8 @@ struct _pkinit_identity_crypto_context {
     int cert_id_len;
     CK_MECHANISM_TYPE mech;
 #endif
+    krb5_boolean defer_id_prompt;
+    pkinit_deferred_id *deferred_ids;
 };
 
 struct _pkinit_plg_crypto_context {
diff --git a/src/plugins/preauth/pkinit/pkinit_identity.c b/src/plugins/preauth/pkinit/pkinit_identity.c
index a53810c..eb198f4 100644
--- a/src/plugins/preauth/pkinit/pkinit_identity.c
+++ b/src/plugins/preauth/pkinit/pkinit_identity.c
@@ -683,3 +683,117 @@ pkinit_identity_prompt(krb5_context context,
 errout:
     return retval;
 }
+
+/*
+ * Create an entry in the passed-in list for the named identity, optionally
+ * with the specified token flag value and/or supplied password, replacing any
+ * existing entry with the same identity name.
+ */
+krb5_error_code
+pkinit_set_deferred_id(pkinit_deferred_id **identities,
+                       const char *identity, unsigned long ck_flags,
+                       const char *password)
+{
+    int i;
+    pkinit_deferred_id *out = NULL, *ids;
+    char *tmp;
+
+    /* Search for an entry that's already in the list. */
+    ids = *identities;
+    for (i = 0; ids != NULL && ids[i] != NULL; i++) {
+        if (strcmp(ids[i]->identity, identity) == 0) {
+            /* Replace its password value, then we're done. */
+            tmp = password ? strdup(password) : NULL;
+            if (password != NULL && tmp == NULL)
+                return ENOMEM;
+            ids[i]->ck_flags = ck_flags;
+            free(ids[i]->password);
+            ids[i]->password = tmp;
+            return 0;
+        }
+    }
+
+    /* Resize the list. */
+    out = realloc(ids, sizeof(*ids) * (i + 2));
+    if (out == NULL)
+        goto oom;
+    *identities = out;
+
+    /* Allocate the new final entry. */
+    out[i] = malloc(sizeof(*(out[i])));
+    if (out[i] == NULL)
+        goto oom;
+
+    /* Populate the new entry. */
+    out[i]->magic = PKINIT_DEFERRED_ID_MAGIC;
+    out[i]->identity = strdup(identity);
+    if (out[i]->identity == NULL)
+        goto oom;
+
+    out[i]->ck_flags = ck_flags;
+    out[i]->password = password ? strdup(password) : NULL;
+    if (password != NULL && out[i]->password == NULL)
+        goto oom;
+
+    /* Terminate the list. */
+    out[i + 1] = NULL;
+    return 0;
+
+oom:
+    if (out != NULL && out[i] != NULL) {
+        free(out[i]->identity);
+        free(out[i]);
+        out[i] = NULL;
+    }
+    return ENOMEM;
+}
+
+/*
+ * Return a password which we've associated with the named identity, if we've
+ * stored one.  Otherwise return NULL.
+ */
+const char *
+pkinit_find_deferred_id(pkinit_deferred_id *identities,
+                        const char *identity)
+{
+    int i;
+
+    for (i = 0; identities != NULL && identities[i] != NULL; i++) {
+        if (strcmp(identities[i]->identity, identity) == 0)
+            return identities[i]->password;
+    }
+    return NULL;
+}
+
+/*
+ * Return the flags associated with the specified identity, or 0 if we don't
+ * have such an identity.
+ */
+unsigned long
+pkinit_get_deferred_id_flags(pkinit_deferred_id *identities,
+                             const char *identity)
+{
+    int i;
+
+    for (i = 0; identities != NULL && identities[i] != NULL; i++) {
+        if (strcmp(identities[i]->identity, identity) == 0)
+            return identities[i]->ck_flags;
+    }
+    return 0;
+}
+
+/*
+ * Free a deferred_id list.
+ */
+void
+pkinit_free_deferred_ids(pkinit_deferred_id *identities)
+{
+    int i;
+
+    for (i = 0; identities != NULL && identities[i] != NULL; i++) {
+        free(identities[i]->identity);
+        free(identities[i]->password);
+        free(identities[i]);
+    }
+    free(identities);
+}


More information about the cvs-krb5 mailing list