Chromium Code Reviews| Index: chrome/browser/extensions/api/enterprise_enterprise_key_private/enterprise_enterprise_key_private_api.cc |
| diff --git a/chrome/browser/extensions/api/enterprise_enterprise_key_private/enterprise_enterprise_key_private_api.cc b/chrome/browser/extensions/api/enterprise_enterprise_key_private/enterprise_enterprise_key_private_api.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..68d4fa3f79a568f6c41c0ac06acb3c430e79569d |
| --- /dev/null |
| +++ b/chrome/browser/extensions/api/enterprise_enterprise_key_private/enterprise_enterprise_key_private_api.cc |
| @@ -0,0 +1,367 @@ |
| +// Copyright (c) 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/extensions/api/enterprise_enterprise_key_private/enterprise_enterprise_key_private_api.h" |
| + |
| +#include <string> |
|
Mattias Nissler (ping if slow)
2013/04/04 12:51:34
newline
davidyu
2013/04/09 09:30:08
Done.
|
| +#include "base/base64.h" |
| +#include "base/compiler_specific.h" |
| +#include "base/message_loop.h" |
| +#include "base/prefs/pref_service.h" |
| +#include "base/values.h" |
| +#include "chrome/browser/browser_process.h" |
| +#include "chrome/browser/chromeos/cros/cros_library.h" |
| +#include "chrome/browser/chromeos/cros/cryptohome_library.h" |
| +#include "chrome/browser/chromeos/policy/enterprise_install_attributes.h" |
| +#include "chrome/browser/chromeos/settings/cros_settings.h" |
| +#include "chrome/browser/chromeos/settings/cros_settings_names.h" |
| +#include "chrome/browser/infobars/infobar_service.h" |
| +#include "chrome/browser/policy/browser_policy_connector.h" |
| +#include "chrome/browser/profiles/profile.h" |
| +#include "chrome/browser/signin/signin_manager.h" |
| +#include "chrome/browser/signin/signin_manager_factory.h" |
| +#include "chrome/common/extensions/api/enterprise_enterprise_key_private.h" |
| +#include "chrome/common/pref_names.h" |
| +#include "chromeos/cryptohome/async_method_caller.h" |
| +#include "chromeos/dbus/cryptohome_client.h" |
| +#include "chromeos/dbus/dbus_method_call_status.h" |
| +#include "chromeos/dbus/dbus_thread_manager.h" |
| +#include "components/user_prefs/pref_registry_syncable.h" |
| +#include "third_party/cros_system_api/dbus/service_constants.h" |
| + |
| +namespace extensions { |
| + |
| +namespace api_eekp = api::enterprise_enterprise_key_private; |
| + |
| +// Base |
| + |
| +const char* EEKPChallengeKeyBase::kKeyName = "attest-ent-default"; |
| + |
| +EEKPChallengeKeyBase::EEKPChallengeKeyBase() |
| + : cryptohome_client_( |
| + chromeos::DBusThreadManager::Get()->GetCryptohomeClient()), |
| + async_caller_(cryptohome::AsyncMethodCaller::GetInstance()) { |
| + chromeos::CrosLibrary* cros_library = chromeos::CrosLibrary::Get(); |
| + chromeos::CryptohomeLibrary* cryptohome = |
| + cros_library->GetCryptohomeLibrary(); |
| + install_attributes_ = new policy::EnterpriseInstallAttributes(cryptohome); |
|
Mattias Nissler (ping if slow)
2013/04/04 12:51:34
Please use the instance that you can retrieve from
davidyu
2013/04/09 09:30:08
Done.
|
| +} |
| + |
| +EEKPChallengeKeyBase::~EEKPChallengeKeyBase() { |
| + delete install_attributes_; |
|
Mattias Nissler (ping if slow)
2013/04/04 12:51:34
This should have been wrapped in a scoped_ptr, but
davidyu
2013/04/09 09:30:08
Done.
|
| +} |
| + |
| +bool EEKPChallengeKeyBase::IsRemoteAttestationEnabledForDevice() const { |
| + chromeos::CrosSettings* settings = chromeos::CrosSettings::Get(); |
| + bool value; |
| + return settings->GetBoolean(chromeos::kDeviceAttestationEnabled, &value) && |
|
Mattias Nissler (ping if slow)
2013/04/04 12:51:34
This should probably retrieve the trusted value, s
davidyu
2013/04/09 09:30:08
Done.
|
| + value; |
| +} |
| + |
| +bool EEKPChallengeKeyBase::IsEnterpriseDevice() const { |
| + return install_attributes_->IsEnterpriseDevice(); |
| +} |
| + |
| +std::string EEKPChallengeKeyBase::GetEnterpriseDomain() const { |
| + return install_attributes_->GetDomain(); |
| +} |
| + |
| +std::string EEKPChallengeKeyBase::GetDeviceId() const { |
| + return install_attributes_->GetDeviceId(); |
| +} |
| + |
| +// ChallengeMachineKey() |
| + |
| +EEKPChallengeMachineKey::EEKPChallengeMachineKey() |
| + : ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { |
| +} |
| + |
| +EEKPChallengeMachineKey::~EEKPChallengeMachineKey() { |
| +} |
| + |
| +bool EEKPChallengeMachineKey::RunImpl() { |
| + scoped_ptr<api_eekp::ChallengeMachineKey::Params> |
| + params(api_eekp::ChallengeMachineKey::Params::Create(*args_)); |
| + EXTENSION_FUNCTION_VALIDATE(params.get()); |
| + |
| + std::string challenge; |
| + if (!base::Base64Decode(params->challenge, &challenge)) { |
| + SetError("Challenge is not base64 encoded."); |
| + SendResponse(false); |
| + return false; |
| + } |
| + |
| + // Check if the device is enterprise enrolled. |
| + if (!IsEnterpriseDevice()) { |
| + SetError("The device is not enterprise enrolled."); |
| + SendResponse(false); |
| + return false; |
| + } |
| + |
| + // Check if RA is enabled in the device policy. |
| + if (!IsRemoteAttestationEnabledForDevice()) { |
| + SetError("Remote attestation is not enabled for your device."); |
| + SendResponse(false); |
| + return false; |
| + } |
| + |
| + LOG(ERROR) << "About to call TpmAttestationSignEnterpriseChallenge()"; |
|
Mattias Nissler (ping if slow)
2013/04/04 12:51:34
DVLOG would be more appropriate.
davidyu
2013/04/09 09:30:08
Removed.
|
| + async_caller_->TpmAttestationSignEnterpriseChallenge( |
| + chromeos::CryptohomeClient::DEVICE_KEY, |
| + kKeyName, |
| + GetEnterpriseDomain(), |
| + GetDeviceId(), |
| + challenge, |
| + base::Bind(&EEKPChallengeMachineKey::SignChallengeCallback, |
| + weak_factory_.GetWeakPtr())); |
| + |
| + AddRef(); |
| + return true; |
| +} |
| + |
| +void EEKPChallengeMachineKey::SignChallengeCallback( |
| + bool success, const std::string& response) { |
| + LOG(ERROR) << "TpmAttestationSignEnterpriseChallenge() returned"; |
| + if (!success) { |
| + SetError("Challenge failed."); |
| + SendResponse(false); |
| + Release(); |
| + return; |
| + } |
| + |
| + std::string encoded_response; |
| + if (!base::Base64Encode(response, &encoded_response)) { |
| + SetError("Response cannot be encoded in base64."); |
| + SendResponse(false); |
| + Release(); |
| + return; |
| + } |
| + |
| + results_ = api_eekp::ChallengeMachineKey::Results::Create(encoded_response); |
| + SendResponse(true); |
| + Release(); |
| +} |
| + |
| +// ChallengeUserKey() |
|
Mattias Nissler (ping if slow)
2013/04/04 12:51:34
huh?
davidyu
2013/04/09 09:30:08
I just want to say that the methods below are for
|
| + |
| +EEKPChallengeUserKey::EEKPChallengeUserKey() |
| + : ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { |
| +} |
| + |
| +EEKPChallengeUserKey::~EEKPChallengeUserKey() { |
| +} |
| + |
| +void EEKPChallengeUserKey::RegisterUserPrefs(PrefRegistrySyncable* registry) { |
| + registry->RegisterBooleanPref(prefs::kAttestationEnabled, false, |
| + PrefRegistrySyncable::UNSYNCABLE_PREF); |
| + registry->RegisterListPref(prefs::kAttestationExtensionWhitelist, |
| + PrefRegistrySyncable::UNSYNCABLE_PREF); |
| +} |
| + |
| +bool EEKPChallengeUserKey::RunImpl() { |
| + scoped_ptr<api_eekp::ChallengeUserKey::Params> params( |
| + api_eekp::ChallengeUserKey::Params::Create(*args_)); |
| + EXTENSION_FUNCTION_VALIDATE(params.get()); |
| + |
| + std::string challenge; |
| + if (!base::Base64Decode(params->challenge, &challenge)) { |
| + SetError("Challenge is not base64 encoded."); |
| + SendResponse(false); |
| + return false; |
| + } |
| + |
| + // Check if RA is enabled in the user policy. |
| + if (!IsRemoteAttestationEnabledForUser()) { |
| + SetError("Remote attestation is not enabled for your account."); |
| + SendResponse(false); |
| + return false; |
| + } |
| + |
| + // Check if the extension is whitelisted in the user policy. |
| + if (!IsExtensionWhitelisted()) { |
| + SetError("The extension does not have permission to call this function."); |
| + SendResponse(false); |
| + return false; |
| + } |
| + |
| + std::string user_domain = GetUserDomain(); |
| + |
| + if (IsEnterpriseDevice()) { |
| + // Check if RA is enabled in the device policy. |
| + if (!IsRemoteAttestationEnabledForDevice()) { |
| + SetError("Remote attestation is not enabled for your device."); |
| + SendResponse(false); |
| + return false; |
| + } |
| + |
| + // Check if the user domain is the same as the enrolled enterprise domain. |
| + std::string enterprise_domain = GetEnterpriseDomain(); |
| + if (user_domain != GetEnterpriseDomain()) { |
|
Mattias Nissler (ping if slow)
2013/04/04 12:51:34
You could use BrowserPolicyConnector::GetUserAffil
davidyu
2013/04/09 09:30:08
I could. However, for personal devices, we still n
Mattias Nissler (ping if slow)
2013/04/10 16:53:25
Wait, this code only runs for enterprise devices,
davidyu
2013/04/11 07:04:21
Right. For this branch, it runs for enterprise dev
|
| + SetError("User domain " + user_domain + " and Enterprise domain " + |
| + enterprise_domain + " don't match"); |
| + SendResponse(false); |
| + return false; |
| + } |
| + |
| + // If remote attestation is enabled at the device level, we don't need to |
| + // ask for user consent. |
| + MessageLoop::current()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&EEKPChallengeUserKey::UserConsentCallback, |
| + weak_factory_.GetWeakPtr(), |
| + params->challenge, params->register_key, user_domain, true)); |
| + } else { |
| + // If this is a personal device, we should explicitly ask the user before |
| + // we use the key. |
| + AskForUserConsent(base::Bind(&EEKPChallengeUserKey::UserConsentCallback, |
| + weak_factory_.GetWeakPtr(), |
| + challenge, params->register_key, user_domain)); |
| + } |
| + |
| + AddRef(); |
| + return true; |
| +} |
| + |
| +void EEKPChallengeUserKey::UserConsentCallback(const std::string& challenge, |
| + bool register_key, |
| + const std::string& domain, |
| + bool action) { |
| + if (!action) { |
| + SetError("User rejects the action."); |
| + SendResponse(false); |
| + Release(); |
| + return; |
| + } |
| + |
| + async_caller_->TpmAttestationSignEnterpriseChallenge( |
| + chromeos::CryptohomeClient::USER_KEY, |
| + kKeyName, |
| + domain, |
| + GetDeviceId(), |
| + challenge, |
| + base::Bind(&EEKPChallengeUserKey::SignChallengeCallback, |
| + weak_factory_.GetWeakPtr(), |
| + register_key)); |
| +} |
| + |
| +void EEKPChallengeUserKey::SignChallengeCallback(bool register_key, |
| + bool success, |
| + const std::string& response) { |
| + if (!success) { |
| + SetError("Challenge failed."); |
| + SendResponse(false); |
| + Release(); |
| + return; |
| + } |
| + |
| + if (register_key) { |
| + cryptohome_client_->TpmAttestationGetPublicKey( |
| + chromeos::CryptohomeClient::USER_KEY, |
| + kKeyName, |
| + base::Bind(&EEKPChallengeUserKey::GetPublicKeyCallback, |
| + weak_factory_.GetWeakPtr(), |
| + response)); |
| + } else { |
| + MessageLoop::current()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&EEKPChallengeUserKey::RegisterKeyCallback, |
| + weak_factory_.GetWeakPtr(), |
| + response, "", true, cryptohome::MOUNT_ERROR_NONE)); |
| + } |
| +} |
| + |
| +void EEKPChallengeUserKey::GetPublicKeyCallback( |
| + const std::string& response, |
| + chromeos::DBusMethodCallStatus call_status, |
| + bool result, |
| + const std::string& public_key) { |
| + if (call_status != chromeos::DBUS_METHOD_CALL_SUCCESS || !result) { |
| + SetError("Cannot get public key."); |
| + SendResponse(false); |
| + Release(); |
| + return; |
| + } |
| + |
| + async_caller_->TpmAttestationRegisterKey( |
| + chromeos::CryptohomeClient::USER_KEY, |
| + kKeyName, |
| + base::Bind(&EEKPChallengeUserKey::RegisterKeyCallback, |
| + weak_factory_.GetWeakPtr(), |
| + response, public_key)); |
| +} |
| + |
| +void EEKPChallengeUserKey::RegisterKeyCallback( |
| + const std::string& response, |
| + const std::string& public_key, |
| + bool success, |
| + cryptohome::MountError return_code) { |
| + if (!success || return_code != cryptohome::MOUNT_ERROR_NONE) { |
| + SetError("Key registration failed."); |
| + SendResponse(false); |
| + Release(); |
| + return; |
| + } |
| + |
| + std::string encoded_response, encoded_public_key; |
| + if (!base::Base64Encode(response, &encoded_response)) { |
| + SetError("Response cannot be encoded in base64."); |
| + SendResponse(false); |
| + Release(); |
| + return; |
| + } |
| + |
| + if (!base::Base64Encode(public_key, &encoded_public_key)) { |
| + SetError("Public key cannot be encoded in base64."); |
| + SendResponse(false); |
| + Release(); |
| + return; |
| + } |
| + |
| + results_ = api_eekp::ChallengeUserKey::Results::Create( |
| + encoded_response, encoded_public_key); |
| + SendResponse(true); |
| + Release(); |
| +} |
| + |
| +void EEKPChallengeUserKey::AskForUserConsent( |
| + const base::Callback<void(bool)>& callback) { |
| + // TODO(davidyu): right now we just simply reject the request before we have |
|
Mattias Nissler (ping if slow)
2013/04/04 12:51:34
That'll make testing rather hard?
davidyu
2013/04/09 09:30:08
I'll fix this once we have a conclusion about how
|
| + // a way to ask for user consent. |
| + MessageLoop::current()->PostTask(FROM_HERE, base::Bind(callback, false)); |
| +} |
| + |
| +bool EEKPChallengeUserKey::IsExtensionWhitelisted() const { |
| + const base::ListValue* list = |
| + profile()->GetPrefs()->GetList(prefs::kAttestationExtensionWhitelist); |
| + std::string id; |
| + for (int i = 0; i < static_cast<int>(list->GetSize()); ++i) { |
| + list->GetString(i, &id); |
| + if (id == extension_->id()) { |
|
Mattias Nissler (ping if slow)
2013/04/04 12:51:34
no need for braces for single-line conditional/bod
davidyu
2013/04/09 09:30:08
Done.
|
| + return true; |
| + } |
| + } |
| + return false; |
| +} |
| + |
| +bool EEKPChallengeUserKey::IsRemoteAttestationEnabledForUser() const { |
| + return profile()->GetPrefs()->GetBoolean(prefs::kAttestationEnabled); |
| +} |
| + |
| +std::string EEKPChallengeUserKey::GetUserDomain() const { |
| + SigninManager* signin_manager = |
| + SigninManagerFactory::GetForProfile(profile()); |
| + if (!signin_manager) { |
| + return ""; |
| + } |
| + |
| + std::string email = signin_manager->GetAuthenticatedUsername(); |
| + std::string::size_type i = email.find('@'); |
| + if (i == std::string::npos) { |
| + return ""; |
| + } |
| + |
| + return email.substr(i + 1); |
| +} |
| + |
| +} // namespace extensions |