1 // Copyright 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 "platform_verification_flow.h" 6 7 #include "base/command_line.h" 8 #include "base/logging.h" 9 #include "base/message_loop/message_loop.h" 10 #include "base/prefs/pref_service.h" 11 #include "base/time/time.h" 12 #include "base/timer/timer.h" 13 #include "chrome/browser/chromeos/attestation/attestation_ca_client.h" 14 #include "chrome/browser/chromeos/attestation/attestation_signed_data.pb.h" 15 #include "chrome/browser/chromeos/attestation/platform_verification_dialog.h" 16 #include "chrome/browser/chromeos/profiles/profile_helper.h" 17 #include "chrome/browser/chromeos/settings/cros_settings.h" 18 #include "chrome/browser/content_settings/host_content_settings_map.h" 19 #include "chrome/browser/profiles/profile.h" 20 #include "chrome/common/pref_names.h" 21 #include "chromeos/attestation/attestation_flow.h" 22 #include "chromeos/cryptohome/async_method_caller.h" 23 #include "chromeos/dbus/cryptohome_client.h" 24 #include "chromeos/dbus/dbus_thread_manager.h" 25 #include "components/content_settings/core/common/content_settings_pattern.h" 26 #include "components/pref_registry/pref_registry_syncable.h" 27 #include "components/user_manager/user.h" 28 #include "components/user_prefs/user_prefs.h" 29 #include "content/public/browser/browser_context.h" 30 #include "content/public/browser/browser_thread.h" 31 #include "content/public/browser/user_metrics.h" 32 #include "content/public/browser/web_contents.h" 33 #include "content/public/common/url_constants.h" 34 #include "net/cert/x509_certificate.h" 35 36 namespace { 37 38 const char kDefaultHttpsPort[] = "443"; 39 const int kTimeoutInSeconds = 8; 40 41 // A callback method to handle DBus errors. 42 void DBusCallback(const base::Callback<void(bool)>& on_success, 43 const base::Closure& on_failure, 44 chromeos::DBusMethodCallStatus call_status, 45 bool result) { 46 if (call_status == chromeos::DBUS_METHOD_CALL_SUCCESS) { 47 on_success.Run(result); 48 } else { 49 LOG(ERROR) << "PlatformVerificationFlow: DBus call failed!"; 50 on_failure.Run(); 51 } 52 } 53 54 // A helper to call a ChallengeCallback with an error result. 55 void ReportError( 56 const chromeos::attestation::PlatformVerificationFlow::ChallengeCallback& 57 callback, 58 chromeos::attestation::PlatformVerificationFlow::Result error) { 59 callback.Run(error, std::string(), std::string(), std::string()); 60 } 61 } // namespace 62 63 namespace chromeos { 64 namespace attestation { 65 66 // A default implementation of the Delegate interface. 67 class DefaultDelegate : public PlatformVerificationFlow::Delegate { 68 public: 69 DefaultDelegate() {} 70 virtual ~DefaultDelegate() {} 71 72 virtual void ShowConsentPrompt( 73 content::WebContents* web_contents, 74 const PlatformVerificationFlow::Delegate::ConsentCallback& callback) 75 OVERRIDE { 76 PlatformVerificationDialog::ShowDialog(web_contents, callback); 77 } 78 79 virtual PrefService* GetPrefs(content::WebContents* web_contents) OVERRIDE { 80 return user_prefs::UserPrefs::Get(web_contents->GetBrowserContext()); 81 } 82 83 virtual const GURL& GetURL(content::WebContents* web_contents) OVERRIDE { 84 const GURL& url = web_contents->GetLastCommittedURL(); 85 if (!url.is_valid()) 86 return web_contents->GetVisibleURL(); 87 return url; 88 } 89 90 virtual user_manager::User* GetUser( 91 content::WebContents* web_contents) OVERRIDE { 92 return ProfileHelper::Get()->GetUserByProfile( 93 Profile::FromBrowserContext(web_contents->GetBrowserContext())); 94 } 95 96 virtual HostContentSettingsMap* GetContentSettings( 97 content::WebContents* web_contents) OVERRIDE { 98 return Profile::FromBrowserContext(web_contents->GetBrowserContext())-> 99 GetHostContentSettingsMap(); 100 } 101 102 virtual bool IsGuestOrIncognito(content::WebContents* web_contents) OVERRIDE { 103 Profile* profile = 104 Profile::FromBrowserContext(web_contents->GetBrowserContext()); 105 return (profile->IsOffTheRecord() || profile->IsGuestSession()); 106 } 107 108 private: 109 DISALLOW_COPY_AND_ASSIGN(DefaultDelegate); 110 }; 111 112 PlatformVerificationFlow::ChallengeContext::ChallengeContext( 113 content::WebContents* web_contents, 114 const std::string& service_id, 115 const std::string& challenge, 116 const ChallengeCallback& callback) 117 : web_contents(web_contents), 118 service_id(service_id), 119 challenge(challenge), 120 callback(callback) {} 121 122 PlatformVerificationFlow::ChallengeContext::~ChallengeContext() {} 123 124 PlatformVerificationFlow::PlatformVerificationFlow() 125 : attestation_flow_(NULL), 126 async_caller_(cryptohome::AsyncMethodCaller::GetInstance()), 127 cryptohome_client_(DBusThreadManager::Get()->GetCryptohomeClient()), 128 delegate_(NULL), 129 timeout_delay_(base::TimeDelta::FromSeconds(kTimeoutInSeconds)) { 130 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 131 scoped_ptr<ServerProxy> attestation_ca_client(new AttestationCAClient()); 132 default_attestation_flow_.reset(new AttestationFlow( 133 async_caller_, 134 cryptohome_client_, 135 attestation_ca_client.Pass())); 136 attestation_flow_ = default_attestation_flow_.get(); 137 default_delegate_.reset(new DefaultDelegate()); 138 delegate_ = default_delegate_.get(); 139 } 140 141 PlatformVerificationFlow::PlatformVerificationFlow( 142 AttestationFlow* attestation_flow, 143 cryptohome::AsyncMethodCaller* async_caller, 144 CryptohomeClient* cryptohome_client, 145 Delegate* delegate) 146 : attestation_flow_(attestation_flow), 147 async_caller_(async_caller), 148 cryptohome_client_(cryptohome_client), 149 delegate_(delegate), 150 timeout_delay_(base::TimeDelta::FromSeconds(kTimeoutInSeconds)) { 151 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 152 if (!delegate_) { 153 default_delegate_.reset(new DefaultDelegate()); 154 delegate_ = default_delegate_.get(); 155 } 156 } 157 158 PlatformVerificationFlow::~PlatformVerificationFlow() { 159 } 160 161 void PlatformVerificationFlow::ChallengePlatformKey( 162 content::WebContents* web_contents, 163 const std::string& service_id, 164 const std::string& challenge, 165 const ChallengeCallback& callback) { 166 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 167 if (!delegate_->GetURL(web_contents).is_valid()) { 168 LOG(WARNING) << "PlatformVerificationFlow: Invalid URL."; 169 ReportError(callback, INTERNAL_ERROR); 170 return; 171 } 172 if (!IsAttestationEnabled(web_contents)) { 173 ReportError(callback, POLICY_REJECTED); 174 return; 175 } 176 // A platform key must be bound to a user. They are not allowed in incognito 177 // or guest mode. 178 if (delegate_->IsGuestOrIncognito(web_contents)) { 179 VLOG(1) << "Platform verification denied because the current session is " 180 << "guest or incognito."; 181 ReportError(callback, PLATFORM_NOT_VERIFIED); 182 return; 183 } 184 ChallengeContext context(web_contents, service_id, challenge, callback); 185 BoolDBusMethodCallback dbus_callback = base::Bind( 186 &DBusCallback, 187 base::Bind(&PlatformVerificationFlow::CheckConsent, this, context), 188 base::Bind(&ReportError, callback, INTERNAL_ERROR)); 189 cryptohome_client_->TpmAttestationIsEnrolled(dbus_callback); 190 } 191 192 void PlatformVerificationFlow::CheckConsent(const ChallengeContext& context, 193 bool attestation_enrolled) { 194 PrefService* pref_service = delegate_->GetPrefs(context.web_contents); 195 if (!pref_service) { 196 LOG(ERROR) << "Failed to get user prefs."; 197 ReportError(context.callback, INTERNAL_ERROR); 198 return; 199 } 200 bool consent_required = ( 201 // Consent required if attestation has never been enrolled on this device. 202 !attestation_enrolled || 203 // Consent required if this is the first use of attestation for content 204 // protection on this device. 205 !pref_service->GetBoolean(prefs::kRAConsentFirstTime) || 206 // Consent required if consent has never been given for this domain. 207 !GetDomainPref(delegate_->GetContentSettings(context.web_contents), 208 delegate_->GetURL(context.web_contents), 209 NULL)); 210 Delegate::ConsentCallback consent_callback = base::Bind( 211 &PlatformVerificationFlow::OnConsentResponse, 212 this, 213 context, 214 consent_required); 215 if (consent_required) 216 delegate_->ShowConsentPrompt(context.web_contents, consent_callback); 217 else 218 consent_callback.Run(CONSENT_RESPONSE_NONE); 219 } 220 221 void PlatformVerificationFlow::RegisterProfilePrefs( 222 user_prefs::PrefRegistrySyncable* prefs) { 223 prefs->RegisterBooleanPref(prefs::kRAConsentFirstTime, 224 false, 225 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 226 } 227 228 void PlatformVerificationFlow::OnConsentResponse( 229 const ChallengeContext& context, 230 bool consent_required, 231 ConsentResponse consent_response) { 232 if (consent_required) { 233 if (consent_response == CONSENT_RESPONSE_NONE) { 234 // No user response - do not proceed and do not modify any settings. 235 LOG(WARNING) << "PlatformVerificationFlow: No response from user."; 236 ReportError(context.callback, USER_REJECTED); 237 return; 238 } 239 if (!UpdateSettings(context.web_contents, consent_response)) { 240 ReportError(context.callback, INTERNAL_ERROR); 241 return; 242 } 243 if (consent_response == CONSENT_RESPONSE_DENY) { 244 content::RecordAction( 245 base::UserMetricsAction("PlatformVerificationRejected")); 246 VLOG(1) << "Platform verification denied by user."; 247 ReportError(context.callback, USER_REJECTED); 248 return; 249 } else if (consent_response == CONSENT_RESPONSE_ALLOW) { 250 content::RecordAction( 251 base::UserMetricsAction("PlatformVerificationAccepted")); 252 VLOG(1) << "Platform verification accepted by user."; 253 } 254 } 255 256 // At this point all user interaction is complete and we can proceed with the 257 // certificate request. 258 user_manager::User* user = delegate_->GetUser(context.web_contents); 259 if (!user) { 260 ReportError(context.callback, INTERNAL_ERROR); 261 LOG(ERROR) << "Profile does not map to a valid user."; 262 return; 263 } 264 265 GetCertificate(context, user->email(), false /* Don't force a new key */); 266 } 267 268 void PlatformVerificationFlow::GetCertificate(const ChallengeContext& context, 269 const std::string& user_id, 270 bool force_new_key) { 271 scoped_ptr<base::Timer> timer(new base::Timer(false, // Don't retain. 272 false)); // Don't repeat. 273 base::Closure timeout_callback = base::Bind( 274 &PlatformVerificationFlow::OnCertificateTimeout, 275 this, 276 context); 277 timer->Start(FROM_HERE, timeout_delay_, timeout_callback); 278 279 AttestationFlow::CertificateCallback certificate_callback = base::Bind( 280 &PlatformVerificationFlow::OnCertificateReady, 281 this, 282 context, 283 user_id, 284 base::Passed(&timer)); 285 attestation_flow_->GetCertificate( 286 PROFILE_CONTENT_PROTECTION_CERTIFICATE, 287 user_id, 288 context.service_id, 289 force_new_key, 290 certificate_callback); 291 } 292 293 void PlatformVerificationFlow::OnCertificateReady( 294 const ChallengeContext& context, 295 const std::string& user_id, 296 scoped_ptr<base::Timer> timer, 297 bool operation_success, 298 const std::string& certificate) { 299 // Log failure before checking the timer so all failures are logged, even if 300 // they took too long. 301 if (!operation_success) { 302 LOG(WARNING) << "PlatformVerificationFlow: Failed to certify platform."; 303 } 304 if (!timer->IsRunning()) { 305 LOG(WARNING) << "PlatformVerificationFlow: Certificate ready but call has " 306 << "already timed out."; 307 return; 308 } 309 timer->Stop(); 310 if (!operation_success) { 311 ReportError(context.callback, PLATFORM_NOT_VERIFIED); 312 return; 313 } 314 if (IsExpired(certificate)) { 315 GetCertificate(context, user_id, true /* Force a new key */); 316 return; 317 } 318 cryptohome::AsyncMethodCaller::DataCallback cryptohome_callback = base::Bind( 319 &PlatformVerificationFlow::OnChallengeReady, 320 this, 321 context, 322 certificate); 323 std::string key_name = kContentProtectionKeyPrefix; 324 key_name += context.service_id; 325 async_caller_->TpmAttestationSignSimpleChallenge(KEY_USER, 326 user_id, 327 key_name, 328 context.challenge, 329 cryptohome_callback); 330 } 331 332 void PlatformVerificationFlow::OnCertificateTimeout( 333 const ChallengeContext& context) { 334 LOG(WARNING) << "PlatformVerificationFlow: Timing out."; 335 ReportError(context.callback, TIMEOUT); 336 } 337 338 void PlatformVerificationFlow::OnChallengeReady( 339 const ChallengeContext& context, 340 const std::string& certificate, 341 bool operation_success, 342 const std::string& response_data) { 343 if (!operation_success) { 344 LOG(ERROR) << "PlatformVerificationFlow: Failed to sign challenge."; 345 ReportError(context.callback, INTERNAL_ERROR); 346 return; 347 } 348 SignedData signed_data_pb; 349 if (response_data.empty() || !signed_data_pb.ParseFromString(response_data)) { 350 LOG(ERROR) << "PlatformVerificationFlow: Failed to parse response data."; 351 ReportError(context.callback, INTERNAL_ERROR); 352 return; 353 } 354 VLOG(1) << "Platform verification successful."; 355 context.callback.Run(SUCCESS, 356 signed_data_pb.data(), 357 signed_data_pb.signature(), 358 certificate); 359 } 360 361 bool PlatformVerificationFlow::IsAttestationEnabled( 362 content::WebContents* web_contents) { 363 // Check the device policy for the feature. 364 bool enabled_for_device = false; 365 if (!CrosSettings::Get()->GetBoolean(kAttestationForContentProtectionEnabled, 366 &enabled_for_device)) { 367 LOG(ERROR) << "Failed to get device setting."; 368 return false; 369 } 370 if (!enabled_for_device) { 371 VLOG(1) << "Platform verification denied because Verified Access is " 372 << "disabled for the device."; 373 return false; 374 } 375 376 // Check the user preference for the feature. 377 PrefService* pref_service = delegate_->GetPrefs(web_contents); 378 if (!pref_service) { 379 LOG(ERROR) << "Failed to get user prefs."; 380 return false; 381 } 382 if (!pref_service->GetBoolean(prefs::kEnableDRM)) { 383 VLOG(1) << "Platform verification denied because content protection " 384 << "identifiers have been disabled by the user."; 385 return false; 386 } 387 388 // Check the user preference for this domain. 389 bool enabled_for_domain = false; 390 bool found = GetDomainPref(delegate_->GetContentSettings(web_contents), 391 delegate_->GetURL(web_contents), 392 &enabled_for_domain); 393 if (found && !enabled_for_domain) { 394 VLOG(1) << "Platform verification denied because the domain has been " 395 << "blocked by the user."; 396 return false; 397 } 398 return true; 399 } 400 401 bool PlatformVerificationFlow::UpdateSettings( 402 content::WebContents* web_contents, 403 ConsentResponse consent_response) { 404 PrefService* pref_service = delegate_->GetPrefs(web_contents); 405 if (!pref_service) { 406 LOG(ERROR) << "Failed to get user prefs."; 407 return false; 408 } 409 pref_service->SetBoolean(prefs::kRAConsentFirstTime, true); 410 RecordDomainConsent(delegate_->GetContentSettings(web_contents), 411 delegate_->GetURL(web_contents), 412 (consent_response == CONSENT_RESPONSE_ALLOW)); 413 return true; 414 } 415 416 bool PlatformVerificationFlow::GetDomainPref( 417 HostContentSettingsMap* content_settings, 418 const GURL& url, 419 bool* pref_value) { 420 CHECK(content_settings); 421 CHECK(url.is_valid()); 422 ContentSetting setting = content_settings->GetContentSetting( 423 url, 424 url, 425 CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER, 426 std::string()); 427 if (setting != CONTENT_SETTING_ALLOW && setting != CONTENT_SETTING_BLOCK) 428 return false; 429 if (pref_value) 430 *pref_value = (setting == CONTENT_SETTING_ALLOW); 431 return true; 432 } 433 434 void PlatformVerificationFlow::RecordDomainConsent( 435 HostContentSettingsMap* content_settings, 436 const GURL& url, 437 bool allow_domain) { 438 CHECK(content_settings); 439 CHECK(url.is_valid()); 440 // Build a pattern to represent scheme and host. 441 scoped_ptr<ContentSettingsPattern::BuilderInterface> builder( 442 ContentSettingsPattern::CreateBuilder(false)); 443 builder->WithScheme(url.scheme()) 444 ->WithDomainWildcard() 445 ->WithHost(url.host()) 446 ->WithPathWildcard(); 447 if (!url.port().empty()) 448 builder->WithPort(url.port()); 449 else if (url.SchemeIs(url::kHttpsScheme)) 450 builder->WithPort(kDefaultHttpsPort); 451 else if (url.SchemeIs(url::kHttpScheme)) 452 builder->WithPortWildcard(); 453 ContentSettingsPattern pattern = builder->Build(); 454 if (pattern.IsValid()) { 455 ContentSetting setting = allow_domain ? CONTENT_SETTING_ALLOW 456 : CONTENT_SETTING_BLOCK; 457 content_settings->SetContentSetting( 458 pattern, 459 pattern, 460 CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER, 461 std::string(), 462 setting); 463 } else { 464 LOG(WARNING) << "Not recording action: invalid URL pattern"; 465 } 466 } 467 468 bool PlatformVerificationFlow::IsExpired(const std::string& certificate) { 469 scoped_refptr<net::X509Certificate> x509( 470 net::X509Certificate::CreateFromBytes(certificate.data(), 471 certificate.length())); 472 if (!x509.get() || x509->valid_expiry().is_null()) { 473 LOG(WARNING) << "Failed to parse certificate, cannot check expiry."; 474 return false; 475 } 476 return (base::Time::Now() > x509->valid_expiry()); 477 } 478 479 } // namespace attestation 480 } // namespace chromeos 481