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/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