| /* 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; |
| } |
| |
| 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; |
| } else { |
| return SECSuccess; |
| } |
| } else { |
| PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE); |
| return SECFailure; |
| } |
| return SECSuccess; |
| |
| 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 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 = checkKeyParams(&sd->signatureAlgorithm, pubKey); |
| if (rv != SECSuccess) { |
| 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; |
| } |
| } |
| 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 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); |
| } |
| |
| /* 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 typeAndEKUAllowed = PR_TRUE; |
| 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 (certUsage != certUsageIPsec) { |
| if (!(certType & requiredCertType)) { |
| typeAndEKUAllowed = PR_FALSE; |
| } |
| } else { |
| PRBool isCritical; |
| PRBool allowed = cert_EKUAllowsIPsecIKE(cert, &isCritical); |
| /* If the extension isn't critical, we allow any EKU value. */ |
| if (isCritical && !allowed) { |
| typeAndEKUAllowed = PR_FALSE; |
| } |
| } |
| if (!typeAndEKUAllowed) { |
| 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; |
| |
| 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; |
| } |