krb5 commit: Support PKINIT NSS deferred identity prompting

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


https://github.com/krb5/krb5/commit/c5bf0caa8abf2b931f5ad258463d706d3cfd5f5b
commit c5bf0caa8abf2b931f5ad258463d706d3cfd5f5b
Author: Nalin Dahyabhai <nalin at dahyabhai.net>
Date:   Mon Jul 8 16:49:16 2013 -0400

    Support PKINIT NSS deferred identity prompting
    
    The password callback which we usually supply to NSS already gets a
    pointer to the pkinit_identity_crypto_context structure, but it needs to
    be passed the name of the identity for which it's being called.
    
    If it gets a name, and it's deferring prompting, just add the identity
    to the list of deferred identity prompts (the password callback wouldn't
    have been called if its result wasn't needed), and either return NULL
    (as an indication that we couldn't get a password) or an empty string (a
    value which we know is invalid) if that's handier.
    
    Otherwise, check for a password that's been stashed for its use for that
    identity, and return a copy of it if one's found.  If none of that
    works, try to use the prompter callback to ask for the password.
    
    ticket: 7680

 src/plugins/preauth/pkinit/pkinit_crypto_nss.c |  312 +++++++++++++++++++-----
 1 files changed, 252 insertions(+), 60 deletions(-)

diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_nss.c b/src/plugins/preauth/pkinit/pkinit_crypto_nss.c
index 56a2170..20ac6eb 100644
--- a/src/plugins/preauth/pkinit/pkinit_crypto_nss.c
+++ b/src/plugins/preauth/pkinit/pkinit_crypto_nss.c
@@ -126,6 +126,7 @@ struct _pkinit_identity_crypto_context {
     SECMODModule *pem_module;   /* used for FILE: and DIR: */
     struct _pkinit_identity_crypto_module {
         char *name;
+        char *spec;
         SECMODModule *module;
     } **id_modules;             /* used for PKCS11: */
     struct _pkinit_identity_crypto_userdb {
@@ -148,9 +149,11 @@ struct _pkinit_identity_crypto_context {
         krb5_context context;
         krb5_prompter_fct prompter;
         void *prompter_data;
+        const char *identity;
     } pwcb_args;
     krb5_boolean defer_id_prompt;
     pkinit_deferred_id *deferred_ids;
+    krb5_boolean defer_with_dummy_password;
 };
 
 struct _pkinit_cert_info {      /* aka _pkinit_cert_handle */
@@ -575,7 +578,8 @@ cmsdump(unsigned char *data, unsigned int length)
 
 /* A password-prompt callback for NSS that calls the libkrb5 callback. */
 static char *
-crypto_pwfn(const char *what, PRBool is_hardware, PRBool retry, void *arg)
+crypto_pwfn(const char *what, PRBool is_hardware, CK_FLAGS token_flags,
+            PRBool retry, void *arg)
 {
     int ret;
     pkinit_identity_crypto_context id;
@@ -583,6 +587,7 @@ crypto_pwfn(const char *what, PRBool is_hardware, PRBool retry, void *arg)
     krb5_prompt_type prompt_types[2];
     krb5_data reply;
     char *text, *answer;
+    const char *warning, *password;
     size_t text_size;
     void *data;
 
@@ -593,6 +598,56 @@ crypto_pwfn(const char *what, PRBool is_hardware, PRBool retry, void *arg)
     if (arg == NULL)
         return NULL;
     id = arg;
+
+    /* If we need to warn about the PIN, figure out the text. */
+    if (token_flags & CKF_USER_PIN_LOCKED)
+        warning = "PIN locked";
+    else if (token_flags & CKF_USER_PIN_FINAL_TRY)
+        warning = "PIN final try";
+    else if (token_flags & CKF_USER_PIN_COUNT_LOW)
+        warning = "PIN count low";
+    else
+        warning = NULL;
+
+    /*
+     * If we have the name of an identity here, then we're either supposed to
+     * save its name, or attempt to use a password, if one was supplied.
+     */
+    if (id->pwcb_args.identity != NULL) {
+        if (id->defer_id_prompt) {
+            /* If we're in the defer-prompts step, just save the identity name
+             * and "fail". */
+            if (!is_hardware)
+                token_flags = 0;
+            pkinit_set_deferred_id(&id->deferred_ids, id->pwcb_args.identity,
+                                   token_flags, NULL);
+            if (id->defer_with_dummy_password) {
+                /* Return a useless result. */
+                answer = PR_Malloc(1);
+                if (answer != NULL) {
+                    *answer = '\0';
+                    return answer;
+                }
+            }
+        } else {
+            /* Check if we already have a password for this identity.  If so,
+             * just return a copy of it. */
+            password = pkinit_find_deferred_id(id->deferred_ids,
+                                               id->pwcb_args.identity);
+            if (password != NULL) {
+                /* The result will be freed with PR_Free, so return a copy. */
+                text_size = strlen(password) + 1;
+                answer = PR_Malloc(text_size);
+                if (answer != NULL) {
+                    memcpy(answer, password, text_size);
+                    pkiDebug("%s: returning %ld-char answer\n", __FUNCTION__,
+                             (long)strlen(answer));
+                    return answer;
+                }
+            }
+        }
+    }
+
     if (id->pwcb_args.prompter == NULL)
         return NULL;
 
@@ -603,10 +658,14 @@ crypto_pwfn(const char *what, PRBool is_hardware, PRBool retry, void *arg)
         pkiDebug("out of memory");
         return NULL;
     }
-    if (is_hardware)
-        snprintf(text, text_size, "%s PIN", what);
-    else
+    if (is_hardware) {
+        if (warning != NULL)
+            snprintf(text, text_size, "%s PIN (%s)", what, warning);
+        else
+            snprintf(text, text_size, "%s PIN", what);
+    } else {
         snprintf(text, text_size, "%s %s", _("Pass phrase for"), what);
+    }
     memset(&prompt, 0, sizeof(prompt));
     prompt.prompt = text;
     prompt.hidden = 1;
@@ -651,16 +710,33 @@ crypto_pwfn(const char *what, PRBool is_hardware, PRBool retry, void *arg)
 static char *
 crypto_pwcb(PK11SlotInfo *slot, PRBool retry, void *arg)
 {
-    return crypto_pwfn(PK11_GetTokenName(slot), PK11_IsHW(slot), retry, arg);
+    pkinit_identity_crypto_context id;
+    const char *what = NULL;
+    CK_TOKEN_INFO tinfo;
+    CK_FLAGS tflags;
+
+    if (PK11_GetTokenInfo(slot, &tinfo) == SECSuccess)
+        tflags = tinfo.flags;
+    else
+        tflags = 0;
+    if (arg != NULL) {
+        id = arg;
+        what = id->pwcb_args.identity;
+    }
+    return crypto_pwfn((what != NULL) ? what : PK11_GetTokenName(slot),
+                       PK11_IsHW(slot), tflags, retry, arg);
 }
 
-/* Make sure we're using our callback, and set up the callback data. */
+/*
+ * Make sure we're using our callback, and set up the callback data.
+ */
 static void *
 crypto_pwcb_prep(pkinit_identity_crypto_context id_cryptoctx,
-                 krb5_context context)
+                 const char *identity, krb5_context context)
 {
     PK11_SetPasswordFunc(crypto_pwcb);
     id_cryptoctx->pwcb_args.context = context;
+    id_cryptoctx->pwcb_args.identity = identity;
     return id_cryptoctx;
 }
 
@@ -804,9 +880,12 @@ pkinit_fini_identity_crypto(pkinit_identity_crypto_context id_cryptoctx)
     if (id_cryptoctx->id_userdbs != NULL)
         for (i = 0; id_cryptoctx->id_userdbs[i] != NULL; i++)
             PK11_FreeSlot(id_cryptoctx->id_userdbs[i]->userdb);
-    if (id_cryptoctx->id_modules != NULL)
-        for (i = 0; id_cryptoctx->id_modules[i] != NULL; i++)
-            SECMOD_DestroyModule(id_cryptoctx->id_modules[i]->module);
+    if (id_cryptoctx->id_modules != NULL) {
+        for (i = 0; id_cryptoctx->id_modules[i] != NULL; i++) {
+            if (id_cryptoctx->id_modules[i]->module != NULL)
+                SECMOD_DestroyModule(id_cryptoctx->id_modules[i]->module);
+        }
+    }
     if (id_cryptoctx->id_crls != NULL)
         for (i = 0; id_cryptoctx->id_crls[i] != NULL; i++)
             CERT_UncacheCRL(CERT_GetDefaultCertDB(), id_cryptoctx->id_crls[i]);
@@ -1398,7 +1477,7 @@ client_create_dh(krb5_context context,
 
     /* Generate a public value and a private key. */
     slot = PK11_GetBestSlot(CKM_DH_PKCS_KEY_PAIR_GEN,
-                            crypto_pwcb_prep(id_cryptoctx, context));
+                            crypto_pwcb_prep(id_cryptoctx, NULL, context));
     if (slot == NULL) {
         PORT_FreeArena(pool, PR_TRUE);
         pkiDebug("%s: error selecting slot\n", __FUNCTION__);
@@ -1407,7 +1486,7 @@ client_create_dh(krb5_context context,
     pub = NULL;
     priv = PK11_GenerateKeyPair(slot, CKM_DH_PKCS_KEY_PAIR_GEN,
                                 &dh_param, &pub, PR_FALSE, PR_FALSE,
-                                crypto_pwcb_prep(id_cryptoctx, context));
+                                crypto_pwcb_prep(id_cryptoctx, NULL, context));
 
     /* Finish building the return values. */
     memset(&encoded, 0, sizeof(encoded));
@@ -1487,7 +1566,7 @@ client_process_dh(krb5_context context,
     /* Generate the shared value using our private key and the KDC's
      * public key. */
     slot = PK11_GetBestSlot(CKM_DH_PKCS_KEY_PAIR_GEN,
-                            crypto_pwcb_prep(id_cryptoctx, context));
+                            crypto_pwcb_prep(id_cryptoctx, NULL, context));
     if (slot == NULL) {
         SECKEY_DestroyPublicKey(pub);
         PORT_FreeArena(pool, PR_TRUE);
@@ -1498,7 +1577,7 @@ client_process_dh(krb5_context context,
                          CKM_DH_PKCS_DERIVE,
                          CKM_TLS_MASTER_KEY_DERIVE_DH,
                          CKA_DERIVE,
-                         0, crypto_pwcb_prep(id_cryptoctx, context));
+                         0, crypto_pwcb_prep(id_cryptoctx, NULL, context));
     if (sym == NULL) {
         PK11_FreeSlot(slot);
         SECKEY_DestroyPublicKey(pub);
@@ -1622,7 +1701,7 @@ server_process_dh(krb5_context context,
 
     /* Generate a public value and a private key using the parameters. */
     slot = PK11_GetBestSlot(CKM_DH_PKCS_KEY_PAIR_GEN,
-                            crypto_pwcb_prep(id_cryptoctx, context));
+                            crypto_pwcb_prep(id_cryptoctx, NULL, context));
     if (slot == NULL) {
         PORT_FreeArena(pool, PR_TRUE);
         return ENOMEM;
@@ -1630,7 +1709,7 @@ server_process_dh(krb5_context context,
     pub = NULL;
     priv = PK11_GenerateKeyPair(slot, CKM_DH_PKCS_KEY_PAIR_GEN,
                                 &dh_params, &pub, PR_FALSE, PR_FALSE,
-                                crypto_pwcb_prep(id_cryptoctx, context));
+                                crypto_pwcb_prep(id_cryptoctx, NULL, context));
     if (priv == NULL) {
         PK11_FreeSlot(slot);
         PORT_FreeArena(pool, PR_TRUE);
@@ -1657,7 +1736,7 @@ server_process_dh(krb5_context context,
                          CKM_DH_PKCS_DERIVE,
                          CKM_TLS_MASTER_KEY_DERIVE_DH,
                          CKA_DERIVE,
-                         0, crypto_pwcb_prep(id_cryptoctx, context));
+                         0, crypto_pwcb_prep(id_cryptoctx, NULL, context));
     if (sym == NULL) {
         SECKEY_DestroyPrivateKey(priv);
         SECKEY_DestroyPublicKey(pub);
@@ -1898,7 +1977,8 @@ cert_maybe_add_to_list(CERTCertList *list, CERTCertificate *cert)
 static SECStatus
 cert_load_ca_certs_from_slot(krb5_context context,
                              pkinit_identity_crypto_context id,
-                             PK11SlotInfo *slot)
+                             PK11SlotInfo *slot,
+                             const char *identity)
 {
     CERTCertificate *cert;
     CERTCertList *list;
@@ -1907,12 +1987,14 @@ cert_load_ca_certs_from_slot(krb5_context context,
     SECStatus status;
 
     /* Log in if the slot requires it. */
-    if (!PK11_IsLoggedIn(slot, crypto_pwcb_prep(id, context)) &&
+    PK11_TokenRefresh(slot);
+    if (!PK11_IsLoggedIn(slot, crypto_pwcb_prep(id, identity, context)) &&
         PK11_NeedLogin(slot)) {
         pkiDebug("%s: logging in to token \"%s\"\n",
                  __FUNCTION__, PK11_GetTokenName(slot));
         if (PK11_Authenticate(slot, PR_TRUE,
-                              crypto_pwcb_prep(id, context)) != SECSuccess) {
+                              crypto_pwcb_prep(id, identity,
+                                               context)) != SECSuccess) {
             pkiDebug("%s: error logging into \"%s\": %s, skipping\n",
                      __FUNCTION__, PK11_GetTokenName(slot),
                      PORT_ErrorToName(PORT_GetError()));
@@ -1954,7 +2036,7 @@ cert_load_ca_certs_from_slot(krb5_context context,
               CERTDB_TRUSTED_CLIENT_CA | CERTDB_NS_TRUSTED_CA)) == 0)
             continue;
         /* DestroyCertList frees all of the certs in the list,
-         * so we need to create a copy that it can own. */
+         * so we need to create a copy that we can own. */
         cert = CERT_DupCertificate(node->cert);
         /* Add it to the list. */
         if (cert_maybe_add_to_list(id->ca_certs, cert) != SECSuccess)
@@ -1970,7 +2052,9 @@ cert_load_certs_with_keys_from_slot(krb5_context context,
                                     pkinit_identity_crypto_context
                                     id_cryptoctx,
                                     PK11SlotInfo *slot,
-                                    const char *label, const char *id)
+                                    const char *cert_label,
+                                    const char *cert_id,
+                                    const char *identity)
 {
     CERTCertificate *cert;
     CERTCertList *clist;
@@ -1979,17 +2063,19 @@ cert_load_certs_with_keys_from_slot(krb5_context context,
     int status;
 
     /* Log in if the slot requires it. */
-    if (!PK11_IsLoggedIn(slot, crypto_pwcb_prep(id_cryptoctx, context)) &&
+    PK11_TokenRefresh(slot);
+    if (!PK11_IsLoggedIn(slot, crypto_pwcb_prep(id_cryptoctx, identity,
+                                                context)) &&
         PK11_NeedLogin(slot)) {
         pkiDebug("%s: logging in to token \"%s\"\n",
                  __FUNCTION__, PK11_GetTokenName(slot));
         if (PK11_Authenticate(slot, PR_TRUE,
-                              crypto_pwcb_prep(id_cryptoctx,
+                              crypto_pwcb_prep(id_cryptoctx, identity,
                                                context)) != SECSuccess) {
             pkiDebug("%s: error logging into \"%s\": %s, skipping\n",
                      __FUNCTION__, PK11_GetTokenName(slot),
                      PORT_ErrorToName(PORT_GetError()));
-            return ENOMEM;
+            return id_cryptoctx->defer_id_prompt ? 0 : ENOMEM;
         }
     }
     /* Get the list of certs from the slot. */
@@ -2015,21 +2101,21 @@ cert_load_certs_with_keys_from_slot(krb5_context context,
              !CERT_LIST_END(cnode, clist);
          cnode = CERT_LIST_NEXT(cnode)) {
         if (cnode->cert->nickname != NULL) {
-            if ((label != NULL) && (id != NULL)) {
-                if ((strcmp(id, cnode->cert->nickname) != 0) &&
-                    (strcmp(label, cnode->cert->nickname) != 0))
+            if ((cert_label != NULL) && (cert_id != NULL)) {
+                if ((strcmp(cert_id, cnode->cert->nickname) != 0) &&
+                    (strcmp(cert_label, cnode->cert->nickname) != 0))
                     continue;
-            } else if (label != NULL) {
-                if (strcmp(label, cnode->cert->nickname) != 0)
+            } else if (cert_label != NULL) {
+                if (strcmp(cert_label, cnode->cert->nickname) != 0)
                     continue;
-            } else if (id != NULL) {
-                if (strcmp(id, cnode->cert->nickname) != 0)
+            } else if (cert_id != NULL) {
+                if (strcmp(cert_id, cnode->cert->nickname) != 0)
                     continue;
             }
         }
         key = PK11_FindPrivateKeyFromCert(slot, cnode->cert,
                                           crypto_pwcb_prep(id_cryptoctx,
-                                                           context));
+                                                           identity, context));
         if (key == NULL) {
             pkiDebug("%s: no key for \"%s\", skipping it\n",
                      __FUNCTION__,
@@ -2053,6 +2139,10 @@ cert_load_certs_with_keys_from_slot(krb5_context context,
     return status;
 }
 
+/*
+ * Reassemble the identity as it was supplied by the user or the library
+ * configuration.
+ */
 static char *
 reassemble_pkcs11_name(PLArenaPool *pool, pkinit_identity_opts *idopts)
 {
@@ -2091,6 +2181,43 @@ reassemble_pkcs11_name(PLArenaPool *pool, pkinit_identity_opts *idopts)
     return ret;
 }
 
+/*
+ * Assemble an identity string that will distinguish this token from any other
+ * that is accessible through the same module, even if the user didn't specify
+ * a token name.
+ */
+static char *
+reassemble_pkcs11_identity(PLArenaPool *pool, pkinit_identity_opts *idopts,
+                           long slotid, const char *tokenname)
+{
+    struct k5buf buf;
+    int n = 0;
+    char *ret;
+
+    k5_buf_init_dynamic(&buf);
+    k5_buf_add(&buf, "PKCS11:");
+    n = 0;
+    if (idopts->p11_module_name != NULL) {
+        k5_buf_add_fmt(&buf, "%smodule_name=%s",
+                       n++ ? ":" : "",
+                       idopts->p11_module_name);
+    }
+
+    if (slotid != PK_NOSLOT)
+        k5_buf_add_fmt(&buf, "%sslotid=%ld", n++ ? ":" : "", slotid);
+
+    if (tokenname != NULL)
+        k5_buf_add_fmt(&buf, "%stoken=%s", n++ ? ":" : "", tokenname);
+
+    if (k5_buf_len(&buf) >= 0)
+        ret = PORT_ArenaStrdup(pool, k5_buf_data(&buf));
+    else
+        ret = NULL;
+    k5_free_buf(&buf);
+
+    return ret;
+}
+
 static SECStatus
 crypto_load_pkcs11(krb5_context context,
                    pkinit_plg_crypto_context plg_cryptoctx,
@@ -2100,9 +2227,10 @@ crypto_load_pkcs11(krb5_context context,
 {
     struct _pkinit_identity_crypto_module **id_modules, *module;
     PK11SlotInfo *slot;
-    char *spec;
+    CK_TOKEN_INFO tinfo;
+    char *spec, *identity;
     size_t spec_size;
-    const char *label, *id, *tokenname;
+    const char *tokenname;
     SECStatus status;
     int i, j;
 
@@ -2142,15 +2270,27 @@ crypto_load_pkcs11(krb5_context context,
     /* Allocate a bigger list. */
     id_modules = PORT_ArenaZAlloc(id_cryptoctx->pool,
                                   sizeof(id_modules[0]) * (i + 2));
+    if (id_modules == NULL)
+        return SECFailure;
     for (j = 0; j < i; j++)
         id_modules[j] = id_cryptoctx->id_modules[j];
 
-    /* Actually load the module. */
+    /* Actually load the module, or just ref an already-loaded copy. */
     module = PORT_ArenaZAlloc(id_cryptoctx->pool, sizeof(*module));
     if (module == NULL)
         return SECFailure;
     module->name = reassemble_pkcs11_name(id_cryptoctx->pool, idopts);
-    module->module = SECMOD_LoadUserModule(spec, NULL, PR_FALSE);
+    if (module->name == NULL)
+        return SECFailure;
+    module->spec = spec;
+    for (j = 0; j < i; j++) {
+        if (strcmp(module->spec, id_modules[j]->spec) == 0)
+            break;
+    }
+    if (j < i)
+        module->module = SECMOD_ReferenceModule(id_modules[j]->module);
+    else
+        module->module = SECMOD_LoadUserModule(spec, NULL, PR_FALSE);
     if (module->module == NULL) {
         pkiDebug("%s: error loading PKCS11 module \"%s\"",
                  __FUNCTION__, idopts->p11_module_name);
@@ -2160,6 +2300,7 @@ crypto_load_pkcs11(krb5_context context,
         pkiDebug("%s: error really loading PKCS11 module \"%s\"",
                  __FUNCTION__, idopts->p11_module_name);
         SECMOD_DestroyModule(module->module);
+        module->module = NULL;
         return SECFailure;
     }
     SECMOD_UpdateSlotList(module->module);
@@ -2177,6 +2318,7 @@ crypto_load_pkcs11(krb5_context context,
          (i < module->module->slotCount) &&
          ((slot = module->module->slots[i]) != NULL);
          i++) {
+        PK11_TokenRefresh(slot);
         if (idopts->slotid != PK_NOSLOT) {
             if (idopts->slotid != PK11_GetSlotID(slot))
                 continue;
@@ -2184,21 +2326,46 @@ crypto_load_pkcs11(krb5_context context,
         tokenname = PK11_GetTokenName(slot);
         if (tokenname == NULL || strlen(tokenname) == 0)
             continue;
+        /* If we're looking for a specific token, and this isn't it, go on. */
         if (idopts->token_label != NULL) {
             if (strcmp(idopts->cert_label, tokenname) != 0)
                 continue;
         }
-        /* Load private keys and their certs from this slot. */
-        label = idopts->cert_label;
-        id = idopts->cert_id_string;
+        /* Assemble a useful identity string, in case of an incomplete one. */
+        identity = reassemble_pkcs11_identity(id_cryptoctx->pool, idopts,
+                                              (long)PK11_GetSlotID(slot),
+                                              tokenname);
+        /*
+         * Skip past all of the loading-certificates-and-keys logic, pick up
+         * the token flags, and call it done for now.
+         */
+        if (id_cryptoctx->defer_id_prompt) {
+            if (!PK11_IsLoggedIn(slot, crypto_pwcb_prep(id_cryptoctx, identity,
+                                                        context)) &&
+                PK11_NeedLogin(slot)) {
+                pkiDebug("%s: reading flags for token \"%s\"\n",
+                         __FUNCTION__, PK11_GetTokenName(slot));
+                if (PK11_GetTokenInfo(slot, &tinfo) == SECSuccess) {
+                    pkinit_set_deferred_id(&id_cryptoctx->deferred_ids,
+                                           identity, tinfo.flags, NULL);
+                }
+            }
+            return SECSuccess;
+        }
+        if (!PK11_IsPresent(slot))
+            continue;
+        /* Load private keys and their certs from this token. */
         if (cert_load_certs_with_keys_from_slot(context, id_cryptoctx,
-                                                slot, label, id) == 0)
+                                                slot, idopts->cert_label,
+                                                idopts->cert_id_string,
+                                                identity) == 0)
             status = SECSuccess;
         /* If no label was specified, then we've looked at a token, so we're
          * done. */
         if (idopts->token_label == NULL)
             break;
     }
+
     return status;
 }
 
@@ -2348,6 +2515,7 @@ crypto_load_pkcs12(krb5_context context,
     SECItem tmp, password;
     PRBool retry;
     int attempt;
+    char *identity;
 
     if ((slot = crypto_get_p12_slot(id_cryptoctx)) == NULL) {
         pkiDebug("%s: skipping identity PKCS12 bundle \"%s\": "
@@ -2373,11 +2541,15 @@ crypto_load_pkcs12(krb5_context context,
     password.len = 2;
     attempt = 0;
     ctx = NULL;
+    identity = reassemble_pkcs12_name(id_cryptoctx->pool, name);
+    if (identity == NULL)
+        return SECFailure;
+    id_cryptoctx->id_p12_slot.p12name = identity;
     do {
         retry = PR_FALSE;
         ctx = SEC_PKCS12DecoderStart(&password,
                                      slot,
-                                     crypto_pwcb_prep(id_cryptoctx,
+                                     crypto_pwcb_prep(id_cryptoctx, identity,
                                                       context),
                                      NULL, NULL, NULL, NULL, NULL);
         if (ctx == NULL) {
@@ -2397,13 +2569,19 @@ crypto_load_pkcs12(krb5_context context,
             unsigned char *ucs2s;
             size_t i, n_ucs2s;
             SECErrorCodes err;
+
             err = PORT_GetError();
             SEC_PKCS12DecoderFinish(ctx);
             switch (err) {
             case SEC_ERROR_BAD_PASSWORD:
+                if (id_cryptoctx->defer_id_prompt) {
+                    pkinit_set_deferred_id(&id_cryptoctx->deferred_ids,
+                                           identity, 0, NULL);
+                    return SECSuccess;
+                }
                 pkiDebug("%s: prompting for password for %s\n",
                          __FUNCTION__, name);
-                newpass = crypto_pwfn(name, PR_FALSE, (attempt > 0),
+                newpass = crypto_pwfn(name, PR_FALSE, 0, (attempt > 0),
                                       id_cryptoctx);
                 attempt++;
                 if (newpass != NULL) {
@@ -2451,14 +2629,12 @@ crypto_load_pkcs12(krb5_context context,
             free(password.data);
         return SECFailure;
     }
-    id_cryptoctx->id_p12_slot.p12name =
-        reassemble_pkcs12_name(id_cryptoctx->pool, name);
     pkiDebug("%s: imported PKCS12 bundle \"%s\"\n", __FUNCTION__, name);
     SEC_PKCS12DecoderFinish(ctx);
     if (password.data != emptypwd)
         free(password.data);
     if (cert_load_certs_with_keys_from_slot(context, id_cryptoctx, slot,
-                                            NULL, NULL) == 0)
+                                            NULL, NULL, identity) == 0)
         return SECSuccess;
     else
         return SECFailure;
@@ -2551,6 +2727,8 @@ crypto_load_files(krb5_context context,
             return SECFailure;
         cobj->name = reassemble_files_name(id_cryptoctx->pool,
                                            certfile, keyfile);
+        if (cobj->name == NULL)
+            return SECFailure;
         cobj->obj = PK11_CreateGenericObject(slot, attrs, n_attrs, permanent);
         if (cobj->obj == NULL) {
             pkiDebug("%s: error loading %scertificate \"%s\"\n",
@@ -2627,7 +2805,8 @@ crypto_load_files(krb5_context context,
             return SECFailure;
         kobj->obj = PK11_CreateGenericObject(slot, attrs, n_attrs, permanent);
         if (kobj->obj == NULL) {
-            pkiDebug("%s: error loading key \"%s\"\n", __FUNCTION__, keyfile);
+            pkiDebug("%s: error loading key \"%s\": %s\n", __FUNCTION__,
+                     keyfile, PORT_ErrorToName(PORT_GetError()));
             status = SECFailure;
         } else {
             pkiDebug("%s: loaded key \"%s\"\n", __FUNCTION__, keyfile);
@@ -2660,13 +2839,13 @@ crypto_load_files(krb5_context context,
          * passwords at it, but it will cause the module to clear the
          * needs-login flag so that we can continue importing PEM items.
          */
-        if (!PK11_IsLoggedIn(slot, crypto_pwcb_prep(id_cryptoctx,
+        if (!PK11_IsLoggedIn(slot, crypto_pwcb_prep(id_cryptoctx, cobj->name,
                                                     context)) &&
             PK11_NeedLogin(slot)) {
             pkiDebug("%s: logging in to token \"%s\"\n",
                      __FUNCTION__, PK11_GetTokenName(slot));
             if (PK11_Authenticate(slot, PR_TRUE,
-                                  crypto_pwcb_prep(id_cryptoctx,
+                                  crypto_pwcb_prep(id_cryptoctx, cobj->name,
                                                    context)) != SECSuccess) {
                 pkiDebug("%s: error logging into \"%s\": %s, skipping\n",
                          __FUNCTION__, PK11_GetTokenName(slot),
@@ -2681,6 +2860,7 @@ crypto_load_files(krb5_context context,
         if (cobj != NULL && cobj->cert != NULL && kobj->obj != NULL) {
             key = PK11_FindPrivateKeyFromCert(slot, cobj->cert,
                                               crypto_pwcb_prep(id_cryptoctx,
+                                                               cobj->name,
                                                                context));
             if (key == NULL) {
                 pkiDebug("%s: no private key found for \"%s\"(%s), "
@@ -2873,6 +3053,8 @@ crypto_load_nssdb(krb5_context context,
     if (userdb == NULL)
         return SECFailure;
     userdb->name = reassemble_nssdb_name(id_cryptoctx->pool, configdir);
+    if (userdb->name == NULL)
+        return SECFailure;
     userdb->userdb = SECMOD_OpenUserDB(p);
     if (userdb->userdb == NULL) {
         pkiDebug("%s: error loading NSS cert database \"%s\"\n",
@@ -2887,11 +3069,13 @@ crypto_load_nssdb(krb5_context context,
     id_cryptoctx->id_userdbs = id_userdbs;
 
     /* Load the CAs from the database. */
-    cert_load_ca_certs_from_slot(context, id_cryptoctx, userdb->userdb);
+    cert_load_ca_certs_from_slot(context, id_cryptoctx, userdb->userdb,
+                                 userdb->name);
 
     /* Load the keys from the database. */
     return cert_load_certs_with_keys_from_slot(context, id_cryptoctx,
-                                               userdb->userdb, NULL, NULL);
+                                               userdb->userdb, NULL, NULL,
+                                               userdb->name);
 }
 
 /* Load up a certificate and associated key. */
@@ -2910,6 +3094,7 @@ crypto_load_certs(krb5_context context,
 
     switch (idopts->idtype) {
     case IDTYPE_FILE:
+        id_cryptoctx->defer_with_dummy_password = TRUE;
         status = crypto_load_files(context,
                                    plg_cryptoctx,
                                    req_cryptoctx,
@@ -2920,11 +3105,12 @@ crypto_load_certs(krb5_context context,
             pkiDebug("%s: error loading files \"%s\" and \"%s\"\n",
                      __FUNCTION__, idopts->cert_filename,
                      idopts->key_filename);
-            return ENOMEM;
+            return defer_id_prompts ? 0 : ENOMEM;
         }
         return 0;
         break;
     case IDTYPE_NSS:
+        id_cryptoctx->defer_with_dummy_password = FALSE;
         status = crypto_load_nssdb(context,
                                    plg_cryptoctx,
                                    req_cryptoctx,
@@ -2937,6 +3123,7 @@ crypto_load_certs(krb5_context context,
         return 0;
         break;
     case IDTYPE_DIR:
+        id_cryptoctx->defer_with_dummy_password = TRUE;
         status = crypto_load_dir(context,
                                  plg_cryptoctx,
                                  req_cryptoctx,
@@ -2945,11 +3132,12 @@ crypto_load_certs(krb5_context context,
         if (status != SECSuccess) {
             pkiDebug("%s: error loading directory \"%s\"\n",
                      __FUNCTION__, idopts->cert_filename);
-            return ENOMEM;
+            return defer_id_prompts ? 0 : ENOMEM;
         }
         return 0;
         break;
     case IDTYPE_PKCS11:
+        id_cryptoctx->defer_with_dummy_password = FALSE;
         status = crypto_load_pkcs11(context,
                                     plg_cryptoctx,
                                     req_cryptoctx, idopts, id_cryptoctx);
@@ -2961,6 +3149,7 @@ crypto_load_certs(krb5_context context,
         return 0;
         break;
     case IDTYPE_PKCS12:
+        id_cryptoctx->defer_with_dummy_password = FALSE;
         status = crypto_load_pkcs12(context,
                                     plg_cryptoctx,
                                     req_cryptoctx,
@@ -3636,7 +3825,7 @@ pkinit_create_td_trusted_certifiers(krb5_context context,
     /* Get the list of tokens.  All of them. */
     slist = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE,
                               PR_FALSE,
-                              crypto_pwcb_prep(id_cryptoctx, context));
+                              crypto_pwcb_prep(id_cryptoctx, NULL, context));
     if (slist == NULL) {
         CERT_DestroyCertList(clist);
         return ENOENT;
@@ -3646,9 +3835,9 @@ pkinit_create_td_trusted_certifiers(krb5_context context,
     i = 0;
     status = SECSuccess;
     for (sle = slist->head; sle != NULL; sle = sle->next) {
-        /* Skip over slots we would still need to log in to use. */
+        /* Skip over slots we would still need to log in to before using. */
         if (!PK11_IsLoggedIn(sle->slot,
-                             crypto_pwcb_prep(id_cryptoctx, context)) &&
+                             crypto_pwcb_prep(id_cryptoctx, NULL, context)) &&
             PK11_NeedLogin(sle->slot)) {
             pkiDebug("%s: skipping token \"%s\"\n",
                      __FUNCTION__, PK11_GetTokenName(sle->slot));
@@ -4975,7 +5164,7 @@ crypto_signeddata_common_verify(krb5_context context,
                                              NULL, NULL,
                                              crypto_pwcb,
                                              crypto_pwcb_prep(id_cryptoctx,
-                                                              context),
+                                                              NULL, context),
                                              NULL, NULL);
         if (ecmsg == NULL) {
             pkiDebug("%s: plain-data not parsable\n", __FUNCTION__);
@@ -5225,7 +5414,8 @@ cms_envelopeddata_verify(krb5_context context,
                                        NULL, NULL,
                                        crypto_pwcb,
                                        crypto_pwcb_prep(id_cryptoctx,
-                                                        context), NULL, NULL);
+                                                        NULL, context),
+                                       NULL, NULL);
     if (msg == NULL)
         return ENOMEM;
 
@@ -5400,7 +5590,8 @@ cms_signeddata_create(krb5_context context,
     if (NSS_CMSDEREncode(msg, &plain, &encoded, pool) != SECSuccess) {
         NSS_CMSMessage_Destroy(msg);
         PORT_FreeArena(pool, PR_TRUE);
-        pkiDebug("%s: error encoding signed-data\n", __FUNCTION__);
+        pkiDebug("%s: error encoding signed-data: %s\n", __FUNCTION__,
+                 PORT_ErrorToName(PORT_GetError()));
         return ENOMEM;
     }
     if (secitem_to_buf_len(&encoded, signed_data, signed_data_len) != 0) {
@@ -5504,7 +5695,8 @@ cms_signeddata_verify(krb5_context context,
                                        NULL, NULL,
                                        crypto_pwcb,
                                        crypto_pwcb_prep(id_cryptoctx,
-                                                        context), NULL, NULL);
+                                                        NULL, context),
+                                       NULL, NULL);
     if (msg == NULL)
         return ENOMEM;
 


More information about the cvs-krb5 mailing list