522 lines
21 KiB
C++
522 lines
21 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
|
|
|
|
#include "EnterpriseRoots.h"
|
|
|
|
#include "PKCS11ModuleDB.h"
|
|
#include "mozilla/ArrayUtils.h"
|
|
#include "mozilla/IntegerPrintfMacros.h"
|
|
#include "mozilla/Casting.h"
|
|
#include "mozilla/Logging.h"
|
|
#include "mozilla/Unused.h"
|
|
#include "mozpkix/Result.h"
|
|
#include "nsCRT.h"
|
|
#include "nsThreadUtils.h"
|
|
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
# include "mozilla/java/EnterpriseRootsWrappers.h"
|
|
#endif // MOZ_WIDGET_ANDROID
|
|
|
|
#ifdef XP_MACOSX
|
|
# include <Security/Security.h>
|
|
# include "KeychainSecret.h"
|
|
#endif
|
|
|
|
#ifdef XP_WIN
|
|
# include <windows.h>
|
|
# include <wincrypt.h>
|
|
#endif // XP_WIN
|
|
|
|
extern mozilla::LazyLogModule gPIPNSSLog;
|
|
|
|
using namespace mozilla;
|
|
using namespace psm;
|
|
|
|
void EnterpriseCert::CopyBytes(nsTArray<uint8_t>& dest) const {
|
|
dest.Assign(mDER);
|
|
}
|
|
|
|
pkix::Result EnterpriseCert::GetInput(pkix::Input& input) const {
|
|
return input.Init(mDER.Elements(), mDER.Length());
|
|
}
|
|
|
|
bool EnterpriseCert::GetIsRoot() const { return mIsRoot; }
|
|
|
|
bool EnterpriseCert::IsKnownRoot(UniqueSECMODModule& rootsModule) {
|
|
if (!rootsModule) {
|
|
return false;
|
|
}
|
|
|
|
SECItem certItem = {siBuffer, mDER.Elements(),
|
|
static_cast<unsigned int>(mDER.Length())};
|
|
AutoSECMODListReadLock lock;
|
|
for (int i = 0; i < rootsModule->slotCount; i++) {
|
|
PK11SlotInfo* slot = rootsModule->slots[i];
|
|
if (PK11_FindEncodedCertInSlot(slot, &certItem, nullptr) !=
|
|
CK_INVALID_HANDLE) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#ifdef XP_WIN
|
|
struct CertStoreLocation {
|
|
const wchar_t* mName;
|
|
const bool mIsRoot;
|
|
|
|
CertStoreLocation(const wchar_t* name, bool isRoot)
|
|
: mName(name), mIsRoot(isRoot) {}
|
|
};
|
|
|
|
// The documentation doesn't make this clear, but the certificate location
|
|
// identified by "ROOT" contains trusted root certificates. The certificate
|
|
// location identified by "CA" contains intermediate certificates.
|
|
MOZ_RUNINIT const CertStoreLocation kCertStoreLocations[] = {
|
|
CertStoreLocation(L"ROOT", true), CertStoreLocation(L"CA", false)};
|
|
|
|
// Because HCERTSTORE is just a typedef void*, we can't use any of the nice
|
|
// 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:
|
|
explicit ScopedCertStore(HCERTSTORE certstore) : certstore(certstore) {}
|
|
|
|
~ScopedCertStore() { CertCloseStore(certstore, 0); }
|
|
|
|
HCERTSTORE get() { return certstore; }
|
|
|
|
private:
|
|
ScopedCertStore(const ScopedCertStore&) = delete;
|
|
ScopedCertStore& operator=(const ScopedCertStore&) = delete;
|
|
HCERTSTORE certstore;
|
|
};
|
|
|
|
// To determine if a certificate would be useful when verifying a server
|
|
// certificate for TLS server auth, Windows provides the function
|
|
// `CertGetEnhancedKeyUsage`, which combines the extended key usage extension
|
|
// with something called "enhanced key usage", which appears to be a Microsoft
|
|
// concept.
|
|
static bool CertCanBeUsedForTLSServerAuth(PCCERT_CONTEXT certificate) {
|
|
DWORD usageSize = 0;
|
|
if (!CertGetEnhancedKeyUsage(certificate, 0, NULL, &usageSize)) {
|
|
return false;
|
|
}
|
|
nsTArray<uint8_t> usageBytes;
|
|
usageBytes.SetLength(usageSize);
|
|
PCERT_ENHKEY_USAGE usage(
|
|
reinterpret_cast<PCERT_ENHKEY_USAGE>(usageBytes.Elements()));
|
|
if (!CertGetEnhancedKeyUsage(certificate, 0, usage, &usageSize)) {
|
|
return false;
|
|
}
|
|
// https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certgetenhancedkeyusage:
|
|
// "If the cUsageIdentifier member is zero, the certificate might be valid
|
|
// for all uses or the certificate might have no valid uses. The return from
|
|
// a call to GetLastError can be used to determine whether the certificate is
|
|
// good for all uses or for none. If GetLastError returns CRYPT_E_NOT_FOUND,
|
|
// the certificate is good for all uses. If it returns zero, the certificate
|
|
// has no valid uses."
|
|
if (usage->cUsageIdentifier == 0) {
|
|
return GetLastError() == static_cast<DWORD>(CRYPT_E_NOT_FOUND);
|
|
}
|
|
for (DWORD i = 0; i < usage->cUsageIdentifier; i++) {
|
|
if (!nsCRT::strcmp(usage->rgpszUsageIdentifier[i],
|
|
szOID_PKIX_KP_SERVER_AUTH) ||
|
|
!nsCRT::strcmp(usage->rgpszUsageIdentifier[i],
|
|
szOID_ANY_ENHANCED_KEY_USAGE)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Loads the enterprise roots at the registry location corresponding to the
|
|
// given location flag.
|
|
// Supported flags are:
|
|
// CERT_SYSTEM_STORE_LOCAL_MACHINE
|
|
// (for HKLM\SOFTWARE\Microsoft\SystemCertificates)
|
|
// CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY
|
|
// (for HKLM\SOFTWARE\Policy\Microsoft\SystemCertificates)
|
|
// CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE
|
|
// (for HKLM\SOFTWARE\Microsoft\EnterpriseCertificates)
|
|
// CERT_SYSTEM_STORE_CURRENT_USER
|
|
// (for HKCU\SOFTWARE\Microsoft\SystemCertificates)
|
|
// CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY
|
|
// (for HKCU\SOFTWARE\Policy\Microsoft\SystemCertificates)
|
|
static void GatherEnterpriseCertsForLocation(DWORD locationFlag,
|
|
nsTArray<EnterpriseCert>& certs,
|
|
UniqueSECMODModule& rootsModule) {
|
|
MOZ_ASSERT(locationFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE ||
|
|
locationFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY ||
|
|
locationFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE ||
|
|
locationFlag == CERT_SYSTEM_STORE_CURRENT_USER ||
|
|
locationFlag == CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY,
|
|
"unexpected locationFlag for GatherEnterpriseRootsForLocation");
|
|
if (!(locationFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE ||
|
|
locationFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY ||
|
|
locationFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE ||
|
|
locationFlag == CERT_SYSTEM_STORE_CURRENT_USER ||
|
|
locationFlag == CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY)) {
|
|
return;
|
|
}
|
|
|
|
DWORD flags =
|
|
locationFlag | 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
|
|
for (const auto& location : kCertStoreLocations) {
|
|
ScopedCertStore certStore(CertOpenStore(CERT_STORE_PROV_SYSTEM_REGISTRY_W,
|
|
0, NULL, flags, location.mName));
|
|
if (!certStore.get()) {
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
("failed to open certificate store"));
|
|
continue;
|
|
}
|
|
PCCERT_CONTEXT certificate = nullptr;
|
|
uint32_t numImported = 0;
|
|
while ((certificate = CertFindCertificateInStore(
|
|
certStore.get(), X509_ASN_ENCODING, 0, CERT_FIND_ANY, nullptr,
|
|
certificate))) {
|
|
if (!CertCanBeUsedForTLSServerAuth(certificate)) {
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
("skipping cert not relevant for TLS server auth"));
|
|
continue;
|
|
}
|
|
EnterpriseCert enterpriseCert(certificate->pbCertEncoded,
|
|
certificate->cbCertEncoded,
|
|
location.mIsRoot);
|
|
if (enterpriseCert.GetIsRoot() ||
|
|
!enterpriseCert.IsKnownRoot(rootsModule)) {
|
|
certs.AppendElement(std::move(enterpriseCert));
|
|
numImported++;
|
|
} else {
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
("skipping intermediate that is a known root cert"));
|
|
}
|
|
}
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
("imported %u certs from %S", numImported, location.mName));
|
|
}
|
|
}
|
|
|
|
static void GatherEnterpriseCertsWindows(nsTArray<EnterpriseCert>& certs,
|
|
UniqueSECMODModule& rootsModule) {
|
|
GatherEnterpriseCertsForLocation(CERT_SYSTEM_STORE_LOCAL_MACHINE, certs,
|
|
rootsModule);
|
|
GatherEnterpriseCertsForLocation(CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY,
|
|
certs, rootsModule);
|
|
GatherEnterpriseCertsForLocation(CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE,
|
|
certs, rootsModule);
|
|
GatherEnterpriseCertsForLocation(CERT_SYSTEM_STORE_CURRENT_USER, certs,
|
|
rootsModule);
|
|
GatherEnterpriseCertsForLocation(CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY,
|
|
certs, rootsModule);
|
|
}
|
|
#endif // XP_WIN
|
|
|
|
#ifdef XP_MACOSX
|
|
enum class CertificateTrustResult {
|
|
CanUseAsIntermediate,
|
|
CanUseAsTrustAnchor,
|
|
DoNotUse,
|
|
};
|
|
|
|
ScopedCFType<CFArrayRef> GetCertificateTrustSettingsInDomain(
|
|
const SecCertificateRef certificate, SecTrustSettingsDomain domain) {
|
|
CFArrayRef trustSettingsRaw;
|
|
OSStatus rv =
|
|
SecTrustSettingsCopyTrustSettings(certificate, domain, &trustSettingsRaw);
|
|
if (rv != errSecSuccess || !trustSettingsRaw) {
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
(" SecTrustSettingsCopyTrustSettings failed (or not found) for "
|
|
"domain %" PRIu32,
|
|
domain));
|
|
return nullptr;
|
|
}
|
|
ScopedCFType<CFArrayRef> trustSettings(trustSettingsRaw);
|
|
return trustSettings;
|
|
}
|
|
|
|
// This function processes trust settings returned by
|
|
// SecTrustSettingsCopyTrustSettings. See the documentation at
|
|
// https://developer.apple.com/documentation/security/1400261-sectrustsettingscopytrustsetting
|
|
// `trustSettings` is an array of CFDictionaryRef. Each dictionary may impose
|
|
// a constraint.
|
|
CertificateTrustResult ProcessCertificateTrustSettings(
|
|
ScopedCFType<CFArrayRef>& trustSettings) {
|
|
// If the array is empty, the certificate is a trust anchor.
|
|
const CFIndex numTrustDictionaries = CFArrayGetCount(trustSettings.get());
|
|
if (numTrustDictionaries == 0) {
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
(" empty trust settings -> trust anchor"));
|
|
return CertificateTrustResult::CanUseAsTrustAnchor;
|
|
}
|
|
CertificateTrustResult currentTrustSettings =
|
|
CertificateTrustResult::CanUseAsIntermediate;
|
|
for (CFIndex i = 0; i < numTrustDictionaries; i++) {
|
|
CFDictionaryRef trustDictionary = reinterpret_cast<CFDictionaryRef>(
|
|
CFArrayGetValueAtIndex(trustSettings.get(), i));
|
|
// kSecTrustSettingsApplication specifies an external application that
|
|
// determines the certificate's trust settings.
|
|
// kSecTrustSettingsPolicyString appears to be a mechanism like name
|
|
// constraints.
|
|
// These are not supported, so conservatively assume this certificate is
|
|
// distrusted if either are present.
|
|
if (CFDictionaryContainsKey(trustDictionary,
|
|
kSecTrustSettingsApplication) ||
|
|
CFDictionaryContainsKey(trustDictionary,
|
|
kSecTrustSettingsPolicyString)) {
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
(" found unsupported policy -> assuming distrusted"));
|
|
return CertificateTrustResult::DoNotUse;
|
|
}
|
|
|
|
// kSecTrustSettingsKeyUsage seems to be essentially the equivalent of the
|
|
// x509 keyUsage extension. For parity, we allow
|
|
// kSecTrustSettingsKeyUseSignature, kSecTrustSettingsKeyUseSignCert, and
|
|
// kSecTrustSettingsKeyUseAny.
|
|
if (CFDictionaryContainsKey(trustDictionary, kSecTrustSettingsKeyUsage)) {
|
|
CFNumberRef keyUsage = (CFNumberRef)CFDictionaryGetValue(
|
|
trustDictionary, kSecTrustSettingsKeyUsage);
|
|
int32_t keyUsageValue;
|
|
if (!keyUsage ||
|
|
CFNumberGetValue(keyUsage, kCFNumberSInt32Type, &keyUsageValue) ||
|
|
keyUsageValue < 0) {
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
(" no trust settings key usage or couldn't get value"));
|
|
return CertificateTrustResult::DoNotUse;
|
|
}
|
|
switch ((uint64_t)keyUsageValue) {
|
|
case kSecTrustSettingsKeyUseSignature: // fall-through
|
|
case kSecTrustSettingsKeyUseSignCert: // fall-through
|
|
case kSecTrustSettingsKeyUseAny:
|
|
break;
|
|
default:
|
|
return CertificateTrustResult::DoNotUse;
|
|
}
|
|
}
|
|
|
|
// If there is a specific policy, ensure that it's for the
|
|
// 'kSecPolicyAppleSSL' policy, which is the TLS server auth policy (i.e.
|
|
// x509 + domain name checking).
|
|
if (CFDictionaryContainsKey(trustDictionary, kSecTrustSettingsPolicy)) {
|
|
SecPolicyRef policy = (SecPolicyRef)CFDictionaryGetValue(
|
|
trustDictionary, kSecTrustSettingsPolicy);
|
|
if (!policy) {
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
(" kSecTrustSettingsPolicy present, but null?"));
|
|
continue;
|
|
}
|
|
ScopedCFType<CFDictionaryRef> policyProperties(
|
|
SecPolicyCopyProperties(policy));
|
|
CFStringRef policyOid = (CFStringRef)CFDictionaryGetValue(
|
|
policyProperties.get(), kSecPolicyOid);
|
|
if (!CFEqual(policyOid, kSecPolicyAppleSSL)) {
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, (" policy doesn't match"));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// By default, the trust setting result value is
|
|
// kSecTrustSettingsResultTrustRoot.
|
|
int32_t trustSettingsValue = kSecTrustSettingsResultTrustRoot;
|
|
if (CFDictionaryContainsKey(trustDictionary, kSecTrustSettingsResult)) {
|
|
CFNumberRef trustSetting = (CFNumberRef)CFDictionaryGetValue(
|
|
trustDictionary, kSecTrustSettingsResult);
|
|
if (!trustSetting || !CFNumberGetValue(trustSetting, kCFNumberSInt32Type,
|
|
&trustSettingsValue)) {
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
(" no trust settings result or couldn't get value"));
|
|
continue;
|
|
}
|
|
}
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
(" trust setting: %d", trustSettingsValue));
|
|
if (trustSettingsValue == kSecTrustSettingsResultDeny) {
|
|
return CertificateTrustResult::DoNotUse;
|
|
}
|
|
if (trustSettingsValue == kSecTrustSettingsResultTrustRoot ||
|
|
trustSettingsValue == kSecTrustSettingsResultTrustAsRoot) {
|
|
currentTrustSettings = CertificateTrustResult::CanUseAsTrustAnchor;
|
|
}
|
|
}
|
|
return currentTrustSettings;
|
|
}
|
|
|
|
CertificateTrustResult GetCertificateTrustResult(
|
|
const SecCertificateRef certificate) {
|
|
ScopedCFType<CFStringRef> subject(
|
|
SecCertificateCopySubjectSummary(certificate));
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
("determining trust for '%s'",
|
|
CFStringGetCStringPtr(subject.get(), kCFStringEncodingUTF8)));
|
|
// There are three trust settings domains: kSecTrustSettingsDomainUser,
|
|
// kSecTrustSettingsDomainAdmin, and kSecTrustSettingsDomainSystem. User
|
|
// overrides admin and admin overrides system. However, if the given
|
|
// certificate has trust settings in the system domain, it shipped with the
|
|
// OS, so we don't want to use it.
|
|
ScopedCFType<CFArrayRef> systemTrustSettings(
|
|
GetCertificateTrustSettingsInDomain(certificate,
|
|
kSecTrustSettingsDomainSystem));
|
|
if (systemTrustSettings) {
|
|
return CertificateTrustResult::DoNotUse;
|
|
}
|
|
|
|
// At this point, if there is no trust information regarding this
|
|
// certificate, it can be used as an intermediate.
|
|
CertificateTrustResult certificateTrustResult =
|
|
CertificateTrustResult::CanUseAsIntermediate;
|
|
|
|
// Process trust information in the user domain, if any.
|
|
ScopedCFType<CFArrayRef> userTrustSettings(
|
|
GetCertificateTrustSettingsInDomain(certificate,
|
|
kSecTrustSettingsDomainUser));
|
|
if (userTrustSettings) {
|
|
certificateTrustResult = ProcessCertificateTrustSettings(userTrustSettings);
|
|
// If there is definite information one way or another (either indicating
|
|
// this is a trusted root or a distrusted certificate), use that
|
|
// information.
|
|
if (certificateTrustResult !=
|
|
CertificateTrustResult::CanUseAsIntermediate) {
|
|
return certificateTrustResult;
|
|
}
|
|
}
|
|
|
|
// Process trust information in the admin domain, if any.
|
|
ScopedCFType<CFArrayRef> adminTrustSettings(
|
|
GetCertificateTrustSettingsInDomain(certificate,
|
|
kSecTrustSettingsDomainAdmin));
|
|
if (adminTrustSettings) {
|
|
certificateTrustResult =
|
|
ProcessCertificateTrustSettings(adminTrustSettings);
|
|
}
|
|
|
|
// Use whatever result we ended up with.
|
|
return certificateTrustResult;
|
|
}
|
|
|
|
OSStatus GatherEnterpriseCertsMacOS(nsTArray<EnterpriseCert>& certs,
|
|
UniqueSECMODModule& rootsModule) {
|
|
// The following builds a search dictionary corresponding to:
|
|
// { class: "certificate",
|
|
// match limit: "match all" }
|
|
// This operates on items that have been added to the keychain and thus gives
|
|
// us all 3rd party certificates. Unfortunately, if a root that shipped with
|
|
// the OS has had its trust settings changed, it can also be returned from
|
|
// this query. Further work (below) filters such certificates out.
|
|
const CFStringRef keys[] = {kSecClass, kSecMatchLimit};
|
|
const void* values[] = {kSecClassCertificate, kSecMatchLimitAll};
|
|
static_assert(std::size(keys) == std::size(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, std::size(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++) {
|
|
// Because we asked for certificates, each CFTypeRef in the array is really
|
|
// a SecCertificateRef.
|
|
const SecCertificateRef certificate =
|
|
(const SecCertificateRef)CFArrayGetValueAtIndex(arr.get(), i);
|
|
CertificateTrustResult certificateTrustResult =
|
|
GetCertificateTrustResult(certificate);
|
|
if (certificateTrustResult == CertificateTrustResult::DoNotUse) {
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("skipping distrusted cert"));
|
|
continue;
|
|
}
|
|
ScopedCFType<CFDataRef> der(SecCertificateCopyData(certificate));
|
|
if (!der) {
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
("couldn't get bytes of certificate?"));
|
|
continue;
|
|
}
|
|
bool isRoot =
|
|
certificateTrustResult == CertificateTrustResult::CanUseAsTrustAnchor;
|
|
EnterpriseCert enterpriseCert(CFDataGetBytePtr(der.get()),
|
|
CFDataGetLength(der.get()), isRoot);
|
|
if (enterpriseCert.GetIsRoot() ||
|
|
!enterpriseCert.IsKnownRoot(rootsModule)) {
|
|
certs.AppendElement(std::move(enterpriseCert));
|
|
numImported++;
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
("importing as %s", isRoot ? "root" : "intermediate"));
|
|
} else {
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
("skipping intermediate that is a known root cert"));
|
|
}
|
|
}
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("imported %u certs", numImported));
|
|
return errSecSuccess;
|
|
}
|
|
#endif // XP_MACOSX
|
|
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
void GatherEnterpriseCertsAndroid(nsTArray<EnterpriseCert>& certs,
|
|
UniqueSECMODModule& rootsModule) {
|
|
if (!jni::IsAvailable()) {
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("JNI not available"));
|
|
return;
|
|
}
|
|
jni::ObjectArray::LocalRef roots =
|
|
java::EnterpriseRoots::GatherEnterpriseRoots();
|
|
uint32_t numImported = 0;
|
|
for (size_t i = 0; i < roots->Length(); i++) {
|
|
jni::ByteArray::LocalRef root = roots->GetElement(i);
|
|
// Currently we treat all certificates gleaned from the Android
|
|
// CA store as roots.
|
|
EnterpriseCert enterpriseCert(
|
|
reinterpret_cast<uint8_t*>(root->GetElements().Elements()),
|
|
root->Length(), true);
|
|
if (enterpriseCert.GetIsRoot() ||
|
|
!enterpriseCert.IsKnownRoot(rootsModule)) {
|
|
certs.AppendElement(std::move(enterpriseCert));
|
|
numImported++;
|
|
} else {
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
("skipping intermediate that is a known root cert"));
|
|
}
|
|
}
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("imported %u certs", numImported));
|
|
}
|
|
#endif // MOZ_WIDGET_ANDROID
|
|
|
|
nsresult GatherEnterpriseCerts(nsTArray<EnterpriseCert>& certs) {
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
if (NS_IsMainThread()) {
|
|
return NS_ERROR_NOT_SAME_THREAD;
|
|
}
|
|
|
|
certs.Clear();
|
|
UniqueSECMODModule rootsModule(SECMOD_FindModule(kRootModuleName.get()));
|
|
#ifdef XP_WIN
|
|
GatherEnterpriseCertsWindows(certs, rootsModule);
|
|
#endif // XP_WIN
|
|
#ifdef XP_MACOSX
|
|
OSStatus rv = GatherEnterpriseCertsMacOS(certs, rootsModule);
|
|
if (rv != errSecSuccess) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
#endif // XP_MACOSX
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
GatherEnterpriseCertsAndroid(certs, rootsModule);
|
|
#endif // MOZ_WIDGET_ANDROID
|
|
return NS_OK;
|
|
}
|