author | Brian Smith <brian@briansmith.org> |
Thu, 15 May 2014 18:59:52 -0700 | |
changeset 183447 | a4ae7060f43ac1a4e49b30dfd7a95c5212940d4b |
parent 183446 | fe7bffe6bb06fbe0cf047f1c979acb14875d3d1c |
child 183448 | 6deed68c2358d584d0988b200adc23fc88aa4113 |
push id | 43556 |
push user | brian@briansmith.org |
push date | Fri, 16 May 2014 07:43:05 +0000 |
treeherder | mozilla-inbound@6deed68c2358 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | keeler |
bugs | 1006958 |
milestone | 32.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/security/apps/AppSignatureVerification.cpp +++ b/security/apps/AppSignatureVerification.cpp @@ -600,17 +600,18 @@ VerifySignature(AppTrustedRoot trustedRo // Verify certificate. AppTrustDomain trustDomain(nullptr); // TODO: null pinArg if (trustDomain.SetTrustedRoot(trustedRoot) != SECSuccess) { return MapSECStatus(SECFailure); } if (BuildCertChain(trustDomain, signerCert, PR_Now(), EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE, - KeyPurposeId::id_kp_codeSigning, SEC_OID_X509_ANY_POLICY, + KeyPurposeId::id_kp_codeSigning, + CertPolicyId::anyPolicy, nullptr, builtChain) != SECSuccess) { return MapSECStatus(SECFailure); } // See NSS_CMSContentInfo_GetContentTypeOID, which isn't exported from NSS. SECOidData* contentTypeOidData = SECOID_FindOID(&signedData->contentInfo.contentType);
--- a/security/apps/AppTrustDomain.cpp +++ b/security/apps/AppTrustDomain.cpp @@ -98,25 +98,25 @@ AppTrustDomain::FindPotentialIssuers(con results = CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(), encodedIssuerName, time, true); return SECSuccess; } SECStatus AppTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA, - SECOidTag policy, + const CertPolicyId& policy, const CERTCertificate* candidateCert, /*out*/ TrustLevel* trustLevel) { - MOZ_ASSERT(policy == SEC_OID_X509_ANY_POLICY); + MOZ_ASSERT(policy.IsAnyPolicy()); MOZ_ASSERT(candidateCert); MOZ_ASSERT(trustLevel); MOZ_ASSERT(mTrustedRoot); - if (!candidateCert || !trustLevel || policy != SEC_OID_X509_ANY_POLICY) { + if (!candidateCert || !trustLevel || !policy.IsAnyPolicy()) { PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; } if (!mTrustedRoot) { PR_SetError(PR_INVALID_STATE_ERROR, 0); return SECFailure; }
--- a/security/apps/AppTrustDomain.h +++ b/security/apps/AppTrustDomain.h @@ -16,17 +16,17 @@ namespace mozilla { namespace psm { class AppTrustDomain MOZ_FINAL : public mozilla::pkix::TrustDomain { public: AppTrustDomain(void* pinArg); SECStatus SetTrustedRoot(AppTrustedRoot trustedRoot); SECStatus GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA, - SECOidTag policy, + const mozilla::pkix::CertPolicyId& policy, const CERTCertificate* candidateCert, /*out*/ mozilla::pkix::TrustLevel* trustLevel) MOZ_OVERRIDE; SECStatus FindPotentialIssuers(const SECItem* encodedIssuerName, PRTime time, /*out*/ mozilla::pkix::ScopedCERTCertList& results) MOZ_OVERRIDE; SECStatus VerifySignedData(const CERTSignedData* signedData, const CERTCertificate* cert) MOZ_OVERRIDE;
--- a/security/certverifier/CertVerifier.cpp +++ b/security/certverifier/CertVerifier.cpp @@ -296,17 +296,17 @@ destroyCertListThatShouldNotExist(CERTCe } } #endif static SECStatus BuildCertChainForOneKeyUsage(TrustDomain& trustDomain, CERTCertificate* cert, PRTime time, KeyUsages ku1, KeyUsages ku2, KeyUsages ku3, KeyPurposeId eku, - SECOidTag requiredPolicy, + const CertPolicyId& requiredPolicy, const SECItem* stapledOCSPResponse, ScopedCERTCertList& builtChain) { PR_ASSERT(ku1); PR_ASSERT(ku2); SECStatus rv = BuildCertChain(trustDomain, cert, time, EndEntityOrCA::MustBeEndEntity, ku1, @@ -387,47 +387,48 @@ CertVerifier::MozillaPKIXVerifyCert( case certificateUsageSSLClient: { // XXX: We don't really have a trust bit for SSL client authentication so // just use trustEmail as it is the closest alternative. NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache, pinArg); rv = BuildCertChain(trustDomain, cert, time, EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE, KeyPurposeId::id_kp_clientAuth, - SEC_OID_X509_ANY_POLICY, stapledOCSPResponse, + CertPolicyId::anyPolicy, stapledOCSPResponse, builtChain); break; } case certificateUsageSSLServer: { // TODO: When verifying a certificate in an SSL handshake, we should // restrict the acceptable key usage based on the key exchange method // chosen by the server. #ifndef MOZ_NO_EV_CERTS // Try to validate for EV first. - SECOidTag evPolicy = SEC_OID_UNKNOWN; - rv = GetFirstEVPolicy(cert, evPolicy); - if (rv == SECSuccess && evPolicy != SEC_OID_UNKNOWN) { + CertPolicyId evPolicy; + SECOidTag evPolicyOidTag; + rv = GetFirstEVPolicy(cert, evPolicy, evPolicyOidTag); + if (rv == SECSuccess) { NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching == NSSCertDBTrustDomain::NeverFetchOCSP ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV : NSSCertDBTrustDomain::FetchOCSPForEV, mOCSPCache, pinArg, &callbackContainer); rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time, KU_DIGITAL_SIGNATURE, // ECDHE/DHE KU_KEY_ENCIPHERMENT, // RSA KU_KEY_AGREEMENT, // ECDH/DH KeyPurposeId::id_kp_serverAuth, evPolicy, stapledOCSPResponse, builtChain); if (rv == SECSuccess) { if (evOidPolicy) { - *evOidPolicy = evPolicy; + *evOidPolicy = evPolicyOidTag; } break; } builtChain = nullptr; // clear built chain, just in case. } #endif if (flags & FLAG_MUST_BE_EV) { @@ -439,63 +440,65 @@ CertVerifier::MozillaPKIXVerifyCert( // Now try non-EV. NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache, pinArg, &callbackContainer); rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time, KU_DIGITAL_SIGNATURE, // ECDHE/DHE KU_KEY_ENCIPHERMENT, // RSA KU_KEY_AGREEMENT, // ECDH/DH KeyPurposeId::id_kp_serverAuth, - SEC_OID_X509_ANY_POLICY, + CertPolicyId::anyPolicy, stapledOCSPResponse, builtChain); break; } case certificateUsageSSLCA: { NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache, pinArg); rv = BuildCertChain(trustDomain, cert, time, EndEntityOrCA::MustBeCA, KU_KEY_CERT_SIGN, KeyPurposeId::id_kp_serverAuth, - SEC_OID_X509_ANY_POLICY, + CertPolicyId::anyPolicy, stapledOCSPResponse, builtChain); break; } case certificateUsageEmailSigner: { NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache, pinArg); rv = BuildCertChain(trustDomain, cert, time, EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE, - KeyPurposeId::id_kp_emailProtection, SEC_OID_X509_ANY_POLICY, + KeyPurposeId::id_kp_emailProtection, + CertPolicyId::anyPolicy, stapledOCSPResponse, builtChain); break; } case certificateUsageEmailRecipient: { // TODO: The higher level S/MIME processing should pass in which key // usage it is trying to verify for, and base its algorithm choices // based on the result of the verification(s). NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache, pinArg); rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time, KU_KEY_ENCIPHERMENT, // RSA KU_KEY_AGREEMENT, // ECDH/DH 0, KeyPurposeId::id_kp_emailProtection, - SEC_OID_X509_ANY_POLICY, + CertPolicyId::anyPolicy, stapledOCSPResponse, builtChain); break; } case certificateUsageObjectSigner: { NSSCertDBTrustDomain trustDomain(trustObjectSigning, ocspFetching, mOCSPCache, pinArg); rv = BuildCertChain(trustDomain, cert, time, EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE, - KeyPurposeId::id_kp_codeSigning, SEC_OID_X509_ANY_POLICY, + KeyPurposeId::id_kp_codeSigning, + CertPolicyId::anyPolicy, stapledOCSPResponse, builtChain); break; } case certificateUsageVerifyCA: case certificateUsageStatusResponder: { // XXX This is a pretty useless way to verify a certificate. It is used // by the implementation of window.crypto.importCertificates and in the @@ -512,30 +515,30 @@ CertVerifier::MozillaPKIXVerifyCert( endEntityOrCA = EndEntityOrCA::MustBeEndEntity; keyUsage = KU_DIGITAL_SIGNATURE; eku = KeyPurposeId::id_kp_OCSPSigning; } NSSCertDBTrustDomain sslTrust(trustSSL, ocspFetching, mOCSPCache, pinArg); rv = BuildCertChain(sslTrust, cert, time, endEntityOrCA, - keyUsage, eku, SEC_OID_X509_ANY_POLICY, + keyUsage, eku, CertPolicyId::anyPolicy, stapledOCSPResponse, builtChain); if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) { NSSCertDBTrustDomain emailTrust(trustEmail, ocspFetching, mOCSPCache, pinArg); rv = BuildCertChain(emailTrust, cert, time, endEntityOrCA, keyUsage, - eku, SEC_OID_X509_ANY_POLICY, + eku, CertPolicyId::anyPolicy, stapledOCSPResponse, builtChain); if (rv == SECFailure && SEC_ERROR_UNKNOWN_ISSUER) { NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning, ocspFetching, mOCSPCache, pinArg); rv = BuildCertChain(objectSigningTrust, cert, time, endEntityOrCA, - keyUsage, eku, SEC_OID_X509_ANY_POLICY, + keyUsage, eku, CertPolicyId::anyPolicy, stapledOCSPResponse, builtChain); } } break; } default: @@ -608,17 +611,18 @@ CertVerifier::VerifyCert(CERTCertificate #ifndef NSS_NO_LIBPKIX ScopedCERTCertList trustAnchors; SECStatus rv; SECOidTag evPolicy = SEC_OID_UNKNOWN; // Do EV checking only for sslserver usage if (usage == certificateUsageSSLServer) { - SECStatus srv = GetFirstEVPolicy(cert, evPolicy); + CertPolicyId unusedPolicyId; + SECStatus srv = GetFirstEVPolicy(cert, unusedPolicyId, evPolicy); if (srv == SECSuccess) { if (evPolicy != SEC_OID_UNKNOWN) { trustAnchors = GetRootsForOid(evPolicy); } if (!trustAnchors) { return SECFailure; } // pkix ignores an empty trustanchors list and
--- a/security/certverifier/ExtendedValidation.cpp +++ b/security/certverifier/ExtendedValidation.cpp @@ -5,16 +5,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ExtendedValidation.h" #include "cert.h" #include "certdb.h" #include "base64.h" #include "pkix/nullptr.h" +#include "pkix/pkixtypes.h" #include "pk11pub.h" #include "secerr.h" #include "prerror.h" #include "prinit.h" #ifdef PR_LOGGING extern PRLogModuleInfo* gPIPNSSLog; #endif @@ -875,29 +876,31 @@ GetRootsForOid(SECOidTag oid_tag) } return certList; } #endif bool CertIsAuthoritativeForEVPolicy(const CERTCertificate* cert, - SECOidTag policyOidTag) + const mozilla::pkix::CertPolicyId& policy) { PR_ASSERT(cert); - PR_ASSERT(policyOidTag != SEC_OID_UNKNOWN); - if (!cert || !policyOidTag) { + if (!cert) { return false; } for (size_t iEV = 0; iEV < PR_ARRAY_SIZE(myTrustedEVInfos); ++iEV) { nsMyTrustedEVInfo& entry = myTrustedEVInfos[iEV]; - if (entry.oid_tag == policyOidTag && entry.cert && - CERT_CompareCerts(cert, entry.cert)) { - return true; + if (entry.cert && CERT_CompareCerts(cert, entry.cert)) { + const SECOidData* oidData = SECOID_FindOIDByTag(entry.oid_tag); + if (oidData && oidData->oid.len == policy.numBytes && + !memcmp(oidData->oid.data, policy.bytes, policy.numBytes)) { + return true; + } } } return false; } static PRStatus IdentityInfoInit() @@ -1000,20 +1003,24 @@ CleanupIdentityInfo() } } memset(&sIdentityInfoCallOnce, 0, sizeof(PRCallOnceType)); } // Find the first policy OID that is known to be an EV policy OID. SECStatus -GetFirstEVPolicy(CERTCertificate* cert, SECOidTag& outOidTag) +GetFirstEVPolicy(CERTCertificate* cert, + /*out*/ mozilla::pkix::CertPolicyId& policy, + /*out*/ SECOidTag& policyOidTag) { - if (!cert) + if (!cert) { + PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; + } if (cert->extensions) { for (int i=0; cert->extensions[i]; i++) { const SECItem* oid = &cert->extensions[i]->id; SECOidTag oidTag = SECOID_FindOIDTag(oid); if (oidTag != SEC_OID_X509_CERTIFICATE_POLICIES) continue; @@ -1030,24 +1037,35 @@ GetFirstEVPolicy(CERTCertificate* cert, policyInfos = policies->policyInfos; bool found = false; while (*policyInfos) { const CERTPolicyInfo* policyInfo = *policyInfos++; SECOidTag oid_tag = policyInfo->oid; if (oid_tag != SEC_OID_UNKNOWN && isEVPolicy(oid_tag)) { - // in our list of OIDs accepted for EV - outOidTag = oid_tag; - found = true; + const SECOidData* oidData = SECOID_FindOIDByTag(oid_tag); + PR_ASSERT(oidData); + PR_ASSERT(oidData->oid.data); + PR_ASSERT(oidData->oid.len > 0); + PR_ASSERT(oidData->oid.len <= mozilla::pkix::CertPolicyId::MAX_BYTES); + if (oidData && oidData->oid.data && oidData->oid.len > 0 && + oidData->oid.len <= mozilla::pkix::CertPolicyId::MAX_BYTES) { + policy.numBytes = static_cast<uint16_t>(oidData->oid.len); + memcpy(policy.bytes, oidData->oid.data, policy.numBytes); + policyOidTag = oid_tag; + found = true; + } break; } } CERT_DestroyCertificatePoliciesExtension(policies); - if (found) + if (found) { return SECSuccess; + } } } + PR_SetError(SEC_ERROR_POLICY_VALIDATION_FAILED, 0); return SECFailure; } } } // namespace mozilla::psm
--- a/security/certverifier/ExtendedValidation.h +++ b/security/certverifier/ExtendedValidation.h @@ -4,27 +4,31 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_psm_ExtendedValidation_h #define mozilla_psm_ExtendedValidation_h #include "certt.h" #include "prtypes.h" +namespace mozilla { namespace pkix { struct CertPolicyId; } } + namespace mozilla { namespace psm { #ifndef MOZ_NO_EV_CERTS void EnsureIdentityInfoLoaded(); void CleanupIdentityInfo(); -SECStatus GetFirstEVPolicy(CERTCertificate* cert, SECOidTag& outOidTag); +SECStatus GetFirstEVPolicy(CERTCertificate* cert, + /*out*/ mozilla::pkix::CertPolicyId& policy, + /*out*/ SECOidTag& policyOidTag); // CertIsAuthoritativeForEVPolicy does NOT evaluate whether the cert is trusted // or distrusted. bool CertIsAuthoritativeForEVPolicy(const CERTCertificate* cert, - SECOidTag policyOidTag); + const mozilla::pkix::CertPolicyId& policy); #endif #ifndef NSS_NO_LIBPKIX CERTCertList* GetRootsForOid(SECOidTag oid_tag); #endif } } // namespace mozilla::psm
--- a/security/certverifier/NSSCertDBTrustDomain.cpp +++ b/security/certverifier/NSSCertDBTrustDomain.cpp @@ -63,30 +63,30 @@ NSSCertDBTrustDomain::FindPotentialIssue // "there was an error trying to retrieve the potential issuers." results = CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(), encodedIssuerName, time, true); return SECSuccess; } SECStatus NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA, - SECOidTag policy, + const CertPolicyId& policy, const CERTCertificate* candidateCert, /*out*/ TrustLevel* trustLevel) { PR_ASSERT(candidateCert); PR_ASSERT(trustLevel); if (!candidateCert || !trustLevel) { PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; } #ifdef MOZ_NO_EV_CERTS - if (policy != SEC_OID_X509_ANY_POLICY) { + if (!policy.IsAnyPolicy()) { PR_SetError(SEC_ERROR_POLICY_VALIDATION_FAILED, 0); return SECFailure; } #endif // XXX: CERT_GetCertTrust seems to be abusing SECStatus as a boolean, where // SECSuccess means that there is a trust record and SECFailure means there // is not a trust record. I looked at NSS's internal uses of @@ -109,17 +109,17 @@ NSSCertDBTrustDomain::GetCertTrust(EndEn *trustLevel = TrustLevel::ActivelyDistrusted; return SECSuccess; } // For TRUST, we only use the CERTDB_TRUSTED_CA bit, because Gecko hasn't // needed to consider end-entity certs to be their own trust anchors since // Gecko implemented nsICertOverrideService. if (flags & CERTDB_TRUSTED_CA) { - if (policy == SEC_OID_X509_ANY_POLICY) { + if (policy.IsAnyPolicy()) { *trustLevel = TrustLevel::TrustAnchor; return SECSuccess; } #ifndef MOZ_NO_EV_CERTS if (CertIsAuthoritativeForEVPolicy(candidateCert, policy)) { *trustLevel = TrustLevel::TrustAnchor; return SECSuccess; }
--- a/security/certverifier/NSSCertDBTrustDomain.h +++ b/security/certverifier/NSSCertDBTrustDomain.h @@ -61,17 +61,17 @@ public: CERTChainVerifyCallback* checkChainCallback = nullptr); virtual SECStatus FindPotentialIssuers( const SECItem* encodedIssuerName, PRTime time, /*out*/ mozilla::pkix::ScopedCERTCertList& results); virtual SECStatus GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA, - SECOidTag policy, + const mozilla::pkix::CertPolicyId& policy, const CERTCertificate* candidateCert, /*out*/ mozilla::pkix::TrustLevel* trustLevel); virtual SECStatus VerifySignedData(const CERTSignedData* signedData, const CERTCertificate* cert); virtual SECStatus CheckRevocation(mozilla::pkix::EndEntityOrCA endEntityOrCA, const CERTCertificate* cert,
--- a/security/pkix/include/pkix/bind.h +++ b/security/pkix/include/pkix/bind.h @@ -91,17 +91,17 @@ private: B2& b2; void operator=(const Bind2&) /*= delete*/; }; template <typename R, typename P1, typename B1, typename B2, typename B3> class Bind3 { public: - typedef R (*F)(P1&, B1&, B2&, B3&); + typedef R (*F)(P1&, B1, B2&, B3&); Bind3(F f, B1& b1, B2& b2, B3& b3) : f(f), b1(b1), b2(b2), b3(b3) { } R operator()(P1& p1) const { return f(p1, b1, b2, b3); } private: const F f; B1& b1; B2& b2; B3& b3; void operator=(const Bind3&) /*= delete*/; @@ -137,20 +137,20 @@ bind(R (*f)(P1&, B1&), Placeholder1&, B1 template <typename R, typename P1, typename B1, typename B2> inline internal::Bind2<R, P1, B1, B2> bind(R (*f)(P1&, B1&, B2&), Placeholder1&, B1& b1, B2& b2) { return internal::Bind2<R, P1, B1, B2>(f, b1, b2); } template <typename R, typename P1, typename B1, typename B2, typename B3> -inline internal::Bind3<R, P1, B1, B2, B3> -bind(R (*f)(P1&, B1&, B2&, B3&), Placeholder1&, B1& b1, B2& b2, B3& b3) +inline internal::Bind3<R, P1, const B1, const B2, B3> +bind(R (*f)(P1&, B1, const B2&, B3&), Placeholder1&, B1& b1, const B2& b2, B3& b3) { - return internal::Bind3<R, P1, B1, B2, B3>(f, b1, b2, b3); + return internal::Bind3<R, P1, const B1, const B2, B3>(f, b1, b2, b3); } template <typename R, typename P1, typename B1, typename B2, typename B3, typename B4> inline internal::Bind4<R, P1, const B1, const B2, B3, B4> bind(R (*f)(P1&, B1, B2, B3&, B4&), Placeholder1&, const B1& b1, const B2& b2, B3& b3, B4& b4) {
--- a/security/pkix/include/pkix/pkix.h +++ b/security/pkix/include/pkix/pkix.h @@ -90,17 +90,17 @@ namespace mozilla { namespace pkix { // TODO(bug 968451): Document more of these. SECStatus BuildCertChain(TrustDomain& trustDomain, CERTCertificate* cert, PRTime time, EndEntityOrCA endEntityOrCA, /*optional*/ KeyUsages requiredKeyUsagesIfPresent, KeyPurposeId requiredEKUIfPresent, - /*optional*/ SECOidTag requiredPolicy, + const CertPolicyId& requiredPolicy, /*optional*/ const SECItem* stapledOCSPResponse, /*out*/ ScopedCERTCertList& results); // Verify the given signed data using the public key of the given certificate. // (EC)DSA parameter inheritance is not supported. SECStatus VerifySignedData(const CERTSignedData* sd, const CERTCertificate* cert, void* pkcs11PinArg);
--- a/security/pkix/include/pkix/pkixtypes.h +++ b/security/pkix/include/pkix/pkixtypes.h @@ -25,16 +25,17 @@ #ifndef mozilla_pkix__pkixtypes_h #define mozilla_pkix__pkixtypes_h #include "pkix/enumclass.h" #include "pkix/ScopedPtr.h" #include "plarena.h" #include "cert.h" #include "keyhi.h" +#include "stdint.h" namespace mozilla { namespace pkix { typedef ScopedPtr<PLArenaPool, PL_FreeArenaPool> ScopedPLArenaPool; typedef ScopedPtr<CERTCertificate, CERT_DestroyCertificate> ScopedCERTCertificate; typedef ScopedPtr<CERTCertList, CERT_DestroyCertList> ScopedCERTCertList; @@ -49,16 +50,26 @@ MOZILLA_PKIX_ENUM_CLASS KeyPurposeId { anyExtendedKeyUsage = 0, id_kp_serverAuth = 1, // id-kp-serverAuth id_kp_clientAuth = 2, // id-kp-clientAuth id_kp_codeSigning = 3, // id-kp-codeSigning id_kp_emailProtection = 4, // id-kp-emailProtection id_kp_OCSPSigning = 9, // id-kp-OCSPSigning }; +struct CertPolicyId { + uint16_t numBytes; + static const uint16_t MAX_BYTES = 24; + uint8_t bytes[MAX_BYTES]; + + bool IsAnyPolicy() const; + + static const CertPolicyId anyPolicy; +}; + MOZILLA_PKIX_ENUM_CLASS TrustLevel { TrustAnchor = 1, // certificate is a trusted root CA certificate or // equivalent *for the given policy*. ActivelyDistrusted = 2, // certificate is known to be bad InheritsTrust = 3 // certificate must chain to a trust anchor }; // Applications control the behavior of path building and verification by @@ -69,26 +80,26 @@ class TrustDomain { public: virtual ~TrustDomain() { } // Determine the level of trust in the given certificate for the given role. // This will be called for every certificate encountered during path // building. // - // When policy == SEC_OID_X509_ANY_POLICY, then no policy-related checking - // should be done. When policy != SEC_OID_X509_ANY_POLICY, then GetCertTrust - // MUST NOT return with *trustLevel == TrustAnchor unless the given cert is - // considered a trust anchor *for that policy*. In particular, if the user - // has marked an intermediate certificate as trusted, but that intermediate - // isn't in the list of EV roots, then GetCertTrust must result in + // When policy.IsAnyPolicy(), then no policy-related checking should be done. + // When !policy.IsAnyPolicy(), then GetCertTrust MUST NOT return with + // *trustLevel == TrustAnchor unless the given cert is considered a trust + // anchor *for that policy*. In particular, if the user has marked an + // intermediate certificate as trusted, but that intermediate isn't in the + // list of EV roots, then GetCertTrust must result in // *trustLevel == InheritsTrust instead of *trustLevel == TrustAnchor // (assuming the candidate cert is not actively distrusted). virtual SECStatus GetCertTrust(EndEntityOrCA endEntityOrCA, - SECOidTag policy, + const CertPolicyId& policy, const CERTCertificate* candidateCert, /*out*/ TrustLevel* trustLevel) = 0; // Find all certificates (intermediate and/or root) in the certificate // database that have a subject name matching |encodedIssuerName| at // the given time. Certificates where the given time is not within the // certificate's validity period may be excluded. On input, |results| // will be null on input. If no potential issuers are found, then this
--- a/security/pkix/lib/pkixbuild.cpp +++ b/security/pkix/lib/pkixbuild.cpp @@ -110,29 +110,29 @@ BackCert::Init() } static Result BuildForward(TrustDomain& trustDomain, BackCert& subject, PRTime time, EndEntityOrCA endEntityOrCA, KeyUsages requiredKeyUsagesIfPresent, KeyPurposeId requiredEKUIfPresent, - SECOidTag requiredPolicy, + const CertPolicyId& requiredPolicy, /*optional*/ const SECItem* stapledOCSPResponse, unsigned int subCACount, /*out*/ ScopedCERTCertList& results); // The code that executes in the inner loop of BuildForward static Result BuildForwardInner(TrustDomain& trustDomain, BackCert& subject, PRTime time, EndEntityOrCA endEntityOrCA, KeyPurposeId requiredEKUIfPresent, - SECOidTag requiredPolicy, + const CertPolicyId& requiredPolicy, CERTCertificate* potentialIssuerCertToDup, unsigned int subCACount, ScopedCERTCertList& results) { PORT_Assert(potentialIssuerCertToDup); BackCert potentialIssuer(potentialIssuerCertToDup, &subject, BackCert::IncludeCN::No); @@ -192,17 +192,17 @@ BuildForwardInner(TrustDomain& trustDoma // pkix/pkix.h. static Result BuildForward(TrustDomain& trustDomain, BackCert& subject, PRTime time, EndEntityOrCA endEntityOrCA, KeyUsages requiredKeyUsagesIfPresent, KeyPurposeId requiredEKUIfPresent, - SECOidTag requiredPolicy, + const CertPolicyId& requiredPolicy, /*optional*/ const SECItem* stapledOCSPResponse, unsigned int subCACount, /*out*/ ScopedCERTCertList& results) { // Avoid stack overflows and poor performance by limiting cert length. // XXX: 6 is not enough for chains.sh anypolicywithlevel.cfg tests static const size_t MAX_DEPTH = 8; if (subCACount >= MAX_DEPTH - 1) { @@ -332,17 +332,17 @@ BuildForward(TrustDomain& trustDomain, SECStatus BuildCertChain(TrustDomain& trustDomain, CERTCertificate* certToDup, PRTime time, EndEntityOrCA endEntityOrCA, /*optional*/ KeyUsages requiredKeyUsagesIfPresent, /*optional*/ KeyPurposeId requiredEKUIfPresent, - /*optional*/ SECOidTag requiredPolicy, + const CertPolicyId& requiredPolicy, /*optional*/ const SECItem* stapledOCSPResponse, /*out*/ ScopedCERTCertList& results) { PORT_Assert(certToDup); if (!certToDup) { PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure;
--- a/security/pkix/lib/pkixcheck.cpp +++ b/security/pkix/lib/pkixcheck.cpp @@ -102,73 +102,126 @@ CheckKeyUsage(EndEntityOrCA endEntityOrC // return Fail(RecoverableError, SEC_ERROR_INADEQUATE_CERT_TYPE); //} } return Success; } // RFC5820 4.2.1.4. Certificate Policies -// + // "The user-initial-policy-set contains the special value any-policy if the // user is not concerned about certificate policy." -Result -CheckCertificatePolicies(BackCert& cert, EndEntityOrCA endEntityOrCA, - bool isTrustAnchor, SECOidTag requiredPolicy) +// +// id-ce OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 29} +// id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 } +// anyPolicy OBJECT IDENTIFIER ::= { id-ce-certificatePolicies 0 } + +/*static*/ const CertPolicyId CertPolicyId::anyPolicy = { + 4, { (40*2)+5, 29, 32, 0 } +}; + +bool CertPolicyId::IsAnyPolicy() const { - if (requiredPolicy == SEC_OID_X509_ANY_POLICY) { - return Success; + return this == &anyPolicy || + (numBytes == anyPolicy.numBytes && + !memcmp(bytes, anyPolicy.bytes, anyPolicy.numBytes)); +} + +// PolicyInformation ::= SEQUENCE { +// policyIdentifier CertPolicyId, +// policyQualifiers SEQUENCE SIZE (1..MAX) OF +// PolicyQualifierInfo OPTIONAL } +inline der::Result +CheckPolicyInformation(der::Input& input, EndEntityOrCA endEntityOrCA, + const CertPolicyId& requiredPolicy, + /*in/out*/ bool& found) +{ + if (input.MatchTLV(der::OIDTag, requiredPolicy.numBytes, + requiredPolicy.bytes)) { + found = true; + } else if (endEntityOrCA == EndEntityOrCA::MustBeCA && + input.MatchTLV(der::OIDTag, CertPolicyId::anyPolicy.numBytes, + CertPolicyId::anyPolicy.bytes)) { + found = true; } - // It is likely some callers will pass SEC_OID_UNKNOWN when they don't care, - // instead of passing SEC_OID_X509_ANY_POLICY. Help them out by failing hard. - if (requiredPolicy == SEC_OID_UNKNOWN) { - PR_SetError(SEC_ERROR_INVALID_ARGS, 0); - return FatalError; + // RFC 5280 Section 4.2.1.4 says "Optional qualifiers, which MAY be present, + // are not expected to change the definition of the policy." Also, it seems + // that Section 6, which defines validation, does not require any matching of + // qualifiers. Thus, doing anything with the policy qualifiers would be a + // waste of time and a source of potential incompatibilities, so we just + // ignore them. + + // Skip unmatched OID and/or policyQualifiers + input.SkipToEnd(); + + return der::Success; +} + +// certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation +Result +CheckCertificatePolicies(EndEntityOrCA endEntityOrCA, + const SECItem* encodedCertificatePolicies, + const SECItem* encodedInhibitAnyPolicy, + TrustLevel trustLevel, + const CertPolicyId& requiredPolicy) +{ + if (requiredPolicy.numBytes == 0 || + requiredPolicy.numBytes > sizeof requiredPolicy.bytes) { + return Fail(FatalError, SEC_ERROR_INVALID_ARGS); + } + + // Ignore all policy information if the caller indicates any policy is + // acceptable. See TrustDomain::GetCertTrust and the policy part of + // BuildCertChain's documentation. + if (requiredPolicy.IsAnyPolicy()) { + return Success; } // Bug 989051. Until we handle inhibitAnyPolicy we will fail close when // inhibitAnyPolicy extension is present and we need to evaluate certificate // policies. - if (cert.encodedInhibitAnyPolicy) { + if (encodedInhibitAnyPolicy) { return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED); } // The root CA certificate may omit the policies that it has been // trusted for, so we cannot require the policies to be present in those // certificates. Instead, the determination of which roots are trusted for // which policies is made by the TrustDomain's GetCertTrust method. - if (isTrustAnchor && endEntityOrCA == EndEntityOrCA::MustBeCA) { + if (trustLevel == TrustLevel::TrustAnchor && + endEntityOrCA == EndEntityOrCA::MustBeCA) { return Success; } - if (!cert.encodedCertificatePolicies) { + if (!encodedCertificatePolicies) { return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED); } - ScopedPtr<CERTCertificatePolicies, CERT_DestroyCertificatePoliciesExtension> - policies(CERT_DecodeCertificatePoliciesExtension( - cert.encodedCertificatePolicies)); - if (!policies) { - return MapSECStatus(SECFailure); + bool found = false; + + der::Input input; + if (input.Init(encodedCertificatePolicies->data, + encodedCertificatePolicies->len) != der::Success) { + return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED); + } + if (der::NestedOf(input, der::SEQUENCE, der::SEQUENCE, der::EmptyAllowed::No, + bind(CheckPolicyInformation, _1, endEntityOrCA, + requiredPolicy, ref(found))) != der::Success) { + return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED); + } + if (der::End(input) != der::Success) { + return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED); + } + if (!found) { + return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED); } - for (const CERTPolicyInfo* const* policyInfos = policies->policyInfos; - *policyInfos; ++policyInfos) { - if ((*policyInfos)->oid == requiredPolicy) { - return Success; - } - // Intermediate certs are allowed to have the anyPolicy OID - if (endEntityOrCA == EndEntityOrCA::MustBeCA && - (*policyInfos)->oid == SEC_OID_X509_ANY_POLICY) { - return Success; - } - } - - return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED); + return Success; } static const long UNLIMITED_PATH_LEN = -1; // must be less than zero // BasicConstraints ::= SEQUENCE { // cA BOOLEAN DEFAULT FALSE, // pathLenConstraint INTEGER (0..MAX) OPTIONAL } static der::Result @@ -495,17 +548,17 @@ CheckExtendedKeyUsage(EndEntityOrCA endE Result CheckIssuerIndependentProperties(TrustDomain& trustDomain, BackCert& cert, PRTime time, EndEntityOrCA endEntityOrCA, KeyUsages requiredKeyUsagesIfPresent, KeyPurposeId requiredEKUIfPresent, - SECOidTag requiredPolicy, + const CertPolicyId& requiredPolicy, unsigned int subCACount, /*optional out*/ TrustLevel* trustLevelOut) { Result rv; TrustLevel trustLevel; rv = MapSECStatus(trustDomain.GetCertTrust(endEntityOrCA, requiredPolicy, @@ -522,19 +575,16 @@ CheckIssuerIndependentProperties(TrustDo // The TrustDomain returned a trust level that we weren't expecting. PORT_SetError(PR_INVALID_STATE_ERROR); return FatalError; } if (trustLevelOut) { *trustLevelOut = trustLevel; } - bool isTrustAnchor = endEntityOrCA == EndEntityOrCA::MustBeCA && - trustLevel == TrustLevel::TrustAnchor; - // XXX: Good enough for now. There could be an illegal explicit version // number or one we don't support, but we can safely treat those all as v3 // for now since processing of v3 certificates is strictly more strict than // processing of v1 certificates. der::Version version = (!cert.GetNSSCert()->version.data && !cert.GetNSSCert()->version.len) ? der::Version::v1 : der::Version::v3; @@ -550,17 +600,18 @@ CheckIssuerIndependentProperties(TrustDo // 4.2.1.3. Key Usage rv = CheckKeyUsage(endEntityOrCA, cert.encodedKeyUsage, requiredKeyUsagesIfPresent, arena); if (rv != Success) { return rv; } // 4.2.1.4. Certificate Policies - rv = CheckCertificatePolicies(cert, endEntityOrCA, isTrustAnchor, + rv = CheckCertificatePolicies(endEntityOrCA, cert.encodedCertificatePolicies, + cert.encodedInhibitAnyPolicy, trustLevel, requiredPolicy); if (rv != Success) { return rv; } // 4.2.1.5. Policy Mappings are not supported; see the documentation about // policy enforcement in pkix.h.
--- a/security/pkix/lib/pkixcheck.h +++ b/security/pkix/lib/pkixcheck.h @@ -33,17 +33,17 @@ namespace mozilla { namespace pkix { Result CheckIssuerIndependentProperties( TrustDomain& trustDomain, BackCert& cert, PRTime time, EndEntityOrCA endEntityOrCA, KeyUsages requiredKeyUsagesIfPresent, KeyPurposeId requiredEKUIfPresent, - SECOidTag requiredPolicy, + const CertPolicyId& requiredPolicy, unsigned int subCACount, /*optional out*/ TrustLevel* trustLevel = nullptr); Result CheckNameConstraints(BackCert& cert); } } // namespace mozilla::pkix #endif // mozilla_pkix__pkixcheck_h
--- a/security/pkix/lib/pkixder.h +++ b/security/pkix/lib/pkixder.h @@ -158,16 +158,41 @@ public: } if (memcmp(input, toMatch, N)) { return false; } input += N; return true; } + template <uint16_t N> + bool MatchTLV(uint8_t tag, uint16_t len, const uint8_t (&value)[N]) + { + static_assert(N <= 127, "buffer larger than largest length supported"); + if (len > N) { + PR_NOT_REACHED("overflow prevented dynamically instead of statically"); + return false; + } + uint16_t totalLen = 2u + len; + if (EnsureLength(totalLen) != Success) { + return false; + } + if (*input != tag) { + return false; + } + if (*(input + 1) != len) { + return false; + } + if (memcmp(input + 2, value, len)) { + return false; + } + input += totalLen; + return true; + } + Result Skip(uint16_t len) { if (EnsureLength(len) != Success) { return Fail(SEC_ERROR_BAD_DER); } input += len; return Success; }
--- a/security/pkix/lib/pkixocsp.cpp +++ b/security/pkix/lib/pkixocsp.cpp @@ -142,17 +142,17 @@ CheckOCSPResponseSignerCert(TrustDomain& // to--in particular, it doesn't allow SEC_OID_OCSP_RESPONDER to be implied // by a missing EKU extension, unlike other EKUs. // // TODO(bug 926261): If we're validating for a policy then the policy OID we // are validating for should be passed to CheckIssuerIndependentProperties. rv = CheckIssuerIndependentProperties(trustDomain, cert, time, EndEntityOrCA::MustBeEndEntity, 0, KeyPurposeId::id_kp_OCSPSigning, - SEC_OID_X509_ANY_POLICY, 0); + CertPolicyId::anyPolicy, 0); if (rv != Success) { return rv; } // It is possible that there exists a certificate with the same key as the // issuer but with a different name, so we need to compare names // TODO: needs test if (!SECITEM_ItemsAreEqual(&cert.GetNSSCert()->derIssuer,