| 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..027e6ec0cd4bd452fae1fd0e86a06c4fe98a6282
|
| --- /dev/null
|
| +++ b/chrome/browser/extensions/api/enterprise_enterprise_key_private/enterprise_enterprise_key_private_api.cc
|
| @@ -0,0 +1,357 @@
|
| +// 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>
|
| +
|
| +#include "base/base64.h"
|
| +#include "base/callback.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/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/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 "google_apis/gaia/gaia_auth_util.h"
|
| +#include "third_party/cros_system_api/dbus/service_constants.h"
|
| +
|
| +namespace extensions {
|
| +
|
| +namespace api_eekp = api::enterprise_enterprise_key_private;
|
| +
|
| +// Base class
|
| +
|
| +const char EEKPChallengeKeyBase::kKeyName[] = "attest-ent-default";
|
| +
|
| +EEKPChallengeKeyBase::EEKPChallengeKeyBase()
|
| + : cryptohome_client_(
|
| + chromeos::DBusThreadManager::Get()->GetCryptohomeClient()),
|
| + async_caller_(cryptohome::AsyncMethodCaller::GetInstance()),
|
| + install_attributes_(g_browser_process->browser_policy_connector()->
|
| + GetInstallAttributes()) {
|
| +}
|
| +
|
| +void EEKPChallengeKeyBase::GetDeviceAttestationEnabled(
|
| + const base::Callback<void(bool)>& callback) const {
|
| + chromeos::CrosSettings* settings = chromeos::CrosSettings::Get();
|
| + chromeos::CrosSettingsProvider::TrustedStatus status =
|
| + settings->PrepareTrustedValues(
|
| + base::Bind(&EEKPChallengeKeyBase::GetDeviceAttestationEnabled, this,
|
| + callback));
|
| +
|
| + bool value = false;
|
| + switch (status) {
|
| + case chromeos::CrosSettingsProvider::TRUSTED:
|
| + if (!settings->GetBoolean(chromeos::kDeviceAttestationEnabled, &value))
|
| + value = false;
|
| + break;
|
| + case chromeos::CrosSettingsProvider::TEMPORARILY_UNTRUSTED:
|
| + // Do nothing. This function will be called again when the values are
|
| + // ready.
|
| + return;
|
| + case chromeos::CrosSettingsProvider::PERMANENTLY_UNTRUSTED:
|
| + // If the value cannot be trusted, we assume that the device attestation
|
| + // is false to be on the safe side.
|
| + break;
|
| + }
|
| +
|
| + MessageLoop::current()->PostTask(FROM_HERE, base::Bind(callback, 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();
|
| +}
|
| +
|
| +// Implementation of ChallengeMachineKey()
|
| +
|
| +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.
|
| + GetDeviceAttestationEnabled(
|
| + base::Bind(&EEKPChallengeMachineKey::GetDeviceAttestationEnabledCallback,
|
| + this, challenge));
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void EEKPChallengeMachineKey::GetDeviceAttestationEnabledCallback(
|
| + const std::string& challenge, bool enabled) {
|
| + if (!enabled) {
|
| + SetError("Remote attestation is not enabled for your device.");
|
| + SendResponse(false);
|
| + return;
|
| + }
|
| +
|
| + // Everything is checked. Sign the challenge.
|
| + async_caller_->TpmAttestationSignEnterpriseChallenge(
|
| + chromeos::CryptohomeClient::DEVICE_KEY,
|
| + kKeyName,
|
| + GetEnterpriseDomain(),
|
| + GetDeviceId(),
|
| + chromeos::CryptohomeClient::CHALLENGE_RESPONSE_OPTION_NONE,
|
| + challenge,
|
| + base::Bind(&EEKPChallengeMachineKey::SignChallengeCallback, this));
|
| +}
|
| +
|
| +void EEKPChallengeMachineKey::SignChallengeCallback(
|
| + bool success, const std::string& response) {
|
| + if (!success) {
|
| + SetError("Challenge failed.");
|
| + SendResponse(false);
|
| + return;
|
| + }
|
| +
|
| + std::string encoded_response;
|
| + if (!base::Base64Encode(response, &encoded_response)) {
|
| + SetError("Response cannot be encoded in base64.");
|
| + SendResponse(false);
|
| + return;
|
| + }
|
| +
|
| + results_ = api_eekp::ChallengeMachineKey::Results::Create(encoded_response);
|
| + SendResponse(true);
|
| +}
|
| +
|
| +// Implementation of ChallengeUserKey()
|
| +
|
| +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 the user domain is the same as the enrolled enterprise domain.
|
| + std::string enterprise_domain = GetEnterpriseDomain();
|
| + if (user_domain != enterprise_domain) {
|
| + SetError("User domain " + user_domain + " and Enterprise domain " +
|
| + enterprise_domain + " don't match");
|
| + SendResponse(false);
|
| + return false;
|
| + }
|
| +
|
| + // Check if RA is enabled in the device policy.
|
| + GetDeviceAttestationEnabled(
|
| + base::Bind(&EEKPChallengeUserKey::GetDeviceAttestationEnabledCallback,
|
| + this, challenge, params->register_key, user_domain));
|
| + } else {
|
| + // If this is a personal device, we should explicitly ask the user before
|
| + // we use the key.
|
| + AskForUserConsent(
|
| + base::Bind(&EEKPChallengeUserKey::UserConsentCallback, this,
|
| + challenge, params->register_key, user_domain));
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void EEKPChallengeUserKey::GetDeviceAttestationEnabledCallback(
|
| + const std::string& challenge,
|
| + bool register_key,
|
| + const std::string& domain,
|
| + bool enabled) {
|
| + if (!enabled) {
|
| + SetError("Remote attestation is not enabled for your device.");
|
| + SendResponse(false);
|
| + return;
|
| + }
|
| +
|
| + // 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, this,
|
| + challenge, register_key, domain, 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);
|
| + return;
|
| + }
|
| +
|
| + // Everything is checked. Sign the challenge.
|
| +
|
| + async_caller_->TpmAttestationSignEnterpriseChallenge(
|
| + chromeos::CryptohomeClient::USER_KEY,
|
| + kKeyName,
|
| + domain,
|
| + GetDeviceId(),
|
| + register_key ? chromeos::CryptohomeClient::INCLUDE_SIGNED_PUBLIC_KEY :
|
| + chromeos::CryptohomeClient::CHALLENGE_RESPONSE_OPTION_NONE,
|
| + challenge,
|
| + base::Bind(&EEKPChallengeUserKey::SignChallengeCallback, this,
|
| + register_key));
|
| +}
|
| +
|
| +void EEKPChallengeUserKey::SignChallengeCallback(bool register_key,
|
| + bool success,
|
| + const std::string& response) {
|
| + if (!success) {
|
| + SetError("Challenge failed.");
|
| + SendResponse(false);
|
| + return;
|
| + }
|
| +
|
| + if (register_key) {
|
| + cryptohome_client_->TpmAttestationGetPublicKey(
|
| + chromeos::CryptohomeClient::USER_KEY,
|
| + kKeyName,
|
| + base::Bind(&EEKPChallengeUserKey::GetPublicKeyCallback, this,
|
| + response));
|
| + } else {
|
| + MessageLoop::current()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&EEKPChallengeUserKey::RegisterKeyCallback, this,
|
| + 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);
|
| + return;
|
| + }
|
| +
|
| + async_caller_->TpmAttestationRegisterKey(
|
| + chromeos::CryptohomeClient::USER_KEY,
|
| + kKeyName,
|
| + base::Bind(&EEKPChallengeUserKey::RegisterKeyCallback, this,
|
| + 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);
|
| + return;
|
| + }
|
| +
|
| + std::string encoded_response, encoded_public_key;
|
| + if (!base::Base64Encode(response, &encoded_response)) {
|
| + SetError("Response cannot be encoded in base64.");
|
| + SendResponse(false);
|
| + return;
|
| + }
|
| +
|
| + if (!base::Base64Encode(public_key, &encoded_public_key)) {
|
| + SetError("Public key cannot be encoded in base64.");
|
| + SendResponse(false);
|
| + return;
|
| + }
|
| +
|
| + results_ = api_eekp::ChallengeUserKey::Results::Create(
|
| + encoded_response, encoded_public_key);
|
| + SendResponse(true);
|
| +}
|
| +
|
| +void EEKPChallengeUserKey::AskForUserConsent(
|
| + const base::Callback<void(bool)>& callback) {
|
| + // TODO(davidyu): right now we just simply reject the request before we have
|
| + // 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);
|
| + StringValue value(extension_->id());
|
| + return list->Find(value) != list->end();
|
| +}
|
| +
|
| +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 "";
|
| +
|
| + return gaia::ExtractDomainName(
|
| + gaia::CanonicalizeEmail(signin_manager->GetAuthenticatedUsername()));
|
| +}
|
| +
|
| +} // namespace extensions
|
|
|