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:
David Keeler 2016-04-13 15:36:22 -07:00
Родитель 4f13f0068e
Коммит 8ba29d1473
6 изменённых файлов: 413 добавлений и 68 удалений

Просмотреть файл

@ -390,4 +390,10 @@ interface nsIX509CertDB : nsISupports {
* Get all the known certs in the database * Get all the known certs in the database
*/ */
nsIX509CertList getCerts(); 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; 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 nsresult
VerifyCertAtTime(nsIX509Cert* aCert, VerifyCertAtTime(nsIX509Cert* aCert,
int64_t /*SECCertificateUsage*/ aUsage, int64_t /*SECCertificateUsage*/ aUsage,

Просмотреть файл

@ -12,6 +12,7 @@
#include "SharedSSLState.h" #include "SharedSSLState.h"
#include "cert.h" #include "cert.h"
#include "certdb.h" #include "certdb.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Casting.h" #include "mozilla/Casting.h"
#include "mozilla/Preferences.h" #include "mozilla/Preferences.h"
#include "mozilla/PublicSSL.h" #include "mozilla/PublicSSL.h"
@ -19,6 +20,7 @@
#include "mozilla/StaticPtr.h" #include "mozilla/StaticPtr.h"
#include "mozilla/SyncRunnable.h" #include "mozilla/SyncRunnable.h"
#include "mozilla/Telemetry.h" #include "mozilla/Telemetry.h"
#include "mozilla/unused.h"
#include "nsAppDirectoryServiceDefs.h" #include "nsAppDirectoryServiceDefs.h"
#include "nsCRT.h" #include "nsCRT.h"
#include "nsCertVerificationThread.h" #include "nsCertVerificationThread.h"
@ -442,7 +444,7 @@ GetUserSid(nsAString& sidString)
} }
char sid_buffer[SECURITY_MAX_SID_SIZE]; char sid_buffer[SECURITY_MAX_SID_SIZE];
SID* sid = BitwiseCast<SID*, char*>(sid_buffer); SID* sid = BitwiseCast<SID*, char*>(sid_buffer);
DWORD cbSid = MOZ_ARRAY_LENGTH(sid_buffer); DWORD cbSid = ArrayLength(sid_buffer);
SID_NAME_USE eUse; SID_NAME_USE eUse;
// There doesn't appear to be a defined maximum length for the domain name // 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 // here. To deal with this, we start with a reasonable buffer length and
@ -652,31 +654,50 @@ AccountHasFamilySafetyEnabled(bool& enabled)
return NS_OK; return NS_OK;
} }
const char* kImportedFamilySafetyRootPref = // It would be convenient to just use nsIX509CertDB in the following code.
"security.family_safety.imported_root.db_key"; // 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 // Helper function to convert a PCCERT_CONTEXT (i.e. a certificate obtained via
MaybeImportFamilySafetyRoot(PCCERT_CONTEXT certificate, // a Windows API) to a temporary CERTCertificate (i.e. a certificate for use
bool& wasFamilySafetyRoot) // with NSS APIs).
static UniqueCERTCertificate
PCCERT_CONTEXTToCERTCertificate(PCCERT_CONTEXT pccert)
{ {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("MaybeImportFamilySafetyRoot")); MOZ_ASSERT(pccert);
wasFamilySafetyRoot = false; if (!pccert) {
return nullptr;
// 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).
SECItem derCert = { SECItem derCert = {
siBuffer, siBuffer,
certificate->pbCertEncoded, pccert->pbCertEncoded,
certificate->cbCertEncoded pccert->cbCertEncoded
}; };
UniqueCERTCertificate nssCertificate( return UniqueCERTCertificate(
CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &derCert, CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &derCert,
nullptr, // nickname unnecessary nullptr, // nickname unnecessary
false, // not permanent false, // not permanent
true)); // copy DER 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) { if (!nssCertificate) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't decode certificate")); MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't decode certificate"));
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
@ -685,34 +706,30 @@ MaybeImportFamilySafetyRoot(PCCERT_CONTEXT certificate,
UniquePORTString subjectName(CERT_GetCommonName(&nssCertificate->subject)); UniquePORTString subjectName(CERT_GetCommonName(&nssCertificate->subject));
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("subject name is '%s'", subjectName.get())); ("subject name is '%s'", subjectName.get()));
if (nsCRT::strcmp(subjectName.get(), "Microsoft Family Safety") == 0) { if (nsCRT::strcmp(subjectName.get(), kMicrosoftFamilySafetyCN) == 0) {
wasFamilySafetyRoot = true; wasFamilySafetyRoot = true;
CERTCertTrust trust = { CERTCertTrust trust = {
CERTDB_TRUSTED_CA | CERTDB_VALID_CA | CERTDB_USER, CERTDB_TRUSTED_CA | CERTDB_VALID_CA | CERTDB_USER,
0, 0,
0 0
}; };
SECStatus srv = __CERT_AddTempCertToPerm( if (CERT_ChangeCertTrust(nullptr, nssCertificate.get(), &trust)
nssCertificate.get(), "Microsoft Family Safety", &trust); != SECSuccess) {
if (srv != SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("couldn't permanently add certificate")); ("couldn't trust certificate for TLS server auth"));
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
nsAutoCString dbKey; MOZ_ASSERT(!mFamilySafetyRoot);
nsresult rv = nsNSSCertificate::GetDbKey(nssCertificate, dbKey); mFamilySafetyRoot = Move(nssCertificate);
if (NS_FAILED(rv)) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("GetDbKey failed"));
return rv;
}
Preferences::SetCString(kImportedFamilySafetyRootPref, dbKey);
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("added Family Safety root")); MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("added Family Safety root"));
} }
return NS_OK; return NS_OK;
} }
// Because HCERTSTORE is just a typedef void*, we can't use any of the nice // 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 class ScopedCertStore final
{ {
public: public:
@ -734,25 +751,25 @@ private:
HCERTSTORE certstore; HCERTSTORE certstore;
}; };
static const wchar_t* WindowsDefaultRootStoreName = L"ROOT"; static const wchar_t* kWindowsDefaultRootStoreName = L"ROOT";
static nsresult nsresult
LoadFamilySafetyRoot() nsNSSComponent::LoadFamilySafetyRoot()
{ {
ScopedCertStore certstore( ScopedCertStore certstore(
CertOpenSystemStore(0, WindowsDefaultRootStoreName)); CertOpenSystemStore(0, kWindowsDefaultRootStoreName));
if (!certstore.get()) { if (!certstore.get()) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("couldn't get certstore '%S'", WindowsDefaultRootStoreName)); ("couldn't get certstore '%S'", kWindowsDefaultRootStoreName));
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
// Any resources held by the certificate are released by the next call to // Any resources held by the certificate are released by the next call to
// CertFindCertificateInStore. // CertFindCertificateInStore.
PCCERT_CONTEXT certificate = nullptr; PCCERT_CONTEXT certificate = nullptr;
while (certificate = CertFindCertificateInStore(certstore.get(), while ((certificate = CertFindCertificateInStore(certstore.get(),
X509_ASN_ENCODING, 0, X509_ASN_ENCODING, 0,
CERT_FIND_ANY, nullptr, CERT_FIND_ANY, nullptr,
certificate)) { certificate))) {
bool wasFamilySafetyRoot = false; bool wasFamilySafetyRoot = false;
nsresult rv = MaybeImportFamilySafetyRoot(certificate, nsresult rv = MaybeImportFamilySafetyRoot(certificate,
wasFamilySafetyRoot); wasFamilySafetyRoot);
@ -763,38 +780,32 @@ LoadFamilySafetyRoot()
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
static void void
UnloadFamilySafetyRoot() nsNSSComponent::UnloadFamilySafetyRoot()
{ {
MOZ_ASSERT(NS_IsMainThread());
if (!NS_IsMainThread()) {
return;
}
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("UnloadFamilySafetyRoot")); MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("UnloadFamilySafetyRoot"));
nsAdoptingCString dbKey = Preferences::GetCString( if (!mFamilySafetyRoot) {
kImportedFamilySafetyRootPref); MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Family Safety Root wasn't present"));
if (!dbKey || dbKey.IsEmpty()) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("Family Safety root wasn't previously imported"));
return; return;
} }
UniqueCERTCertificate cert; // It would be intuitive to set the trust to { 0, 0, 0 } here. However, this
nsresult rv = nsNSSCertificateDB::FindCertByDBKey(dbKey, cert); // doesn't work for temporary certificates because CERT_ChangeCertTrust first
if (NS_FAILED(rv)) { // 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, MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("finding previously-imported Family Safety root failed")); ("couldn't untrust certificate for TLS server auth"));
return;
} }
if (!cert) { mFamilySafetyRoot = nullptr;
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);
} }
#endif // XP_WIN #endif // XP_WIN
@ -812,8 +823,8 @@ const char* kFamilySafetyModePref = "security.family_safety.mode";
// 5: Family Safety was enabled // 5: Family Safety was enabled
// 6: failed to import the Family Safety root // 6: failed to import the Family Safety root
// 7: successfully imported the root // 7: successfully imported the root
static void void
MaybeEnableFamilySafetyCompatibility() nsNSSComponent::MaybeEnableFamilySafetyCompatibility()
{ {
#ifdef XP_WIN #ifdef XP_WIN
UnloadFamilySafetyRoot(); UnloadFamilySafetyRoot();
@ -853,6 +864,211 @@ MaybeEnableFamilySafetyCompatibility()
#endif // XP_WIN #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 void
nsNSSComponent::LoadLoadableRoots() nsNSSComponent::LoadLoadableRoots()
{ {
@ -1559,6 +1775,7 @@ nsNSSComponent::InitializeNSS()
LoadLoadableRoots(); LoadLoadableRoots();
MaybeEnableFamilySafetyCompatibility(); MaybeEnableFamilySafetyCompatibility();
MaybeImportEnterpriseRoots();
ConfigureTLSSessionIdentifiers(); ConfigureTLSSessionIdentifiers();
@ -1657,6 +1874,11 @@ nsNSSComponent::ShutdownNSS()
Preferences::RemoveObserver(this, "security."); Preferences::RemoveObserver(this, "security.");
#ifdef XP_WIN
mFamilySafetyRoot = nullptr;
mEnterpriseRoots = nullptr;
#endif
#ifndef MOZ_NO_SMART_CARDS #ifndef MOZ_NO_SMART_CARDS
ShutdownSmartCardThreads(); ShutdownSmartCardThreads();
#endif #endif
@ -1800,6 +2022,8 @@ nsNSSComponent::Observe(nsISupports* aSubject, const char* aTopic,
MutexAutoLock lock(mutex); MutexAutoLock lock(mutex);
mContentSigningRootHash = mContentSigningRootHash =
Preferences::GetString("security.content.signature.root_hash"); Preferences::GetString("security.content.signature.root_hash");
} else if (prefName.Equals(kEnterpriseRootModePref)) {
MaybeImportEnterpriseRoots();
} else { } else {
clearSessionCache = false; clearSessionCache = false;
} }

Просмотреть файл

@ -7,18 +7,25 @@
#ifndef _nsNSSComponent_h_ #ifndef _nsNSSComponent_h_
#define _nsNSSComponent_h_ #define _nsNSSComponent_h_
#include "ScopedNSSTypes.h"
#include "SharedCertVerifier.h"
#include "mozilla/Mutex.h" #include "mozilla/Mutex.h"
#include "mozilla/RefPtr.h" #include "mozilla/RefPtr.h"
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "nsIStringBundle.h"
#include "nsIObserver.h" #include "nsIObserver.h"
#include "nsIStringBundle.h"
#include "nsNSSCallbacks.h" #include "nsNSSCallbacks.h"
#include "SharedCertVerifier.h"
#include "prerror.h" #include "prerror.h"
#include "sslt.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 nsIDOMWindow;
class nsIPrompt; class nsIPrompt;
class nsIX509CertList;
class SmartCardThreadList; class SmartCardThreadList;
namespace mozilla { namespace psm { namespace mozilla { namespace psm {
@ -87,6 +94,10 @@ public:
NS_IMETHOD IsCertContentSigningRoot(CERTCertificate* cert, bool& result) = 0; NS_IMETHOD IsCertContentSigningRoot(CERTCertificate* cert, bool& result) = 0;
#ifdef XP_WIN
NS_IMETHOD GetEnterpriseRoots(nsIX509CertList** enterpriseRoots) = 0;
#endif
virtual ::already_AddRefed<mozilla::psm::SharedCertVerifier> virtual ::already_AddRefed<mozilla::psm::SharedCertVerifier>
GetDefaultCertVerifier() = 0; GetDefaultCertVerifier() = 0;
}; };
@ -141,6 +152,10 @@ public:
NS_IMETHOD IsCertContentSigningRoot(CERTCertificate* cert, bool& result) override; NS_IMETHOD IsCertContentSigningRoot(CERTCertificate* cert, bool& result) override;
#ifdef XP_WIN
NS_IMETHOD GetEnterpriseRoots(nsIX509CertList** enterpriseRoots) override;
#endif
::already_AddRefed<mozilla::psm::SharedCertVerifier> ::already_AddRefed<mozilla::psm::SharedCertVerifier>
GetDefaultCertVerifier() override; GetDefaultCertVerifier() override;
@ -171,6 +186,20 @@ private:
void DoProfileBeforeChange(); 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; mozilla::Mutex mutex;
nsCOMPtr<nsIStringBundle> mPIPNSSBundle; 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_constructX509FromBase64.js]
[test_content_signing.js] [test_content_signing.js]
[test_datasignatureverifier.js] [test_datasignatureverifier.js]
[test_enterprise_roots.js]
skip-if = os != 'win' # tests a Windows-specific feature
[test_ev_certs.js] [test_ev_certs.js]
run-sequentially = hardcoded ports run-sequentially = hardcoded ports
[test_getchain.js] [test_getchain.js]