Mercurial > trustbridge > nss-cmake-static
diff nss/lib/certhigh/certvfy.c @ 0:1e5118fa0cb1
This is NSS with a Cmake Buildsyste
To compile a static NSS library for Windows we've used the
Chromium-NSS fork and added a Cmake buildsystem to compile
it statically for Windows. See README.chromium for chromium
changes and README.trustbridge for our modifications.
author | Andre Heinecke <andre.heinecke@intevation.de> |
---|---|
date | Mon, 28 Jul 2014 10:47:06 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nss/lib/certhigh/certvfy.c Mon Jul 28 10:47:06 2014 +0200 @@ -0,0 +1,1887 @@ +/* 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 "nspr.h" +#include "secerr.h" +#include "secport.h" +#include "seccomon.h" +#include "secoid.h" +#include "sslerr.h" +#include "genname.h" +#include "keyhi.h" +#include "cert.h" +#include "certdb.h" +#include "certi.h" +#include "cryptohi.h" +#ifndef NSS_DISABLE_LIBPKIX +#include "pkix.h" +/*#include "pkix_sample_modules.h" */ +#include "pkix_pl_cert.h" +#endif /* NSS_DISABLE_LIBPKIX */ + + +#include "nsspki.h" +#include "pkitm.h" +#include "pkim.h" +#include "pki3hack.h" +#include "base.h" + +#ifdef NSS_DISABLE_LIBPKIX +SECStatus +cert_VerifyCertChainPkix( + CERTCertificate *cert, + PRBool checkSig, + SECCertUsage requiredUsage, + PRTime time, + void *wincx, + CERTVerifyLog *log, + PRBool *pSigerror, + PRBool *pRevoked) +{ + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + return SECFailure; +} + +SECStatus +CERT_SetUsePKIXForValidation(PRBool enable) +{ + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + return SECFailure; +} + +PRBool +CERT_GetUsePKIXForValidation() +{ + return PR_FALSE; +} + +SECStatus CERT_PKIXVerifyCert( + CERTCertificate *cert, + SECCertificateUsage usages, + CERTValInParam *paramsIn, + CERTValOutParam *paramsOut, + void *wincx) +{ + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + return SECFailure; +} +#endif /* NSS_DISABLE_LIBPKIX */ + +/* + * Check the validity times of a certificate + */ +SECStatus +CERT_CertTimesValid(CERTCertificate *c) +{ + SECCertTimeValidity valid = CERT_CheckCertValidTimes(c, PR_Now(), PR_TRUE); + return (valid == secCertTimeValid) ? SECSuccess : SECFailure; +} + +/* + * verify the signature of a signed data object with the given DER publickey + */ +SECStatus +CERT_VerifySignedDataWithPublicKey(const CERTSignedData *sd, + SECKEYPublicKey *pubKey, + void *wincx) +{ + SECStatus rv; + SECItem sig; + SECOidTag hashAlg = SEC_OID_UNKNOWN; + + if ( !pubKey || !sd ) { + PORT_SetError(PR_INVALID_ARGUMENT_ERROR); + return SECFailure; + } + + /* check the signature */ + sig = sd->signature; + /* convert sig->len from bit counts to byte count. */ + DER_ConvertBitString(&sig); + + rv = VFY_VerifyDataWithAlgorithmID(sd->data.data, sd->data.len, pubKey, + &sig, &sd->signatureAlgorithm, &hashAlg, wincx); + if (rv == SECSuccess) { + /* Are we honoring signatures for this algorithm? */ + PRUint32 policyFlags = 0; + rv = NSS_GetAlgorithmPolicy(hashAlg, &policyFlags); + if (rv == SECSuccess && + !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) { + PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED); + rv = SECFailure; + } + } + return rv; +} + +/* + * verify the signature of a signed data object with the given DER publickey + */ +SECStatus +CERT_VerifySignedDataWithPublicKeyInfo(CERTSignedData *sd, + CERTSubjectPublicKeyInfo *pubKeyInfo, + void *wincx) +{ + SECKEYPublicKey *pubKey; + SECStatus rv = SECFailure; + + /* get cert's public key */ + pubKey = SECKEY_ExtractPublicKey(pubKeyInfo); + if (pubKey) { + rv = CERT_VerifySignedDataWithPublicKey(sd, pubKey, wincx); + SECKEY_DestroyPublicKey(pubKey); + } + return rv; +} + +/* + * verify the signature of a signed data object with the given certificate + */ +SECStatus +CERT_VerifySignedData(CERTSignedData *sd, CERTCertificate *cert, + PRTime t, void *wincx) +{ + SECKEYPublicKey *pubKey = 0; + SECStatus rv = SECFailure; + SECCertTimeValidity validity; + + /* check the certificate's validity */ + validity = CERT_CheckCertValidTimes(cert, t, PR_FALSE); + if ( validity != secCertTimeValid ) { + return rv; + } + + /* get cert's public key */ + pubKey = CERT_ExtractPublicKey(cert); + if (pubKey) { + rv = CERT_VerifySignedDataWithPublicKey(sd, pubKey, wincx); + SECKEY_DestroyPublicKey(pubKey); + } + return rv; +} + + +SECStatus +SEC_CheckCRL(CERTCertDBHandle *handle,CERTCertificate *cert, + CERTCertificate *caCert, PRTime t, void * wincx) +{ + return CERT_CheckCRL(cert, caCert, NULL, t, wincx); +} + +/* + * Find the issuer of a cert. Use the authorityKeyID if it exists. + */ +CERTCertificate * +CERT_FindCertIssuer(CERTCertificate *cert, PRTime validTime, SECCertUsage usage) +{ + NSSCertificate *me; + NSSTime *nssTime; + NSSTrustDomain *td; + NSSCryptoContext *cc; + NSSCertificate *chain[3]; + NSSUsage nssUsage; + PRStatus status; + + me = STAN_GetNSSCertificate(cert); + if (!me) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + nssTime = NSSTime_SetPRTime(NULL, validTime); + nssUsage.anyUsage = PR_FALSE; + nssUsage.nss3usage = usage; + nssUsage.nss3lookingForCA = PR_TRUE; + memset(chain, 0, 3*sizeof(NSSCertificate *)); + td = STAN_GetDefaultTrustDomain(); + cc = STAN_GetDefaultCryptoContext(); + (void)NSSCertificate_BuildChain(me, nssTime, &nssUsage, NULL, + chain, 2, NULL, &status, td, cc); + nss_ZFreeIf(nssTime); + if (status == PR_SUCCESS) { + PORT_Assert(me == chain[0]); + /* if it's a root, the chain will only have one cert */ + if (!chain[1]) { + /* already has a reference from the call to BuildChain */ + return cert; + } + NSSCertificate_Destroy(chain[0]); /* the first cert in the chain */ + return STAN_GetCERTCertificate(chain[1]); /* return the 2nd */ + } + if (chain[0]) { + PORT_Assert(me == chain[0]); + NSSCertificate_Destroy(chain[0]); /* the first cert in the chain */ + } + PORT_SetError (SEC_ERROR_UNKNOWN_ISSUER); + return NULL; +} + +/* + * return required trust flags for various cert usages for CAs + */ +SECStatus +CERT_TrustFlagsForCACertUsage(SECCertUsage usage, + unsigned int *retFlags, + SECTrustType *retTrustType) +{ + unsigned int requiredFlags; + SECTrustType trustType; + + switch ( usage ) { + case certUsageSSLClient: + requiredFlags = CERTDB_TRUSTED_CLIENT_CA; + trustType = trustSSL; + break; + case certUsageSSLServer: + case certUsageSSLCA: + requiredFlags = CERTDB_TRUSTED_CA; + trustType = trustSSL; + break; + case certUsageSSLServerWithStepUp: + requiredFlags = CERTDB_TRUSTED_CA | CERTDB_GOVT_APPROVED_CA; + trustType = trustSSL; + break; + case certUsageEmailSigner: + case certUsageEmailRecipient: + requiredFlags = CERTDB_TRUSTED_CA; + trustType = trustEmail; + break; + case certUsageObjectSigner: + requiredFlags = CERTDB_TRUSTED_CA; + trustType = trustObjectSigning; + break; + case certUsageVerifyCA: + case certUsageAnyCA: + case certUsageStatusResponder: + requiredFlags = CERTDB_TRUSTED_CA; + trustType = trustTypeNone; + break; + default: + PORT_Assert(0); + goto loser; + } + if ( retFlags != NULL ) { + *retFlags = requiredFlags; + } + if ( retTrustType != NULL ) { + *retTrustType = trustType; + } + + return(SECSuccess); +loser: + return(SECFailure); +} + +void +cert_AddToVerifyLog(CERTVerifyLog *log, CERTCertificate *cert, long error, + unsigned int depth, void *arg) +{ + CERTVerifyLogNode *node, *tnode; + + PORT_Assert(log != NULL); + + node = (CERTVerifyLogNode *)PORT_ArenaAlloc(log->arena, + sizeof(CERTVerifyLogNode)); + if ( node != NULL ) { + node->cert = CERT_DupCertificate(cert); + node->error = error; + node->depth = depth; + node->arg = arg; + + if ( log->tail == NULL ) { + /* empty list */ + log->head = log->tail = node; + node->prev = NULL; + node->next = NULL; + } else if ( depth >= log->tail->depth ) { + /* add to tail */ + node->prev = log->tail; + log->tail->next = node; + log->tail = node; + node->next = NULL; + } else if ( depth < log->head->depth ) { + /* add at head */ + node->prev = NULL; + node->next = log->head; + log->head->prev = node; + log->head = node; + } else { + /* add in middle */ + tnode = log->tail; + while ( tnode != NULL ) { + if ( depth >= tnode->depth ) { + /* insert after tnode */ + node->prev = tnode; + node->next = tnode->next; + tnode->next->prev = node; + tnode->next = node; + break; + } + + tnode = tnode->prev; + } + } + + log->count++; + } + return; +} + +#define EXIT_IF_NOT_LOGGING(log) \ + if ( log == NULL ) { \ + goto loser; \ + } + +#define LOG_ERROR_OR_EXIT(log,cert,depth,arg) \ + if ( log != NULL ) { \ + cert_AddToVerifyLog(log, cert, PORT_GetError(), depth, \ + (void *)(PRWord)arg); \ + } else { \ + goto loser; \ + } + +#define LOG_ERROR(log,cert,depth,arg) \ + if ( log != NULL ) { \ + cert_AddToVerifyLog(log, cert, PORT_GetError(), depth, \ + (void *)(PRWord)arg); \ + } + +static SECStatus +cert_VerifyCertChainOld(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool checkSig, PRBool* sigerror, + SECCertUsage certUsage, PRTime t, void *wincx, + CERTVerifyLog *log, PRBool* revoked) +{ + SECTrustType trustType; + CERTBasicConstraints basicConstraint; + CERTCertificate *issuerCert = NULL; + CERTCertificate *subjectCert = NULL; + CERTCertificate *badCert = NULL; + PRBool isca; + SECStatus rv; + SECStatus rvFinal = SECSuccess; + int count; + int currentPathLen = 0; + int pathLengthLimit = CERT_UNLIMITED_PATH_CONSTRAINT; + unsigned int caCertType; + unsigned int requiredCAKeyUsage; + unsigned int requiredFlags; + PLArenaPool *arena = NULL; + CERTGeneralName *namesList = NULL; + CERTCertificate **certsList = NULL; + int certsListLen = 16; + int namesCount = 0; + PRBool subjectCertIsSelfIssued; + CERTCertTrust issuerTrust; + + if (revoked) { + *revoked = PR_FALSE; + } + + if (CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_TRUE, + &requiredCAKeyUsage, + &caCertType) + != SECSuccess ) { + PORT_Assert(0); + EXIT_IF_NOT_LOGGING(log); + requiredCAKeyUsage = 0; + caCertType = 0; + } + + switch ( certUsage ) { + case certUsageSSLClient: + case certUsageSSLServer: + case certUsageSSLCA: + case certUsageSSLServerWithStepUp: + case certUsageEmailSigner: + case certUsageEmailRecipient: + case certUsageObjectSigner: + case certUsageVerifyCA: + case certUsageAnyCA: + case certUsageStatusResponder: + if ( CERT_TrustFlagsForCACertUsage(certUsage, &requiredFlags, + &trustType) != SECSuccess ) { + PORT_Assert(0); + EXIT_IF_NOT_LOGGING(log); + /* XXX continuing with requiredFlags = 0 seems wrong. It'll + * cause the following test to be true incorrectly: + * flags = SEC_GET_TRUST_FLAGS(issuerCert->trust, trustType); + * if (( flags & requiredFlags ) == requiredFlags) { + * rv = rvFinal; + * goto done; + * } + * There are three other instances of this problem. + */ + requiredFlags = 0; + trustType = trustSSL; + } + break; + default: + PORT_Assert(0); + EXIT_IF_NOT_LOGGING(log); + requiredFlags = 0; + trustType = trustSSL;/* This used to be 0, but we need something + * that matches the enumeration type. + */ + caCertType = 0; + } + + subjectCert = CERT_DupCertificate(cert); + if ( subjectCert == NULL ) { + goto loser; + } + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + goto loser; + } + + certsList = PORT_ZNewArray(CERTCertificate *, certsListLen); + if (certsList == NULL) + goto loser; + + /* RFC 3280 says that the name constraints will apply to the names + ** in the leaf (EE) cert, whether it is self issued or not, so + ** we pretend that it is not. + */ + subjectCertIsSelfIssued = PR_FALSE; + for ( count = 0; count < CERT_MAX_CERT_CHAIN; count++ ) { + PRBool validCAOverride = PR_FALSE; + + /* Construct a list of names for the current and all previous + * certifcates (except leaf (EE) certs, root CAs, and self-issued + * intermediate CAs) to be verified against the name constraints + * extension of the issuer certificate. + */ + if (subjectCertIsSelfIssued == PR_FALSE) { + CERTGeneralName *subjectNameList; + int subjectNameListLen; + int i; + PRBool getSubjectCN = (!count && certUsage == certUsageSSLServer); + subjectNameList = + CERT_GetConstrainedCertificateNames(subjectCert, arena, + getSubjectCN); + if (!subjectNameList) + goto loser; + subjectNameListLen = CERT_GetNamesLength(subjectNameList); + if (!subjectNameListLen) + goto loser; + if (certsListLen <= namesCount + subjectNameListLen) { + CERTCertificate **tmpCertsList; + certsListLen = (namesCount + subjectNameListLen) * 2; + tmpCertsList = + (CERTCertificate **)PORT_Realloc(certsList, + certsListLen * sizeof(CERTCertificate *)); + if (tmpCertsList == NULL) { + goto loser; + } + certsList = tmpCertsList; + } + for (i = 0; i < subjectNameListLen; i++) { + certsList[namesCount + i] = subjectCert; + } + namesCount += subjectNameListLen; + namesList = cert_CombineNamesLists(namesList, subjectNameList); + } + + /* check if the cert has an unsupported critical extension */ + if ( subjectCert->options.bits.hasUnsupportedCriticalExt ) { + PORT_SetError(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION); + LOG_ERROR_OR_EXIT(log,subjectCert,count,0); + } + + /* find the certificate of the issuer */ + issuerCert = CERT_FindCertIssuer(subjectCert, t, certUsage); + if ( ! issuerCert ) { + PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER); + LOG_ERROR(log,subjectCert,count,0); + goto loser; + } + + /* verify the signature on the cert */ + if ( checkSig ) { + rv = CERT_VerifySignedData(&subjectCert->signatureWrap, + issuerCert, t, wincx); + + if ( rv != SECSuccess ) { + if (sigerror) { + *sigerror = PR_TRUE; + } + if ( PORT_GetError() == SEC_ERROR_EXPIRED_CERTIFICATE ) { + PORT_SetError(SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE); + LOG_ERROR_OR_EXIT(log,issuerCert,count+1,0); + } else { + if (PORT_GetError() != + SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + } + LOG_ERROR_OR_EXIT(log,subjectCert,count,0); + } + } + } + + /* If the basicConstraint extension is included in an immediate CA + * certificate, make sure that the isCA flag is on. If the + * pathLenConstraint component exists, it must be greater than the + * number of CA certificates we have seen so far. If the extension + * is omitted, we will assume that this is a CA certificate with + * an unlimited pathLenConstraint (since it already passes the + * netscape-cert-type extension checking). + */ + + rv = CERT_FindBasicConstraintExten(issuerCert, &basicConstraint); + if ( rv != SECSuccess ) { + if (PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) { + LOG_ERROR_OR_EXIT(log,issuerCert,count+1,0); + } + pathLengthLimit = CERT_UNLIMITED_PATH_CONSTRAINT; + /* no basic constraints found, we aren't (yet) a CA. */ + isca = PR_FALSE; + } else { + if ( basicConstraint.isCA == PR_FALSE ) { + PORT_SetError (SEC_ERROR_CA_CERT_INVALID); + LOG_ERROR_OR_EXIT(log,issuerCert,count+1,0); + } + pathLengthLimit = basicConstraint.pathLenConstraint; + isca = PR_TRUE; + } + /* make sure that the path len constraint is properly set.*/ + if (pathLengthLimit >= 0 && currentPathLen > pathLengthLimit) { + PORT_SetError (SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID); + LOG_ERROR_OR_EXIT(log, issuerCert, count+1, pathLengthLimit); + } + + /* make sure that the entire chain is within the name space of the + * current issuer certificate. + */ + rv = CERT_CompareNameSpace(issuerCert, namesList, certsList, + arena, &badCert); + if (rv != SECSuccess || badCert != NULL) { + PORT_SetError(SEC_ERROR_CERT_NOT_IN_NAME_SPACE); + LOG_ERROR_OR_EXIT(log, badCert, count + 1, 0); + goto loser; + } + + /* XXX - the error logging may need to go down into CRL stuff at some + * point + */ + /* check revoked list (issuer) */ + rv = SEC_CheckCRL(handle, subjectCert, issuerCert, t, wincx); + if (rv == SECFailure) { + if (revoked) { + *revoked = PR_TRUE; + } + LOG_ERROR_OR_EXIT(log,subjectCert,count,0); + } else if (rv == SECWouldBlock) { + /* We found something fishy, so we intend to issue an + * error to the user, but the user may wish to continue + * processing, in which case we better make sure nothing + * worse has happened... so keep cranking the loop */ + rvFinal = SECFailure; + if (revoked) { + *revoked = PR_TRUE; + } + LOG_ERROR(log,subjectCert,count,0); + } + + if ( CERT_GetCertTrust(issuerCert, &issuerTrust) == SECSuccess) { + /* we have some trust info, but this does NOT imply that this + * cert is actually trusted for any purpose. The cert may be + * explicitly UNtrusted. We won't know until we examine the + * trust bits. + */ + unsigned int flags; + + if (certUsage != certUsageAnyCA && + certUsage != certUsageStatusResponder) { + + /* + * XXX This choice of trustType seems arbitrary. + */ + if ( certUsage == certUsageVerifyCA ) { + if ( subjectCert->nsCertType & NS_CERT_TYPE_EMAIL_CA ) { + trustType = trustEmail; + } else if ( subjectCert->nsCertType & NS_CERT_TYPE_SSL_CA ) { + trustType = trustSSL; + } else { + trustType = trustObjectSigning; + } + } + + flags = SEC_GET_TRUST_FLAGS(&issuerTrust, trustType); + if (( flags & requiredFlags ) == requiredFlags) { + /* we found a trusted one, so return */ + rv = rvFinal; + goto done; + } + if (flags & CERTDB_VALID_CA) { + validCAOverride = PR_TRUE; + } + /* is it explicitly distrusted? */ + if ((flags & CERTDB_TERMINAL_RECORD) && + ((flags & (CERTDB_TRUSTED|CERTDB_TRUSTED_CA)) == 0)) { + /* untrusted -- the cert is explicitly untrusted, not + * just that it doesn't chain to a trusted cert */ + PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER); + LOG_ERROR_OR_EXIT(log,issuerCert,count+1,flags); + } + } else { + /* Check if we have any valid trust when cheching for + * certUsageAnyCA or certUsageStatusResponder. */ + for (trustType = trustSSL; trustType < trustTypeNone; + trustType++) { + flags = SEC_GET_TRUST_FLAGS(&issuerTrust, trustType); + if ((flags & requiredFlags) == requiredFlags) { + rv = rvFinal; + goto done; + } + if (flags & CERTDB_VALID_CA) + validCAOverride = PR_TRUE; + } + /* We have 2 separate loops because we want any single trust + * bit to allow this usage to return trusted. Only if none of + * the trust bits are on do we check to see if the cert is + * untrusted */ + for (trustType = trustSSL; trustType < trustTypeNone; + trustType++) { + flags = SEC_GET_TRUST_FLAGS(&issuerTrust, trustType); + /* is it explicitly distrusted? */ + if ((flags & CERTDB_TERMINAL_RECORD) && + ((flags & (CERTDB_TRUSTED|CERTDB_TRUSTED_CA)) == 0)) { + /* untrusted -- the cert is explicitly untrusted, not + * just that it doesn't chain to a trusted cert */ + PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER); + LOG_ERROR_OR_EXIT(log,issuerCert,count+1,flags); + } + } + } + } + + if (!validCAOverride) { + /* + * Make sure that if this is an intermediate CA in the chain that + * it was given permission by its signer to be a CA. + */ + /* + * if basicConstraints says it is a ca, then we check the + * nsCertType. If the nsCertType has any CA bits set, then + * it must have the right one. + */ + if (!isca || (issuerCert->nsCertType & NS_CERT_TYPE_CA)) { + isca = (issuerCert->nsCertType & caCertType) ? PR_TRUE : PR_FALSE; + } + + if ( !isca ) { + PORT_SetError(SEC_ERROR_CA_CERT_INVALID); + LOG_ERROR_OR_EXIT(log,issuerCert,count+1,0); + } + + /* make sure key usage allows cert signing */ + if (CERT_CheckKeyUsage(issuerCert, requiredCAKeyUsage) != SECSuccess) { + PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE); + LOG_ERROR_OR_EXIT(log,issuerCert,count+1,requiredCAKeyUsage); + } + } + + /* make sure that the issuer is not self signed. If it is, then + * stop here to prevent looping. + */ + if (issuerCert->isRoot) { + PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER); + LOG_ERROR(log, issuerCert, count+1, 0); + goto loser; + } + /* The issuer cert will be the subject cert in the next loop. + * A cert is self-issued if its subject and issuer are equal and + * both are of non-zero length. + */ + subjectCertIsSelfIssued = (PRBool) + SECITEM_ItemsAreEqual(&issuerCert->derIssuer, + &issuerCert->derSubject) && + issuerCert->derSubject.len > 0; + if (subjectCertIsSelfIssued == PR_FALSE) { + /* RFC 3280 says only non-self-issued intermediate CA certs + * count in path length. + */ + ++currentPathLen; + } + + CERT_DestroyCertificate(subjectCert); + subjectCert = issuerCert; + issuerCert = NULL; + } + + PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER); + LOG_ERROR(log,subjectCert,count,0); +loser: + rv = SECFailure; +done: + if (certsList != NULL) { + PORT_Free(certsList); + } + if ( issuerCert ) { + CERT_DestroyCertificate(issuerCert); + } + + if ( subjectCert ) { + CERT_DestroyCertificate(subjectCert); + } + + if ( arena != NULL ) { + PORT_FreeArena(arena, PR_FALSE); + } + return rv; +} + +SECStatus +cert_VerifyCertChain(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool checkSig, PRBool* sigerror, + SECCertUsage certUsage, PRTime t, void *wincx, + CERTVerifyLog *log, PRBool* revoked) +{ + if (CERT_GetUsePKIXForValidation()) { + return cert_VerifyCertChainPkix(cert, checkSig, certUsage, t, + wincx, log, sigerror, revoked); + } + return cert_VerifyCertChainOld(handle, cert, checkSig, sigerror, + certUsage, t, wincx, log, revoked); +} + +SECStatus +CERT_VerifyCertChain(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool checkSig, SECCertUsage certUsage, PRTime t, + void *wincx, CERTVerifyLog *log) +{ + return cert_VerifyCertChain(handle, cert, checkSig, NULL, certUsage, t, + wincx, log, NULL); +} + +/* + * verify that a CA can sign a certificate with the requested usage. + */ +SECStatus +CERT_VerifyCACertForUsage(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool checkSig, SECCertUsage certUsage, PRTime t, + void *wincx, CERTVerifyLog *log) +{ + SECTrustType trustType; + CERTBasicConstraints basicConstraint; + PRBool isca; + PRBool validCAOverride = PR_FALSE; + SECStatus rv; + SECStatus rvFinal = SECSuccess; + unsigned int flags; + unsigned int caCertType; + unsigned int requiredCAKeyUsage; + unsigned int requiredFlags; + CERTCertificate *issuerCert; + CERTCertTrust certTrust; + + + if (CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_TRUE, + &requiredCAKeyUsage, + &caCertType) != SECSuccess ) { + PORT_Assert(0); + EXIT_IF_NOT_LOGGING(log); + requiredCAKeyUsage = 0; + caCertType = 0; + } + + switch ( certUsage ) { + case certUsageSSLClient: + case certUsageSSLServer: + case certUsageSSLCA: + case certUsageSSLServerWithStepUp: + case certUsageEmailSigner: + case certUsageEmailRecipient: + case certUsageObjectSigner: + case certUsageVerifyCA: + case certUsageStatusResponder: + if ( CERT_TrustFlagsForCACertUsage(certUsage, &requiredFlags, + &trustType) != SECSuccess ) { + PORT_Assert(0); + EXIT_IF_NOT_LOGGING(log); + requiredFlags = 0; + trustType = trustSSL; + } + break; + default: + PORT_Assert(0); + EXIT_IF_NOT_LOGGING(log); + requiredFlags = 0; + trustType = trustSSL;/* This used to be 0, but we need something + * that matches the enumeration type. + */ + caCertType = 0; + } + + /* If the basicConstraint extension is included in an intermmediate CA + * certificate, make sure that the isCA flag is on. If the + * pathLenConstraint component exists, it must be greater than the + * number of CA certificates we have seen so far. If the extension + * is omitted, we will assume that this is a CA certificate with + * an unlimited pathLenConstraint (since it already passes the + * netscape-cert-type extension checking). + */ + + rv = CERT_FindBasicConstraintExten(cert, &basicConstraint); + if ( rv != SECSuccess ) { + if (PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) { + LOG_ERROR_OR_EXIT(log,cert,0,0); + } + /* no basic constraints found, we aren't (yet) a CA. */ + isca = PR_FALSE; + } else { + if ( basicConstraint.isCA == PR_FALSE ) { + PORT_SetError (SEC_ERROR_CA_CERT_INVALID); + LOG_ERROR_OR_EXIT(log,cert,0,0); + } + + /* can't check path length if we don't know the previous path */ + isca = PR_TRUE; + } + + if ( CERT_GetCertTrust(cert, &certTrust) == SECSuccess ) { + /* we have some trust info, but this does NOT imply that this + * cert is actually trusted for any purpose. The cert may be + * explicitly UNtrusted. We won't know until we examine the + * trust bits. + */ + if (certUsage == certUsageStatusResponder) { + /* Check the special case of certUsageStatusResponder */ + issuerCert = CERT_FindCertIssuer(cert, t, certUsage); + if (issuerCert) { + if (SEC_CheckCRL(handle, cert, issuerCert, t, wincx) + != SECSuccess) { + PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE); + CERT_DestroyCertificate(issuerCert); + goto loser; + } + CERT_DestroyCertificate(issuerCert); + } + /* XXX We have NOT determined that this cert is trusted. + * For years, NSS has treated this as trusted, + * but it seems incorrect. + */ + rv = rvFinal; + goto done; + } + + /* + * check the trust params of the issuer + */ + flags = SEC_GET_TRUST_FLAGS(&certTrust, trustType); + if ( ( flags & requiredFlags ) == requiredFlags) { + /* we found a trusted one, so return */ + rv = rvFinal; + goto done; + } + if (flags & CERTDB_VALID_CA) { + validCAOverride = PR_TRUE; + } + /* is it explicitly distrusted? */ + if ((flags & CERTDB_TERMINAL_RECORD) && + ((flags & (CERTDB_TRUSTED|CERTDB_TRUSTED_CA)) == 0)) { + /* untrusted -- the cert is explicitly untrusted, not + * just that it doesn't chain to a trusted cert */ + PORT_SetError(SEC_ERROR_UNTRUSTED_CERT); + LOG_ERROR_OR_EXIT(log,cert,0,flags); + } + } + if (!validCAOverride) { + /* + * Make sure that if this is an intermediate CA in the chain that + * it was given permission by its signer to be a CA. + */ + /* + * if basicConstraints says it is a ca, then we check the + * nsCertType. If the nsCertType has any CA bits set, then + * it must have the right one. + */ + if (!isca || (cert->nsCertType & NS_CERT_TYPE_CA)) { + isca = (cert->nsCertType & caCertType) ? PR_TRUE : PR_FALSE; + } + + if (!isca) { + PORT_SetError(SEC_ERROR_CA_CERT_INVALID); + LOG_ERROR_OR_EXIT(log,cert,0,0); + } + + /* make sure key usage allows cert signing */ + if (CERT_CheckKeyUsage(cert, requiredCAKeyUsage) != SECSuccess) { + PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE); + LOG_ERROR_OR_EXIT(log,cert,0,requiredCAKeyUsage); + } + } + /* make sure that the issuer is not self signed. If it is, then + * stop here to prevent looping. + */ + if (cert->isRoot) { + PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER); + LOG_ERROR(log, cert, 0, 0); + goto loser; + } + + return CERT_VerifyCertChain(handle, cert, checkSig, certUsage, t, + wincx, log); +loser: + rv = SECFailure; +done: + return rv; +} + +#define NEXT_USAGE() { \ + i*=2; \ + certUsage++; \ + continue; \ +} + +#define VALID_USAGE() { \ + NEXT_USAGE(); \ +} + +#define INVALID_USAGE() { \ + if (returnedUsages) { \ + *returnedUsages &= (~i); \ + } \ + if (PR_TRUE == requiredUsage) { \ + valid = SECFailure; \ + } \ + NEXT_USAGE(); \ +} + +/* + * check the leaf cert against trust and usage. + * returns success if the cert is not distrusted. If the cert is + * trusted, then the trusted bool will be true. + * returns failure if the cert is distrusted. If failure, flags + * will return the flag bits that indicated distrust. + */ +SECStatus +cert_CheckLeafTrust(CERTCertificate *cert, SECCertUsage certUsage, + unsigned int *failedFlags, PRBool *trusted) +{ + unsigned int flags; + CERTCertTrust trust; + + *failedFlags = 0; + *trusted = PR_FALSE; + + /* check trust flags to see if this cert is directly trusted */ + if ( CERT_GetCertTrust(cert, &trust) == SECSuccess ) { + switch ( certUsage ) { + case certUsageSSLClient: + case certUsageSSLServer: + flags = trust.sslFlags; + + /* is the cert directly trusted or not trusted ? */ + if ( flags & CERTDB_TERMINAL_RECORD) { /* the trust record is + * authoritative */ + if ( flags & CERTDB_TRUSTED ) { /* trust this cert */ + *trusted = PR_TRUE; + return SECSuccess; + } else { /* don't trust this cert */ + *failedFlags = flags; + return SECFailure; + } + } + break; + case certUsageSSLServerWithStepUp: + /* XXX - step up certs can't be directly trusted, only distrust */ + flags = trust.sslFlags; + if ( flags & CERTDB_TERMINAL_RECORD) { /* the trust record is + * authoritative */ + if (( flags & CERTDB_TRUSTED ) == 0) { + /* don't trust this cert */ + *failedFlags = flags; + return SECFailure; + } + } + break; + case certUsageSSLCA: + flags = trust.sslFlags; + if ( flags & CERTDB_TERMINAL_RECORD) { /* the trust record is + * authoritative */ + if (( flags & (CERTDB_TRUSTED|CERTDB_TRUSTED_CA) ) == 0) { + /* don't trust this cert */ + *failedFlags = flags; + return SECFailure; + } + } + break; + case certUsageEmailSigner: + case certUsageEmailRecipient: + flags = trust.emailFlags; + if ( flags & CERTDB_TERMINAL_RECORD) { /* the trust record is + * authoritative */ + if ( flags & CERTDB_TRUSTED ) { /* trust this cert */ + *trusted = PR_TRUE; + return SECSuccess; + } + else { /* don't trust this cert */ + *failedFlags = flags; + return SECFailure; + } + } + + break; + case certUsageObjectSigner: + flags = trust.objectSigningFlags; + + if ( flags & CERTDB_TERMINAL_RECORD) { /* the trust record is + * authoritative */ + if ( flags & CERTDB_TRUSTED ) { /* trust this cert */ + *trusted = PR_TRUE; + return SECSuccess; + } else { /* don't trust this cert */ + *failedFlags = flags; + return SECFailure; + } + } + break; + case certUsageVerifyCA: + case certUsageStatusResponder: + flags = trust.sslFlags; + /* is the cert directly trusted or not trusted ? */ + if ( ( flags & ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) == + ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) { + *trusted = PR_TRUE; + return SECSuccess; + } + flags = trust.emailFlags; + /* is the cert directly trusted or not trusted ? */ + if ( ( flags & ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) == + ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) { + *trusted = PR_TRUE; + return SECSuccess; + } + flags = trust.objectSigningFlags; + /* is the cert directly trusted or not trusted ? */ + if ( ( flags & ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) == + ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) { + *trusted = PR_TRUE; + return SECSuccess; + } + /* fall through to test distrust */ + case certUsageAnyCA: + case certUsageUserCertImport: + /* do we distrust these certs explicitly */ + flags = trust.sslFlags; + if ( flags & CERTDB_TERMINAL_RECORD) { /* the trust record is + * authoritative */ + if ((flags & (CERTDB_TRUSTED|CERTDB_TRUSTED_CA)) == 0) { + *failedFlags = flags; + return SECFailure; + } + } + flags = trust.emailFlags; + if ( flags & CERTDB_TERMINAL_RECORD) { /* the trust record is + * authoritative */ + if ((flags & (CERTDB_TRUSTED|CERTDB_TRUSTED_CA)) == 0) { + *failedFlags = flags; + return SECFailure; + } + } + /* fall through */ + case certUsageProtectedObjectSigner: + flags = trust.objectSigningFlags; + if ( flags & CERTDB_TERMINAL_RECORD) { /* the trust record is + * authoritative */ + if ((flags & (CERTDB_TRUSTED|CERTDB_TRUSTED_CA)) == 0) { + *failedFlags = flags; + return SECFailure; + } + } + break; + } + } + return SECSuccess; +} + +/* + * verify a certificate by checking if it's valid and that we + * trust the issuer. + * + * certificateUsage contains a bitfield of all cert usages that are + * required for verification to succeed + * + * a bitfield of cert usages is returned in *returnedUsages + * if requiredUsages is non-zero, the returned bitmap is only + * for those required usages, otherwise it is for all usages + * + */ +SECStatus +CERT_VerifyCertificate(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool checkSig, SECCertificateUsage requiredUsages, PRTime t, + void *wincx, CERTVerifyLog *log, SECCertificateUsage* returnedUsages) +{ + SECStatus rv; + SECStatus valid; + unsigned int requiredKeyUsage; + unsigned int requiredCertType; + unsigned int flags; + unsigned int certType; + PRBool allowOverride; + SECCertTimeValidity validity; + CERTStatusConfig *statusConfig; + PRInt32 i; + SECCertUsage certUsage = 0; + PRBool checkedOCSP = PR_FALSE; + PRBool checkAllUsages = PR_FALSE; + PRBool revoked = PR_FALSE; + PRBool sigerror = PR_FALSE; + PRBool trusted = PR_FALSE; + + if (!requiredUsages) { + /* there are no required usages, so the user probably wants to + get status for all usages */ + checkAllUsages = PR_TRUE; + } + + if (returnedUsages) { + *returnedUsages = 0; + } else { + /* we don't have a place to return status for all usages, + so we can skip checks for usages that aren't required */ + checkAllUsages = PR_FALSE; + } + valid = SECSuccess ; /* start off assuming cert is valid */ + + /* make sure that the cert is valid at time t */ + allowOverride = (PRBool)((requiredUsages & certificateUsageSSLServer) || + (requiredUsages & certificateUsageSSLServerWithStepUp)); + validity = CERT_CheckCertValidTimes(cert, t, allowOverride); + if ( validity != secCertTimeValid ) { + valid = SECFailure; + LOG_ERROR_OR_EXIT(log,cert,0,validity); + } + + /* check key usage and netscape cert type */ + cert_GetCertType(cert); + certType = cert->nsCertType; + + for (i=1; i<=certificateUsageHighest && + (SECSuccess == valid || returnedUsages || log) ; ) { + PRBool requiredUsage = (i & requiredUsages) ? PR_TRUE : PR_FALSE; + if (PR_FALSE == requiredUsage && PR_FALSE == checkAllUsages) { + NEXT_USAGE(); + } + if (returnedUsages) { + *returnedUsages |= i; /* start off assuming this usage is valid */ + } + switch ( certUsage ) { + case certUsageSSLClient: + case certUsageSSLServer: + case certUsageSSLServerWithStepUp: + case certUsageSSLCA: + case certUsageEmailSigner: + case certUsageEmailRecipient: + case certUsageObjectSigner: + case certUsageStatusResponder: + rv = CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_FALSE, + &requiredKeyUsage, + &requiredCertType); + if ( rv != SECSuccess ) { + PORT_Assert(0); + /* EXIT_IF_NOT_LOGGING(log); XXX ??? */ + requiredKeyUsage = 0; + requiredCertType = 0; + INVALID_USAGE(); + } + break; + + case certUsageAnyCA: + case certUsageProtectedObjectSigner: + case certUsageUserCertImport: + case certUsageVerifyCA: + /* these usages cannot be verified */ + NEXT_USAGE(); + + default: + PORT_Assert(0); + requiredKeyUsage = 0; + requiredCertType = 0; + INVALID_USAGE(); + } + if ( CERT_CheckKeyUsage(cert, requiredKeyUsage) != SECSuccess ) { + if (PR_TRUE == requiredUsage) { + PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE); + } + LOG_ERROR(log,cert,0,requiredKeyUsage); + INVALID_USAGE(); + } + if ( !( certType & requiredCertType ) ) { + if (PR_TRUE == requiredUsage) { + PORT_SetError(SEC_ERROR_INADEQUATE_CERT_TYPE); + } + LOG_ERROR(log,cert,0,requiredCertType); + INVALID_USAGE(); + } + + rv = cert_CheckLeafTrust(cert, certUsage, &flags, &trusted); + if (rv == SECFailure) { + if (PR_TRUE == requiredUsage) { + PORT_SetError(SEC_ERROR_UNTRUSTED_CERT); + } + LOG_ERROR(log, cert, 0, flags); + INVALID_USAGE(); + } else if (trusted) { + VALID_USAGE(); + } + + if (PR_TRUE == revoked || PR_TRUE == sigerror) { + INVALID_USAGE(); + } + + rv = cert_VerifyCertChain(handle, cert, + checkSig, &sigerror, + certUsage, t, wincx, log, + &revoked); + + if (rv != SECSuccess) { + /* EXIT_IF_NOT_LOGGING(log); XXX ???? */ + INVALID_USAGE(); + } + + /* + * Check OCSP revocation status, but only if the cert we are checking + * is not a status responder itself. We only do this in the case + * where we checked the cert chain (above); explicit trust "wins" + * (avoids status checking, just as it avoids CRL checking) by + * bypassing this code. + */ + + if (PR_FALSE == checkedOCSP) { + checkedOCSP = PR_TRUE; /* only check OCSP once */ + statusConfig = CERT_GetStatusConfig(handle); + if (requiredUsages != certificateUsageStatusResponder && + statusConfig != NULL) { + if (statusConfig->statusChecker != NULL) { + rv = (* statusConfig->statusChecker)(handle, cert, + t, wincx); + if (rv != SECSuccess) { + LOG_ERROR(log,cert,0,0); + revoked = PR_TRUE; + INVALID_USAGE(); + } + } + } + } + + NEXT_USAGE(); + } + +loser: + return(valid); +} + +SECStatus +CERT_VerifyCert(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool checkSig, SECCertUsage certUsage, PRTime t, + void *wincx, CERTVerifyLog *log) +{ + return cert_VerifyCertWithFlags(handle, cert, checkSig, certUsage, t, + CERT_VERIFYCERT_USE_DEFAULTS, wincx, log); +} + +SECStatus +cert_VerifyCertWithFlags(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool checkSig, SECCertUsage certUsage, PRTime t, + PRUint32 flags, void *wincx, CERTVerifyLog *log) +{ + SECStatus rv; + unsigned int requiredKeyUsage; + unsigned int requiredCertType; + unsigned int failedFlags; + unsigned int certType; + PRBool trusted; + PRBool allowOverride; + SECCertTimeValidity validity; + CERTStatusConfig *statusConfig; + +#ifdef notdef + /* check if this cert is in the Evil list */ + rv = CERT_CheckForEvilCert(cert); + if ( rv != SECSuccess ) { + PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE); + LOG_ERROR_OR_EXIT(log,cert,0,0); + } +#endif + + /* make sure that the cert is valid at time t */ + allowOverride = (PRBool)((certUsage == certUsageSSLServer) || + (certUsage == certUsageSSLServerWithStepUp)); + validity = CERT_CheckCertValidTimes(cert, t, allowOverride); + if ( validity != secCertTimeValid ) { + LOG_ERROR_OR_EXIT(log,cert,0,validity); + } + + /* check key usage and netscape cert type */ + cert_GetCertType(cert); + certType = cert->nsCertType; + switch ( certUsage ) { + case certUsageSSLClient: + case certUsageSSLServer: + case certUsageSSLServerWithStepUp: + case certUsageSSLCA: + case certUsageEmailSigner: + case certUsageEmailRecipient: + case certUsageObjectSigner: + case certUsageStatusResponder: + rv = CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_FALSE, + &requiredKeyUsage, + &requiredCertType); + if ( rv != SECSuccess ) { + PORT_Assert(0); + EXIT_IF_NOT_LOGGING(log); + requiredKeyUsage = 0; + requiredCertType = 0; + } + break; + case certUsageVerifyCA: + case certUsageAnyCA: + requiredKeyUsage = KU_KEY_CERT_SIGN; + requiredCertType = NS_CERT_TYPE_CA; + if ( ! ( certType & NS_CERT_TYPE_CA ) ) { + certType |= NS_CERT_TYPE_CA; + } + break; + default: + PORT_Assert(0); + EXIT_IF_NOT_LOGGING(log); + requiredKeyUsage = 0; + requiredCertType = 0; + } + if ( CERT_CheckKeyUsage(cert, requiredKeyUsage) != SECSuccess ) { + PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE); + LOG_ERROR_OR_EXIT(log,cert,0,requiredKeyUsage); + } + if ( !( certType & requiredCertType ) ) { + PORT_SetError(SEC_ERROR_INADEQUATE_CERT_TYPE); + LOG_ERROR_OR_EXIT(log,cert,0,requiredCertType); + } + + rv = cert_CheckLeafTrust(cert, certUsage, &failedFlags, &trusted); + if (rv == SECFailure) { + PORT_SetError(SEC_ERROR_UNTRUSTED_CERT); + LOG_ERROR_OR_EXIT(log, cert, 0, failedFlags); + } else if (trusted) { + goto done; + } + + + rv = CERT_VerifyCertChain(handle, cert, checkSig, certUsage, + t, wincx, log); + if (rv != SECSuccess) { + EXIT_IF_NOT_LOGGING(log); + } + + /* + * Check revocation status, but only if the cert we are checking is not a + * status responder itself and the caller did not ask us to skip the check. + * We only do this in the case where we checked the cert chain (above); + * explicit trust "wins" (avoids status checking, just as it avoids CRL + * checking, which is all done inside VerifyCertChain) by bypassing this + * code. + */ + if (!(flags & CERT_VERIFYCERT_SKIP_OCSP) && + certUsage != certUsageStatusResponder) { + statusConfig = CERT_GetStatusConfig(handle); + if (statusConfig && statusConfig->statusChecker) { + rv = (* statusConfig->statusChecker)(handle, cert, + t, wincx); + if (rv != SECSuccess) { + LOG_ERROR_OR_EXIT(log,cert,0,0); + } + } + } + +done: + if (log && log->head) { + return SECFailure; + } + return(SECSuccess); + +loser: + rv = SECFailure; + + return(rv); +} + +/* + * verify a certificate by checking if its valid and that we + * trust the issuer. Verify time against now. + */ +SECStatus +CERT_VerifyCertificateNow(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool checkSig, SECCertificateUsage requiredUsages, + void *wincx, SECCertificateUsage* returnedUsages) +{ + return(CERT_VerifyCertificate(handle, cert, checkSig, + requiredUsages, PR_Now(), wincx, NULL, returnedUsages)); +} + +/* obsolete, do not use for new code */ +SECStatus +CERT_VerifyCertNow(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool checkSig, SECCertUsage certUsage, void *wincx) +{ + return(CERT_VerifyCert(handle, cert, checkSig, + certUsage, PR_Now(), wincx, NULL)); +} + + +/* [ FROM pcertdb.c ] */ +/* + * Supported usage values and types: + * certUsageSSLClient + * certUsageSSLServer + * certUsageSSLServerWithStepUp + * certUsageEmailSigner + * certUsageEmailRecipient + * certUsageObjectSigner + */ + +CERTCertificate * +CERT_FindMatchingCert(CERTCertDBHandle *handle, SECItem *derName, + CERTCertOwner owner, SECCertUsage usage, + PRBool preferTrusted, PRTime validTime, PRBool validOnly) +{ + CERTCertList *certList = NULL; + CERTCertificate *cert = NULL; + CERTCertTrust certTrust; + unsigned int requiredTrustFlags; + SECTrustType requiredTrustType; + unsigned int flags; + + PRBool lookingForCA = PR_FALSE; + SECStatus rv; + CERTCertListNode *node; + CERTCertificate *saveUntrustedCA = NULL; + + /* if preferTrusted is set, must be a CA cert */ + PORT_Assert( ! ( preferTrusted && ( owner != certOwnerCA ) ) ); + + if ( owner == certOwnerCA ) { + lookingForCA = PR_TRUE; + if ( preferTrusted ) { + rv = CERT_TrustFlagsForCACertUsage(usage, &requiredTrustFlags, + &requiredTrustType); + if ( rv != SECSuccess ) { + goto loser; + } + requiredTrustFlags |= CERTDB_VALID_CA; + } + } + + certList = CERT_CreateSubjectCertList(NULL, handle, derName, validTime, + validOnly); + if ( certList != NULL ) { + rv = CERT_FilterCertListByUsage(certList, usage, lookingForCA); + if ( rv != SECSuccess ) { + goto loser; + } + + node = CERT_LIST_HEAD(certList); + + while ( !CERT_LIST_END(node, certList) ) { + cert = node->cert; + + /* looking for a trusted CA cert */ + if ( ( owner == certOwnerCA ) && preferTrusted && + ( requiredTrustType != trustTypeNone ) ) { + + if ( CERT_GetCertTrust(cert, &certTrust) != SECSuccess ) { + flags = 0; + } else { + flags = SEC_GET_TRUST_FLAGS(&certTrust, requiredTrustType); + } + + if ( ( flags & requiredTrustFlags ) != requiredTrustFlags ) { + /* cert is not trusted */ + /* if this is the first cert to get this far, then save + * it, so we can use it if we can't find a trusted one + */ + if ( saveUntrustedCA == NULL ) { + saveUntrustedCA = cert; + } + goto endloop; + } + } + /* if we got this far, then this cert meets all criteria */ + break; + +endloop: + node = CERT_LIST_NEXT(node); + cert = NULL; + } + + /* use the saved one if we have it */ + if ( cert == NULL ) { + cert = saveUntrustedCA; + } + + /* if we found one then bump the ref count before freeing the list */ + if ( cert != NULL ) { + /* bump the ref count */ + cert = CERT_DupCertificate(cert); + } + + CERT_DestroyCertList(certList); + } + + return(cert); + +loser: + if ( certList != NULL ) { + CERT_DestroyCertList(certList); + } + + return(NULL); +} + + +/* [ From certdb.c ] */ +/* + * Filter a list of certificates, removing those certs that do not have + * one of the named CA certs somewhere in their cert chain. + * + * "certList" - the list of certificates to filter + * "nCANames" - number of CA names + * "caNames" - array of CA names in string(rfc 1485) form + * "usage" - what use the certs are for, this is used when + * selecting CA certs + */ +SECStatus +CERT_FilterCertListByCANames(CERTCertList *certList, int nCANames, + char **caNames, SECCertUsage usage) +{ + CERTCertificate *issuerCert = NULL; + CERTCertificate *subjectCert; + CERTCertListNode *node, *freenode; + CERTCertificate *cert; + int n; + char **names; + PRBool found; + PRTime time; + + if ( nCANames <= 0 ) { + return(SECSuccess); + } + + time = PR_Now(); + + node = CERT_LIST_HEAD(certList); + + while ( ! CERT_LIST_END(node, certList) ) { + cert = node->cert; + + subjectCert = CERT_DupCertificate(cert); + + /* traverse the CA certs for this cert */ + found = PR_FALSE; + while ( subjectCert != NULL ) { + n = nCANames; + names = caNames; + + if (subjectCert->issuerName != NULL) { + while ( n > 0 ) { + if ( PORT_Strcmp(*names, subjectCert->issuerName) == 0 ) { + found = PR_TRUE; + break; + } + + n--; + names++; + } + } + + if ( found ) { + break; + } + + issuerCert = CERT_FindCertIssuer(subjectCert, time, usage); + if ( issuerCert == subjectCert ) { + CERT_DestroyCertificate(issuerCert); + issuerCert = NULL; + break; + } + CERT_DestroyCertificate(subjectCert); + subjectCert = issuerCert; + + } + CERT_DestroyCertificate(subjectCert); + if ( !found ) { + /* CA was not found, so remove this cert from the list */ + freenode = node; + node = CERT_LIST_NEXT(node); + CERT_RemoveCertListNode(freenode); + } else { + /* CA was found, so leave it in the list */ + node = CERT_LIST_NEXT(node); + } + } + + return(SECSuccess); +} + +/* + * Given a certificate, return a string containing the nickname, and possibly + * one of the validity strings, based on the current validity state of the + * certificate. + * + * "arena" - arena to allocate returned string from. If NULL, then heap + * is used. + * "cert" - the cert to get nickname from + * "expiredString" - the string to append to the nickname if the cert is + * expired. + * "notYetGoodString" - the string to append to the nickname if the cert is + * not yet good. + */ +char * +CERT_GetCertNicknameWithValidity(PLArenaPool *arena, CERTCertificate *cert, + char *expiredString, char *notYetGoodString) +{ + SECCertTimeValidity validity; + char *nickname = NULL, *tmpstr = NULL; + + validity = CERT_CheckCertValidTimes(cert, PR_Now(), PR_FALSE); + + /* if the cert is good, then just use the nickname directly */ + if ( validity == secCertTimeValid ) { + if ( arena == NULL ) { + nickname = PORT_Strdup(cert->nickname); + } else { + nickname = PORT_ArenaStrdup(arena, cert->nickname); + } + + if ( nickname == NULL ) { + goto loser; + } + } else { + + /* if the cert is not valid, then tack one of the strings on the + * end + */ + if ( validity == secCertTimeExpired ) { + tmpstr = PR_smprintf("%s%s", cert->nickname, + expiredString); + } else if ( validity == secCertTimeNotValidYet ) { + /* not yet valid */ + tmpstr = PR_smprintf("%s%s", cert->nickname, + notYetGoodString); + } else { + /* undetermined */ + tmpstr = PR_smprintf("%s", + "(NULL) (Validity Unknown)"); + } + + if ( tmpstr == NULL ) { + goto loser; + } + + if ( arena ) { + /* copy the string into the arena and free the malloc'd one */ + nickname = PORT_ArenaStrdup(arena, tmpstr); + PORT_Free(tmpstr); + } else { + nickname = tmpstr; + } + if ( nickname == NULL ) { + goto loser; + } + } + return(nickname); + +loser: + return(NULL); +} + +/* + * Collect the nicknames from all certs in a CertList. If the cert is not + * valid, append a string to that nickname. + * + * "certList" - the list of certificates + * "expiredString" - the string to append to the nickname of any expired cert + * "notYetGoodString" - the string to append to the nickname of any cert + * that is not yet valid + */ +CERTCertNicknames * +CERT_NicknameStringsFromCertList(CERTCertList *certList, char *expiredString, + char *notYetGoodString) +{ + CERTCertNicknames *names; + PLArenaPool *arena; + CERTCertListNode *node; + char **nn; + + /* allocate an arena */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if ( arena == NULL ) { + return(NULL); + } + + /* allocate the structure */ + names = PORT_ArenaAlloc(arena, sizeof(CERTCertNicknames)); + if ( names == NULL ) { + goto loser; + } + + /* init the structure */ + names->arena = arena; + names->head = NULL; + names->numnicknames = 0; + names->nicknames = NULL; + names->totallen = 0; + + /* count the certs in the list */ + node = CERT_LIST_HEAD(certList); + while ( ! CERT_LIST_END(node, certList) ) { + names->numnicknames++; + node = CERT_LIST_NEXT(node); + } + + /* allocate nicknames array */ + names->nicknames = PORT_ArenaAlloc(arena, + sizeof(char *) * names->numnicknames); + if ( names->nicknames == NULL ) { + goto loser; + } + + /* just in case printf can't deal with null strings */ + if (expiredString == NULL ) { + expiredString = ""; + } + + if ( notYetGoodString == NULL ) { + notYetGoodString = ""; + } + + /* traverse the list of certs and collect the nicknames */ + nn = names->nicknames; + node = CERT_LIST_HEAD(certList); + while ( ! CERT_LIST_END(node, certList) ) { + *nn = CERT_GetCertNicknameWithValidity(arena, node->cert, + expiredString, + notYetGoodString); + if ( *nn == NULL ) { + goto loser; + } + + names->totallen += PORT_Strlen(*nn); + + nn++; + node = CERT_LIST_NEXT(node); + } + + return(names); + +loser: + PORT_FreeArena(arena, PR_FALSE); + return(NULL); +} + +/* + * Extract the nickname from a nickmake string that may have either + * expiredString or notYetGoodString appended. + * + * Args: + * "namestring" - the string containing the nickname, and possibly + * one of the validity label strings + * "expiredString" - the expired validity label string + * "notYetGoodString" - the not yet good validity label string + * + * Returns the raw nickname + */ +char * +CERT_ExtractNicknameString(char *namestring, char *expiredString, + char *notYetGoodString) +{ + int explen, nyglen, namelen; + int retlen; + char *retstr; + + namelen = PORT_Strlen(namestring); + explen = PORT_Strlen(expiredString); + nyglen = PORT_Strlen(notYetGoodString); + + if ( namelen > explen ) { + if ( PORT_Strcmp(expiredString, &namestring[namelen-explen]) == 0 ) { + retlen = namelen - explen; + retstr = (char *)PORT_Alloc(retlen+1); + if ( retstr == NULL ) { + goto loser; + } + + PORT_Memcpy(retstr, namestring, retlen); + retstr[retlen] = '\0'; + goto done; + } + } + + if ( namelen > nyglen ) { + if ( PORT_Strcmp(notYetGoodString, &namestring[namelen-nyglen]) == 0) { + retlen = namelen - nyglen; + retstr = (char *)PORT_Alloc(retlen+1); + if ( retstr == NULL ) { + goto loser; + } + + PORT_Memcpy(retstr, namestring, retlen); + retstr[retlen] = '\0'; + goto done; + } + } + + /* if name string is shorter than either invalid string, then it must + * be a raw nickname + */ + retstr = PORT_Strdup(namestring); + +done: + return(retstr); + +loser: + return(NULL); +} + +CERTCertList * +CERT_GetCertChainFromCert(CERTCertificate *cert, PRTime time, SECCertUsage usage) +{ + CERTCertList *chain = NULL; + int count = 0; + + if (NULL == cert) { + return NULL; + } + + cert = CERT_DupCertificate(cert); + if (NULL == cert) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + chain = CERT_NewCertList(); + if (NULL == chain) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + while (cert != NULL && ++count <= CERT_MAX_CERT_CHAIN) { + if (SECSuccess != CERT_AddCertToListTail(chain, cert)) { + /* return partial chain */ + PORT_SetError(SEC_ERROR_NO_MEMORY); + return chain; + } + + if (cert->isRoot) { + /* return complete chain */ + return chain; + } + + cert = CERT_FindCertIssuer(cert, time, usage); + } + + /* return partial chain */ + PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER); + return chain; +}