gecko-dev/security/nss/lib/pki/certificate.c

1102 строки
30 KiB
C

/* 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;
}
}