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