mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-02 14:30:43 +00:00
bug 1300420 - add enterprise root support for OS X r=spohl,franziskus
If the preference security.enterprise_roots.enabled is set to true, the platform will import trusted TLS certificates from the OS X keystore. Differential Revision: https://phabricator.services.mozilla.com/D2169 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
96e550bb6b
commit
85865937f5
@ -6,14 +6,19 @@
|
||||
|
||||
#include "EnterpriseRoots.h"
|
||||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/Unused.h"
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
#include <Security/Security.h>
|
||||
#endif // XP_MACOSX
|
||||
|
||||
extern LazyLogModule gPIPNSSLog;
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
#ifdef XP_WIN
|
||||
|
||||
const wchar_t* kWindowsDefaultRootStoreName = L"ROOT";
|
||||
NS_NAMED_LITERAL_CSTRING(kMicrosoftFamilySafetyCN, "Microsoft Family Safety");
|
||||
|
||||
@ -118,6 +123,10 @@ GatherEnterpriseRootsForLocation(DWORD locationFlag, UniqueCERTCertList& roots)
|
||||
locationFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE)) {
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(roots, "roots unexpectedly NULL?");
|
||||
if (!roots) {
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD flags = locationFlag |
|
||||
CERT_STORE_OPEN_EXISTING_FLAG |
|
||||
@ -160,10 +169,6 @@ GatherEnterpriseRootsForLocation(DWORD locationFlag, UniqueCERTCertList& roots)
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("skipping Family Safety Root"));
|
||||
continue;
|
||||
}
|
||||
MOZ_ASSERT(roots, "roots unexpectedly NULL?");
|
||||
if (!roots) {
|
||||
return;
|
||||
}
|
||||
if (CERT_AddCertToListTail(roots.get(), nssCertificate.get())
|
||||
!= SECSuccess) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't add cert to list"));
|
||||
@ -188,6 +193,105 @@ GatherEnterpriseRootsWindows(UniqueCERTCertList& roots)
|
||||
}
|
||||
#endif // XP_WIN
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
template<typename T>
|
||||
class ScopedCFType
|
||||
{
|
||||
public:
|
||||
explicit ScopedCFType(T value)
|
||||
: mValue(value)
|
||||
{
|
||||
}
|
||||
|
||||
~ScopedCFType() { CFRelease((CFTypeRef)mValue); }
|
||||
|
||||
T get() { return mValue; }
|
||||
|
||||
private:
|
||||
T mValue;
|
||||
};
|
||||
|
||||
OSStatus
|
||||
GatherEnterpriseRootsOSX(UniqueCERTCertList& roots)
|
||||
{
|
||||
MOZ_ASSERT(roots, "roots unexpectedly NULL?");
|
||||
if (!roots) {
|
||||
return errSecBadReq;
|
||||
}
|
||||
// The following builds a search dictionary corresponding to:
|
||||
// { class: "certificate",
|
||||
// match limit: "match all",
|
||||
// policy: "SSL (TLS)",
|
||||
// only include trusted certificates: true }
|
||||
// This operates on items that have been added to the keychain and thus gives
|
||||
// us all 3rd party certificates that have been trusted for SSL (TLS), which
|
||||
// is what we want (thus we don't import built-in root certificates that ship
|
||||
// with the OS).
|
||||
const CFStringRef keys[] = { kSecClass,
|
||||
kSecMatchLimit,
|
||||
kSecMatchPolicy,
|
||||
kSecMatchTrustedOnly };
|
||||
// https://developer.apple.com/documentation/security/1392592-secpolicycreatessl
|
||||
ScopedCFType<SecPolicyRef> sslPolicy(SecPolicyCreateSSL(true, nullptr));
|
||||
const void* values[] = { kSecClassCertificate,
|
||||
kSecMatchLimitAll,
|
||||
sslPolicy.get(),
|
||||
kCFBooleanTrue };
|
||||
static_assert(ArrayLength(keys) == ArrayLength(values),
|
||||
"mismatched SecItemCopyMatching key/value array sizes");
|
||||
// https://developer.apple.com/documentation/corefoundation/1516782-cfdictionarycreate
|
||||
ScopedCFType<CFDictionaryRef> searchDictionary(
|
||||
CFDictionaryCreate(nullptr, (const void**)&keys, (const void**)&values,
|
||||
ArrayLength(keys), &kCFTypeDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks));
|
||||
CFTypeRef items;
|
||||
// https://developer.apple.com/documentation/security/1398306-secitemcopymatching
|
||||
OSStatus rv = SecItemCopyMatching(searchDictionary.get(), &items);
|
||||
if (rv != errSecSuccess) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("SecItemCopyMatching failed"));
|
||||
return rv;
|
||||
}
|
||||
// If given a match limit greater than 1 (which we did), SecItemCopyMatching
|
||||
// returns a CFArrayRef.
|
||||
ScopedCFType<CFArrayRef> arr(reinterpret_cast<CFArrayRef>(items));
|
||||
CFIndex count = CFArrayGetCount(arr.get());
|
||||
uint32_t numImported = 0;
|
||||
for (CFIndex i = 0; i < count; i++) {
|
||||
const CFTypeRef c = CFArrayGetValueAtIndex(arr.get(), i);
|
||||
// Because we asked for certificates, each CFTypeRef in the array is really
|
||||
// a SecCertificateRef.
|
||||
const SecCertificateRef s = (const SecCertificateRef)c;
|
||||
ScopedCFType<CFDataRef> der(SecCertificateCopyData(s));
|
||||
const unsigned char* ptr = CFDataGetBytePtr(der.get());
|
||||
unsigned int len = CFDataGetLength(der.get());
|
||||
SECItem derItem = {
|
||||
siBuffer,
|
||||
const_cast<unsigned char*>(ptr),
|
||||
len,
|
||||
};
|
||||
UniqueCERTCertificate cert(
|
||||
CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &derItem,
|
||||
nullptr, // nickname unnecessary
|
||||
false, // not permanent
|
||||
true)); // copy DER
|
||||
if (!cert) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't decode 3rd party root certificate"));
|
||||
continue;
|
||||
}
|
||||
UniquePORTString subjectName(CERT_GetCommonName(&cert->subject));
|
||||
if (CERT_AddCertToListTail(roots.get(), cert.get()) != SECSuccess) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't add cert to list"));
|
||||
continue;
|
||||
}
|
||||
numImported++;
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Imported '%s'", subjectName.get()));
|
||||
mozilla::Unused << cert.release(); // owned by roots
|
||||
}
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("imported %u roots", numImported));
|
||||
return errSecSuccess;
|
||||
}
|
||||
#endif // XP_MACOSX
|
||||
|
||||
nsresult
|
||||
GatherEnterpriseRoots(UniqueCERTCertList& result)
|
||||
{
|
||||
@ -198,6 +302,12 @@ GatherEnterpriseRoots(UniqueCERTCertList& result)
|
||||
#ifdef XP_WIN
|
||||
GatherEnterpriseRootsWindows(roots);
|
||||
#endif // XP_WIN
|
||||
#ifdef XP_MACOSX
|
||||
OSStatus rv = GatherEnterpriseRootsOSX(roots);
|
||||
if (rv != errSecSuccess) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
#endif // XP_MACOSX
|
||||
result = std::move(roots);
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -138,6 +138,11 @@ UNIFIED_SOURCES += [
|
||||
'TransportSecurityInfo.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['OS_ARCH'] == 'Darwin':
|
||||
OS_LIBS += [
|
||||
'-framework Security'
|
||||
]
|
||||
|
||||
IPDL_SOURCES += [
|
||||
'PPSMContentDownloader.ipdl',
|
||||
]
|
||||
|
@ -1903,13 +1903,11 @@ nsNSSComponent::InitializeNSS()
|
||||
mMitmDetecionEnabled =
|
||||
Preferences::GetBool("security.pki.mitm_canary_issuer.enabled", true);
|
||||
|
||||
#ifdef XP_WIN
|
||||
nsCOMPtr<nsINSSComponent> handle(this);
|
||||
NS_DispatchToCurrentThread(NS_NewRunnableFunction("nsNSSComponent::TrustLoaded3rdPartyRoots",
|
||||
[handle]() {
|
||||
MOZ_ALWAYS_SUCCEEDS(handle->TrustLoaded3rdPartyRoots());
|
||||
}));
|
||||
#endif // XP_WIN
|
||||
|
||||
// TLSServerSocket may be run with the session cache enabled. It is
|
||||
// necessary to call this once before that can happen. This specifies a
|
||||
|
@ -84,7 +84,9 @@ run-sequentially = hardcoded ports
|
||||
skip-if = toolkit == 'android'
|
||||
[test_der.js]
|
||||
[test_enterprise_roots.js]
|
||||
skip-if = os != 'win' # tests a Windows-specific feature
|
||||
# This feature is implemented for Windows and OS X. However, we don't currently
|
||||
# have a way to test it on OS X.
|
||||
skip-if = os != 'win'
|
||||
[test_ev_certs.js]
|
||||
tags = blocklist
|
||||
run-sequentially = hardcoded ports
|
||||
|
Loading…
x
Reference in New Issue
Block a user