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/. */
#include "prtime.h"
#include "cert.h"
#include "certi.h"
#include "certdb.h"
#include "secitem.h"
#include "secder.h"
/* Call to PK11_FreeSlot below */
#include "secasn1.h"
#include "secerr.h"
#include "nssilock.h"
#include "prmon.h"
#include "base64.h"
#include "sechash.h"
#include "plhash.h"
#include "pk11func.h" /* sigh */
#include "nsspki.h"
#include "pki.h"
#include "pkim.h"
#include "pki3hack.h"
#include "ckhelper.h"
#include "base.h"
#include "pkistore.h"
#include "dev3hack.h"
#include "dev.h"
#include "secmodi.h"
extern void CERT_MaybeLockCertTempPerm(const CERTCertificate *cert);
extern void CERT_MaybeUnlockCertTempPerm(const CERTCertificate *cert);
PRBool
SEC_CertNicknameConflict(const char *nickname, const SECItem *derSubject,
CERTCertDBHandle *handle)
{
CERTCertificate *cert;
PRBool conflict = PR_FALSE;
cert = CERT_FindCertByNickname(handle, nickname);
if (!cert) {
return conflict;
}
conflict = !SECITEM_ItemsAreEqual(derSubject, &cert->derSubject);
CERT_DestroyCertificate(cert);
return conflict;
}
SECStatus
SEC_DeletePermCertificate(CERTCertificate *cert)
{
PRStatus nssrv;
NSSTrustDomain *td = STAN_GetDefaultTrustDomain();
NSSCertificate *c = STAN_GetNSSCertificate(cert);
CERTCertTrust *certTrust;
if (c == NULL) {
/* error code is set */
return SECFailure;
}
certTrust = nssTrust_GetCERTCertTrustForCert(c, cert);
if (certTrust) {
NSSTrust *nssTrust = nssTrustDomain_FindTrustForCertificate(td, c);
if (nssTrust) {
nssrv = STAN_DeleteCertTrustMatchingSlot(c);
if (nssrv != PR_SUCCESS) {
CERT_MapStanError();
}
/* This call always returns PR_SUCCESS! */
(void)nssTrust_Destroy(nssTrust);
}
}
/* get rid of the token instances */
nssrv = NSSCertificate_DeleteStoredObject(c, NULL);
/* get rid of the cache entry */
nssTrustDomain_LockCertCache(td);
nssTrustDomain_RemoveCertFromCacheLOCKED(td, c);
nssTrustDomain_UnlockCertCache(td);
return (nssrv == PR_SUCCESS) ? SECSuccess : SECFailure;
}
SECStatus
CERT_GetCertTrust(const CERTCertificate *cert, CERTCertTrust *trust)
{
SECStatus rv;
CERT_LockCertTrust(cert);
if (!cert || cert->trust == NULL) {
rv = SECFailure;
} else {
*trust = *cert->trust;
rv = SECSuccess;
}
CERT_UnlockCertTrust(cert);
return (rv);
}
extern const NSSError NSS_ERROR_NO_ERROR;
extern const NSSError NSS_ERROR_INTERNAL_ERROR;
extern const NSSError NSS_ERROR_NO_MEMORY;
extern const NSSError NSS_ERROR_INVALID_POINTER;
extern const NSSError NSS_ERROR_INVALID_ARENA;
extern const NSSError NSS_ERROR_INVALID_ARENA_MARK;
extern const NSSError NSS_ERROR_DUPLICATE_POINTER;
extern const NSSError NSS_ERROR_POINTER_NOT_REGISTERED;
extern const NSSError NSS_ERROR_TRACKER_NOT_EMPTY;
extern const NSSError NSS_ERROR_TRACKER_NOT_INITIALIZED;
extern const NSSError NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD;
extern const NSSError NSS_ERROR_VALUE_TOO_LARGE;
extern const NSSError NSS_ERROR_UNSUPPORTED_TYPE;
extern const NSSError NSS_ERROR_BUFFER_TOO_SHORT;
extern const NSSError NSS_ERROR_INVALID_ATOB_CONTEXT;
extern const NSSError NSS_ERROR_INVALID_BASE64;
extern const NSSError NSS_ERROR_INVALID_BTOA_CONTEXT;
extern const NSSError NSS_ERROR_INVALID_ITEM;
extern const NSSError NSS_ERROR_INVALID_STRING;
extern const NSSError NSS_ERROR_INVALID_ASN1ENCODER;
extern const NSSError NSS_ERROR_INVALID_ASN1DECODER;
extern const NSSError NSS_ERROR_INVALID_BER;
extern const NSSError NSS_ERROR_INVALID_ATAV;
extern const NSSError NSS_ERROR_INVALID_ARGUMENT;
extern const NSSError NSS_ERROR_INVALID_UTF8;
extern const NSSError NSS_ERROR_INVALID_NSSOID;
extern const NSSError NSS_ERROR_UNKNOWN_ATTRIBUTE;
extern const NSSError NSS_ERROR_NOT_FOUND;
extern const NSSError NSS_ERROR_INVALID_PASSWORD;
extern const NSSError NSS_ERROR_USER_CANCELED;
extern const NSSError NSS_ERROR_MAXIMUM_FOUND;
extern const NSSError NSS_ERROR_CERTIFICATE_ISSUER_NOT_FOUND;
extern const NSSError NSS_ERROR_CERTIFICATE_IN_CACHE;
extern const NSSError NSS_ERROR_HASH_COLLISION;
extern const NSSError NSS_ERROR_DEVICE_ERROR;
extern const NSSError NSS_ERROR_INVALID_CERTIFICATE;
extern const NSSError NSS_ERROR_BUSY;
extern const NSSError NSS_ERROR_ALREADY_INITIALIZED;
extern const NSSError NSS_ERROR_PKCS11;
/* Look at the stan error stack and map it to NSS 3 errors */
#define STAN_MAP_ERROR(x, y) \
else if (error == (x)) { secError = y; }
/*
* map Stan errors into NSS errors
* This function examines the stan error stack and automatically sets
* PORT_SetError(); to the appropriate SEC_ERROR value.
*/
void
CERT_MapStanError()
{
PRInt32 *errorStack;
NSSError error, prevError;
int secError;
int i;
errorStack = NSS_GetErrorStack();
if (errorStack == 0) {
PORT_SetError(0);
return;
}
error = prevError = CKR_GENERAL_ERROR;
/* get the 'top 2' error codes from the stack */
for (i = 0; errorStack[i]; i++) {
prevError = error;
error = errorStack[i];
}
if (error == NSS_ERROR_PKCS11) {
/* map it */
secError = PK11_MapError(prevError);
}
STAN_MAP_ERROR(NSS_ERROR_NO_ERROR, 0)
STAN_MAP_ERROR(NSS_ERROR_NO_MEMORY, SEC_ERROR_NO_MEMORY)
STAN_MAP_ERROR(NSS_ERROR_INVALID_BASE64, SEC_ERROR_BAD_DATA)
STAN_MAP_ERROR(NSS_ERROR_INVALID_BER, SEC_ERROR_BAD_DER)
STAN_MAP_ERROR(NSS_ERROR_INVALID_ATAV, SEC_ERROR_INVALID_AVA)
STAN_MAP_ERROR(NSS_ERROR_INVALID_PASSWORD, SEC_ERROR_BAD_PASSWORD)
STAN_MAP_ERROR(NSS_ERROR_BUSY, SEC_ERROR_BUSY)
STAN_MAP_ERROR(NSS_ERROR_DEVICE_ERROR, SEC_ERROR_IO)
STAN_MAP_ERROR(NSS_ERROR_CERTIFICATE_ISSUER_NOT_FOUND,
SEC_ERROR_UNKNOWN_ISSUER)
STAN_MAP_ERROR(NSS_ERROR_INVALID_CERTIFICATE, SEC_ERROR_CERT_NOT_VALID)
STAN_MAP_ERROR(NSS_ERROR_INVALID_UTF8, SEC_ERROR_BAD_DATA)
STAN_MAP_ERROR(NSS_ERROR_INVALID_NSSOID, SEC_ERROR_BAD_DATA)
/* these are library failure for lack of a better error code */
STAN_MAP_ERROR(NSS_ERROR_NOT_FOUND, SEC_ERROR_LIBRARY_FAILURE)
STAN_MAP_ERROR(NSS_ERROR_CERTIFICATE_IN_CACHE, SEC_ERROR_LIBRARY_FAILURE)
STAN_MAP_ERROR(NSS_ERROR_MAXIMUM_FOUND, SEC_ERROR_LIBRARY_FAILURE)
STAN_MAP_ERROR(NSS_ERROR_USER_CANCELED, SEC_ERROR_LIBRARY_FAILURE)
STAN_MAP_ERROR(NSS_ERROR_TRACKER_NOT_INITIALIZED, SEC_ERROR_LIBRARY_FAILURE)
STAN_MAP_ERROR(NSS_ERROR_ALREADY_INITIALIZED, SEC_ERROR_LIBRARY_FAILURE)
STAN_MAP_ERROR(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD,
SEC_ERROR_LIBRARY_FAILURE)
STAN_MAP_ERROR(NSS_ERROR_HASH_COLLISION, SEC_ERROR_LIBRARY_FAILURE)
STAN_MAP_ERROR(NSS_ERROR_INTERNAL_ERROR, SEC_ERROR_LIBRARY_FAILURE)
/* these are all invalid arguments */
STAN_MAP_ERROR(NSS_ERROR_INVALID_ARGUMENT, SEC_ERROR_INVALID_ARGS)
STAN_MAP_ERROR(NSS_ERROR_INVALID_POINTER, SEC_ERROR_INVALID_ARGS)
STAN_MAP_ERROR(NSS_ERROR_INVALID_ARENA, SEC_ERROR_INVALID_ARGS)
STAN_MAP_ERROR(NSS_ERROR_INVALID_ARENA_MARK, SEC_ERROR_INVALID_ARGS)
STAN_MAP_ERROR(NSS_ERROR_DUPLICATE_POINTER, SEC_ERROR_INVALID_ARGS)
STAN_MAP_ERROR(NSS_ERROR_POINTER_NOT_REGISTERED, SEC_ERROR_INVALID_ARGS)
STAN_MAP_ERROR(NSS_ERROR_TRACKER_NOT_EMPTY, SEC_ERROR_INVALID_ARGS)
STAN_MAP_ERROR(NSS_ERROR_VALUE_TOO_LARGE, SEC_ERROR_INVALID_ARGS)
STAN_MAP_ERROR(NSS_ERROR_UNSUPPORTED_TYPE, SEC_ERROR_INVALID_ARGS)
STAN_MAP_ERROR(NSS_ERROR_BUFFER_TOO_SHORT, SEC_ERROR_INVALID_ARGS)
STAN_MAP_ERROR(NSS_ERROR_INVALID_ATOB_CONTEXT, SEC_ERROR_INVALID_ARGS)
STAN_MAP_ERROR(NSS_ERROR_INVALID_BTOA_CONTEXT, SEC_ERROR_INVALID_ARGS)
STAN_MAP_ERROR(NSS_ERROR_INVALID_ITEM, SEC_ERROR_INVALID_ARGS)
STAN_MAP_ERROR(NSS_ERROR_INVALID_STRING, SEC_ERROR_INVALID_ARGS)
STAN_MAP_ERROR(NSS_ERROR_INVALID_ASN1ENCODER, SEC_ERROR_INVALID_ARGS)
STAN_MAP_ERROR(NSS_ERROR_INVALID_ASN1DECODER, SEC_ERROR_INVALID_ARGS)
STAN_MAP_ERROR(NSS_ERROR_UNKNOWN_ATTRIBUTE, SEC_ERROR_INVALID_ARGS)
else { secError = SEC_ERROR_LIBRARY_FAILURE; }
PORT_SetError(secError);
}
SECStatus
CERT_ChangeCertTrust(CERTCertDBHandle *handle, CERTCertificate *cert,
CERTCertTrust *trust)
{
SECStatus rv = SECSuccess;
PRStatus ret;
ret = STAN_ChangeCertTrust(cert, trust);
if (ret != PR_SUCCESS) {
rv = SECFailure;
CERT_MapStanError();
}
return rv;
}
extern const NSSError NSS_ERROR_INVALID_CERTIFICATE;
SECStatus
__CERT_AddTempCertToPerm(CERTCertificate *cert, char *nickname,
CERTCertTrust *trust)
{
NSSUTF8 *stanNick;
PK11SlotInfo *slot;
NSSToken *internal;
NSSCryptoContext *context;
nssCryptokiObject *permInstance;
NSSCertificate *c = STAN_GetNSSCertificate(cert);
nssCertificateStoreTrace lockTrace = { NULL, NULL, PR_FALSE, PR_FALSE };
nssCertificateStoreTrace unlockTrace = { NULL, NULL, PR_FALSE, PR_FALSE };
SECStatus rv;
PRStatus ret;
if (c == NULL) {
CERT_MapStanError();
return SECFailure;
}
context = c->object.cryptoContext;
if (!context) {
PORT_SetError(SEC_ERROR_ADDING_CERT);
return SECFailure; /* wasn't a temp cert */
}
stanNick = nssCertificate_GetNickname(c, NULL);
if (stanNick && nickname && strcmp(nickname, stanNick) != 0) {
/* different: take the new nickname */
cert->nickname = NULL;
nss_ZFreeIf(stanNick);
stanNick = NULL;
}
if (!stanNick && nickname) {
/* Either there was no nickname yet, or we have a new nickname */
stanNick = nssUTF8_Duplicate((NSSUTF8 *)nickname, NULL);
} /* else: old stanNick is identical to new nickname */
/* Delete the temp instance */
nssCertificateStore_Lock(context->certStore, &lockTrace);
nssCertificateStore_RemoveCertLOCKED(context->certStore, c);
nssCertificateStore_Unlock(context->certStore, &lockTrace, &unlockTrace);
c->object.cryptoContext = NULL;
/* if the id has not been set explicitly yet, create one from the public
* key. */
if (c->id.data == NULL) {
SECItem *keyID = pk11_mkcertKeyID(cert);
if (keyID) {
nssItem_Create(c->object.arena, &c->id, keyID->len, keyID->data);
SECITEM_FreeItem(keyID, PR_TRUE);
}
/* if any of these failed, continue with our null c->id */
}
/* Import the perm instance onto the internal token */
slot = PK11_GetInternalKeySlot();
internal = PK11Slot_GetNSSToken(slot);
if (!internal) {
PK11_FreeSlot(slot);
PORT_SetError(SEC_ERROR_NO_TOKEN);
return SECFailure;
}
permInstance = nssToken_ImportCertificate(
internal, NULL, NSSCertificateType_PKIX, &c->id, stanNick, &c->encoding,
&c->issuer, &c->subject, &c->serial, cert->emailAddr, PR_TRUE);
(void)nssToken_Destroy(internal);
nss_ZFreeIf(stanNick);
stanNick = NULL;
PK11_FreeSlot(slot);
if (!permInstance) {
if (NSS_GetError() == NSS_ERROR_INVALID_CERTIFICATE) {
PORT_SetError(SEC_ERROR_REUSED_ISSUER_AND_SERIAL);
}
return SECFailure;
}
nssPKIObject_AddInstance(&c->object, permInstance);
nssTrustDomain_AddCertsToCache(STAN_GetDefaultTrustDomain(), &c, 1);
/* reset the CERTCertificate fields */
CERT_LockCertTempPerm(cert);
cert->nssCertificate = NULL;
CERT_UnlockCertTempPerm(cert);
cert = STAN_GetCERTCertificateOrRelease(c); /* should return same pointer */
if (!cert) {
CERT_MapStanError();
return SECFailure;
}
CERT_LockCertTempPerm(cert);
cert->istemp = PR_FALSE;
cert->isperm = PR_TRUE;
CERT_UnlockCertTempPerm(cert);
if (!trust) {
return SECSuccess;
}
ret = STAN_ChangeCertTrust(cert, trust);
rv = SECSuccess;
if (ret != PR_SUCCESS) {
rv = SECFailure;
CERT_MapStanError();
}
return rv;
}
SECStatus
CERT_AddTempCertToPerm(CERTCertificate *cert, char *nickname,
CERTCertTrust *trust)
{
return __CERT_AddTempCertToPerm(cert, nickname, trust);
}
CERTCertificate *
CERT_NewTempCertificate(CERTCertDBHandle *handle, SECItem *derCert,
char *nickname, PRBool isperm, PRBool copyDER)
{
NSSCertificate *c;
CERTCertificate *cc;
NSSCertificate *tempCert = NULL;
nssPKIObject *pkio;
NSSCryptoContext *gCC = STAN_GetDefaultCryptoContext();
NSSTrustDomain *gTD = STAN_GetDefaultTrustDomain();
if (!isperm) {
NSSDER encoding;
NSSITEM_FROM_SECITEM(&encoding, derCert);
/* First, see if it is already a temp cert */
c = NSSCryptoContext_FindCertificateByEncodedCertificate(gCC,
&encoding);
if (!c && handle) {
/* Then, see if it is already a perm cert */
c = NSSTrustDomain_FindCertificateByEncodedCertificate(handle,
&encoding);
}
if (c) {
/* actually, that search ends up going by issuer/serial,
* so it is still possible to return a cert with the same
* issuer/serial but a different encoding, and we're
* going to reject that
*/
if (!nssItem_Equal(&c->encoding, &encoding, NULL)) {
nssCertificate_Destroy(c);
PORT_SetError(SEC_ERROR_REUSED_ISSUER_AND_SERIAL);
cc = NULL;
} else {
cc = STAN_GetCERTCertificateOrRelease(c);
if (cc == NULL) {
CERT_MapStanError();
}
}
return cc;
}
}
pkio = nssPKIObject_Create(NULL, NULL, gTD, gCC, nssPKIMonitor);
if (!pkio) {
CERT_MapStanError();
return NULL;
}
c = nss_ZNEW(pkio->arena, NSSCertificate);
if (!c) {
CERT_MapStanError();
nssPKIObject_Destroy(pkio);
return NULL;
}
c->object = *pkio;
if (copyDER) {
nssItem_Create(c->object.arena, &c->encoding, derCert->len,
derCert->data);
} else {
NSSITEM_FROM_SECITEM(&c->encoding, derCert);
}
/* Forces a decoding of the cert in order to obtain the parts used
* below
*/
/* 'c' is not adopted here, if we fail loser frees what has been
* allocated so far for 'c' */
cc = STAN_GetCERTCertificate(c);
if (!cc) {
CERT_MapStanError();
goto loser;
}
nssItem_Create(c->object.arena, &c->issuer, cc->derIssuer.len,
cc->derIssuer.data);
nssItem_Create(c->object.arena, &c->subject, cc->derSubject.len,
cc->derSubject.data);
/* CERTCertificate stores serial numbers decoded. I need the DER
* here. sigh.
*/
SECItem derSerial = { 0 };
CERT_SerialNumberFromDERCert(&cc->derCert, &derSerial);
if (!derSerial.data)
goto loser;
nssItem_Create(c->object.arena, &c->serial, derSerial.len,
derSerial.data);
PORT_Free(derSerial.data);
if (nickname) {
c->object.tempName =
nssUTF8_Create(c->object.arena, nssStringType_UTF8String,
(NSSUTF8 *)nickname, PORT_Strlen(nickname));
}
if (cc->emailAddr && cc->emailAddr[0]) {
c->email = nssUTF8_Create(
c->object.arena, nssStringType_PrintableString,
(NSSUTF8 *)cc->emailAddr, PORT_Strlen(cc->emailAddr));
}
tempCert = NSSCryptoContext_FindOrImportCertificate(gCC, c);
if (!tempCert) {
CERT_MapStanError();
goto loser;
}
/* destroy our copy */
NSSCertificate_Destroy(c);
/* and use the stored entry */
c = tempCert;
cc = STAN_GetCERTCertificateOrRelease(c);
if (!cc) {
/* STAN_GetCERTCertificateOrRelease destroys c on failure. */
CERT_MapStanError();
return NULL;
}
CERT_LockCertTempPerm(cc);
cc->istemp = PR_TRUE;
cc->isperm = PR_FALSE;
CERT_UnlockCertTempPerm(cc);
return cc;
loser:
/* Perhaps this should be nssCertificate_Destroy(c) */
nssPKIObject_Destroy(&c->object);
return NULL;
}
/* This symbol is exported for backward compatibility. */
CERTCertificate *
__CERT_NewTempCertificate(CERTCertDBHandle *handle, SECItem *derCert,
char *nickname, PRBool isperm, PRBool copyDER)
{
return CERT_NewTempCertificate(handle, derCert, nickname, isperm, copyDER);
}
static CERTCertificate *
common_FindCertByIssuerAndSN(CERTCertDBHandle *handle,
CERTIssuerAndSN *issuerAndSN,
void *wincx)
{
PK11SlotInfo *slot;
CERTCertificate *cert;
cert = PK11_FindCertByIssuerAndSN(&slot, issuerAndSN, wincx);
if (cert && slot) {
PK11_FreeSlot(slot);
}
return cert;
}
/* maybe all the wincx's should be some const for internal token login? */
CERTCertificate *
CERT_FindCertByIssuerAndSN(CERTCertDBHandle *handle,
CERTIssuerAndSN *issuerAndSN)
{
return common_FindCertByIssuerAndSN(handle, issuerAndSN, NULL);
}
/* maybe all the wincx's should be some const for internal token login? */
CERTCertificate *
CERT_FindCertByIssuerAndSNCX(CERTCertDBHandle *handle,
CERTIssuerAndSN *issuerAndSN,
void *wincx)
{
return common_FindCertByIssuerAndSN(handle, issuerAndSN, wincx);
}
static NSSCertificate *
get_best_temp_or_perm(NSSCertificate *ct, NSSCertificate *cp)
{
NSSUsage usage;
NSSCertificate *arr[3];
if (!ct) {
return nssCertificate_AddRef(cp);
} else if (!cp) {
return nssCertificate_AddRef(ct);
}
arr[0] = ct;
arr[1] = cp;
arr[2] = NULL;
usage.anyUsage = PR_TRUE;
return nssCertificateArray_FindBestCertificate(arr, NULL, &usage, NULL);
}
CERTCertificate *
CERT_FindCertByName(CERTCertDBHandle *handle, SECItem *name)
{
NSSCertificate *cp, *ct, *c;
NSSDER subject;
NSSUsage usage;
NSSCryptoContext *cc;
NSSITEM_FROM_SECITEM(&subject, name);
usage.anyUsage = PR_TRUE;
cc = STAN_GetDefaultCryptoContext();
ct = NSSCryptoContext_FindBestCertificateBySubject(cc, &subject, NULL,
&usage, NULL);
cp = NSSTrustDomain_FindBestCertificateBySubject(handle, &subject, NULL,
&usage, NULL);
c = get_best_temp_or_perm(ct, cp);
if (ct) {
CERT_DestroyCertificate(STAN_GetCERTCertificateOrRelease(ct));
}
if (cp) {
CERT_DestroyCertificate(STAN_GetCERTCertificateOrRelease(cp));
}
return c ? STAN_GetCERTCertificateOrRelease(c) : NULL;
}
CERTCertificate *
CERT_FindCertByKeyID(CERTCertDBHandle *handle, SECItem *name, SECItem *keyID)
{
CERTCertList *list;
CERTCertificate *cert = NULL;
CERTCertListNode *node;
list = CERT_CreateSubjectCertList(NULL, handle, name, 0, PR_FALSE);
if (list == NULL)
return NULL;
node = CERT_LIST_HEAD(list);
while (!CERT_LIST_END(node, list)) {
if (node->cert &&
SECITEM_ItemsAreEqual(&node->cert->subjectKeyID, keyID)) {
cert = CERT_DupCertificate(node->cert);
goto done;
}
node = CERT_LIST_NEXT(node);
}
PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER);
done:
CERT_DestroyCertList(list);
return cert;
}
CERTCertificate *
CERT_FindCertByNickname(CERTCertDBHandle *handle, const char *nickname)
{
NSSCryptoContext *cc;
NSSCertificate *c, *ct;
CERTCertificate *cert;
NSSUsage usage;
usage.anyUsage = PR_TRUE;
cc = STAN_GetDefaultCryptoContext();
ct = NSSCryptoContext_FindBestCertificateByNickname(cc, nickname, NULL,
&usage, NULL);
cert = PK11_FindCertFromNickname(nickname, NULL);
c = NULL;
if (cert) {
c = get_best_temp_or_perm(ct, STAN_GetNSSCertificate(cert));
CERT_DestroyCertificate(cert);
if (ct) {
CERT_DestroyCertificate(STAN_GetCERTCertificateOrRelease(ct));
}
} else {
c = ct;
}
return c ? STAN_GetCERTCertificateOrRelease(c) : NULL;
}
CERTCertificate *
CERT_FindCertByDERCert(CERTCertDBHandle *handle, SECItem *derCert)
{
NSSCryptoContext *cc;
NSSCertificate *c;
NSSDER encoding;
NSSITEM_FROM_SECITEM(&encoding, derCert);
cc = STAN_GetDefaultCryptoContext();
c = NSSCryptoContext_FindCertificateByEncodedCertificate(cc, &encoding);
if (!c) {
c = NSSTrustDomain_FindCertificateByEncodedCertificate(handle,
&encoding);
if (!c)
return NULL;
}
return STAN_GetCERTCertificateOrRelease(c);
}
static CERTCertificate *
common_FindCertByNicknameOrEmailAddrForUsage(CERTCertDBHandle *handle,
const char *name, PRBool anyUsage,
SECCertUsage lookingForUsage,
void *wincx)
{
NSSCryptoContext *cc;
NSSCertificate *c, *ct;
CERTCertificate *cert = NULL;
NSSUsage usage;
CERTCertList *certlist;
if (NULL == name) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return NULL;
}
usage.anyUsage = anyUsage;
if (!anyUsage) {
usage.nss3lookingForCA = PR_FALSE;
usage.nss3usage = lookingForUsage;
}
cc = STAN_GetDefaultCryptoContext();
ct = NSSCryptoContext_FindBestCertificateByNickname(cc, name, NULL, &usage,
NULL);
if (!ct && PORT_Strchr(name, '@') != NULL) {
char *lowercaseName = CERT_FixupEmailAddr(name);
if (lowercaseName) {
ct = NSSCryptoContext_FindBestCertificateByEmail(
cc, lowercaseName, NULL, &usage, NULL);
PORT_Free(lowercaseName);
}
}
if (anyUsage) {
cert = PK11_FindCertFromNickname(name, wincx);
} else {
if (ct) {
/* Does ct really have the required usage? */
nssDecodedCert *dc;
dc = nssCertificate_GetDecoding(ct);
if (!dc->matchUsage(dc, &usage)) {
CERT_DestroyCertificate(STAN_GetCERTCertificateOrRelease(ct));
ct = NULL;
}
}
certlist = PK11_FindCertsFromNickname(name, wincx);
if (certlist) {
SECStatus rv =
CERT_FilterCertListByUsage(certlist, lookingForUsage, PR_FALSE);
if (SECSuccess == rv && !CERT_LIST_EMPTY(certlist)) {
cert = CERT_DupCertificate(CERT_LIST_HEAD(certlist)->cert);
}
CERT_DestroyCertList(certlist);
}
}
if (cert) {
c = get_best_temp_or_perm(ct, STAN_GetNSSCertificate(cert));
CERT_DestroyCertificate(cert);
if (ct) {
CERT_DestroyCertificate(STAN_GetCERTCertificateOrRelease(ct));
}
} else {
c = ct;
}
return c ? STAN_GetCERTCertificateOrRelease(c) : NULL;
}
CERTCertificate *
CERT_FindCertByNicknameOrEmailAddr(CERTCertDBHandle *handle, const char *name)
{
return common_FindCertByNicknameOrEmailAddrForUsage(handle, name, PR_TRUE,
0, NULL);
}
CERTCertificate *
CERT_FindCertByNicknameOrEmailAddrCX(CERTCertDBHandle *handle, const char *name,
void *wincx)
{
return common_FindCertByNicknameOrEmailAddrForUsage(handle, name, PR_TRUE,
0, wincx);
}
CERTCertificate *
CERT_FindCertByNicknameOrEmailAddrForUsage(CERTCertDBHandle *handle,
const char *name,
SECCertUsage lookingForUsage)
{
return common_FindCertByNicknameOrEmailAddrForUsage(handle, name, PR_FALSE,
lookingForUsage, NULL);
}
CERTCertificate *
CERT_FindCertByNicknameOrEmailAddrForUsageCX(CERTCertDBHandle *handle,
const char *name,
SECCertUsage lookingForUsage,
void *wincx)
{
return common_FindCertByNicknameOrEmailAddrForUsage(handle, name, PR_FALSE,
lookingForUsage, wincx);
}
static void
add_to_subject_list(CERTCertList *certList, CERTCertificate *cert,
PRBool validOnly, PRTime sorttime)
{
SECStatus secrv;
if (!validOnly ||
CERT_CheckCertValidTimes(cert, sorttime, PR_FALSE) ==
secCertTimeValid) {
secrv = CERT_AddCertToListSorted(certList, cert, CERT_SortCBValidity,
(void *)&sorttime);
if (secrv != SECSuccess) {
CERT_DestroyCertificate(cert);
}
} else {
CERT_DestroyCertificate(cert);
}
}
CERTCertList *
CERT_CreateSubjectCertList(CERTCertList *certList, CERTCertDBHandle *handle,
const SECItem *name, PRTime sorttime,
PRBool validOnly)
{
NSSCryptoContext *cc;
NSSCertificate **tSubjectCerts, **pSubjectCerts;
NSSCertificate **ci;
CERTCertificate *cert;
NSSDER subject;
PRBool myList = PR_FALSE;
cc = STAN_GetDefaultCryptoContext();
NSSITEM_FROM_SECITEM(&subject, name);
/* Collect both temp and perm certs for the subject */
tSubjectCerts =
NSSCryptoContext_FindCertificatesBySubject(cc, &subject, NULL, 0, NULL);
pSubjectCerts = NSSTrustDomain_FindCertificatesBySubject(handle, &subject,
NULL, 0, NULL);
if (!tSubjectCerts && !pSubjectCerts) {
return NULL;
}
if (certList == NULL) {
certList = CERT_NewCertList();
myList = PR_TRUE;
if (!certList)
goto loser;
}
/* Iterate over the matching temp certs. Add them to the list */
ci = tSubjectCerts;
while (ci && *ci) {
cert = STAN_GetCERTCertificateOrRelease(*ci);
/* *ci may be invalid at this point, don't reference it again */
if (cert) {
/* NOTE: add_to_subject_list adopts the incoming cert. */
add_to_subject_list(certList, cert, validOnly, sorttime);
}
ci++;
}
/* Iterate over the matching perm certs. Add them to the list */
ci = pSubjectCerts;
while (ci && *ci) {
cert = STAN_GetCERTCertificateOrRelease(*ci);
/* *ci may be invalid at this point, don't reference it again */
if (cert) {
/* NOTE: add_to_subject_list adopts the incoming cert. */
add_to_subject_list(certList, cert, validOnly, sorttime);
}
ci++;
}
/* all the references have been adopted or freed at this point, just
* free the arrays now */
nss_ZFreeIf(tSubjectCerts);
nss_ZFreeIf(pSubjectCerts);
return certList;
loser:
/* need to free the references in tSubjectCerts and pSubjectCerts! */
nssCertificateArray_Destroy(tSubjectCerts);
nssCertificateArray_Destroy(pSubjectCerts);
if (myList && certList != NULL) {
CERT_DestroyCertList(certList);
}
return NULL;
}
void
CERT_DestroyCertificate(CERTCertificate *cert)
{
if (cert) {
/* don't use STAN_GetNSSCertificate because we don't want to
* go to the trouble of translating the CERTCertificate into
* an NSSCertificate just to destroy it. If it hasn't been done
* yet, don't do it at all
*
* cert->nssCertificate contains its own locks and refcount, but as it
* may be NULL, the pointer itself must be guarded by some other lock.
* Rather than creating a new global lock for only this purpose, share
* an existing global lock that happens to be taken near the write in
* fill_CERTCertificateFields(). The longer-term goal is to refactor
* all these global locks to be certificate-scoped. */
CERT_MaybeLockCertTempPerm(cert);
NSSCertificate *tmp = cert->nssCertificate;
CERT_MaybeUnlockCertTempPerm(cert);
if (tmp) {
/* delete the NSSCertificate */
NSSCertificate_Destroy(tmp);
} else if (cert->arena) {
PORT_FreeArena(cert->arena, PR_FALSE);
}
}
return;
}
int
CERT_GetDBContentVersion(CERTCertDBHandle *handle)
{
/* should read the DB content version from the pkcs #11 device */
return 0;
}
SECStatus
certdb_SaveSingleProfile(CERTCertificate *cert, const char *emailAddr,
SECItem *emailProfile, SECItem *profileTime)
{
PRTime oldtime;
PRTime newtime;
SECStatus rv = SECFailure;
PRBool saveit;
SECItem oldprof, oldproftime;
SECItem *oldProfile = NULL;
SECItem *oldProfileTime = NULL;
PK11SlotInfo *slot = NULL;
NSSCertificate *c;
NSSCryptoContext *cc;
nssSMIMEProfile *stanProfile = NULL;
PRBool freeOldProfile = PR_FALSE;
c = STAN_GetNSSCertificate(cert);
if (!c)
return SECFailure;
cc = c->object.cryptoContext;
if (cc != NULL) {
stanProfile = nssCryptoContext_FindSMIMEProfileForCertificate(cc, c);
if (stanProfile) {
PORT_Assert(stanProfile->profileData);
SECITEM_FROM_NSSITEM(&oldprof, stanProfile->profileData);
oldProfile = &oldprof;
SECITEM_FROM_NSSITEM(&oldproftime, stanProfile->profileTime);
oldProfileTime = &oldproftime;
}
} else {
oldProfile = PK11_FindSMimeProfile(&slot, (char *)emailAddr,
&cert->derSubject, &oldProfileTime);
freeOldProfile = PR_TRUE;
}
saveit = PR_FALSE;
/* both profileTime and emailProfile have to exist or not exist */
if (emailProfile == NULL) {
profileTime = NULL;
} else if (profileTime == NULL) {
emailProfile = NULL;
}
if (oldProfileTime == NULL) {
saveit = PR_TRUE;
} else {
/* there was already a profile for this email addr */
if (profileTime) {
/* we have an old and new profile - save whichever is more recent*/
if (oldProfileTime->len == 0) {
/* always replace if old entry doesn't have a time */
oldtime = LL_MININT;
} else {
rv = DER_UTCTimeToTime(&oldtime, oldProfileTime);
if (rv != SECSuccess) {
goto loser;
}
}
rv = DER_UTCTimeToTime(&newtime, profileTime);
if (rv != SECSuccess) {
goto loser;
}
if (LL_CMP(newtime, >, oldtime)) {
/* this is a newer profile, save it and cert */
saveit = PR_TRUE;
}
} else {
saveit = PR_TRUE;
}
}
if (saveit) {
if (cc) {
if (stanProfile && profileTime && emailProfile) {
/* stanProfile is already stored in the crypto context,
* overwrite the data
*/
NSSArena *arena = stanProfile->object.arena;
stanProfile->profileTime = nssItem_Create(
arena, NULL, profileTime->len, profileTime->data);
stanProfile->profileData = nssItem_Create(
arena, NULL, emailProfile->len, emailProfile->data);
} else if (profileTime && emailProfile) {
PRStatus nssrv;
NSSItem profTime, profData;
NSSITEM_FROM_SECITEM(&profTime, profileTime);
NSSITEM_FROM_SECITEM(&profData, emailProfile);
stanProfile = nssSMIMEProfile_Create(c, &profTime, &profData);
if (!stanProfile)
goto loser;
nssrv = nssCryptoContext_ImportSMIMEProfile(cc, stanProfile);
rv = (nssrv == PR_SUCCESS) ? SECSuccess : SECFailure;
}
} else {
rv = PK11_SaveSMimeProfile(slot, (char *)emailAddr,
&cert->derSubject, emailProfile,
profileTime);
}
} else {
rv = SECSuccess;
}
loser:
if (oldProfile && freeOldProfile) {
SECITEM_FreeItem(oldProfile, PR_TRUE);
}
if (oldProfileTime && freeOldProfile) {
SECITEM_FreeItem(oldProfileTime, PR_TRUE);
}
if (stanProfile) {
nssSMIMEProfile_Destroy(stanProfile);
}
if (slot) {
PK11_FreeSlot(slot);
}
return (rv);
}
/*
*
* Manage S/MIME profiles
*
*/
SECStatus
CERT_SaveSMimeProfile(CERTCertificate *cert, SECItem *emailProfile,
SECItem *profileTime)
{
const char *emailAddr;
SECStatus rv;
PRBool isperm = PR_FALSE;
if (!cert) {
return SECFailure;
}
if (cert->slot && !PK11_IsInternal(cert->slot)) {
/* this cert comes from an external source, we need to add it
to the cert db before creating an S/MIME profile */
PK11SlotInfo *internalslot = PK11_GetInternalKeySlot();
if (!internalslot) {
return SECFailure;
}
rv = PK11_ImportCert(internalslot, cert, CK_INVALID_HANDLE, NULL,
PR_FALSE);
PK11_FreeSlot(internalslot);
if (rv != SECSuccess) {
return SECFailure;
}
}
rv = CERT_GetCertIsPerm(cert, &isperm);
if (rv != SECSuccess) {
return SECFailure;
}
if (cert->slot && isperm && CERT_IsUserCert(cert) &&
(!emailProfile || !emailProfile->len)) {
/* Don't clobber emailProfile for user certs. */
return SECSuccess;
}
for (emailAddr = CERT_GetFirstEmailAddress(cert); emailAddr != NULL;
emailAddr = CERT_GetNextEmailAddress(cert, emailAddr)) {
rv = certdb_SaveSingleProfile(cert, emailAddr, emailProfile,
profileTime);
if (rv != SECSuccess) {
return SECFailure;
}
}
return SECSuccess;
}
SECItem *
CERT_FindSMimeProfile(CERTCertificate *cert)
{
PK11SlotInfo *slot = NULL;
NSSCertificate *c;
NSSCryptoContext *cc;
SECItem *rvItem = NULL;
if (!cert || !cert->emailAddr || !cert->emailAddr[0]) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return NULL;
}
c = STAN_GetNSSCertificate(cert);
if (!c)
return NULL;
cc = c->object.cryptoContext;
if (cc != NULL) {
nssSMIMEProfile *stanProfile;
stanProfile = nssCryptoContext_FindSMIMEProfileForCertificate(cc, c);
if (stanProfile) {
rvItem =
SECITEM_AllocItem(NULL, NULL, stanProfile->profileData->size);
if (rvItem) {
rvItem->data = stanProfile->profileData->data;
}
nssSMIMEProfile_Destroy(stanProfile);
}
return rvItem;
}
rvItem =
PK11_FindSMimeProfile(&slot, cert->emailAddr, &cert->derSubject, NULL);
if (slot) {
PK11_FreeSlot(slot);
}
return rvItem;
}
SECStatus
CERT_GetCertIsPerm(const CERTCertificate *cert, PRBool *isperm)
{
if (cert == NULL) {
return SECFailure;
}
CERT_LockCertTempPerm(cert);
*isperm = cert->isperm;
CERT_UnlockCertTempPerm(cert);
return SECSuccess;
}
SECStatus
CERT_GetCertIsTemp(const CERTCertificate *cert, PRBool *istemp)
{
if (cert == NULL) {
return SECFailure;
}
CERT_LockCertTempPerm(cert);
*istemp = cert->istemp;
CERT_UnlockCertTempPerm(cert);
return SECSuccess;
}
/*
* deprecated functions that are now just stubs.
*/
/*
* Close the database
*/
void
__CERT_ClosePermCertDB(CERTCertDBHandle *handle)
{
PORT_Assert("CERT_ClosePermCertDB is Deprecated" == NULL);
return;
}
SECStatus
CERT_OpenCertDBFilename(CERTCertDBHandle *handle, char *certdbname,
PRBool readOnly)
{
PORT_Assert("CERT_OpenCertDBFilename is Deprecated" == NULL);
PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
return SECFailure;
}
SECItem *
SECKEY_HashPassword(char *pw, SECItem *salt)
{
PORT_Assert("SECKEY_HashPassword is Deprecated" == NULL);
PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
return NULL;
}
SECStatus
__CERT_TraversePermCertsForSubject(CERTCertDBHandle *handle,
SECItem *derSubject, void *cb, void *cbarg)
{
PORT_Assert("CERT_TraversePermCertsForSubject is Deprecated" == NULL);
PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
return SECFailure;
}
SECStatus
__CERT_TraversePermCertsForNickname(CERTCertDBHandle *handle, char *nickname,
void *cb, void *cbarg)
{
PORT_Assert("CERT_TraversePermCertsForNickname is Deprecated" == NULL);
PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
return SECFailure;
}