/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * This file manages PKCS #11 instances of certificates. */ #include #include "secport.h" #include "seccomon.h" #include "secmod.h" #include "secmodi.h" #include "secmodti.h" #include "pkcs11.h" #include "pk11func.h" #include "cert.h" #include "certi.h" #include "secitem.h" #include "keyhi.h" #include "secoid.h" #include "pkcs7t.h" #include "cmsreclist.h" #include "certdb.h" #include "secerr.h" #include "sslerr.h" #include "pki3hack.h" #include "dev3hack.h" #include "devm.h" #include "nsspki.h" #include "pki.h" #include "pkim.h" #include "pkitm.h" #include "pkistore.h" /* to remove temp cert */ #include "devt.h" #include "ckhelper.h" #include "pkcs11uri.h" extern const NSSError NSS_ERROR_NOT_FOUND; extern const NSSError NSS_ERROR_INVALID_CERTIFICATE; struct nss3_cert_cbstr { SECStatus (*callback)(CERTCertificate *, void *); nssList *cached; void *arg; }; /* Translate from NSSCertificate to CERTCertificate, then pass the latter * to a callback. */ static PRStatus convert_cert(NSSCertificate *c, void *arg) { CERTCertificate *nss3cert; SECStatus secrv; struct nss3_cert_cbstr *nss3cb = (struct nss3_cert_cbstr *)arg; /* 'c' is not adopted. caller will free it */ nss3cert = STAN_GetCERTCertificate(c); if (!nss3cert) return PR_FAILURE; secrv = (*nss3cb->callback)(nss3cert, nss3cb->arg); return (secrv) ? PR_FAILURE : PR_SUCCESS; } /* * build a cert nickname based on the token name and the label of the * certificate If the label in NULL, build a label based on the ID. */ static int toHex(int x) { return (x < 10) ? (x + '0') : (x + 'a' - 10); } #define MAX_CERT_ID 4 #define DEFAULT_STRING "Cert ID " static char * pk11_buildNickname(PK11SlotInfo *slot, CK_ATTRIBUTE *cert_label, CK_ATTRIBUTE *key_label, CK_ATTRIBUTE *cert_id) { int prefixLen = PORT_Strlen(slot->token_name); int suffixLen = 0; char *suffix = NULL; char buildNew[sizeof(DEFAULT_STRING) + MAX_CERT_ID * 2]; char *next, *nickname; if (cert_label && (cert_label->ulValueLen)) { suffixLen = cert_label->ulValueLen; suffix = (char *)cert_label->pValue; } else if (key_label && (key_label->ulValueLen)) { suffixLen = key_label->ulValueLen; suffix = (char *)key_label->pValue; } else if (cert_id && cert_id->ulValueLen > 0) { int i, first = cert_id->ulValueLen - MAX_CERT_ID; int offset = sizeof(DEFAULT_STRING); char *idValue = (char *)cert_id->pValue; PORT_Memcpy(buildNew, DEFAULT_STRING, sizeof(DEFAULT_STRING) - 1); next = buildNew + offset; if (first < 0) first = 0; for (i = first; i < (int)cert_id->ulValueLen; i++) { *next++ = toHex((idValue[i] >> 4) & 0xf); *next++ = toHex(idValue[i] & 0xf); } *next++ = 0; suffix = buildNew; suffixLen = PORT_Strlen(buildNew); } else { PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return NULL; } /* if is internal key slot, add code to skip the prefix!! */ next = nickname = (char *)PORT_Alloc(prefixLen + 1 + suffixLen + 1); if (nickname == NULL) return NULL; PORT_Memcpy(next, slot->token_name, prefixLen); next += prefixLen; *next++ = ':'; PORT_Memcpy(next, suffix, suffixLen); next += suffixLen; *next++ = 0; return nickname; } PRBool PK11_IsUserCert(PK11SlotInfo *slot, CERTCertificate *cert, CK_OBJECT_HANDLE certID) { CK_OBJECT_CLASS theClass; if (slot == NULL) return PR_FALSE; if (cert == NULL) return PR_FALSE; theClass = CKO_PRIVATE_KEY; if (pk11_LoginStillRequired(slot, NULL)) { theClass = CKO_PUBLIC_KEY; } if (PK11_MatchItem(slot, certID, theClass) != CK_INVALID_HANDLE) { return PR_TRUE; } if (theClass == CKO_PUBLIC_KEY) { SECKEYPublicKey *pubKey = CERT_ExtractPublicKey(cert); CK_ATTRIBUTE theTemplate; if (pubKey == NULL) { return PR_FALSE; } PK11_SETATTRS(&theTemplate, 0, NULL, 0); switch (pubKey->keyType) { case rsaKey: case rsaPssKey: case rsaOaepKey: PK11_SETATTRS(&theTemplate, CKA_MODULUS, pubKey->u.rsa.modulus.data, pubKey->u.rsa.modulus.len); break; case dsaKey: PK11_SETATTRS(&theTemplate, CKA_VALUE, pubKey->u.dsa.publicValue.data, pubKey->u.dsa.publicValue.len); break; case dhKey: PK11_SETATTRS(&theTemplate, CKA_VALUE, pubKey->u.dh.publicValue.data, pubKey->u.dh.publicValue.len); break; case ecKey: case edKey: PK11_SETATTRS(&theTemplate, CKA_EC_POINT, pubKey->u.ec.publicValue.data, pubKey->u.ec.publicValue.len); break; case keaKey: case fortezzaKey: case kyberKey: case nullKey: /* fall through and return false */ break; } if (theTemplate.ulValueLen == 0) { SECKEY_DestroyPublicKey(pubKey); return PR_FALSE; } if (pubKey->keyType != ecKey && pubKey->keyType != edKey) { pk11_SignedToUnsigned(&theTemplate); } if (pk11_FindObjectByTemplate(slot, &theTemplate, 1) != CK_INVALID_HANDLE) { SECKEY_DestroyPublicKey(pubKey); return PR_TRUE; } SECKEY_DestroyPublicKey(pubKey); } return PR_FALSE; } /* * Check out if a cert has ID of zero. This is a magic ID that tells * NSS that this cert may be an automagically trusted cert. * The Cert has to be self signed as well. That check is done elsewhere. * */ PRBool pk11_isID0(PK11SlotInfo *slot, CK_OBJECT_HANDLE certID) { CK_ATTRIBUTE keyID = { CKA_ID, NULL, 0 }; PRBool isZero = PR_FALSE; int i; CK_RV crv; crv = PK11_GetAttributes(NULL, slot, certID, &keyID, 1); if (crv != CKR_OK) { return isZero; } if (keyID.ulValueLen != 0) { char *value = (char *)keyID.pValue; isZero = PR_TRUE; /* ID exists, may be zero */ for (i = 0; i < (int)keyID.ulValueLen; i++) { if (value[i] != 0) { isZero = PR_FALSE; /* nope */ break; } } } PORT_Free(keyID.pValue); return isZero; } /* * Create an NSSCertificate from a slot/certID pair, return it as a * CERTCertificate. Optionally, output the nickname string. */ static CERTCertificate * pk11_fastCert(PK11SlotInfo *slot, CK_OBJECT_HANDLE certID, CK_ATTRIBUTE *privateLabel, char **nickptr) { NSSCertificate *c; nssCryptokiObject *co = NULL; nssPKIObject *pkio; NSSTrustDomain *td = STAN_GetDefaultTrustDomain(); /* Get the cryptoki object from the handle */ NSSToken *token = PK11Slot_GetNSSToken(slot); if (!token || !token->defaultSession) { (void)nssToken_Destroy(token); /* null token is ok */ PORT_SetError(SEC_ERROR_NO_TOKEN); return NULL; } co = nssCryptokiObject_Create(token, token->defaultSession, certID); (void)nssToken_Destroy(token); if (!co) { return NULL; } /* Create a PKI object from the cryptoki instance */ pkio = nssPKIObject_Create(NULL, co, td, NULL, nssPKIMonitor); if (!pkio) { nssCryptokiObject_Destroy(co); return NULL; } /* Create a certificate */ c = nssCertificate_Create(pkio); if (!c) { nssPKIObject_Destroy(pkio); return NULL; } /* Build and output a nickname, if desired. * This must be done before calling nssTrustDomain_AddCertsToCache * because that function may destroy c, pkio and co! */ if ((nickptr) && (co->label)) { CK_ATTRIBUTE label, id; label.type = CKA_LABEL; label.pValue = co->label; label.ulValueLen = PORT_Strlen(co->label); id.type = CKA_ID; id.pValue = c->id.data; id.ulValueLen = c->id.size; *nickptr = pk11_buildNickname(slot, &label, privateLabel, &id); } /* This function may destroy the cert in "c" and all its subordinate * structures, and replace the value in "c" with the address of a * different NSSCertificate that it found in the cache. * Presumably, the nickname which we just output above remains valid. :) */ (void)nssTrustDomain_AddCertsToCache(td, &c, 1); return STAN_GetCERTCertificateOrRelease(c); } /* * Build an CERTCertificate structure from a PKCS#11 object ID.... certID * Must be a CertObject. This code does not explicitly checks that. */ CERTCertificate * PK11_MakeCertFromHandle(PK11SlotInfo *slot, CK_OBJECT_HANDLE certID, CK_ATTRIBUTE *privateLabel) { char *nickname = NULL; CERTCertificate *cert = NULL; CERTCertTrust *trust; if (slot == NULL || certID == CK_INVALID_HANDLE) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return NULL; } cert = pk11_fastCert(slot, certID, privateLabel, &nickname); if (cert == NULL) { goto loser; } if (nickname) { if (cert->nickname != NULL) { cert->dbnickname = cert->nickname; } cert->nickname = PORT_ArenaStrdup(cert->arena, nickname); PORT_Free(nickname); nickname = NULL; } /* remember where this cert came from.... If we have just looked * it up from the database and it already has a slot, don't add a new * one. */ if (cert->slot == NULL) { cert->slot = PK11_ReferenceSlot(slot); cert->pkcs11ID = certID; cert->ownSlot = PR_TRUE; cert->series = slot->series; } trust = (CERTCertTrust *)PORT_ArenaAlloc(cert->arena, sizeof(CERTCertTrust)); if (trust == NULL) goto loser; PORT_Memset(trust, 0, sizeof(CERTCertTrust)); if (!pk11_HandleTrustObject(slot, cert, trust)) { unsigned int type; /* build some cert trust flags */ if (CERT_IsCACert(cert, &type)) { unsigned int trustflags = CERTDB_VALID_CA; /* Allow PKCS #11 modules to give us trusted CA's. We only accept * valid CA's which are self-signed here. They must have an object * ID of '0'. */ if (pk11_isID0(slot, certID) && cert->isRoot) { trustflags |= CERTDB_TRUSTED_CA; /* is the slot a fortezza card? allow the user or * admin to turn on objectSigning, but don't turn * full trust on explicitly */ if (PK11_DoesMechanism(slot, CKM_KEA_KEY_DERIVE)) { trust->objectSigningFlags |= CERTDB_VALID_CA; } } if ((type & NS_CERT_TYPE_SSL_CA) == NS_CERT_TYPE_SSL_CA) { trust->sslFlags |= trustflags; } if ((type & NS_CERT_TYPE_EMAIL_CA) == NS_CERT_TYPE_EMAIL_CA) { trust->emailFlags |= trustflags; } if ((type & NS_CERT_TYPE_OBJECT_SIGNING_CA) == NS_CERT_TYPE_OBJECT_SIGNING_CA) { trust->objectSigningFlags |= trustflags; } } } if (PK11_IsUserCert(slot, cert, certID)) { trust->sslFlags |= CERTDB_USER; trust->emailFlags |= CERTDB_USER; /* trust->objectSigningFlags |= CERTDB_USER; */ } CERT_LockCertTrust(cert); cert->trust = trust; CERT_UnlockCertTrust(cert); return cert; loser: if (nickname) PORT_Free(nickname); if (cert) CERT_DestroyCertificate(cert); return NULL; } /* * Build get a certificate from a private key */ CERTCertificate * PK11_GetCertFromPrivateKey(SECKEYPrivateKey *privKey) { PK11SlotInfo *slot = privKey->pkcs11Slot; CK_OBJECT_HANDLE handle = privKey->pkcs11ID; CK_OBJECT_HANDLE certID = PK11_MatchItem(slot, handle, CKO_CERTIFICATE); CERTCertificate *cert; if (certID == CK_INVALID_HANDLE) { PORT_SetError(SSL_ERROR_NO_CERTIFICATE); return NULL; } cert = PK11_MakeCertFromHandle(slot, certID, NULL); return (cert); } CK_OBJECT_HANDLE * PK11_FindCertHandlesForKeyHandle(PK11SlotInfo *slot, CK_OBJECT_HANDLE keyHandle, int *certHandleCountOut) { if (!slot || !certHandleCountOut || keyHandle == CK_INVALID_HANDLE) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return NULL; } PORTCheapArenaPool arena; PORT_InitCheapArena(&arena, DER_DEFAULT_CHUNKSIZE); CK_ATTRIBUTE idTemplate[] = { { CKA_ID, NULL, 0 }, }; const int idAttrCount = sizeof(idTemplate) / sizeof(idTemplate[0]); CK_RV crv = PK11_GetAttributes(&arena.arena, slot, keyHandle, idTemplate, idAttrCount); if (crv != CKR_OK) { PORT_DestroyCheapArena(&arena); PORT_SetError(PK11_MapError(crv)); return NULL; } if ((idTemplate[0].ulValueLen == 0) || (idTemplate[0].ulValueLen == -1)) { PORT_DestroyCheapArena(&arena); PORT_SetError(SEC_ERROR_BAD_KEY); return NULL; } CK_OBJECT_CLASS searchClass = CKO_CERTIFICATE; CK_ATTRIBUTE searchTemplate[] = { idTemplate[0], { CKA_CLASS, &searchClass, sizeof(searchClass) } }; const size_t searchAttrCount = sizeof(searchTemplate) / sizeof(searchTemplate[0]); CK_OBJECT_HANDLE *ids = pk11_FindObjectsByTemplate(slot, searchTemplate, searchAttrCount, certHandleCountOut); PORT_DestroyCheapArena(&arena); return ids; } CERTCertList * PK11_GetCertsMatchingPrivateKey(SECKEYPrivateKey *privKey) { if (!privKey) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return NULL; } CERTCertList *certs = CERT_NewCertList(); if (!certs) { PORT_SetError(SEC_ERROR_NO_MEMORY); return NULL; } PK11SlotInfo *slot = privKey->pkcs11Slot; CK_OBJECT_HANDLE handle = privKey->pkcs11ID; CK_OBJECT_HANDLE certID = PK11_MatchItem(slot, handle, CKO_CERTIFICATE); /* If we can't get a matching certID, there are no matching certificates, * which is not an error. */ if (certID == CK_INVALID_HANDLE) { return certs; } int certHandleCount = 0; CK_OBJECT_HANDLE *certHandles = PK11_FindCertHandlesForKeyHandle(slot, handle, &certHandleCount); if (!certHandles) { /* If certHandleCount is 0, there are no matching certificates, which is * not an error. */ if (certHandleCount == 0) { return certs; } CERT_DestroyCertList(certs); return NULL; } int i; for (i = 0; i < certHandleCount; i++) { CERTCertificate *cert = PK11_MakeCertFromHandle(slot, certHandles[i], NULL); /* If PK11_MakeCertFromHandle fails for one handle, optimistically assume other handles may succeed (i.e. this is best-effort). */ if (!cert) { continue; } if (CERT_AddCertToListTail(certs, cert) != SECSuccess) { CERT_DestroyCertificate(cert); } } PORT_Free(certHandles); return certs; } /* * delete a cert and it's private key (if no other certs are pointing to the * private key. */ SECStatus PK11_DeleteTokenCertAndKey(CERTCertificate *cert, void *wincx) { SECKEYPrivateKey *privKey = PK11_FindKeyByAnyCert(cert, wincx); CK_OBJECT_HANDLE pubKey; PK11SlotInfo *slot = NULL; pubKey = pk11_FindPubKeyByAnyCert(cert, &slot, wincx); if (privKey) { /* For 3.4, utilize the generic cert delete function */ SEC_DeletePermCertificate(cert); PK11_DeleteTokenPrivateKey(privKey, PR_FALSE); } if ((pubKey != CK_INVALID_HANDLE) && (slot != NULL)) { PK11_DestroyTokenObject(slot, pubKey); PK11_FreeSlot(slot); } return SECSuccess; } /* * cert callback structure */ typedef struct pk11DoCertCallbackStr { SECStatus (*callback)(PK11SlotInfo *slot, CERTCertificate *, void *); SECStatus (*noslotcallback)(CERTCertificate *, void *); SECStatus (*itemcallback)(CERTCertificate *, SECItem *, void *); void *callbackArg; } pk11DoCertCallback; typedef struct pk11CertCallbackStr { SECStatus (*callback)(CERTCertificate *, SECItem *, void *); void *callbackArg; } pk11CertCallback; struct fake_der_cb_argstr { SECStatus (*callback)(CERTCertificate *, SECItem *, void *); void *arg; }; static SECStatus fake_der_cb(CERTCertificate *c, void *a) { struct fake_der_cb_argstr *fda = (struct fake_der_cb_argstr *)a; return (*fda->callback)(c, &c->derCert, fda->arg); } /* * Extract all the certs on a card from a slot. */ SECStatus PK11_TraverseSlotCerts(SECStatus (*callback)(CERTCertificate *, SECItem *, void *), void *arg, void *wincx) { NSSTrustDomain *defaultTD = STAN_GetDefaultTrustDomain(); struct fake_der_cb_argstr fda; struct nss3_cert_cbstr pk11cb; /* authenticate to the tokens first */ (void)pk11_TraverseAllSlots(NULL, NULL, PR_TRUE, wincx); fda.callback = callback; fda.arg = arg; pk11cb.callback = fake_der_cb; pk11cb.arg = &fda; NSSTrustDomain_TraverseCertificates(defaultTD, convert_cert, &pk11cb); return SECSuccess; } static void transfer_token_certs_to_collection(nssList *certList, NSSToken *token, nssPKIObjectCollection *collection) { NSSCertificate **certs; PRUint32 i, count; NSSToken **tokens, **tp; count = nssList_Count(certList); if (count == 0) { return; } certs = nss_ZNEWARRAY(NULL, NSSCertificate *, count); if (!certs) { return; } nssList_GetArray(certList, (void **)certs, count); for (i = 0; i < count; i++) { tokens = nssPKIObject_GetTokens(&certs[i]->object, NULL); if (tokens) { for (tp = tokens; *tp; tp++) { if (*tp == token) { nssPKIObjectCollection_AddObject(collection, (nssPKIObject *)certs[i]); } } nssTokenArray_Destroy(tokens); } CERT_DestroyCertificate(STAN_GetCERTCertificateOrRelease(certs[i])); } nss_ZFreeIf(certs); } static void transfer_uri_certs_to_collection(nssList *certList, PK11URI *uri, nssPKIObjectCollection *collection) { NSSCertificate **certs; PRUint32 i, count; NSSToken **tokens, **tp; PK11SlotInfo *slot; const SECItem *id; count = nssList_Count(certList); if (count == 0) { return; } certs = nss_ZNEWARRAY(NULL, NSSCertificate *, count); if (!certs) { return; } id = PK11URI_GetPathAttributeItem(uri, PK11URI_PATTR_ID); nssList_GetArray(certList, (void **)certs, count); for (i = 0; i < count; i++) { /* * Filter the subject matched certs based on the * CKA_ID from the URI */ if (id && (id->len != certs[i]->id.size || memcmp(id, certs[i]->id.data, certs[i]->id.size))) continue; tokens = nssPKIObject_GetTokens(&certs[i]->object, NULL); if (tokens) { for (tp = tokens; *tp; tp++) { const char *value; slot = (*tp)->pk11slot; value = PK11URI_GetPathAttribute(uri, PK11URI_PATTR_TOKEN); if (value && !pk11_MatchString(value, (char *)slot->tokenInfo.label, sizeof(slot->tokenInfo.label))) { continue; } value = PK11URI_GetPathAttribute(uri, PK11URI_PATTR_MANUFACTURER); if (value && !pk11_MatchString(value, (char *)slot->tokenInfo.manufacturerID, sizeof(slot->tokenInfo.manufacturerID))) { continue; } value = PK11URI_GetPathAttribute(uri, PK11URI_PATTR_MODEL); if (value && !pk11_MatchString(value, (char *)slot->tokenInfo.model, sizeof(slot->tokenInfo.model))) { continue; } value = PK11URI_GetPathAttribute(uri, PK11URI_PATTR_SERIAL); if (value && !pk11_MatchString(value, (char *)slot->tokenInfo.serialNumber, sizeof(slot->tokenInfo.serialNumber))) { continue; } nssPKIObjectCollection_AddObject(collection, (nssPKIObject *)certs[i]); break; } nssTokenArray_Destroy(tokens); } CERT_DestroyCertificate(STAN_GetCERTCertificateOrRelease(certs[i])); } nss_ZFreeIf(certs); } static NSSCertificate ** find_certs_from_uri(const char *uriString, void *wincx) { PK11URI *uri = NULL; CK_ATTRIBUTE attributes[10]; CK_ULONG nattributes = 0; const SECItem *id; const char *label, *type; PK11SlotInfo *slotinfo; nssCryptokiObject **instances; PRStatus status; nssPKIObjectCollection *collection = NULL; NSSTrustDomain *defaultTD = STAN_GetDefaultTrustDomain(); NSSCertificate **certs = NULL; nssList *certList = NULL; SECStatus rv; CK_OBJECT_CLASS s_class = CKO_CERTIFICATE; static const CK_BBOOL s_true = CK_TRUE; NSSToken **tokens, **tok; uri = PK11URI_ParseURI(uriString); if (uri == NULL) { goto loser; } collection = nssCertificateCollection_Create(defaultTD, NULL); if (!collection) { goto loser; } certList = nssList_Create(NULL, PR_FALSE); if (!certList) { goto loser; } /* if the "type" attribute is specified its value must be "cert" */ type = PK11URI_GetPathAttribute(uri, PK11URI_PATTR_TYPE); if (type && strcmp(type, "cert")) { goto loser; } label = PK11URI_GetPathAttribute(uri, PK11URI_PATTR_OBJECT); if (label) { (void)nssTrustDomain_GetCertsForNicknameFromCache(defaultTD, label, certList); } else { (void)nssTrustDomain_GetCertsFromCache(defaultTD, certList); } transfer_uri_certs_to_collection(certList, uri, collection); /* add the CKA_CLASS and CKA_TOKEN attributes manually */ attributes[nattributes].type = CKA_CLASS; attributes[nattributes].pValue = (void *)&s_class; attributes[nattributes].ulValueLen = sizeof(s_class); nattributes++; attributes[nattributes].type = CKA_TOKEN; attributes[nattributes].pValue = (void *)&s_true; attributes[nattributes].ulValueLen = sizeof(s_true); nattributes++; if (label) { attributes[nattributes].type = CKA_LABEL; attributes[nattributes].pValue = (void *)label; attributes[nattributes].ulValueLen = strlen(label); nattributes++; } id = PK11URI_GetPathAttributeItem(uri, PK11URI_PATTR_ID); if (id) { attributes[nattributes].type = CKA_ID; attributes[nattributes].pValue = (void *)id->data; attributes[nattributes].ulValueLen = id->len; nattributes++; } tokens = NSSTrustDomain_FindTokensByURI(defaultTD, uri); for (tok = tokens; tok && *tok; tok++) { if (nssToken_IsPresent(*tok)) { slotinfo = (*tok)->pk11slot; rv = pk11_AuthenticateUnfriendly(slotinfo, PR_TRUE, wincx); if (rv != SECSuccess) { continue; } instances = nssToken_FindObjectsByTemplate(*tok, NULL, attributes, nattributes, 0, &status); nssPKIObjectCollection_AddInstances(collection, instances, 0); nss_ZFreeIf(instances); } (void)nssToken_Destroy(*tok); } nss_ZFreeIf(tokens); nssList_Destroy(certList); certs = nssPKIObjectCollection_GetCertificates(collection, NULL, 0, NULL); loser: if (collection) { nssPKIObjectCollection_Destroy(collection); } if (uri) { PK11URI_DestroyURI(uri); } return certs; } CERTCertificate * PK11_FindCertFromURI(const char *uri, void *wincx) { static const NSSUsage usage = { PR_TRUE /* ... */ }; NSSCertificate *cert = NULL; NSSCertificate **certs = NULL; CERTCertificate *rvCert = NULL; certs = find_certs_from_uri(uri, wincx); if (certs) { cert = nssCertificateArray_FindBestCertificate(certs, NULL, &usage, NULL); if (cert) { rvCert = STAN_GetCERTCertificateOrRelease(cert); } nssCertificateArray_Destroy(certs); } return rvCert; } CERTCertList * PK11_FindCertsFromURI(const char *uri, void *wincx) { int i; CERTCertList *certList = NULL; NSSCertificate **foundCerts; NSSCertificate *c; foundCerts = find_certs_from_uri(uri, wincx); if (foundCerts) { PRTime now = PR_Now(); certList = CERT_NewCertList(); for (i = 0, c = *foundCerts; c; c = foundCerts[++i]) { if (certList) { CERTCertificate *certCert = STAN_GetCERTCertificateOrRelease(c); /* c may be invalid after this, don't reference it */ if (certCert) { /* CERT_AddCertToListSorted adopts certCert */ CERT_AddCertToListSorted(certList, certCert, CERT_SortCBValidity, &now); } } else { nssCertificate_Destroy(c); } } if (certList && CERT_LIST_HEAD(certList) == NULL) { CERT_DestroyCertList(certList); certList = NULL; } /* all the certs have been adopted or freed, free the raw array */ nss_ZFreeIf(foundCerts); } return certList; } static NSSCertificate ** find_certs_from_nickname(const char *nickname, void *wincx) { PRStatus status; NSSCertificate **certs = NULL; NSSToken *token = NULL; NSSTrustDomain *defaultTD = STAN_GetDefaultTrustDomain(); PK11SlotInfo *slot = NULL; SECStatus rv; char *nickCopy; char *delimit = NULL; char *tokenName; if (!PORT_Strncasecmp(nickname, "pkcs11:", strlen("pkcs11:"))) { certs = find_certs_from_uri(nickname, wincx); if (certs) return certs; } nickCopy = PORT_Strdup(nickname); if (!nickCopy) { /* error code is set */ return NULL; } if ((delimit = PORT_Strchr(nickCopy, ':')) != NULL) { tokenName = nickCopy; nickname = delimit + 1; *delimit = '\0'; /* find token by name */ token = NSSTrustDomain_FindTokenByName(defaultTD, (NSSUTF8 *)tokenName); if (token) { slot = PK11_ReferenceSlot(token->pk11slot); } else { PORT_SetError(SEC_ERROR_NO_TOKEN); } *delimit = ':'; } else { slot = PK11_GetInternalKeySlot(); token = PK11Slot_GetNSSToken(slot); if (!token) { PORT_SetError(SEC_ERROR_NO_TOKEN); } } if (token) { nssList *certList; nssCryptokiObject **instances; nssPKIObjectCollection *collection; nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly; if (!PK11_IsPresent(slot)) { goto loser; } rv = pk11_AuthenticateUnfriendly(slot, PR_TRUE, wincx); if (rv != SECSuccess) { goto loser; } collection = nssCertificateCollection_Create(defaultTD, NULL); if (!collection) { goto loser; } certList = nssList_Create(NULL, PR_FALSE); if (!certList) { nssPKIObjectCollection_Destroy(collection); goto loser; } (void)nssTrustDomain_GetCertsForNicknameFromCache(defaultTD, nickname, certList); transfer_token_certs_to_collection(certList, token, collection); instances = nssToken_FindCertificatesByNickname(token, NULL, nickname, tokenOnly, 0, &status); nssPKIObjectCollection_AddInstances(collection, instances, 0); nss_ZFreeIf(instances); /* if it wasn't found, repeat the process for email address */ if (nssPKIObjectCollection_Count(collection) == 0 && PORT_Strchr(nickname, '@') != NULL) { char *lowercaseName = CERT_FixupEmailAddr(nickname); if (lowercaseName) { (void)nssTrustDomain_GetCertsForEmailAddressFromCache(defaultTD, lowercaseName, certList); transfer_token_certs_to_collection(certList, token, collection); instances = nssToken_FindCertificatesByEmail(token, NULL, lowercaseName, tokenOnly, 0, &status); nssPKIObjectCollection_AddInstances(collection, instances, 0); nss_ZFreeIf(instances); PORT_Free(lowercaseName); } } certs = nssPKIObjectCollection_GetCertificates(collection, NULL, 0, NULL); nssPKIObjectCollection_Destroy(collection); nssList_Destroy(certList); } loser: if (token) { (void)nssToken_Destroy(token); } if (slot) { PK11_FreeSlot(slot); } if (nickCopy) PORT_Free(nickCopy); return certs; } CERTCertificate * PK11_FindCertFromNickname(const char *nickname, void *wincx) { CERTCertificate *rvCert = NULL; NSSCertificate *cert = NULL; NSSCertificate **certs = NULL; static const NSSUsage usage = { PR_TRUE /* ... */ }; certs = find_certs_from_nickname(nickname, wincx); if (certs) { cert = nssCertificateArray_FindBestCertificate(certs, NULL, &usage, NULL); if (cert) { rvCert = STAN_GetCERTCertificateOrRelease(cert); } nssCertificateArray_Destroy(certs); } return rvCert; } /* Traverse slots callback */ typedef struct FindCertsEmailArgStr { char *email; CERTCertList *certList; } FindCertsEmailArg; SECStatus FindCertsEmailCallback(CERTCertificate *cert, SECItem *item, void *arg) { FindCertsEmailArg *cbparam = (FindCertsEmailArg *)arg; const char *cert_email = CERT_GetFirstEmailAddress(cert); PRBool found = PR_FALSE; /* Email address present in certificate? */ if (cert_email == NULL) { return SECSuccess; } /* Parameter correctly set? */ if (cbparam->email == NULL) { return SECFailure; } /* Loop over all email addresses */ do { if (!strcmp(cert_email, cbparam->email)) { /* found one matching email address */ PRTime now = PR_Now(); found = PR_TRUE; CERT_AddCertToListSorted(cbparam->certList, CERT_DupCertificate(cert), CERT_SortCBValidity, &now); } cert_email = CERT_GetNextEmailAddress(cert, cert_email); } while (cert_email && !found); return SECSuccess; } /* Find all certificates with matching email address */ CERTCertList * PK11_FindCertsFromEmailAddress(const char *email, void *wincx) { FindCertsEmailArg cbparam; SECStatus rv; cbparam.certList = CERT_NewCertList(); if (cbparam.certList == NULL) { return NULL; } cbparam.email = CERT_FixupEmailAddr(email); if (cbparam.email == NULL) { CERT_DestroyCertList(cbparam.certList); return NULL; } rv = PK11_TraverseSlotCerts(FindCertsEmailCallback, &cbparam, NULL); if (rv != SECSuccess) { CERT_DestroyCertList(cbparam.certList); PORT_Free(cbparam.email); return NULL; } /* empty list? */ if (CERT_LIST_EMPTY(cbparam.certList)) { CERT_DestroyCertList(cbparam.certList); cbparam.certList = NULL; } PORT_Free(cbparam.email); return cbparam.certList; } CERTCertList * PK11_FindCertsFromNickname(const char *nickname, void *wincx) { int i; CERTCertList *certList = NULL; NSSCertificate **foundCerts = NULL; NSSCertificate *c; foundCerts = find_certs_from_nickname(nickname, wincx); if (foundCerts) { PRTime now = PR_Now(); certList = CERT_NewCertList(); for (i = 0, c = *foundCerts; c; c = foundCerts[++i]) { if (certList) { CERTCertificate *certCert = STAN_GetCERTCertificateOrRelease(c); /* c may be invalid after this, don't reference it */ if (certCert) { /* CERT_AddCertToListSorted adopts certCert */ CERT_AddCertToListSorted(certList, certCert, CERT_SortCBValidity, &now); } } else { nssCertificate_Destroy(c); } } /* all the certs have been adopted or freed, free the raw array */ nss_ZFreeIf(foundCerts); } return certList; } /* * extract a key ID for a certificate... * NOTE: We call this function from PKCS11.c If we ever use * pkcs11 to extract the public key (we currently do not), this will break. */ SECItem * PK11_GetPubIndexKeyID(CERTCertificate *cert) { SECKEYPublicKey *pubk; SECItem *newItem = NULL; pubk = CERT_ExtractPublicKey(cert); if (pubk == NULL) return NULL; switch (pubk->keyType) { case rsaKey: newItem = SECITEM_DupItem(&pubk->u.rsa.modulus); break; case dsaKey: newItem = SECITEM_DupItem(&pubk->u.dsa.publicValue); break; case dhKey: newItem = SECITEM_DupItem(&pubk->u.dh.publicValue); break; case ecKey: case edKey: newItem = SECITEM_DupItem(&pubk->u.ec.publicValue); break; case fortezzaKey: default: newItem = NULL; /* Fortezza Fix later... */ } SECKEY_DestroyPublicKey(pubk); /* make hash of it */ return newItem; } /* * generate a CKA_ID from a certificate. */ SECItem * pk11_mkcertKeyID(CERTCertificate *cert) { SECItem *pubKeyData = PK11_GetPubIndexKeyID(cert); SECItem *certCKA_ID; if (pubKeyData == NULL) return NULL; certCKA_ID = PK11_MakeIDFromPubKey(pubKeyData); SECITEM_FreeItem(pubKeyData, PR_TRUE); return certCKA_ID; } /* * Write the cert into the token. */ SECStatus PK11_ImportCert(PK11SlotInfo *slot, CERTCertificate *cert, CK_OBJECT_HANDLE key, const char *nickname, PRBool includeTrust) { PRStatus status; NSSCertificate *c; nssCryptokiObject *keyobj, *certobj; NSSToken *token = NULL; char *emailAddr = NULL; nssCertificateStoreTrace lockTrace = { NULL, NULL, PR_FALSE, PR_FALSE }; nssCertificateStoreTrace unlockTrace = { NULL, NULL, PR_FALSE, PR_FALSE }; SECItem *keyID = pk11_mkcertKeyID(cert); if (keyID == NULL) { goto loser; /* error code should be set already */ } token = PK11Slot_GetNSSToken(slot); if (!token) { PORT_SetError(SEC_ERROR_NO_TOKEN); goto loser; } if (PK11_IsInternal(slot) && cert->emailAddr && cert->emailAddr[0]) { emailAddr = cert->emailAddr; } /* need to get the cert as a stan cert */ CERT_LockCertTempPerm(cert); NSSCertificate *nssCert = cert->nssCertificate; CERT_UnlockCertTempPerm(cert); if (nssCert) { c = nssCert; } else { c = STAN_GetNSSCertificate(cert); if (c == NULL) { goto loser; } } /* set the id for the cert */ nssItem_Create(c->object.arena, &c->id, keyID->len, keyID->data); if (!c->id.data) { goto loser; } if (key != CK_INVALID_HANDLE) { /* create an object for the key, ... */ keyobj = nss_ZNEW(NULL, nssCryptokiObject); if (!keyobj) { goto loser; } keyobj->token = nssToken_AddRef(token); keyobj->handle = key; keyobj->isTokenObject = PR_TRUE; /* ... in order to set matching attributes for the key */ status = nssCryptokiPrivateKey_SetCertificate(keyobj, NULL, nickname, &c->id, &c->subject); nssCryptokiObject_Destroy(keyobj); if (status != PR_SUCCESS) { goto loser; } } /* do the token import */ certobj = nssToken_ImportCertificate(token, NULL, NSSCertificateType_PKIX, &c->id, nickname, &c->encoding, &c->issuer, &c->subject, &c->serial, emailAddr, PR_TRUE); if (!certobj) { if (NSS_GetError() == NSS_ERROR_INVALID_CERTIFICATE) { PORT_SetError(SEC_ERROR_REUSED_ISSUER_AND_SERIAL); SECITEM_FreeItem(keyID, PR_TRUE); return SECFailure; } goto loser; } if (c->object.cryptoContext) { /* Delete the temp instance */ NSSCryptoContext *cc = c->object.cryptoContext; nssCertificateStore_Lock(cc->certStore, &lockTrace); nssCertificateStore_RemoveCertLOCKED(cc->certStore, c); nssCertificateStore_Unlock(cc->certStore, &lockTrace, &unlockTrace); c->object.cryptoContext = NULL; CERT_LockCertTempPerm(cert); cert->istemp = PR_FALSE; cert->isperm = PR_TRUE; CERT_UnlockCertTempPerm(cert); } /* add the new instance to the cert, force an update of the * CERTCertificate, and finish */ nssPKIObject_AddInstance(&c->object, certobj); /* nssTrustDomain_AddCertsToCache may release a reference to 'c' and * replace 'c' with a different value. So we add a reference to 'c' to * prevent 'c' from being destroyed. */ nssCertificate_AddRef(c); nssTrustDomain_AddCertsToCache(STAN_GetDefaultTrustDomain(), &c, 1); (void)STAN_ForceCERTCertificateUpdate(c); nssCertificate_Destroy(c); SECITEM_FreeItem(keyID, PR_TRUE); (void)nssToken_Destroy(token); return SECSuccess; loser: if (token) { (void)nssToken_Destroy(token); } CERT_MapStanError(); SECITEM_FreeItem(keyID, PR_TRUE); if (PORT_GetError() != SEC_ERROR_TOKEN_NOT_LOGGED_IN) { PORT_SetError(SEC_ERROR_ADDING_CERT); } return SECFailure; } SECStatus PK11_ImportDERCert(PK11SlotInfo *slot, SECItem *derCert, CK_OBJECT_HANDLE key, char *nickname, PRBool includeTrust) { CERTCertificate *cert; SECStatus rv; cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), derCert, NULL, PR_FALSE, PR_TRUE); if (cert == NULL) return SECFailure; rv = PK11_ImportCert(slot, cert, key, nickname, includeTrust); CERT_DestroyCertificate(cert); return rv; } /* * return the private key From a given Cert */ SECKEYPrivateKey * PK11_FindPrivateKeyFromCert(PK11SlotInfo *slot, CERTCertificate *cert, void *wincx) { int err; CK_OBJECT_HANDLE certh; CK_OBJECT_HANDLE keyh; PRBool needLogin; SECStatus rv; certh = PK11_FindCertInSlot(slot, cert, wincx); if (certh == CK_INVALID_HANDLE) { return NULL; } /* * prevent a login race condition. If slot is logged in between * our call to pk11_LoginStillRequired and the * PK11_MatchItem. The matchItem call will either succeed, or * we will call it one more time after calling PK11_Authenticate * (which is a noop on an authenticated token). */ needLogin = pk11_LoginStillRequired(slot, wincx); keyh = PK11_MatchItem(slot, certh, CKO_PRIVATE_KEY); if ((keyh == CK_INVALID_HANDLE) && needLogin && (SSL_ERROR_NO_CERTIFICATE == (err = PORT_GetError()) || SEC_ERROR_TOKEN_NOT_LOGGED_IN == err)) { /* try it again authenticated */ rv = PK11_Authenticate(slot, PR_TRUE, wincx); if (rv != SECSuccess) { return NULL; } keyh = PK11_MatchItem(slot, certh, CKO_PRIVATE_KEY); } if (keyh == CK_INVALID_HANDLE) { return NULL; } return PK11_MakePrivKey(slot, nullKey, PR_TRUE, keyh, wincx); } /* * import a cert for a private key we have already generated. Set the label * on both to be the nickname. This is for the Key Gen, orphaned key case. */ PK11SlotInfo * PK11_KeyForCertExists(CERTCertificate *cert, CK_OBJECT_HANDLE *keyPtr, void *wincx) { PK11SlotList *list; PK11SlotListElement *le; SECItem *keyID; CK_OBJECT_HANDLE key; PK11SlotInfo *slot = NULL; SECStatus rv; int err; keyID = pk11_mkcertKeyID(cert); /* get them all! */ list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_TRUE, wincx); if ((keyID == NULL) || (list == NULL)) { if (keyID) SECITEM_FreeItem(keyID, PR_TRUE); if (list) PK11_FreeSlotList(list); return NULL; } /* Look for the slot that holds the Key */ for (le = list->head; le; le = le->next) { /* * prevent a login race condition. If le->slot is logged in between * our call to pk11_LoginStillRequired and the * pk11_FindPrivateKeyFromCertID, the find will either succeed, or * we will call it one more time after calling PK11_Authenticate * (which is a noop on an authenticated token). */ PRBool needLogin = pk11_LoginStillRequired(le->slot, wincx); key = pk11_FindPrivateKeyFromCertID(le->slot, keyID); if ((key == CK_INVALID_HANDLE) && needLogin && (SSL_ERROR_NO_CERTIFICATE == (err = PORT_GetError()) || SEC_ERROR_TOKEN_NOT_LOGGED_IN == err)) { /* authenticate and try again */ rv = PK11_Authenticate(le->slot, PR_TRUE, wincx); if (rv != SECSuccess) continue; key = pk11_FindPrivateKeyFromCertID(le->slot, keyID); } if (key != CK_INVALID_HANDLE) { slot = PK11_ReferenceSlot(le->slot); if (keyPtr) *keyPtr = key; break; } } SECITEM_FreeItem(keyID, PR_TRUE); PK11_FreeSlotList(list); return slot; } /* * import a cert for a private key we have already generated. Set the label * on both to be the nickname. This is for the Key Gen, orphaned key case. */ PK11SlotInfo * PK11_KeyForDERCertExists(SECItem *derCert, CK_OBJECT_HANDLE *keyPtr, void *wincx) { CERTCertificate *cert; PK11SlotInfo *slot = NULL; /* letting this use go -- the only thing that the cert is used for is * to get the ID attribute. */ cert = CERT_DecodeDERCertificate(derCert, PR_FALSE, NULL); if (cert == NULL) return NULL; slot = PK11_KeyForCertExists(cert, keyPtr, wincx); CERT_DestroyCertificate(cert); return slot; } PK11SlotInfo * PK11_ImportCertForKey(CERTCertificate *cert, const char *nickname, void *wincx) { PK11SlotInfo *slot = NULL; CK_OBJECT_HANDLE key; slot = PK11_KeyForCertExists(cert, &key, wincx); if (slot) { if (PK11_ImportCert(slot, cert, key, nickname, PR_FALSE) != SECSuccess) { PK11_FreeSlot(slot); slot = NULL; } } else { PORT_SetError(SEC_ERROR_ADDING_CERT); } return slot; } PK11SlotInfo * PK11_ImportDERCertForKey(SECItem *derCert, char *nickname, void *wincx) { CERTCertificate *cert; PK11SlotInfo *slot = NULL; cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), derCert, NULL, PR_FALSE, PR_TRUE); if (cert == NULL) return NULL; slot = PK11_ImportCertForKey(cert, nickname, wincx); CERT_DestroyCertificate(cert); return slot; } static CK_OBJECT_HANDLE pk11_FindCertObjectByTemplate(PK11SlotInfo **slotPtr, CK_ATTRIBUTE *searchTemplate, size_t count, void *wincx) { PK11SlotList *list; PK11SlotListElement *le; CK_OBJECT_HANDLE certHandle = CK_INVALID_HANDLE; PK11SlotInfo *slot = NULL; SECStatus rv; *slotPtr = NULL; /* get them all! */ list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_TRUE, wincx); if (list == NULL) { return CK_INVALID_HANDLE; } /* Look for the slot that holds the Key */ for (le = list->head; le; le = le->next) { rv = pk11_AuthenticateUnfriendly(le->slot, PR_TRUE, wincx); if (rv != SECSuccess) continue; certHandle = pk11_FindObjectByTemplate(le->slot, searchTemplate, count); if (certHandle != CK_INVALID_HANDLE) { slot = PK11_ReferenceSlot(le->slot); break; } } PK11_FreeSlotList(list); if (slot == NULL) { return CK_INVALID_HANDLE; } *slotPtr = slot; return certHandle; } CERTCertificate * PK11_FindCertByIssuerAndSNOnToken(PK11SlotInfo *slot, CERTIssuerAndSN *issuerSN, void *wincx) { CERTCertificate *rvCert = NULL; NSSCertificate *cert = NULL; NSSDER issuer, serial; NSSTrustDomain *td = STAN_GetDefaultTrustDomain(); NSSToken *token = NULL; nssSession *session; nssCryptokiObject *instance = NULL; nssPKIObject *object = NULL; SECItem *derSerial; PRStatus status; if (!issuerSN || !issuerSN->derIssuer.data || !issuerSN->derIssuer.len || !issuerSN->serialNumber.data || !issuerSN->serialNumber.len || issuerSN->derIssuer.len > CERT_MAX_DN_BYTES || issuerSN->serialNumber.len > CERT_MAX_SERIAL_NUMBER_BYTES) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return NULL; } token = PK11Slot_GetNSSToken(slot); if (!token) { PORT_SetError(SEC_ERROR_NO_TOKEN); return NULL; } session = nssToken_GetDefaultSession(token); /* non-owning */ if (!session) { (void)nssToken_Destroy(token); return NULL; } /* PKCS#11 needs to use DER-encoded serial numbers. Create a * CERTIssuerAndSN that actually has the encoded value and pass that * to PKCS#11 (and the crypto context). */ derSerial = SEC_ASN1EncodeItem(NULL, NULL, &issuerSN->serialNumber, SEC_ASN1_GET(SEC_IntegerTemplate)); if (!derSerial) { (void)nssToken_Destroy(token); return NULL; } NSSITEM_FROM_SECITEM(&issuer, &issuerSN->derIssuer); NSSITEM_FROM_SECITEM(&serial, derSerial); instance = nssToken_FindCertificateByIssuerAndSerialNumber(token, session, &issuer, &serial, nssTokenSearchType_TokenForced, &status); (void)nssToken_Destroy(token); SECITEM_FreeItem(derSerial, PR_TRUE); if (!instance) { goto loser; } object = nssPKIObject_Create(NULL, instance, td, NULL, nssPKIMonitor); if (!object) { goto loser; } instance = NULL; /* adopted by the previous call */ cert = nssCertificate_Create(object); if (!cert) { goto loser; } object = NULL; /* adopted by the previous call */ nssTrustDomain_AddCertsToCache(td, &cert, 1); /* on failure, cert is freed below */ rvCert = STAN_GetCERTCertificate(cert); if (!rvCert) { goto loser; } return rvCert; loser: if (instance) { nssCryptokiObject_Destroy(instance); } if (object) { nssPKIObject_Destroy(object); } if (cert) { nssCertificate_Destroy(cert); } return NULL; } static PRCallOnceType keyIDHashCallOnce; static PRStatus PR_CALLBACK pk11_keyIDHash_populate(void *wincx) { CERTCertList *certList; CERTCertListNode *node = NULL; SECItem subjKeyID = { siBuffer, NULL, 0 }; SECItem *slotid = NULL; SECMODModuleList *modules, *mlp; SECMODListLock *moduleLock; int i; certList = PK11_ListCerts(PK11CertListUser, wincx); if (!certList) { return PR_FAILURE; } for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList); node = CERT_LIST_NEXT(node)) { if (CERT_FindSubjectKeyIDExtension(node->cert, &subjKeyID) == SECSuccess && subjKeyID.data != NULL) { cert_AddSubjectKeyIDMapping(&subjKeyID, node->cert); SECITEM_FreeItem(&subjKeyID, PR_FALSE); } } CERT_DestroyCertList(certList); /* * Record the state of each slot in a hash. The concatenation of slotID * and moduleID is used as its key, with the slot series as its value. */ slotid = SECITEM_AllocItem(NULL, NULL, sizeof(CK_SLOT_ID) + sizeof(SECMODModuleID)); if (!slotid) { PORT_SetError(SEC_ERROR_NO_MEMORY); return PR_FAILURE; } moduleLock = SECMOD_GetDefaultModuleListLock(); if (!moduleLock) { SECITEM_FreeItem(slotid, PR_TRUE); PORT_SetError(SEC_ERROR_NOT_INITIALIZED); return PR_FAILURE; } SECMOD_GetReadLock(moduleLock); modules = SECMOD_GetDefaultModuleList(); for (mlp = modules; mlp; mlp = mlp->next) { for (i = 0; i < mlp->module->slotCount; i++) { memcpy(slotid->data, &mlp->module->slots[i]->slotID, sizeof(CK_SLOT_ID)); memcpy(&slotid->data[sizeof(CK_SLOT_ID)], &mlp->module->moduleID, sizeof(SECMODModuleID)); cert_UpdateSubjectKeyIDSlotCheck(slotid, mlp->module->slots[i]->series); } } SECMOD_ReleaseReadLock(moduleLock); SECITEM_FreeItem(slotid, PR_TRUE); return PR_SUCCESS; } /* * We're looking for a cert which we have the private key for that's on the * list of recipients. This searches one slot. * this is the new version for NSS SMIME code * this stuff should REALLY be in the SMIME code, but some things in here are not public * (they should be!) */ static CERTCertificate * pk11_FindCertObjectByRecipientNew(PK11SlotInfo *slot, NSSCMSRecipient **recipientlist, int *rlIndex, void *pwarg) { NSSCMSRecipient *ri = NULL; int i; PRBool tokenRescanDone = PR_FALSE; CERTCertTrust trust; for (i = 0; (ri = recipientlist[i]) != NULL; i++) { CERTCertificate *cert = NULL; if (ri->kind == RLSubjKeyID) { SECItem *derCert = cert_FindDERCertBySubjectKeyID(ri->id.subjectKeyID); if (!derCert && !tokenRescanDone) { /* * We didn't find the cert by its key ID. If we have slots * with removable tokens, a failure from * cert_FindDERCertBySubjectKeyID doesn't necessarily imply * that the cert is unavailable - the token might simply * have been inserted after the initial run of * pk11_keyIDHash_populate (wrapped by PR_CallOnceWithArg), * or a different token might have been present in that * slot, initially. Let's check for new tokens... */ PK11SlotList *sl = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_FALSE, pwarg); if (sl) { PK11SlotListElement *le; SECItem *slotid = SECITEM_AllocItem(NULL, NULL, sizeof(CK_SLOT_ID) + sizeof(SECMODModuleID)); if (!slotid) { PORT_SetError(SEC_ERROR_NO_MEMORY); PK11_FreeSlotList(sl); return NULL; } for (le = sl->head; le; le = le->next) { memcpy(slotid->data, &le->slot->slotID, sizeof(CK_SLOT_ID)); memcpy(&slotid->data[sizeof(CK_SLOT_ID)], &le->slot->module->moduleID, sizeof(SECMODModuleID)); /* * Any changes with the slot since our last check? * If so, re-read the certs in that specific slot. */ if (cert_SubjectKeyIDSlotCheckSeries(slotid) != PK11_GetSlotSeries(le->slot)) { CERTCertListNode *node = NULL; SECItem subjKeyID = { siBuffer, NULL, 0 }; CERTCertList *cl = PK11_ListCertsInSlot(le->slot); if (!cl) { continue; } for (node = CERT_LIST_HEAD(cl); !CERT_LIST_END(node, cl); node = CERT_LIST_NEXT(node)) { if (CERT_IsUserCert(node->cert) && CERT_FindSubjectKeyIDExtension(node->cert, &subjKeyID) == SECSuccess) { if (subjKeyID.data) { cert_AddSubjectKeyIDMapping(&subjKeyID, node->cert); cert_UpdateSubjectKeyIDSlotCheck(slotid, PK11_GetSlotSeries(le->slot)); } SECITEM_FreeItem(&subjKeyID, PR_FALSE); } } CERT_DestroyCertList(cl); } } PK11_FreeSlotList(sl); SECITEM_FreeItem(slotid, PR_TRUE); } /* only check once per message/recipientlist */ tokenRescanDone = PR_TRUE; /* do another lookup (hopefully we found that cert...) */ derCert = cert_FindDERCertBySubjectKeyID(ri->id.subjectKeyID); } if (derCert) { cert = PK11_FindCertFromDERCertItem(slot, derCert, pwarg); SECITEM_FreeItem(derCert, PR_TRUE); } } else { cert = PK11_FindCertByIssuerAndSNOnToken(slot, ri->id.issuerAndSN, pwarg); } if (cert) { /* this isn't our cert */ if (CERT_GetCertTrust(cert, &trust) != SECSuccess || ((trust.emailFlags & CERTDB_USER) != CERTDB_USER)) { CERT_DestroyCertificate(cert); continue; } ri->slot = PK11_ReferenceSlot(slot); *rlIndex = i; return cert; } } *rlIndex = -1; return NULL; } /* * This function is the same as above, but it searches all the slots. * this is the new version for NSS SMIME code * this stuff should REALLY be in the SMIME code, but some things in here are not public * (they should be!) */ static CERTCertificate * pk11_AllFindCertObjectByRecipientNew(NSSCMSRecipient **recipientlist, void *wincx, int *rlIndex) { PK11SlotList *list; PK11SlotListElement *le; CERTCertificate *cert = NULL; SECStatus rv; /* get them all! */ list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_TRUE, wincx); if (list == NULL) { return CK_INVALID_HANDLE; } /* Look for the slot that holds the Key */ for (le = list->head; le; le = le->next) { rv = pk11_AuthenticateUnfriendly(le->slot, PR_TRUE, wincx); if (rv != SECSuccess) continue; cert = pk11_FindCertObjectByRecipientNew(le->slot, recipientlist, rlIndex, wincx); if (cert) break; } PK11_FreeSlotList(list); return cert; } /* * We're looking for a cert which we have the private key for that's on the * list of recipients. This searches one slot. */ static CERTCertificate * pk11_FindCertObjectByRecipient(PK11SlotInfo *slot, SEC_PKCS7RecipientInfo **recipientArray, SEC_PKCS7RecipientInfo **rip, void *pwarg) { SEC_PKCS7RecipientInfo *ri = NULL; CERTCertTrust trust; int i; for (i = 0; (ri = recipientArray[i]) != NULL; i++) { CERTCertificate *cert; cert = PK11_FindCertByIssuerAndSNOnToken(slot, ri->issuerAndSN, pwarg); if (cert) { /* this isn't our cert */ if (CERT_GetCertTrust(cert, &trust) != SECSuccess || ((trust.emailFlags & CERTDB_USER) != CERTDB_USER)) { CERT_DestroyCertificate(cert); continue; } *rip = ri; return cert; } } *rip = NULL; return NULL; } /* * This function is the same as above, but it searches all the slots. */ static CERTCertificate * pk11_AllFindCertObjectByRecipient(PK11SlotInfo **slotPtr, SEC_PKCS7RecipientInfo **recipientArray, SEC_PKCS7RecipientInfo **rip, void *wincx) { PK11SlotList *list; PK11SlotListElement *le; CERTCertificate *cert = NULL; PK11SlotInfo *slot = NULL; SECStatus rv; *slotPtr = NULL; /* get them all! */ list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_TRUE, wincx); if (list == NULL) { return CK_INVALID_HANDLE; } *rip = NULL; /* Look for the slot that holds the Key */ for (le = list->head; le; le = le->next) { rv = pk11_AuthenticateUnfriendly(le->slot, PR_TRUE, wincx); if (rv != SECSuccess) continue; cert = pk11_FindCertObjectByRecipient(le->slot, recipientArray, rip, wincx); if (cert) { slot = PK11_ReferenceSlot(le->slot); break; } } PK11_FreeSlotList(list); if (slot == NULL) { return NULL; } *slotPtr = slot; PORT_Assert(cert != NULL); return cert; } /* * We need to invert the search logic for PKCS 7 because if we search for * each cert on the list over all the slots, we wind up with lots of spurious * password prompts. This way we get only one password prompt per slot, at * the max, and most of the time we can find the cert, and only prompt for * the key... */ CERTCertificate * PK11_FindCertAndKeyByRecipientList(PK11SlotInfo **slotPtr, SEC_PKCS7RecipientInfo **array, SEC_PKCS7RecipientInfo **rip, SECKEYPrivateKey **privKey, void *wincx) { CERTCertificate *cert = NULL; *privKey = NULL; *slotPtr = NULL; cert = pk11_AllFindCertObjectByRecipient(slotPtr, array, rip, wincx); if (!cert) { return NULL; } *privKey = PK11_FindKeyByAnyCert(cert, wincx); if (*privKey == NULL) { goto loser; } return cert; loser: if (cert) CERT_DestroyCertificate(cert); if (*slotPtr) PK11_FreeSlot(*slotPtr); *slotPtr = NULL; return NULL; } /* * This is the new version of the above function for NSS SMIME code * this stuff should REALLY be in the SMIME code, but some things in here are not public * (they should be!) */ int PK11_FindCertAndKeyByRecipientListNew(NSSCMSRecipient **recipientlist, void *wincx) { CERTCertificate *cert; NSSCMSRecipient *rl; PRStatus rv; int rlIndex; rv = PR_CallOnceWithArg(&keyIDHashCallOnce, pk11_keyIDHash_populate, wincx); if (rv != PR_SUCCESS) return -1; cert = pk11_AllFindCertObjectByRecipientNew(recipientlist, wincx, &rlIndex); if (!cert) { return -1; } rl = recipientlist[rlIndex]; /* at this point, rl->slot is set */ rl->privkey = PK11_FindKeyByAnyCert(cert, wincx); if (rl->privkey == NULL) { goto loser; } /* make a cert from the cert handle */ rl->cert = cert; return rlIndex; loser: if (cert) CERT_DestroyCertificate(cert); if (rl->slot) PK11_FreeSlot(rl->slot); rl->slot = NULL; return -1; } CERTCertificate * PK11_FindCertByIssuerAndSN(PK11SlotInfo **slotPtr, CERTIssuerAndSN *issuerSN, void *wincx) { CERTCertificate *rvCert = NULL; NSSCertificate *cert; NSSDER issuer, serial; NSSCryptoContext *cc; SECItem *derSerial; if (!issuerSN || !issuerSN->derIssuer.data || !issuerSN->derIssuer.len || !issuerSN->serialNumber.data || !issuerSN->serialNumber.len || issuerSN->derIssuer.len > CERT_MAX_DN_BYTES || issuerSN->serialNumber.len > CERT_MAX_SERIAL_NUMBER_BYTES) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return NULL; } if (slotPtr) *slotPtr = NULL; /* PKCS#11 needs to use DER-encoded serial numbers. Create a * CERTIssuerAndSN that actually has the encoded value and pass that * to PKCS#11 (and the crypto context). */ derSerial = SEC_ASN1EncodeItem(NULL, NULL, &issuerSN->serialNumber, SEC_ASN1_GET(SEC_IntegerTemplate)); if (!derSerial) { return NULL; } NSSITEM_FROM_SECITEM(&issuer, &issuerSN->derIssuer); NSSITEM_FROM_SECITEM(&serial, derSerial); cc = STAN_GetDefaultCryptoContext(); cert = NSSCryptoContext_FindCertificateByIssuerAndSerialNumber(cc, &issuer, &serial); if (cert) { SECITEM_FreeItem(derSerial, PR_TRUE); return STAN_GetCERTCertificateOrRelease(cert); } do { /* free the old cert on retry. Associated slot was not present */ if (rvCert) { CERT_DestroyCertificate(rvCert); rvCert = NULL; } cert = NSSTrustDomain_FindCertificateByIssuerAndSerialNumber( STAN_GetDefaultTrustDomain(), &issuer, &serial); if (!cert) { break; } rvCert = STAN_GetCERTCertificateOrRelease(cert); if (rvCert == NULL) { break; } /* Check to see if the cert's token is still there */ } while (!PK11_IsPresent(rvCert->slot)); if (rvCert && slotPtr) *slotPtr = PK11_ReferenceSlot(rvCert->slot); SECITEM_FreeItem(derSerial, PR_TRUE); return rvCert; } CK_OBJECT_HANDLE PK11_FindObjectForCert(CERTCertificate *cert, void *wincx, PK11SlotInfo **pSlot) { CK_OBJECT_HANDLE certHandle; CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; CK_ATTRIBUTE *attr; CK_ATTRIBUTE searchTemplate[] = { { CKA_CLASS, NULL, 0 }, { CKA_VALUE, NULL, 0 }, }; const size_t templateSize = sizeof(searchTemplate) / sizeof(searchTemplate[0]); attr = searchTemplate; PK11_SETATTRS(attr, CKA_CLASS, &certClass, sizeof(certClass)); attr++; PK11_SETATTRS(attr, CKA_VALUE, cert->derCert.data, cert->derCert.len); if (cert->slot) { certHandle = PK11_FindCertInSlot(cert->slot, cert, wincx); if (certHandle != CK_INVALID_HANDLE) { *pSlot = PK11_ReferenceSlot(cert->slot); return certHandle; } } certHandle = pk11_FindCertObjectByTemplate(pSlot, searchTemplate, templateSize, wincx); if (certHandle != CK_INVALID_HANDLE) { if (cert->slot == NULL) { cert->slot = PK11_ReferenceSlot(*pSlot); cert->pkcs11ID = certHandle; cert->ownSlot = PR_TRUE; cert->series = cert->slot->series; } } return (certHandle); } SECKEYPrivateKey * PK11_FindKeyByAnyCert(CERTCertificate *cert, void *wincx) { CK_OBJECT_HANDLE certHandle; CK_OBJECT_HANDLE keyHandle; PK11SlotInfo *slot = NULL; SECKEYPrivateKey *privKey = NULL; PRBool needLogin; SECStatus rv; int err; certHandle = PK11_FindObjectForCert(cert, wincx, &slot); if (certHandle == CK_INVALID_HANDLE) { return NULL; } /* * prevent a login race condition. If slot is logged in between * our call to pk11_LoginStillRequired and the * PK11_MatchItem. The matchItem call will either succeed, or * we will call it one more time after calling PK11_Authenticate * (which is a noop on an authenticated token). */ needLogin = pk11_LoginStillRequired(slot, wincx); keyHandle = PK11_MatchItem(slot, certHandle, CKO_PRIVATE_KEY); if ((keyHandle == CK_INVALID_HANDLE) && needLogin && (SSL_ERROR_NO_CERTIFICATE == (err = PORT_GetError()) || SEC_ERROR_TOKEN_NOT_LOGGED_IN == err)) { /* authenticate and try again */ rv = PK11_Authenticate(slot, PR_TRUE, wincx); if (rv == SECSuccess) { keyHandle = PK11_MatchItem(slot, certHandle, CKO_PRIVATE_KEY); } } if (keyHandle != CK_INVALID_HANDLE) { privKey = PK11_MakePrivKey(slot, nullKey, PR_TRUE, keyHandle, wincx); } if (slot) { PK11_FreeSlot(slot); } return privKey; } CK_OBJECT_HANDLE pk11_FindPubKeyByAnyCert(CERTCertificate *cert, PK11SlotInfo **slot, void *wincx) { CK_OBJECT_HANDLE certHandle; CK_OBJECT_HANDLE keyHandle; certHandle = PK11_FindObjectForCert(cert, wincx, slot); if (certHandle == CK_INVALID_HANDLE) { return CK_INVALID_HANDLE; } keyHandle = PK11_MatchItem(*slot, certHandle, CKO_PUBLIC_KEY); if (keyHandle == CK_INVALID_HANDLE) { PK11_FreeSlot(*slot); return CK_INVALID_HANDLE; } return keyHandle; } /* * find the number of certs in the slot with the same subject name */ int PK11_NumberCertsForCertSubject(CERTCertificate *cert) { CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; CK_ATTRIBUTE theTemplate[] = { { CKA_CLASS, NULL, 0 }, { CKA_SUBJECT, NULL, 0 }, }; CK_ATTRIBUTE *attr = theTemplate; int templateSize = sizeof(theTemplate) / sizeof(theTemplate[0]); PK11_SETATTRS(attr, CKA_CLASS, &certClass, sizeof(certClass)); attr++; PK11_SETATTRS(attr, CKA_SUBJECT, cert->derSubject.data, cert->derSubject.len); if (cert->slot == NULL) { PK11SlotList *list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_TRUE, NULL); PK11SlotListElement *le; int count = 0; if (!list) { /* error code is set */ return 0; } /* loop through all the fortezza tokens */ for (le = list->head; le; le = le->next) { count += PK11_NumberObjectsFor(le->slot, theTemplate, templateSize); } PK11_FreeSlotList(list); return count; } return PK11_NumberObjectsFor(cert->slot, theTemplate, templateSize); } /* * Walk all the certs with the same subject */ SECStatus PK11_TraverseCertsForSubject(CERTCertificate *cert, SECStatus (*callback)(CERTCertificate *, void *), void *arg) { if (!cert) { return SECFailure; } if (cert->slot == NULL) { PK11SlotList *list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_TRUE, NULL); PK11SlotListElement *le; if (!list) { /* error code is set */ return SECFailure; } /* loop through all the tokens */ for (le = list->head; le; le = le->next) { PK11_TraverseCertsForSubjectInSlot(cert, le->slot, callback, arg); } PK11_FreeSlotList(list); return SECSuccess; } return PK11_TraverseCertsForSubjectInSlot(cert, cert->slot, callback, arg); } SECStatus PK11_TraverseCertsForSubjectInSlot(CERTCertificate *cert, PK11SlotInfo *slot, SECStatus (*callback)(CERTCertificate *, void *), void *arg) { PRStatus nssrv = PR_SUCCESS; NSSToken *token; NSSDER subject; NSSTrustDomain *td; nssList *subjectList; nssPKIObjectCollection *collection; nssCryptokiObject **instances; NSSCertificate **certs; nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly; td = STAN_GetDefaultTrustDomain(); NSSITEM_FROM_SECITEM(&subject, &cert->derSubject); token = PK11Slot_GetNSSToken(slot); if (!token) { return SECSuccess; } if (!nssToken_IsPresent(token)) { (void)nssToken_Destroy(token); return SECSuccess; } collection = nssCertificateCollection_Create(td, NULL); if (!collection) { (void)nssToken_Destroy(token); return SECFailure; } subjectList = nssList_Create(NULL, PR_FALSE); if (!subjectList) { nssPKIObjectCollection_Destroy(collection); (void)nssToken_Destroy(token); return SECFailure; } (void)nssTrustDomain_GetCertsForSubjectFromCache(td, &subject, subjectList); transfer_token_certs_to_collection(subjectList, token, collection); instances = nssToken_FindCertificatesBySubject(token, NULL, &subject, tokenOnly, 0, &nssrv); nssPKIObjectCollection_AddInstances(collection, instances, 0); nss_ZFreeIf(instances); nssList_Destroy(subjectList); certs = nssPKIObjectCollection_GetCertificates(collection, NULL, 0, NULL); nssPKIObjectCollection_Destroy(collection); (void)nssToken_Destroy(token); if (certs) { CERTCertificate *oldie; NSSCertificate **cp; for (cp = certs; *cp; cp++) { oldie = STAN_GetCERTCertificate(*cp); if (!oldie) { continue; } if ((*callback)(oldie, arg) != SECSuccess) { nssrv = PR_FAILURE; break; } } nssCertificateArray_Destroy(certs); } return (nssrv == PR_SUCCESS) ? SECSuccess : SECFailure; } SECStatus PK11_TraverseCertsForNicknameInSlot(SECItem *nickname, PK11SlotInfo *slot, SECStatus (*callback)(CERTCertificate *, void *), void *arg) { PRStatus nssrv = PR_SUCCESS; NSSToken *token; NSSTrustDomain *td; NSSUTF8 *nick; PRBool created = PR_FALSE; nssCryptokiObject **instances; nssPKIObjectCollection *collection = NULL; NSSCertificate **certs; nssList *nameList = NULL; nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly; token = PK11Slot_GetNSSToken(slot); if (!token || !nssToken_IsPresent(token)) { (void)nssToken_Destroy(token); return SECSuccess; } if (nickname->data[nickname->len - 1] != '\0') { nick = nssUTF8_Create(NULL, nssStringType_UTF8String, nickname->data, nickname->len); created = PR_TRUE; } else { nick = (NSSUTF8 *)nickname->data; } td = STAN_GetDefaultTrustDomain(); collection = nssCertificateCollection_Create(td, NULL); if (!collection) { goto loser; } nameList = nssList_Create(NULL, PR_FALSE); if (!nameList) { goto loser; } (void)nssTrustDomain_GetCertsForNicknameFromCache(td, nick, nameList); transfer_token_certs_to_collection(nameList, token, collection); instances = nssToken_FindCertificatesByNickname(token, NULL, nick, tokenOnly, 0, &nssrv); nssPKIObjectCollection_AddInstances(collection, instances, 0); nss_ZFreeIf(instances); nssList_Destroy(nameList); certs = nssPKIObjectCollection_GetCertificates(collection, NULL, 0, NULL); nssPKIObjectCollection_Destroy(collection); (void)nssToken_Destroy(token); if (certs) { CERTCertificate *oldie; NSSCertificate **cp; for (cp = certs; *cp; cp++) { oldie = STAN_GetCERTCertificate(*cp); if (!oldie) { continue; } if ((*callback)(oldie, arg) != SECSuccess) { nssrv = PR_FAILURE; break; } } nssCertificateArray_Destroy(certs); } if (created) nss_ZFreeIf(nick); return (nssrv == PR_SUCCESS) ? SECSuccess : SECFailure; loser: (void)nssToken_Destroy(token); if (created) { nss_ZFreeIf(nick); } if (collection) { nssPKIObjectCollection_Destroy(collection); } if (nameList) { nssList_Destroy(nameList); } return SECFailure; } SECStatus PK11_TraverseCertsInSlot(PK11SlotInfo *slot, SECStatus (*callback)(CERTCertificate *, void *), void *arg) { PRStatus nssrv; NSSTrustDomain *td = STAN_GetDefaultTrustDomain(); NSSToken *tok; nssList *certList = NULL; nssCryptokiObject **instances; nssPKIObjectCollection *collection; NSSCertificate **certs; nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly; tok = PK11Slot_GetNSSToken(slot); if (!tok) { return SECSuccess; } if (!nssToken_IsPresent(tok)) { (void)nssToken_Destroy(tok); return SECSuccess; } collection = nssCertificateCollection_Create(td, NULL); if (!collection) { (void)nssToken_Destroy(tok); return SECFailure; } certList = nssList_Create(NULL, PR_FALSE); if (!certList) { nssPKIObjectCollection_Destroy(collection); (void)nssToken_Destroy(tok); return SECFailure; } (void)nssTrustDomain_GetCertsFromCache(td, certList); transfer_token_certs_to_collection(certList, tok, collection); instances = nssToken_FindObjects(tok, NULL, CKO_CERTIFICATE, tokenOnly, 0, &nssrv); nssPKIObjectCollection_AddInstances(collection, instances, 0); nss_ZFreeIf(instances); nssList_Destroy(certList); certs = nssPKIObjectCollection_GetCertificates(collection, NULL, 0, NULL); nssPKIObjectCollection_Destroy(collection); (void)nssToken_Destroy(tok); if (certs) { CERTCertificate *oldie; NSSCertificate **cp; for (cp = certs; *cp; cp++) { oldie = STAN_GetCERTCertificate(*cp); if (!oldie) { continue; } if ((*callback)(oldie, arg) != SECSuccess) { nssrv = PR_FAILURE; break; } } nssCertificateArray_Destroy(certs); } return (nssrv == PR_SUCCESS) ? SECSuccess : SECFailure; } /* * return the certificate associated with a derCert */ CERTCertificate * PK11_FindCertFromDERCert(PK11SlotInfo *slot, CERTCertificate *cert, void *wincx) { return PK11_FindCertFromDERCertItem(slot, &cert->derCert, wincx); } CERTCertificate * PK11_FindCertFromDERCertItem(PK11SlotInfo *slot, const SECItem *inDerCert, void *wincx) { NSSDER derCert; NSSToken *tok; nssCryptokiObject *co = NULL; SECStatus rv; CERTCertificate *cert = NULL; NSSITEM_FROM_SECITEM(&derCert, inDerCert); rv = pk11_AuthenticateUnfriendly(slot, PR_TRUE, wincx); if (rv != SECSuccess) { PK11_FreeSlot(slot); return NULL; } tok = PK11Slot_GetNSSToken(slot); if (!tok) { PK11_FreeSlot(slot); return NULL; } co = nssToken_FindCertificateByEncodedCertificate(tok, NULL, &derCert, nssTokenSearchType_TokenOnly, NULL); (void)nssToken_Destroy(tok); if (co) { cert = PK11_MakeCertFromHandle(slot, co->handle, NULL); nssCryptokiObject_Destroy(co); } return cert; } /* * import a cert for a private key we have already generated. Set the label * on both to be the nickname. */ static CK_OBJECT_HANDLE pk11_findKeyObjectByDERCert(PK11SlotInfo *slot, CERTCertificate *cert, void *wincx) { SECItem *keyID; CK_OBJECT_HANDLE key; SECStatus rv; PRBool needLogin; int err; if ((slot == NULL) || (cert == NULL)) { return CK_INVALID_HANDLE; } keyID = pk11_mkcertKeyID(cert); if (keyID == NULL) { return CK_INVALID_HANDLE; } /* * prevent a login race condition. If slot is logged in between * our call to pk11_LoginStillRequired and the * pk11_FindPrivateKeyFromCerID. The matchItem call will either succeed, or * we will call it one more time after calling PK11_Authenticate * (which is a noop on an authenticated token). */ needLogin = pk11_LoginStillRequired(slot, wincx); key = pk11_FindPrivateKeyFromCertID(slot, keyID); if ((key == CK_INVALID_HANDLE) && needLogin && (SSL_ERROR_NO_CERTIFICATE == (err = PORT_GetError()) || SEC_ERROR_TOKEN_NOT_LOGGED_IN == err)) { /* authenticate and try again */ rv = PK11_Authenticate(slot, PR_TRUE, wincx); if (rv != SECSuccess) goto loser; key = pk11_FindPrivateKeyFromCertID(slot, keyID); } loser: SECITEM_ZfreeItem(keyID, PR_TRUE); return key; } SECKEYPrivateKey * PK11_FindKeyByDERCert(PK11SlotInfo *slot, CERTCertificate *cert, void *wincx) { CK_OBJECT_HANDLE keyHandle; if ((slot == NULL) || (cert == NULL)) { return NULL; } keyHandle = pk11_findKeyObjectByDERCert(slot, cert, wincx); if (keyHandle == CK_INVALID_HANDLE) { return NULL; } return PK11_MakePrivKey(slot, nullKey, PR_TRUE, keyHandle, wincx); } SECStatus PK11_ImportCertForKeyToSlot(PK11SlotInfo *slot, CERTCertificate *cert, char *nickname, PRBool addCertUsage, void *wincx) { CK_OBJECT_HANDLE keyHandle; if ((slot == NULL) || (cert == NULL) || (nickname == NULL)) { return SECFailure; } keyHandle = pk11_findKeyObjectByDERCert(slot, cert, wincx); if (keyHandle == CK_INVALID_HANDLE) { return SECFailure; } return PK11_ImportCert(slot, cert, keyHandle, nickname, addCertUsage); } /* remove when the real version comes out */ #define SEC_OID_MISSI_KEA 300 /* until we have v3 stuff merged */ PRBool KEAPQGCompare(CERTCertificate *server, CERTCertificate *cert) { /* not implemented */ return PR_FALSE; } PRBool PK11_FortezzaHasKEA(CERTCertificate *cert) { /* look at the subject and see if it is a KEA for MISSI key */ SECOidData *oid; CERTCertTrust trust; if (CERT_GetCertTrust(cert, &trust) != SECSuccess || ((trust.sslFlags & CERTDB_USER) != CERTDB_USER)) { return PR_FALSE; } oid = SECOID_FindOID(&cert->subjectPublicKeyInfo.algorithm.algorithm); if (!oid) { return PR_FALSE; } return (PRBool)((oid->offset == SEC_OID_MISSI_KEA_DSS_OLD) || (oid->offset == SEC_OID_MISSI_KEA_DSS) || (oid->offset == SEC_OID_MISSI_KEA)); } /* * Find a kea cert on this slot that matches the domain of it's peer */ static CERTCertificate * pk11_GetKEAMate(PK11SlotInfo *slot, CERTCertificate *peer) { int i; CERTCertificate *returnedCert = NULL; for (i = 0; i < slot->cert_count; i++) { CERTCertificate *cert = slot->cert_array[i]; if (PK11_FortezzaHasKEA(cert) && KEAPQGCompare(peer, cert)) { returnedCert = CERT_DupCertificate(cert); break; } } return returnedCert; } /* * The following is a FORTEZZA only Certificate request. We call this when we * are doing a non-client auth SSL connection. We are only interested in the * fortezza slots, and we are only interested in certs that share the same root * key as the server. */ CERTCertificate * PK11_FindBestKEAMatch(CERTCertificate *server, void *wincx) { PK11SlotList *keaList = PK11_GetAllTokens(CKM_KEA_KEY_DERIVE, PR_FALSE, PR_TRUE, wincx); PK11SlotListElement *le; CERTCertificate *returnedCert = NULL; SECStatus rv; if (!keaList) { /* error code is set */ return NULL; } /* loop through all the fortezza tokens */ for (le = keaList->head; le; le = le->next) { rv = PK11_Authenticate(le->slot, PR_TRUE, wincx); if (rv != SECSuccess) continue; if (le->slot->session == CK_INVALID_HANDLE) { continue; } returnedCert = pk11_GetKEAMate(le->slot, server); if (returnedCert) break; } PK11_FreeSlotList(keaList); return returnedCert; } /* * find a matched pair of kea certs to key exchange parameters from one * fortezza card to another as necessary. */ SECStatus PK11_GetKEAMatchedCerts(PK11SlotInfo *slot1, PK11SlotInfo *slot2, CERTCertificate **cert1, CERTCertificate **cert2) { CERTCertificate *returnedCert = NULL; int i; for (i = 0; i < slot1->cert_count; i++) { CERTCertificate *cert = slot1->cert_array[i]; if (PK11_FortezzaHasKEA(cert)) { returnedCert = pk11_GetKEAMate(slot2, cert); if (returnedCert != NULL) { *cert2 = returnedCert; *cert1 = CERT_DupCertificate(cert); return SECSuccess; } } } return SECFailure; } CK_OBJECT_HANDLE PK11_FindEncodedCertInSlot(PK11SlotInfo *slot, SECItem *derCert, void *wincx) { if (!slot || !derCert) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; CK_ATTRIBUTE theTemplate[] = { { CKA_VALUE, NULL, 0 }, { CKA_CLASS, NULL, 0 } }; const size_t tsize = sizeof(theTemplate) / sizeof(theTemplate[0]); CK_ATTRIBUTE *attrs = theTemplate; PK11_SETATTRS(attrs, CKA_VALUE, derCert->data, derCert->len); attrs++; PK11_SETATTRS(attrs, CKA_CLASS, &certClass, sizeof(certClass)); SECStatus rv = pk11_AuthenticateUnfriendly(slot, PR_TRUE, wincx); if (rv != SECSuccess) { return CK_INVALID_HANDLE; } return pk11_FindObjectByTemplate(slot, theTemplate, tsize); } CK_OBJECT_HANDLE PK11_FindCertInSlot(PK11SlotInfo *slot, CERTCertificate *cert, void *wincx) { CK_OBJECT_HANDLE certh; if (cert->slot == slot) { certh = cert->pkcs11ID; if ((certh == CK_INVALID_HANDLE) || (cert->series != slot->series)) { certh = PK11_FindEncodedCertInSlot(slot, &cert->derCert, wincx); cert->pkcs11ID = certh; cert->series = slot->series; } } else { certh = PK11_FindEncodedCertInSlot(slot, &cert->derCert, wincx); } return certh; } /* Looking for PK11_GetKeyIDFromCert? * Use PK11_GetLowLevelKeyIDForCert instead. */ struct listCertsStr { PK11CertListType type; CERTCertList *certList; }; static PRStatus pk11ListCertCallback(NSSCertificate *c, void *arg) { struct listCertsStr *listCertP = (struct listCertsStr *)arg; CERTCertificate *newCert = NULL; PK11CertListType type = listCertP->type; CERTCertList *certList = listCertP->certList; PRBool isUnique = PR_FALSE; PRBool isCA = PR_FALSE; char *nickname = NULL; unsigned int certType; SECStatus rv; if ((type == PK11CertListUnique) || (type == PK11CertListRootUnique) || (type == PK11CertListCAUnique) || (type == PK11CertListUserUnique)) { /* only list one instance of each certificate, even if several exist */ isUnique = PR_TRUE; } if ((type == PK11CertListCA) || (type == PK11CertListRootUnique) || (type == PK11CertListCAUnique)) { isCA = PR_TRUE; } /* if we want user certs and we don't have one skip this cert */ if (((type == PK11CertListUser) || (type == PK11CertListUserUnique)) && !NSSCertificate_IsPrivateKeyAvailable(c, NULL, NULL)) { return PR_SUCCESS; } /* PK11CertListRootUnique means we want CA certs without a private key. * This is for legacy app support . PK11CertListCAUnique should be used * instead to get all CA certs, regardless of private key */ if ((type == PK11CertListRootUnique) && NSSCertificate_IsPrivateKeyAvailable(c, NULL, NULL)) { return PR_SUCCESS; } /* caller still owns the reference to 'c' */ newCert = STAN_GetCERTCertificate(c); if (!newCert) { return PR_SUCCESS; } /* if we want CA certs and it ain't one, skip it */ if (isCA && (!CERT_IsCACert(newCert, &certType))) { return PR_SUCCESS; } if (isUnique) { CERT_DupCertificate(newCert); nickname = STAN_GetCERTCertificateName(certList->arena, c); /* put slot certs at the end */ if (newCert->slot && !PK11_IsInternal(newCert->slot)) { rv = CERT_AddCertToListTailWithData(certList, newCert, nickname); } else { rv = CERT_AddCertToListHeadWithData(certList, newCert, nickname); } /* if we didn't add the cert to the list, don't leak it */ if (rv != SECSuccess) { CERT_DestroyCertificate(newCert); } } else { /* add multiple instances to the cert list */ nssCryptokiObject **ip; nssCryptokiObject **instances = nssPKIObject_GetInstances(&c->object); if (!instances) { return PR_SUCCESS; } for (ip = instances; *ip; ip++) { nssCryptokiObject *instance = *ip; PK11SlotInfo *slot = instance->token->pk11slot; /* put the same CERTCertificate in the list for all instances */ CERT_DupCertificate(newCert); nickname = STAN_GetCERTCertificateNameForInstance( certList->arena, c, instance); /* put slot certs at the end */ if (slot && !PK11_IsInternal(slot)) { rv = CERT_AddCertToListTailWithData(certList, newCert, nickname); } else { rv = CERT_AddCertToListHeadWithData(certList, newCert, nickname); } /* if we didn't add the cert to the list, don't leak it */ if (rv != SECSuccess) { CERT_DestroyCertificate(newCert); } } nssCryptokiObjectArray_Destroy(instances); } return PR_SUCCESS; } CERTCertList * PK11_ListCerts(PK11CertListType type, void *pwarg) { NSSTrustDomain *defaultTD = STAN_GetDefaultTrustDomain(); CERTCertList *certList = NULL; struct listCertsStr listCerts; certList = CERT_NewCertList(); listCerts.type = type; listCerts.certList = certList; /* authenticate to the slots */ (void)pk11_TraverseAllSlots(NULL, NULL, PR_TRUE, pwarg); NSSTrustDomain_TraverseCertificates(defaultTD, pk11ListCertCallback, &listCerts); return certList; } SECItem * PK11_GetLowLevelKeyIDForCert(PK11SlotInfo *slot, CERTCertificate *cert, void *wincx) { CK_OBJECT_HANDLE certHandle; PK11SlotInfo *slotRef = NULL; SECItem *item; if (slot) { certHandle = PK11_FindCertInSlot(slot, cert, wincx); } else { certHandle = PK11_FindObjectForCert(cert, wincx, &slotRef); if (certHandle == CK_INVALID_HANDLE) { return pk11_mkcertKeyID(cert); } slot = slotRef; } if (certHandle == CK_INVALID_HANDLE) { return NULL; } item = pk11_GetLowLevelKeyFromHandle(slot, certHandle); if (slotRef) PK11_FreeSlot(slotRef); return item; } /* argument type for listCertsCallback */ typedef struct { CERTCertList *list; PK11SlotInfo *slot; } ListCertsArg; static SECStatus listCertsCallback(CERTCertificate *cert, void *arg) { ListCertsArg *cdata = (ListCertsArg *)arg; char *nickname = NULL; nssCryptokiObject *instance, **ci; nssCryptokiObject **instances; NSSCertificate *c = STAN_GetNSSCertificate(cert); SECStatus rv; if (c == NULL) { return SECFailure; } instances = nssPKIObject_GetInstances(&c->object); if (!instances) { return SECFailure; } instance = NULL; for (ci = instances; *ci; ci++) { if ((*ci)->token->pk11slot == cdata->slot) { instance = *ci; break; } } PORT_Assert(instance != NULL); if (!instance) { nssCryptokiObjectArray_Destroy(instances); PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure; } nickname = STAN_GetCERTCertificateNameForInstance(cdata->list->arena, c, instance); nssCryptokiObjectArray_Destroy(instances); CERT_DupCertificate(cert); rv = CERT_AddCertToListTailWithData(cdata->list, cert, nickname); if (rv != SECSuccess) { CERT_DestroyCertificate(cert); } return rv; } CERTCertList * PK11_ListCertsInSlot(PK11SlotInfo *slot) { SECStatus status; CERTCertList *certs; ListCertsArg cdata; certs = CERT_NewCertList(); if (certs == NULL) return NULL; cdata.list = certs; cdata.slot = slot; status = PK11_TraverseCertsInSlot(slot, listCertsCallback, &cdata); if (status != SECSuccess) { CERT_DestroyCertList(certs); certs = NULL; } return certs; } PK11SlotList * PK11_GetAllSlotsForCert(CERTCertificate *cert, void *arg) { nssCryptokiObject **ip; PK11SlotList *slotList; NSSCertificate *c; nssCryptokiObject **instances; PRBool found = PR_FALSE; if (!cert) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return NULL; } c = STAN_GetNSSCertificate(cert); if (!c) { CERT_MapStanError(); return NULL; } /* add multiple instances to the cert list */ instances = nssPKIObject_GetInstances(&c->object); if (!instances) { PORT_SetError(SEC_ERROR_NO_TOKEN); return NULL; } slotList = PK11_NewSlotList(); if (!slotList) { nssCryptokiObjectArray_Destroy(instances); return NULL; } for (ip = instances; *ip; ip++) { nssCryptokiObject *instance = *ip; PK11SlotInfo *slot = instance->token->pk11slot; if (slot) { PK11_AddSlotToList(slotList, slot, PR_TRUE); found = PR_TRUE; } } if (!found) { PK11_FreeSlotList(slotList); PORT_SetError(SEC_ERROR_NO_TOKEN); slotList = NULL; } nssCryptokiObjectArray_Destroy(instances); return slotList; } /* * Using __PK11_SetCertificateNickname is *DANGEROUS*. * * The API will update the NSS database, but it *will NOT* update the in-memory data. * As a result, after calling this API, there will be INCONSISTENCY between * in-memory data and the database. * * Use of the API should be limited to short-lived tools, which will exit immediately * after using this API. * * If you ignore this warning, your process is TAINTED and will most likely misbehave. */ SECStatus __PK11_SetCertificateNickname(CERTCertificate *cert, const char *nickname) { /* Can't set nickname of temp cert. */ if (!cert->slot || cert->pkcs11ID == CK_INVALID_HANDLE) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } return PK11_SetObjectNickname(cert->slot, cert->pkcs11ID, nickname); }