Check USB device path access when prompting users to select a device.
[chromium-blink-merge.git] / chrome / browser / chromeos / attestation / platform_verification_flow.cc
blobcb1520859962f06bac7e982aa7773fe106da0138
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.
5 #include "chrome/browser/chromeos/attestation/platform_verification_flow.h"
7 #include "base/command_line.h"
8 #include "base/logging.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/metrics/histogram.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/profiles/profile_helper.h"
16 #include "chrome/browser/chromeos/settings/cros_settings.h"
17 #include "chrome/browser/media/protected_media_identifier_permission_context.h"
18 #include "chrome/browser/media/protected_media_identifier_permission_context_factory.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chromeos/attestation/attestation_flow.h"
21 #include "chromeos/chromeos_switches.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/browser/host_content_settings_map.h"
26 #include "components/content_settings/core/common/content_settings_pattern.h"
27 #include "components/content_settings/core/common/permission_request_id.h"
28 #include "components/user_manager/user.h"
29 #include "content/public/browser/browser_context.h"
30 #include "content/public/browser/browser_thread.h"
31 #include "content/public/browser/render_process_host.h"
32 #include "content/public/browser/render_view_host.h"
33 #include "content/public/browser/user_metrics.h"
34 #include "content/public/browser/web_contents.h"
35 #include "content/public/common/url_constants.h"
36 #include "net/cert/x509_certificate.h"
38 namespace {
40 const int kTimeoutInSeconds = 8;
41 const char kAttestationResultHistogram[] =
42 "ChromeOS.PlatformVerification.Result";
43 const char kAttestationAvailableHistogram[] =
44 "ChromeOS.PlatformVerification.Available";
45 const int kAttestationResultHistogramMax = 10;
47 // A callback method to handle DBus errors.
48 void DBusCallback(const base::Callback<void(bool)>& on_success,
49 const base::Closure& on_failure,
50 chromeos::DBusMethodCallStatus call_status,
51 bool result) {
52 if (call_status == chromeos::DBUS_METHOD_CALL_SUCCESS) {
53 on_success.Run(result);
54 } else {
55 LOG(ERROR) << "PlatformVerificationFlow: DBus call failed!";
56 on_failure.Run();
60 // A helper to call a ChallengeCallback with an error result.
61 void ReportError(
62 const chromeos::attestation::PlatformVerificationFlow::ChallengeCallback&
63 callback,
64 chromeos::attestation::PlatformVerificationFlow::Result error) {
65 UMA_HISTOGRAM_ENUMERATION(kAttestationResultHistogram, error,
66 kAttestationResultHistogramMax);
67 callback.Run(error, std::string(), std::string(), std::string());
69 } // namespace
71 namespace chromeos {
72 namespace attestation {
74 // A default implementation of the Delegate interface.
75 class DefaultDelegate : public PlatformVerificationFlow::Delegate {
76 public:
77 DefaultDelegate() {}
78 ~DefaultDelegate() override {}
80 const GURL& GetURL(content::WebContents* web_contents) override {
81 const GURL& url = web_contents->GetLastCommittedURL();
82 if (!url.is_valid())
83 return web_contents->GetVisibleURL();
84 return url;
87 const user_manager::User* GetUser(
88 content::WebContents* web_contents) override {
89 return ProfileHelper::Get()->GetUserByProfile(
90 Profile::FromBrowserContext(web_contents->GetBrowserContext()));
93 bool IsPermittedByUser(content::WebContents* web_contents) override {
94 ProtectedMediaIdentifierPermissionContext* permission_context =
95 ProtectedMediaIdentifierPermissionContextFactory::GetForProfile(
96 Profile::FromBrowserContext(web_contents->GetBrowserContext()));
98 // TODO(xhwang): Using delegate_->GetURL() here is not right. The platform
99 // verification may be requested by a frame from a different origin. This
100 // will be solved when http://crbug.com/454847 is fixed.
101 const GURL& requesting_origin = GetURL(web_contents).GetOrigin();
103 GURL embedding_origin = web_contents->GetLastCommittedURL().GetOrigin();
105 ContentSetting content_setting = permission_context->GetPermissionStatus(
106 requesting_origin, embedding_origin);
108 return content_setting == CONTENT_SETTING_ALLOW;
111 bool IsInSupportedMode(content::WebContents* web_contents) override {
112 Profile* profile =
113 Profile::FromBrowserContext(web_contents->GetBrowserContext());
114 if (profile->IsOffTheRecord() || profile->IsGuestSession())
115 return false;
117 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
118 return !command_line->HasSwitch(chromeos::switches::kSystemDevMode) ||
119 command_line->HasSwitch(chromeos::switches::kAllowRAInDevMode);
122 private:
123 DISALLOW_COPY_AND_ASSIGN(DefaultDelegate);
126 PlatformVerificationFlow::ChallengeContext::ChallengeContext(
127 content::WebContents* web_contents,
128 const std::string& service_id,
129 const std::string& challenge,
130 const ChallengeCallback& callback)
131 : web_contents(web_contents),
132 service_id(service_id),
133 challenge(challenge),
134 callback(callback) {}
136 PlatformVerificationFlow::ChallengeContext::~ChallengeContext() {}
138 PlatformVerificationFlow::PlatformVerificationFlow()
139 : attestation_flow_(NULL),
140 async_caller_(cryptohome::AsyncMethodCaller::GetInstance()),
141 cryptohome_client_(DBusThreadManager::Get()->GetCryptohomeClient()),
142 delegate_(NULL),
143 timeout_delay_(base::TimeDelta::FromSeconds(kTimeoutInSeconds)) {
144 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
145 scoped_ptr<ServerProxy> attestation_ca_client(new AttestationCAClient());
146 default_attestation_flow_.reset(new AttestationFlow(
147 async_caller_,
148 cryptohome_client_,
149 attestation_ca_client.Pass()));
150 attestation_flow_ = default_attestation_flow_.get();
151 default_delegate_.reset(new DefaultDelegate());
152 delegate_ = default_delegate_.get();
155 PlatformVerificationFlow::PlatformVerificationFlow(
156 AttestationFlow* attestation_flow,
157 cryptohome::AsyncMethodCaller* async_caller,
158 CryptohomeClient* cryptohome_client,
159 Delegate* delegate)
160 : attestation_flow_(attestation_flow),
161 async_caller_(async_caller),
162 cryptohome_client_(cryptohome_client),
163 delegate_(delegate),
164 timeout_delay_(base::TimeDelta::FromSeconds(kTimeoutInSeconds)) {
165 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
166 if (!delegate_) {
167 default_delegate_.reset(new DefaultDelegate());
168 delegate_ = default_delegate_.get();
172 PlatformVerificationFlow::~PlatformVerificationFlow() {
175 void PlatformVerificationFlow::ChallengePlatformKey(
176 content::WebContents* web_contents,
177 const std::string& service_id,
178 const std::string& challenge,
179 const ChallengeCallback& callback) {
180 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
182 if (!delegate_->GetURL(web_contents).is_valid()) {
183 LOG(WARNING) << "PlatformVerificationFlow: Invalid URL.";
184 ReportError(callback, INTERNAL_ERROR);
185 return;
188 // Note: The following two checks are also checked in GetPermissionStatus.
189 // Checking them here explicitly to report the correct error type.
191 if (!IsAttestationAllowedByPolicy()) {
192 VLOG(1) << "Platform verification not allowed by device policy.";
193 ReportError(callback, POLICY_REJECTED);
194 return;
197 // TODO(xhwang): Change to DCHECK when prefixed EME support is removed.
198 // See http://crbug.com/249976
199 if (!delegate_->IsInSupportedMode(web_contents)) {
200 VLOG(1) << "Platform verification denied because it's not supported in the "
201 << "current mode.";
202 ReportError(callback, PLATFORM_NOT_VERIFIED);
203 return;
206 if (!delegate_->IsPermittedByUser(web_contents)) {
207 VLOG(1) << "Platform verification not permitted by user.";
208 ReportError(callback, USER_REJECTED);
209 return;
212 ChallengeContext context(web_contents, service_id, challenge, callback);
213 // Check if the device has been prepared to use attestation.
214 BoolDBusMethodCallback dbus_callback =
215 base::Bind(&DBusCallback,
216 base::Bind(&PlatformVerificationFlow::OnAttestationPrepared,
217 this, context),
218 base::Bind(&ReportError, callback, INTERNAL_ERROR));
219 cryptohome_client_->TpmAttestationIsPrepared(dbus_callback);
222 void PlatformVerificationFlow::OnAttestationPrepared(
223 const ChallengeContext& context,
224 bool attestation_prepared) {
225 UMA_HISTOGRAM_BOOLEAN(kAttestationAvailableHistogram, attestation_prepared);
227 if (!attestation_prepared) {
228 // This device is not currently able to use attestation features.
229 ReportError(context.callback, PLATFORM_NOT_VERIFIED);
230 return;
233 // Permission allowed. Now proceed to get certificate.
234 const user_manager::User* user = delegate_->GetUser(context.web_contents);
235 if (!user) {
236 ReportError(context.callback, INTERNAL_ERROR);
237 LOG(ERROR) << "Profile does not map to a valid user.";
238 return;
241 GetCertificate(context, user->email(), false /* Don't force a new key */);
244 void PlatformVerificationFlow::GetCertificate(const ChallengeContext& context,
245 const std::string& user_id,
246 bool force_new_key) {
247 scoped_ptr<base::Timer> timer(new base::Timer(false, // Don't retain.
248 false)); // Don't repeat.
249 base::Closure timeout_callback = base::Bind(
250 &PlatformVerificationFlow::OnCertificateTimeout,
251 this,
252 context);
253 timer->Start(FROM_HERE, timeout_delay_, timeout_callback);
255 AttestationFlow::CertificateCallback certificate_callback = base::Bind(
256 &PlatformVerificationFlow::OnCertificateReady,
257 this,
258 context,
259 user_id,
260 base::Passed(&timer));
261 attestation_flow_->GetCertificate(
262 PROFILE_CONTENT_PROTECTION_CERTIFICATE,
263 user_id,
264 context.service_id,
265 force_new_key,
266 certificate_callback);
269 void PlatformVerificationFlow::OnCertificateReady(
270 const ChallengeContext& context,
271 const std::string& user_id,
272 scoped_ptr<base::Timer> timer,
273 bool operation_success,
274 const std::string& certificate) {
275 // Log failure before checking the timer so all failures are logged, even if
276 // they took too long.
277 if (!operation_success) {
278 LOG(WARNING) << "PlatformVerificationFlow: Failed to certify platform.";
280 if (!timer->IsRunning()) {
281 LOG(WARNING) << "PlatformVerificationFlow: Certificate ready but call has "
282 << "already timed out.";
283 return;
285 timer->Stop();
286 if (!operation_success) {
287 ReportError(context.callback, PLATFORM_NOT_VERIFIED);
288 return;
290 if (IsExpired(certificate)) {
291 GetCertificate(context, user_id, true /* Force a new key */);
292 return;
294 cryptohome::AsyncMethodCaller::DataCallback cryptohome_callback = base::Bind(
295 &PlatformVerificationFlow::OnChallengeReady,
296 this,
297 context,
298 certificate);
299 std::string key_name = kContentProtectionKeyPrefix;
300 key_name += context.service_id;
301 async_caller_->TpmAttestationSignSimpleChallenge(KEY_USER,
302 user_id,
303 key_name,
304 context.challenge,
305 cryptohome_callback);
308 void PlatformVerificationFlow::OnCertificateTimeout(
309 const ChallengeContext& context) {
310 LOG(WARNING) << "PlatformVerificationFlow: Timing out.";
311 ReportError(context.callback, TIMEOUT);
314 void PlatformVerificationFlow::OnChallengeReady(
315 const ChallengeContext& context,
316 const std::string& certificate,
317 bool operation_success,
318 const std::string& response_data) {
319 if (!operation_success) {
320 LOG(ERROR) << "PlatformVerificationFlow: Failed to sign challenge.";
321 ReportError(context.callback, INTERNAL_ERROR);
322 return;
324 SignedData signed_data_pb;
325 if (response_data.empty() || !signed_data_pb.ParseFromString(response_data)) {
326 LOG(ERROR) << "PlatformVerificationFlow: Failed to parse response data.";
327 ReportError(context.callback, INTERNAL_ERROR);
328 return;
330 VLOG(1) << "Platform verification successful.";
331 UMA_HISTOGRAM_ENUMERATION(kAttestationResultHistogram, SUCCESS,
332 kAttestationResultHistogramMax);
333 context.callback.Run(SUCCESS,
334 signed_data_pb.data(),
335 signed_data_pb.signature(),
336 certificate);
339 bool PlatformVerificationFlow::IsAttestationAllowedByPolicy() {
340 // Check the device policy for the feature.
341 bool enabled_for_device = false;
342 if (!CrosSettings::Get()->GetBoolean(kAttestationForContentProtectionEnabled,
343 &enabled_for_device)) {
344 LOG(ERROR) << "Failed to get device setting.";
345 return false;
347 if (!enabled_for_device) {
348 VLOG(1) << "Platform verification denied because Verified Access is "
349 << "disabled for the device.";
350 return false;
353 return true;
356 bool PlatformVerificationFlow::IsExpired(const std::string& certificate) {
357 scoped_refptr<net::X509Certificate> x509(
358 net::X509Certificate::CreateFromBytes(certificate.data(),
359 certificate.length()));
360 if (!x509.get() || x509->valid_expiry().is_null()) {
361 LOG(WARNING) << "Failed to parse certificate, cannot check expiry.";
362 return false;
364 return (base::Time::Now() > x509->valid_expiry());
367 } // namespace attestation
368 } // namespace chromeos