/* 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 "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_pl_cert.h" #else #include "nss.h" #endif /* NSS_DISABLE_LIBPKIX */ #include "nsspki.h" #include "pkitm.h" #include "pkim.h" #include "pki3hack.h" #include "base.h" #include "keyi.h" /* * 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; } static SECStatus checkKeyParams(const SECAlgorithmID *sigAlgorithm, const SECKEYPublicKey *key) { SECStatus rv; SECOidTag sigAlg; SECOidTag curve; PRUint32 policyFlags = 0; PRInt32 minLen, len; sigAlg = SECOID_GetAlgorithmTag(sigAlgorithm); switch (sigAlg) { case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE: case SEC_OID_ANSIX962_ECDSA_SHA224_SIGNATURE: case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE: case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE: case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE: if (key->keyType != ecKey) { PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); return SECFailure; } curve = SECKEY_GetECCOid(&key->u.ec.DEREncodedParams); if (curve != 0) { if (NSS_GetAlgorithmPolicy(curve, &policyFlags) == SECFailure || !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) { PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED); return SECFailure; } return SECSuccess; } PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE); return SECFailure; case SEC_OID_PKCS1_RSA_PSS_SIGNATURE: { PORTCheapArenaPool tmpArena; SECOidTag hashAlg; SECOidTag maskHashAlg; PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE); rv = sec_DecodeRSAPSSParams(&tmpArena.arena, &sigAlgorithm->parameters, &hashAlg, &maskHashAlg, NULL); PORT_DestroyCheapArena(&tmpArena); if (rv != SECSuccess) { return SECFailure; } if (NSS_GetAlgorithmPolicy(hashAlg, &policyFlags) == SECSuccess && !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) { PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED); return SECFailure; } if (NSS_GetAlgorithmPolicy(maskHashAlg, &policyFlags) == SECSuccess && !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) { PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED); return SECFailure; } } /* fall through to RSA key checking */ case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION: case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION: case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION: case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION: case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION: case SEC_OID_ISO_SHA_WITH_RSA_SIGNATURE: case SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE: if (key->keyType != rsaKey && key->keyType != rsaPssKey) { PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); return SECFailure; } len = 8 * key->u.rsa.modulus.len; rv = NSS_OptionGet(NSS_RSA_MIN_KEY_SIZE, &minLen); if (rv != SECSuccess) { return SECFailure; } if (len < minLen) { return SECFailure; } return SECSuccess; case SEC_OID_ANSIX9_DSA_SIGNATURE: case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST: case SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST: case SEC_OID_SDN702_DSA_SIGNATURE: case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA224_DIGEST: case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA256_DIGEST: if (key->keyType != dsaKey) { PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); return SECFailure; } len = 8 * key->u.dsa.params.prime.len; rv = NSS_OptionGet(NSS_DSA_MIN_KEY_SIZE, &minLen); if (rv != SECSuccess) { return SECFailure; } if (len < minLen) { return SECFailure; } return SECSuccess; default: return SECSuccess; } } /* * 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 sigAlg; SECOidTag encAlg; SECOidTag hashAlg; PRUint32 policyFlags; if (!pubKey || !sd) { PORT_SetError(PR_INVALID_ARGUMENT_ERROR); return SECFailure; } /* Can we use this algorithm for signature verification? */ sigAlg = SECOID_GetAlgorithmTag(&sd->signatureAlgorithm); rv = sec_DecodeSigAlg(pubKey, sigAlg, &sd->signatureAlgorithm.parameters, &encAlg, &hashAlg); if (rv != SECSuccess) { return SECFailure; /* error is set */ } rv = NSS_GetAlgorithmPolicy(encAlg, &policyFlags); if (rv == SECSuccess && !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) { PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED); return SECFailure; } rv = NSS_GetAlgorithmPolicy(hashAlg, &policyFlags); if (rv == SECSuccess && !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) { PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED); return SECFailure; } rv = checkKeyParams(&sd->signatureAlgorithm, pubKey); if (rv != SECSuccess) { PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED); 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) { return SECFailure; /* error is set */ } /* for some algorithms, hash algorithm is only known after verification */ rv = NSS_GetAlgorithmPolicy(hashAlg, &policyFlags); if (rv == SECSuccess && !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) { PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED); return SECFailure; } return SECSuccess; } /* * 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 certUsageIPsec: 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); \ } /* /C=CN/O=WoSign CA Limited/CN=CA \xE6\xB2\x83\xE9\x80\x9A\xE6\xA0\xB9\xE8\xAF\x81\xE4\xB9\xA6 * Using a consistent naming convention, this would actually be called * 'CA沃通根证书DN', but since GCC 6.2.1 apparently can't handle UTF-8 * identifiers, this will have to do. */ static const unsigned char CAWoSignRootDN[72] = { 0x30, 0x46, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11, 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D, 0x69, 0x74, 0x65, 0x64, 0x31, 0x1B, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x12, 0x43, 0x41, 0x20, 0xE6, 0xB2, 0x83, 0xE9, 0x80, 0x9A, 0xE6, 0xA0, 0xB9, 0xE8, 0xAF, 0x81, 0xE4, 0xB9, 0xA6 }; /* /C=CN/O=WoSign CA Limited/CN=CA WoSign ECC Root */ static const unsigned char CAWoSignECCRootDN[72] = { 0x30, 0x46, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11, 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D, 0x69, 0x74, 0x65, 0x64, 0x31, 0x1B, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x43, 0x41, 0x20, 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x45, 0x43, 0x43, 0x20, 0x52, 0x6F, 0x6F, 0x74 }; /* /C=CN/O=WoSign CA Limited/CN=Certification Authority of WoSign */ static const unsigned char CertificationAuthorityofWoSignDN[87] = { 0x30, 0x55, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11, 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D, 0x69, 0x74, 0x65, 0x64, 0x31, 0x2A, 0x30, 0x28, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x21, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79, 0x20, 0x6F, 0x66, 0x20, 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E }; /* /C=CN/O=WoSign CA Limited/CN=Certification Authority of WoSign G2 */ static const unsigned char CertificationAuthorityofWoSignG2DN[90] = { 0x30, 0x58, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11, 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D, 0x69, 0x74, 0x65, 0x64, 0x31, 0x2D, 0x30, 0x2B, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x24, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79, 0x20, 0x6F, 0x66, 0x20, 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x47, 0x32 }; /* /C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Certification Authority */ static const unsigned char StartComCertificationAuthorityDN[127] = { 0x30, 0x7D, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, 0x4C, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x0D, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20, 0x4C, 0x74, 0x64, 0x2E, 0x31, 0x2B, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x22, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6C, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79 }; /* /C=IL/O=StartCom Ltd./CN=StartCom Certification Authority G2 */ static const unsigned char StartComCertificationAuthorityG2DN[85] = { 0x30, 0x53, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, 0x4C, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x0D, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20, 0x4C, 0x74, 0x64, 0x2E, 0x31, 0x2C, 0x30, 0x2A, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x23, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79, 0x20, 0x47, 0x32 }; struct DataAndLength { const unsigned char *data; PRUint32 len; }; static const struct DataAndLength StartComAndWoSignDNs[] = { { CAWoSignRootDN, sizeof(CAWoSignRootDN) }, { CAWoSignECCRootDN, sizeof(CAWoSignECCRootDN) }, { CertificationAuthorityofWoSignDN, sizeof(CertificationAuthorityofWoSignDN) }, { CertificationAuthorityofWoSignG2DN, sizeof(CertificationAuthorityofWoSignG2DN) }, { StartComCertificationAuthorityDN, sizeof(StartComCertificationAuthorityDN) }, { StartComCertificationAuthorityG2DN, sizeof(StartComCertificationAuthorityG2DN) }, }; static PRBool CertIsStartComOrWoSign(const CERTCertificate *cert) { int i; const struct DataAndLength *dn = StartComAndWoSignDNs; for (i = 0; i < sizeof(StartComAndWoSignDNs) / sizeof(struct DataAndLength); ++i, dn++) { if (cert->derSubject.len == dn->len && memcmp(cert->derSubject.data, dn->data, dn->len) == 0) { return PR_TRUE; } } return PR_FALSE; } SECStatus isIssuerCertAllowedAtCertIssuanceTime(CERTCertificate *issuerCert, CERTCertificate *referenceCert) { if (!issuerCert || !referenceCert) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } if (CertIsStartComOrWoSign(issuerCert)) { /* PRTime is microseconds since the epoch, whereas JS time is milliseconds. * (new Date("2016-10-21T00:00:00Z")).getTime() * 1000 */ static const PRTime OCTOBER_21_2016 = 1477008000000000; PRTime notBefore, notAfter; SECStatus rv; rv = CERT_GetCertTimes(referenceCert, ¬Before, ¬After); if (rv != SECSuccess) return rv; if (notBefore > OCTOBER_21_2016) { return SECFailure; } } return SECSuccess; } 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 certUsageIPsec: 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 || certUsage == certUsageIPsec)); 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); } /* check that the signatureAlgorithm field of the certificate * matches the signature field of the tbsCertificate */ if (SECOID_CompareAlgorithmID( &subjectCert->signatureWrap.signatureAlgorithm, &subjectCert->signature)) { PORT_SetError(SEC_ERROR_ALGORITHM_MISMATCH); LOG_ERROR(log, subjectCert, count, 0); goto loser; } /* 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; } rv = isIssuerCertAllowedAtCertIssuanceTime(issuerCert, cert); if (rv != SECSuccess) { PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER); LOG_ERROR(log, issuerCert, 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 certUsageIPsec: 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: case certUsageIPsec: 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) || (requiredUsages & certificateUsageIPsec)); 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: case certUsageIPsec: 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) || (certUsage == certUsageIPsec)); 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 certUsageIPsec: 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 * certUsageIPsec * 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; const char *srcNickname = cert->nickname; if (!srcNickname) { srcNickname = "{???}"; } 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(srcNickname); } else { nickname = PORT_ArenaStrdup(arena, srcNickname); } 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", srcNickname, expiredString); } else if (validity == secCertTimeNotValidYet) { /* not yet valid */ tmpstr = PR_smprintf("%s%s", srcNickname, 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; }