SECStatus AppTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA, const CertPolicyId& policy, const SECItem& candidateCertDER, /*out*/ TrustLevel* trustLevel) { MOZ_ASSERT(policy.IsAnyPolicy()); MOZ_ASSERT(trustLevel); MOZ_ASSERT(mTrustedRoot); if (!trustLevel || !policy.IsAnyPolicy()) { PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; } if (!mTrustedRoot) { PR_SetError(PR_INVALID_STATE_ERROR, 0); return SECFailure; } // Handle active distrust of the certificate. // XXX: This would be cleaner and more efficient if we could get the trust // information without constructing a CERTCertificate here, but NSS doesn't // expose it in any other easy-to-use fashion. ScopedCERTCertificate candidateCert( CERT_NewTempCertificate(CERT_GetDefaultCertDB(), const_cast<SECItem*>(&candidateCertDER), nullptr, false, true)); if (!candidateCert) { return SECFailure; } CERTCertTrust trust; if (CERT_GetCertTrust(candidateCert.get(), &trust) == SECSuccess) { PRUint32 flags = SEC_GET_TRUST_FLAGS(&trust, trustObjectSigning); // For DISTRUST, we use the CERTDB_TRUSTED or CERTDB_TRUSTED_CA bit, // because we can have active distrust for either type of cert. Note that // CERTDB_TERMINAL_RECORD means "stop trying to inherit trust" so if the // relevant trust bit isn't set then that means the cert must be considered // distrusted. PRUint32 relevantTrustBit = endEntityOrCA == EndEntityOrCA::MustBeCA ? CERTDB_TRUSTED_CA : CERTDB_TRUSTED; if (((flags & (relevantTrustBit | CERTDB_TERMINAL_RECORD))) == CERTDB_TERMINAL_RECORD) { *trustLevel = TrustLevel::ActivelyDistrusted; return SECSuccess; } } // mTrustedRoot is the only trust anchor for this validation. if (CERT_CompareCerts(mTrustedRoot.get(), candidateCert.get())) { *trustLevel = TrustLevel::TrustAnchor; return SECSuccess; } *trustLevel = TrustLevel::InheritsTrust; return SECSuccess; }
// 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 (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 (trustLevel == TrustLevel::TrustAnchor && endEntityOrCA == EndEntityOrCA::MustBeCA) { return Success; } if (!encodedCertificatePolicies) { return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED); } 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); } return Success; }
// certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation Result CheckCertificatePolicies(EndEntityOrCA endEntityOrCA, const Input* encodedCertificatePolicies, const Input* encodedInhibitAnyPolicy, TrustLevel trustLevel, const CertPolicyId& requiredPolicy) { if (requiredPolicy.numBytes == 0 || requiredPolicy.numBytes > sizeof requiredPolicy.bytes) { return Result::FATAL_ERROR_INVALID_ARGS; } bool requiredPolicyFound = requiredPolicy.IsAnyPolicy(); if (requiredPolicyFound) { return Success; } // Bug 989051. Until we handle inhibitAnyPolicy we will fail close when // inhibitAnyPolicy extension is present and we are validating for a policy. if (!requiredPolicyFound && encodedInhibitAnyPolicy) { return Result::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 (trustLevel == TrustLevel::TrustAnchor && endEntityOrCA == EndEntityOrCA::MustBeCA) { requiredPolicyFound = true; } Input requiredPolicyDER; if (requiredPolicyDER.Init(requiredPolicy.bytes, requiredPolicy.numBytes) != Success) { return Result::FATAL_ERROR_INVALID_ARGS; } if (encodedCertificatePolicies) { Reader extension(*encodedCertificatePolicies); Reader certificatePolicies; Result rv = der::ExpectTagAndGetValue(extension, der::SEQUENCE, certificatePolicies); if (rv != Success) { return Result::ERROR_POLICY_VALIDATION_FAILED; } if (!extension.AtEnd()) { return Result::ERROR_POLICY_VALIDATION_FAILED; } do { // PolicyInformation ::= SEQUENCE { // policyIdentifier CertPolicyId, // policyQualifiers SEQUENCE SIZE (1..MAX) OF // PolicyQualifierInfo OPTIONAL } Reader policyInformation; rv = der::ExpectTagAndGetValue(certificatePolicies, der::SEQUENCE, policyInformation); if (rv != Success) { return Result::ERROR_POLICY_VALIDATION_FAILED; } Reader policyIdentifier; rv = der::ExpectTagAndGetValue(policyInformation, der::OIDTag, policyIdentifier); if (rv != Success) { return rv; } if (policyIdentifier.MatchRest(requiredPolicyDER)) { requiredPolicyFound = true; } else if (endEntityOrCA == EndEntityOrCA::MustBeCA && policyIdentifier.MatchRest(anyPolicy)) { requiredPolicyFound = true; } // 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. } while (!requiredPolicyFound && !certificatePolicies.AtEnd()); } if (!requiredPolicyFound) { return Result::ERROR_POLICY_VALIDATION_FAILED; } return Success; }