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