Source code

Revision control

Copy as Markdown

Other Tools

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