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