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
36 namespace extensions {
37
38 namespace api_epkp = api::enterprise_platform_keys_private;
39
40 // Base class
41
42 const char EPKPChallengeKeyBase::kChallengeBadBase64Error[] =
43 "Challenge is not base64 encoded.";
44 const char EPKPChallengeKeyBase::kDevicePolicyDisabledError[] =
45 "Remote attestation is not enabled for your device.";
46 const char EPKPChallengeKeyBase::kExtensionNotWhitelistedError[] =
47 "The extension does not have permission to call this function.";
48 const char EPKPChallengeKeyBase::kResponseBadBase64Error[] =
49 "Response cannot be encoded in base64.";
50 const char EPKPChallengeKeyBase::kSignChallengeFailedError[] =
51 "Failed to sign the challenge.";
52 const char EPKPChallengeKeyBase::kUserNotManaged[] =
53 "The user account is not enterprise managed.";
54
PrepareKeyContext(chromeos::attestation::AttestationKeyType key_type,const std::string & user_id,const std::string & key_name,chromeos::attestation::AttestationCertificateProfile certificate_profile,bool require_user_consent,const base::Callback<void (PrepareKeyResult)> & callback)55 EPKPChallengeKeyBase::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
~PrepareKeyContext()70 EPKPChallengeKeyBase::PrepareKeyContext::~PrepareKeyContext() {
71 }
72
EPKPChallengeKeyBase()73 EPKPChallengeKeyBase::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
EPKPChallengeKeyBase(chromeos::CryptohomeClient * cryptohome_client,cryptohome::AsyncMethodCaller * async_caller,chromeos::attestation::AttestationFlow * attestation_flow,policy::EnterpriseInstallAttributes * install_attributes)88 EPKPChallengeKeyBase::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
~EPKPChallengeKeyBase()99 EPKPChallengeKeyBase::~EPKPChallengeKeyBase() {
100 }
101
GetDeviceAttestationEnabled(const base::Callback<void (bool)> & callback) const102 void 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
IsEnterpriseDevice() const129 bool EPKPChallengeKeyBase::IsEnterpriseDevice() const {
130 return install_attributes_->IsEnterpriseDevice();
131 }
132
IsExtensionWhitelisted() const133 bool 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
IsUserManaged() const140 bool 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
GetEnterpriseDomain() const149 std::string EPKPChallengeKeyBase::GetEnterpriseDomain() const {
150 return install_attributes_->GetDomain();
151 }
152
GetUserEmail() const153 std::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
GetDeviceId() const162 std::string EPKPChallengeKeyBase::GetDeviceId() const {
163 return install_attributes_->GetDeviceId();
164 }
165
PrepareKey(chromeos::attestation::AttestationKeyType key_type,const std::string & user_id,const std::string & key_name,chromeos::attestation::AttestationCertificateProfile certificate_profile,bool require_user_consent,const base::Callback<void (PrepareKeyResult)> & callback)166 void 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
IsAttestationPreparedCallback(const PrepareKeyContext & context,chromeos::DBusMethodCallStatus status,bool result)183 void 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
DoesKeyExistCallback(const PrepareKeyContext & context,chromeos::DBusMethodCallStatus status,bool result)201 void 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
AskForUserConsent(const base::Callback<void (bool)> & callback) const228 void 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
AskForUserConsentCallback(const PrepareKeyContext & context,bool result)235 void 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
GetCertificateCallback(const base::Callback<void (PrepareKeyResult)> & callback,bool success,const std::string & pem_certificate_chain)254 void 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
268 const char EPKPChallengeMachineKey::kGetCertificateFailedError[] =
269 "Failed to get Enterprise machine certificate. Error code = %d";
270 const char EPKPChallengeMachineKey::kNonEnterpriseDeviceError[] =
271 "The device is not enterprise enrolled.";
272
273 const char EPKPChallengeMachineKey::kKeyName[] = "attest-ent-machine";
274
EPKPChallengeMachineKey()275 EPKPChallengeMachineKey::EPKPChallengeMachineKey() : EPKPChallengeKeyBase() {
276 }
277
EPKPChallengeMachineKey(chromeos::CryptohomeClient * cryptohome_client,cryptohome::AsyncMethodCaller * async_caller,chromeos::attestation::AttestationFlow * attestation_flow,policy::EnterpriseInstallAttributes * install_attributes)278 EPKPChallengeMachineKey::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
~EPKPChallengeMachineKey()289 EPKPChallengeMachineKey::~EPKPChallengeMachineKey() {
290 }
291
RunAsync()292 bool 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
GetDeviceAttestationEnabledCallback(const std::string & challenge,bool enabled)329 void 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
PrepareKeyCallback(const std::string & challenge,PrepareKeyResult result)346 void 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
SignChallengeCallback(bool success,const std::string & response)366 void 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
383 const char EPKPChallengeUserKey::kGetCertificateFailedError[] =
384 "Failed to get Enterprise user certificate. Error code = %d";
385 const char EPKPChallengeUserKey::kKeyRegistrationFailedError[] =
386 "Key registration failed.";
387 const char EPKPChallengeUserKey::kUserPolicyDisabledError[] =
388 "Remote attestation is not enabled for your account.";
389
390 const char EPKPChallengeUserKey::kKeyName[] = "attest-ent-user";
391
EPKPChallengeUserKey()392 EPKPChallengeUserKey::EPKPChallengeUserKey() : EPKPChallengeKeyBase() {
393 }
394
EPKPChallengeUserKey(chromeos::CryptohomeClient * cryptohome_client,cryptohome::AsyncMethodCaller * async_caller,chromeos::attestation::AttestationFlow * attestation_flow,policy::EnterpriseInstallAttributes * install_attributes)395 EPKPChallengeUserKey::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
~EPKPChallengeUserKey()406 EPKPChallengeUserKey::~EPKPChallengeUserKey() {
407 }
408
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * registry)409 void 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
RunAsync()419 bool 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
GetDeviceAttestationEnabledCallback(const std::string & challenge,bool register_key,bool require_user_consent,bool enabled)469 void 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
PrepareKeyCallback(const std::string & challenge,bool register_key,PrepareKeyResult result)489 void 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
SignChallengeCallback(bool register_key,bool success,const std::string & response)513 void 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
RegisterKeyCallback(const std::string & response,bool success,cryptohome::MountError return_code)533 void 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
IsRemoteAttestationEnabledForUser() const550 bool EPKPChallengeUserKey::IsRemoteAttestationEnabledForUser() const {
551 return GetProfile()->GetPrefs()->GetBoolean(prefs::kAttestationEnabled);
552 }
553
554 } // namespace extensions
555