diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /security/nss/lib/pki/certificate.c | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/nss/lib/pki/certificate.c')
-rw-r--r-- | security/nss/lib/pki/certificate.c | 1101 |
1 files changed, 1101 insertions, 0 deletions
diff --git a/security/nss/lib/pki/certificate.c b/security/nss/lib/pki/certificate.c new file mode 100644 index 0000000000..0cfdcb12b5 --- /dev/null +++ b/security/nss/lib/pki/certificate.c @@ -0,0 +1,1101 @@ +/* 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/. */ + +#ifndef NSSPKI_H +#include "nsspki.h" +#endif /* NSSPKI_H */ + +#ifndef PKIT_H +#include "pkit.h" +#endif /* PKIT_H */ + +#ifndef PKIM_H +#include "pkim.h" +#endif /* PKIM_H */ + +#ifndef DEV_H +#include "dev.h" +#endif /* DEV_H */ + +#include "pkistore.h" + +#include "pki3hack.h" +#include "pk11func.h" +#include "hasht.h" + +#ifndef BASE_H +#include "base.h" +#endif /* BASE_H */ + +extern const NSSError NSS_ERROR_NOT_FOUND; + +/* Creates a certificate from a base object */ +NSS_IMPLEMENT NSSCertificate * +nssCertificate_Create( + nssPKIObject *object) +{ + PRStatus status; + NSSCertificate *rvCert; + nssArenaMark *mark; + NSSArena *arena = object->arena; + PR_ASSERT(object->instances != NULL && object->numInstances > 0); + PR_ASSERT(object->lockType == nssPKIMonitor); + mark = nssArena_Mark(arena); + rvCert = nss_ZNEW(arena, NSSCertificate); + if (!rvCert) { + return (NSSCertificate *)NULL; + } + rvCert->object = *object; + /* XXX should choose instance based on some criteria */ + status = nssCryptokiCertificate_GetAttributes(object->instances[0], + NULL, /* XXX sessionOpt */ + arena, + &rvCert->type, + &rvCert->id, + &rvCert->encoding, + &rvCert->issuer, + &rvCert->serial, + &rvCert->subject); + if (status != PR_SUCCESS || + !rvCert->encoding.data || + !rvCert->encoding.size || + !rvCert->issuer.data || + !rvCert->issuer.size || + !rvCert->serial.data || + !rvCert->serial.size) { + if (mark) + nssArena_Release(arena, mark); + return (NSSCertificate *)NULL; + } + if (mark) + nssArena_Unmark(arena, mark); + return rvCert; +} + +NSS_IMPLEMENT NSSCertificate * +nssCertificate_AddRef( + NSSCertificate *c) +{ + if (c) { + nssPKIObject_AddRef(&c->object); + } + return c; +} + +NSS_IMPLEMENT PRStatus +nssCertificate_Destroy( + NSSCertificate *c) +{ + nssCertificateStoreTrace lockTrace = { NULL, NULL, PR_FALSE, PR_FALSE }; + nssCertificateStoreTrace unlockTrace = { NULL, NULL, PR_FALSE, PR_FALSE }; + + if (c) { + PRUint32 i; + nssDecodedCert *dc = c->decoding; + NSSTrustDomain *td = STAN_GetDefaultTrustDomain(); + NSSCryptoContext *cc = c->object.cryptoContext; + + PR_ASSERT(c->object.refCount > 0); + + /* --- LOCK storage --- */ + if (cc) { + nssCertificateStore_Lock(cc->certStore, &lockTrace); + } else { + nssTrustDomain_LockCertCache(td); + } + if (PR_ATOMIC_DECREMENT(&c->object.refCount) == 0) { + /* --- remove cert and UNLOCK storage --- */ + if (cc) { + nssCertificateStore_RemoveCertLOCKED(cc->certStore, c); + nssCertificateStore_Unlock(cc->certStore, &lockTrace, + &unlockTrace); + } else { + nssTrustDomain_RemoveCertFromCacheLOCKED(td, c); + nssTrustDomain_UnlockCertCache(td); + } + /* free cert data */ + for (i = 0; i < c->object.numInstances; i++) { + nssCryptokiObject_Destroy(c->object.instances[i]); + } + nssPKIObject_DestroyLock(&c->object); + nssArena_Destroy(c->object.arena); + nssDecodedCert_Destroy(dc); + } else { + /* --- UNLOCK storage --- */ + if (cc) { + nssCertificateStore_Unlock(cc->certStore, + &lockTrace, + &unlockTrace); + } else { + nssTrustDomain_UnlockCertCache(td); + } + } + } + return PR_SUCCESS; +} + +NSS_IMPLEMENT PRStatus +NSSCertificate_Destroy(NSSCertificate *c) +{ + return nssCertificate_Destroy(c); +} + +NSS_IMPLEMENT NSSDER * +nssCertificate_GetEncoding(NSSCertificate *c) +{ + if (c->encoding.size > 0 && c->encoding.data) { + return &c->encoding; + } else { + return (NSSDER *)NULL; + } +} + +NSS_IMPLEMENT NSSDER * +nssCertificate_GetIssuer(NSSCertificate *c) +{ + if (c->issuer.size > 0 && c->issuer.data) { + return &c->issuer; + } else { + return (NSSDER *)NULL; + } +} + +NSS_IMPLEMENT NSSDER * +nssCertificate_GetSerialNumber(NSSCertificate *c) +{ + if (c->serial.size > 0 && c->serial.data) { + return &c->serial; + } else { + return (NSSDER *)NULL; + } +} + +NSS_IMPLEMENT NSSDER * +nssCertificate_GetSubject(NSSCertificate *c) +{ + if (c->subject.size > 0 && c->subject.data) { + return &c->subject; + } else { + return (NSSDER *)NULL; + } +} + +/* Returns a copy, Caller must free using nss_ZFreeIf */ +NSS_IMPLEMENT NSSUTF8 * +nssCertificate_GetNickname( + NSSCertificate *c, + NSSToken *tokenOpt) +{ + return nssPKIObject_GetNicknameForToken(&c->object, tokenOpt); +} + +NSS_IMPLEMENT NSSASCII7 * +nssCertificate_GetEmailAddress(NSSCertificate *c) +{ + return c->email; +} + +NSS_IMPLEMENT PRStatus +NSSCertificate_DeleteStoredObject( + NSSCertificate *c, + NSSCallback *uhh) +{ + return nssPKIObject_DeleteStoredObject(&c->object, uhh, PR_TRUE); +} + +NSS_IMPLEMENT PRStatus +NSSCertificate_Validate( + NSSCertificate *c, + NSSTime *timeOpt, /* NULL for "now" */ + NSSUsage *usage, + NSSPolicies *policiesOpt /* NULL for none */ +) +{ + nss_SetError(NSS_ERROR_NOT_FOUND); + return PR_FAILURE; +} + +NSS_IMPLEMENT void ** /* void *[] */ +NSSCertificate_ValidateCompletely( + NSSCertificate *c, + NSSTime *timeOpt, /* NULL for "now" */ + NSSUsage *usage, + NSSPolicies *policiesOpt, /* NULL for none */ + void **rvOpt, /* NULL for allocate */ + PRUint32 rvLimit, /* zero for no limit */ + NSSArena *arenaOpt /* NULL for heap */ +) +{ + nss_SetError(NSS_ERROR_NOT_FOUND); + return NULL; +} + +NSS_IMPLEMENT PRStatus +NSSCertificate_ValidateAndDiscoverUsagesAndPolicies( + NSSCertificate *c, + NSSTime **notBeforeOutOpt, + NSSTime **notAfterOutOpt, + void *allowedUsages, + void *disallowedUsages, + void *allowedPolicies, + void *disallowedPolicies, + /* more args.. work on this fgmr */ + NSSArena *arenaOpt) +{ + nss_SetError(NSS_ERROR_NOT_FOUND); + return PR_FAILURE; +} + +NSS_IMPLEMENT NSSDER * +NSSCertificate_Encode( + NSSCertificate *c, + NSSDER *rvOpt, + NSSArena *arenaOpt) +{ + /* Item, DER, BER are all typedefs now... */ + return nssItem_Duplicate((NSSItem *)&c->encoding, arenaOpt, rvOpt); +} + +NSS_IMPLEMENT nssDecodedCert * +nssCertificate_GetDecoding( + NSSCertificate *c) +{ + nssDecodedCert *deco = NULL; + if (c->type == NSSCertificateType_PKIX) { + (void)STAN_GetCERTCertificate(c); + } + nssPKIObject_Lock(&c->object); + if (!c->decoding) { + deco = nssDecodedCert_Create(NULL, &c->encoding, c->type); + PORT_Assert(!c->decoding); + c->decoding = deco; + } else { + deco = c->decoding; + } + nssPKIObject_Unlock(&c->object); + return deco; +} + +static NSSCertificate ** +filter_subject_certs_for_id( + NSSCertificate **subjectCerts, + void *id) +{ + NSSCertificate **si; + nssDecodedCert *dcp; + int nextOpenSlot = 0; + int i; + nssCertIDMatch matchLevel = nssCertIDMatch_Unknown; + nssCertIDMatch match; + + /* walk the subject certs */ + for (si = subjectCerts; *si; si++) { + dcp = nssCertificate_GetDecoding(*si); + if (!dcp) { + NSSCertificate_Destroy(*si); + continue; + } + match = dcp->matchIdentifier(dcp, id); + switch (match) { + case nssCertIDMatch_Yes: + if (matchLevel == nssCertIDMatch_Unknown) { + /* we have non-definitive matches, forget them */ + for (i = 0; i < nextOpenSlot; i++) { + NSSCertificate_Destroy(subjectCerts[i]); + subjectCerts[i] = NULL; + } + nextOpenSlot = 0; + /* only keep definitive matches from now on */ + matchLevel = nssCertIDMatch_Yes; + } + /* keep the cert */ + subjectCerts[nextOpenSlot++] = *si; + break; + case nssCertIDMatch_Unknown: + if (matchLevel == nssCertIDMatch_Unknown) { + /* only have non-definitive matches so far, keep it */ + subjectCerts[nextOpenSlot++] = *si; + break; + } + /* else fall through, we have a definitive match already */ + case nssCertIDMatch_No: + default: + NSSCertificate_Destroy(*si); + *si = NULL; + } + } + subjectCerts[nextOpenSlot] = NULL; + return subjectCerts; +} + +static NSSCertificate ** +filter_certs_for_valid_issuers(NSSCertificate **certs) +{ + NSSCertificate **cp; + nssDecodedCert *dcp; + int nextOpenSlot = 0; + + for (cp = certs; *cp; cp++) { + dcp = nssCertificate_GetDecoding(*cp); + if (dcp && dcp->isValidIssuer(dcp)) { + certs[nextOpenSlot++] = *cp; + } else { + NSSCertificate_Destroy(*cp); + } + } + certs[nextOpenSlot] = NULL; + return certs; +} + +static NSSCertificate * +find_cert_issuer( + NSSCertificate *c, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt, + NSSTrustDomain *td, + NSSCryptoContext *cc) +{ + NSSArena *arena; + NSSCertificate **certs = NULL; + NSSCertificate **ccIssuers = NULL; + NSSCertificate **tdIssuers = NULL; + NSSCertificate *issuer = NULL; + + if (!cc) + cc = c->object.cryptoContext; + if (!td) + td = NSSCertificate_GetTrustDomain(c); + arena = nssArena_Create(); + if (!arena) { + return (NSSCertificate *)NULL; + } + if (cc) { + ccIssuers = nssCryptoContext_FindCertificatesBySubject(cc, + &c->issuer, + NULL, + 0, + arena); + } + if (td) + tdIssuers = nssTrustDomain_FindCertificatesBySubject(td, + &c->issuer, + NULL, + 0, + arena); + certs = nssCertificateArray_Join(ccIssuers, tdIssuers); + if (certs) { + nssDecodedCert *dc = NULL; + void *issuerID = NULL; + dc = nssCertificate_GetDecoding(c); + if (dc) { + issuerID = dc->getIssuerIdentifier(dc); + } + /* XXX review based on CERT_FindCertIssuer + * this function is not using the authCertIssuer field as a fallback + * if authority key id does not exist + */ + if (issuerID) { + certs = filter_subject_certs_for_id(certs, issuerID); + } + certs = filter_certs_for_valid_issuers(certs); + issuer = nssCertificateArray_FindBestCertificate(certs, + timeOpt, + usage, + policiesOpt); + nssCertificateArray_Destroy(certs); + } + nssArena_Destroy(arena); + return issuer; +} + +/* This function returns the built chain, as far as it gets, +** even if/when it fails to find an issuer, and returns PR_FAILURE +*/ +NSS_IMPLEMENT NSSCertificate ** +nssCertificate_BuildChain( + NSSCertificate *c, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt, + NSSCertificate **rvOpt, + PRUint32 rvLimit, + NSSArena *arenaOpt, + PRStatus *statusOpt, + NSSTrustDomain *td, + NSSCryptoContext *cc) +{ + NSSCertificate **rvChain = NULL; + NSSUsage issuerUsage = *usage; + nssPKIObjectCollection *collection = NULL; + PRUint32 rvCount = 0; + PRStatus st; + PRStatus ret = PR_SUCCESS; + + if (!c || !cc || + (!td && (td = NSSCertificate_GetTrustDomain(c)) == NULL)) { + goto loser; + } + /* bump the usage up to CA level */ + issuerUsage.nss3lookingForCA = PR_TRUE; + collection = nssCertificateCollection_Create(td, NULL); + if (!collection) + goto loser; + st = nssPKIObjectCollection_AddObject(collection, (nssPKIObject *)c); + if (st != PR_SUCCESS) + goto loser; + for (rvCount = 1; (!rvLimit || rvCount < rvLimit); ++rvCount) { + CERTCertificate *cCert = STAN_GetCERTCertificate(c); + if (cCert->isRoot) { + /* not including the issuer of the self-signed cert, which is, + * of course, itself + */ + break; + } + c = find_cert_issuer(c, timeOpt, &issuerUsage, policiesOpt, td, cc); + if (!c) { + ret = PR_FAILURE; + break; + } + st = nssPKIObjectCollection_AddObject(collection, (nssPKIObject *)c); + nssCertificate_Destroy(c); /* collection has it */ + if (st != PR_SUCCESS) + goto loser; + } + rvChain = nssPKIObjectCollection_GetCertificates(collection, + rvOpt, + rvLimit, + arenaOpt); + if (rvChain) { + nssPKIObjectCollection_Destroy(collection); + if (statusOpt) + *statusOpt = ret; + if (ret != PR_SUCCESS) + nss_SetError(NSS_ERROR_CERTIFICATE_ISSUER_NOT_FOUND); + return rvChain; + } + +loser: + if (collection) + nssPKIObjectCollection_Destroy(collection); + if (statusOpt) + *statusOpt = PR_FAILURE; + nss_SetError(NSS_ERROR_CERTIFICATE_ISSUER_NOT_FOUND); + return rvChain; +} + +NSS_IMPLEMENT NSSCertificate ** +NSSCertificate_BuildChain( + NSSCertificate *c, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt, + NSSCertificate **rvOpt, + PRUint32 rvLimit, /* zero for no limit */ + NSSArena *arenaOpt, + PRStatus *statusOpt, + NSSTrustDomain *td, + NSSCryptoContext *cc) +{ + return nssCertificate_BuildChain(c, timeOpt, usage, policiesOpt, + rvOpt, rvLimit, arenaOpt, statusOpt, + td, cc); +} + +NSS_IMPLEMENT NSSCryptoContext * +nssCertificate_GetCryptoContext(NSSCertificate *c) +{ + return c->object.cryptoContext; +} + +NSS_IMPLEMENT NSSTrustDomain * +nssCertificate_GetTrustDomain(NSSCertificate *c) +{ + return c->object.trustDomain; +} + +NSS_IMPLEMENT NSSTrustDomain * +NSSCertificate_GetTrustDomain(NSSCertificate *c) +{ + return nssCertificate_GetTrustDomain(c); +} + +NSS_IMPLEMENT NSSToken * +NSSCertificate_GetToken( + NSSCertificate *c, + PRStatus *statusOpt) +{ + return (NSSToken *)NULL; +} + +NSS_IMPLEMENT NSSSlot * +NSSCertificate_GetSlot( + NSSCertificate *c, + PRStatus *statusOpt) +{ + return (NSSSlot *)NULL; +} + +NSS_IMPLEMENT NSSModule * +NSSCertificate_GetModule( + NSSCertificate *c, + PRStatus *statusOpt) +{ + return (NSSModule *)NULL; +} + +NSS_IMPLEMENT NSSItem * +NSSCertificate_Encrypt( + NSSCertificate *c, + NSSAlgorithmAndParameters *apOpt, + NSSItem *data, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt) +{ + nss_SetError(NSS_ERROR_NOT_FOUND); + return NULL; +} + +NSS_IMPLEMENT PRStatus +NSSCertificate_Verify( + NSSCertificate *c, + NSSAlgorithmAndParameters *apOpt, + NSSItem *data, + NSSItem *signature, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt, + NSSCallback *uhh) +{ + nss_SetError(NSS_ERROR_NOT_FOUND); + return PR_FAILURE; +} + +NSS_IMPLEMENT NSSItem * +NSSCertificate_VerifyRecover( + NSSCertificate *c, + NSSAlgorithmAndParameters *apOpt, + NSSItem *signature, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt) +{ + nss_SetError(NSS_ERROR_NOT_FOUND); + return NULL; +} + +NSS_IMPLEMENT NSSItem * +NSSCertificate_WrapSymmetricKey( + NSSCertificate *c, + NSSAlgorithmAndParameters *apOpt, + NSSSymmetricKey *keyToWrap, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt) +{ + nss_SetError(NSS_ERROR_NOT_FOUND); + return NULL; +} + +NSS_IMPLEMENT NSSCryptoContext * +NSSCertificate_CreateCryptoContext( + NSSCertificate *c, + NSSAlgorithmAndParameters *apOpt, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt, + NSSCallback *uhh) +{ + nss_SetError(NSS_ERROR_NOT_FOUND); + return NULL; +} + +NSS_IMPLEMENT NSSPublicKey * +NSSCertificate_GetPublicKey( + NSSCertificate *c) +{ +#if 0 + CK_ATTRIBUTE pubktemplate[] = { + { CKA_CLASS, NULL, 0 }, + { CKA_ID, NULL, 0 }, + { CKA_SUBJECT, NULL, 0 } + }; + PRStatus nssrv; + CK_ULONG count = sizeof(pubktemplate) / sizeof(pubktemplate[0]); + NSS_CK_SET_ATTRIBUTE_ITEM(pubktemplate, 0, &g_ck_class_pubkey); + if (c->id.size > 0) { + /* CKA_ID */ + NSS_CK_ITEM_TO_ATTRIBUTE(&c->id, &pubktemplate[1]); + } else { + /* failure, yes? */ + return (NSSPublicKey *)NULL; + } + if (c->subject.size > 0) { + /* CKA_SUBJECT */ + NSS_CK_ITEM_TO_ATTRIBUTE(&c->subject, &pubktemplate[2]); + } else { + /* failure, yes? */ + return (NSSPublicKey *)NULL; + } + /* Try the cert's token first */ + if (c->token) { + nssrv = nssToken_FindObjectByTemplate(c->token, pubktemplate, count); + } +#endif + /* Try all other key tokens */ + return (NSSPublicKey *)NULL; +} + +NSS_IMPLEMENT NSSPrivateKey * +NSSCertificate_FindPrivateKey( + NSSCertificate *c, + NSSCallback *uhh) +{ + nss_SetError(NSS_ERROR_NOT_FOUND); + return NULL; +} + +NSS_IMPLEMENT PRBool +NSSCertificate_IsPrivateKeyAvailable( + NSSCertificate *c, + NSSCallback *uhh, + PRStatus *statusOpt) +{ + PRBool isUser = PR_FALSE; + nssCryptokiObject **ip; + nssCryptokiObject **instances = nssPKIObject_GetInstances(&c->object); + if (!instances) { + return PR_FALSE; + } + for (ip = instances; *ip; ip++) { + nssCryptokiObject *instance = *ip; + if (nssToken_IsPrivateKeyAvailable(instance->token, c, instance)) { + isUser = PR_TRUE; + } + } + nssCryptokiObjectArray_Destroy(instances); + return isUser; +} + +/* sort the subject cert list from newest to oldest */ +PRIntn +nssCertificate_SubjectListSort( + void *v1, + void *v2) +{ + NSSCertificate *c1 = (NSSCertificate *)v1; + NSSCertificate *c2 = (NSSCertificate *)v2; + nssDecodedCert *dc1 = nssCertificate_GetDecoding(c1); + nssDecodedCert *dc2 = nssCertificate_GetDecoding(c2); + if (!dc1) { + return dc2 ? 1 : 0; + } else if (!dc2) { + return -1; + } else { + return dc1->isNewerThan(dc1, dc2) ? -1 : 1; + } +} + +NSS_IMPLEMENT PRBool +NSSUserCertificate_IsStillPresent( + NSSUserCertificate *uc, + PRStatus *statusOpt) +{ + nss_SetError(NSS_ERROR_NOT_FOUND); + return PR_FALSE; +} + +NSS_IMPLEMENT NSSItem * +NSSUserCertificate_Decrypt( + NSSUserCertificate *uc, + NSSAlgorithmAndParameters *apOpt, + NSSItem *data, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt) +{ + nss_SetError(NSS_ERROR_NOT_FOUND); + return NULL; +} + +NSS_IMPLEMENT NSSItem * +NSSUserCertificate_Sign( + NSSUserCertificate *uc, + NSSAlgorithmAndParameters *apOpt, + NSSItem *data, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt) +{ + nss_SetError(NSS_ERROR_NOT_FOUND); + return NULL; +} + +NSS_IMPLEMENT NSSItem * +NSSUserCertificate_SignRecover( + NSSUserCertificate *uc, + NSSAlgorithmAndParameters *apOpt, + NSSItem *data, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt) +{ + nss_SetError(NSS_ERROR_NOT_FOUND); + return NULL; +} + +NSS_IMPLEMENT NSSSymmetricKey * +NSSUserCertificate_UnwrapSymmetricKey( + NSSUserCertificate *uc, + NSSAlgorithmAndParameters *apOpt, + NSSItem *wrappedKey, + NSSTime *timeOpt, + NSSUsage *usage, + NSSPolicies *policiesOpt, + NSSCallback *uhh, + NSSItem *rvOpt, + NSSArena *arenaOpt) +{ + nss_SetError(NSS_ERROR_NOT_FOUND); + return NULL; +} + +NSS_IMPLEMENT NSSSymmetricKey * +NSSUserCertificate_DeriveSymmetricKey( + NSSUserCertificate *uc, /* provides private key */ + NSSCertificate *c, /* provides public key */ + NSSAlgorithmAndParameters *apOpt, + NSSOID *target, + PRUint32 keySizeOpt, /* zero for best allowed */ + NSSOperations operations, + NSSCallback *uhh) +{ + nss_SetError(NSS_ERROR_NOT_FOUND); + return NULL; +} + +NSS_IMPLEMENT nssSMIMEProfile * +nssSMIMEProfile_Create( + NSSCertificate *cert, + NSSItem *profileTime, + NSSItem *profileData) +{ + NSSArena *arena; + nssSMIMEProfile *rvProfile; + nssPKIObject *object; + NSSTrustDomain *td = nssCertificate_GetTrustDomain(cert); + NSSCryptoContext *cc = nssCertificate_GetCryptoContext(cert); + arena = nssArena_Create(); + if (!arena) { + return NULL; + } + object = nssPKIObject_Create(arena, NULL, td, cc, nssPKILock); + if (!object) { + goto loser; + } + rvProfile = nss_ZNEW(arena, nssSMIMEProfile); + if (!rvProfile) { + goto loser; + } + rvProfile->object = *object; + rvProfile->certificate = cert; + rvProfile->email = nssUTF8_Duplicate(cert->email, arena); + rvProfile->subject = nssItem_Duplicate(&cert->subject, arena, NULL); + if (profileTime) { + rvProfile->profileTime = nssItem_Duplicate(profileTime, arena, NULL); + } + if (profileData) { + rvProfile->profileData = nssItem_Duplicate(profileData, arena, NULL); + } + return rvProfile; +loser: + if (object) + nssPKIObject_Destroy(object); + else if (arena) + nssArena_Destroy(arena); + return (nssSMIMEProfile *)NULL; +} + +/* execute a callback function on all members of a cert list */ +NSS_EXTERN PRStatus +nssCertificateList_DoCallback( + nssList *certList, + PRStatus (*callback)(NSSCertificate *c, void *arg), + void *arg) +{ + nssListIterator *certs; + NSSCertificate *cert; + certs = nssList_CreateIterator(certList); + if (!certs) { + return PR_FAILURE; + } + for (cert = (NSSCertificate *)nssListIterator_Start(certs); + cert != (NSSCertificate *)NULL; + cert = (NSSCertificate *)nssListIterator_Next(certs)) { + (void)(*callback)(cert, arg); + } + nssListIterator_Finish(certs); + nssListIterator_Destroy(certs); + return PR_SUCCESS; +} + +static PRStatus +add_ref_callback(NSSCertificate *c, void *a) +{ + nssCertificate_AddRef(c); + return PR_SUCCESS; +} + +NSS_IMPLEMENT void +nssCertificateList_AddReferences( + nssList *certList) +{ + (void)nssCertificateList_DoCallback(certList, add_ref_callback, NULL); +} + +/* + * Is this trust record safe to apply to all certs of the same issuer/SN + * independent of the cert matching the hash. This is only true is the trust + * is unknown or distrusted. In general this feature is only useful to + * explicitly distrusting certs. It is not safe to use to trust certs, so + * only allow unknown and untrusted trust types. + */ +PRBool +nssTrust_IsSafeToIgnoreCertHash(nssTrustLevel serverAuth, + nssTrustLevel clientAuth, nssTrustLevel codeSigning, + nssTrustLevel email, PRBool stepup) +{ + /* step up is a trust type, if it's on, we must have a hash for the cert */ + if (stepup) { + return PR_FALSE; + } + if ((serverAuth != nssTrustLevel_Unknown) && + (serverAuth != nssTrustLevel_NotTrusted)) { + return PR_FALSE; + } + if ((clientAuth != nssTrustLevel_Unknown) && + (clientAuth != nssTrustLevel_NotTrusted)) { + return PR_FALSE; + } + if ((codeSigning != nssTrustLevel_Unknown) && + (codeSigning != nssTrustLevel_NotTrusted)) { + return PR_FALSE; + } + if ((email != nssTrustLevel_Unknown) && + (email != nssTrustLevel_NotTrusted)) { + return PR_FALSE; + } + /* record only has Unknown and Untrusted entries, ok to accept without a + * hash */ + return PR_TRUE; +} + +NSS_IMPLEMENT NSSTrust * +nssTrust_Create( + nssPKIObject *object, + NSSItem *certData) +{ + PRStatus status; + PRUint32 i; + PRUint32 lastTrustOrder, myTrustOrder; + unsigned char sha1_hashcmp[SHA1_LENGTH]; + unsigned char sha1_hashin[SHA1_LENGTH]; + NSSItem sha1_hash; + NSSTrust *rvt; + nssCryptokiObject *instance; + nssTrustLevel serverAuth, clientAuth, codeSigning, emailProtection; + SECStatus rv; /* Should be stan flavor */ + PRBool stepUp; + + lastTrustOrder = 1 << 16; /* just make it big */ + PR_ASSERT(object->instances != NULL && object->numInstances > 0); + rvt = nss_ZNEW(object->arena, NSSTrust); + if (!rvt) { + return (NSSTrust *)NULL; + } + rvt->object = *object; + + /* should be stan flavor of Hashbuf */ + rv = PK11_HashBuf(SEC_OID_SHA1, sha1_hashcmp, certData->data, certData->size); + if (rv != SECSuccess) { + return (NSSTrust *)NULL; + } + sha1_hash.data = sha1_hashin; + sha1_hash.size = sizeof(sha1_hashin); + /* trust has to peek into the base object members */ + nssPKIObject_Lock(object); + for (i = 0; i < object->numInstances; i++) { + instance = object->instances[i]; + myTrustOrder = nssToken_GetTrustOrder(instance->token); + status = nssCryptokiTrust_GetAttributes(instance, NULL, + &sha1_hash, + &serverAuth, + &clientAuth, + &codeSigning, + &emailProtection, + &stepUp); + if (status != PR_SUCCESS) { + nssPKIObject_Unlock(object); + return (NSSTrust *)NULL; + } + /* if no hash is specified, then trust applies to all certs with + * this issuer/SN. NOTE: This is only true for entries that + * have distrust and unknown record */ + if (!( + /* we continue if there is no hash, and the trust type is + * safe to accept without a hash ... or ... */ + ((sha1_hash.size == 0) && + nssTrust_IsSafeToIgnoreCertHash(serverAuth, clientAuth, + codeSigning, emailProtection, + stepUp)) || + /* we have a hash of the correct size, and it matches */ + ((sha1_hash.size == SHA1_LENGTH) && (PORT_Memcmp(sha1_hashin, + sha1_hashcmp, + SHA1_LENGTH) == 0)))) { + nssPKIObject_Unlock(object); + return (NSSTrust *)NULL; + } + if (rvt->serverAuth == nssTrustLevel_Unknown || + myTrustOrder < lastTrustOrder) { + rvt->serverAuth = serverAuth; + } + if (rvt->clientAuth == nssTrustLevel_Unknown || + myTrustOrder < lastTrustOrder) { + rvt->clientAuth = clientAuth; + } + if (rvt->emailProtection == nssTrustLevel_Unknown || + myTrustOrder < lastTrustOrder) { + rvt->emailProtection = emailProtection; + } + if (rvt->codeSigning == nssTrustLevel_Unknown || + myTrustOrder < lastTrustOrder) { + rvt->codeSigning = codeSigning; + } + rvt->stepUpApproved = stepUp; + lastTrustOrder = myTrustOrder; + } + nssPKIObject_Unlock(object); + return rvt; +} + +NSS_IMPLEMENT NSSTrust * +nssTrust_AddRef(NSSTrust *trust) +{ + if (trust) { + nssPKIObject_AddRef(&trust->object); + } + return trust; +} + +NSS_IMPLEMENT PRStatus +nssTrust_Destroy(NSSTrust *trust) +{ + if (trust) { + (void)nssPKIObject_Destroy(&trust->object); + } + return PR_SUCCESS; +} + +NSS_IMPLEMENT nssSMIMEProfile * +nssSMIMEProfile_AddRef(nssSMIMEProfile *profile) +{ + if (profile) { + nssPKIObject_AddRef(&profile->object); + } + return profile; +} + +NSS_IMPLEMENT PRStatus +nssSMIMEProfile_Destroy(nssSMIMEProfile *profile) +{ + if (profile) { + (void)nssPKIObject_Destroy(&profile->object); + } + return PR_SUCCESS; +} + +NSS_IMPLEMENT NSSCRL * +nssCRL_Create(nssPKIObject *object) +{ + PRStatus status; + NSSCRL *rvCRL; + NSSArena *arena = object->arena; + PR_ASSERT(object->instances != NULL && object->numInstances > 0); + rvCRL = nss_ZNEW(arena, NSSCRL); + if (!rvCRL) { + return (NSSCRL *)NULL; + } + rvCRL->object = *object; + /* XXX should choose instance based on some criteria */ + status = nssCryptokiCRL_GetAttributes(object->instances[0], + NULL, /* XXX sessionOpt */ + arena, + &rvCRL->encoding, + NULL, /* subject */ + NULL, /* class */ + &rvCRL->url, + &rvCRL->isKRL); + if (status != PR_SUCCESS) { + if (!arena) { + nssPKIObject_Destroy((nssPKIObject *)rvCRL); + } + return (NSSCRL *)NULL; + } + return rvCRL; +} + +NSS_IMPLEMENT NSSCRL * +nssCRL_AddRef(NSSCRL *crl) +{ + if (crl) { + nssPKIObject_AddRef(&crl->object); + } + return crl; +} + +NSS_IMPLEMENT PRStatus +nssCRL_Destroy(NSSCRL *crl) +{ + if (crl) { + (void)nssPKIObject_Destroy(&crl->object); + } + return PR_SUCCESS; +} + +NSS_IMPLEMENT PRStatus +nssCRL_DeleteStoredObject( + NSSCRL *crl, + NSSCallback *uhh) +{ + return nssPKIObject_DeleteStoredObject(&crl->object, uhh, PR_TRUE); +} + +NSS_IMPLEMENT NSSDER * +nssCRL_GetEncoding(NSSCRL *crl) +{ + if (crl && crl->encoding.data != NULL && crl->encoding.size > 0) { + return &crl->encoding; + } else { + return (NSSDER *)NULL; + } +} |