diff options
Diffstat (limited to 'security/nss/lib/pk11wrap/pk11cert.c')
-rw-r--r-- | security/nss/lib/pk11wrap/pk11cert.c | 4224 |
1 files changed, 4224 insertions, 0 deletions
diff --git a/security/nss/lib/pk11wrap/pk11cert.c b/security/nss/lib/pk11wrap/pk11cert.c new file mode 100644 index 000000000..a100caca3 --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11cert.c @@ -0,0 +1,4224 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Portions created by Sun Microsystems, Inc. are Copyright (C) 2003 + * Sun Microsystems, Inc. All Rights Reserved. + * + * Contributor(s): + * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ +/* + * This file implements the Symkey wrapper and the PKCS context + * Interfaces. + */ + +#include "secport.h" +#include "seccomon.h" +#include "secmod.h" +#include "nssilock.h" +#include "secmodi.h" +#include "pkcs11.h" +#include "pk11func.h" +#include "cert.h" +#include "certi.h" +#include "secitem.h" +#include "key.h" +#include "hasht.h" +#include "secoid.h" +#include "pkcs7t.h" +#include "cmsreclist.h" + +#include "certdb.h" +#include "secerr.h" +#include "sslerr.h" + +#ifndef NSS_3_4_CODE +#define NSS_3_4_CODE +#endif /* NSS_3_4_CODE */ +#include "pki3hack.h" +#include "dev3hack.h" + +#include "devm.h" +#include "nsspki.h" +#include "pki.h" +#include "pkim.h" +#include "pkitm.h" +#include "pkistore.h" /* to remove temp cert */ +#include "devt.h" + +#define PK11_SEARCH_CHUNKSIZE 10 + +extern const NSSError NSS_ERROR_NOT_FOUND; + +CK_OBJECT_HANDLE +pk11_FindPubKeyByAnyCert(CERTCertificate *cert, PK11SlotInfo **slot, void *wincx); + +struct nss3_cert_cbstr { + SECStatus(* callback)(CERTCertificate*, void *); + nssList *cached; + void *arg; +}; + +/* Translate from NSSCertificate to CERTCertificate, then pass the latter + * to a callback. + */ +static PRStatus convert_cert(NSSCertificate *c, void *arg) +{ + CERTCertificate *nss3cert; + SECStatus secrv; + struct nss3_cert_cbstr *nss3cb = (struct nss3_cert_cbstr *)arg; + nss3cert = STAN_GetCERTCertificate(c); + if (!nss3cert) return PR_FAILURE; + secrv = (*nss3cb->callback)(nss3cert, nss3cb->arg); + return (secrv) ? PR_FAILURE : PR_SUCCESS; +} + +void +PK11Slot_SetNSSToken(PK11SlotInfo *sl, NSSToken *nsst) +{ + sl->nssToken = nsst; +} + +NSSToken * +PK11Slot_GetNSSToken(PK11SlotInfo *sl) +{ + return sl->nssToken; +} + +/* + * build a cert nickname based on the token name and the label of the + * certificate If the label in NULL, build a label based on the ID. + */ +static int toHex(int x) { return (x < 10) ? (x+'0') : (x+'a'-10); } +#define MAX_CERT_ID 4 +#define DEFAULT_STRING "Cert ID " +static char * +pk11_buildNickname(PK11SlotInfo *slot,CK_ATTRIBUTE *cert_label, + CK_ATTRIBUTE *key_label, CK_ATTRIBUTE *cert_id) +{ + int prefixLen = PORT_Strlen(slot->token_name); + int suffixLen = 0; + char *suffix = NULL; + char buildNew[sizeof(DEFAULT_STRING)+MAX_CERT_ID*2]; + char *next,*nickname; + + if (cert_label && (cert_label->ulValueLen)) { + suffixLen = cert_label->ulValueLen; + suffix = (char*)cert_label->pValue; + } else if (key_label && (key_label->ulValueLen)) { + suffixLen = key_label->ulValueLen; + suffix = (char*)key_label->pValue; + } else if (cert_id && cert_id->ulValueLen > 0) { + int i,first = cert_id->ulValueLen - MAX_CERT_ID; + int offset = sizeof(DEFAULT_STRING); + char *idValue = (char *)cert_id->pValue; + + PORT_Memcpy(buildNew,DEFAULT_STRING,sizeof(DEFAULT_STRING)-1); + next = buildNew + offset; + if (first < 0) first = 0; + for (i=first; i < (int) cert_id->ulValueLen; i++) { + *next++ = toHex((idValue[i] >> 4) & 0xf); + *next++ = toHex(idValue[i] & 0xf); + } + *next++ = 0; + suffix = buildNew; + suffixLen = PORT_Strlen(buildNew); + } else { + PORT_SetError( SEC_ERROR_LIBRARY_FAILURE ); + return NULL; + } + + /* if is internal key slot, add code to skip the prefix!! */ + next = nickname = (char *)PORT_Alloc(prefixLen+1+suffixLen+1); + if (nickname == NULL) return NULL; + + PORT_Memcpy(next,slot->token_name,prefixLen); + next += prefixLen; + *next++ = ':'; + PORT_Memcpy(next,suffix,suffixLen); + next += suffixLen; + *next++ = 0; + return nickname; +} + +/* + * return the object handle that matches the template + */ +CK_OBJECT_HANDLE +pk11_FindObjectByTemplate(PK11SlotInfo *slot,CK_ATTRIBUTE *theTemplate,int tsize) +{ + CK_OBJECT_HANDLE object; + CK_RV crv; + CK_ULONG objectCount; + + /* + * issue the find + */ + PK11_EnterSlotMonitor(slot); + crv=PK11_GETTAB(slot)->C_FindObjectsInit(slot->session, theTemplate, tsize); + if (crv != CKR_OK) { + PK11_ExitSlotMonitor(slot); + PORT_SetError( PK11_MapError(crv) ); + return CK_INVALID_HANDLE; + } + + crv=PK11_GETTAB(slot)->C_FindObjects(slot->session,&object,1,&objectCount); + PK11_GETTAB(slot)->C_FindObjectsFinal(slot->session); + PK11_ExitSlotMonitor(slot); + if ((crv != CKR_OK) || (objectCount < 1)) { + /* shouldn't use SSL_ERROR... here */ + PORT_SetError( crv != CKR_OK ? PK11_MapError(crv) : + SSL_ERROR_NO_CERTIFICATE); + return CK_INVALID_HANDLE; + } + + /* blow up if the PKCS #11 module returns us and invalid object handle */ + PORT_Assert(object != CK_INVALID_HANDLE); + return object; +} + +/* + * return all the object handles that matches the template + */ +CK_OBJECT_HANDLE * +pk11_FindObjectsByTemplate(PK11SlotInfo *slot, + CK_ATTRIBUTE *findTemplate,int findCount,int *object_count) { + CK_OBJECT_HANDLE *objID = NULL; + CK_ULONG returned_count = 0; + CK_RV crv; + + + PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_FindObjectsInit(slot->session, findTemplate, + findCount); + if (crv != CKR_OK) { + PK11_ExitSlotMonitor(slot); + PORT_SetError( PK11_MapError(crv) ); + *object_count = -1; + return NULL; + } + + + /* + * collect all the Matching Objects + */ + do { + CK_OBJECT_HANDLE *oldObjID = objID; + + if (objID == NULL) { + objID = (CK_OBJECT_HANDLE *) PORT_Alloc(sizeof(CK_OBJECT_HANDLE)* + (*object_count+ PK11_SEARCH_CHUNKSIZE)); + } else { + objID = (CK_OBJECT_HANDLE *) PORT_Realloc(objID, + sizeof(CK_OBJECT_HANDLE)*(*object_count+PK11_SEARCH_CHUNKSIZE)); + } + + if (objID == NULL) { + if (oldObjID) PORT_Free(oldObjID); + break; + } + crv = PK11_GETTAB(slot)->C_FindObjects(slot->session, + &objID[*object_count],PK11_SEARCH_CHUNKSIZE,&returned_count); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + PORT_Free(objID); + objID = NULL; + break; + } + *object_count += returned_count; + } while (returned_count == PK11_SEARCH_CHUNKSIZE); + + PK11_GETTAB(slot)->C_FindObjectsFinal(slot->session); + PK11_ExitSlotMonitor(slot); + + if (objID && (*object_count == 0)) { + PORT_Free(objID); + return NULL; + } + if (objID == NULL) *object_count = -1; + return objID; +} +/* + * given a PKCS #11 object, match it's peer based on the KeyID. searchID + * is typically a privateKey or a certificate while the peer is the opposite + */ +CK_OBJECT_HANDLE +PK11_MatchItem(PK11SlotInfo *slot, CK_OBJECT_HANDLE searchID, + CK_OBJECT_CLASS matchclass) +{ + CK_ATTRIBUTE theTemplate[] = { + { CKA_ID, NULL, 0 }, + { CKA_CLASS, NULL, 0 } + }; + /* if you change the array, change the variable below as well */ + CK_ATTRIBUTE *keyclass = &theTemplate[1]; + int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]); + /* if you change the array, change the variable below as well */ + CK_OBJECT_HANDLE peerID; + CK_OBJECT_HANDLE parent; + PRArenaPool *arena; + CK_RV crv; + + /* now we need to create space for the public key */ + arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) return CK_INVALID_HANDLE; + + crv = PK11_GetAttributes(arena,slot,searchID,theTemplate,tsize); + if (crv != CKR_OK) { + PORT_FreeArena(arena,PR_FALSE); + PORT_SetError( PK11_MapError(crv) ); + return CK_INVALID_HANDLE; + } + + if ((theTemplate[0].ulValueLen == 0) || (theTemplate[0].ulValueLen == -1)) { + PORT_FreeArena(arena,PR_FALSE); + PORT_SetError(SEC_ERROR_BAD_KEY); + return CK_INVALID_HANDLE; + } + + + + /* + * issue the find + */ + parent = *(CK_OBJECT_CLASS *)(keyclass->pValue); + *(CK_OBJECT_CLASS *)(keyclass->pValue) = matchclass; + + peerID = pk11_FindObjectByTemplate(slot,theTemplate,tsize); + PORT_FreeArena(arena,PR_FALSE); + + return peerID; +} + +PRBool +PK11_IsUserCert(PK11SlotInfo *slot, CERTCertificate *cert, + CK_OBJECT_HANDLE certID) +{ + CK_OBJECT_CLASS theClass; + + if (slot == NULL) return PR_FALSE; + if (cert == NULL) return PR_FALSE; + + theClass = CKO_PRIVATE_KEY; + if (!PK11_IsLoggedIn(slot,NULL) && PK11_NeedLogin(slot)) { + theClass = CKO_PUBLIC_KEY; + } + if (PK11_MatchItem(slot, certID , theClass) != CK_INVALID_HANDLE) { + return PR_TRUE; + } + + if (theClass == CKO_PUBLIC_KEY) { + SECKEYPublicKey *pubKey= CERT_ExtractPublicKey(cert); + CK_ATTRIBUTE theTemplate; + + if (pubKey == NULL) { + return PR_FALSE; + } + + PK11_SETATTRS(&theTemplate,0,NULL,0); + switch (pubKey->keyType) { + case rsaKey: + PK11_SETATTRS(&theTemplate,CKA_MODULUS, pubKey->u.rsa.modulus.data, + pubKey->u.rsa.modulus.len); + break; + case dsaKey: + PK11_SETATTRS(&theTemplate,CKA_VALUE, pubKey->u.dsa.publicValue.data, + pubKey->u.dsa.publicValue.len); + break; + case dhKey: + PK11_SETATTRS(&theTemplate,CKA_VALUE, pubKey->u.dh.publicValue.data, + pubKey->u.dh.publicValue.len); + break; + case ecKey: +#ifdef NSS_ENABLE_ECC + PK11_SETATTRS(&theTemplate,CKA_EC_POINT, + pubKey->u.ec.publicValue.data, + pubKey->u.ec.publicValue.len); +#endif /* NSS_ENABLE_ECC */ + break; + case keaKey: + case fortezzaKey: + case nullKey: + /* fall through and return false */ + break; + } + + if (theTemplate.ulValueLen == 0) { + SECKEY_DestroyPublicKey(pubKey); + return PR_FALSE; + } + pk11_SignedToUnsigned(&theTemplate); + if (pk11_FindObjectByTemplate(slot,&theTemplate,1) != CK_INVALID_HANDLE) { + SECKEY_DestroyPublicKey(pubKey); + return PR_TRUE; + } + SECKEY_DestroyPublicKey(pubKey); + } + return PR_FALSE; +} + +/* + * Check out if a cert has ID of zero. This is a magic ID that tells + * NSS that this cert may be an automagically trusted cert. + * The Cert has to be self signed as well. That check is done elsewhere. + * + */ +PRBool +pk11_isID0(PK11SlotInfo *slot, CK_OBJECT_HANDLE certID) +{ + CK_ATTRIBUTE keyID = {CKA_ID, NULL, 0}; + PRBool isZero = PR_FALSE; + int i; + CK_RV crv; + + + crv = PK11_GetAttributes(NULL,slot,certID,&keyID,1); + if (crv != CKR_OK) { + return isZero; + } + + if (keyID.ulValueLen != 0) { + char *value = (char *)keyID.pValue; + isZero = PR_TRUE; /* ID exists, may be zero */ + for (i=0; i < (int) keyID.ulValueLen; i++) { + if (value[i] != 0) { + isZero = PR_FALSE; /* nope */ + break; + } + } + } + PORT_Free(keyID.pValue); + return isZero; + +} + +/* + * Create an NSSCertificate from a slot/certID pair, return it as a + * CERTCertificate. + */ +static CERTCertificate +*pk11_fastCert(PK11SlotInfo *slot, CK_OBJECT_HANDLE certID, + CK_ATTRIBUTE *privateLabel, char **nickptr) +{ + NSSCertificate *c; + nssCryptokiObject *co; + nssPKIObject *pkio; + NSSToken *token; + NSSTrustDomain *td = STAN_GetDefaultTrustDomain(); + + /* Get the cryptoki object from the handle */ + token = PK11Slot_GetNSSToken(slot); + co = nssCryptokiObject_Create(token, token->defaultSession, certID); + if (!co) { + return NULL; + } + + /* Create a PKI object from the cryptoki instance */ + pkio = nssPKIObject_Create(NULL, co, td, NULL); + if (!pkio) { + nssCryptokiObject_Destroy(co); + return NULL; + } + + /* Create a certificate */ + c = nssCertificate_Create(pkio); + if (!c) { + nssPKIObject_Destroy(pkio); + return NULL; + } + + nssTrustDomain_AddCertsToCache(td, &c, 1); + + /* Build the old-fashioned nickname */ + if ((nickptr) && (co->label)) { + CK_ATTRIBUTE label, id; + label.type = CKA_LABEL; + label.pValue = co->label; + label.ulValueLen = PORT_Strlen(co->label); + id.type = CKA_ID; + id.pValue = c->id.data; + id.ulValueLen = c->id.size; + *nickptr = pk11_buildNickname(slot, &label, privateLabel, &id); + } + return STAN_GetCERTCertificate(c); +} + +CK_TRUST +pk11_GetTrustField(PK11SlotInfo *slot, PRArenaPool *arena, + CK_OBJECT_HANDLE id, CK_ATTRIBUTE_TYPE type) +{ + CK_TRUST rv = 0; + SECItem item; + + item.data = NULL; + item.len = 0; + + if( SECSuccess == PK11_ReadAttribute(slot, id, type, arena, &item) ) { + PORT_Assert(item.len == sizeof(CK_TRUST)); + PORT_Memcpy(&rv, item.data, sizeof(CK_TRUST)); + /* Damn, is there an endian problem here? */ + return rv; + } + + return 0; +} + +PRBool +pk11_HandleTrustObject(PK11SlotInfo *slot, CERTCertificate *cert, CERTCertTrust *trust) +{ + PRArenaPool *arena; + + CK_ATTRIBUTE tobjTemplate[] = { + { CKA_CLASS, NULL, 0 }, + { CKA_CERT_SHA1_HASH, NULL, 0 }, + }; + + CK_OBJECT_CLASS tobjc = CKO_NETSCAPE_TRUST; + CK_OBJECT_HANDLE tobjID; + unsigned char sha1_hash[SHA1_LENGTH]; + + CK_TRUST serverAuth, codeSigning, emailProtection, clientAuth; + + PK11_HashBuf(SEC_OID_SHA1, sha1_hash, cert->derCert.data, cert->derCert.len); + + PK11_SETATTRS(&tobjTemplate[0], CKA_CLASS, &tobjc, sizeof(tobjc)); + PK11_SETATTRS(&tobjTemplate[1], CKA_CERT_SHA1_HASH, sha1_hash, + SHA1_LENGTH); + + tobjID = pk11_FindObjectByTemplate(slot, tobjTemplate, + sizeof(tobjTemplate)/sizeof(tobjTemplate[0])); + if( CK_INVALID_HANDLE == tobjID ) { + return PR_FALSE; + } + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if( NULL == arena ) return PR_FALSE; + + /* Unfortunately, it seems that PK11_GetAttributes doesn't deal + * well with nonexistant attributes. I guess we have to check + * the trust info fields one at a time. + */ + + /* We could verify CKA_CERT_HASH here */ + + /* We could verify CKA_EXPIRES here */ + + + /* "Purpose" trust information */ + serverAuth = pk11_GetTrustField(slot, arena, tobjID, CKA_TRUST_SERVER_AUTH); + clientAuth = pk11_GetTrustField(slot, arena, tobjID, CKA_TRUST_CLIENT_AUTH); + codeSigning = pk11_GetTrustField(slot, arena, tobjID, CKA_TRUST_CODE_SIGNING); + emailProtection = pk11_GetTrustField(slot, arena, tobjID, + CKA_TRUST_EMAIL_PROTECTION); + /* Here's where the fun logic happens. We have to map back from the + * key usage, extended key usage, purpose, and possibly other trust values + * into the old trust-flags bits. */ + + /* First implementation: keep it simple for testing. We can study what other + * mappings would be appropriate and add them later.. fgmr 20000724 */ + + if ( serverAuth == CKT_NETSCAPE_TRUSTED ) { + trust->sslFlags |= CERTDB_VALID_PEER | CERTDB_TRUSTED; + } + + if ( serverAuth == CKT_NETSCAPE_TRUSTED_DELEGATOR ) { + trust->sslFlags |= CERTDB_VALID_CA | CERTDB_TRUSTED_CA | + CERTDB_NS_TRUSTED_CA; + } + if ( clientAuth == CKT_NETSCAPE_TRUSTED_DELEGATOR ) { + trust->sslFlags |= CERTDB_TRUSTED_CLIENT_CA ; + } + + if ( emailProtection == CKT_NETSCAPE_TRUSTED ) { + trust->emailFlags |= CERTDB_VALID_PEER | CERTDB_TRUSTED; + } + + if ( emailProtection == CKT_NETSCAPE_TRUSTED_DELEGATOR ) { + trust->emailFlags |= CERTDB_VALID_CA | CERTDB_TRUSTED_CA | CERTDB_NS_TRUSTED_CA; + } + + if( codeSigning == CKT_NETSCAPE_TRUSTED ) { + trust->objectSigningFlags |= CERTDB_VALID_PEER | CERTDB_TRUSTED; + } + + if( codeSigning == CKT_NETSCAPE_TRUSTED_DELEGATOR ) { + trust->objectSigningFlags |= CERTDB_VALID_CA | CERTDB_TRUSTED_CA | CERTDB_NS_TRUSTED_CA; + } + + /* There's certainly a lot more logic that can go here.. */ + + PORT_FreeArena(arena, PR_FALSE); + + return PR_TRUE; +} + +/* + * Build an CERTCertificate structure from a PKCS#11 object ID.... certID + * Must be a CertObject. This code does not explicitly checks that. + */ +CERTCertificate * +PK11_MakeCertFromHandle(PK11SlotInfo *slot,CK_OBJECT_HANDLE certID, + CK_ATTRIBUTE *privateLabel) +{ + char * nickname = NULL; + CERTCertificate *cert = NULL; + CERTCertTrust *trust; + PRBool isFortezzaRootCA = PR_FALSE; + PRBool swapNickname = PR_FALSE; + + cert = pk11_fastCert(slot,certID,privateLabel, &nickname); + if (cert == NULL) goto loser; + + if (nickname) { + if (cert->nickname != NULL) { + cert->dbnickname = cert->nickname; + } + cert->nickname = PORT_ArenaStrdup(cert->arena,nickname); + PORT_Free(nickname); + nickname = NULL; + swapNickname = PR_TRUE; + } + + /* remember where this cert came from.... If we have just looked + * it up from the database and it already has a slot, don't add a new + * one. */ + if (cert->slot == NULL) { + cert->slot = PK11_ReferenceSlot(slot); + cert->pkcs11ID = certID; + cert->ownSlot = PR_TRUE; + cert->series = slot->series; + } + + trust = (CERTCertTrust*)PORT_ArenaAlloc(cert->arena, sizeof(CERTCertTrust)); + if (trust == NULL) goto loser; + PORT_Memset(trust,0, sizeof(CERTCertTrust)); + cert->trust = trust; + + + + if(! pk11_HandleTrustObject(slot, cert, trust) ) { + unsigned int type; + + /* build some cert trust flags */ + if (CERT_IsCACert(cert, &type)) { + unsigned int trustflags = CERTDB_VALID_CA; + + /* Allow PKCS #11 modules to give us trusted CA's. We only accept + * valid CA's which are self-signed here. They must have an object + * ID of '0'. */ + if (pk11_isID0(slot,certID) && + SECITEM_CompareItem(&cert->derSubject,&cert->derIssuer) + == SECEqual) { + trustflags |= CERTDB_TRUSTED_CA; + /* is the slot a fortezza card? allow the user or + * admin to turn on objectSigning, but don't turn + * full trust on explicitly */ + if (PK11_DoesMechanism(slot,CKM_KEA_KEY_DERIVE)) { + trust->objectSigningFlags |= CERTDB_VALID_CA; + isFortezzaRootCA = PR_TRUE; + } + } + if ((type & NS_CERT_TYPE_SSL_CA) == NS_CERT_TYPE_SSL_CA) { + trust->sslFlags |= trustflags; + } + if ((type & NS_CERT_TYPE_EMAIL_CA) == NS_CERT_TYPE_EMAIL_CA) { + trust->emailFlags |= trustflags; + } + if ((type & NS_CERT_TYPE_OBJECT_SIGNING_CA) + == NS_CERT_TYPE_OBJECT_SIGNING_CA) { + trust->objectSigningFlags |= trustflags; + } + } + } + + if (PK11_IsUserCert(slot,cert,certID)) { + trust->sslFlags |= CERTDB_USER; + trust->emailFlags |= CERTDB_USER; + /* trust->objectSigningFlags |= CERTDB_USER; */ + } + + return cert; + +loser: + if (nickname) PORT_Free(nickname); + if (cert) CERT_DestroyCertificate(cert); + return NULL; +} + + +/* + * Build get a certificate from a private key + */ +CERTCertificate * +PK11_GetCertFromPrivateKey(SECKEYPrivateKey *privKey) +{ + PK11SlotInfo *slot = privKey->pkcs11Slot; + CK_OBJECT_HANDLE handle = privKey->pkcs11ID; + CK_OBJECT_HANDLE certID = + PK11_MatchItem(slot,handle,CKO_CERTIFICATE); + CERTCertificate *cert; + + if (certID == CK_INVALID_HANDLE) { + PORT_SetError(SSL_ERROR_NO_CERTIFICATE); + return NULL; + } + cert = PK11_MakeCertFromHandle(slot,certID,NULL); + return (cert); + +} + +/* + * destroy a private key if there are no matching certs. + * this function also frees the privKey structure. + */ +SECStatus +PK11_DeleteTokenPrivateKey(SECKEYPrivateKey *privKey, PRBool force) +{ + CERTCertificate *cert=PK11_GetCertFromPrivateKey(privKey); + + /* found a cert matching the private key?. */ + if (!force && cert != NULL) { + /* yes, don't delete the key */ + CERT_DestroyCertificate(cert); + SECKEY_DestroyPrivateKey(privKey); + return SECWouldBlock; + } + /* now, then it's safe for the key to go away */ + PK11_DestroyTokenObject(privKey->pkcs11Slot,privKey->pkcs11ID); + SECKEY_DestroyPrivateKey(privKey); + return SECSuccess; +} + +/* + * destroy a private key if there are no matching certs. + * this function also frees the privKey structure. + */ +SECStatus +PK11_DeleteTokenPublicKey(SECKEYPublicKey *pubKey) +{ + /* now, then it's safe for the key to go away */ + if (pubKey->pkcs11Slot == NULL) { + return SECFailure; + } + PK11_DestroyTokenObject(pubKey->pkcs11Slot,pubKey->pkcs11ID); + SECKEY_DestroyPublicKey(pubKey); + return SECSuccess; +} + + +/* + * delete a cert and it's private key (if no other certs are pointing to the + * private key. + */ +SECStatus +PK11_DeleteTokenCertAndKey(CERTCertificate *cert,void *wincx) +{ + SECKEYPrivateKey *privKey = PK11_FindKeyByAnyCert(cert,wincx); + CK_OBJECT_HANDLE pubKey; + PK11SlotInfo *slot = NULL; + + pubKey = pk11_FindPubKeyByAnyCert(cert, &slot, wincx); + if (privKey) { +#ifdef NSS_CLASSIC + PK11_DestroyTokenObject(cert->slot,cert->pkcs11ID); +#else + /* For 3.4, utilize the generic cert delete function */ + SEC_DeletePermCertificate(cert); +#endif + PK11_DeleteTokenPrivateKey(privKey, PR_FALSE); + } + if ((pubKey != CK_INVALID_HANDLE) && (slot != NULL)) { + PK11_DestroyTokenObject(slot,pubKey); + PK11_FreeSlot(slot); + } + return SECSuccess; +} + +/* + * count the number of objects that match the template. + */ +int +PK11_NumberObjectsFor(PK11SlotInfo *slot, CK_ATTRIBUTE *findTemplate, + int templateCount) +{ + CK_OBJECT_HANDLE objID[PK11_SEARCH_CHUNKSIZE]; + int object_count = 0; + CK_ULONG returned_count = 0; + CK_RV crv; + + PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_FindObjectsInit(slot->session, + findTemplate, templateCount); + if (crv != CKR_OK) { + PK11_ExitSlotMonitor(slot); + PORT_SetError( PK11_MapError(crv) ); + return 0; + } + + /* + * collect all the Matching Objects + */ + do { + crv = PK11_GETTAB(slot)->C_FindObjects(slot->session, + objID,PK11_SEARCH_CHUNKSIZE,&returned_count); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + break; + } + object_count += returned_count; + } while (returned_count == PK11_SEARCH_CHUNKSIZE); + + PK11_GETTAB(slot)->C_FindObjectsFinal(slot->session); + PK11_ExitSlotMonitor(slot); + return object_count; +} + +/* + * cert callback structure + */ +typedef struct pk11DoCertCallbackStr { + SECStatus(* callback)(PK11SlotInfo *slot, CERTCertificate*, void *); + SECStatus(* noslotcallback)(CERTCertificate*, void *); + SECStatus(* itemcallback)(CERTCertificate*, SECItem *, void *); + void *callbackArg; +} pk11DoCertCallback; + +#ifdef NSS_CLASSIC +/* + * callback to map object handles to certificate structures. + */ +static SECStatus +pk11_DoCerts(PK11SlotInfo *slot, CK_OBJECT_HANDLE certID, void *arg) +{ + CERTCertificate *cert; + pk11DoCertCallback *certcb = (pk11DoCertCallback *) arg; + + cert = PK11_MakeCertFromHandle(slot, certID, NULL); + + if (cert == NULL) { + return SECFailure; + } + + if (certcb ) { + if (certcb->callback) { + (*certcb->callback)(slot, cert, certcb->callbackArg); + } + if (certcb->noslotcallback) { + (*certcb->noslotcallback)(cert, certcb->callbackArg); + } + if (certcb->itemcallback) { + (*certcb->itemcallback)(cert, NULL, certcb->callbackArg); + } + } + + CERT_DestroyCertificate(cert); + + return SECSuccess; +} +#endif + +static SECStatus +pk11_CollectCrls(PK11SlotInfo *slot, CK_OBJECT_HANDLE crlID, void *arg) +{ + SECItem derCrl; + CERTCrlHeadNode *head = (CERTCrlHeadNode *) arg; + CERTCrlNode *new_node = NULL; + CK_ATTRIBUTE fetchCrl[3] = { + { CKA_VALUE, NULL, 0}, + { CKA_NETSCAPE_KRL, NULL, 0}, + { CKA_NETSCAPE_URL, NULL, 0}, + }; + const int fetchCrlSize = sizeof(fetchCrl)/sizeof(fetchCrl[2]); + CK_RV crv; + SECStatus rv = SECFailure; + + crv = PK11_GetAttributes(head->arena,slot,crlID,fetchCrl,fetchCrlSize); + if (CKR_OK != crv) { + PORT_SetError(PK11_MapError(crv)); + goto loser; + } + + if (!fetchCrl[1].pValue) { + PORT_SetError(SEC_ERROR_CRL_INVALID); + goto loser; + } + + new_node = (CERTCrlNode *)PORT_ArenaAlloc(head->arena, sizeof(CERTCrlNode)); + if (new_node == NULL) { + goto loser; + } + + if (*((CK_BBOOL *)fetchCrl[1].pValue)) + new_node->type = SEC_KRL_TYPE; + else + new_node->type = SEC_CRL_TYPE; + + derCrl.type = siBuffer; + derCrl.data = (unsigned char *)fetchCrl[0].pValue; + derCrl.len = fetchCrl[0].ulValueLen; + new_node->crl=CERT_DecodeDERCrl(head->arena,&derCrl,new_node->type); + if (new_node->crl == NULL) { + goto loser; + } + + if (fetchCrl[2].pValue) { + int nnlen = fetchCrl[2].ulValueLen; + new_node->crl->url = (char *)PORT_ArenaAlloc(head->arena, nnlen+1); + if ( !new_node->crl->url ) { + goto loser; + } + PORT_Memcpy(new_node->crl->url, fetchCrl[2].pValue, nnlen); + new_node->crl->url[nnlen] = 0; + } else { + new_node->crl->url = NULL; + } + + + new_node->next = NULL; + if (head->last) { + head->last->next = new_node; + head->last = new_node; + } else { + head->first = head->last = new_node; + } + rv = SECSuccess; + +loser: + return(rv); +} + + +/* + * key call back structure. + */ +typedef struct pk11KeyCallbackStr { + SECStatus (* callback)(SECKEYPrivateKey *,void *); + void *callbackArg; + void *wincx; +} pk11KeyCallback; + +/* + * callback to map Object Handles to Private Keys; + */ +SECStatus +pk11_DoKeys(PK11SlotInfo *slot, CK_OBJECT_HANDLE keyHandle, void *arg) +{ + SECStatus rv = SECSuccess; + SECKEYPrivateKey *privKey; + pk11KeyCallback *keycb = (pk11KeyCallback *) arg; + + privKey = PK11_MakePrivKey(slot,nullKey,PR_TRUE,keyHandle,keycb->wincx); + + if (privKey == NULL) { + return SECFailure; + } + + if (keycb && (keycb->callback)) { + rv = (*keycb->callback)(privKey,keycb->callbackArg); + } + + SECKEY_DestroyPrivateKey(privKey); + return rv; +} + +/* Traverse slots callback */ +typedef struct pk11TraverseSlotStr { + SECStatus (*callback)(PK11SlotInfo *,CK_OBJECT_HANDLE, void *); + void *callbackArg; + CK_ATTRIBUTE *findTemplate; + int templateCount; +} pk11TraverseSlot; + +/* + * Extract all the certs on a card from a slot. + */ +SECStatus +PK11_TraverseSlot(PK11SlotInfo *slot, void *arg) +{ + int i; + CK_OBJECT_HANDLE *objID = NULL; + int object_count = 0; + pk11TraverseSlot *slotcb = (pk11TraverseSlot*) arg; + + objID = pk11_FindObjectsByTemplate(slot,slotcb->findTemplate, + slotcb->templateCount,&object_count); + + /*Actually this isn't a failure... there just were no objs to be found*/ + if (object_count == 0) { + return SECSuccess; + } + + if (objID == NULL) { + return SECFailure; + } + + for (i=0; i < object_count; i++) { + (*slotcb->callback)(slot,objID[i],slotcb->callbackArg); + } + PORT_Free(objID); + return SECSuccess; +} + +typedef struct pk11CertCallbackStr { + SECStatus(* callback)(CERTCertificate*,SECItem *,void *); + void *callbackArg; +} pk11CertCallback; + +/* + * Extract all the certs on a card from a slot. + */ +SECStatus +pk11_TraverseAllSlots( SECStatus (*callback)(PK11SlotInfo *,void *), + void *arg,void *wincx) { + PK11SlotList *list; + PK11SlotListElement *le; + SECStatus rv; + + /* get them all! */ + list = PK11_GetAllTokens(CKM_INVALID_MECHANISM,PR_FALSE,PR_FALSE,wincx); + if (list == NULL) return SECFailure; + + /* look at each slot and authenticate as necessary */ + for (le = list->head ; le; le = le->next) { + if (!PK11_IsFriendly(le->slot)) { + rv = PK11_Authenticate(le->slot, PR_FALSE, wincx); + if (rv != SECSuccess) continue; + } + if (callback) { + (*callback)(le->slot,arg); + } + } + + PK11_FreeSlotList(list); + + return SECSuccess; +} + +struct fake_der_cb_argstr +{ + SECStatus(* callback)(CERTCertificate*, SECItem *, void *); + void *arg; +}; + +static SECStatus fake_der_cb(CERTCertificate *c, void *a) +{ + struct fake_der_cb_argstr *fda = (struct fake_der_cb_argstr *)a; + return (*fda->callback)(c, &c->derCert, fda->arg); +} + +/* + * Extract all the certs on a card from a slot. + */ +SECStatus +PK11_TraverseSlotCerts(SECStatus(* callback)(CERTCertificate*,SECItem *,void *), + void *arg, void *wincx) { +#ifdef NSS_CLASSIC + pk11DoCertCallback caller; + pk11TraverseSlot creater; + CK_ATTRIBUTE theTemplate; + CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; + + PK11_SETATTRS(&theTemplate, CKA_CLASS, &certClass, sizeof(certClass)); + + caller.callback = NULL; + caller.noslotcallback = NULL; + caller.itemcallback = callback; + caller.callbackArg = arg; + creater.callback = pk11_DoCerts; + creater.callbackArg = (void *) & caller; + creater.findTemplate = &theTemplate; + creater.templateCount = 1; + + return pk11_TraverseAllSlots(PK11_TraverseSlot, &creater, wincx); +#else + NSSTrustDomain *defaultTD = STAN_GetDefaultTrustDomain(); + struct fake_der_cb_argstr fda; + struct nss3_cert_cbstr pk11cb; + + /* authenticate to the tokens first */ + (void) pk11_TraverseAllSlots( NULL, NULL, wincx); + + fda.callback = callback; + fda.arg = arg; + pk11cb.callback = fake_der_cb; + pk11cb.arg = &fda; + NSSTrustDomain_TraverseCertificates(defaultTD, convert_cert, &pk11cb); + return SECSuccess; +#endif +} + +/* + * Extract all the certs on a card from a slot. + */ +SECStatus +PK11_LookupCrls(CERTCrlHeadNode *nodes, int type, void *wincx) { + pk11TraverseSlot creater; + CK_ATTRIBUTE theTemplate[2]; + CK_ATTRIBUTE *attrs; + CK_OBJECT_CLASS certClass = CKO_NETSCAPE_CRL; + + attrs = theTemplate; + PK11_SETATTRS(attrs, CKA_CLASS, &certClass, sizeof(certClass)); attrs++; + if (type != -1) { + CK_BBOOL isKrl = (CK_BBOOL) (type == SEC_KRL_TYPE); + PK11_SETATTRS(attrs, CKA_NETSCAPE_KRL, &isKrl, sizeof(isKrl)); attrs++; + } + + creater.callback = pk11_CollectCrls; + creater.callbackArg = (void *) nodes; + creater.findTemplate = theTemplate; + creater.templateCount = (attrs - theTemplate); + + return pk11_TraverseAllSlots(PK11_TraverseSlot, &creater, wincx); +} + +/*********************************************************************** + * PK11_TraversePrivateKeysInSlot + * + * Traverses all the private keys on a slot. + * + * INPUTS + * slot + * The PKCS #11 slot whose private keys you want to traverse. + * callback + * A callback function that will be called for each key. + * arg + * An argument that will be passed to the callback function. + */ +SECStatus +PK11_TraversePrivateKeysInSlot( PK11SlotInfo *slot, + SECStatus(* callback)(SECKEYPrivateKey*, void*), void *arg) +{ + pk11KeyCallback perKeyCB; + pk11TraverseSlot perObjectCB; + CK_OBJECT_CLASS privkClass = CKO_PRIVATE_KEY; + CK_BBOOL ckTrue = CK_TRUE; + CK_ATTRIBUTE theTemplate[2]; + int templateSize = 2; + + theTemplate[0].type = CKA_CLASS; + theTemplate[0].pValue = &privkClass; + theTemplate[0].ulValueLen = sizeof(privkClass); + theTemplate[1].type = CKA_TOKEN; + theTemplate[1].pValue = &ckTrue; + theTemplate[1].ulValueLen = sizeof(ckTrue); + + if(slot==NULL) { + return SECSuccess; + } + + perObjectCB.callback = pk11_DoKeys; + perObjectCB.callbackArg = &perKeyCB; + perObjectCB.findTemplate = theTemplate; + perObjectCB.templateCount = templateSize; + perKeyCB.callback = callback; + perKeyCB.callbackArg = arg; + perKeyCB.wincx = NULL; + + return PK11_TraverseSlot(slot, &perObjectCB); +} + +CK_OBJECT_HANDLE * +PK11_FindObjectsFromNickname(char *nickname,PK11SlotInfo **slotptr, + CK_OBJECT_CLASS objclass, int *returnCount, void *wincx) +{ + char *tokenName; + char *delimit; + PK11SlotInfo *slot; + CK_OBJECT_HANDLE *objID; + CK_ATTRIBUTE findTemplate[] = { + { CKA_LABEL, NULL, 0}, + { CKA_CLASS, NULL, 0}, + }; + int findCount = sizeof(findTemplate)/sizeof(findTemplate[0]); + SECStatus rv; + PK11_SETATTRS(&findTemplate[1], CKA_CLASS, &objclass, sizeof(objclass)); + + *slotptr = slot = NULL; + *returnCount = 0; + /* first find the slot associated with this nickname */ + if ((delimit = PORT_Strchr(nickname,':')) != NULL) { + int len = delimit - nickname; + tokenName = (char*)PORT_Alloc(len+1); + PORT_Memcpy(tokenName,nickname,len); + tokenName[len] = 0; + + slot = *slotptr = PK11_FindSlotByName(tokenName); + PORT_Free(tokenName); + /* if we couldn't find a slot, assume the nickname is an internal cert + * with no proceding slot name */ + if (slot == NULL) { + slot = *slotptr = PK11_GetInternalKeySlot(); + } else { + nickname = delimit+1; + } + } else { + *slotptr = slot = PK11_GetInternalKeySlot(); + } + if (slot == NULL) { + return CK_INVALID_HANDLE; + } + + if (!PK11_IsFriendly(slot)) { + rv = PK11_Authenticate(slot, PR_TRUE, wincx); + if (rv != SECSuccess) { + PK11_FreeSlot(slot); + *slotptr = NULL; + return CK_INVALID_HANDLE; + } + } + + findTemplate[0].pValue = nickname; + findTemplate[0].ulValueLen = PORT_Strlen(nickname); + objID = pk11_FindObjectsByTemplate(slot,findTemplate,findCount,returnCount); + if (objID == NULL) { + /* PKCS #11 isn't clear on whether or not the NULL is + * stored in the template.... try the find again with the + * full null terminated string. */ + findTemplate[0].ulValueLen += 1; + objID = pk11_FindObjectsByTemplate(slot,findTemplate,findCount, + returnCount); + if (objID == NULL) { + /* Well that's the best we can do. It's just not here */ + /* what about faked nicknames? */ + PK11_FreeSlot(slot); + *slotptr = NULL; + *returnCount = 0; + } + } + + return objID; +} + +static void +transfer_token_certs_to_collection(nssList *certList, NSSToken *token, + nssPKIObjectCollection *collection) +{ + NSSCertificate **certs; + PRUint32 i, count; + NSSToken **tokens, **tp; + count = nssList_Count(certList); + if (count == 0) { + return; + } + certs = nss_ZNEWARRAY(NULL, NSSCertificate *, count); + if (!certs) { + return; + } + nssList_GetArray(certList, (void **)certs, count); + for (i=0; i<count; i++) { + tokens = nssPKIObject_GetTokens(&certs[i]->object, NULL); + if (tokens) { + for (tp = tokens; *tp; tp++) { + if (*tp == token) { + nssPKIObjectCollection_AddObject(collection, + (nssPKIObject *)certs[i]); + } + } + nssTokenArray_Destroy(tokens); + } + /* *must* be a valid CERTCertificate, came from cache */ + CERT_DestroyCertificate(STAN_GetCERTCertificate(certs[i])); + } + nss_ZFreeIf(certs); +} + +CERTCertificate * +PK11_FindCertFromNickname(char *nickname, void *wincx) +{ +#ifdef NSS_CLASSIC + PK11SlotInfo *slot; + int count=0; + CK_OBJECT_HANDLE *certID = PK11_FindObjectsFromNickname(nickname,&slot, + CKO_CERTIFICATE, &count, wincx); + CERTCertificate *cert; + + if (certID == CK_INVALID_HANDLE) return NULL; + cert = PK11_MakeCertFromHandle(slot,certID[0],NULL); + PK11_FreeSlot(slot); + PORT_Free(certID); + return cert; +#else + PRStatus status; + CERTCertificate *rvCert = NULL; + NSSCertificate *cert = NULL; + NSSCertificate **certs = NULL; + NSSUsage usage; + NSSToken *token; + PK11SlotInfo *slot = NULL; + char *nickCopy; + char *delimit = NULL; + char *tokenName; + NSSTrustDomain *defaultTD = STAN_GetDefaultTrustDomain(); + usage.anyUsage = PR_TRUE; + nickCopy = PORT_Strdup(nickname); + if ((delimit = PORT_Strchr(nickCopy,':')) != NULL) { + tokenName = nickCopy; + nickname = delimit + 1; + *delimit = '\0'; + /* find token by name */ + token = NSSTrustDomain_FindTokenByName(defaultTD, (NSSUTF8 *)tokenName); + if (token) { + slot = PK11_ReferenceSlot(token->pk11slot); + } + *delimit = ':'; + } else { + slot = PK11_GetInternalKeySlot(); + token = PK11Slot_GetNSSToken(slot); + } + if (token) { + nssList *certList; + nssCryptokiObject **instances; + nssPKIObjectCollection *collection; + nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly; + if (!PK11_IsPresent(slot)) { + goto loser; + } + if (!PK11_IsFriendly(slot)) { + if (PK11_Authenticate(slot, PR_TRUE, wincx) != SECSuccess) { + goto loser; + } + } + collection = nssCertificateCollection_Create(defaultTD, NULL); + if (!collection) { + goto loser; + } + certList = nssList_Create(NULL, PR_FALSE); + if (!certList) { + nssPKIObjectCollection_Destroy(collection); + goto loser; + } + (void)nssTrustDomain_GetCertsForNicknameFromCache(defaultTD, + nickname, + certList); + transfer_token_certs_to_collection(certList, token, collection); + instances = nssToken_FindCertificatesByNickname(token, + NULL, + nickname, + tokenOnly, + 0, + &status); + nssPKIObjectCollection_AddInstances(collection, instances, 0); + nss_ZFreeIf(instances); + /* if it wasn't found, repeat the process for email address */ + if (nssPKIObjectCollection_Count(collection) == 0 && + PORT_Strchr(nickname, '@') != NULL) + { + char* lowercaseName = CERT_FixupEmailAddr(nickname); + if (lowercaseName) { + (void)nssTrustDomain_GetCertsForEmailAddressFromCache(defaultTD, + lowercaseName, + certList); + transfer_token_certs_to_collection(certList, token, collection); + instances = nssToken_FindCertificatesByEmail(token, + NULL, + lowercaseName, + tokenOnly, + 0, + &status); + nssPKIObjectCollection_AddInstances(collection, instances, 0); + nss_ZFreeIf(instances); + PORT_Free(lowercaseName); + } + } + certs = nssPKIObjectCollection_GetCertificates(collection, + NULL, 0, NULL); + nssPKIObjectCollection_Destroy(collection); + if (certs) { + cert = nssCertificateArray_FindBestCertificate(certs, NULL, + &usage, NULL); + if (cert) { + rvCert = STAN_GetCERTCertificate(cert); + } + nssCertificateArray_Destroy(certs); + } + nssList_Destroy(certList); + } + if (slot) { + PK11_FreeSlot(slot); + } + if (nickCopy) PORT_Free(nickCopy); + return rvCert; +loser: + if (slot) { + PK11_FreeSlot(slot); + } + if (nickCopy) PORT_Free(nickCopy); + return NULL; +#endif +} + +CERTCertList * +PK11_FindCertsFromNickname(char *nickname, void *wincx) { +#ifdef NSS_CLASSIC + PK11SlotInfo *slot; + int i,count = 0; + CK_OBJECT_HANDLE *certID = PK11_FindObjectsFromNickname(nickname,&slot, + CKO_CERTIFICATE, &count, wincx); + CERTCertList *certList = NULL; + + if (certID == NULL) return NULL; + + certList= CERT_NewCertList(); + + for (i=0; i < count; i++) { + CERTCertificate *cert = PK11_MakeCertFromHandle(slot,certID[i],NULL); + + if (cert) CERT_AddCertToListTail(certList,cert); + } + + if (CERT_LIST_HEAD(certList) == NULL) { + CERT_DestroyCertList(certList); + certList = NULL; + } + PK11_FreeSlot(slot); + PORT_Free(certID); + return certList; +#else + char *nickCopy; + char *delimit = NULL; + char *tokenName; + int i; + CERTCertList *certList = NULL; + nssPKIObjectCollection *collection = NULL; + NSSCertificate **foundCerts = NULL; + NSSTrustDomain *defaultTD = STAN_GetDefaultTrustDomain(); + NSSCertificate *c; + NSSToken *token; + PK11SlotInfo *slot; + nickCopy = PORT_Strdup(nickname); + if ((delimit = PORT_Strchr(nickCopy,':')) != NULL) { + tokenName = nickCopy; + nickname = delimit + 1; + *delimit = '\0'; + /* find token by name */ + token = NSSTrustDomain_FindTokenByName(defaultTD, (NSSUTF8 *)tokenName); + if (token) { + slot = PK11_ReferenceSlot(token->pk11slot); + } else { + slot = NULL; + } + *delimit = ':'; + } else { + slot = PK11_GetInternalKeySlot(); + token = PK11Slot_GetNSSToken(slot); + } + if (token) { + PRStatus status; + nssList *nameList; + nssCryptokiObject **instances; + nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly; + if (!PK11_IsFriendly(slot)) { + if (PK11_Authenticate(slot, PR_TRUE, wincx) != SECSuccess) { + PK11_FreeSlot(slot); + if (nickCopy) PORT_Free(nickCopy); + return NULL; + } + } + collection = nssCertificateCollection_Create(defaultTD, NULL); + if (!collection) { + PK11_FreeSlot(slot); + if (nickCopy) PORT_Free(nickCopy); + return NULL; + } + nameList = nssList_Create(NULL, PR_FALSE); + if (!nameList) { + PK11_FreeSlot(slot); + if (nickCopy) PORT_Free(nickCopy); + return NULL; + } + (void)nssTrustDomain_GetCertsForNicknameFromCache(defaultTD, + nickname, + nameList); + transfer_token_certs_to_collection(nameList, token, collection); + instances = nssToken_FindCertificatesByNickname(token, + NULL, + nickname, + tokenOnly, + 0, + &status); + nssPKIObjectCollection_AddInstances(collection, instances, 0); + nss_ZFreeIf(instances); + nssList_Destroy(nameList); + foundCerts = nssPKIObjectCollection_GetCertificates(collection, + NULL, 0, NULL); + nssPKIObjectCollection_Destroy(collection); + } + if (slot) { + PK11_FreeSlot(slot); + } + if (nickCopy) PORT_Free(nickCopy); + if (foundCerts) { + PRTime now = PR_Now(); + certList = CERT_NewCertList(); + for (i=0, c = *foundCerts; c; c = foundCerts[++i]) { + CERTCertificate *certCert = STAN_GetCERTCertificate(c); + if (certCert) { + CERT_AddCertToListSorted(certList, certCert, + CERT_SortCBValidity, &now); + } + } + if (CERT_LIST_HEAD(certList) == NULL) { + CERT_DestroyCertList(certList); + certList = NULL; + } + nss_ZFreeIf(foundCerts); + } + return certList; +#endif +} + +/* + * extract a key ID for a certificate... + * NOTE: We call this function from PKCS11.c If we ever use + * pkcs11 to extract the public key (we currently do not), this will break. + */ +SECItem * +PK11_GetPubIndexKeyID(CERTCertificate *cert) { + SECKEYPublicKey *pubk; + SECItem *newItem = NULL; + + pubk = CERT_ExtractPublicKey(cert); + if (pubk == NULL) return NULL; + + switch (pubk->keyType) { + case rsaKey: + newItem = SECITEM_DupItem(&pubk->u.rsa.modulus); + break; + case dsaKey: + newItem = SECITEM_DupItem(&pubk->u.dsa.publicValue); + break; + case dhKey: + newItem = SECITEM_DupItem(&pubk->u.dh.publicValue); + break; + case ecKey: +#ifdef NSS_ENABLE_ECC + newItem = SECITEM_DupItem(&pubk->u.ec.publicValue); +#endif /* NSS_ENABLE_ECC */ + break; + case fortezzaKey: + default: + newItem = NULL; /* Fortezza Fix later... */ + } + SECKEY_DestroyPublicKey(pubk); + /* make hash of it */ + return newItem; +} + +/* + * generate a CKA_ID from a certificate. + */ +SECItem * +pk11_mkcertKeyID(CERTCertificate *cert) { + SECItem *pubKeyData = PK11_GetPubIndexKeyID(cert) ; + SECItem *certCKA_ID; + + if (pubKeyData == NULL) return NULL; + + certCKA_ID = PK11_MakeIDFromPubKey(pubKeyData); + SECITEM_FreeItem(pubKeyData,PR_TRUE); + return certCKA_ID; +} + + +/* + * Generate a CKA_ID from the relevant public key data. The CKA_ID is generated + * from the pubKeyData by SHA1_Hashing it to produce a smaller CKA_ID (to make + * smart cards happy. + */ +SECItem * +PK11_MakeIDFromPubKey(SECItem *pubKeyData) { + PK11Context *context; + SECItem *certCKA_ID; + SECStatus rv; + + context = PK11_CreateDigestContext(SEC_OID_SHA1); + if (context == NULL) { + return NULL; + } + + rv = PK11_DigestBegin(context); + if (rv == SECSuccess) { + rv = PK11_DigestOp(context,pubKeyData->data,pubKeyData->len); + } + if (rv != SECSuccess) { + PK11_DestroyContext(context,PR_TRUE); + return NULL; + } + + certCKA_ID = (SECItem *)PORT_Alloc(sizeof(SECItem)); + if (certCKA_ID == NULL) { + PK11_DestroyContext(context,PR_TRUE); + return NULL; + } + + certCKA_ID->len = SHA1_LENGTH; + certCKA_ID->data = (unsigned char*)PORT_Alloc(certCKA_ID->len); + if (certCKA_ID->data == NULL) { + PORT_Free(certCKA_ID); + PK11_DestroyContext(context,PR_TRUE); + return NULL; + } + + rv = PK11_DigestFinal(context,certCKA_ID->data,&certCKA_ID->len, + SHA1_LENGTH); + PK11_DestroyContext(context,PR_TRUE); + if (rv != SECSuccess) { + SECITEM_FreeItem(certCKA_ID,PR_TRUE); + return NULL; + } + + return certCKA_ID; +} + +extern const NSSError NSS_ERROR_INVALID_CERTIFICATE; + +/* + * Write the cert into the token. + */ +SECStatus +PK11_ImportCert(PK11SlotInfo *slot, CERTCertificate *cert, + CK_OBJECT_HANDLE key, char *nickname, PRBool includeTrust) { +#ifdef NSS_CLASSIC + int len = 0; + SECItem *keyID = pk11_mkcertKeyID(cert); + CK_ATTRIBUTE keyAttrs[] = { + { CKA_LABEL, NULL, 0}, + { CKA_SUBJECT, NULL, 0}, + }; + CK_OBJECT_CLASS certc = CKO_CERTIFICATE; + CK_CERTIFICATE_TYPE certType = CKC_X_509; + CK_OBJECT_HANDLE certID; + CK_SESSION_HANDLE rwsession; + CK_BBOOL cktrue = CK_TRUE; + SECStatus rv = SECFailure; + CK_ATTRIBUTE certAttrs[] = { + { CKA_ID, NULL, 0 }, + { CKA_LABEL, NULL, 0}, + { CKA_CLASS, NULL, 0}, + { CKA_TOKEN, NULL, 0}, + { CKA_CERTIFICATE_TYPE, NULL, 0}, + { CKA_SUBJECT, NULL, 0}, + { CKA_ISSUER, NULL, 0}, + { CKA_SERIAL_NUMBER, NULL, 0}, + { CKA_VALUE, NULL, 0}, + { CKA_NETSCAPE_TRUST, NULL, 0}, + { CKA_NETSCAPE_EMAIL, NULL, 0}, + }; + int certCount = sizeof(certAttrs)/sizeof(certAttrs[0]), keyCount = 2; + int realCount = 0; + CK_ATTRIBUTE *attrs; + CK_RV crv; + SECCertUsage *certUsage = NULL; + SECItem derSerial = { 0 }; + NSSToken *token; + + if (keyID == NULL) { + PORT_SetError(SEC_ERROR_ADDING_CERT); + return rv; + } + + len = ((nickname) ? PORT_Strlen(nickname) : 0); + + attrs = certAttrs; + PK11_SETATTRS(attrs,CKA_ID, keyID->data, keyID->len); attrs++; + if (nickname) { + PK11_SETATTRS(attrs,CKA_LABEL, nickname, len ); attrs++; + } + PK11_SETATTRS(attrs,CKA_CLASS, &certc, sizeof(certc) ); attrs++; + PK11_SETATTRS(attrs,CKA_TOKEN, &cktrue, sizeof(cktrue) ); attrs++; + PK11_SETATTRS(attrs,CKA_CERTIFICATE_TYPE, &certType, + sizeof(certType)); attrs++; + PK11_SETATTRS(attrs,CKA_SUBJECT, cert->derSubject.data, + cert->derSubject.len ); attrs++; + PK11_SETATTRS(attrs,CKA_ISSUER, cert->derIssuer.data, + cert->derIssuer.len ); attrs++; + if (PR_TRUE) { + /* CERTCertificate stores serial numbers decoded. I need the DER + * here. sigh. + */ + CERT_SerialNumberFromDERCert(&cert->derCert, &derSerial); + PK11_SETATTRS(attrs,CKA_SERIAL_NUMBER, derSerial.data, derSerial.len); + attrs++; + } + PK11_SETATTRS(attrs,CKA_VALUE, cert->derCert.data, + cert->derCert.len); attrs++; + if (includeTrust && PK11_IsInternal(slot)) { + certUsage = (SECCertUsage*)PORT_Alloc(sizeof(SECCertUsage)); + if(!certUsage) { + SECITEM_FreeItem(keyID,PR_TRUE); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return rv; + } + *certUsage = certUsageUserCertImport; + PK11_SETATTRS(attrs,CKA_NETSCAPE_TRUST, certUsage, + sizeof(SECCertUsage)); + attrs++; + if (cert->emailAddr && cert->emailAddr[0]) { + PK11_SETATTRS(attrs,CKA_NETSCAPE_EMAIL, cert->emailAddr, + PORT_Strlen(cert->emailAddr); + attrs++; + } + } + realCount = attrs - certAttrs; + PORT_Assert(realCount <= certCount); + + attrs = keyAttrs; + if(nickname) { + PK11_SETATTRS(attrs,CKA_LABEL, nickname, len ); attrs++; + } + PK11_SETATTRS(attrs,CKA_SUBJECT, cert->derSubject.data, + cert->derSubject.len ); + + if(!nickname) { + certCount--; + keyCount--; + } + + rwsession = PK11_GetRWSession(slot); + if (key != CK_INVALID_HANDLE) { + crv = PK11_GETTAB(slot)->C_SetAttributeValue(rwsession,key,keyAttrs, + keyCount); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + goto done; + } + } + + crv = PK11_GETTAB(slot)-> + C_CreateObject(rwsession,certAttrs,realCount,&certID); + if (crv == CKR_OK) { + rv = SECSuccess; + } else { + PORT_SetError( PK11_MapError(crv) ); + } + + if (!cert->nickname && nickname) { + cert->nickname = PORT_ArenaStrdup(cert->arena, nickname); + } + + cert->pkcs11ID = certID; + cert->dbhandle = STAN_GetDefaultTrustDomain(); + if (cert->slot == NULL) { + cert->slot = PK11_ReferenceSlot(slot); + cert->series = slot->series; + cert->ownSlot = PR_TRUE; + if (cert->nssCertificate) { + nssCryptokiInstance *instance; + NSSCertificate *c = cert->nssCertificate; + instance = nss_ZNEW(c->object.arena, nssCryptokiInstance); + instance->token = slot->nssToken; + instance->handle = cert->pkcs11ID; + instance->isTokenObject = PR_TRUE; + nssPKIObject_AddInstance(&c->object, instance); + } else { + cert->nssCertificate = STAN_GetNSSCertificate(cert); + } + } + cert->trust = nssTrust_GetCERTCertTrustForCert(cert->nssCertificate, cert); + token = PK11Slot_GetNSSToken(slot); + +done: + if (derSerial.data) PORT_Free(derSerial.data); + SECITEM_FreeItem(keyID,PR_TRUE); + PK11_RestoreROSession(slot,rwsession); + if(certUsage) { + PORT_Free(certUsage); + } + return rv; +#else + PRStatus status; + NSSCertificate *c; + nssCryptokiObject *keyobj, *certobj; + NSSToken *token = PK11Slot_GetNSSToken(slot); + SECItem *keyID = pk11_mkcertKeyID(cert); + char *emailAddr = NULL; + + if (keyID == NULL) { + goto loser; + } + + if (PK11_IsInternal(slot) && cert->emailAddr && cert->emailAddr[0]) { + emailAddr = cert->emailAddr; + } + + /* need to get the cert as a stan cert */ + if (cert->nssCertificate) { + c = cert->nssCertificate; + } else { + c = STAN_GetNSSCertificate(cert); + } + + if (c->object.cryptoContext) { + /* Delete the temp instance */ + NSSCryptoContext *cc = c->object.cryptoContext; + nssCertificateStore_Lock(cc->certStore); + nssCertificateStore_RemoveCertLOCKED(cc->certStore, c); + nssCertificateStore_Unlock(cc->certStore); + c->object.cryptoContext = NULL; + cert->istemp = PR_FALSE; + cert->isperm = PR_TRUE; + } + + /* set the id for the cert */ + nssItem_Create(c->object.arena, &c->id, keyID->len, keyID->data); + if (!c->id.data) { + goto loser; + } + + if (key != CK_INVALID_HANDLE) { + /* create an object for the key, ... */ + keyobj = nss_ZNEW(NULL, nssCryptokiObject); + if (!keyobj) { + goto loser; + } + keyobj->token = nssToken_AddRef(token); + keyobj->handle = key; + keyobj->isTokenObject = PR_TRUE; + + /* ... in order to set matching attributes for the key */ + status = nssCryptokiPrivateKey_SetCertificate(keyobj, NULL, nickname, + &c->id, &c->subject); + nssCryptokiObject_Destroy(keyobj); + if (status != PR_SUCCESS) { + goto loser; + } + } + + /* do the token import */ + certobj = nssToken_ImportCertificate(token, NULL, + NSSCertificateType_PKIX, + &c->id, + nickname, + &c->encoding, + &c->issuer, + &c->subject, + &c->serial, + emailAddr, + PR_TRUE); + if (!certobj) { + if (NSS_GetError() == NSS_ERROR_INVALID_CERTIFICATE) { + PORT_SetError(SEC_ERROR_REUSED_ISSUER_AND_SERIAL); + SECITEM_FreeItem(keyID,PR_TRUE); + return SECFailure; + } + goto loser; + } + /* add the new instance to the cert, force an update of the + * CERTCertificate, and finish + */ + nssPKIObject_AddInstance(&c->object, certobj); + nssTrustDomain_AddCertsToCache(STAN_GetDefaultTrustDomain(), &c, 1); + (void)STAN_ForceCERTCertificateUpdate(c); + SECITEM_FreeItem(keyID,PR_TRUE); + return SECSuccess; +loser: + SECITEM_FreeItem(keyID,PR_TRUE); + PORT_SetError(SEC_ERROR_ADDING_CERT); + return SECFailure; +#endif +} + +SECStatus +PK11_ImportDERCert(PK11SlotInfo *slot, SECItem *derCert, + CK_OBJECT_HANDLE key, char *nickname, PRBool includeTrust) { + CERTCertificate *cert; + SECStatus rv; + + cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), + derCert, NULL, PR_FALSE, PR_TRUE); + if (cert == NULL) return SECFailure; + + rv = PK11_ImportCert(slot, cert, key, nickname, includeTrust); + CERT_DestroyCertificate (cert); + return rv; +} + +/* + * get a certificate handle, look at the cached handle first.. + */ +CK_OBJECT_HANDLE +pk11_getcerthandle(PK11SlotInfo *slot, CERTCertificate *cert, + CK_ATTRIBUTE *theTemplate,int tsize) +{ + CK_OBJECT_HANDLE certh; + + if (cert->slot == slot) { + certh = cert->pkcs11ID; + if ((certh == CK_INVALID_HANDLE) || + (cert->series != slot->series)) { + certh = pk11_FindObjectByTemplate(slot,theTemplate,tsize); + cert->pkcs11ID = certh; + cert->series = slot->series; + } + } else { + certh = pk11_FindObjectByTemplate(slot,theTemplate,tsize); + } + return certh; +} + +/* + * return the private key From a given Cert + */ +SECKEYPrivateKey * +PK11_FindPrivateKeyFromCert(PK11SlotInfo *slot, CERTCertificate *cert, + void *wincx) { + CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; + CK_ATTRIBUTE theTemplate[] = { + { CKA_VALUE, NULL, 0 }, + { CKA_CLASS, NULL, 0 } + }; + /* if you change the array, change the variable below as well */ + int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]); + CK_OBJECT_HANDLE certh; + CK_OBJECT_HANDLE keyh; + CK_ATTRIBUTE *attrs = theTemplate; + SECStatus rv; + + PK11_SETATTRS(attrs, CKA_VALUE, cert->derCert.data, + cert->derCert.len); attrs++; + PK11_SETATTRS(attrs, CKA_CLASS, &certClass, sizeof(certClass)); + + /* + * issue the find + */ + rv = PK11_Authenticate(slot, PR_TRUE, wincx); + if (rv != SECSuccess) { + return NULL; + } + + certh = pk11_getcerthandle(slot,cert,theTemplate,tsize); + if (certh == CK_INVALID_HANDLE) { + return NULL; + } + keyh = PK11_MatchItem(slot,certh,CKO_PRIVATE_KEY); + if (keyh == CK_INVALID_HANDLE) { return NULL; } + return PK11_MakePrivKey(slot, nullKey, PR_TRUE, keyh, wincx); +} + + +/* + * return the private key with the given ID + */ +static CK_OBJECT_HANDLE +pk11_FindPrivateKeyFromCertID(PK11SlotInfo *slot, SECItem *keyID) { + CK_OBJECT_CLASS privKey = CKO_PRIVATE_KEY; + CK_ATTRIBUTE theTemplate[] = { + { CKA_ID, NULL, 0 }, + { CKA_CLASS, NULL, 0 }, + }; + /* if you change the array, change the variable below as well */ + int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]); + CK_ATTRIBUTE *attrs = theTemplate; + + PK11_SETATTRS(attrs, CKA_ID, keyID->data, keyID->len ); attrs++; + PK11_SETATTRS(attrs, CKA_CLASS, &privKey, sizeof(privKey)); + + return pk11_FindObjectByTemplate(slot,theTemplate,tsize); +} + +/* + * import a cert for a private key we have already generated. Set the label + * on both to be the nickname. This is for the Key Gen, orphaned key case. + */ +PK11SlotInfo * +PK11_KeyForCertExists(CERTCertificate *cert, CK_OBJECT_HANDLE *keyPtr, + void *wincx) { + PK11SlotList *list; + PK11SlotListElement *le; + SECItem *keyID; + CK_OBJECT_HANDLE key; + PK11SlotInfo *slot = NULL; + SECStatus rv; + + keyID = pk11_mkcertKeyID(cert); + /* get them all! */ + list = PK11_GetAllTokens(CKM_INVALID_MECHANISM,PR_FALSE,PR_TRUE,wincx); + if ((keyID == NULL) || (list == NULL)) { + if (keyID) SECITEM_FreeItem(keyID,PR_TRUE); + if (list) PK11_FreeSlotList(list); + return NULL; + } + + /* Look for the slot that holds the Key */ + for (le = list->head ; le; le = le->next) { + rv = PK11_Authenticate(le->slot, PR_TRUE, wincx); + if (rv != SECSuccess) continue; + + key = pk11_FindPrivateKeyFromCertID(le->slot,keyID); + if (key != CK_INVALID_HANDLE) { + slot = PK11_ReferenceSlot(le->slot); + if (keyPtr) *keyPtr = key; + break; + } + } + + SECITEM_FreeItem(keyID,PR_TRUE); + PK11_FreeSlotList(list); + return slot; + +} +/* + * import a cert for a private key we have already generated. Set the label + * on both to be the nickname. This is for the Key Gen, orphaned key case. + */ +PK11SlotInfo * +PK11_KeyForDERCertExists(SECItem *derCert, CK_OBJECT_HANDLE *keyPtr, + void *wincx) { + CERTCertificate *cert; + PK11SlotInfo *slot = NULL; + + /* letting this use go -- the only thing that the cert is used for is + * to get the ID attribute. + */ + cert = CERT_DecodeDERCertificate(derCert, PR_FALSE, NULL); + if (cert == NULL) return NULL; + + slot = PK11_KeyForCertExists(cert, keyPtr, wincx); + CERT_DestroyCertificate (cert); + return slot; +} + +PK11SlotInfo * +PK11_ImportCertForKey(CERTCertificate *cert, char *nickname,void *wincx) { + PK11SlotInfo *slot = NULL; + CK_OBJECT_HANDLE key; + + slot = PK11_KeyForCertExists(cert,&key,wincx); + + if (slot) { + if (PK11_ImportCert(slot,cert,key,nickname,PR_FALSE) != SECSuccess) { + PK11_FreeSlot(slot); + slot = NULL; + } + } else { + PORT_SetError(SEC_ERROR_ADDING_CERT); + } + + return slot; +} + +PK11SlotInfo * +PK11_ImportDERCertForKey(SECItem *derCert, char *nickname,void *wincx) { + CERTCertificate *cert; + PK11SlotInfo *slot = NULL; + + cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), + derCert, NULL, PR_FALSE, PR_TRUE); + if (cert == NULL) return NULL; + + slot = PK11_ImportCertForKey(cert, nickname, wincx); + CERT_DestroyCertificate (cert); + return slot; +} + +static CK_OBJECT_HANDLE +pk11_FindCertObjectByTemplate(PK11SlotInfo **slotPtr, + CK_ATTRIBUTE *searchTemplate, int count, void *wincx) { + PK11SlotList *list; + PK11SlotListElement *le; + CK_OBJECT_HANDLE certHandle = CK_INVALID_HANDLE; + PK11SlotInfo *slot = NULL; + SECStatus rv; + + *slotPtr = NULL; + + /* get them all! */ + list = PK11_GetAllTokens(CKM_INVALID_MECHANISM,PR_FALSE,PR_TRUE,wincx); + if (list == NULL) { + if (list) PK11_FreeSlotList(list); + return CK_INVALID_HANDLE; + } + + + /* Look for the slot that holds the Key */ + for (le = list->head ; le; le = le->next) { + if (!PK11_IsFriendly(le->slot)) { + rv = PK11_Authenticate(le->slot, PR_TRUE, wincx); + if (rv != SECSuccess) continue; + } + + certHandle = pk11_FindObjectByTemplate(le->slot,searchTemplate,count); + if (certHandle != CK_INVALID_HANDLE) { + slot = PK11_ReferenceSlot(le->slot); + break; + } + } + + PK11_FreeSlotList(list); + + if (slot == NULL) { + return CK_INVALID_HANDLE; + } + *slotPtr = slot; + return certHandle; +} + +CERTCertificate * +PK11_FindCertByIssuerAndSNOnToken(PK11SlotInfo *slot, + CERTIssuerAndSN *issuerSN, void *wincx) +{ + CERTCertificate *rvCert = NULL; + NSSCertificate *cert = NULL; + NSSDER issuer, serial; + NSSTrustDomain *td = STAN_GetDefaultTrustDomain(); + NSSToken *token = slot->nssToken; + nssSession *session; + nssCryptokiObject *instance = NULL; + nssPKIObject *object = NULL; + SECItem *derSerial; + PRStatus status; + + /* Paranoia */ + if (token == NULL) { + PORT_SetError(SEC_ERROR_NO_TOKEN); + return NULL; + } + + + /* PKCS#11 needs to use DER-encoded serial numbers. Create a + * CERTIssuerAndSN that actually has the encoded value and pass that + * to PKCS#11 (and the crypto context). + */ + derSerial = SEC_ASN1EncodeItem(NULL, NULL, + &issuerSN->serialNumber, + SEC_IntegerTemplate); + if (!derSerial) { + return NULL; + } + + NSSITEM_FROM_SECITEM(&issuer, &issuerSN->derIssuer); + NSSITEM_FROM_SECITEM(&serial, derSerial); + + session = nssToken_GetDefaultSession(token); + if (!session) { + goto loser; + } + + instance = nssToken_FindCertificateByIssuerAndSerialNumber(token,session, + &issuer, &serial, nssTokenSearchType_TokenForced, &status); + + SECITEM_FreeItem(derSerial, PR_TRUE); + + if (!instance) { + goto loser; + } + object = nssPKIObject_Create(NULL, instance, td, NULL); + if (!object) { + goto loser; + } + instance = NULL; /* adopted by the previous call */ + cert = nssCertificate_Create(object); + if (!cert) { + goto loser; + } + object = NULL; /* adopted by the previous call */ + nssTrustDomain_AddCertsToCache(td, &cert,1); + rvCert = STAN_GetCERTCertificate(cert); + if (!rvCert) { + goto loser; + } + return rvCert; + +loser: + if (instance) { + nssCryptokiObject_Destroy(instance); + } + if (object) { + nssPKIObject_Destroy(object); + } + if (cert) { + nssCertificate_Destroy(cert); + } + return NULL; +} + +/* + * We're looking for a cert which we have the private key for that's on the + * list of recipients. This searches one slot. + * this is the new version for NSS SMIME code + * this stuff should REALLY be in the SMIME code, but some things in here are not public + * (they should be!) + */ +static CERTCertificate * +pk11_FindCertObjectByRecipientNew(PK11SlotInfo *slot, NSSCMSRecipient **recipientlist, int *rlIndex, void *pwarg) +{ + NSSCMSRecipient *ri = NULL; + int i; + + for (i=0; (ri = recipientlist[i]) != NULL; i++) { + CERTCertificate *cert = NULL; + if (ri->kind == RLSubjKeyID) { + SECItem *derCert = cert_FindDERCertBySubjectKeyID(ri->id.subjectKeyID); + if (derCert) { + cert = PK11_FindCertFromDERCertItem(slot, derCert, pwarg); + SECITEM_FreeItem(derCert, PR_TRUE); + } + } else { + cert = PK11_FindCertByIssuerAndSNOnToken(slot, ri->id.issuerAndSN, + pwarg); + } + if (cert) { + /* this isn't our cert */ + if ((cert->trust == NULL) || + ((cert->trust->emailFlags & CERTDB_USER) != CERTDB_USER)) { + CERT_DestroyCertificate(cert); + continue; + } + ri->slot = PK11_ReferenceSlot(slot); + *rlIndex = i; + return cert; + } + } + *rlIndex = -1; + return NULL; +} + +/* + * This function is the same as above, but it searches all the slots. + * this is the new version for NSS SMIME code + * this stuff should REALLY be in the SMIME code, but some things in here are not public + * (they should be!) + */ +static CERTCertificate * +pk11_AllFindCertObjectByRecipientNew(NSSCMSRecipient **recipientlist, void *wincx, int *rlIndex) +{ + PK11SlotList *list; + PK11SlotListElement *le; + CERTCertificate *cert = NULL; + SECStatus rv; + + /* get them all! */ + list = PK11_GetAllTokens(CKM_INVALID_MECHANISM,PR_FALSE,PR_TRUE,wincx); + if (list == NULL) { + if (list) PK11_FreeSlotList(list); + return CK_INVALID_HANDLE; + } + + /* Look for the slot that holds the Key */ + for (le = list->head ; le; le = le->next) { + if ( !PK11_IsFriendly(le->slot)) { + rv = PK11_Authenticate(le->slot, PR_TRUE, wincx); + if (rv != SECSuccess) continue; + } + + cert = pk11_FindCertObjectByRecipientNew(le->slot, + recipientlist, rlIndex, wincx); + if (cert) + break; + } + + PK11_FreeSlotList(list); + + return cert; +} + +/* + * We're looking for a cert which we have the private key for that's on the + * list of recipients. This searches one slot. + */ +static CERTCertificate * +pk11_FindCertObjectByRecipient(PK11SlotInfo *slot, + SEC_PKCS7RecipientInfo **recipientArray, + SEC_PKCS7RecipientInfo **rip, void *pwarg) +{ + SEC_PKCS7RecipientInfo *ri = NULL; + int i; + + for (i=0; (ri = recipientArray[i]) != NULL; i++) { + CERTCertificate *cert; + + cert = PK11_FindCertByIssuerAndSNOnToken(slot, ri->issuerAndSN, + pwarg); + if (cert) { + /* this isn't our cert */ + if ((cert->trust == NULL) || + ((cert->trust->emailFlags & CERTDB_USER) != CERTDB_USER)) { + CERT_DestroyCertificate(cert); + continue; + } + *rip = ri; + return cert; + } + + } + *rip = NULL; + return NULL; +} + +/* + * This function is the same as above, but it searches all the slots. + */ +static CERTCertificate * +pk11_AllFindCertObjectByRecipient(PK11SlotInfo **slotPtr, + SEC_PKCS7RecipientInfo **recipientArray,SEC_PKCS7RecipientInfo **rip, + void *wincx) { + PK11SlotList *list; + PK11SlotListElement *le; + CERTCertificate * cert = NULL; + PK11SlotInfo *slot = NULL; + SECStatus rv; + + *slotPtr = NULL; + + /* get them all! */ + list = PK11_GetAllTokens(CKM_INVALID_MECHANISM,PR_FALSE,PR_TRUE,wincx); + if (list == NULL) { + if (list) PK11_FreeSlotList(list); + return CK_INVALID_HANDLE; + } + + *rip = NULL; + + /* Look for the slot that holds the Key */ + for (le = list->head ; le; le = le->next) { + if ( !PK11_IsFriendly(le->slot)) { + rv = PK11_Authenticate(le->slot, PR_TRUE, wincx); + if (rv != SECSuccess) continue; + } + + cert = pk11_FindCertObjectByRecipient(le->slot, recipientArray, + rip, wincx); + if (cert) { + slot = PK11_ReferenceSlot(le->slot); + break; + } + } + + PK11_FreeSlotList(list); + + if (slot == NULL) { + return NULL; + } + *slotPtr = slot; + PORT_Assert(cert != NULL); + return cert; +} + +/* + * We need to invert the search logic for PKCS 7 because if we search for + * each cert on the list over all the slots, we wind up with lots of spurious + * password prompts. This way we get only one password prompt per slot, at + * the max, and most of the time we can find the cert, and only prompt for + * the key... + */ +CERTCertificate * +PK11_FindCertAndKeyByRecipientList(PK11SlotInfo **slotPtr, + SEC_PKCS7RecipientInfo **array, SEC_PKCS7RecipientInfo **rip, + SECKEYPrivateKey**privKey, void *wincx) +{ + CERTCertificate *cert = NULL; + SECStatus rv; + + *privKey = NULL; + *slotPtr = NULL; + cert = pk11_AllFindCertObjectByRecipient(slotPtr,array,rip,wincx); + if (!cert) { + return NULL; + } + + rv = PK11_Authenticate(*slotPtr,PR_TRUE,wincx); + if (rv != SECSuccess) { + goto loser; + } + + *privKey = PK11_FindKeyByAnyCert(cert, wincx); + if (*privKey == NULL) { + goto loser; + } + + return cert; +loser: + if (cert) CERT_DestroyCertificate(cert); + if (*slotPtr) PK11_FreeSlot(*slotPtr); + *slotPtr = NULL; + return NULL; +} + +static PRCallOnceType keyIDHashCallOnce; + +static PRStatus PR_CALLBACK +pk11_keyIDHash_populate(void *wincx) +{ + CERTCertList *certList; + CERTCertListNode *node = NULL; + SECItem subjKeyID = {siBuffer, NULL, 0}; + + certList = PK11_ListCerts(PK11CertListUser, wincx); + if (!certList) { + return PR_FAILURE; + } + + for (node = CERT_LIST_HEAD(certList); + !CERT_LIST_END(node, certList); + node = CERT_LIST_NEXT(node)) { + if (CERT_FindSubjectKeyIDExtension(node->cert, + &subjKeyID) == SECSuccess && + subjKeyID.data != NULL) { + cert_AddSubjectKeyIDMapping(&subjKeyID, node->cert); + SECITEM_FreeItem(&subjKeyID, PR_FALSE); + } + } + CERT_DestroyCertList(certList); + return PR_SUCCESS; +} + +/* + * This is the new version of the above function for NSS SMIME code + * this stuff should REALLY be in the SMIME code, but some things in here are not public + * (they should be!) + */ +int +PK11_FindCertAndKeyByRecipientListNew(NSSCMSRecipient **recipientlist, void *wincx) +{ + CERTCertificate *cert; + NSSCMSRecipient *rl; + PRStatus rv; + int rlIndex; + + rv = PR_CallOnceWithArg(&keyIDHashCallOnce, pk11_keyIDHash_populate, wincx); + if (rv != PR_SUCCESS) + return -1; + + cert = pk11_AllFindCertObjectByRecipientNew(recipientlist, wincx, &rlIndex); + if (!cert) { + return -1; + } + + rl = recipientlist[rlIndex]; + + /* at this point, rl->slot is set */ + + /* authenticate to the token */ + if (PK11_Authenticate(rl->slot, PR_TRUE, wincx) != SECSuccess) { + goto loser; + } + + rl->privkey = PK11_FindKeyByAnyCert(cert, wincx); + if (rl->privkey == NULL) { + goto loser; + } + + /* make a cert from the cert handle */ + rl->cert = cert; + return rlIndex; + +loser: + if (cert) CERT_DestroyCertificate(cert); + if (rl->slot) PK11_FreeSlot(rl->slot); + rl->slot = NULL; + return -1; +} + +CERTCertificate * +PK11_FindCertByIssuerAndSN(PK11SlotInfo **slotPtr, CERTIssuerAndSN *issuerSN, + void *wincx) +{ +#ifdef NSS_CLASSIC + CK_OBJECT_HANDLE certHandle; + CERTCertificate *cert = NULL; + CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; + CK_ATTRIBUTE searchTemplate[] = { + { CKA_CLASS, NULL, 0 }, + { CKA_ISSUER, NULL, 0 }, + { CKA_SERIAL_NUMBER, NULL, 0} + }; + int count = sizeof(searchTemplate)/sizeof(CK_ATTRIBUTE); + CK_ATTRIBUTE *attrs = searchTemplate; + + PK11_SETATTRS(attrs, CKA_CLASS, &certClass, sizeof(certClass)); attrs++; + PK11_SETATTRS(attrs, CKA_ISSUER, issuerSN->derIssuer.data, + issuerSN->derIssuer.len); attrs++; + PK11_SETATTRS(attrs, CKA_SERIAL_NUMBER, issuerSN->serialNumber.data, + issuerSN->serialNumber.len); + + certHandle = pk11_FindCertObjectByTemplate + (slotPtr,searchTemplate,count,wincx); + if (certHandle == CK_INVALID_HANDLE) { + return NULL; + } + cert = PK11_MakeCertFromHandle(*slotPtr,certHandle,NULL); + if (cert == NULL) { + PK11_FreeSlot(*slotPtr); + return NULL; + } + return cert; +#else + CERTCertificate *rvCert = NULL; + NSSCertificate *cert; + NSSDER issuer, serial; + NSSCryptoContext *cc; + SECItem *derSerial; + + if (slotPtr) *slotPtr = NULL; + + /* PKCS#11 needs to use DER-encoded serial numbers. Create a + * CERTIssuerAndSN that actually has the encoded value and pass that + * to PKCS#11 (and the crypto context). + */ + derSerial = SEC_ASN1EncodeItem(NULL, NULL, + &issuerSN->serialNumber, + SEC_IntegerTemplate); + if (!derSerial) { + return NULL; + } + + NSSITEM_FROM_SECITEM(&issuer, &issuerSN->derIssuer); + NSSITEM_FROM_SECITEM(&serial, derSerial); + + cc = STAN_GetDefaultCryptoContext(); + cert = NSSCryptoContext_FindCertificateByIssuerAndSerialNumber(cc, + &issuer, + &serial); + if (cert) { + SECITEM_FreeItem(derSerial, PR_TRUE); + return STAN_GetCERTCertificate(cert); + } +retry: + cert = NSSTrustDomain_FindCertificateByIssuerAndSerialNumber( + STAN_GetDefaultTrustDomain(), + &issuer, + &serial); + if (cert) { + rvCert = STAN_GetCERTCertificate(cert); + /* Check to see if the cert's token is still there */ + if (!PK11_IsPresent(rvCert->slot)) { + CERT_DestroyCertificate(rvCert); + goto retry; + } + if (slotPtr) *slotPtr = PK11_ReferenceSlot(rvCert->slot); + } + SECITEM_FreeItem(derSerial, PR_TRUE); + return rvCert; +#endif +} + +CK_OBJECT_HANDLE +PK11_FindObjectForCert(CERTCertificate *cert, void *wincx, PK11SlotInfo **pSlot) +{ + CK_OBJECT_HANDLE certHandle; + CK_ATTRIBUTE searchTemplate = { CKA_VALUE, NULL, 0 }; + + PK11_SETATTRS(&searchTemplate, CKA_VALUE, cert->derCert.data, + cert->derCert.len); + + if (cert->slot) { + certHandle = pk11_getcerthandle(cert->slot,cert,&searchTemplate,1); + if (certHandle != CK_INVALID_HANDLE) { + *pSlot = PK11_ReferenceSlot(cert->slot); + return certHandle; + } + } + + certHandle = pk11_FindCertObjectByTemplate(pSlot,&searchTemplate,1,wincx); + if (certHandle != CK_INVALID_HANDLE) { + if (cert->slot == NULL) { + cert->slot = PK11_ReferenceSlot(*pSlot); + cert->pkcs11ID = certHandle; + cert->ownSlot = PR_TRUE; + cert->series = cert->slot->series; + } + } + + return(certHandle); +} + +SECKEYPrivateKey * +PK11_FindKeyByAnyCert(CERTCertificate *cert, void *wincx) +{ + CK_OBJECT_HANDLE certHandle; + CK_OBJECT_HANDLE keyHandle; + PK11SlotInfo *slot = NULL; + SECKEYPrivateKey *privKey; + SECStatus rv; + + certHandle = PK11_FindObjectForCert(cert, wincx, &slot); + if (certHandle == CK_INVALID_HANDLE) { + return NULL; + } + rv = PK11_Authenticate(slot, PR_TRUE, wincx); + if (rv != SECSuccess) { + PK11_FreeSlot(slot); + return NULL; + } + keyHandle = PK11_MatchItem(slot,certHandle,CKO_PRIVATE_KEY); + if (keyHandle == CK_INVALID_HANDLE) { + PK11_FreeSlot(slot); + return NULL; + } + privKey = PK11_MakePrivKey(slot, nullKey, PR_TRUE, keyHandle, wincx); + PK11_FreeSlot(slot); + return privKey; +} + +CK_OBJECT_HANDLE +pk11_FindPubKeyByAnyCert(CERTCertificate *cert, PK11SlotInfo **slot, void *wincx) +{ + CK_OBJECT_HANDLE certHandle; + CK_OBJECT_HANDLE keyHandle; + + certHandle = PK11_FindObjectForCert(cert, wincx, slot); + if (certHandle == CK_INVALID_HANDLE) { + return CK_INVALID_HANDLE; + } + keyHandle = PK11_MatchItem(*slot,certHandle,CKO_PUBLIC_KEY); + if (keyHandle == CK_INVALID_HANDLE) { + PK11_FreeSlot(*slot); + return CK_INVALID_HANDLE; + } + return keyHandle; +} + +SECKEYPrivateKey * +PK11_FindKeyByKeyID(PK11SlotInfo *slot, SECItem *keyID, void *wincx) +{ + CK_OBJECT_HANDLE keyHandle; + SECKEYPrivateKey *privKey; + + keyHandle = pk11_FindPrivateKeyFromCertID(slot, keyID); + if (keyHandle == CK_INVALID_HANDLE) { + return NULL; + } + privKey = PK11_MakePrivKey(slot, nullKey, PR_TRUE, keyHandle, wincx); + return privKey; +} + +/* + * find the number of certs in the slot with the same subject name + */ +int +PK11_NumberCertsForCertSubject(CERTCertificate *cert) +{ + CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; + CK_ATTRIBUTE theTemplate[] = { + { CKA_CLASS, NULL, 0 }, + { CKA_SUBJECT, NULL, 0 }, + }; + CK_ATTRIBUTE *attr = theTemplate; + int templateSize = sizeof(theTemplate)/sizeof(theTemplate[0]); + + PK11_SETATTRS(attr,CKA_CLASS, &certClass, sizeof(certClass)); attr++; + PK11_SETATTRS(attr,CKA_SUBJECT,cert->derSubject.data,cert->derSubject.len); + + if (cert->slot == NULL) { + PK11SlotList *list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, + PR_FALSE,PR_TRUE,NULL); + PK11SlotListElement *le; + int count = 0; + + /* loop through all the fortezza tokens */ + for (le = list->head; le; le = le->next) { + count += PK11_NumberObjectsFor(le->slot,theTemplate,templateSize); + } + PK11_FreeSlotList(list); + return count; + } + + return PK11_NumberObjectsFor(cert->slot,theTemplate,templateSize); +} + +/* + * Walk all the certs with the same subject + */ +SECStatus +PK11_TraverseCertsForSubject(CERTCertificate *cert, + SECStatus(* callback)(CERTCertificate*, void *), void *arg) +{ + if(!cert) { + return SECFailure; + } + if (cert->slot == NULL) { + PK11SlotList *list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, + PR_FALSE,PR_TRUE,NULL); + PK11SlotListElement *le; + + /* loop through all the fortezza tokens */ + for (le = list->head; le; le = le->next) { + PK11_TraverseCertsForSubjectInSlot(cert,le->slot,callback,arg); + } + PK11_FreeSlotList(list); + return SECSuccess; + + } + + return PK11_TraverseCertsForSubjectInSlot(cert, cert->slot, callback, arg); +} + +SECStatus +PK11_TraverseCertsForSubjectInSlot(CERTCertificate *cert, PK11SlotInfo *slot, + SECStatus(* callback)(CERTCertificate*, void *), void *arg) +{ +#ifdef NSS_CLASSIC + pk11DoCertCallback caller; + pk11TraverseSlot callarg; + CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; + CK_ATTRIBUTE theTemplate[] = { + { CKA_CLASS, NULL, 0 }, + { CKA_SUBJECT, NULL, 0 }, + }; + CK_ATTRIBUTE *attr = theTemplate; + int templateSize = sizeof(theTemplate)/sizeof(theTemplate[0]); + + PK11_SETATTRS(attr,CKA_CLASS, &certClass, sizeof(certClass)); attr++; + PK11_SETATTRS(attr,CKA_SUBJECT,cert->derSubject.data,cert->derSubject.len); + + if (slot == NULL) { + return SECSuccess; + } + caller.noslotcallback = callback; + caller.callback = NULL; + caller.itemcallback = NULL; + caller.callbackArg = arg; + callarg.callback = pk11_DoCerts; + callarg.callbackArg = (void *) & caller; + callarg.findTemplate = theTemplate; + callarg.templateCount = templateSize; + + return PK11_TraverseSlot(slot, &callarg); +#else + PRStatus nssrv = PR_SUCCESS; + NSSToken *token; + NSSDER subject; + NSSTrustDomain *td; + nssList *subjectList; + nssPKIObjectCollection *collection; + nssCryptokiObject **instances; + NSSCertificate **certs; + nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly; + td = STAN_GetDefaultTrustDomain(); + NSSITEM_FROM_SECITEM(&subject, &cert->derSubject); + token = PK11Slot_GetNSSToken(slot); + if (!nssToken_IsPresent(token)) { + return SECSuccess; + } + collection = nssCertificateCollection_Create(td, NULL); + if (!collection) { + return SECFailure; + } + subjectList = nssList_Create(NULL, PR_FALSE); + if (!subjectList) { + nssPKIObjectCollection_Destroy(collection); + return SECFailure; + } + (void)nssTrustDomain_GetCertsForSubjectFromCache(td, &subject, + subjectList); + transfer_token_certs_to_collection(subjectList, token, collection); + instances = nssToken_FindCertificatesBySubject(token, NULL, + &subject, + tokenOnly, 0, &nssrv); + nssPKIObjectCollection_AddInstances(collection, instances, 0); + nss_ZFreeIf(instances); + nssList_Destroy(subjectList); + certs = nssPKIObjectCollection_GetCertificates(collection, + NULL, 0, NULL); + nssPKIObjectCollection_Destroy(collection); + if (certs) { + CERTCertificate *oldie; + NSSCertificate **cp; + for (cp = certs; *cp; cp++) { + oldie = STAN_GetCERTCertificate(*cp); + if ((*callback)(oldie, arg) != SECSuccess) { + nssrv = PR_FAILURE; + break; + } + } + nssCertificateArray_Destroy(certs); + } + return (nssrv == PR_SUCCESS) ? SECSuccess : SECFailure; +#endif +} + +SECStatus +PK11_TraverseCertsForNicknameInSlot(SECItem *nickname, PK11SlotInfo *slot, + SECStatus(* callback)(CERTCertificate*, void *), void *arg) +{ +#ifdef NSS_CLASSIC + pk11DoCertCallback caller; + pk11TraverseSlot callarg; + CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; + CK_ATTRIBUTE theTemplate[] = { + { CKA_CLASS, NULL, 0 }, + { CKA_LABEL, NULL, 0 }, + }; + CK_ATTRIBUTE *attr = theTemplate; + int templateSize = sizeof(theTemplate)/sizeof(theTemplate[0]); + + if(!nickname) { + return SECSuccess; + } + + PK11_SETATTRS(attr,CKA_CLASS, &certClass, sizeof(certClass)); attr++; + PK11_SETATTRS(attr,CKA_LABEL,nickname->data,nickname->len); + + if (slot == NULL) { + return SECSuccess; + } + + caller.noslotcallback = callback; + caller.callback = NULL; + caller.itemcallback = NULL; + caller.callbackArg = arg; + callarg.callback = pk11_DoCerts; + callarg.callbackArg = (void *) & caller; + callarg.findTemplate = theTemplate; + callarg.templateCount = templateSize; + + return PK11_TraverseSlot(slot, &callarg); +#else + struct nss3_cert_cbstr pk11cb; + PRStatus nssrv = PR_SUCCESS; + NSSToken *token; + NSSTrustDomain *td; + NSSUTF8 *nick; + PRBool created = PR_FALSE; + nssCryptokiObject **instances; + nssPKIObjectCollection *collection = NULL; + NSSCertificate **certs; + nssList *nameList = NULL; + nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly; + pk11cb.callback = callback; + pk11cb.arg = arg; + token = PK11Slot_GetNSSToken(slot); + if (!nssToken_IsPresent(token)) { + return SECSuccess; + } + if (nickname->data[nickname->len-1] != '\0') { + nick = nssUTF8_Create(NULL, nssStringType_UTF8String, + nickname->data, nickname->len); + created = PR_TRUE; + } else { + nick = (NSSUTF8 *)nickname->data; + } + td = STAN_GetDefaultTrustDomain(); + collection = nssCertificateCollection_Create(td, NULL); + if (!collection) { + goto loser; + } + nameList = nssList_Create(NULL, PR_FALSE); + if (!nameList) { + goto loser; + } + (void)nssTrustDomain_GetCertsForNicknameFromCache(td, nick, nameList); + transfer_token_certs_to_collection(nameList, token, collection); + instances = nssToken_FindCertificatesByNickname(token, NULL, + nick, + tokenOnly, 0, &nssrv); + nssPKIObjectCollection_AddInstances(collection, instances, 0); + nss_ZFreeIf(instances); + nssList_Destroy(nameList); + certs = nssPKIObjectCollection_GetCertificates(collection, + NULL, 0, NULL); + nssPKIObjectCollection_Destroy(collection); + if (certs) { + CERTCertificate *oldie; + NSSCertificate **cp; + for (cp = certs; *cp; cp++) { + oldie = STAN_GetCERTCertificate(*cp); + if ((*callback)(oldie, arg) != SECSuccess) { + nssrv = PR_FAILURE; + break; + } + } + nssCertificateArray_Destroy(certs); + } + if (created) nss_ZFreeIf(nick); + return (nssrv == PR_SUCCESS) ? SECSuccess : SECFailure; +loser: + if (created) { + nss_ZFreeIf(nick); + } + if (collection) { + nssPKIObjectCollection_Destroy(collection); + } + if (nameList) { + nssList_Destroy(nameList); + } + return SECFailure; +#endif +} + +SECStatus +PK11_TraverseCertsInSlot(PK11SlotInfo *slot, + SECStatus(* callback)(CERTCertificate*, void *), void *arg) +{ +#ifdef NSS_CLASSIC + pk11DoCertCallback caller; + pk11TraverseSlot callarg; + CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; + CK_ATTRIBUTE theTemplate[] = { + { CKA_CLASS, NULL, 0 }, + }; + CK_ATTRIBUTE *attr = theTemplate; + int templateSize = sizeof(theTemplate)/sizeof(theTemplate[0]); + + PK11_SETATTRS(attr,CKA_CLASS, &certClass, sizeof(certClass)); attr++; + + if (slot == NULL) { + return SECSuccess; + } + + caller.noslotcallback = callback; + caller.callback = NULL; + caller.itemcallback = NULL; + caller.callbackArg = arg; + callarg.callback = pk11_DoCerts; + callarg.callbackArg = (void *) & caller; + callarg.findTemplate = theTemplate; + callarg.templateCount = templateSize; + return PK11_TraverseSlot(slot, &callarg); +#else + PRStatus nssrv; + NSSTrustDomain *td = STAN_GetDefaultTrustDomain(); + NSSToken *tok; + nssList *certList = NULL; + nssCryptokiObject **instances; + nssPKIObjectCollection *collection; + NSSCertificate **certs; + nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly; + tok = PK11Slot_GetNSSToken(slot); + if (!nssToken_IsPresent(tok)) { + return SECSuccess; + } + collection = nssCertificateCollection_Create(td, NULL); + if (!collection) { + return SECFailure; + } + certList = nssList_Create(NULL, PR_FALSE); + if (!certList) { + nssPKIObjectCollection_Destroy(collection); + return SECFailure; + } + (void *)nssTrustDomain_GetCertsFromCache(td, certList); + transfer_token_certs_to_collection(certList, tok, collection); + instances = nssToken_FindCertificates(tok, NULL, + tokenOnly, 0, &nssrv); + nssPKIObjectCollection_AddInstances(collection, instances, 0); + nss_ZFreeIf(instances); + nssList_Destroy(certList); + certs = nssPKIObjectCollection_GetCertificates(collection, + NULL, 0, NULL); + nssPKIObjectCollection_Destroy(collection); + if (certs) { + CERTCertificate *oldie; + NSSCertificate **cp; + for (cp = certs; *cp; cp++) { + oldie = STAN_GetCERTCertificate(*cp); + if ((*callback)(oldie, arg) != SECSuccess) { + nssrv = PR_FAILURE; + break; + } + } + nssCertificateArray_Destroy(certs); + } + return (nssrv == PR_SUCCESS) ? SECSuccess : SECFailure; +#endif +} + +/* + * return the certificate associated with a derCert + */ +CERTCertificate * +PK11_FindCertFromDERCert(PK11SlotInfo *slot, CERTCertificate *cert, + void *wincx) +{ + return PK11_FindCertFromDERCertItem(slot, &cert->derCert, wincx); +} + +CERTCertificate * +PK11_FindCertFromDERCertItem(PK11SlotInfo *slot, SECItem *inDerCert, + void *wincx) + +{ + CERTCertificate *rvCert = NULL; + NSSCertificate *c; + NSSDER derCert; + NSSToken *tok; + NSSTrustDomain *td = STAN_GetDefaultTrustDomain(); + tok = PK11Slot_GetNSSToken(slot); + NSSITEM_FROM_SECITEM(&derCert, inDerCert); + if (!PK11_IsFriendly(slot)) { + if (PK11_Authenticate(slot, PR_TRUE, wincx) != SECSuccess) { + PK11_FreeSlot(slot); + return NULL; + } + } + c = NSSTrustDomain_FindCertificateByEncodedCertificate(td, &derCert); + if (c) { + PRBool isToken = PR_FALSE; + NSSToken **tp; + NSSToken **tokens = nssPKIObject_GetTokens(&c->object, NULL); + if (tokens) { + for (tp = tokens; *tp; tp++) { + if (*tp == tok) { + isToken = PR_TRUE; + break; + } + } + if (!isToken) { + NSSCertificate_Destroy(c); + c = NULL; + } + nssTokenArray_Destroy(tokens); + } + } + if (c) { + rvCert = STAN_GetCERTCertificate(c); + } + return rvCert; +} + +/* mcgreer 3.4 -- nobody uses this, ignoring */ +/* + * return the certificate associated with a derCert + */ +CERTCertificate * +PK11_FindCertFromDERSubjectAndNickname(PK11SlotInfo *slot, + CERTCertificate *cert, + char *nickname, void *wincx) +{ + CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; + CK_ATTRIBUTE theTemplate[] = { + { CKA_SUBJECT, NULL, 0 }, + { CKA_LABEL, NULL, 0 }, + { CKA_CLASS, NULL, 0 } + }; + /* if you change the array, change the variable below as well */ + int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]); + CK_OBJECT_HANDLE certh; + CK_ATTRIBUTE *attrs = theTemplate; + SECStatus rv; + + PK11_SETATTRS(attrs, CKA_SUBJECT, cert->derSubject.data, + cert->derSubject.len); attrs++; + PK11_SETATTRS(attrs, CKA_LABEL, nickname, PORT_Strlen(nickname)); + PK11_SETATTRS(attrs, CKA_CLASS, &certClass, sizeof(certClass)); + + /* + * issue the find + */ + if ( !PK11_IsFriendly(slot)) { + rv = PK11_Authenticate(slot, PR_TRUE, wincx); + if (rv != SECSuccess) return NULL; + } + + certh = pk11_getcerthandle(slot,cert,theTemplate,tsize); + if (certh == CK_INVALID_HANDLE) { + return NULL; + } + + return PK11_MakeCertFromHandle(slot, certh, NULL); +} + +/* + * import a cert for a private key we have already generated. Set the label + * on both to be the nickname. + */ +static CK_OBJECT_HANDLE +pk11_findKeyObjectByDERCert(PK11SlotInfo *slot, CERTCertificate *cert, + void *wincx) +{ + SECItem *keyID; + CK_OBJECT_HANDLE key; + SECStatus rv; + + if((slot == NULL) || (cert == NULL)) { + return CK_INVALID_HANDLE; + } + + keyID = pk11_mkcertKeyID(cert); + if(keyID == NULL) { + return CK_INVALID_HANDLE; + } + + key = CK_INVALID_HANDLE; + + rv = PK11_Authenticate(slot, PR_TRUE, wincx); + if (rv != SECSuccess) goto loser; + + key = pk11_FindPrivateKeyFromCertID(slot, keyID); + +loser: + SECITEM_ZfreeItem(keyID, PR_TRUE); + return key; +} + +SECKEYPrivateKey * +PK11_FindKeyByDERCert(PK11SlotInfo *slot, CERTCertificate *cert, + void *wincx) +{ + CK_OBJECT_HANDLE keyHandle; + + if((slot == NULL) || (cert == NULL)) { + return NULL; + } + + keyHandle = pk11_findKeyObjectByDERCert(slot, cert, wincx); + if (keyHandle == CK_INVALID_HANDLE) { + return NULL; + } + + return PK11_MakePrivKey(slot,nullKey,PR_TRUE,keyHandle,wincx); +} + +SECStatus +PK11_ImportCertForKeyToSlot(PK11SlotInfo *slot, CERTCertificate *cert, + char *nickname, + PRBool addCertUsage,void *wincx) +{ + CK_OBJECT_HANDLE keyHandle; + + if((slot == NULL) || (cert == NULL) || (nickname == NULL)) { + return SECFailure; + } + + keyHandle = pk11_findKeyObjectByDERCert(slot, cert, wincx); + if (keyHandle == CK_INVALID_HANDLE) { + return SECFailure; + } + + return PK11_ImportCert(slot, cert, keyHandle, nickname, addCertUsage); +} + + +/* remove when the real version comes out */ +#define SEC_OID_MISSI_KEA 300 /* until we have v3 stuff merged */ +PRBool +KEAPQGCompare(CERTCertificate *server,CERTCertificate *cert) { + + if ( SECKEY_KEAParamCompare(server,cert) == SECEqual ) { + return PR_TRUE; + } else { + return PR_FALSE; + } +} + +PRBool +PK11_FortezzaHasKEA(CERTCertificate *cert) { + /* look at the subject and see if it is a KEA for MISSI key */ + SECOidData *oid; + + if ((cert->trust == NULL) || + ((cert->trust->sslFlags & CERTDB_USER) != CERTDB_USER)) { + return PR_FALSE; + } + + oid = SECOID_FindOID(&cert->subjectPublicKeyInfo.algorithm.algorithm); + + + return (PRBool)((oid->offset == SEC_OID_MISSI_KEA_DSS_OLD) || + (oid->offset == SEC_OID_MISSI_KEA_DSS) || + (oid->offset == SEC_OID_MISSI_KEA)) ; +} + +/* + * Find a kea cert on this slot that matches the domain of it's peer + */ +static CERTCertificate +*pk11_GetKEAMate(PK11SlotInfo *slot,CERTCertificate *peer) +{ + int i; + CERTCertificate *returnedCert = NULL; + + for (i=0; i < slot->cert_count; i++) { + CERTCertificate *cert = slot->cert_array[i]; + + if (PK11_FortezzaHasKEA(cert) && KEAPQGCompare(peer,cert)) { + returnedCert = CERT_DupCertificate(cert); + break; + } + } + return returnedCert; +} + +/* + * The following is a FORTEZZA only Certificate request. We call this when we + * are doing a non-client auth SSL connection. We are only interested in the + * fortezza slots, and we are only interested in certs that share the same root + * key as the server. + */ +CERTCertificate * +PK11_FindBestKEAMatch(CERTCertificate *server, void *wincx) +{ + PK11SlotList *keaList = PK11_GetAllTokens(CKM_KEA_KEY_DERIVE, + PR_FALSE,PR_TRUE,wincx); + PK11SlotListElement *le; + CERTCertificate *returnedCert = NULL; + SECStatus rv; + + /* loop through all the fortezza tokens */ + for (le = keaList->head; le; le = le->next) { + rv = PK11_Authenticate(le->slot, PR_TRUE, wincx); + if (rv != SECSuccess) continue; + if (le->slot->session == CK_INVALID_SESSION) { + continue; + } + returnedCert = pk11_GetKEAMate(le->slot,server); + if (returnedCert) break; + } + PK11_FreeSlotList(keaList); + + return returnedCert; +} + +/* + * find a matched pair of kea certs to key exchange parameters from one + * fortezza card to another as necessary. + */ +SECStatus +PK11_GetKEAMatchedCerts(PK11SlotInfo *slot1, PK11SlotInfo *slot2, + CERTCertificate **cert1, CERTCertificate **cert2) +{ + CERTCertificate *returnedCert = NULL; + int i; + + for (i=0; i < slot1->cert_count; i++) { + CERTCertificate *cert = slot1->cert_array[i]; + + if (PK11_FortezzaHasKEA(cert)) { + returnedCert = pk11_GetKEAMate(slot2,cert); + if (returnedCert != NULL) { + *cert2 = returnedCert; + *cert1 = CERT_DupCertificate(cert); + return SECSuccess; + } + } + } + return SECFailure; +} + +SECOidTag +PK11_FortezzaMapSig(SECOidTag algTag) +{ + switch (algTag) { + case SEC_OID_MISSI_KEA_DSS: + case SEC_OID_MISSI_DSS: + case SEC_OID_MISSI_DSS_OLD: + case SEC_OID_MISSI_KEA_DSS_OLD: + case SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST: + return SEC_OID_ANSIX9_DSA_SIGNATURE; + default: + break; + } + return algTag; +} + +/* + * return the private key From a given Cert + */ +CK_OBJECT_HANDLE +PK11_FindCertInSlot(PK11SlotInfo *slot, CERTCertificate *cert, void *wincx) +{ + CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; + CK_ATTRIBUTE theTemplate[] = { + { CKA_VALUE, NULL, 0 }, + { CKA_CLASS, NULL, 0 } + }; + /* if you change the array, change the variable below as well */ + int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]); + CK_ATTRIBUTE *attrs = theTemplate; + SECStatus rv; + + PK11_SETATTRS(attrs, CKA_VALUE, cert->derCert.data, + cert->derCert.len); attrs++; + PK11_SETATTRS(attrs, CKA_CLASS, &certClass, sizeof(certClass)); + + /* + * issue the find + */ + rv = PK11_Authenticate(slot, PR_TRUE, wincx); + if (rv != SECSuccess) { + return CK_INVALID_HANDLE; + } + + return pk11_getcerthandle(slot,cert,theTemplate,tsize); +} + +SECItem * +PK11_GetKeyIDFromCert(CERTCertificate *cert, void *wincx) +{ + CK_OBJECT_HANDLE handle; + PK11SlotInfo *slot = NULL; + CK_ATTRIBUTE theTemplate[] = { + { CKA_ID, NULL, 0 }, + }; + int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]); + SECItem *item = NULL; + CK_RV crv; + + handle = PK11_FindObjectForCert(cert,wincx,&slot); + if (handle == CK_INVALID_HANDLE) { + goto loser; + } + + + crv = PK11_GetAttributes(NULL,slot,handle,theTemplate,tsize); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + goto loser; + } + + item = PORT_ZNew(SECItem); + if (item) { + item->data = (unsigned char*) theTemplate[0].pValue; + item->len = theTemplate[0].ulValueLen; + } + + +loser: + PK11_FreeSlot(slot); + return item; +} + +SECItem * +PK11_GetKeyIDFromPrivateKey(SECKEYPrivateKey *key, void *wincx) +{ + CK_ATTRIBUTE theTemplate[] = { + { CKA_ID, NULL, 0 }, + }; + int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]); + SECItem *item = NULL; + CK_RV crv; + + crv = PK11_GetAttributes(NULL,key->pkcs11Slot,key->pkcs11ID, + theTemplate,tsize); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + goto loser; + } + + item = PORT_ZNew(SECItem); + if (item) { + item->data = (unsigned char*) theTemplate[0].pValue; + item->len = theTemplate[0].ulValueLen; + } + + +loser: + return item; +} + +struct listCertsStr { + PK11CertListType type; + CERTCertList *certList; +}; + +static PRStatus +pk11ListCertCallback(NSSCertificate *c, void *arg) +{ + struct listCertsStr *listCertP = (struct listCertsStr *)arg; + CERTCertificate *newCert = NULL; + PK11CertListType type = listCertP->type; + CERTCertList *certList = listCertP->certList; + PRBool isUnique = PR_FALSE; + PRBool isCA = PR_FALSE; + char *nickname = NULL; + unsigned int certType; + + if ((type == PK11CertListUnique) || (type == PK11CertListRootUnique) || + (type == PK11CertListCAUnique) || (type == PK11CertListUserUnique) ) { + /* only list one instance of each certificate, even if several exist */ + isUnique = PR_TRUE; + } + if ((type == PK11CertListCA) || (type == PK11CertListRootUnique) || + (type == PK11CertListCAUnique)) { + isCA = PR_TRUE; + } + + /* if we want user certs and we don't have one skip this cert */ + if ( ( (type == PK11CertListUser) || (type == PK11CertListUserUnique) ) && + !NSSCertificate_IsPrivateKeyAvailable(c, NULL,NULL)) { + return PR_SUCCESS; + } + + /* PK11CertListRootUnique means we want CA certs without a private key. + * This is for legacy app support . PK11CertListCAUnique should be used + * instead to get all CA certs, regardless of private key + */ + if ((type == PK11CertListRootUnique) && + NSSCertificate_IsPrivateKeyAvailable(c, NULL,NULL)) { + return PR_SUCCESS; + } + + newCert = STAN_GetCERTCertificate(c); + if (!newCert) { + return PR_SUCCESS; + } + /* if we want CA certs and it ain't one, skip it */ + if( isCA && (!CERT_IsCACert(newCert, &certType)) ) { + return PR_SUCCESS; + } + if (isUnique) { + CERT_DupCertificate(newCert); + + nickname = STAN_GetCERTCertificateName(certList->arena, c); + + /* put slot certs at the end */ + if (newCert->slot && !PK11_IsInternal(newCert->slot)) { + CERT_AddCertToListTailWithData(certList,newCert,nickname); + } else { + CERT_AddCertToListHeadWithData(certList,newCert,nickname); + } + } else { + /* add multiple instances to the cert list */ + nssCryptokiObject **ip; + nssCryptokiObject **instances = nssPKIObject_GetInstances(&c->object); + if (!instances) { + return PR_SUCCESS; + } + for (ip = instances; *ip; ip++) { + nssCryptokiObject *instance = *ip; + PK11SlotInfo *slot = instance->token->pk11slot; + + /* put the same CERTCertificate in the list for all instances */ + CERT_DupCertificate(newCert); + + nickname = STAN_GetCERTCertificateNameForInstance( + certList->arena, c, instance); + + /* put slot certs at the end */ + if (slot && !PK11_IsInternal(slot)) { + CERT_AddCertToListTailWithData(certList,newCert,nickname); + } else { + CERT_AddCertToListHeadWithData(certList,newCert,nickname); + } + } + nssCryptokiObjectArray_Destroy(instances); + } + return PR_SUCCESS; +} + + +CERTCertList * +PK11_ListCerts(PK11CertListType type, void *pwarg) +{ +#ifdef NSS_CLASSIC + CERTCertList *certList = NULL; + struct listCertsStr listCerts; + + certList= CERT_NewCertList(); + listCerts.type = type; + listCerts.certList = certList; + + PK11_TraverseSlotCerts(pk11ListCertCallback,&listCerts,pwarg); + + if (CERT_LIST_HEAD(certList) == NULL) { + CERT_DestroyCertList(certList); + certList = NULL; + } + return certList; +#else + NSSTrustDomain *defaultTD = STAN_GetDefaultTrustDomain(); + CERTCertList *certList = NULL; + struct listCertsStr listCerts; + certList = CERT_NewCertList(); + listCerts.type = type; + listCerts.certList = certList; + + /* authenticate to the slots */ + (void) pk11_TraverseAllSlots( NULL, NULL, pwarg); + NSSTrustDomain_TraverseCertificates(defaultTD, pk11ListCertCallback, + &listCerts); + return certList; +#endif +} + +static SECItem * +pk11_GetLowLevelKeyFromHandle(PK11SlotInfo *slot, CK_OBJECT_HANDLE handle) { + CK_ATTRIBUTE theTemplate[] = { + { CKA_ID, NULL, 0 }, + }; + int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]); + CK_RV crv; + SECItem *item; + + item = SECITEM_AllocItem(NULL, NULL, 0); + + if (item == NULL) { + return NULL; + } + + crv = PK11_GetAttributes(NULL,slot,handle,theTemplate,tsize); + if (crv != CKR_OK) { + SECITEM_FreeItem(item,PR_TRUE); + PORT_SetError( PK11_MapError(crv) ); + return NULL; + } + + item->data = (unsigned char*) theTemplate[0].pValue; + item->len =theTemplate[0].ulValueLen; + + return item; +} + +SECItem * +PK11_GetLowLevelKeyIDForCert(PK11SlotInfo *slot, + CERTCertificate *cert, void *wincx) +{ + CK_ATTRIBUTE theTemplate[] = { + { CKA_VALUE, NULL, 0 }, + { CKA_CLASS, NULL, 0 } + }; + /* if you change the array, change the variable below as well */ + int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]); + CK_OBJECT_HANDLE certHandle; + CK_ATTRIBUTE *attrs = theTemplate; + PK11SlotInfo *slotRef = NULL; + SECItem *item; + SECStatus rv; + + if (slot) { + PK11_SETATTRS(attrs, CKA_VALUE, cert->derCert.data, + cert->derCert.len); attrs++; + + rv = PK11_Authenticate(slot, PR_TRUE, wincx); + if (rv != SECSuccess) { + return NULL; + } + certHandle = pk11_getcerthandle(slot,cert,theTemplate,tsize); + } else { + certHandle = PK11_FindObjectForCert(cert, wincx, &slotRef); + if (certHandle == CK_INVALID_HANDLE) { + return pk11_mkcertKeyID(cert); + } + slot = slotRef; + } + + if (certHandle == CK_INVALID_HANDLE) { + return NULL; + } + + item = pk11_GetLowLevelKeyFromHandle(slot,certHandle); + if (slotRef) PK11_FreeSlot(slotRef); + return item; +} + +SECItem * +PK11_GetLowLevelKeyIDForPrivateKey(SECKEYPrivateKey *privKey) +{ + return pk11_GetLowLevelKeyFromHandle(privKey->pkcs11Slot,privKey->pkcs11ID); +} + +/* argument type for listCertsCallback */ +typedef struct { + CERTCertList *list; + PK11SlotInfo *slot; +} ListCertsArg; + +static SECStatus +listCertsCallback(CERTCertificate* cert, void*arg) +{ + ListCertsArg *cdata = (ListCertsArg*)arg; + char *nickname = NULL; + nssCryptokiObject *instance, **ci; + nssCryptokiObject **instances; + NSSCertificate *c = STAN_GetNSSCertificate(cert); + + instances = nssPKIObject_GetInstances(&c->object); + instance = NULL; + for (ci = instances; *ci; ci++) { + if ((*ci)->token->pk11slot == cdata->slot) { + instance = *ci; + break; + } + } + PORT_Assert(instance != NULL); + if (!instance) { + nssCryptokiObjectArray_Destroy(instances); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + nickname = STAN_GetCERTCertificateNameForInstance(cdata->list->arena, + c, instance); + nssCryptokiObjectArray_Destroy(instances); + + return CERT_AddCertToListTailWithData(cdata->list, + CERT_DupCertificate(cert),nickname); +} + +CERTCertList * +PK11_ListCertsInSlot(PK11SlotInfo *slot) +{ + SECStatus status; + CERTCertList *certs; + ListCertsArg cdata; + + certs = CERT_NewCertList(); + if(certs == NULL) return NULL; + cdata.list = certs; + cdata.slot = slot; + + status = PK11_TraverseCertsInSlot(slot, listCertsCallback, + &cdata); + + if( status != SECSuccess ) { + CERT_DestroyCertList(certs); + certs = NULL; + } + + return certs; +} + +static SECStatus +privateKeyListCallback(SECKEYPrivateKey *key, void *arg) +{ + SECKEYPrivateKeyList *list = (SECKEYPrivateKeyList*)arg; + return SECKEY_AddPrivateKeyToListTail(list, SECKEY_CopyPrivateKey(key)); +} + +SECKEYPrivateKeyList* +PK11_ListPrivateKeysInSlot(PK11SlotInfo *slot) +{ + SECStatus status; + SECKEYPrivateKeyList *keys; + + keys = SECKEY_NewPrivateKeyList(); + if(keys == NULL) return NULL; + + status = PK11_TraversePrivateKeysInSlot(slot, privateKeyListCallback, + (void*)keys); + + if( status != SECSuccess ) { + SECKEY_DestroyPrivateKeyList(keys); + keys = NULL; + } + + return keys; +} + +SECKEYPublicKeyList* +PK11_ListPublicKeysInSlot(PK11SlotInfo *slot, char *nickname) +{ + CK_ATTRIBUTE findTemp[4]; + CK_ATTRIBUTE *attrs; + CK_BBOOL ckTrue = CK_TRUE; + CK_OBJECT_CLASS keyclass = CKO_PUBLIC_KEY; + int tsize = 0; + int objCount = 0; + CK_OBJECT_HANDLE *key_ids; + SECKEYPublicKeyList *keys; + int i,len; + + + attrs = findTemp; + PK11_SETATTRS(attrs, CKA_CLASS, &keyclass, sizeof(keyclass)); attrs++; + PK11_SETATTRS(attrs, CKA_TOKEN, &ckTrue, sizeof(ckTrue)); attrs++; + if (nickname) { + len = PORT_Strlen(nickname)-1; + PK11_SETATTRS(attrs, CKA_LABEL, nickname, len); attrs++; + } + tsize = attrs - findTemp; + PORT_Assert(tsize <= sizeof(findTemp)/sizeof(CK_ATTRIBUTE)); + + key_ids = pk11_FindObjectsByTemplate(slot,findTemp,tsize,&objCount); + if (key_ids == NULL) { + return NULL; + } + keys = SECKEY_NewPublicKeyList(); + if (keys == NULL) { + PORT_Free(key_ids); + } + + for (i=0; i < objCount ; i++) { + SECKEYPublicKey *pubKey = + PK11_ExtractPublicKey(slot,nullKey,key_ids[i]); + if (pubKey) { + SECKEY_AddPublicKeyToListTail(keys, pubKey); + } + } + + PORT_Free(key_ids); + return keys; +} + +SECKEYPrivateKeyList* +PK11_ListPrivKeysInSlot(PK11SlotInfo *slot, char *nickname, void *wincx) +{ + CK_ATTRIBUTE findTemp[4]; + CK_ATTRIBUTE *attrs; + CK_BBOOL ckTrue = CK_TRUE; + CK_OBJECT_CLASS keyclass = CKO_PRIVATE_KEY; + int tsize = 0; + int objCount = 0; + CK_OBJECT_HANDLE *key_ids; + SECKEYPrivateKeyList *keys; + int i,len; + + + attrs = findTemp; + PK11_SETATTRS(attrs, CKA_CLASS, &keyclass, sizeof(keyclass)); attrs++; + PK11_SETATTRS(attrs, CKA_TOKEN, &ckTrue, sizeof(ckTrue)); attrs++; + if (nickname) { + len = PORT_Strlen(nickname)-1; + PK11_SETATTRS(attrs, CKA_LABEL, nickname, len); attrs++; + } + tsize = attrs - findTemp; + PORT_Assert(tsize <= sizeof(findTemp)/sizeof(CK_ATTRIBUTE)); + + key_ids = pk11_FindObjectsByTemplate(slot,findTemp,tsize,&objCount); + if (key_ids == NULL) { + return NULL; + } + keys = SECKEY_NewPrivateKeyList(); + if (keys == NULL) { + PORT_Free(key_ids); + } + + for (i=0; i < objCount ; i++) { + SECKEYPrivateKey *privKey = + PK11_MakePrivKey(slot,nullKey,PR_TRUE,key_ids[i],wincx); + SECKEY_AddPrivateKeyToListTail(keys, privKey); + } + + PORT_Free(key_ids); + return keys; +} + +/* + * return the certificate associated with a derCert + */ +SECItem * +PK11_FindCrlByName(PK11SlotInfo **slot, CK_OBJECT_HANDLE *crlHandle, + SECItem *name, int type, char **url) +{ +#ifdef NSS_CLASSIC + CK_OBJECT_CLASS crlClass = CKO_NETSCAPE_CRL; + CK_ATTRIBUTE theTemplate[] = { + { CKA_SUBJECT, NULL, 0 }, + { CKA_CLASS, NULL, 0 }, + { CKA_NETSCAPE_KRL, NULL, 0 }, + }; + CK_ATTRIBUTE crlData[] = { + { CKA_VALUE, NULL, 0 }, + { CKA_NETSCAPE_URL, NULL, 0 }, + }; + /* if you change the array, change the variable below as well */ + int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]); + CK_BBOOL ck_true = CK_TRUE; + CK_BBOOL ck_false = CK_FALSE; + CK_OBJECT_HANDLE crlh = CK_INVALID_HANDLE; + CK_ATTRIBUTE *attrs = theTemplate; + CK_RV crv; + SECItem *derCrl = NULL; + + PK11_SETATTRS(attrs, CKA_SUBJECT, name->data, name->len); attrs++; + PK11_SETATTRS(attrs, CKA_CLASS, &crlClass, sizeof(crlClass)); attrs++; + PK11_SETATTRS(attrs, CKA_NETSCAPE_KRL, (type == SEC_CRL_TYPE) ? + &ck_false : &ck_true, sizeof (CK_BBOOL)); attrs++; + + if (*slot) { + crlh = pk11_FindObjectByTemplate(*slot,theTemplate,tsize); + } else { + PK11SlotList *list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, + PR_FALSE,PR_TRUE,NULL); + PK11SlotListElement *le; + + /* loop through all the fortezza tokens */ + for (le = list->head; le; le = le->next) { + crlh = pk11_FindObjectByTemplate(le->slot,theTemplate,tsize); + if (crlh != CK_INVALID_HANDLE) { + *slot = PK11_ReferenceSlot(le->slot); + break; + } + } + PK11_FreeSlotList(list); + } + + if (crlh == CK_INVALID_HANDLE) { + PORT_SetError(SEC_ERROR_NO_KRL); + return NULL; + } + crv = PK11_GetAttributes(NULL,*slot,crlh,crlData,2); + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError (crv)); + goto loser; + } + + derCrl = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); + if (derCrl == NULL) { + goto loser; + } + + derCrl->data = crlData[0].pValue; + derCrl->len = crlData[0].ulValueLen; + + if (crlHandle) { + *crlHandle = crlh; + } + + if ((url) && crlData[1].ulValueLen != 0) { + /* make sure it's a null terminated string */ + *url = PORT_ZAlloc (crlData[1].ulValueLen+1); + if (*url) { + PORT_Memcpy(*url,crlData[1].pValue,crlData[1].ulValueLen); + } + } + + +loser: + if (!derCrl) { + if (crlData[0].pValue) PORT_Free(crlData[0].pValue); + } + if (crlData[1].pValue) PORT_Free(crlData[1].pValue); + return derCrl; +#else + NSSCRL **crls, **crlp, *crl; + NSSDER subject; + SECItem *rvItem; + NSSTrustDomain *td = STAN_GetDefaultTrustDomain(); + NSSITEM_FROM_SECITEM(&subject, name); + if (*slot) { + nssCryptokiObject **instances; + nssPKIObjectCollection *collection; + nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly; + NSSToken *token = PK11Slot_GetNSSToken(*slot); + collection = nssCRLCollection_Create(td, NULL); + if (!collection) { + return NULL; + } + instances = nssToken_FindCRLsBySubject(token, NULL, &subject, + tokenOnly, 0, NULL); + nssPKIObjectCollection_AddInstances(collection, instances, 0); + nss_ZFreeIf(instances); + crls = nssPKIObjectCollection_GetCRLs(collection, NULL, 0, NULL); + nssPKIObjectCollection_Destroy(collection); + } else { + crls = nssTrustDomain_FindCRLsBySubject(td, &subject); + } + if ((!crls) || (*crls == NULL)) { + if (crls) { + nssCRLArray_Destroy(crls); + } + if (NSS_GetError() == NSS_ERROR_NOT_FOUND) { + PORT_SetError(SEC_ERROR_CRL_NOT_FOUND); + } + return NULL; + } + crl = NULL; + for (crlp = crls; *crlp; crlp++) { + if ((!(*crlp)->isKRL && type == SEC_CRL_TYPE) || + ((*crlp)->isKRL && type != SEC_CRL_TYPE)) + { + crl = nssCRL_AddRef(*crlp); + break; + } + } + nssCRLArray_Destroy(crls); + if (!crl) { + /* CRL collection was found, but no interesting CRL's were on it. + * Not an error */ + PORT_SetError(SEC_ERROR_CRL_NOT_FOUND); + return NULL; + } + if (crl->url) { + *url = PORT_Strdup(crl->url); + if (!*url) { + nssCRL_Destroy(crl); + return NULL; + } + } else { + *url = NULL; + } + rvItem = SECITEM_AllocItem(NULL, NULL, crl->encoding.size); + if (!rvItem) { + PORT_Free(*url); + nssCRL_Destroy(crl); + return NULL; + } + memcpy(rvItem->data, crl->encoding.data, crl->encoding.size); + *slot = PK11_ReferenceSlot(crl->object.instances[0]->token->pk11slot); + *crlHandle = crl->object.instances[0]->handle; + nssCRL_Destroy(crl); + return rvItem; +#endif +} + +CK_OBJECT_HANDLE +PK11_PutCrl(PK11SlotInfo *slot, SECItem *crl, SECItem *name, + char *url, int type) +{ +#ifdef NSS_CLASSIC + CK_OBJECT_CLASS crlClass = CKO_NETSCAPE_CRL; + CK_ATTRIBUTE theTemplate[] = { + { CKA_SUBJECT, NULL, 0 }, + { CKA_CLASS, NULL, 0 }, + { CKA_NETSCAPE_KRL, NULL, 0 }, + { CKA_NETSCAPE_URL, NULL, 0 }, + { CKA_VALUE, NULL, 0 }, + { CKA_TOKEN, NULL, 0 } + }; + /* if you change the array, change the variable below as well */ + int tsize; + CK_BBOOL ck_true = CK_TRUE; + CK_BBOOL ck_false = CK_FALSE; + CK_OBJECT_HANDLE crlh = CK_INVALID_HANDLE; + CK_ATTRIBUTE *attrs = theTemplate; + CK_SESSION_HANDLE rwsession; + CK_RV crv; + + PK11_SETATTRS(attrs, CKA_SUBJECT, name->data, name->len); attrs++; + PK11_SETATTRS(attrs, CKA_CLASS, &crlClass, sizeof(crlClass)); attrs++; + PK11_SETATTRS(attrs, CKA_NETSCAPE_KRL, (type == SEC_CRL_TYPE) ? + &ck_false : &ck_true, sizeof (CK_BBOOL)); attrs++; + if (url) { + PK11_SETATTRS(attrs, CKA_NETSCAPE_URL, url, PORT_Strlen(url)+1); attrs++; + } + PK11_SETATTRS(attrs, CKA_VALUE,crl->data,crl->len); attrs++; + PK11_SETATTRS(attrs, CKA_TOKEN, &ck_true,sizeof(CK_BBOOL)); attrs++; + + tsize = attrs - &theTemplate[0]; + PORT_Assert(tsize <= sizeof(theTemplate)/sizeof(theTemplate[0])); + + rwsession = PK11_GetRWSession(slot); + if (rwsession == CK_INVALID_SESSION) { + PORT_SetError(SEC_ERROR_READ_ONLY); + return crlh; + } + + crv = PK11_GETTAB(slot)-> + C_CreateObject(rwsession,theTemplate,tsize,&crlh); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + } + + PK11_RestoreROSession(slot,rwsession); + + return crlh; +#else + NSSItem derCRL, derSubject; + NSSToken *token = PK11Slot_GetNSSToken(slot); + nssCryptokiObject *object; + PRBool isKRL = (type == SEC_CRL_TYPE) ? PR_FALSE : PR_TRUE; + CK_OBJECT_HANDLE rvH; + + NSSITEM_FROM_SECITEM(&derSubject, name); + NSSITEM_FROM_SECITEM(&derCRL, crl); + + object = nssToken_ImportCRL(token, NULL, + &derSubject, &derCRL, isKRL, url, PR_TRUE); + + if (object) { + rvH = object->handle; + nssCryptokiObject_Destroy(object); + } else { + rvH = CK_INVALID_HANDLE; + } + return rvH; +#endif +} + + +/* + * delete a crl. + */ +SECStatus +SEC_DeletePermCRL(CERTSignedCrl *crl) +{ +#ifdef NSS_CLASSIC + PK11SlotInfo *slot = crl->slot; + CK_RV crv; + + if (slot == NULL) { + /* shouldn't happen */ + PORT_SetError( SEC_ERROR_CRL_INVALID); + return SECFailure; + } + + crv = PK11_DestroyTokenObject(slot,crl->pkcs11ID); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + crl->slot = NULL; + PK11_FreeSlot(slot); + return SECSuccess; +#else + PRStatus status; + NSSToken *token; + nssCryptokiObject *object; + PK11SlotInfo *slot = crl->slot; + + if (slot == NULL) { + PORT_Assert(slot); + /* shouldn't happen */ + PORT_SetError( SEC_ERROR_CRL_INVALID); + return SECFailure; + } + token = PK11Slot_GetNSSToken(slot); + + object = nss_ZNEW(NULL, nssCryptokiObject); + object->token = nssToken_AddRef(token); + object->handle = crl->pkcs11ID; + object->isTokenObject = PR_TRUE; + + status = nssToken_DeleteStoredObject(object); + + nssCryptokiObject_Destroy(object); + return (status == PR_SUCCESS) ? SECSuccess : SECFailure; +#endif +} + +/* + * return the certificate associated with a derCert + */ +SECItem * +PK11_FindSMimeProfile(PK11SlotInfo **slot, char *emailAddr, + SECItem *name, SECItem **profileTime) +{ + CK_OBJECT_CLASS smimeClass = CKO_NETSCAPE_SMIME; + CK_ATTRIBUTE theTemplate[] = { + { CKA_SUBJECT, NULL, 0 }, + { CKA_CLASS, NULL, 0 }, + { CKA_NETSCAPE_EMAIL, NULL, 0 }, + }; + CK_ATTRIBUTE smimeData[] = { + { CKA_SUBJECT, NULL, 0 }, + { CKA_VALUE, NULL, 0 }, + }; + /* if you change the array, change the variable below as well */ + int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]); + CK_OBJECT_HANDLE smimeh = CK_INVALID_HANDLE; + CK_ATTRIBUTE *attrs = theTemplate; + CK_RV crv; + SECItem *emailProfile = NULL; + + PK11_SETATTRS(attrs, CKA_SUBJECT, name->data, name->len); attrs++; + PK11_SETATTRS(attrs, CKA_CLASS, &smimeClass, sizeof(smimeClass)); attrs++; + PK11_SETATTRS(attrs, CKA_NETSCAPE_EMAIL, emailAddr, strlen(emailAddr)); + attrs++; + + if (*slot) { + smimeh = pk11_FindObjectByTemplate(*slot,theTemplate,tsize); + } else { + PK11SlotList *list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, + PR_FALSE,PR_TRUE,NULL); + PK11SlotListElement *le; + + /* loop through all the fortezza tokens */ + for (le = list->head; le; le = le->next) { + smimeh = pk11_FindObjectByTemplate(le->slot,theTemplate,tsize); + if (smimeh != CK_INVALID_HANDLE) { + *slot = PK11_ReferenceSlot(le->slot); + break; + } + } + PK11_FreeSlotList(list); + } + + if (smimeh == CK_INVALID_HANDLE) { + PORT_SetError(SEC_ERROR_NO_KRL); + return NULL; + } + + if (profileTime) { + PK11_SETATTRS(smimeData, CKA_NETSCAPE_SMIME_TIMESTAMP, NULL, 0); + } + + crv = PK11_GetAttributes(NULL,*slot,smimeh,smimeData,2); + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError (crv)); + goto loser; + } + + if (!profileTime) { + SECItem profileSubject; + + profileSubject.data = (unsigned char*) smimeData[0].pValue; + profileSubject.len = smimeData[0].ulValueLen; + if (!SECITEM_ItemsAreEqual(&profileSubject,name)) { + goto loser; + } + } + + emailProfile = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); + if (emailProfile == NULL) { + goto loser; + } + + emailProfile->data = (unsigned char*) smimeData[1].pValue; + emailProfile->len = smimeData[1].ulValueLen; + + if (profileTime) { + *profileTime = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); + if (*profileTime) { + (*profileTime)->data = (unsigned char*) smimeData[0].pValue; + (*profileTime)->len = smimeData[0].ulValueLen; + } + } + +loser: + if (emailProfile == NULL) { + if (smimeData[1].pValue) { + PORT_Free(smimeData[1].pValue); + } + } + if (profileTime == NULL || *profileTime == NULL) { + if (smimeData[0].pValue) { + PORT_Free(smimeData[0].pValue); + } + } + return emailProfile; +} + + +SECStatus +PK11_SaveSMimeProfile(PK11SlotInfo *slot, char *emailAddr, SECItem *derSubj, + SECItem *emailProfile, SECItem *profileTime) +{ + CK_OBJECT_CLASS smimeClass = CKO_NETSCAPE_SMIME; + CK_BBOOL ck_true = CK_TRUE; + CK_ATTRIBUTE theTemplate[] = { + { CKA_CLASS, NULL, 0 }, + { CKA_TOKEN, NULL, 0 }, + { CKA_SUBJECT, NULL, 0 }, + { CKA_NETSCAPE_EMAIL, NULL, 0 }, + { CKA_NETSCAPE_SMIME_TIMESTAMP, NULL, 0 }, + { CKA_VALUE, NULL, 0 } + }; + /* if you change the array, change the variable below as well */ + int realSize = 0; + CK_OBJECT_HANDLE smimeh = CK_INVALID_HANDLE; + CK_ATTRIBUTE *attrs = theTemplate; + CK_SESSION_HANDLE rwsession; + PK11SlotInfo *free_slot = NULL; + CK_RV crv; +#ifdef DEBUG + int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]); +#endif + + PK11_SETATTRS(attrs, CKA_CLASS, &smimeClass, sizeof(smimeClass)); attrs++; + PK11_SETATTRS(attrs, CKA_TOKEN, &ck_true, sizeof(ck_true)); attrs++; + PK11_SETATTRS(attrs, CKA_SUBJECT, derSubj->data, derSubj->len); attrs++; + PK11_SETATTRS(attrs, CKA_NETSCAPE_EMAIL, + emailAddr, PORT_Strlen(emailAddr)+1); attrs++; + if (profileTime) { + PK11_SETATTRS(attrs, CKA_NETSCAPE_SMIME_TIMESTAMP, profileTime->data, + profileTime->len); attrs++; + PK11_SETATTRS(attrs, CKA_VALUE,emailProfile->data, + emailProfile->len); attrs++; + } + realSize = attrs - theTemplate; + PORT_Assert (realSize <= tsize); + + if (slot == NULL) { + free_slot = slot = PK11_GetInternalKeySlot(); + /* we need to free the key slot in the end!!! */ + } + + rwsession = PK11_GetRWSession(slot); + if (rwsession == CK_INVALID_SESSION) { + PORT_SetError(SEC_ERROR_READ_ONLY); + if (free_slot) { + PK11_FreeSlot(free_slot); + } + return SECFailure; + } + + crv = PK11_GETTAB(slot)-> + C_CreateObject(rwsession,theTemplate,realSize,&smimeh); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + } + + PK11_RestoreROSession(slot,rwsession); + + if (free_slot) { + PK11_FreeSlot(free_slot); + } + return SECSuccess; +} + + +CERTSignedCrl * crl_storeCRL (PK11SlotInfo *slot,char *url, + CERTSignedCrl *newCrl, SECItem *derCrl, int type); + +/* import the CRL into the token */ + +CERTSignedCrl* PK11_ImportCRL(PK11SlotInfo * slot, SECItem *derCRL, char *url, + int type, void *wincx, PRInt32 importOptions, PRArenaPool* arena, + PRInt32 decodeoptions) +{ + CERTSignedCrl *newCrl, *crl; + SECStatus rv; + + newCrl = crl = NULL; + + do { + newCrl = CERT_DecodeDERCrlWithFlags(arena, derCRL, type, + decodeoptions); + if (newCrl == NULL) { + if (type == SEC_CRL_TYPE) { + /* only promote error when the error code is too generic */ + if (PORT_GetError () == SEC_ERROR_BAD_DER) + PORT_SetError(SEC_ERROR_CRL_INVALID); + } else { + PORT_SetError(SEC_ERROR_KRL_INVALID); + } + break; + } + + if (0 == (importOptions & CRL_IMPORT_BYPASS_CHECKS)){ + CERTCertificate *caCert; + CERTCertDBHandle* handle = CERT_GetDefaultCertDB(); + PR_ASSERT(handle != NULL); + caCert = CERT_FindCertByName (handle, + &newCrl->crl.derName); + if (caCert == NULL) { + PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER); + break; + } + + /* If caCert is a v3 certificate, make sure that it can be used for + crl signing purpose */ + rv = CERT_CheckCertUsage (caCert, KU_CRL_SIGN); + if (rv != SECSuccess) { + break; + } + + rv = CERT_VerifySignedData(&newCrl->signatureWrap, caCert, + PR_Now(), wincx); + if (rv != SECSuccess) { + if (type == SEC_CRL_TYPE) { + PORT_SetError(SEC_ERROR_CRL_BAD_SIGNATURE); + } else { + PORT_SetError(SEC_ERROR_KRL_BAD_SIGNATURE); + } + break; + } + } + + crl = crl_storeCRL(slot, url, newCrl, derCRL, type); + + } while (0); + + if (crl == NULL) { + SEC_DestroyCrl (newCrl); + } + return (crl); +} |