Bug 1033563, Part 3: Change mozilla::pkix::TrustDomain::FindPotentialIssuers API to be iterator-like, r=keeler

--HG--
extra : rebase_source : e8c734ecb2de2c52dd8909c8b48f4bdb09d0128e
This commit is contained in:
Brian Smith 2014-07-02 16:15:16 -07:00
parent b79a1c89fd
commit 5a21cfd0b8
8 changed files with 142 additions and 60 deletions

View File

@ -86,9 +86,9 @@ AppTrustDomain::SetTrustedRoot(AppTrustedRoot trustedRoot)
}
SECStatus
AppTrustDomain::FindPotentialIssuers(const SECItem* encodedIssuerName,
PRTime time,
/*out*/ mozilla::pkix::ScopedCERTCertList& results)
AppTrustDomain::FindIssuer(const SECItem& encodedIssuerName,
IssuerChecker& checker, PRTime time)
{
MOZ_ASSERT(mTrustedRoot);
if (!mTrustedRoot) {
@ -96,8 +96,31 @@ AppTrustDomain::FindPotentialIssuers(const SECItem* encodedIssuerName,
return SECFailure;
}
results = CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
encodedIssuerName, time, true);
// TODO(bug 1035418): If/when mozilla::pkix relaxes the restriction that
// FindIssuer must only pass certificates with a matching subject name to
// checker.Check, we can stop using CERT_CreateSubjectCertList and instead
// use logic like this:
//
// 1. First, try the trusted trust anchor.
// 2. Secondly, iterate through the certificates that were stored in the CMS
// message, passing each one to checker.Check.
mozilla::pkix::ScopedCERTCertList
candidates(CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
&encodedIssuerName, time, true));
if (candidates) {
for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
!CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
bool keepGoing;
SECStatus srv = checker.Check(n->cert->derCert, keepGoing);
if (srv != SECSuccess) {
return SECFailure;
}
if (!keepGoing) {
break;
}
}
}
return SECSuccess;
}

View File

@ -24,10 +24,8 @@ public:
const mozilla::pkix::CertPolicyId& policy,
const SECItem& candidateCertDER,
/*out*/ mozilla::pkix::TrustLevel* trustLevel) MOZ_OVERRIDE;
SECStatus FindPotentialIssuers(const SECItem* encodedIssuerName,
PRTime time,
/*out*/ mozilla::pkix::ScopedCERTCertList& results)
MOZ_OVERRIDE;
SECStatus FindIssuer(const SECItem& encodedIssuerName,
IssuerChecker& checker, PRTime time) MOZ_OVERRIDE;
SECStatus VerifySignedData(const CERTSignedData& signedData,
const SECItem& subjectPublicKeyInfo) MOZ_OVERRIDE;
SECStatus CheckRevocation(mozilla::pkix::EndEntityOrCA endEntityOrCA,

View File

@ -56,15 +56,28 @@ NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType,
}
SECStatus
NSSCertDBTrustDomain::FindPotentialIssuers(
const SECItem* encodedIssuerName, PRTime time,
/*out*/ mozilla::pkix::ScopedCERTCertList& results)
NSSCertDBTrustDomain::FindIssuer(const SECItem& encodedIssuerName,
IssuerChecker& checker, PRTime time)
{
// TODO: normalize encodedIssuerName
// TODO: NSS seems to be ambiguous between "no potential issuers found" and
// "there was an error trying to retrieve the potential issuers."
results = CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
encodedIssuerName, time, true);
mozilla::pkix::ScopedCERTCertList
candidates(CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
&encodedIssuerName, time, true));
if (candidates) {
for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
!CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
bool keepGoing;
SECStatus srv = checker.Check(n->cert->derCert, keepGoing);
if (srv != SECSuccess) {
return SECFailure;
}
if (!keepGoing) {
break;
}
}
}
return SECSuccess;
}

View File

@ -53,10 +53,8 @@ public:
CertVerifier::ocsp_get_config ocspGETConfig,
CERTChainVerifyCallback* checkChainCallback = nullptr);
virtual SECStatus FindPotentialIssuers(
const SECItem* encodedIssuerName,
PRTime time,
/*out*/ mozilla::pkix::ScopedCERTCertList& results);
virtual SECStatus FindIssuer(const SECItem& encodedIssuerName,
IssuerChecker& checker, PRTime time);
virtual SECStatus GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA,
const mozilla::pkix::CertPolicyId& policy,

View File

@ -142,17 +142,62 @@ public:
const SECItem& candidateCertDER,
/*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
// function should return SECSuccess with results being either null or
// an empty list. Otherwise, this function should construct a
// CERTCertList and return it in |results|, transfering ownership.
virtual SECStatus FindPotentialIssuers(const SECItem* encodedIssuerName,
PRTime time,
/*out*/ ScopedCERTCertList& results) = 0;
class IssuerChecker
{
public:
virtual SECStatus Check(const SECItem& potentialIssuerDER,
/*out*/ bool& keepGoing) = 0;
protected:
IssuerChecker();
virtual ~IssuerChecker();
private:
IssuerChecker(const IssuerChecker&) /*= delete*/;
void operator=(const IssuerChecker&) /*= delete*/;
};
// Search for a CA certificate with the given name. The implementation must
// call checker.Check with the DER encoding of the potential issuer
// certificate. The implementation must follow these rules:
//
// * The subject name of the certificate given to checker.Check must be equal
// to encodedIssuerName.
// * The implementation must be reentrant and must limit the amount of stack
// space it uses; see the note on reentrancy and stack usage below.
// * When checker.Check does not return SECSuccess then immediately return
// SECFailure.
// * When checker.Check returns SECSuccess and sets keepGoing = false, then
// immediately return SECSuccess.
// * When checker.Check returns SECSuccess and sets keepGoing = true, then
// call checker.Check again with a different potential issuer certificate,
// if any more are available.
// * When no more potential issuer certificates are available, return
// SECSuccess.
// * Don't call checker.Check with the same potential issuer certificate more
// than once in a given call of FindIssuer.
// * The given time parameter may be used to filter out certificates that are
// not valid at the given time, or it may be ignored.
//
// Note on reentrancy and stack usage: checker.Check will attempt to
// recursively build a certificate path from the potential issuer it is given
// to a trusted root, as determined by this TrustDomain. That means that
// checker.Check may call any/all of the methods on this TrustDomain. In
// particular, there will be call stacks that look like this:
//
// BuildCertChain
// [...]
// TrustDomain::FindIssuer
// [...]
// IssuerChecker::Check
// [...]
// TrustDomain::FindIssuer
// [...]
// IssuerChecker::Check
// [...]
//
// checker.Check is responsible for limiting the recursion to a reasonable
// limit.
virtual SECStatus FindIssuer(const SECItem& encodedIssuerName,
IssuerChecker& checker, PRTime time) = 0;
// Verify the given signature using the given public key.
//

View File

@ -41,7 +41,12 @@ static Result BuildForward(TrustDomain& trustDomain,
unsigned int subCACount,
/*out*/ ScopedCERTCertList& results);
class PathBuildingStep
TrustDomain::IssuerChecker::IssuerChecker() { }
TrustDomain::IssuerChecker::~IssuerChecker() { }
// The implementation of TrustDomain::IssuerTracker is in a subclass only to
// hide the implementation from external users.
class PathBuildingStep : public TrustDomain::IssuerChecker
{
public:
PathBuildingStep(TrustDomain& trustDomain, const BackCert& subject,
@ -65,7 +70,7 @@ public:
{
}
SECStatus Build(const SECItem& potentialIssuerDER,
SECStatus Check(const SECItem& potentialIssuerDER,
/*out*/ bool& keepGoing);
Result CheckResult() const;
@ -132,7 +137,7 @@ PathBuildingStep::CheckResult() const
// The code that executes in the inner loop of BuildForward
SECStatus
PathBuildingStep::Build(const SECItem& potentialIssuerDER,
PathBuildingStep::Check(const SECItem& potentialIssuerDER,
/*out*/ bool& keepGoing)
{
BackCert potentialIssuer(potentialIssuerDER, &subject,
@ -191,7 +196,7 @@ PathBuildingStep::Build(const SECItem& potentialIssuerDER,
return RecordResult(PR_GetError(), keepGoing);
}
return RecordResult(0, keepGoing);
return RecordResult(0/*PRErrorCode::success*/, keepGoing);
}
// Recursively build the path from the given subject certificate to the root.
@ -281,25 +286,10 @@ BuildForward(TrustDomain& trustDomain,
stapledOCSPResponse, subCACount, results);
// TODO(bug 965136): Add SKI/AKI matching optimizations
ScopedCERTCertList candidates;
if (trustDomain.FindPotentialIssuers(&subject.GetIssuer(), time,
candidates) != SECSuccess) {
if (trustDomain.FindIssuer(subject.GetIssuer(), pathBuilder, time)
!= SECSuccess) {
return MapSECStatus(SECFailure);
}
if (!candidates) {
return Fail(RecoverableError, SEC_ERROR_UNKNOWN_ISSUER);
}
for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
!CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
bool keepGoing;
SECStatus srv = pathBuilder.Build(n->cert->derCert, keepGoing);
if (srv != SECSuccess) {
return MapSECStatus(SECFailure);
}
if (!keepGoing) {
break;
}
}
rv = pathBuilder.CheckResult();
if (rv != Success) {

View File

@ -126,12 +126,26 @@ private:
return SECSuccess;
}
SECStatus FindPotentialIssuers(const SECItem* encodedIssuerName,
PRTime time,
/*out*/ ScopedCERTCertList& results)
SECStatus FindIssuer(const SECItem& encodedIssuerName,
IssuerChecker& checker, PRTime time)
{
results = CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
encodedIssuerName, time, true);
mozilla::pkix::ScopedCERTCertList
candidates(CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
&encodedIssuerName, time, true));
if (candidates) {
for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
!CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
bool keepGoing;
SECStatus srv = checker.Check(n->cert->derCert, keepGoing);
if (srv != SECSuccess) {
return SECFailure;
}
if (!keepGoing) {
break;
}
}
}
return SECSuccess;
}

View File

@ -81,11 +81,12 @@ private:
return SECSuccess;
}
SECStatus FindPotentialIssuers(const SECItem* encodedIssuerName,
PRTime time,
/*out*/ ScopedCERTCertList& results)
SECStatus FindIssuer(const SECItem& /*encodedIssuerName*/,
IssuerChecker& /*checker*/, PRTime /*time*/)
{
return SECSuccess;
PR_NOT_REACHED("FindIssuer should not be called");
PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
return SECFailure;
}
SECStatus VerifySignedData(const CERTSignedData& signedData,