Home | History | Annotate | Download | only in attestation
      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