зеркало из https://github.com/mozilla/gecko-dev.git
bug 1265113 - implement platform support for enterprise roots r=Cykesiopka,mhowell,rbarnes
MozReview-Commit-ID: JKxwCjoH0Oa --HG-- extra : rebase_source : 9eaf3f1c5371e7b4b4df304bc6ce132ade5775da
This commit is contained in:
Родитель
4f13f0068e
Коммит
8ba29d1473
|
@ -390,4 +390,10 @@ interface nsIX509CertDB : nsISupports {
|
|||
* Get all the known certs in the database
|
||||
*/
|
||||
nsIX509CertList getCerts();
|
||||
|
||||
/*
|
||||
* Get a list of imported enterprise root certificates (currently only
|
||||
* implemented on Windows).
|
||||
*/
|
||||
nsIX509CertList getEnterpriseRoots();
|
||||
};
|
||||
|
|
|
@ -1476,6 +1476,32 @@ nsNSSCertificateDB::GetCerts(nsIX509CertList **_retval)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNSSCertificateDB::GetEnterpriseRoots(nsIX509CertList** enterpriseRoots)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!NS_IsMainThread()) {
|
||||
return NS_ERROR_NOT_SAME_THREAD;
|
||||
}
|
||||
|
||||
NS_ENSURE_ARG_POINTER(enterpriseRoots);
|
||||
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
nsCOMPtr<nsINSSComponent> psm(do_GetService(PSM_COMPONENT_CONTRACTID));
|
||||
if (!psm) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return psm->GetEnterpriseRoots(enterpriseRoots);
|
||||
#else
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
nsresult
|
||||
VerifyCertAtTime(nsIX509Cert* aCert,
|
||||
int64_t /*SECCertificateUsage*/ aUsage,
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "SharedSSLState.h"
|
||||
#include "cert.h"
|
||||
#include "certdb.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/PublicSSL.h"
|
||||
|
@ -19,6 +20,7 @@
|
|||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/SyncRunnable.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "nsAppDirectoryServiceDefs.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nsCertVerificationThread.h"
|
||||
|
@ -442,7 +444,7 @@ GetUserSid(nsAString& sidString)
|
|||
}
|
||||
char sid_buffer[SECURITY_MAX_SID_SIZE];
|
||||
SID* sid = BitwiseCast<SID*, char*>(sid_buffer);
|
||||
DWORD cbSid = MOZ_ARRAY_LENGTH(sid_buffer);
|
||||
DWORD cbSid = ArrayLength(sid_buffer);
|
||||
SID_NAME_USE eUse;
|
||||
// There doesn't appear to be a defined maximum length for the domain name
|
||||
// here. To deal with this, we start with a reasonable buffer length and
|
||||
|
@ -652,31 +654,50 @@ AccountHasFamilySafetyEnabled(bool& enabled)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
const char* kImportedFamilySafetyRootPref =
|
||||
"security.family_safety.imported_root.db_key";
|
||||
// It would be convenient to just use nsIX509CertDB in the following code.
|
||||
// However, since nsIX509CertDB depends on nsNSSComponent initialization (and
|
||||
// since this code runs during that initialization), we can't use it. Instead,
|
||||
// we can use NSS APIs directly (as long as we're called late enough in
|
||||
// nsNSSComponent initialization such that those APIs are safe to use).
|
||||
|
||||
static nsresult
|
||||
MaybeImportFamilySafetyRoot(PCCERT_CONTEXT certificate,
|
||||
bool& wasFamilySafetyRoot)
|
||||
// Helper function to convert a PCCERT_CONTEXT (i.e. a certificate obtained via
|
||||
// a Windows API) to a temporary CERTCertificate (i.e. a certificate for use
|
||||
// with NSS APIs).
|
||||
static UniqueCERTCertificate
|
||||
PCCERT_CONTEXTToCERTCertificate(PCCERT_CONTEXT pccert)
|
||||
{
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("MaybeImportFamilySafetyRoot"));
|
||||
wasFamilySafetyRoot = false;
|
||||
|
||||
// It would be convenient to just use nsIX509CertDB here. However, since
|
||||
// nsIX509CertDB depends on nsNSSComponent initialization, we can't use it.
|
||||
// Instead, we can use NSS APIs directly (as long as we're called late enough
|
||||
// in nsNSSComponent initialization such that those APIs are safe to use).
|
||||
MOZ_ASSERT(pccert);
|
||||
if (!pccert) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SECItem derCert = {
|
||||
siBuffer,
|
||||
certificate->pbCertEncoded,
|
||||
certificate->cbCertEncoded
|
||||
pccert->pbCertEncoded,
|
||||
pccert->cbCertEncoded
|
||||
};
|
||||
UniqueCERTCertificate nssCertificate(
|
||||
return UniqueCERTCertificate(
|
||||
CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &derCert,
|
||||
nullptr, // nickname unnecessary
|
||||
false, // not permanent
|
||||
true)); // copy DER
|
||||
}
|
||||
|
||||
static const char* kMicrosoftFamilySafetyCN = "Microsoft Family Safety";
|
||||
|
||||
nsresult
|
||||
nsNSSComponent::MaybeImportFamilySafetyRoot(PCCERT_CONTEXT certificate,
|
||||
bool& wasFamilySafetyRoot)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!NS_IsMainThread()) {
|
||||
return NS_ERROR_NOT_SAME_THREAD;
|
||||
}
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("MaybeImportFamilySafetyRoot"));
|
||||
wasFamilySafetyRoot = false;
|
||||
|
||||
UniqueCERTCertificate nssCertificate(
|
||||
PCCERT_CONTEXTToCERTCertificate(certificate));
|
||||
if (!nssCertificate) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't decode certificate"));
|
||||
return NS_ERROR_FAILURE;
|
||||
|
@ -685,34 +706,30 @@ MaybeImportFamilySafetyRoot(PCCERT_CONTEXT certificate,
|
|||
UniquePORTString subjectName(CERT_GetCommonName(&nssCertificate->subject));
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("subject name is '%s'", subjectName.get()));
|
||||
if (nsCRT::strcmp(subjectName.get(), "Microsoft Family Safety") == 0) {
|
||||
if (nsCRT::strcmp(subjectName.get(), kMicrosoftFamilySafetyCN) == 0) {
|
||||
wasFamilySafetyRoot = true;
|
||||
CERTCertTrust trust = {
|
||||
CERTDB_TRUSTED_CA | CERTDB_VALID_CA | CERTDB_USER,
|
||||
0,
|
||||
0
|
||||
};
|
||||
SECStatus srv = __CERT_AddTempCertToPerm(
|
||||
nssCertificate.get(), "Microsoft Family Safety", &trust);
|
||||
if (srv != SECSuccess) {
|
||||
if (CERT_ChangeCertTrust(nullptr, nssCertificate.get(), &trust)
|
||||
!= SECSuccess) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("couldn't permanently add certificate"));
|
||||
("couldn't trust certificate for TLS server auth"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsAutoCString dbKey;
|
||||
nsresult rv = nsNSSCertificate::GetDbKey(nssCertificate, dbKey);
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("GetDbKey failed"));
|
||||
return rv;
|
||||
}
|
||||
Preferences::SetCString(kImportedFamilySafetyRootPref, dbKey);
|
||||
MOZ_ASSERT(!mFamilySafetyRoot);
|
||||
mFamilySafetyRoot = Move(nssCertificate);
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("added Family Safety root"));
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Because HCERTSTORE is just a typedef void*, we can't use any of the nice
|
||||
// scoped pointer templates.
|
||||
// scoped or unique pointer templates. To elaborate, any attempt would
|
||||
// instantiate those templates with T = void. When T gets used in the context
|
||||
// of T&, this results in void&, which isn't legal.
|
||||
class ScopedCertStore final
|
||||
{
|
||||
public:
|
||||
|
@ -734,25 +751,25 @@ private:
|
|||
HCERTSTORE certstore;
|
||||
};
|
||||
|
||||
static const wchar_t* WindowsDefaultRootStoreName = L"ROOT";
|
||||
static const wchar_t* kWindowsDefaultRootStoreName = L"ROOT";
|
||||
|
||||
static nsresult
|
||||
LoadFamilySafetyRoot()
|
||||
nsresult
|
||||
nsNSSComponent::LoadFamilySafetyRoot()
|
||||
{
|
||||
ScopedCertStore certstore(
|
||||
CertOpenSystemStore(0, WindowsDefaultRootStoreName));
|
||||
CertOpenSystemStore(0, kWindowsDefaultRootStoreName));
|
||||
if (!certstore.get()) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("couldn't get certstore '%S'", WindowsDefaultRootStoreName));
|
||||
("couldn't get certstore '%S'", kWindowsDefaultRootStoreName));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
// Any resources held by the certificate are released by the next call to
|
||||
// CertFindCertificateInStore.
|
||||
PCCERT_CONTEXT certificate = nullptr;
|
||||
while (certificate = CertFindCertificateInStore(certstore.get(),
|
||||
X509_ASN_ENCODING, 0,
|
||||
CERT_FIND_ANY, nullptr,
|
||||
certificate)) {
|
||||
while ((certificate = CertFindCertificateInStore(certstore.get(),
|
||||
X509_ASN_ENCODING, 0,
|
||||
CERT_FIND_ANY, nullptr,
|
||||
certificate))) {
|
||||
bool wasFamilySafetyRoot = false;
|
||||
nsresult rv = MaybeImportFamilySafetyRoot(certificate,
|
||||
wasFamilySafetyRoot);
|
||||
|
@ -763,38 +780,32 @@ LoadFamilySafetyRoot()
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
static void
|
||||
UnloadFamilySafetyRoot()
|
||||
void
|
||||
nsNSSComponent::UnloadFamilySafetyRoot()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!NS_IsMainThread()) {
|
||||
return;
|
||||
}
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("UnloadFamilySafetyRoot"));
|
||||
nsAdoptingCString dbKey = Preferences::GetCString(
|
||||
kImportedFamilySafetyRootPref);
|
||||
if (!dbKey || dbKey.IsEmpty()) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("Family Safety root wasn't previously imported"));
|
||||
if (!mFamilySafetyRoot) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Family Safety Root wasn't present"));
|
||||
return;
|
||||
}
|
||||
UniqueCERTCertificate cert;
|
||||
nsresult rv = nsNSSCertificateDB::FindCertByDBKey(dbKey, cert);
|
||||
if (NS_FAILED(rv)) {
|
||||
// It would be intuitive to set the trust to { 0, 0, 0 } here. However, this
|
||||
// doesn't work for temporary certificates because CERT_ChangeCertTrust first
|
||||
// looks up the current trust settings in the permanent cert database, finds
|
||||
// that such trust doesn't exist, considers the current trust to be
|
||||
// { 0, 0, 0 }, and decides that it doesn't need to update the trust since
|
||||
// they're the same. To work around this, we set a non-zero flag to ensure
|
||||
// that the trust will get updated.
|
||||
CERTCertTrust trust = { CERTDB_USER, 0, 0 };
|
||||
if (CERT_ChangeCertTrust(nullptr, mFamilySafetyRoot.get(), &trust)
|
||||
!= SECSuccess) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("finding previously-imported Family Safety root failed"));
|
||||
return;
|
||||
("couldn't untrust certificate for TLS server auth"));
|
||||
}
|
||||
if (!cert) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("previously-imported Family Safety root not found"));
|
||||
return;
|
||||
}
|
||||
SECStatus srv = SEC_DeletePermCertificate(cert.get());
|
||||
if (srv != SECSuccess) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("couldn't delete previously-imported Family Safety root"));
|
||||
return;
|
||||
}
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("deleted previously-imported Family Safety root"));
|
||||
Preferences::ClearUser(kImportedFamilySafetyRootPref);
|
||||
mFamilySafetyRoot = nullptr;
|
||||
}
|
||||
|
||||
#endif // XP_WIN
|
||||
|
@ -812,8 +823,8 @@ const char* kFamilySafetyModePref = "security.family_safety.mode";
|
|||
// 5: Family Safety was enabled
|
||||
// 6: failed to import the Family Safety root
|
||||
// 7: successfully imported the root
|
||||
static void
|
||||
MaybeEnableFamilySafetyCompatibility()
|
||||
void
|
||||
nsNSSComponent::MaybeEnableFamilySafetyCompatibility()
|
||||
{
|
||||
#ifdef XP_WIN
|
||||
UnloadFamilySafetyRoot();
|
||||
|
@ -853,6 +864,211 @@ MaybeEnableFamilySafetyCompatibility()
|
|||
#endif // XP_WIN
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
// Helper function to determine if the OS considers the given certificate to be
|
||||
// a trust anchor for TLS server auth certificates. This is to be used in the
|
||||
// context of importing what are presumed to be root certificates from the OS.
|
||||
// If this function returns true but it turns out that the given certificate is
|
||||
// in some way unsuitable to issue certificates, mozilla::pkix will never build
|
||||
// a valid chain that includes the certificate, so importing it even if it
|
||||
// isn't a valid CA poses no risk.
|
||||
static bool
|
||||
CertIsTrustAnchorForTLSServerAuth(PCCERT_CONTEXT certificate)
|
||||
{
|
||||
MOZ_ASSERT(certificate);
|
||||
if (!certificate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PCCERT_CHAIN_CONTEXT pChainContext = nullptr;
|
||||
CERT_ENHKEY_USAGE enhkeyUsage;
|
||||
memset(&enhkeyUsage, 0, sizeof(CERT_ENHKEY_USAGE));
|
||||
LPSTR identifiers[] = {
|
||||
"1.3.6.1.5.5.7.3.1", // id-kp-serverAuth
|
||||
};
|
||||
enhkeyUsage.cUsageIdentifier = ArrayLength(identifiers);
|
||||
enhkeyUsage.rgpszUsageIdentifier = identifiers;
|
||||
CERT_USAGE_MATCH certUsage;
|
||||
memset(&certUsage, 0, sizeof(CERT_USAGE_MATCH));
|
||||
certUsage.dwType = USAGE_MATCH_TYPE_AND;
|
||||
certUsage.Usage = enhkeyUsage;
|
||||
CERT_CHAIN_PARA chainPara;
|
||||
memset(&chainPara, 0, sizeof(CERT_CHAIN_PARA));
|
||||
chainPara.cbSize = sizeof(CERT_CHAIN_PARA);
|
||||
chainPara.RequestedUsage = certUsage;
|
||||
|
||||
if (!CertGetCertificateChain(nullptr, certificate, nullptr, nullptr,
|
||||
&chainPara, 0, nullptr, &pChainContext)) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("CertGetCertificateChain failed"));
|
||||
return false;
|
||||
}
|
||||
bool trusted = pChainContext->TrustStatus.dwErrorStatus ==
|
||||
CERT_TRUST_NO_ERROR;
|
||||
bool isRoot = pChainContext->cChain == 1;
|
||||
CertFreeCertificateChain(pChainContext);
|
||||
if (trusted && isRoot) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("certificate is trust anchor for TLS server auth"));
|
||||
return true;
|
||||
}
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("certificate not trust anchor for TLS server auth"));
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
nsNSSComponent::UnloadEnterpriseRoots()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!NS_IsMainThread()) {
|
||||
return;
|
||||
}
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("UnloadEnterpriseRoots"));
|
||||
if (!mEnterpriseRoots) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("no enterprise roots were present"));
|
||||
return;
|
||||
}
|
||||
// It would be intuitive to set the trust to { 0, 0, 0 } here. However, this
|
||||
// doesn't work for temporary certificates because CERT_ChangeCertTrust first
|
||||
// looks up the current trust settings in the permanent cert database, finds
|
||||
// that such trust doesn't exist, considers the current trust to be
|
||||
// { 0, 0, 0 }, and decides that it doesn't need to update the trust since
|
||||
// they're the same. To work around this, we set a non-zero flag to ensure
|
||||
// that the trust will get updated.
|
||||
CERTCertTrust trust = { CERTDB_USER, 0, 0 };
|
||||
for (CERTCertListNode* n = CERT_LIST_HEAD(mEnterpriseRoots.get());
|
||||
!CERT_LIST_END(n, mEnterpriseRoots.get()); n = CERT_LIST_NEXT(n)) {
|
||||
if (!n || !n->cert) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("library failure: CERTCertListNode null or lacks cert"));
|
||||
continue;
|
||||
}
|
||||
if (CERT_ChangeCertTrust(nullptr, n->cert, &trust) != SECSuccess) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("couldn't untrust certificate for TLS server auth"));
|
||||
}
|
||||
}
|
||||
mEnterpriseRoots = nullptr;
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("unloaded enterprise roots"));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNSSComponent::GetEnterpriseRoots(nsIX509CertList** enterpriseRoots)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!NS_IsMainThread()) {
|
||||
return NS_ERROR_NOT_SAME_THREAD;
|
||||
}
|
||||
NS_ENSURE_ARG_POINTER(enterpriseRoots);
|
||||
|
||||
nsNSSShutDownPreventionLock lock;
|
||||
// nsNSSComponent isn't a nsNSSShutDownObject, so we can't check
|
||||
// isAlreadyShutDown(). However, since mEnterpriseRoots is cleared when NSS
|
||||
// shuts down, we can use that as a proxy for checking for NSS shutdown.
|
||||
// (Of course, it may also be the case that no enterprise roots were imported,
|
||||
// so we should just return a null list and NS_OK in this case.)
|
||||
if (!mEnterpriseRoots) {
|
||||
*enterpriseRoots = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
UniqueCERTCertList enterpriseRootsCopy(
|
||||
nsNSSCertList::DupCertList(mEnterpriseRoots, lock));
|
||||
if (!enterpriseRootsCopy) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsCOMPtr<nsIX509CertList> enterpriseRootsCertList(
|
||||
new nsNSSCertList(Move(enterpriseRootsCopy), lock));
|
||||
if (!enterpriseRootsCertList) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
enterpriseRootsCertList.forget(enterpriseRoots);
|
||||
return NS_OK;
|
||||
}
|
||||
#endif // XP_WIN
|
||||
|
||||
static const char* kEnterpriseRootModePref = "security.enterprise_roots.enabled";
|
||||
|
||||
void
|
||||
nsNSSComponent::MaybeImportEnterpriseRoots()
|
||||
{
|
||||
#ifdef XP_WIN
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!NS_IsMainThread()) {
|
||||
return;
|
||||
}
|
||||
UnloadEnterpriseRoots();
|
||||
bool importEnterpriseRoots = Preferences::GetBool(kEnterpriseRootModePref,
|
||||
false);
|
||||
if (!importEnterpriseRoots) {
|
||||
return;
|
||||
}
|
||||
DWORD flags = CERT_SYSTEM_STORE_LOCAL_MACHINE |
|
||||
CERT_STORE_OPEN_EXISTING_FLAG |
|
||||
CERT_STORE_READONLY_FLAG;
|
||||
// The certificate store being opened should consist only of certificates
|
||||
// added by a user or administrator and not any certificates that are part
|
||||
// of Microsoft's root store program.
|
||||
// The 3rd parameter to CertOpenStore should be NULL according to
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa376559%28v=vs.85%29.aspx
|
||||
ScopedCertStore enterpriseRootStore(CertOpenStore(
|
||||
CERT_STORE_PROV_SYSTEM_REGISTRY_W, 0, NULL, flags,
|
||||
kWindowsDefaultRootStoreName));
|
||||
if (!enterpriseRootStore.get()) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("failed to open enterprise root store"));
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(!mEnterpriseRoots);
|
||||
mEnterpriseRoots.reset(CERT_NewCertList());
|
||||
CERTCertTrust trust = {
|
||||
CERTDB_TRUSTED_CA | CERTDB_VALID_CA | CERTDB_USER,
|
||||
0,
|
||||
0
|
||||
};
|
||||
PCCERT_CONTEXT certificate = nullptr;
|
||||
uint32_t numImported = 0;
|
||||
while ((certificate = CertFindCertificateInStore(enterpriseRootStore.get(),
|
||||
X509_ASN_ENCODING, 0,
|
||||
CERT_FIND_ANY, nullptr,
|
||||
certificate))) {
|
||||
if (!CertIsTrustAnchorForTLSServerAuth(certificate)) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("skipping cert not trust anchor for TLS server auth"));
|
||||
continue;
|
||||
}
|
||||
UniqueCERTCertificate nssCertificate(
|
||||
PCCERT_CONTEXTToCERTCertificate(certificate));
|
||||
if (!nssCertificate) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't decode certificate"));
|
||||
continue;
|
||||
}
|
||||
// Don't import the Microsoft Family Safety root (this prevents the
|
||||
// Enterprise Roots feature from interacting poorly with the Family
|
||||
// Safety support).
|
||||
UniquePORTString subjectName(
|
||||
CERT_GetCommonName(&nssCertificate->subject));
|
||||
if (nsCRT::strcmp(subjectName.get(), kMicrosoftFamilySafetyCN) == 0) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("skipping Family Safety Root"));
|
||||
continue;
|
||||
}
|
||||
if (CERT_AddCertToListTail(mEnterpriseRoots.get(), nssCertificate.get())
|
||||
!= SECSuccess) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't add cert to list"));
|
||||
continue;
|
||||
}
|
||||
if (CERT_ChangeCertTrust(nullptr, nssCertificate.get(), &trust)
|
||||
!= SECSuccess) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("couldn't trust certificate for TLS server auth"));
|
||||
}
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Imported '%s'", subjectName.get()));
|
||||
numImported++;
|
||||
// now owned by mEnterpriseRoots
|
||||
Unused << nssCertificate.release();
|
||||
}
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("imported %u roots", numImported));
|
||||
#endif // XP_WIN
|
||||
}
|
||||
|
||||
void
|
||||
nsNSSComponent::LoadLoadableRoots()
|
||||
{
|
||||
|
@ -1559,6 +1775,7 @@ nsNSSComponent::InitializeNSS()
|
|||
LoadLoadableRoots();
|
||||
|
||||
MaybeEnableFamilySafetyCompatibility();
|
||||
MaybeImportEnterpriseRoots();
|
||||
|
||||
ConfigureTLSSessionIdentifiers();
|
||||
|
||||
|
@ -1657,6 +1874,11 @@ nsNSSComponent::ShutdownNSS()
|
|||
|
||||
Preferences::RemoveObserver(this, "security.");
|
||||
|
||||
#ifdef XP_WIN
|
||||
mFamilySafetyRoot = nullptr;
|
||||
mEnterpriseRoots = nullptr;
|
||||
#endif
|
||||
|
||||
#ifndef MOZ_NO_SMART_CARDS
|
||||
ShutdownSmartCardThreads();
|
||||
#endif
|
||||
|
@ -1800,6 +2022,8 @@ nsNSSComponent::Observe(nsISupports* aSubject, const char* aTopic,
|
|||
MutexAutoLock lock(mutex);
|
||||
mContentSigningRootHash =
|
||||
Preferences::GetString("security.content.signature.root_hash");
|
||||
} else if (prefName.Equals(kEnterpriseRootModePref)) {
|
||||
MaybeImportEnterpriseRoots();
|
||||
} else {
|
||||
clearSessionCache = false;
|
||||
}
|
||||
|
|
|
@ -7,18 +7,25 @@
|
|||
#ifndef _nsNSSComponent_h_
|
||||
#define _nsNSSComponent_h_
|
||||
|
||||
#include "ScopedNSSTypes.h"
|
||||
#include "SharedCertVerifier.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIStringBundle.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIStringBundle.h"
|
||||
#include "nsNSSCallbacks.h"
|
||||
#include "SharedCertVerifier.h"
|
||||
#include "prerror.h"
|
||||
#include "sslt.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include "windows.h" // this needs to be before the following includes
|
||||
#include "wincrypt.h"
|
||||
#endif // XP_WIN
|
||||
|
||||
class nsIDOMWindow;
|
||||
class nsIPrompt;
|
||||
class nsIX509CertList;
|
||||
class SmartCardThreadList;
|
||||
|
||||
namespace mozilla { namespace psm {
|
||||
|
@ -87,6 +94,10 @@ public:
|
|||
|
||||
NS_IMETHOD IsCertContentSigningRoot(CERTCertificate* cert, bool& result) = 0;
|
||||
|
||||
#ifdef XP_WIN
|
||||
NS_IMETHOD GetEnterpriseRoots(nsIX509CertList** enterpriseRoots) = 0;
|
||||
#endif
|
||||
|
||||
virtual ::already_AddRefed<mozilla::psm::SharedCertVerifier>
|
||||
GetDefaultCertVerifier() = 0;
|
||||
};
|
||||
|
@ -141,6 +152,10 @@ public:
|
|||
|
||||
NS_IMETHOD IsCertContentSigningRoot(CERTCertificate* cert, bool& result) override;
|
||||
|
||||
#ifdef XP_WIN
|
||||
NS_IMETHOD GetEnterpriseRoots(nsIX509CertList** enterpriseRoots) override;
|
||||
#endif
|
||||
|
||||
::already_AddRefed<mozilla::psm::SharedCertVerifier>
|
||||
GetDefaultCertVerifier() override;
|
||||
|
||||
|
@ -171,6 +186,20 @@ private:
|
|||
|
||||
void DoProfileBeforeChange();
|
||||
|
||||
void MaybeEnableFamilySafetyCompatibility();
|
||||
void MaybeImportEnterpriseRoots();
|
||||
#ifdef XP_WIN
|
||||
nsresult MaybeImportFamilySafetyRoot(PCCERT_CONTEXT certificate,
|
||||
bool& wasFamilySafetyRoot);
|
||||
nsresult LoadFamilySafetyRoot();
|
||||
void UnloadFamilySafetyRoot();
|
||||
|
||||
void UnloadEnterpriseRoots();
|
||||
|
||||
mozilla::UniqueCERTCertificate mFamilySafetyRoot;
|
||||
mozilla::UniqueCERTCertList mEnterpriseRoots;
|
||||
#endif // XP_WIN
|
||||
|
||||
mozilla::Mutex mutex;
|
||||
|
||||
nsCOMPtr<nsIStringBundle> mPIPNSSBundle;
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
"use strict";
|
||||
|
||||
// Tests enterprise root certificate support. When configured to do so, the
|
||||
// platform will attempt to find and import enterprise root certificates. This
|
||||
// feature is specific to Windows.
|
||||
|
||||
do_get_profile(); // must be called before getting nsIX509CertDB
|
||||
|
||||
function check_no_enterprise_roots_imported(certDB, dbKey = undefined) {
|
||||
let enterpriseRoots = certDB.getEnterpriseRoots();
|
||||
equal(enterpriseRoots, null, "should not have imported any enterprise roots");
|
||||
if (dbKey) {
|
||||
let cert = certDB.findCertByDBKey(dbKey);
|
||||
// If the garbage-collector hasn't run, there may be reachable copies of
|
||||
// imported enterprise root certificates. If so, they shouldn't be trusted
|
||||
// to issue TLS server auth certificates.
|
||||
if (cert) {
|
||||
ok(!certDB.isCertTrusted(cert, Ci.nsIX509Cert.CA_CERT,
|
||||
Ci.nsIX509CertDB.TRUSTED_SSL),
|
||||
"previously-imported enterprise root shouldn't be trusted to issue " +
|
||||
"TLS server auth certificates");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function check_some_enterprise_roots_imported(certDB) {
|
||||
let enterpriseRoots = certDB.getEnterpriseRoots();
|
||||
notEqual(enterpriseRoots, null, "should have imported some enterprise roots");
|
||||
let enumerator = enterpriseRoots.getEnumerator();
|
||||
let foundNonBuiltIn = false;
|
||||
let savedDBKey = null;
|
||||
while (enumerator.hasMoreElements()) {
|
||||
let cert = enumerator.getNext().QueryInterface(Ci.nsIX509Cert);
|
||||
if (!cert.isBuiltInRoot && !savedDBKey) {
|
||||
foundNonBuiltIn = true;
|
||||
savedDBKey = cert.dbKey;
|
||||
do_print("saving dbKey from " + cert.commonName);
|
||||
}
|
||||
}
|
||||
ok(foundNonBuiltIn, "should have found non-built-in root");
|
||||
return savedDBKey;
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
let certDB = Cc["@mozilla.org/security/x509certdb;1"]
|
||||
.getService(Ci.nsIX509CertDB);
|
||||
Services.prefs.setBoolPref("security.enterprise_roots.enabled", false);
|
||||
check_no_enterprise_roots_imported(certDB);
|
||||
Services.prefs.setBoolPref("security.enterprise_roots.enabled", true);
|
||||
let savedDBKey = check_some_enterprise_roots_imported(certDB);
|
||||
Services.prefs.setBoolPref("security.enterprise_roots.enabled", false);
|
||||
check_no_enterprise_roots_imported(certDB, savedDBKey);
|
||||
}
|
|
@ -57,6 +57,8 @@ skip-if = toolkit == 'android' || buildapp == 'b2g'
|
|||
[test_constructX509FromBase64.js]
|
||||
[test_content_signing.js]
|
||||
[test_datasignatureverifier.js]
|
||||
[test_enterprise_roots.js]
|
||||
skip-if = os != 'win' # tests a Windows-specific feature
|
||||
[test_ev_certs.js]
|
||||
run-sequentially = hardcoded ports
|
||||
[test_getchain.js]
|
||||
|
|
Загрузка…
Ссылка в новой задаче