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 |