Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/security/nss/lib/pk11wrap/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 92 kB image not shown  

Quelle  pk11cert.c   Sprache: 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 <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:
            case ecMontKey:
                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 && pubKey->keyType != ecMontKey) {
            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:
        case ecMontKey:
            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 },
    };
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=95 H=85 G=90

¤ Dauer der Verarbeitung: 0.27 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.