enterprise_platform_keys_private_api.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
1// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.h"
6
7#include <string>
8
9#include "base/base64.h"
10#include "base/callback.h"
11#include "base/message_loop.h"
12#include "base/prefs/pref_service.h"
13#include "base/stringprintf.h"
14#include "base/values.h"
15#include "chrome/browser/browser_process.h"
16#include "chrome/browser/chromeos/attestation/attestation_ca_client.h"
17#include "chrome/browser/chromeos/policy/enterprise_install_attributes.h"
18#include "chrome/browser/chromeos/settings/cros_settings.h"
19#include "chrome/browser/chromeos/settings/cros_settings_names.h"
20#include "chrome/browser/policy/browser_policy_connector.h"
21#include "chrome/browser/profiles/profile.h"
22#include "chrome/browser/signin/signin_manager.h"
23#include "chrome/browser/signin/signin_manager_factory.h"
24#include "chrome/common/extensions/api/enterprise_platform_keys_private.h"
25#include "chrome/common/pref_names.h"
26#include "chromeos/attestation/attestation_constants.h"
27#include "chromeos/attestation/attestation_flow.h"
28#include "chromeos/cryptohome/async_method_caller.h"
29#include "chromeos/dbus/cryptohome_client.h"
30#include "chromeos/dbus/dbus_method_call_status.h"
31#include "chromeos/dbus/dbus_thread_manager.h"
32#include "components/user_prefs/pref_registry_syncable.h"
33#include "google_apis/gaia/gaia_auth_util.h"
34#include "third_party/cros_system_api/dbus/service_constants.h"
35
36namespace extensions {
37
38namespace api_epkp = api::enterprise_platform_keys_private;
39
40// Base class
41
42EPKPChallengeKeyBase::EPKPChallengeKeyBase()
43    : cryptohome_client_(
44          chromeos::DBusThreadManager::Get()->GetCryptohomeClient()),
45      async_caller_(cryptohome::AsyncMethodCaller::GetInstance()),
46      install_attributes_(g_browser_process->browser_policy_connector()->
47                          GetInstallAttributes()) {
48  scoped_ptr<chromeos::attestation::ServerProxy> ca_client(
49      new chromeos::attestation::AttestationCAClient());
50  attestation_flow_.reset(
51      new chromeos::attestation::AttestationFlow(
52          async_caller_, cryptohome_client_, ca_client.Pass()));
53}
54
55EPKPChallengeKeyBase::~EPKPChallengeKeyBase() {
56}
57
58void EPKPChallengeKeyBase::GetDeviceAttestationEnabled(
59    const base::Callback<void(bool)>& callback) const {
60  chromeos::CrosSettings* settings = chromeos::CrosSettings::Get();
61  chromeos::CrosSettingsProvider::TrustedStatus status =
62      settings->PrepareTrustedValues(
63          base::Bind(&EPKPChallengeKeyBase::GetDeviceAttestationEnabled, this,
64                     callback));
65
66  bool value = false;
67  switch (status) {
68    case chromeos::CrosSettingsProvider::TRUSTED:
69      if (!settings->GetBoolean(chromeos::kDeviceAttestationEnabled, &value))
70        value = false;
71      break;
72    case chromeos::CrosSettingsProvider::TEMPORARILY_UNTRUSTED:
73      // Do nothing. This function will be called again when the values are
74      // ready.
75      return;
76    case chromeos::CrosSettingsProvider::PERMANENTLY_UNTRUSTED:
77      // If the value cannot be trusted, we assume that the device attestation
78      // is false to be on the safe side.
79      break;
80  }
81
82  callback.Run(value);
83}
84
85bool EPKPChallengeKeyBase::IsEnterpriseDevice() const {
86  return install_attributes_->IsEnterpriseDevice();
87}
88
89std::string EPKPChallengeKeyBase::GetEnterpriseDomain() const {
90  return install_attributes_->GetDomain();
91}
92
93std::string EPKPChallengeKeyBase::GetDeviceId() const {
94  return install_attributes_->GetDeviceId();
95}
96
97void EPKPChallengeKeyBase::PrepareKey(
98    chromeos::attestation::AttestationKeyType key_type,
99    const std::string& key_name,
100    chromeos::attestation::AttestationCertificateProfile certificate_profile,
101    bool require_user_consent,
102    const base::Callback<void(PrepareKeyResult)>& callback) {
103  cryptohome_client_->TpmAttestationDoesKeyExist(
104      key_type, key_name, base::Bind(
105          &EPKPChallengeKeyBase::DoesKeyExistCallback, this,
106          certificate_profile, require_user_consent, callback));
107}
108
109void EPKPChallengeKeyBase::DoesKeyExistCallback(
110    chromeos::attestation::AttestationCertificateProfile certificate_profile,
111    bool require_user_consent,
112    const base::Callback<void(PrepareKeyResult)>& callback,
113    chromeos::DBusMethodCallStatus status,
114    bool result) {
115  if (status == chromeos::DBUS_METHOD_CALL_FAILURE) {
116    callback.Run(PREPARE_KEY_DBUS_ERROR);
117    return;
118  }
119
120  if (result) {
121    // The key exists. Do nothing more.
122    callback.Run(PREPARE_KEY_OK);
123  } else {
124    // The key does not exist. Create a new key and have it signed by PCA.
125    if (require_user_consent) {
126      // We should ask the user explicitly before sending any private
127      // information to PCA.
128      AskForUserConsent(
129          base::Bind(&EPKPChallengeKeyBase::AskForUserConsentCallback, this,
130                     certificate_profile, callback));
131    } else {
132      // User consent is not required. Skip to the next step.
133      AskForUserConsentCallback(certificate_profile, callback, true);
134    }
135  }
136}
137
138void EPKPChallengeKeyBase::AskForUserConsent(
139    const base::Callback<void(bool)>& callback) const {
140  // TODO(davidyu): right now we just simply reject the request before we have
141  // a way to ask for user consent.
142  callback.Run(false);
143}
144
145void EPKPChallengeKeyBase::AskForUserConsentCallback(
146    chromeos::attestation::AttestationCertificateProfile certificate_profile,
147    const base::Callback<void(PrepareKeyResult)>& callback,
148    bool result) {
149  if (!result) {
150    // The user rejects the request.
151    callback.Run(PREPARE_KEY_USER_REJECTED);
152    return;
153  }
154
155  // Generate a new key and have it signed by PCA.
156  attestation_flow_->GetCertificate(
157      certificate_profile,
158      true,  // Force a new key to be generated.
159      base::Bind(&EPKPChallengeKeyBase::GetCertificateCallback, this,
160                 callback));
161}
162
163void EPKPChallengeKeyBase::GetCertificateCallback(
164    const base::Callback<void(PrepareKeyResult)>& callback,
165    bool success,
166    const std::string& pem_certificate_chain) {
167  if (!success) {
168    callback.Run(PREPARE_KEY_GET_CERTIFICATE_FAILED);
169    return;
170  }
171
172  callback.Run(PREPARE_KEY_OK);
173}
174
175// Implementation of ChallengeMachineKey()
176
177const char EPKPChallengeMachineKey::kKeyName[] = "attest-ent-machine";
178
179EPKPChallengeMachineKey::~EPKPChallengeMachineKey() {
180}
181
182bool EPKPChallengeMachineKey::RunImpl() {
183  scoped_ptr<api_epkp::ChallengeMachineKey::Params>
184      params(api_epkp::ChallengeMachineKey::Params::Create(*args_));
185  EXTENSION_FUNCTION_VALIDATE(params.get());
186
187  std::string challenge;
188  if (!base::Base64Decode(params->challenge, &challenge)) {
189    SetError("Challenge is not base64 encoded.");
190    SendResponse(false);
191    return false;
192  }
193
194  // Check if the device is enterprise enrolled.
195  if (!IsEnterpriseDevice()) {
196    SetError("The device is not enterprise enrolled.");
197    SendResponse(false);
198    return false;
199  }
200
201  // Check if RA is enabled in the device policy.
202  GetDeviceAttestationEnabled(
203      base::Bind(&EPKPChallengeMachineKey::GetDeviceAttestationEnabledCallback,
204                 this, challenge));
205
206  return true;
207}
208
209void EPKPChallengeMachineKey::GetDeviceAttestationEnabledCallback(
210    const std::string& challenge, bool enabled) {
211  if (!enabled) {
212    SetError("Remote attestation is not enabled for your device.");
213    SendResponse(false);
214    return;
215  }
216
217  PrepareKey(chromeos::attestation::KEY_DEVICE,
218             kKeyName,
219             chromeos::attestation::PROFILE_ENTERPRISE_MACHINE_CERTIFICATE,
220             false,  // user consent is not required.
221             base::Bind(&EPKPChallengeMachineKey::PrepareKeyCallback, this,
222                        challenge));
223}
224
225void EPKPChallengeMachineKey::PrepareKeyCallback(
226    const std::string& challenge, PrepareKeyResult result) {
227  if (result != PREPARE_KEY_OK) {
228    SetError(base::StringPrintf(
229        "Failed to get Enterprise machince certificate. Error code = %d",
230        result));
231    SendResponse(false);
232    return;
233  }
234
235  // Everything is checked. Sign the challenge.
236  async_caller_->TpmAttestationSignEnterpriseChallenge(
237      chromeos::attestation::KEY_DEVICE,
238      kKeyName,
239      GetEnterpriseDomain(),
240      GetDeviceId(),
241      chromeos::attestation::CHALLENGE_OPTION_NONE,
242      challenge,
243      base::Bind(&EPKPChallengeMachineKey::SignChallengeCallback, this));
244}
245
246void EPKPChallengeMachineKey::SignChallengeCallback(
247    bool success, const std::string& response) {
248  if (!success) {
249    SetError("Challenge failed.");
250    SendResponse(false);
251    return;
252  }
253
254  std::string encoded_response;
255  if (!base::Base64Encode(response, &encoded_response)) {
256    SetError("Response cannot be encoded in base64.");
257    SendResponse(false);
258    return;
259  }
260
261  results_ = api_epkp::ChallengeMachineKey::Results::Create(encoded_response);
262  SendResponse(true);
263}
264
265// Implementation of ChallengeUserKey()
266
267const char EPKPChallengeUserKey::kKeyName[] = "attest-ent-user";
268
269EPKPChallengeUserKey::~EPKPChallengeUserKey() {
270}
271
272void EPKPChallengeUserKey::RegisterUserPrefs(
273    user_prefs::PrefRegistrySyncable* registry) {
274  registry->RegisterBooleanPref(
275      prefs::kAttestationEnabled,
276      false,
277      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
278  registry->RegisterListPref(prefs::kAttestationExtensionWhitelist,
279                             user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
280}
281
282bool EPKPChallengeUserKey::RunImpl() {
283  scoped_ptr<api_epkp::ChallengeUserKey::Params> params(
284      api_epkp::ChallengeUserKey::Params::Create(*args_));
285  EXTENSION_FUNCTION_VALIDATE(params.get());
286
287  std::string challenge;
288  if (!base::Base64Decode(params->challenge, &challenge)) {
289    SetError("Challenge is not base64 encoded.");
290    SendResponse(false);
291    return false;
292  }
293
294  // Check if RA is enabled in the user policy.
295  if (!IsRemoteAttestationEnabledForUser()) {
296    SetError("Remote attestation is not enabled for your account.");
297    SendResponse(false);
298    return false;
299  }
300
301  // Check if the extension is whitelisted in the user policy.
302  if (!IsExtensionWhitelisted()) {
303    SetError("The extension does not have permission to call this function.");
304    SendResponse(false);
305    return false;
306  }
307
308  std::string user_domain = GetUserDomain();
309
310  if (IsEnterpriseDevice()) {
311    // Check if the user domain is the same as the enrolled enterprise domain.
312    std::string enterprise_domain = GetEnterpriseDomain();
313    if (user_domain != enterprise_domain) {
314      SetError("User domain " + user_domain + " and Enterprise domain " +
315               enterprise_domain + " don't match");
316      SendResponse(false);
317      return false;
318    }
319
320    // Check if RA is enabled in the device policy.
321    GetDeviceAttestationEnabled(
322        base::Bind(&EPKPChallengeUserKey::GetDeviceAttestationEnabledCallback,
323                   this,
324                   challenge,
325                   params->register_key,
326                   user_domain,
327                   false));  // user consent is not required.
328  } else {
329    // For personal devices, we don't need to check if RA is enabled in the
330    // device, but we need to ask for user consent if the key does not exist.
331    GetDeviceAttestationEnabledCallback(
332        challenge,
333        params->register_key,
334        user_domain,
335        true,  // user consent is required.
336        true);  // attestation is enabled.
337  }
338
339  return true;
340}
341
342void EPKPChallengeUserKey::GetDeviceAttestationEnabledCallback(
343    const std::string& challenge,
344    bool register_key,
345    const std::string& domain,
346    bool require_user_consent,
347    bool enabled) {
348  if (!enabled) {
349    SetError("Remote attestation is not enabled for your device.");
350    SendResponse(false);
351    return;
352  }
353
354  PrepareKey(chromeos::attestation::KEY_USER,
355             kKeyName,
356             chromeos::attestation::PROFILE_ENTERPRISE_USER_CERTIFICATE,
357             require_user_consent,
358             base::Bind(&EPKPChallengeUserKey::PrepareKeyCallback, this,
359                        challenge, register_key, domain));
360}
361
362void EPKPChallengeUserKey::PrepareKeyCallback(const std::string& challenge,
363                                              bool register_key,
364                                              const std::string& domain,
365                                              PrepareKeyResult result) {
366  if (result != PREPARE_KEY_OK) {
367    SetError(base::StringPrintf(
368        "Cannot get a key to sign the challenge. Error code = %d", result));
369    SendResponse(false);
370    return;
371  }
372
373  // Everything is checked. Sign the challenge.
374  async_caller_->TpmAttestationSignEnterpriseChallenge(
375      chromeos::attestation::KEY_USER,
376      kKeyName,
377      domain,
378      GetDeviceId(),
379      register_key ?
380          chromeos::attestation::CHALLENGE_INCLUDE_SIGNED_PUBLIC_KEY :
381          chromeos::attestation::CHALLENGE_OPTION_NONE,
382      challenge,
383      base::Bind(&EPKPChallengeUserKey::SignChallengeCallback, this,
384                 register_key));
385}
386
387void EPKPChallengeUserKey::SignChallengeCallback(bool register_key,
388                                                 bool success,
389                                                 const std::string& response) {
390  if (!success) {
391    SetError("Challenge failed.");
392    SendResponse(false);
393    return;
394  }
395
396  if (register_key) {
397    async_caller_->TpmAttestationRegisterKey(
398        chromeos::attestation::KEY_USER,
399        kKeyName,
400        base::Bind(&EPKPChallengeUserKey::RegisterKeyCallback, this, response));
401  } else {
402    RegisterKeyCallback(response, true, cryptohome::MOUNT_ERROR_NONE);
403  }
404}
405
406void EPKPChallengeUserKey::RegisterKeyCallback(
407    const std::string& response,
408    bool success,
409    cryptohome::MountError return_code) {
410  if (!success || return_code != cryptohome::MOUNT_ERROR_NONE) {
411    SetError("Key registration failed.");
412    SendResponse(false);
413    return;
414  }
415
416  std::string encoded_response;
417  if (!base::Base64Encode(response, &encoded_response)) {
418    SetError("Response cannot be encoded in base64.");
419    SendResponse(false);
420    return;
421  }
422
423  results_ = api_epkp::ChallengeUserKey::Results::Create(encoded_response);
424  SendResponse(true);
425}
426
427bool EPKPChallengeUserKey::IsExtensionWhitelisted() const {
428  const base::ListValue* list =
429      profile()->GetPrefs()->GetList(prefs::kAttestationExtensionWhitelist);
430  StringValue value(extension_->id());
431  return list->Find(value) != list->end();
432}
433
434bool EPKPChallengeUserKey::IsRemoteAttestationEnabledForUser() const {
435  return profile()->GetPrefs()->GetBoolean(prefs::kAttestationEnabled);
436}
437
438std::string EPKPChallengeUserKey::GetUserDomain() const {
439  SigninManagerBase* signin_manager =
440      SigninManagerFactory::GetForProfile(profile());
441  if (!signin_manager)
442    return std::string();
443
444  return gaia::ExtractDomainName(
445      gaia::CanonicalizeEmail(signin_manager->GetAuthenticatedUsername()));
446}
447
448}  // namespace extensions
449