summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/certdb/stanpcertdb.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--security/nss/lib/certdb/stanpcertdb.c1137
1 files changed, 1137 insertions, 0 deletions
diff --git a/security/nss/lib/certdb/stanpcertdb.c b/security/nss/lib/certdb/stanpcertdb.c
new file mode 100644
index 0000000000..e255df1053
--- /dev/null
+++ b/security/nss/lib/certdb/stanpcertdb.c
@@ -0,0 +1,1137 @@
+/* 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;
+}