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