Mercurial > trustbridge > nss-cmake-static
view nss/lib/certhigh/certvfy.c @ 4:b513267f632f tip
Build DBM module
author | Andre Heinecke <andre.heinecke@intevation.de> |
---|---|
date | Tue, 05 Aug 2014 18:58:03 +0200 |
parents | 1e5118fa0cb1 |
children |
line wrap: on
line source
/* 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; }