Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / attestation / platform_verification_flow.cc
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