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/message_loop.h"
12#include "base/prefs/pref_service.h"
13#include "base/strings/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/browser_policy_connector_chromeos.h"
18#include "chrome/browser/chromeos/policy/enterprise_install_attributes.h"
19#include "chrome/browser/chromeos/settings/cros_settings.h"
20#include "chrome/browser/profiles/profile.h"
21#include "chrome/browser/signin/signin_manager_factory.h"
22#include "chrome/common/extensions/api/enterprise_platform_keys_private.h"
23#include "chrome/common/pref_names.h"
24#include "chromeos/attestation/attestation_constants.h"
25#include "chromeos/attestation/attestation_flow.h"
26#include "chromeos/cryptohome/async_method_caller.h"
27#include "chromeos/dbus/cryptohome_client.h"
28#include "chromeos/dbus/dbus_method_call_status.h"
29#include "chromeos/dbus/dbus_thread_manager.h"
30#include "chromeos/settings/cros_settings_names.h"
31#include "components/pref_registry/pref_registry_syncable.h"
32#include "components/signin/core/browser/signin_manager.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
42const char EPKPChallengeKeyBase::kChallengeBadBase64Error[] =
43    "Challenge is not base64 encoded.";
44const char EPKPChallengeKeyBase::kDevicePolicyDisabledError[] =
45    "Remote attestation is not enabled for your device.";
46const char EPKPChallengeKeyBase::kExtensionNotWhitelistedError[] =
47    "The extension does not have permission to call this function.";
48const char EPKPChallengeKeyBase::kResponseBadBase64Error[] =
49    "Response cannot be encoded in base64.";
50const char EPKPChallengeKeyBase::kSignChallengeFailedError[] =
51    "Failed to sign the challenge.";
52const char EPKPChallengeKeyBase::kUserNotManaged[] =
53    "The user account is not enterprise managed.";
54
55EPKPChallengeKeyBase::PrepareKeyContext::PrepareKeyContext(
56    chromeos::attestation::AttestationKeyType key_type,
57    const std::string& user_id,
58    const std::string& key_name,
59    chromeos::attestation::AttestationCertificateProfile certificate_profile,
60    bool require_user_consent,
61    const base::Callback<void(PrepareKeyResult)>& callback)
62    : key_type(key_type),
63      user_id(user_id),
64      key_name(key_name),
65      certificate_profile(certificate_profile),
66      require_user_consent(require_user_consent),
67      callback(callback) {
68}
69
70EPKPChallengeKeyBase::PrepareKeyContext::~PrepareKeyContext() {
71}
72
73EPKPChallengeKeyBase::EPKPChallengeKeyBase()
74    : cryptohome_client_(
75          chromeos::DBusThreadManager::Get()->GetCryptohomeClient()),
76      async_caller_(cryptohome::AsyncMethodCaller::GetInstance()),
77      install_attributes_(g_browser_process->platform_part()
78                              ->browser_policy_connector_chromeos()
79                              ->GetInstallAttributes()) {
80  scoped_ptr<chromeos::attestation::ServerProxy> ca_client(
81      new chromeos::attestation::AttestationCAClient());
82  default_attestation_flow_.reset(
83      new chromeos::attestation::AttestationFlow(
84          async_caller_, cryptohome_client_, ca_client.Pass()));
85  attestation_flow_ = default_attestation_flow_.get();
86}
87
88EPKPChallengeKeyBase::EPKPChallengeKeyBase(
89    chromeos::CryptohomeClient* cryptohome_client,
90    cryptohome::AsyncMethodCaller* async_caller,
91    chromeos::attestation::AttestationFlow* attestation_flow,
92    policy::EnterpriseInstallAttributes* install_attributes) :
93    cryptohome_client_(cryptohome_client),
94    async_caller_(async_caller),
95    attestation_flow_(attestation_flow),
96    install_attributes_(install_attributes) {
97}
98
99EPKPChallengeKeyBase::~EPKPChallengeKeyBase() {
100}
101
102void EPKPChallengeKeyBase::GetDeviceAttestationEnabled(
103    const base::Callback<void(bool)>& callback) const {
104  chromeos::CrosSettings* settings = chromeos::CrosSettings::Get();
105  chromeos::CrosSettingsProvider::TrustedStatus status =
106      settings->PrepareTrustedValues(
107          base::Bind(&EPKPChallengeKeyBase::GetDeviceAttestationEnabled, this,
108                     callback));
109
110  bool value = false;
111  switch (status) {
112    case chromeos::CrosSettingsProvider::TRUSTED:
113      if (!settings->GetBoolean(chromeos::kDeviceAttestationEnabled, &value))
114        value = false;
115      break;
116    case chromeos::CrosSettingsProvider::TEMPORARILY_UNTRUSTED:
117      // Do nothing. This function will be called again when the values are
118      // ready.
119      return;
120    case chromeos::CrosSettingsProvider::PERMANENTLY_UNTRUSTED:
121      // If the value cannot be trusted, we assume that the device attestation
122      // is false to be on the safe side.
123      break;
124  }
125
126  callback.Run(value);
127}
128
129bool EPKPChallengeKeyBase::IsEnterpriseDevice() const {
130  return install_attributes_->IsEnterpriseDevice();
131}
132
133bool EPKPChallengeKeyBase::IsExtensionWhitelisted() const {
134  const base::ListValue* list =
135      GetProfile()->GetPrefs()->GetList(prefs::kAttestationExtensionWhitelist);
136  base::StringValue value(extension_->id());
137  return list->Find(value) != list->end();
138}
139
140bool EPKPChallengeKeyBase::IsUserManaged() const {
141  std::string email = GetUserEmail();
142
143  // TODO(davidyu): Use BrowserPolicyConnector::GetUserAffiliation() and fix
144  // the test.
145  return email.empty() ? false :
146      gaia::ExtractDomainName(email) == GetEnterpriseDomain();
147}
148
149std::string EPKPChallengeKeyBase::GetEnterpriseDomain() const {
150  return install_attributes_->GetDomain();
151}
152
153std::string EPKPChallengeKeyBase::GetUserEmail() const {
154  SigninManagerBase* signin_manager =
155      SigninManagerFactory::GetForProfile(GetProfile());
156  if (!signin_manager)
157    return std::string();
158
159  return gaia::CanonicalizeEmail(signin_manager->GetAuthenticatedUsername());
160}
161
162std::string EPKPChallengeKeyBase::GetDeviceId() const {
163  return install_attributes_->GetDeviceId();
164}
165
166void EPKPChallengeKeyBase::PrepareKey(
167    chromeos::attestation::AttestationKeyType key_type,
168    const std::string& user_id,
169    const std::string& key_name,
170    chromeos::attestation::AttestationCertificateProfile certificate_profile,
171    bool require_user_consent,
172    const base::Callback<void(PrepareKeyResult)>& callback) {
173  const PrepareKeyContext context = PrepareKeyContext(key_type,
174                                                      user_id,
175                                                      key_name,
176                                                      certificate_profile,
177                                                      require_user_consent,
178                                                      callback);
179  cryptohome_client_->TpmAttestationIsPrepared(base::Bind(
180      &EPKPChallengeKeyBase::IsAttestationPreparedCallback, this, context));
181}
182
183void EPKPChallengeKeyBase::IsAttestationPreparedCallback(
184    const PrepareKeyContext& context,
185    chromeos::DBusMethodCallStatus status,
186    bool result) {
187  if (status == chromeos::DBUS_METHOD_CALL_FAILURE) {
188    context.callback.Run(PREPARE_KEY_DBUS_ERROR);
189    return;
190  }
191  if (!result) {
192    context.callback.Run(PREPARE_KEY_RESET_REQUIRED);
193    return;
194  }
195  // Attestation is available, see if the key we need already exists.
196  cryptohome_client_->TpmAttestationDoesKeyExist(
197      context.key_type, context.user_id, context.key_name, base::Bind(
198          &EPKPChallengeKeyBase::DoesKeyExistCallback, this, context));
199}
200
201void EPKPChallengeKeyBase::DoesKeyExistCallback(
202    const PrepareKeyContext& context,
203    chromeos::DBusMethodCallStatus status,
204    bool result) {
205  if (status == chromeos::DBUS_METHOD_CALL_FAILURE) {
206    context.callback.Run(PREPARE_KEY_DBUS_ERROR);
207    return;
208  }
209
210  if (result) {
211    // The key exists. Do nothing more.
212    context.callback.Run(PREPARE_KEY_OK);
213  } else {
214    // The key does not exist. Create a new key and have it signed by PCA.
215    if (context.require_user_consent) {
216      // We should ask the user explicitly before sending any private
217      // information to PCA.
218      AskForUserConsent(
219          base::Bind(&EPKPChallengeKeyBase::AskForUserConsentCallback, this,
220                     context));
221    } else {
222      // User consent is not required. Skip to the next step.
223      AskForUserConsentCallback(context, true);
224    }
225  }
226}
227
228void EPKPChallengeKeyBase::AskForUserConsent(
229    const base::Callback<void(bool)>& callback) const {
230  // TODO(davidyu): right now we just simply reject the request before we have
231  // a way to ask for user consent.
232  callback.Run(false);
233}
234
235void EPKPChallengeKeyBase::AskForUserConsentCallback(
236    const PrepareKeyContext& context,
237    bool result) {
238  if (!result) {
239    // The user rejects the request.
240    context.callback.Run(PREPARE_KEY_USER_REJECTED);
241    return;
242  }
243
244  // Generate a new key and have it signed by PCA.
245  attestation_flow_->GetCertificate(
246      context.certificate_profile,
247      context.user_id,
248      std::string(),  // Not used.
249      true,  // Force a new key to be generated.
250      base::Bind(&EPKPChallengeKeyBase::GetCertificateCallback, this,
251                 context.callback));
252}
253
254void EPKPChallengeKeyBase::GetCertificateCallback(
255    const base::Callback<void(PrepareKeyResult)>& callback,
256    bool success,
257    const std::string& pem_certificate_chain) {
258  if (!success) {
259    callback.Run(PREPARE_KEY_GET_CERTIFICATE_FAILED);
260    return;
261  }
262
263  callback.Run(PREPARE_KEY_OK);
264}
265
266// Implementation of ChallengeMachineKey()
267
268const char EPKPChallengeMachineKey::kGetCertificateFailedError[] =
269    "Failed to get Enterprise machine certificate. Error code = %d";
270const char EPKPChallengeMachineKey::kNonEnterpriseDeviceError[] =
271    "The device is not enterprise enrolled.";
272
273const char EPKPChallengeMachineKey::kKeyName[] = "attest-ent-machine";
274
275EPKPChallengeMachineKey::EPKPChallengeMachineKey() : EPKPChallengeKeyBase() {
276}
277
278EPKPChallengeMachineKey::EPKPChallengeMachineKey(
279    chromeos::CryptohomeClient* cryptohome_client,
280    cryptohome::AsyncMethodCaller* async_caller,
281    chromeos::attestation::AttestationFlow* attestation_flow,
282    policy::EnterpriseInstallAttributes* install_attributes) :
283    EPKPChallengeKeyBase(cryptohome_client,
284                         async_caller,
285                         attestation_flow,
286                         install_attributes) {
287}
288
289EPKPChallengeMachineKey::~EPKPChallengeMachineKey() {
290}
291
292bool EPKPChallengeMachineKey::RunAsync() {
293  scoped_ptr<api_epkp::ChallengeMachineKey::Params>
294      params(api_epkp::ChallengeMachineKey::Params::Create(*args_));
295  EXTENSION_FUNCTION_VALIDATE(params.get());
296
297  std::string challenge;
298  if (!base::Base64Decode(params->challenge, &challenge)) {
299    SetError(kChallengeBadBase64Error);
300    return false;
301  }
302
303  // Check if the device is enterprise enrolled.
304  if (!IsEnterpriseDevice()) {
305    SetError(kNonEnterpriseDeviceError);
306    return false;
307  }
308
309  // Check if the extension is whitelisted in the user policy.
310  if (!IsExtensionWhitelisted()) {
311    SetError(kExtensionNotWhitelistedError);
312    return false;
313  }
314
315  // Check if the user domain is the same as the enrolled enterprise domain.
316  if (!IsUserManaged()) {
317    SetError(kUserNotManaged);
318    return false;
319  }
320
321  // Check if RA is enabled in the device policy.
322  GetDeviceAttestationEnabled(
323      base::Bind(&EPKPChallengeMachineKey::GetDeviceAttestationEnabledCallback,
324                 this, challenge));
325
326  return true;
327}
328
329void EPKPChallengeMachineKey::GetDeviceAttestationEnabledCallback(
330    const std::string& challenge, bool enabled) {
331  if (!enabled) {
332    SetError(kDevicePolicyDisabledError);
333    SendResponse(false);
334    return;
335  }
336
337  PrepareKey(chromeos::attestation::KEY_DEVICE,
338             std::string(),  // Not used.
339             kKeyName,
340             chromeos::attestation::PROFILE_ENTERPRISE_MACHINE_CERTIFICATE,
341             false,  // user consent is not required.
342             base::Bind(&EPKPChallengeMachineKey::PrepareKeyCallback, this,
343                        challenge));
344}
345
346void EPKPChallengeMachineKey::PrepareKeyCallback(
347    const std::string& challenge, PrepareKeyResult result) {
348  if (result != PREPARE_KEY_OK) {
349    SetError(base::StringPrintf(kGetCertificateFailedError, result));
350    SendResponse(false);
351    return;
352  }
353
354  // Everything is checked. Sign the challenge.
355  async_caller_->TpmAttestationSignEnterpriseChallenge(
356      chromeos::attestation::KEY_DEVICE,
357      std::string(),  // Not used.
358      kKeyName,
359      GetEnterpriseDomain(),
360      GetDeviceId(),
361      chromeos::attestation::CHALLENGE_OPTION_NONE,
362      challenge,
363      base::Bind(&EPKPChallengeMachineKey::SignChallengeCallback, this));
364}
365
366void EPKPChallengeMachineKey::SignChallengeCallback(
367    bool success, const std::string& response) {
368  if (!success) {
369    SetError(kSignChallengeFailedError);
370    SendResponse(false);
371    return;
372  }
373
374  std::string encoded_response;
375  base::Base64Encode(response, &encoded_response);
376
377  results_ = api_epkp::ChallengeMachineKey::Results::Create(encoded_response);
378  SendResponse(true);
379}
380
381// Implementation of ChallengeUserKey()
382
383const char EPKPChallengeUserKey::kGetCertificateFailedError[] =
384    "Failed to get Enterprise user certificate. Error code = %d";
385const char EPKPChallengeUserKey::kKeyRegistrationFailedError[] =
386    "Key registration failed.";
387const char EPKPChallengeUserKey::kUserPolicyDisabledError[] =
388    "Remote attestation is not enabled for your account.";
389
390const char EPKPChallengeUserKey::kKeyName[] = "attest-ent-user";
391
392EPKPChallengeUserKey::EPKPChallengeUserKey() : EPKPChallengeKeyBase() {
393}
394
395EPKPChallengeUserKey::EPKPChallengeUserKey(
396    chromeos::CryptohomeClient* cryptohome_client,
397    cryptohome::AsyncMethodCaller* async_caller,
398    chromeos::attestation::AttestationFlow* attestation_flow,
399    policy::EnterpriseInstallAttributes* install_attributes) :
400    EPKPChallengeKeyBase(cryptohome_client,
401                         async_caller,
402                         attestation_flow,
403                         install_attributes) {
404}
405
406EPKPChallengeUserKey::~EPKPChallengeUserKey() {
407}
408
409void EPKPChallengeUserKey::RegisterProfilePrefs(
410    user_prefs::PrefRegistrySyncable* registry) {
411  registry->RegisterBooleanPref(
412      prefs::kAttestationEnabled,
413      false,
414      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
415  registry->RegisterListPref(prefs::kAttestationExtensionWhitelist,
416                             user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
417}
418
419bool EPKPChallengeUserKey::RunAsync() {
420  scoped_ptr<api_epkp::ChallengeUserKey::Params> params(
421      api_epkp::ChallengeUserKey::Params::Create(*args_));
422  EXTENSION_FUNCTION_VALIDATE(params.get());
423
424  std::string challenge;
425  if (!base::Base64Decode(params->challenge, &challenge)) {
426    SetError(kChallengeBadBase64Error);
427    return false;
428  }
429
430  // Check if RA is enabled in the user policy.
431  if (!IsRemoteAttestationEnabledForUser()) {
432    SetError(kUserPolicyDisabledError);
433    return false;
434  }
435
436  // Check if the extension is whitelisted in the user policy.
437  if (!IsExtensionWhitelisted()) {
438    SetError(kExtensionNotWhitelistedError);
439    return false;
440  }
441
442  if (IsEnterpriseDevice()) {
443    // Check if the user domain is the same as the enrolled enterprise domain.
444    if (!IsUserManaged()) {
445      SetError(kUserNotManaged);
446      return false;
447    }
448
449    // Check if RA is enabled in the device policy.
450    GetDeviceAttestationEnabled(
451        base::Bind(&EPKPChallengeUserKey::GetDeviceAttestationEnabledCallback,
452                   this,
453                   challenge,
454                   params->register_key,
455                   false));  // user consent is not required.
456  } else {
457    // For personal devices, we don't need to check if RA is enabled in the
458    // device, but we need to ask for user consent if the key does not exist.
459    GetDeviceAttestationEnabledCallback(
460        challenge,
461        params->register_key,
462        true,  // user consent is required.
463        true);  // attestation is enabled.
464  }
465
466  return true;
467}
468
469void EPKPChallengeUserKey::GetDeviceAttestationEnabledCallback(
470    const std::string& challenge,
471    bool register_key,
472    bool require_user_consent,
473    bool enabled) {
474  if (!enabled) {
475    SetError(kDevicePolicyDisabledError);
476    SendResponse(false);
477    return;
478  }
479
480  PrepareKey(chromeos::attestation::KEY_USER,
481             GetUserEmail(),
482             kKeyName,
483             chromeos::attestation::PROFILE_ENTERPRISE_USER_CERTIFICATE,
484             require_user_consent,
485             base::Bind(&EPKPChallengeUserKey::PrepareKeyCallback, this,
486                        challenge, register_key));
487}
488
489void EPKPChallengeUserKey::PrepareKeyCallback(const std::string& challenge,
490                                              bool register_key,
491                                              PrepareKeyResult result) {
492  if (result != PREPARE_KEY_OK) {
493    SetError(base::StringPrintf(kGetCertificateFailedError, result));
494    SendResponse(false);
495    return;
496  }
497
498  // Everything is checked. Sign the challenge.
499  async_caller_->TpmAttestationSignEnterpriseChallenge(
500      chromeos::attestation::KEY_USER,
501      GetUserEmail(),
502      kKeyName,
503      GetUserEmail(),
504      GetDeviceId(),
505      register_key ?
506          chromeos::attestation::CHALLENGE_INCLUDE_SIGNED_PUBLIC_KEY :
507          chromeos::attestation::CHALLENGE_OPTION_NONE,
508      challenge,
509      base::Bind(&EPKPChallengeUserKey::SignChallengeCallback, this,
510                 register_key));
511}
512
513void EPKPChallengeUserKey::SignChallengeCallback(bool register_key,
514                                                 bool success,
515                                                 const std::string& response) {
516  if (!success) {
517    SetError(kSignChallengeFailedError);
518    SendResponse(false);
519    return;
520  }
521
522  if (register_key) {
523    async_caller_->TpmAttestationRegisterKey(
524        chromeos::attestation::KEY_USER,
525        GetUserEmail(),
526        kKeyName,
527        base::Bind(&EPKPChallengeUserKey::RegisterKeyCallback, this, response));
528  } else {
529    RegisterKeyCallback(response, true, cryptohome::MOUNT_ERROR_NONE);
530  }
531}
532
533void EPKPChallengeUserKey::RegisterKeyCallback(
534    const std::string& response,
535    bool success,
536    cryptohome::MountError return_code) {
537  if (!success || return_code != cryptohome::MOUNT_ERROR_NONE) {
538    SetError(kKeyRegistrationFailedError);
539    SendResponse(false);
540    return;
541  }
542
543  std::string encoded_response;
544  base::Base64Encode(response, &encoded_response);
545
546  results_ = api_epkp::ChallengeUserKey::Results::Create(encoded_response);
547  SendResponse(true);
548}
549
550bool EPKPChallengeUserKey::IsRemoteAttestationEnabledForUser() const {
551  return GetProfile()->GetPrefs()->GetBoolean(prefs::kAttestationEnabled);
552}
553
554}  // namespace extensions
555