blob: 6aa50c033d7b5e6eba190c63da9c689ac7374c5d [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/policy/policy_cert_service.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/task/post_task.h"
#include "chrome/browser/chromeos/policy/policy_cert_service_factory.h"
#include "chrome/browser/chromeos/policy/user_network_configuration_updater.h"
#include "chrome/browser/chromeos/policy/user_network_configuration_updater_factory.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/net/profile_network_context_service.h"
#include "chrome/browser/net/profile_network_context_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chromeos/network/onc/certificate_scope.h"
#include "chromeos/network/policy_certificate_provider.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/storage_partition.h"
#include "extensions/browser/extension_util.h"
#include "net/cert/x509_certificate.h"
#include "services/network/nss_temp_certs_cache_chromeos.h"
#include "url/gurl.h"
namespace policy {
PolicyCertService::~PolicyCertService() {
if (policy_certificate_provider_)
policy_certificate_provider_->RemovePolicyProvidedCertsObserver(this);
}
PolicyCertService::PolicyCertService(
Profile* profile,
chromeos::PolicyCertificateProvider* policy_certificate_provider,
bool may_use_profile_wide_trust_anchors,
const std::string& user_id,
user_manager::UserManager* user_manager)
: profile_(profile),
policy_certificate_provider_(policy_certificate_provider),
may_use_profile_wide_trust_anchors_(may_use_profile_wide_trust_anchors),
user_id_(user_id),
user_manager_(user_manager) {
DCHECK(policy_certificate_provider_);
// Only allow using profile-wide trust anchors if a user is associated with
// this Profile. Profiles without a user would be e.g. the sign-in screen
// Profile which are not supposed to have profile-wide trust anchors.
CHECK((user_manager_ && !user_id_.empty()) ||
!may_use_profile_wide_trust_anchors_);
policy_certificate_provider_->AddPolicyProvidedCertsObserver(this);
profile_wide_all_server_and_authority_certs_ =
policy_certificate_provider_->GetAllServerAndAuthorityCertificates(
chromeos::onc::CertificateScope::Default());
profile_wide_trust_anchors_ = GetAllowedProfileWideTrustAnchors();
}
PolicyCertService::PolicyCertService(const std::string& user_id,
user_manager::UserManager* user_manager)
: profile_(nullptr),
policy_certificate_provider_(nullptr),
may_use_profile_wide_trust_anchors_(true),
user_id_(user_id),
user_manager_(user_manager) {}
void PolicyCertService::OnPolicyProvidedCertsChanged() {
profile_wide_all_server_and_authority_certs_ =
policy_certificate_provider_->GetAllServerAndAuthorityCertificates(
chromeos::onc::CertificateScope::Default());
profile_wide_trust_anchors_ = GetAllowedProfileWideTrustAnchors();
// Make all policy-provided server and authority certificates available to NSS
// as temp certificates.
// While the network service is out-of-process so it is not affected by this,
// this is important for client certificate selection which still happens in
// the browser process.
// Note that this is done on the UI thread because the assumption is that NSS
// has already been initialized by Chrome OS specific start-up code in chrome,
// expecting that the operation of creating in-memory NSS certs is cheap in
// that case.
temp_policy_provided_certs_ =
std::make_unique<network::NSSTempCertsCacheChromeOS>(
profile_wide_all_server_and_authority_certs_);
auto* profile_network_context =
ProfileNetworkContextServiceFactory::GetForContext(profile_);
profile_network_context->UpdateAdditionalCertificates();
}
void PolicyCertService::GetPolicyCertificatesForStoragePartition(
const base::FilePath& partition_path,
net::CertificateList* out_all_server_and_authority_certificates,
net::CertificateList* out_trust_anchors) const {
*out_all_server_and_authority_certificates =
profile_wide_all_server_and_authority_certs_;
*out_trust_anchors = profile_wide_trust_anchors_;
if (policy_certificate_provider_->GetExtensionIdsWithPolicyCertificates()
.empty()) {
return;
}
// The following code adds policy-provided extension specific certificates.
// Policy can specify these keyed by extension ID.
// In general, there is no direct mapping from a StoragePartition path to an
// extension ID, because extensions could be using the default
// StoragePartition of the Profile.
// However, for extensions with isolated storage, the extension will be in a
// StoragePartition that is exclusively used by this extension.
// Policy-provided extension specific certificates are thus only allowed for
// extensions with isolated storage.
// The following code checks those preconditions and attempts to find the
// extension ID (among extensions IDs with policy-provided certificates) that
// corresponds to |partition_path|.
base::FilePath default_storage_partition_path =
content::BrowserContext::GetDefaultStoragePartition(profile_)->GetPath();
// Among the extension IDs that have policy-provided certificates, attempt to
// find the extension ID which corresponds to |partition_path|.
// This is done by iterating the extension IDs because there's no trivial
// conversion from |partition_path| to extension ID as explained above.
std::string current_extension_id_with_policy_certificates;
std::set<std::string> extension_ids_with_policy_certificates =
policy_certificate_provider_->GetExtensionIdsWithPolicyCertificates();
for (const auto& extension_id : extension_ids_with_policy_certificates) {
// Only allow policy-provided certificates for extensions with isolated
// storage. Also sanity-check that it's not the default partition.
content::StoragePartition* extension_partition =
extensions::util::GetStoragePartitionForExtensionId(
extension_id, profile_,
/*can_create=*/false);
if (!extension_partition)
continue;
if (!extensions::util::HasIsolatedStorage(extension_id, profile_) ||
extension_partition->GetPath() == default_storage_partition_path) {
LOG(ERROR) << "Ignoring policy certificates for " << extension_id
<< " because it does not have isolated storage";
continue;
}
if (partition_path == extension_partition->GetPath()) {
current_extension_id_with_policy_certificates = extension_id;
break;
}
}
if (current_extension_id_with_policy_certificates.empty())
return;
net::CertificateList extension_all_server_and_authority_certificates =
policy_certificate_provider_->GetAllServerAndAuthorityCertificates(
chromeos::onc::CertificateScope::ForExtension(
current_extension_id_with_policy_certificates));
out_all_server_and_authority_certificates->insert(
out_all_server_and_authority_certificates->end(),
extension_all_server_and_authority_certificates.begin(),
extension_all_server_and_authority_certificates.end());
net::CertificateList extension_trust_anchors =
policy_certificate_provider_->GetWebTrustedCertificates(
chromeos::onc::CertificateScope::ForExtension(
current_extension_id_with_policy_certificates));
out_trust_anchors->insert(out_trust_anchors->end(),
extension_trust_anchors.begin(),
extension_trust_anchors.end());
}
bool PolicyCertService::UsedPolicyCertificates() const {
// PolicyCertService is only tracking if policy-provided certificates have
// been used for profiles that are associated with a user.
if (user_id_.empty())
return false;
return PolicyCertServiceFactory::UsedPolicyCertificates(user_id_);
}
net::CertificateList PolicyCertService::GetAllowedProfileWideTrustAnchors() {
if (!may_use_profile_wide_trust_anchors_)
return {};
net::CertificateList trust_anchors =
policy_certificate_provider_->GetWebTrustedCertificates(
chromeos::onc::CertificateScope::Default());
// Do not use certificates installed via ONC policy if the current session has
// multiple profiles. This is important to make sure that any possibly tainted
// data is absolutely confined to the managed profile and never, ever leaks to
// any other profile.
if (!trust_anchors.empty() && user_manager_ &&
user_manager_->GetLoggedInUsers().size() > 1u) {
LOG(ERROR) << "Ignoring ONC-pushed certificates update because multiple "
<< "users are logged in.";
return {};
}
return trust_anchors;
}
// static
std::unique_ptr<PolicyCertService> PolicyCertService::CreateForTesting(
const std::string& user_id,
user_manager::UserManager* user_manager) {
return base::WrapUnique(new PolicyCertService(user_id, user_manager));
}
void PolicyCertService::SetPolicyTrustAnchorsForTesting(
const net::CertificateList& trust_anchors) {
// Only allow this call in an instance that has been created through
// PolicyCertService::CreateForTesting.
CHECK_EQ(nullptr, profile_);
CHECK_EQ(nullptr, policy_certificate_provider_);
profile_wide_all_server_and_authority_certs_ = trust_anchors;
profile_wide_trust_anchors_ = trust_anchors;
}
} // namespace policy