Commit 524b141e authored by Pavol Marko's avatar Pavol Marko Committed by Commit Bot

Make interactions with KeyPermissionsManager testable

Split KeyPermissionsManager,
KeyPermissionsManager::PermissionsForExtension and
KeyPermissionsManagerUserService into a public virtual API and
implementation in *Impl classes.
This will be used in a follow-up CL to test interactions with
KeyPermissionsManager in unit tests.

Note: This CL does not change any behavior.

Bug: 1127506
Change-Id: Iacbbd6e533db0b476b2fb5d6e04e241966c7bafa
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2413248Reviewed-by: default avatarMaksim Ivanov <emaxx@chromium.org>
Reviewed-by: default avatarEdman Anjos <edman@chromium.org>
Reviewed-by: default avatarOmar Morsi <omorsi@google.com>
Commit-Queue: Edman Anjos <edman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#808017}
parent c86b3be0
...@@ -1927,6 +1927,8 @@ source_set("chromeos") { ...@@ -1927,6 +1927,8 @@ source_set("chromeos") {
"platform_keys/extension_platform_keys_service_factory.h", "platform_keys/extension_platform_keys_service_factory.h",
"platform_keys/key_permissions/key_permissions_manager.cc", "platform_keys/key_permissions/key_permissions_manager.cc",
"platform_keys/key_permissions/key_permissions_manager.h", "platform_keys/key_permissions/key_permissions_manager.h",
"platform_keys/key_permissions/key_permissions_manager_impl.cc",
"platform_keys/key_permissions/key_permissions_manager_impl.h",
"platform_keys/key_permissions/key_permissions_manager_user_service.cc", "platform_keys/key_permissions/key_permissions_manager_user_service.cc",
"platform_keys/key_permissions/key_permissions_manager_user_service.h", "platform_keys/key_permissions/key_permissions_manager_user_service.h",
"platform_keys/key_permissions/key_permissions_policy_handler.cc", "platform_keys/key_permissions/key_permissions_policy_handler.cc",
...@@ -2984,6 +2986,8 @@ static_library("test_support") { ...@@ -2984,6 +2986,8 @@ static_library("test_support") {
"login/test/test_predicate_waiter.h", "login/test/test_predicate_waiter.h",
"login/version_updater/mock_version_updater_delegate.cc", "login/version_updater/mock_version_updater_delegate.cc",
"login/version_updater/mock_version_updater_delegate.h", "login/version_updater/mock_version_updater_delegate.h",
"platform_keys/key_permissions/mock_key_permissions_manager.cc",
"platform_keys/key_permissions/mock_key_permissions_manager.h",
"platform_keys/mock_platform_keys_service.cc", "platform_keys/mock_platform_keys_service.cc",
"platform_keys/mock_platform_keys_service.h", "platform_keys/mock_platform_keys_service.h",
"plugin_vm/fake_plugin_vm_features.cc", "plugin_vm/fake_plugin_vm_features.cc",
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
#include "base/callback.h" #include "base/callback.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/memory/singleton.h" #include "base/memory/singleton.h"
#include "chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager.h" #include "chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager_impl.h"
#include "chrome/browser/chromeos/platform_keys/platform_keys_service.h" #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
#include "chrome/browser/net/nss_context.h" #include "chrome/browser/net/nss_context.h"
#include "chrome/browser/policy/profile_policy_connector.h" #include "chrome/browser/policy/profile_policy_connector.h"
...@@ -82,8 +82,8 @@ bool IsCertificateAllowed(const scoped_refptr<net::X509Certificate>& cert, ...@@ -82,8 +82,8 @@ bool IsCertificateAllowed(const scoped_refptr<net::X509Certificate>& cert,
std::string spki_der = chromeos::platform_keys::GetSubjectPublicKeyInfo(cert); std::string spki_der = chromeos::platform_keys::GetSubjectPublicKeyInfo(cert);
std::string public_key_spki_der_b64; std::string public_key_spki_der_b64;
base::Base64Encode(spki_der, &public_key_spki_der_b64); base::Base64Encode(spki_der, &public_key_spki_der_b64);
if (!chromeos::platform_keys::KeyPermissionsManager::IsCorporateKeyForProfile( if (!chromeos::platform_keys::KeyPermissionsManagerImpl::
public_key_spki_der_b64, prefs)) { IsCorporateKeyForProfile(public_key_spki_der_b64, prefs)) {
DVLOG(1) << "Certificate is not allowed to be used by ARC."; DVLOG(1) << "Certificate is not allowed to be used by ARC.";
return false; return false;
} }
...@@ -303,8 +303,9 @@ void ArcCertStoreBridge::OnCertificatesListed( ...@@ -303,8 +303,9 @@ void ArcCertStoreBridge::OnCertificatesListed(
void ArcCertStoreBridge::UpdateFromKeyPermissionsPolicy() { void ArcCertStoreBridge::UpdateFromKeyPermissionsPolicy() {
DVLOG(1) << "ArcCertStoreBridge::UpdateFromKeyPermissionsPolicy"; DVLOG(1) << "ArcCertStoreBridge::UpdateFromKeyPermissionsPolicy";
std::vector<std::string> app_ids = chromeos::platform_keys:: std::vector<std::string> app_ids =
KeyPermissionsManager::GetCorporateKeyUsageAllowedAppIds(policy_service_); chromeos::platform_keys::KeyPermissionsManagerImpl::
GetCorporateKeyUsageAllowedAppIds(policy_service_);
std::vector<std::string> permissions; std::vector<std::string> permissions;
for (const auto& app_id : app_ids) { for (const auto& app_id : app_ids) {
if (LooksLikeAndroidPackageName(app_id)) if (LooksLikeAndroidPackageName(app_id))
......
...@@ -4,518 +4,18 @@ ...@@ -4,518 +4,18 @@
#include "chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager.h" #include "chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/base64.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/values.h"
#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
#include "chrome/browser/policy/profile_policy_connector.h"
#include "chrome/common/pref_names.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/core/common/policy_namespace.h"
#include "components/policy/core/common/policy_service.h"
#include "components/policy/policy_constants.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "extensions/browser/state_store.h"
namespace chromeos { namespace chromeos {
namespace platform_keys { namespace platform_keys {
namespace { KeyPermissionsManager::PermissionsForExtension::PermissionsForExtension() =
// The key at which platform key specific data is stored in each extension's default;
// state store.
//
// From older versions of ChromeOS, this key can hold a list of base64 and
// DER-encoded SPKIs. A key can be used for signing at most once if it is part
// of that list.
//
// The current format of data that is written to the PlatformKeys field is a
// list of serialized KeyEntry objects:
// { 'SPKI': string,
// 'signOnce': bool, // if not present, defaults to false
// 'signUnlimited': bool // if not present, defaults to false
// }
//
// Do not change this constant as clients will lose their existing state.
const char kStateStorePlatformKeys[] = "PlatformKeys";
const char kStateStoreSPKI[] = "SPKI";
const char kStateStoreSignOnce[] = "signOnce";
const char kStateStoreSignUnlimited[] = "signUnlimited";
// The profile pref prefs::kPlatformKeys stores a dictionary mapping from
// public key (base64 encoding of an DER-encoded SPKI) to key properties. The
// currently only key property is the key usage, which can either be undefined
// or "corporate". If a key is not present in the pref, the default for the key
// usage is undefined, which in particular means "not for corporate usage".
// E.g. the entry in the profile pref might look like:
// "platform_keys" : {
// "ABCDEF123" : {
// "keyUsage" : "corporate"
// },
// "abcdef567" : {
// "keyUsage" : "corporate"
// }
// }
const char kPrefKeyUsage[] = "keyUsage";
const char kPrefKeyUsageCorporate[] = "corporate";
const char kPolicyAllowCorporateKeyUsage[] = "allowCorporateKeyUsage";
const base::DictionaryValue* GetPrefsEntry(
const std::string& public_key_spki_der_b64,
const PrefService* const profile_prefs) {
if (!profile_prefs)
return nullptr;
const base::DictionaryValue* platform_keys =
profile_prefs->GetDictionary(prefs::kPlatformKeys);
if (!platform_keys)
return nullptr;
const base::Value* key_entry_value =
platform_keys->FindKey(public_key_spki_der_b64);
if (!key_entry_value)
return nullptr;
const base::DictionaryValue* key_entry = nullptr;
key_entry_value->GetAsDictionary(&key_entry);
return key_entry;
}
const base::DictionaryValue* GetKeyPermissionsMap(
policy::PolicyService* const profile_policies) {
if (!profile_policies)
return nullptr;
const policy::PolicyMap& policies = profile_policies->GetPolicies(
policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, std::string()));
const base::Value* policy_value =
policies.GetValue(policy::key::kKeyPermissions);
if (!policy_value) {
DVLOG(1) << "KeyPermissions policy is not set";
return nullptr;
}
const base::DictionaryValue* key_permissions_map = nullptr;
policy_value->GetAsDictionary(&key_permissions_map);
return key_permissions_map;
}
bool GetCorporateKeyUsageFromPref(
const base::DictionaryValue* key_permissions_for_ext) {
if (!key_permissions_for_ext)
return false;
const base::Value* allow_corporate_key_usage =
key_permissions_for_ext->FindKey(kPolicyAllowCorporateKeyUsage);
if (!allow_corporate_key_usage || !allow_corporate_key_usage->is_bool())
return false;
return allow_corporate_key_usage->GetBool();
}
// Returns true if the extension with id |extension_id| is allowed to use
// corporate usage keys by policy in |profile_policies|.
bool PolicyAllowsCorporateKeyUsageForExtension(
const std::string& extension_id,
policy::PolicyService* const profile_policies) {
if (!profile_policies)
return false;
const base::DictionaryValue* key_permissions_map =
GetKeyPermissionsMap(profile_policies);
if (!key_permissions_map)
return false;
const base::Value* key_permissions_for_ext_value =
key_permissions_map->FindKey(extension_id);
const base::DictionaryValue* key_permissions_for_ext = nullptr;
if (!key_permissions_for_ext_value ||
!key_permissions_for_ext_value->GetAsDictionary(
&key_permissions_for_ext) ||
!key_permissions_for_ext)
return false;
bool allow_corporate_key_usage =
GetCorporateKeyUsageFromPref(key_permissions_for_ext);
VLOG_IF(allow_corporate_key_usage, 2)
<< "Policy allows usage of corporate keys by extension " << extension_id;
return allow_corporate_key_usage;
}
bool IsKeyOnUserSlot(const std::vector<TokenId>& key_locations) {
return base::Contains(key_locations, TokenId::kUser);
}
} // namespace
struct KeyPermissionsManager::PermissionsForExtension::KeyEntry {
explicit KeyEntry(const std::string& public_key_spki_der_b64)
: spki_b64(public_key_spki_der_b64) {}
// The base64-encoded DER of a X.509 Subject Public Key Info.
std::string spki_b64;
// True if the key can be used once for singing.
// This permission is granted if an extension generated a key using the
// enterprise.platformKeys API, so that it can build a certification request.
// After the first signing operation this permission will be revoked.
bool sign_once = false;
// True if the key can be used for signing an unlimited number of times.
// This permission is granted by the user to allow the extension to use the
// key for signing through the enterprise.platformKeys or platformKeys API.
// This permission is granted until revoked by the user or the policy.
bool sign_unlimited = false;
};
KeyPermissionsManager::PermissionsForExtension::PermissionsForExtension(
const std::string& extension_id,
std::unique_ptr<base::Value> state_store_value,
PrefService* profile_prefs,
policy::PolicyService* profile_policies,
KeyPermissionsManager* key_permissions)
: extension_id_(extension_id),
profile_prefs_(profile_prefs),
profile_policies_(profile_policies),
key_permissions_(key_permissions) {
DCHECK(profile_prefs_);
DCHECK(profile_policies_);
DCHECK(key_permissions_);
if (state_store_value)
KeyEntriesFromState(*state_store_value);
}
KeyPermissionsManager::PermissionsForExtension::~PermissionsForExtension() {}
bool KeyPermissionsManager::PermissionsForExtension::CanUseKeyForSigning(
const std::string& public_key_spki_der,
const std::vector<TokenId>& key_locations) {
if (key_locations.empty())
return false;
std::string public_key_spki_der_b64;
base::Base64Encode(public_key_spki_der, &public_key_spki_der_b64);
KeyEntry* matching_entry = GetStateStoreEntry(public_key_spki_der_b64);
// In any case, we allow the generating extension to use the generated key a
// single time for signing arbitrary data. The reason is, that the extension
// usually has to sign a certification request containing the public key in
// order to obtain a certificate for the key.
// That means, once a certificate authority generated a certificate for the
// key, the generating extension doesn't have access to the key anymore,
// except if explicitly permitted by the administrator.
if (matching_entry->sign_once)
return true;
// Usage of corporate keys is solely determined by policy. The user must not
// circumvent this decision.
if (key_permissions_->IsCorporateKey(public_key_spki_der_b64,
key_locations)) {
return PolicyAllowsCorporateKeyUsage();
}
// Only permissions for keys that are not designated for corporate usage are
// determined by user decisions.
return matching_entry->sign_unlimited;
}
void KeyPermissionsManager::PermissionsForExtension::SetKeyUsedForSigning(
const std::string& public_key_spki_der,
const std::vector<TokenId>& key_locations) {
if (key_locations.empty())
return;
std::string public_key_spki_der_b64;
base::Base64Encode(public_key_spki_der, &public_key_spki_der_b64);
KeyEntry* matching_entry = GetStateStoreEntry(public_key_spki_der_b64);
if (!matching_entry->sign_once) {
if (!CanUseKeyForSigning(public_key_spki_der, key_locations))
LOG(ERROR) << "Key was not allowed for signing.";
return;
}
matching_entry->sign_once = false;
WriteToStateStore();
}
void KeyPermissionsManager::PermissionsForExtension::
RegisterKeyForCorporateUsage(const std::string& public_key_spki_der,
const std::vector<TokenId>& key_locations) {
if (key_locations.empty()) {
NOTREACHED();
return;
}
std::string public_key_spki_der_b64;
base::Base64Encode(public_key_spki_der, &public_key_spki_der_b64);
KeyEntry* matching_entry = GetStateStoreEntry(public_key_spki_der_b64);
if (matching_entry->sign_once) {
VLOG(1) << "Key is already allowed for signing, skipping.";
return;
}
matching_entry->sign_once = true;
WriteToStateStore();
// Only register the key as corporate in the profile prefs if it is on the
// user slot. Keys on the system slot are implicitly corporate. We have still
// stored the sign_once permission, so the enrolling extension in the same
// profile can use the key for signing once in order to build a CSR even if it
// doesn't have permission to use corporate keys.
if (!IsKeyOnUserSlot(key_locations))
return;
DictionaryPrefUpdate update(profile_prefs_, prefs::kPlatformKeys);
std::unique_ptr<base::DictionaryValue> new_pref_entry(
new base::DictionaryValue);
new_pref_entry->SetKey(kPrefKeyUsage, base::Value(kPrefKeyUsageCorporate));
update->SetWithoutPathExpansion(public_key_spki_der_b64,
std::move(new_pref_entry));
}
void KeyPermissionsManager::PermissionsForExtension::SetUserGrantedPermission(
const std::string& public_key_spki_der,
const std::vector<TokenId>& key_locations) {
if (!key_permissions_->CanUserGrantPermissionFor(public_key_spki_der,
key_locations)) {
LOG(WARNING) << "Tried to grant permission for a key although prohibited "
"(either key is a corporate key or this account is "
"managed).";
return;
}
// It only makes sense to store the sign_unlimited flag for a key if it is on
// a user slot. Currently, system-slot keys are implicitly corporate, so
// CanUserGrantPermissionForKey should return false for them.
DCHECK(IsKeyOnUserSlot(key_locations));
std::string public_key_spki_der_b64;
base::Base64Encode(public_key_spki_der, &public_key_spki_der_b64);
KeyEntry* matching_entry = GetStateStoreEntry(public_key_spki_der_b64);
if (matching_entry->sign_unlimited) {
VLOG(1) << "Key is already allowed for signing, skipping.";
return;
}
matching_entry->sign_unlimited = true;
WriteToStateStore();
}
bool KeyPermissionsManager::PermissionsForExtension::
PolicyAllowsCorporateKeyUsage() const {
return PolicyAllowsCorporateKeyUsageForExtension(extension_id_,
profile_policies_);
}
void KeyPermissionsManager::PermissionsForExtension::WriteToStateStore() {
key_permissions_->SetPlatformKeysOfExtension(extension_id_,
KeyEntriesToState());
}
void KeyPermissionsManager::PermissionsForExtension::KeyEntriesFromState(
const base::Value& state) {
state_store_entries_.clear();
const base::ListValue* entries = nullptr;
if (!state.GetAsList(&entries)) {
LOG(ERROR) << "Found a state store of wrong type.";
return;
}
for (const auto& entry : *entries) {
std::string spki_b64;
const base::DictionaryValue* dict_entry = nullptr;
if (entry.GetAsString(&spki_b64)) {
// This handles the case that the store contained a plain list of base64
// and DER-encoded SPKIs from an older version of ChromeOS.
KeyEntry new_entry(spki_b64);
new_entry.sign_once = true;
state_store_entries_.push_back(new_entry);
} else if (entry.GetAsDictionary(&dict_entry)) {
dict_entry->GetStringWithoutPathExpansion(kStateStoreSPKI, &spki_b64);
KeyEntry new_entry(spki_b64);
dict_entry->GetBooleanWithoutPathExpansion(kStateStoreSignOnce,
&new_entry.sign_once);
dict_entry->GetBooleanWithoutPathExpansion(kStateStoreSignUnlimited,
&new_entry.sign_unlimited);
state_store_entries_.push_back(new_entry);
} else {
LOG(ERROR) << "Found invalid entry of type " << entry.type()
<< " in PlatformKeys state store.";
continue;
}
}
}
std::unique_ptr<base::Value>
KeyPermissionsManager::PermissionsForExtension::KeyEntriesToState() {
std::unique_ptr<base::ListValue> new_state(new base::ListValue);
for (const KeyEntry& entry : state_store_entries_) {
// Drop entries that the extension doesn't have any permissions for anymore.
if (!entry.sign_once && !entry.sign_unlimited)
continue;
std::unique_ptr<base::DictionaryValue> new_entry(new base::DictionaryValue);
new_entry->SetKey(kStateStoreSPKI, base::Value(entry.spki_b64));
// Omit writing default values, namely |false|.
if (entry.sign_once) {
new_entry->SetKey(kStateStoreSignOnce, base::Value(entry.sign_once));
}
if (entry.sign_unlimited) {
new_entry->SetKey(kStateStoreSignUnlimited,
base::Value(entry.sign_unlimited));
}
new_state->Append(std::move(new_entry));
}
return std::move(new_state);
}
KeyPermissionsManager::PermissionsForExtension::KeyEntry*
KeyPermissionsManager::PermissionsForExtension::GetStateStoreEntry(
const std::string& public_key_spki_der_b64) {
for (KeyEntry& entry : state_store_entries_) {
// For every ASN.1 value there is exactly one DER encoding, so it is fine to
// compare the DER (or its base64 encoding).
if (entry.spki_b64 == public_key_spki_der_b64)
return &entry;
}
state_store_entries_.push_back(KeyEntry(public_key_spki_der_b64));
return &state_store_entries_.back();
}
KeyPermissionsManager::KeyPermissionsManager(
bool profile_is_managed,
PrefService* profile_prefs,
policy::PolicyService* profile_policies,
extensions::StateStore* extensions_state_store)
: profile_is_managed_(profile_is_managed),
profile_prefs_(profile_prefs),
profile_policies_(profile_policies),
extensions_state_store_(extensions_state_store) {
DCHECK(profile_prefs_);
DCHECK(extensions_state_store_);
DCHECK(!profile_is_managed_ || profile_policies_);
}
KeyPermissionsManager::~KeyPermissionsManager() {}
void KeyPermissionsManager::GetPermissionsForExtension(
const std::string& extension_id,
const PermissionsCallback& callback) {
extensions_state_store_->GetExtensionValue(
extension_id, kStateStorePlatformKeys,
base::BindOnce(
&KeyPermissionsManager::CreatePermissionObjectAndPassToCallback,
weak_factory_.GetWeakPtr(), extension_id, callback));
}
bool KeyPermissionsManager::CanUserGrantPermissionFor(
const std::string& public_key_spki_der,
const std::vector<TokenId>& key_locations) const {
if (key_locations.empty())
return false;
// As keys cannot be tagged for non-corporate usage, the user can currently
// not grant any permissions if the profile is managed.
if (profile_is_managed_)
return false;
std::string public_key_spki_der_b64;
base::Base64Encode(public_key_spki_der, &public_key_spki_der_b64);
// If this profile is not managed but we find a corporate key, don't allow
// the user to grant permissions.
return !IsCorporateKey(public_key_spki_der_b64, key_locations);
}
// static
bool KeyPermissionsManager::IsCorporateKeyForProfile(
const std::string& public_key_spki_der_b64,
const PrefService* const profile_prefs) {
const base::DictionaryValue* prefs_entry =
GetPrefsEntry(public_key_spki_der_b64, profile_prefs);
if (prefs_entry) {
const base::Value* key_usage = prefs_entry->FindKey(kPrefKeyUsage);
if (!key_usage || !key_usage->is_string())
return false;
return key_usage->GetString() == kPrefKeyUsageCorporate;
}
return false;
}
// static
std::vector<std::string>
KeyPermissionsManager::GetCorporateKeyUsageAllowedAppIds(
policy::PolicyService* const profile_policies) {
std::vector<std::string> permissions;
const base::DictionaryValue* key_permissions_map =
GetKeyPermissionsMap(profile_policies);
if (!key_permissions_map)
return permissions;
for (const auto& item : key_permissions_map->DictItems()) {
const auto& app_id = item.first;
const auto& key_permission = item.second;
const base::DictionaryValue* key_permissions_for_app = nullptr;
if (!key_permission.GetAsDictionary(&key_permissions_for_app) ||
!key_permissions_for_app) {
continue;
}
if (GetCorporateKeyUsageFromPref(key_permissions_for_app))
permissions.push_back(app_id);
}
return permissions;
}
bool KeyPermissionsManager::IsCorporateKey( KeyPermissionsManager::PermissionsForExtension::~PermissionsForExtension() =
const std::string& public_key_spki_der_b64, default;
const std::vector<TokenId>& key_locations) const {
for (const auto key_location : key_locations) {
switch (key_location) {
case TokenId::kUser:
if (IsCorporateKeyForProfile(public_key_spki_der_b64, profile_prefs_))
return true;
break;
case TokenId::kSystem:
return true;
default:
NOTREACHED();
}
}
return false;
}
void KeyPermissionsManager::CreatePermissionObjectAndPassToCallback( KeyPermissionsManager::KeyPermissionsManager() = default;
const std::string& extension_id,
const PermissionsCallback& callback,
std::unique_ptr<base::Value> value) {
callback.Run(std::make_unique<PermissionsForExtension>(
extension_id, std::move(value), profile_prefs_, profile_policies_, this));
}
void KeyPermissionsManager::SetPlatformKeysOfExtension( KeyPermissionsManager::~KeyPermissionsManager() = default;
const std::string& extension_id,
std::unique_ptr<base::Value> value) {
extensions_state_store_->SetExtensionValue(
extension_id, kStateStorePlatformKeys, std::move(value));
}
} // namespace platform_keys } // namespace platform_keys
} // namespace chromeos } // namespace chromeos
...@@ -10,24 +10,8 @@ ...@@ -10,24 +10,8 @@
#include <vector> #include <vector>
#include "base/callback_forward.h" #include "base/callback_forward.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/chromeos/platform_keys/platform_keys.h" #include "chrome/browser/chromeos/platform_keys/platform_keys.h"
class PrefService;
namespace base {
class Value;
}
namespace extensions {
class StateStore;
}
namespace policy {
class PolicyService;
}
namespace chromeos { namespace chromeos {
namespace platform_keys { namespace platform_keys {
...@@ -77,41 +61,32 @@ class KeyPermissionsManager { ...@@ -77,41 +61,32 @@ class KeyPermissionsManager {
// specific extension. // specific extension.
class PermissionsForExtension { class PermissionsForExtension {
public: public:
// |key_permissions| must not be null and outlive this object. PermissionsForExtension();
// Methods of this object refer implicitly to the extension with the id virtual ~PermissionsForExtension();
// |extension_id|. Don't use this constructor directly. Call
// |KeyPermissionsManager::GetPermissionsForExtension| instead.
PermissionsForExtension(const std::string& extension_id,
std::unique_ptr<base::Value> state_store_value,
PrefService* profile_prefs,
policy::PolicyService* profile_policies,
KeyPermissionsManager* key_permissions);
~PermissionsForExtension();
// Returns true if the private key matching |public_key_spki_der| can be // Returns true if the private key matching |public_key_spki_der| can be
// used for signing by the extension with id |extension_id_|. // used for signing by the extension with id |extension_id_|.
// |key_locations| must describe locations available to the user the private // |key_locations| must describe locations available to the user the private
// key is stored on. // key is stored on.
bool CanUseKeyForSigning( virtual bool CanUseKeyForSigning(
const std::string& public_key_spki_der, const std::string& public_key_spki_der,
const std::vector<platform_keys::TokenId>& key_locations); const std::vector<platform_keys::TokenId>& key_locations) = 0;
// Registers the private key matching |public_key_spki_der| as being // Registers the private key matching |public_key_spki_der| as being
// generated by the extension with id |extension_id| and marks it for // generated by the extension with id |extension_id| and marks it for
// corporate usage. |key_locations| must describe locations available to the // corporate usage. |key_locations| must describe locations available to the
// user the private key is stored on. // user the private key is stored on.
void RegisterKeyForCorporateUsage( virtual void RegisterKeyForCorporateUsage(
const std::string& public_key_spki_der, const std::string& public_key_spki_der,
const std::vector<platform_keys::TokenId>& key_locations); const std::vector<platform_keys::TokenId>& key_locations) = 0;
// Sets the user granted permission that the extension with id // Sets the user granted permission that the extension with id
// |extension_id| can use the private key matching |public_key_spki_der| for // |extension_id| can use the private key matching |public_key_spki_der| for
// signing. |key_locations| must describe locations available to the user // signing. |key_locations| must describe locations available to the user
// the private key is stored on. // the private key is stored on.
void SetUserGrantedPermission( virtual void SetUserGrantedPermission(
const std::string& public_key_spki_der, const std::string& public_key_spki_der,
const std::vector<platform_keys::TokenId>& key_locations); const std::vector<platform_keys::TokenId>& key_locations) = 0;
// Must be called when the extension with id |extension_id| used the private // Must be called when the extension with id |extension_id| used the private
// key matching |public_key_spki_der| for signing. |key_locations| must // key matching |public_key_spki_der| for signing. |key_locations| must
...@@ -119,56 +94,13 @@ class KeyPermissionsManager { ...@@ -119,56 +94,13 @@ class KeyPermissionsManager {
// Updates the permissions accordingly. E.g. if this extension generated // Updates the permissions accordingly. E.g. if this extension generated
// the key and no other permission was granted then the permission to sign // the key and no other permission was granted then the permission to sign
// with this key is removed. // with this key is removed.
void SetKeyUsedForSigning( virtual void SetKeyUsedForSigning(
const std::string& public_key_spki_der, const std::string& public_key_spki_der,
const std::vector<platform_keys::TokenId>& key_locations); const std::vector<platform_keys::TokenId>& key_locations) = 0;
private:
struct KeyEntry;
// Writes the current |state_store_entries_| to the state store of
// |extension_id_|.
void WriteToStateStore();
// Reads a KeyEntry list from |state| and stores them in
// |state_store_entries_|.
void KeyEntriesFromState(const base::Value& state);
// Converts |state_store_entries_| to a base::Value for storing in the state
// store.
std::unique_ptr<base::Value> KeyEntriesToState();
// Returns an existing entry for |public_key_spki_der_b64| from
// |state_store_entries_|. If there is no existing entry, creates, adds and
// returns a new entry.
// |public_key_spki_der| must be the base64 encoding of the DER of a Subject
// Public Key Info.
KeyPermissionsManager::PermissionsForExtension::KeyEntry*
GetStateStoreEntry(const std::string& public_key_spki_der_b64);
bool PolicyAllowsCorporateKeyUsage() const;
const std::string extension_id_;
std::vector<KeyEntry> state_store_entries_;
PrefService* const profile_prefs_;
policy::PolicyService* const profile_policies_;
KeyPermissionsManager* const key_permissions_;
DISALLOW_COPY_AND_ASSIGN(PermissionsForExtension);
}; };
// |profile_prefs| and |extensions_state_store| must not be null and must KeyPermissionsManager();
// outlive this object. virtual ~KeyPermissionsManager();
// If |profile_is_managed| is false, |profile_policies| is ignored. Otherwise,
// |profile_policies| must not be null and must outlive this object.
// |profile_is_managed| determines the default usage and permissions for
// keys without explicitly assigned usage.
KeyPermissionsManager(bool profile_is_managed,
PrefService* profile_prefs,
policy::PolicyService* profile_policies,
extensions::StateStore* extensions_state_store);
~KeyPermissionsManager();
using PermissionsCallback = using PermissionsCallback =
base::Callback<void(std::unique_ptr<PermissionsForExtension>)>; base::Callback<void(std::unique_ptr<PermissionsForExtension>)>;
...@@ -176,49 +108,16 @@ class KeyPermissionsManager { ...@@ -176,49 +108,16 @@ class KeyPermissionsManager {
// Passes an object managing the key permissions of the extension with id // Passes an object managing the key permissions of the extension with id
// |extension_id| to |callback|. This can happen synchronously or // |extension_id| to |callback|. This can happen synchronously or
// asynchronously. // asynchronously.
void GetPermissionsForExtension(const std::string& extension_id, virtual void GetPermissionsForExtension(
const PermissionsCallback& callback); const std::string& extension_id,
const PermissionsCallback& callback) = 0;
// Returns true if the user can grant any permission for // Returns true if the user can grant any permission for
// |public_key_spki_derey_id| to extensions. |key_locations| must describe // |public_key_spki_derey_id| to extensions. |key_locations| must describe
// locations available to the user the private key is stored on. // locations available to the user the private key is stored on.
bool CanUserGrantPermissionFor( virtual bool CanUserGrantPermissionFor(
const std::string& public_key_spki_der, const std::string& public_key_spki_der,
const std::vector<platform_keys::TokenId>& key_locations) const; const std::vector<platform_keys::TokenId>& key_locations) const = 0;
// Returns true if |public_key_spki_der_b64| is a corporate usage key.
static bool IsCorporateKeyForProfile(
const std::string& public_key_spki_der_b64,
const PrefService* const profile_prefs);
// Returns the list of apps and extensions ids allowed to use corporate usage
// keys by policy in |profile_policies|.
static std::vector<std::string> GetCorporateKeyUsageAllowedAppIds(
policy::PolicyService* const profile_policies);
private:
bool IsCorporateKey(
const std::string& public_key_spki_der_b64,
const std::vector<platform_keys::TokenId>& key_locations) const;
// Creates a PermissionsForExtension object from |extension_id| and |value|
// and passes the object to |callback|.
void CreatePermissionObjectAndPassToCallback(
const std::string& extension_id,
const PermissionsCallback& callback,
std::unique_ptr<base::Value> value);
// Writes |value| to the state store of the extension with id |extension_id|.
void SetPlatformKeysOfExtension(const std::string& extension_id,
std::unique_ptr<base::Value> value);
const bool profile_is_managed_;
PrefService* const profile_prefs_;
policy::PolicyService* const profile_policies_;
extensions::StateStore* const extensions_state_store_;
base::WeakPtrFactory<KeyPermissionsManager> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(KeyPermissionsManager);
}; };
} // namespace platform_keys } // namespace platform_keys
......
// Copyright 2020 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/platform_keys/key_permissions/key_permissions_manager_impl.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/base64.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/values.h"
#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
#include "chrome/browser/policy/profile_policy_connector.h"
#include "chrome/common/pref_names.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/core/common/policy_namespace.h"
#include "components/policy/core/common/policy_service.h"
#include "components/policy/policy_constants.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "extensions/browser/state_store.h"
namespace chromeos {
namespace platform_keys {
namespace {
// The key at which platform key specific data is stored in each extension's
// state store.
//
// From older versions of ChromeOS, this key can hold a list of base64 and
// DER-encoded SPKIs. A key can be used for signing at most once if it is part
// of that list.
//
// The current format of data that is written to the PlatformKeys field is a
// list of serialized KeyEntry objects:
// { 'SPKI': string,
// 'signOnce': bool, // if not present, defaults to false
// 'signUnlimited': bool // if not present, defaults to false
// }
//
// Do not change this constant as clients will lose their existing state.
const char kStateStorePlatformKeys[] = "PlatformKeys";
const char kStateStoreSPKI[] = "SPKI";
const char kStateStoreSignOnce[] = "signOnce";
const char kStateStoreSignUnlimited[] = "signUnlimited";
// The profile pref prefs::kPlatformKeys stores a dictionary mapping from
// public key (base64 encoding of an DER-encoded SPKI) to key properties. The
// currently only key property is the key usage, which can either be undefined
// or "corporate". If a key is not present in the pref, the default for the key
// usage is undefined, which in particular means "not for corporate usage".
// E.g. the entry in the profile pref might look like:
// "platform_keys" : {
// "ABCDEF123" : {
// "keyUsage" : "corporate"
// },
// "abcdef567" : {
// "keyUsage" : "corporate"
// }
// }
const char kPrefKeyUsage[] = "keyUsage";
const char kPrefKeyUsageCorporate[] = "corporate";
const char kPolicyAllowCorporateKeyUsage[] = "allowCorporateKeyUsage";
const base::DictionaryValue* GetPrefsEntry(
const std::string& public_key_spki_der_b64,
const PrefService* const profile_prefs) {
if (!profile_prefs)
return nullptr;
const base::DictionaryValue* platform_keys =
profile_prefs->GetDictionary(prefs::kPlatformKeys);
if (!platform_keys)
return nullptr;
const base::Value* key_entry_value =
platform_keys->FindKey(public_key_spki_der_b64);
if (!key_entry_value)
return nullptr;
const base::DictionaryValue* key_entry = nullptr;
key_entry_value->GetAsDictionary(&key_entry);
return key_entry;
}
const base::DictionaryValue* GetKeyPermissionsMap(
policy::PolicyService* const profile_policies) {
if (!profile_policies)
return nullptr;
const policy::PolicyMap& policies = profile_policies->GetPolicies(
policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, std::string()));
const base::Value* policy_value =
policies.GetValue(policy::key::kKeyPermissions);
if (!policy_value) {
DVLOG(1) << "KeyPermissions policy is not set";
return nullptr;
}
const base::DictionaryValue* key_permissions_map = nullptr;
policy_value->GetAsDictionary(&key_permissions_map);
return key_permissions_map;
}
bool GetCorporateKeyUsageFromPref(
const base::DictionaryValue* key_permissions_for_ext) {
if (!key_permissions_for_ext)
return false;
const base::Value* allow_corporate_key_usage =
key_permissions_for_ext->FindKey(kPolicyAllowCorporateKeyUsage);
if (!allow_corporate_key_usage || !allow_corporate_key_usage->is_bool())
return false;
return allow_corporate_key_usage->GetBool();
}
// Returns true if the extension with id |extension_id| is allowed to use
// corporate usage keys by policy in |profile_policies|.
bool PolicyAllowsCorporateKeyUsageForExtension(
const std::string& extension_id,
policy::PolicyService* const profile_policies) {
if (!profile_policies)
return false;
const base::DictionaryValue* key_permissions_map =
GetKeyPermissionsMap(profile_policies);
if (!key_permissions_map)
return false;
const base::Value* key_permissions_for_ext_value =
key_permissions_map->FindKey(extension_id);
const base::DictionaryValue* key_permissions_for_ext = nullptr;
if (!key_permissions_for_ext_value ||
!key_permissions_for_ext_value->GetAsDictionary(
&key_permissions_for_ext) ||
!key_permissions_for_ext)
return false;
bool allow_corporate_key_usage =
GetCorporateKeyUsageFromPref(key_permissions_for_ext);
VLOG_IF(allow_corporate_key_usage, 2)
<< "Policy allows usage of corporate keys by extension " << extension_id;
return allow_corporate_key_usage;
}
bool IsKeyOnUserSlot(const std::vector<TokenId>& key_locations) {
return base::Contains(key_locations, TokenId::kUser);
}
} // namespace
struct KeyPermissionsManagerImpl::PermissionsForExtensionImpl::KeyEntry {
explicit KeyEntry(const std::string& public_key_spki_der_b64)
: spki_b64(public_key_spki_der_b64) {}
// The base64-encoded DER of a X.509 Subject Public Key Info.
std::string spki_b64;
// True if the key can be used once for singing.
// This permission is granted if an extension generated a key using the
// enterprise.platformKeys API, so that it can build a certification request.
// After the first signing operation this permission will be revoked.
bool sign_once = false;
// True if the key can be used for signing an unlimited number of times.
// This permission is granted by the user to allow the extension to use the
// key for signing through the enterprise.platformKeys or platformKeys API.
// This permission is granted until revoked by the user or the policy.
bool sign_unlimited = false;
};
KeyPermissionsManagerImpl::PermissionsForExtensionImpl::
PermissionsForExtensionImpl(const std::string& extension_id,
std::unique_ptr<base::Value> state_store_value,
PrefService* profile_prefs,
policy::PolicyService* profile_policies,
KeyPermissionsManagerImpl* key_permissions)
: extension_id_(extension_id),
profile_prefs_(profile_prefs),
profile_policies_(profile_policies),
key_permissions_(key_permissions) {
DCHECK(profile_prefs_);
DCHECK(profile_policies_);
DCHECK(key_permissions_);
if (state_store_value)
KeyEntriesFromState(*state_store_value);
}
KeyPermissionsManagerImpl::PermissionsForExtensionImpl::
~PermissionsForExtensionImpl() {}
bool KeyPermissionsManagerImpl::PermissionsForExtensionImpl::
CanUseKeyForSigning(const std::string& public_key_spki_der,
const std::vector<TokenId>& key_locations) {
if (key_locations.empty())
return false;
std::string public_key_spki_der_b64;
base::Base64Encode(public_key_spki_der, &public_key_spki_der_b64);
KeyEntry* matching_entry = GetStateStoreEntry(public_key_spki_der_b64);
// In any case, we allow the generating extension to use the generated key a
// single time for signing arbitrary data. The reason is, that the extension
// usually has to sign a certification request containing the public key in
// order to obtain a certificate for the key.
// That means, once a certificate authority generated a certificate for the
// key, the generating extension doesn't have access to the key anymore,
// except if explicitly permitted by the administrator.
if (matching_entry->sign_once)
return true;
// Usage of corporate keys is solely determined by policy. The user must not
// circumvent this decision.
if (key_permissions_->IsCorporateKey(public_key_spki_der_b64,
key_locations)) {
return PolicyAllowsCorporateKeyUsage();
}
// Only permissions for keys that are not designated for corporate usage are
// determined by user decisions.
return matching_entry->sign_unlimited;
}
void KeyPermissionsManagerImpl::PermissionsForExtensionImpl::
SetKeyUsedForSigning(const std::string& public_key_spki_der,
const std::vector<TokenId>& key_locations) {
if (key_locations.empty())
return;
std::string public_key_spki_der_b64;
base::Base64Encode(public_key_spki_der, &public_key_spki_der_b64);
KeyEntry* matching_entry = GetStateStoreEntry(public_key_spki_der_b64);
if (!matching_entry->sign_once) {
if (!CanUseKeyForSigning(public_key_spki_der, key_locations))
LOG(ERROR) << "Key was not allowed for signing.";
return;
}
matching_entry->sign_once = false;
WriteToStateStore();
}
void KeyPermissionsManagerImpl::PermissionsForExtensionImpl::
RegisterKeyForCorporateUsage(const std::string& public_key_spki_der,
const std::vector<TokenId>& key_locations) {
if (key_locations.empty()) {
NOTREACHED();
return;
}
std::string public_key_spki_der_b64;
base::Base64Encode(public_key_spki_der, &public_key_spki_der_b64);
KeyEntry* matching_entry = GetStateStoreEntry(public_key_spki_der_b64);
if (matching_entry->sign_once) {
VLOG(1) << "Key is already allowed for signing, skipping.";
return;
}
matching_entry->sign_once = true;
WriteToStateStore();
// Only register the key as corporate in the profile prefs if it is on the
// user slot. Keys on the system slot are implicitly corporate. We have still
// stored the sign_once permission, so the enrolling extension in the same
// profile can use the key for signing once in order to build a CSR even if it
// doesn't have permission to use corporate keys.
if (!IsKeyOnUserSlot(key_locations))
return;
DictionaryPrefUpdate update(profile_prefs_, prefs::kPlatformKeys);
std::unique_ptr<base::DictionaryValue> new_pref_entry(
new base::DictionaryValue);
new_pref_entry->SetKey(kPrefKeyUsage, base::Value(kPrefKeyUsageCorporate));
update->SetWithoutPathExpansion(public_key_spki_der_b64,
std::move(new_pref_entry));
}
void KeyPermissionsManagerImpl::PermissionsForExtensionImpl::
SetUserGrantedPermission(const std::string& public_key_spki_der,
const std::vector<TokenId>& key_locations) {
if (!key_permissions_->CanUserGrantPermissionFor(public_key_spki_der,
key_locations)) {
LOG(WARNING) << "Tried to grant permission for a key although prohibited "
"(either key is a corporate key or this account is "
"managed).";
return;
}
// It only makes sense to store the sign_unlimited flag for a key if it is on
// a user slot. Currently, system-slot keys are implicitly corporate, so
// CanUserGrantPermissionForKey should return false for them.
DCHECK(IsKeyOnUserSlot(key_locations));
std::string public_key_spki_der_b64;
base::Base64Encode(public_key_spki_der, &public_key_spki_der_b64);
KeyEntry* matching_entry = GetStateStoreEntry(public_key_spki_der_b64);
if (matching_entry->sign_unlimited) {
VLOG(1) << "Key is already allowed for signing, skipping.";
return;
}
matching_entry->sign_unlimited = true;
WriteToStateStore();
}
bool KeyPermissionsManagerImpl::PermissionsForExtensionImpl::
PolicyAllowsCorporateKeyUsage() const {
return PolicyAllowsCorporateKeyUsageForExtension(extension_id_,
profile_policies_);
}
void KeyPermissionsManagerImpl::PermissionsForExtensionImpl::
WriteToStateStore() {
key_permissions_->SetPlatformKeysOfExtension(extension_id_,
KeyEntriesToState());
}
void KeyPermissionsManagerImpl::PermissionsForExtensionImpl::
KeyEntriesFromState(const base::Value& state) {
state_store_entries_.clear();
const base::ListValue* entries = nullptr;
if (!state.GetAsList(&entries)) {
LOG(ERROR) << "Found a state store of wrong type.";
return;
}
for (const auto& entry : *entries) {
std::string spki_b64;
const base::DictionaryValue* dict_entry = nullptr;
if (entry.GetAsString(&spki_b64)) {
// This handles the case that the store contained a plain list of base64
// and DER-encoded SPKIs from an older version of ChromeOS.
KeyEntry new_entry(spki_b64);
new_entry.sign_once = true;
state_store_entries_.push_back(new_entry);
} else if (entry.GetAsDictionary(&dict_entry)) {
dict_entry->GetStringWithoutPathExpansion(kStateStoreSPKI, &spki_b64);
KeyEntry new_entry(spki_b64);
dict_entry->GetBooleanWithoutPathExpansion(kStateStoreSignOnce,
&new_entry.sign_once);
dict_entry->GetBooleanWithoutPathExpansion(kStateStoreSignUnlimited,
&new_entry.sign_unlimited);
state_store_entries_.push_back(new_entry);
} else {
LOG(ERROR) << "Found invalid entry of type " << entry.type()
<< " in PlatformKeys state store.";
continue;
}
}
}
std::unique_ptr<base::Value>
KeyPermissionsManagerImpl::PermissionsForExtensionImpl::KeyEntriesToState() {
std::unique_ptr<base::ListValue> new_state(new base::ListValue);
for (const KeyEntry& entry : state_store_entries_) {
// Drop entries that the extension doesn't have any permissions for anymore.
if (!entry.sign_once && !entry.sign_unlimited)
continue;
std::unique_ptr<base::DictionaryValue> new_entry(new base::DictionaryValue);
new_entry->SetKey(kStateStoreSPKI, base::Value(entry.spki_b64));
// Omit writing default values, namely |false|.
if (entry.sign_once) {
new_entry->SetKey(kStateStoreSignOnce, base::Value(entry.sign_once));
}
if (entry.sign_unlimited) {
new_entry->SetKey(kStateStoreSignUnlimited,
base::Value(entry.sign_unlimited));
}
new_state->Append(std::move(new_entry));
}
return std::move(new_state);
}
KeyPermissionsManagerImpl::PermissionsForExtensionImpl::KeyEntry*
KeyPermissionsManagerImpl::PermissionsForExtensionImpl::GetStateStoreEntry(
const std::string& public_key_spki_der_b64) {
for (KeyEntry& entry : state_store_entries_) {
// For every ASN.1 value there is exactly one DER encoding, so it is fine to
// compare the DER (or its base64 encoding).
if (entry.spki_b64 == public_key_spki_der_b64)
return &entry;
}
state_store_entries_.push_back(KeyEntry(public_key_spki_der_b64));
return &state_store_entries_.back();
}
KeyPermissionsManagerImpl::KeyPermissionsManagerImpl(
bool profile_is_managed,
PrefService* profile_prefs,
policy::PolicyService* profile_policies,
extensions::StateStore* extensions_state_store)
: profile_is_managed_(profile_is_managed),
profile_prefs_(profile_prefs),
profile_policies_(profile_policies),
extensions_state_store_(extensions_state_store) {
DCHECK(profile_prefs_);
DCHECK(extensions_state_store_);
DCHECK(!profile_is_managed_ || profile_policies_);
}
KeyPermissionsManagerImpl::~KeyPermissionsManagerImpl() {}
void KeyPermissionsManagerImpl::GetPermissionsForExtension(
const std::string& extension_id,
const PermissionsCallback& callback) {
extensions_state_store_->GetExtensionValue(
extension_id, kStateStorePlatformKeys,
base::BindOnce(
&KeyPermissionsManagerImpl::CreatePermissionObjectAndPassToCallback,
weak_factory_.GetWeakPtr(), extension_id, callback));
}
bool KeyPermissionsManagerImpl::CanUserGrantPermissionFor(
const std::string& public_key_spki_der,
const std::vector<TokenId>& key_locations) const {
if (key_locations.empty())
return false;
// As keys cannot be tagged for non-corporate usage, the user can currently
// not grant any permissions if the profile is managed.
if (profile_is_managed_)
return false;
std::string public_key_spki_der_b64;
base::Base64Encode(public_key_spki_der, &public_key_spki_der_b64);
// If this profile is not managed but we find a corporate key, don't allow
// the user to grant permissions.
return !IsCorporateKey(public_key_spki_der_b64, key_locations);
}
// static
bool KeyPermissionsManagerImpl::IsCorporateKeyForProfile(
const std::string& public_key_spki_der_b64,
const PrefService* const profile_prefs) {
const base::DictionaryValue* prefs_entry =
GetPrefsEntry(public_key_spki_der_b64, profile_prefs);
if (prefs_entry) {
const base::Value* key_usage = prefs_entry->FindKey(kPrefKeyUsage);
if (!key_usage || !key_usage->is_string())
return false;
return key_usage->GetString() == kPrefKeyUsageCorporate;
}
return false;
}
// static
std::vector<std::string>
KeyPermissionsManagerImpl::GetCorporateKeyUsageAllowedAppIds(
policy::PolicyService* const profile_policies) {
std::vector<std::string> permissions;
const base::DictionaryValue* key_permissions_map =
GetKeyPermissionsMap(profile_policies);
if (!key_permissions_map)
return permissions;
for (const auto& item : key_permissions_map->DictItems()) {
const auto& app_id = item.first;
const auto& key_permission = item.second;
const base::DictionaryValue* key_permissions_for_app = nullptr;
if (!key_permission.GetAsDictionary(&key_permissions_for_app) ||
!key_permissions_for_app) {
continue;
}
if (GetCorporateKeyUsageFromPref(key_permissions_for_app))
permissions.push_back(app_id);
}
return permissions;
}
bool KeyPermissionsManagerImpl::IsCorporateKey(
const std::string& public_key_spki_der_b64,
const std::vector<TokenId>& key_locations) const {
for (const auto key_location : key_locations) {
switch (key_location) {
case TokenId::kUser:
LOG(ERROR) << " user";
if (IsCorporateKeyForProfile(public_key_spki_der_b64, profile_prefs_))
return true;
break;
case TokenId::kSystem:
return true;
default:
NOTREACHED();
}
}
return false;
}
void KeyPermissionsManagerImpl::CreatePermissionObjectAndPassToCallback(
const std::string& extension_id,
const PermissionsCallback& callback,
std::unique_ptr<base::Value> value) {
callback.Run(std::make_unique<PermissionsForExtensionImpl>(
extension_id, std::move(value), profile_prefs_, profile_policies_, this));
}
void KeyPermissionsManagerImpl::SetPlatformKeysOfExtension(
const std::string& extension_id,
std::unique_ptr<base::Value> value) {
extensions_state_store_->SetExtensionValue(
extension_id, kStateStorePlatformKeys, std::move(value));
}
} // namespace platform_keys
} // namespace chromeos
// Copyright 2020 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.
#ifndef CHROME_BROWSER_CHROMEOS_PLATFORM_KEYS_KEY_PERMISSIONS_KEY_PERMISSIONS_MANAGER_IMPL_H_
#define CHROME_BROWSER_CHROMEOS_PLATFORM_KEYS_KEY_PERMISSIONS_KEY_PERMISSIONS_MANAGER_IMPL_H_
#include <memory>
#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager.h"
#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
class PrefService;
namespace base {
class Value;
}
namespace extensions {
class StateStore;
}
namespace policy {
class PolicyService;
}
namespace chromeos {
namespace platform_keys {
class KeyPermissionsManagerImpl : public KeyPermissionsManager {
public:
// Implementation of PermissionsForExtension.
class PermissionsForExtensionImpl : public PermissionsForExtension {
public:
// |key_permissions| must not be null and outlive this object.
// Methods of this object refer implicitly to the extension with the id
// |extension_id|. Don't use this constructor directly. Call
// |KeyPermissionsManager::GetPermissionsForExtension| instead.
PermissionsForExtensionImpl(const std::string& extension_id,
std::unique_ptr<base::Value> state_store_value,
PrefService* profile_prefs,
policy::PolicyService* profile_policies,
KeyPermissionsManagerImpl* key_permissions);
PermissionsForExtensionImpl(const PermissionsForExtensionImpl& other) =
delete;
PermissionsForExtensionImpl& operator=(
const PermissionsForExtensionImpl& other) = delete;
~PermissionsForExtensionImpl() override;
bool CanUseKeyForSigning(
const std::string& public_key_spki_der,
const std::vector<platform_keys::TokenId>& key_locations) override;
void RegisterKeyForCorporateUsage(
const std::string& public_key_spki_der,
const std::vector<platform_keys::TokenId>& key_locations) override;
void SetUserGrantedPermission(
const std::string& public_key_spki_der,
const std::vector<platform_keys::TokenId>& key_locations) override;
void SetKeyUsedForSigning(
const std::string& public_key_spki_der,
const std::vector<platform_keys::TokenId>& key_locations) override;
private:
struct KeyEntry;
// Writes the current |state_store_entries_| to the state store of
// |extension_id_|.
void WriteToStateStore();
// Reads a KeyEntry list from |state| and stores them in
// |state_store_entries_|.
void KeyEntriesFromState(const base::Value& state);
// Converts |state_store_entries_| to a base::Value for storing in the state
// store.
std::unique_ptr<base::Value> KeyEntriesToState();
// Returns an existing entry for |public_key_spki_der_b64| from
// |state_store_entries_|. If there is no existing entry, creates, adds and
// returns a new entry.
// |public_key_spki_der| must be the base64 encoding of the DER of a Subject
// Public Key Info.
KeyPermissionsManagerImpl::PermissionsForExtensionImpl::KeyEntry*
GetStateStoreEntry(const std::string& public_key_spki_der_b64);
bool PolicyAllowsCorporateKeyUsage() const;
const std::string extension_id_;
std::vector<KeyEntry> state_store_entries_;
PrefService* const profile_prefs_;
policy::PolicyService* const profile_policies_;
KeyPermissionsManagerImpl* const key_permissions_;
};
// |profile_prefs| and |extensions_state_store| must not be null and must
// outlive this object.
// If |profile_is_managed| is false, |profile_policies| is ignored. Otherwise,
// |profile_policies| must not be null and must outlive this object.
// |profile_is_managed| determines the default usage and permissions for
// keys without explicitly assigned usage.
KeyPermissionsManagerImpl(bool profile_is_managed,
PrefService* profile_prefs,
policy::PolicyService* profile_policies,
extensions::StateStore* extensions_state_store);
~KeyPermissionsManagerImpl() override;
KeyPermissionsManagerImpl(const KeyPermissionsManagerImpl& other) = delete;
KeyPermissionsManagerImpl& operator=(const KeyPermissionsManagerImpl& other) =
delete;
void GetPermissionsForExtension(const std::string& extension_id,
const PermissionsCallback& callback) override;
bool CanUserGrantPermissionFor(
const std::string& public_key_spki_der,
const std::vector<platform_keys::TokenId>& key_locations) const override;
// Returns true if |public_key_spki_der_b64| is a corporate usage key.
static bool IsCorporateKeyForProfile(
const std::string& public_key_spki_der_b64,
const PrefService* const profile_prefs);
// Returns the list of apps and extensions ids allowed to use corporate usage
// keys by policy in |profile_policies|.
static std::vector<std::string> GetCorporateKeyUsageAllowedAppIds(
policy::PolicyService* const profile_policies);
private:
bool IsCorporateKey(
const std::string& public_key_spki_der_b64,
const std::vector<platform_keys::TokenId>& key_locations) const;
// Creates a PermissionsForExtension object from |extension_id| and |value|
// and passes the object to |callback|.
void CreatePermissionObjectAndPassToCallback(
const std::string& extension_id,
const PermissionsCallback& callback,
std::unique_ptr<base::Value> value);
// Writes |value| to the state store of the extension with id |extension_id|.
void SetPlatformKeysOfExtension(const std::string& extension_id,
std::unique_ptr<base::Value> value);
const bool profile_is_managed_;
PrefService* const profile_prefs_;
policy::PolicyService* const profile_policies_;
extensions::StateStore* const extensions_state_store_;
base::WeakPtrFactory<KeyPermissionsManagerImpl> weak_factory_{this};
};
} // namespace platform_keys
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_PLATFORM_KEYS_KEY_PERMISSIONS_KEY_PERMISSIONS_MANAGER_IMPL_H_
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
#include "chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager_user_service.h" #include "chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager_user_service.h"
#include "chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager.h" #include "chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager_impl.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/extensions/extension_system_factory.h" #include "chrome/browser/extensions/extension_system_factory.h"
#include "chrome/browser/policy/profile_policy_connector.h" #include "chrome/browser/policy/profile_policy_connector.h"
...@@ -19,15 +19,31 @@ namespace platform_keys { ...@@ -19,15 +19,31 @@ namespace platform_keys {
// ==================== KeyPermissionsManagerUserService ======================= // ==================== KeyPermissionsManagerUserService =======================
KeyPermissionsManagerUserService::KeyPermissionsManagerUserService( KeyPermissionsManagerUserService::KeyPermissionsManagerUserService() = default;
Profile* profile)
KeyPermissionsManagerUserService::~KeyPermissionsManagerUserService() = default;
// ================== KeyPermissionsManagerUserServiceImpl =====================
class KeyPermissionsManagerUserServiceImpl
: public KeyPermissionsManagerUserService {
public:
explicit KeyPermissionsManagerUserServiceImpl(Profile* profile)
: key_permissions_manager_( : key_permissions_manager_(
profile->GetProfilePolicyConnector()->IsManaged(), profile->GetProfilePolicyConnector()->IsManaged(),
profile->GetPrefs(), profile->GetPrefs(),
profile->GetProfilePolicyConnector()->policy_service(), profile->GetProfilePolicyConnector()->policy_service(),
extensions::ExtensionSystem::Get(profile)->state_store()) {} extensions::ExtensionSystem::Get(profile)->state_store()) {}
KeyPermissionsManagerUserService::~KeyPermissionsManagerUserService() = default; ~KeyPermissionsManagerUserServiceImpl() override = default;
KeyPermissionsManager* key_permissions_manager() override {
return &key_permissions_manager_;
}
private:
KeyPermissionsManagerImpl key_permissions_manager_;
};
// ================== KeyPermissionsManagerUserServiceFactory ================== // ================== KeyPermissionsManagerUserServiceFactory ==================
...@@ -61,7 +77,7 @@ KeyedService* KeyPermissionsManagerUserServiceFactory::BuildServiceInstanceFor( ...@@ -61,7 +77,7 @@ KeyedService* KeyPermissionsManagerUserServiceFactory::BuildServiceInstanceFor(
return nullptr; return nullptr;
} }
return new KeyPermissionsManagerUserService(profile); return new KeyPermissionsManagerUserServiceImpl(profile);
} }
void KeyPermissionsManagerUserServiceFactory::RegisterProfilePrefs( void KeyPermissionsManagerUserServiceFactory::RegisterProfilePrefs(
......
...@@ -10,8 +10,6 @@ ...@@ -10,8 +10,6 @@
#include "components/keyed_service/content/browser_context_keyed_service_factory.h" #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
#include "components/keyed_service/core/keyed_service.h" #include "components/keyed_service/core/keyed_service.h"
class Profile;
namespace user_prefs { namespace user_prefs {
class PrefRegistrySyncable; class PrefRegistrySyncable;
} }
...@@ -24,15 +22,10 @@ namespace platform_keys { ...@@ -24,15 +22,10 @@ namespace platform_keys {
// so as future work can introduce a global device-wide KPM instance. // so as future work can introduce a global device-wide KPM instance.
class KeyPermissionsManagerUserService : public KeyedService { class KeyPermissionsManagerUserService : public KeyedService {
public: public:
explicit KeyPermissionsManagerUserService(Profile* profile); KeyPermissionsManagerUserService();
~KeyPermissionsManagerUserService() override; ~KeyPermissionsManagerUserService() override;
KeyPermissionsManager* key_permissions_manager() { virtual KeyPermissionsManager* key_permissions_manager() = 0;
return &key_permissions_manager_;
}
private:
KeyPermissionsManager key_permissions_manager_;
}; };
class KeyPermissionsManagerUserServiceFactory class KeyPermissionsManagerUserServiceFactory
......
// Copyright 2020 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/platform_keys/key_permissions/mock_key_permissions_manager.h"
namespace chromeos {
namespace platform_keys {
MockKeyPermissionsManager::MockKeyPermissionsManager() = default;
MockKeyPermissionsManager::~MockKeyPermissionsManager() = default;
} // namespace platform_keys
} // namespace chromeos
// Copyright 2020 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.
#ifndef CHROME_BROWSER_CHROMEOS_PLATFORM_KEYS_KEY_PERMISSIONS_MOCK_KEY_PERMISSIONS_MANAGER_H_
#define CHROME_BROWSER_CHROMEOS_PLATFORM_KEYS_KEY_PERMISSIONS_MOCK_KEY_PERMISSIONS_MANAGER_H_
#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager.h"
#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace chromeos {
namespace platform_keys {
class MockKeyPermissionsManager : public KeyPermissionsManager {
public:
MockKeyPermissionsManager();
MockKeyPermissionsManager(const MockKeyPermissionsManager&) = delete;
MockKeyPermissionsManager& operator=(const MockKeyPermissionsManager&) =
delete;
~MockKeyPermissionsManager() override;
MOCK_METHOD(void,
GetPermissionsForExtension,
(const std::string& extension_id,
const PermissionsCallback& callback),
(override));
MOCK_METHOD(bool,
CanUserGrantPermissionFor,
(const std::string& public_key_spki_der,
const std::vector<platform_keys::TokenId>& key_locations),
(const override));
MOCK_METHOD(bool,
IsCorporateKey,
(const std::string& public_key_spki_der_b64,
const std::vector<platform_keys::TokenId>& key_locations),
(const override));
MOCK_METHOD(void,
SetCorporateKey,
(const std::string& public_key_spki_der_b64,
platform_keys::TokenId key_location),
(const override));
};
} // namespace platform_keys
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_PLATFORM_KEYS_KEY_PERMISSIONS_MOCK_KEY_PERMISSIONS_MANAGER_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment