diff options
Diffstat (limited to 'security/nss/lib/certdb/stanpcertdb.c')
-rw-r--r-- | security/nss/lib/certdb/stanpcertdb.c | 1137 |
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; +} |