зеркало из https://github.com/mozilla/gecko-dev.git
Bug 921895: Check extended key usage in insanity::pkix, r=keeler, r=cviecco
--HG-- extra : rebase_source : f8faa0b9269a40dd28850c9444f4723d1dad8451 extra : source : 32ea705bdfd196e037060b3bb7da081c1eed356d
This commit is contained in:
Родитель
e9ac2e3539
Коммит
9e617fc0fc
|
@ -27,6 +27,7 @@ SECStatus BuildCertChain(TrustDomain& trustDomain,
|
||||||
CERTCertificate* cert,
|
CERTCertificate* cert,
|
||||||
PRTime time,
|
PRTime time,
|
||||||
/*optional*/ KeyUsages requiredKeyUsagesIfPresent,
|
/*optional*/ KeyUsages requiredKeyUsagesIfPresent,
|
||||||
|
/*optional*/ SECOidTag requiredEKUIfPresent,
|
||||||
/*out*/ ScopedCERTCertList& results);
|
/*out*/ ScopedCERTCertList& results);
|
||||||
|
|
||||||
// Verify the given signed data using the public key of the given certificate.
|
// Verify the given signed data using the public key of the given certificate.
|
||||||
|
|
|
@ -53,6 +53,7 @@ BackCert::Init()
|
||||||
case 15: out = &encodedKeyUsage; break;
|
case 15: out = &encodedKeyUsage; break;
|
||||||
case 19: out = &encodedBasicConstraints; break;
|
case 19: out = &encodedBasicConstraints; break;
|
||||||
case 35: out = &dummyEncodedAuthorityKeyIdentifier; break; // bug 965136
|
case 35: out = &dummyEncodedAuthorityKeyIdentifier; break; // bug 965136
|
||||||
|
case 37: out = &encodedExtendedKeyUsage; break;
|
||||||
}
|
}
|
||||||
} else if (ext->id.len == 9 &&
|
} else if (ext->id.len == 9 &&
|
||||||
ext->id.data[0] == 0x2b && ext->id.data[1] == 0x06 &&
|
ext->id.data[0] == 0x2b && ext->id.data[1] == 0x06 &&
|
||||||
|
@ -91,6 +92,7 @@ static Result BuildForward(TrustDomain& trustDomain,
|
||||||
PRTime time,
|
PRTime time,
|
||||||
EndEntityOrCA endEntityOrCA,
|
EndEntityOrCA endEntityOrCA,
|
||||||
KeyUsages requiredKeyUsagesIfPresent,
|
KeyUsages requiredKeyUsagesIfPresent,
|
||||||
|
SECOidTag requiredEKUIfPresent,
|
||||||
unsigned int subCACount,
|
unsigned int subCACount,
|
||||||
/*out*/ ScopedCERTCertList& results);
|
/*out*/ ScopedCERTCertList& results);
|
||||||
|
|
||||||
|
@ -100,6 +102,7 @@ BuildForwardInner(TrustDomain& trustDomain,
|
||||||
BackCert& subject,
|
BackCert& subject,
|
||||||
PRTime time,
|
PRTime time,
|
||||||
EndEntityOrCA endEntityOrCA,
|
EndEntityOrCA endEntityOrCA,
|
||||||
|
SECOidTag requiredEKUIfPresent,
|
||||||
CERTCertificate* potentialIssuerCertToDup,
|
CERTCertificate* potentialIssuerCertToDup,
|
||||||
unsigned int subCACount,
|
unsigned int subCACount,
|
||||||
ScopedCERTCertList& results)
|
ScopedCERTCertList& results)
|
||||||
|
@ -142,7 +145,7 @@ BuildForwardInner(TrustDomain& trustDomain,
|
||||||
}
|
}
|
||||||
|
|
||||||
rv = BuildForward(trustDomain, potentialIssuer, time, MustBeCA,
|
rv = BuildForward(trustDomain, potentialIssuer, time, MustBeCA,
|
||||||
KU_KEY_CERT_SIGN,
|
KU_KEY_CERT_SIGN, requiredEKUIfPresent,
|
||||||
newSubCACount, results);
|
newSubCACount, results);
|
||||||
if (rv != Success) {
|
if (rv != Success) {
|
||||||
return rv;
|
return rv;
|
||||||
|
@ -163,6 +166,7 @@ BuildForward(TrustDomain& trustDomain,
|
||||||
PRTime time,
|
PRTime time,
|
||||||
EndEntityOrCA endEntityOrCA,
|
EndEntityOrCA endEntityOrCA,
|
||||||
KeyUsages requiredKeyUsagesIfPresent,
|
KeyUsages requiredKeyUsagesIfPresent,
|
||||||
|
SECOidTag requiredEKUIfPresent,
|
||||||
unsigned int subCACount,
|
unsigned int subCACount,
|
||||||
/*out*/ ScopedCERTCertList& results)
|
/*out*/ ScopedCERTCertList& results)
|
||||||
{
|
{
|
||||||
|
@ -191,7 +195,7 @@ BuildForward(TrustDomain& trustDomain,
|
||||||
|
|
||||||
rv = CheckExtensions(subject, endEntityOrCA,
|
rv = CheckExtensions(subject, endEntityOrCA,
|
||||||
trustLevel == TrustDomain::TrustAnchor,
|
trustLevel == TrustDomain::TrustAnchor,
|
||||||
requiredKeyUsagesIfPresent,
|
requiredKeyUsagesIfPresent, requiredEKUIfPresent,
|
||||||
subCACount);
|
subCACount);
|
||||||
if (rv != Success) {
|
if (rv != Success) {
|
||||||
return rv;
|
return rv;
|
||||||
|
@ -223,6 +227,7 @@ BuildForward(TrustDomain& trustDomain,
|
||||||
for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
|
for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
|
||||||
!CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
|
!CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
|
||||||
rv = BuildForwardInner(trustDomain, subject, time, endEntityOrCA,
|
rv = BuildForwardInner(trustDomain, subject, time, endEntityOrCA,
|
||||||
|
requiredEKUIfPresent,
|
||||||
n->cert, subCACount, results);
|
n->cert, subCACount, results);
|
||||||
if (rv == Success) {
|
if (rv == Success) {
|
||||||
// We found a trusted issuer. At this point, we know the cert is valid
|
// We found a trusted issuer. At this point, we know the cert is valid
|
||||||
|
@ -241,6 +246,7 @@ BuildCertChain(TrustDomain& trustDomain,
|
||||||
CERTCertificate* certToDup,
|
CERTCertificate* certToDup,
|
||||||
PRTime time,
|
PRTime time,
|
||||||
/*optional*/ KeyUsages requiredKeyUsagesIfPresent,
|
/*optional*/ KeyUsages requiredKeyUsagesIfPresent,
|
||||||
|
/*optional*/ SECOidTag requiredEKUIfPresent,
|
||||||
/*out*/ ScopedCERTCertList& results)
|
/*out*/ ScopedCERTCertList& results)
|
||||||
{
|
{
|
||||||
PORT_Assert(certToDup);
|
PORT_Assert(certToDup);
|
||||||
|
@ -260,7 +266,7 @@ BuildCertChain(TrustDomain& trustDomain,
|
||||||
}
|
}
|
||||||
|
|
||||||
rv = BuildForward(trustDomain, ee, time, MustBeEndEntity,
|
rv = BuildForward(trustDomain, ee, time, MustBeEndEntity,
|
||||||
requiredKeyUsagesIfPresent,
|
requiredKeyUsagesIfPresent, requiredEKUIfPresent,
|
||||||
0, results);
|
0, results);
|
||||||
if (rv != Success) {
|
if (rv != Success) {
|
||||||
results = nullptr;
|
results = nullptr;
|
||||||
|
|
|
@ -149,12 +149,17 @@ CheckBasicConstraints(const BackCert& cert,
|
||||||
// CA certificates are not trusted as EE certs.
|
// CA certificates are not trusted as EE certs.
|
||||||
|
|
||||||
if (basicConstraints.isCA) {
|
if (basicConstraints.isCA) {
|
||||||
// XXX: We use SEC_ERROR_INADEQUATE_CERT_TYPE here so we can distinguish
|
// XXX: We use SEC_ERROR_CA_CERT_INVALID here so we can distinguish
|
||||||
// this error from other errors, given that NSS does not have a "CA cert
|
// this error from other errors, given that NSS does not have a "CA cert
|
||||||
// used as end-entity" error code since it doesn't have such a
|
// used as end-entity" error code since it doesn't have such a
|
||||||
// prohibition. We should add such an error code and stop abusing
|
// prohibition. We should add such an error code and stop abusing
|
||||||
// SEC_ERROR_INADEQUATE_CERT_TYPE this way.
|
// SEC_ERROR_CA_CERT_INVALID this way.
|
||||||
return Fail(RecoverableError, SEC_ERROR_INADEQUATE_CERT_TYPE);
|
//
|
||||||
|
// Note, in particular, that this check prevents a delegated OCSP
|
||||||
|
// response signing certificate with the CA bit from successfully
|
||||||
|
// validating when we check it from pkixocsp.cpp, which is a good thing.
|
||||||
|
//
|
||||||
|
return Fail(RecoverableError, SEC_ERROR_CA_CERT_INVALID);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Success;
|
return Success;
|
||||||
|
@ -177,6 +182,82 @@ CheckBasicConstraints(const BackCert& cert,
|
||||||
return Success;
|
return Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4.2.1.12. Extended Key Usage (id-ce-extKeyUsage)
|
||||||
|
// 4.2.1.12. Extended Key Usage (id-ce-extKeyUsage)
|
||||||
|
Result
|
||||||
|
CheckExtendedKeyUsage(EndEntityOrCA endEntityOrCA, const SECItem* encodedEKUs,
|
||||||
|
SECOidTag requiredEKU)
|
||||||
|
{
|
||||||
|
// TODO: Either do not allow anyExtendedKeyUsage to be passed as requiredEKU,
|
||||||
|
// or require that callers pass anyExtendedKeyUsage instead of
|
||||||
|
// SEC_OID_UNKNWON and disallow SEC_OID_UNKNWON.
|
||||||
|
|
||||||
|
// XXX: We're using SEC_ERROR_INADEQUATE_CERT_TYPE here so that callers can
|
||||||
|
// distinguish EKU mismatch from KU mismatch from basic constraints mismatch.
|
||||||
|
// We should probably add a new error code that is more clear for this type
|
||||||
|
// of problem.
|
||||||
|
|
||||||
|
bool foundOCSPSigning = false;
|
||||||
|
|
||||||
|
if (encodedEKUs) {
|
||||||
|
ScopedPtr<CERTOidSequence, CERT_DestroyOidSequence>
|
||||||
|
seq(CERT_DecodeOidSequence(encodedEKUs));
|
||||||
|
if (!seq) {
|
||||||
|
PR_SetError(SEC_ERROR_INADEQUATE_CERT_TYPE, 0);
|
||||||
|
return RecoverableError;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
// XXX: We allow duplicate entries.
|
||||||
|
for (const SECItem* const* oids = seq->oids; oids && *oids; ++oids) {
|
||||||
|
SECOidTag oidTag = SECOID_FindOIDTag(*oids);
|
||||||
|
if (requiredEKU != SEC_OID_UNKNOWN && oidTag == requiredEKU) {
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
if (oidTag == SEC_OID_OCSP_RESPONDER) {
|
||||||
|
foundOCSPSigning = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the EKU extension was included, then the required EKU must be in the
|
||||||
|
// list.
|
||||||
|
if (!found) {
|
||||||
|
PR_SetError(SEC_ERROR_INADEQUATE_CERT_TYPE, 0);
|
||||||
|
return RecoverableError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pkixocsp.cpp depends on the following additional checks.
|
||||||
|
|
||||||
|
if (foundOCSPSigning) {
|
||||||
|
// When validating anything other than an delegated OCSP signing cert,
|
||||||
|
// reject any cert that also claims to be an OCSP responder, because such
|
||||||
|
// a cert does not make sense. For example, if an SSL certificate were to
|
||||||
|
// assert id-kp-OCSPSigning then it could sign OCSP responses for itself,
|
||||||
|
// if not for this check.
|
||||||
|
if (requiredEKU != SEC_OID_OCSP_RESPONDER) {
|
||||||
|
PR_SetError(SEC_ERROR_INADEQUATE_CERT_TYPE, 0);
|
||||||
|
return RecoverableError;
|
||||||
|
}
|
||||||
|
} else if (requiredEKU == SEC_OID_OCSP_RESPONDER &&
|
||||||
|
endEntityOrCA == MustBeEndEntity) {
|
||||||
|
// http://tools.ietf.org/html/rfc6960#section-4.2.2.2:
|
||||||
|
// "OCSP signing delegation SHALL be designated by the inclusion of
|
||||||
|
// id-kp-OCSPSigning in an extended key usage certificate extension
|
||||||
|
// included in the OCSP response signer's certificate."
|
||||||
|
//
|
||||||
|
// id-kp-OCSPSigning is the only EKU that isn't implicitly assumed when the
|
||||||
|
// EKU extension is missing from an end-entity certificate. However, any CA
|
||||||
|
// certificate can issue a delegated OCSP response signing certificate, so
|
||||||
|
// we can't require the EKU be explicitly included for CA certificates.
|
||||||
|
PR_SetError(SEC_ERROR_INADEQUATE_CERT_TYPE, 0);
|
||||||
|
return RecoverableError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Success;
|
||||||
|
}
|
||||||
|
|
||||||
// Checks extensions that apply to both EE and intermediate certs,
|
// Checks extensions that apply to both EE and intermediate certs,
|
||||||
// except for AIA, CRL, and AKI/SKI, which are handled elsewhere.
|
// except for AIA, CRL, and AKI/SKI, which are handled elsewhere.
|
||||||
Result
|
Result
|
||||||
|
@ -184,8 +265,14 @@ CheckExtensions(BackCert& cert,
|
||||||
EndEntityOrCA endEntityOrCA,
|
EndEntityOrCA endEntityOrCA,
|
||||||
bool isTrustAnchor,
|
bool isTrustAnchor,
|
||||||
KeyUsages requiredKeyUsagesIfPresent,
|
KeyUsages requiredKeyUsagesIfPresent,
|
||||||
|
SECOidTag requiredEKUIfPresent,
|
||||||
unsigned int subCACount)
|
unsigned int subCACount)
|
||||||
{
|
{
|
||||||
|
if (endEntityOrCA != MustBeCA && isTrustAnchor) {
|
||||||
|
PR_NOT_REACHED("only CAs can be trust anchors.");
|
||||||
|
return Fail(FatalError, PR_INVALID_STATE_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
// 4.2.1.1. Authority Key Identifier dealt with as part of path building
|
// 4.2.1.1. Authority Key Identifier dealt with as part of path building
|
||||||
// 4.2.1.2. Subject Key Identifier dealt with as part of path building
|
// 4.2.1.2. Subject Key Identifier dealt with as part of path building
|
||||||
|
|
||||||
|
@ -221,6 +308,12 @@ CheckExtensions(BackCert& cert,
|
||||||
// 4.2.1.10. Name Constraints
|
// 4.2.1.10. Name Constraints
|
||||||
// 4.2.1.11. Policy Constraints
|
// 4.2.1.11. Policy Constraints
|
||||||
// 4.2.1.12. Extended Key Usage
|
// 4.2.1.12. Extended Key Usage
|
||||||
|
rv = CheckExtendedKeyUsage(endEntityOrCA, cert.encodedExtendedKeyUsage,
|
||||||
|
requiredEKUIfPresent);
|
||||||
|
if (rv != Success) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
// 4.2.1.13. CRL Distribution Points will be dealt with elsewhere
|
// 4.2.1.13. CRL Distribution Points will be dealt with elsewhere
|
||||||
// 4.2.1.14. Inhibit anyPolicy
|
// 4.2.1.14. Inhibit anyPolicy
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ Result CheckExtensions(BackCert& certExt,
|
||||||
EndEntityOrCA endEntityOrCA,
|
EndEntityOrCA endEntityOrCA,
|
||||||
bool isTrustAnchor,
|
bool isTrustAnchor,
|
||||||
KeyUsages requiredKeyUsagesIfPresent,
|
KeyUsages requiredKeyUsagesIfPresent,
|
||||||
|
SECOidTag requiredEKUIfPresent,
|
||||||
unsigned int depth);
|
unsigned int depth);
|
||||||
|
|
||||||
} } // namespace insanity::pkix
|
} } // namespace insanity::pkix
|
||||||
|
|
|
@ -84,6 +84,7 @@ public:
|
||||||
// nssCert and childCert must be valid for the lifetime of BackCert
|
// nssCert and childCert must be valid for the lifetime of BackCert
|
||||||
BackCert(CERTCertificate* nssCert, BackCert* childCert)
|
BackCert(CERTCertificate* nssCert, BackCert* childCert)
|
||||||
: encodedBasicConstraints(nullptr)
|
: encodedBasicConstraints(nullptr)
|
||||||
|
, encodedExtendedKeyUsage(nullptr)
|
||||||
, encodedKeyUsage(nullptr)
|
, encodedKeyUsage(nullptr)
|
||||||
, childCert(childCert)
|
, childCert(childCert)
|
||||||
, nssCert(nssCert)
|
, nssCert(nssCert)
|
||||||
|
@ -93,6 +94,7 @@ public:
|
||||||
Result Init();
|
Result Init();
|
||||||
|
|
||||||
const SECItem* encodedBasicConstraints;
|
const SECItem* encodedBasicConstraints;
|
||||||
|
const SECItem* encodedExtendedKeyUsage;
|
||||||
const SECItem* encodedKeyUsage;
|
const SECItem* encodedKeyUsage;
|
||||||
|
|
||||||
BackCert* const childCert;
|
BackCert* const childCert;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче