diff --git a/security/certverifier/CertVerifier.cpp b/security/certverifier/CertVerifier.cpp --- a/security/certverifier/CertVerifier.cpp +++ b/security/certverifier/CertVerifier.cpp @@ -120,16 +120,20 @@ IsCertChainRootBuiltInRoot(const UniqueC } CERTCertificate* root = rootNode->cert; if (!root) { return Result::FATAL_ERROR_LIBRARY_FAILURE; } return IsCertBuiltInRoot(root, result); } +// The term "builtin root" traditionally refers to a root CA certificate that +// has been added to the NSS trust store, because it has been approved +// for inclusion according to the Mozilla CA policy, and might be accepted +// by Mozilla applications as an issuer for certificates seen on the public web. Result IsCertBuiltInRoot(CERTCertificate* cert, bool& result) { result = false; #ifdef DEBUG nsCOMPtr component(do_GetService(PSM_COMPONENT_CONTRACTID)); if (!component) { return Result::FATAL_ERROR_LIBRARY_FAILURE; @@ -142,25 +146,38 @@ IsCertBuiltInRoot(CERTCertificate* cert, return Success; } #endif // DEBUG AutoSECMODListReadLock lock; for (SECMODModuleList* list = SECMOD_GetDefaultModuleList(); list; list = list->next) { for (int i = 0; i < list->module->slotCount; i++) { PK11SlotInfo* slot = list->module->slots[i]; - // PK11_HasRootCerts should return true if and only if the given slot has - // an object with a CKA_CLASS of CKO_NETSCAPE_BUILTIN_ROOT_LIST, which - // should be true only of the builtin root list. - // If we can find a copy of the given certificate on the slot with the - // builtin root list, that certificate must be a builtin. - if (PK11_IsPresent(slot) && PK11_HasRootCerts(slot) && - PK11_FindCertInSlot(slot, cert, nullptr) != CK_INVALID_HANDLE) { - result = true; - return Success; + // We're searching for the "builtin root module", which is a module that + // contains an object with a CKA_CLASS of CKO_NETSCAPE_BUILTIN_ROOT_LIST. + // We use PK11_HasRootCerts() to identify a module with that property. + // In the past, we exclusively used the PKCS#11 module named nssckbi, + // which is provided by the NSS library. + // Nowadays, some distributions use a replacement module, which contains + // the builtin roots, but which also contains additional CA certificates, + // such as CAs trusted in a local deployment. + // We want to be able to distinguish between these two categories, + // because a CA, which may issue certificates for the public web, + // is expected to comply with additional requirements. + // If the certificate has attribute CKA_NSS_MOZILLA_CA_POLICY set to true, + // then we treat it as a "builtin root". + if (PK11_IsPresent(slot) && PK11_HasRootCerts(slot)) { + CK_OBJECT_HANDLE handle = PK11_FindCertInSlot(slot, cert, nullptr); + if (handle != CK_INVALID_HANDLE && + PK11_HasAttributeSet(slot, handle, CKA_NSS_MOZILLA_CA_POLICY, + false)) { + // Attribute was found, and is set to true + result = true; + break; + } } } } return Success; } static Result BuildCertChainForOneKeyUsage(NSSCertDBTrustDomain& trustDomain, Input certDER,