Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(378)

Side by Side Diff: chrome/browser/chromeos/attestation/platform_verification_flow.cc

Issue 50093002: Added a timeout for platform verification key generation. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fixed browsertest Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "platform_verification_flow.h" 5 #include "platform_verification_flow.h"
6 6
7 #include "base/command_line.h" 7 #include "base/command_line.h"
8 #include "base/logging.h" 8 #include "base/logging.h"
9 #include "base/message_loop/message_loop.h"
9 #include "base/prefs/pref_service.h" 10 #include "base/prefs/pref_service.h"
10 #include "base/prefs/scoped_user_pref_update.h" 11 #include "base/time/time.h"
12 #include "base/timer/timer.h"
11 #include "chrome/browser/chromeos/attestation/attestation_ca_client.h" 13 #include "chrome/browser/chromeos/attestation/attestation_ca_client.h"
12 #include "chrome/browser/chromeos/attestation/attestation_signed_data.pb.h" 14 #include "chrome/browser/chromeos/attestation/attestation_signed_data.pb.h"
13 #include "chrome/browser/chromeos/attestation/platform_verification_dialog.h" 15 #include "chrome/browser/chromeos/attestation/platform_verification_dialog.h"
14 #include "chrome/browser/chromeos/login/user.h" 16 #include "chrome/browser/chromeos/login/user.h"
15 #include "chrome/browser/chromeos/login/user_manager.h" 17 #include "chrome/browser/chromeos/login/user_manager.h"
16 #include "chrome/browser/chromeos/settings/cros_settings.h" 18 #include "chrome/browser/chromeos/settings/cros_settings.h"
17 #include "chrome/browser/content_settings/host_content_settings_map.h" 19 #include "chrome/browser/content_settings/host_content_settings_map.h"
18 #include "chrome/browser/profiles/profile.h" 20 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/common/content_settings_pattern.h" 21 #include "chrome/common/content_settings_pattern.h"
20 #include "chrome/common/pref_names.h" 22 #include "chrome/common/pref_names.h"
21 #include "chromeos/attestation/attestation_flow.h" 23 #include "chromeos/attestation/attestation_flow.h"
22 #include "chromeos/cryptohome/async_method_caller.h" 24 #include "chromeos/cryptohome/async_method_caller.h"
23 #include "chromeos/dbus/cryptohome_client.h" 25 #include "chromeos/dbus/cryptohome_client.h"
24 #include "chromeos/dbus/dbus_thread_manager.h" 26 #include "chromeos/dbus/dbus_thread_manager.h"
25 #include "components/user_prefs/pref_registry_syncable.h" 27 #include "components/user_prefs/pref_registry_syncable.h"
26 #include "components/user_prefs/user_prefs.h" 28 #include "components/user_prefs/user_prefs.h"
27 #include "content/public/browser/browser_context.h" 29 #include "content/public/browser/browser_context.h"
28 #include "content/public/browser/browser_thread.h" 30 #include "content/public/browser/browser_thread.h"
29 #include "content/public/browser/user_metrics.h" 31 #include "content/public/browser/user_metrics.h"
30 #include "content/public/browser/web_contents.h" 32 #include "content/public/browser/web_contents.h"
31 #include "content/public/common/url_constants.h" 33 #include "content/public/common/url_constants.h"
32 34
33 namespace { 35 namespace {
34 36
35 const char kDefaultHttpsPort[] = "443"; 37 const char kDefaultHttpsPort[] = "443";
38 const int kTimeoutInSeconds = 8;
36 39
37 // A callback method to handle DBus errors. 40 // A callback method to handle DBus errors.
38 void DBusCallback(const base::Callback<void(bool)>& on_success, 41 void DBusCallback(const base::Callback<void(bool)>& on_success,
39 const base::Closure& on_failure, 42 const base::Closure& on_failure,
40 chromeos::DBusMethodCallStatus call_status, 43 chromeos::DBusMethodCallStatus call_status,
41 bool result) { 44 bool result) {
42 if (call_status == chromeos::DBUS_METHOD_CALL_SUCCESS) { 45 if (call_status == chromeos::DBUS_METHOD_CALL_SUCCESS) {
43 on_success.Run(result); 46 on_success.Run(result);
44 } else { 47 } else {
45 LOG(ERROR) << "PlatformVerificationFlow: DBus call failed!"; 48 LOG(ERROR) << "PlatformVerificationFlow: DBus call failed!";
(...skipping 23 matching lines...) Expand all
69 content::WebContents* web_contents, 72 content::WebContents* web_contents,
70 const PlatformVerificationFlow::Delegate::ConsentCallback& callback) 73 const PlatformVerificationFlow::Delegate::ConsentCallback& callback)
71 OVERRIDE { 74 OVERRIDE {
72 PlatformVerificationDialog::ShowDialog(web_contents, callback); 75 PlatformVerificationDialog::ShowDialog(web_contents, callback);
73 } 76 }
74 77
75 private: 78 private:
76 DISALLOW_COPY_AND_ASSIGN(DefaultDelegate); 79 DISALLOW_COPY_AND_ASSIGN(DefaultDelegate);
77 }; 80 };
78 81
82 PlatformVerificationFlow::ChallengeContext::ChallengeContext(
83 content::WebContents* web_contents,
84 const std::string& service_id,
85 const std::string& challenge,
86 const ChallengeCallback& callback)
87 : web_contents(web_contents),
88 service_id(service_id),
89 challenge(challenge),
90 callback(callback) {}
91
92 PlatformVerificationFlow::ChallengeContext::~ChallengeContext() {}
93
79 PlatformVerificationFlow::PlatformVerificationFlow() 94 PlatformVerificationFlow::PlatformVerificationFlow()
80 : attestation_flow_(NULL), 95 : attestation_flow_(NULL),
81 async_caller_(cryptohome::AsyncMethodCaller::GetInstance()), 96 async_caller_(cryptohome::AsyncMethodCaller::GetInstance()),
82 cryptohome_client_(DBusThreadManager::Get()->GetCryptohomeClient()), 97 cryptohome_client_(DBusThreadManager::Get()->GetCryptohomeClient()),
83 user_manager_(UserManager::Get()), 98 user_manager_(UserManager::Get()),
84 delegate_(NULL), 99 delegate_(NULL),
85 testing_prefs_(NULL), 100 testing_prefs_(NULL),
86 testing_content_settings_(NULL), 101 testing_content_settings_(NULL),
87 weak_factory_(this) { 102 timeout_delay_(base::TimeDelta::FromSeconds(kTimeoutInSeconds)) {
88 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 103 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
89 scoped_ptr<ServerProxy> attestation_ca_client(new AttestationCAClient()); 104 scoped_ptr<ServerProxy> attestation_ca_client(new AttestationCAClient());
90 default_attestation_flow_.reset(new AttestationFlow( 105 default_attestation_flow_.reset(new AttestationFlow(
91 async_caller_, 106 async_caller_,
92 cryptohome_client_, 107 cryptohome_client_,
93 attestation_ca_client.Pass())); 108 attestation_ca_client.Pass()));
94 attestation_flow_ = default_attestation_flow_.get(); 109 attestation_flow_ = default_attestation_flow_.get();
95 default_delegate_.reset(new DefaultDelegate()); 110 default_delegate_.reset(new DefaultDelegate());
96 delegate_ = default_delegate_.get(); 111 delegate_ = default_delegate_.get();
97 } 112 }
98 113
99 PlatformVerificationFlow::PlatformVerificationFlow( 114 PlatformVerificationFlow::PlatformVerificationFlow(
100 AttestationFlow* attestation_flow, 115 AttestationFlow* attestation_flow,
101 cryptohome::AsyncMethodCaller* async_caller, 116 cryptohome::AsyncMethodCaller* async_caller,
102 CryptohomeClient* cryptohome_client, 117 CryptohomeClient* cryptohome_client,
103 UserManager* user_manager, 118 UserManager* user_manager,
104 Delegate* delegate) 119 Delegate* delegate)
105 : attestation_flow_(attestation_flow), 120 : attestation_flow_(attestation_flow),
106 async_caller_(async_caller), 121 async_caller_(async_caller),
107 cryptohome_client_(cryptohome_client), 122 cryptohome_client_(cryptohome_client),
108 user_manager_(user_manager), 123 user_manager_(user_manager),
109 delegate_(delegate), 124 delegate_(delegate),
110 testing_prefs_(NULL), 125 testing_prefs_(NULL),
111 testing_content_settings_(NULL), 126 testing_content_settings_(NULL),
112 weak_factory_(this) { 127 timeout_delay_(base::TimeDelta::FromSeconds(kTimeoutInSeconds)) {
113 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 128 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
114 } 129 }
115 130
116 PlatformVerificationFlow::~PlatformVerificationFlow() { 131 PlatformVerificationFlow::~PlatformVerificationFlow() {
117 } 132 }
118 133
119 void PlatformVerificationFlow::ChallengePlatformKey( 134 void PlatformVerificationFlow::ChallengePlatformKey(
120 content::WebContents* web_contents, 135 content::WebContents* web_contents,
121 const std::string& service_id, 136 const std::string& service_id,
122 const std::string& challenge, 137 const std::string& challenge,
123 const ChallengeCallback& callback) { 138 const ChallengeCallback& callback) {
124 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 139 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
125 if (!GetURL(web_contents).is_valid()) { 140 if (!GetURL(web_contents).is_valid()) {
126 LOG(WARNING) << "PlatformVerificationFlow: Invalid URL."; 141 LOG(WARNING) << "PlatformVerificationFlow: Invalid URL.";
127 ReportError(callback, INTERNAL_ERROR); 142 ReportError(callback, INTERNAL_ERROR);
128 return; 143 return;
129 } 144 }
130 if (!IsAttestationEnabled(web_contents)) { 145 if (!IsAttestationEnabled(web_contents)) {
131 LOG(INFO) << "PlatformVerificationFlow: Feature disabled."; 146 LOG(INFO) << "PlatformVerificationFlow: Feature disabled.";
132 ReportError(callback, POLICY_REJECTED); 147 ReportError(callback, POLICY_REJECTED);
133 return; 148 return;
134 } 149 }
150 ChallengeContext context(web_contents, service_id, challenge, callback);
135 BoolDBusMethodCallback dbus_callback = base::Bind( 151 BoolDBusMethodCallback dbus_callback = base::Bind(
136 &DBusCallback, 152 &DBusCallback,
137 base::Bind(&PlatformVerificationFlow::CheckConsent, 153 base::Bind(&PlatformVerificationFlow::CheckConsent, this, context),
138 weak_factory_.GetWeakPtr(),
139 web_contents,
140 service_id,
141 challenge,
142 callback),
143 base::Bind(&ReportError, callback, INTERNAL_ERROR)); 154 base::Bind(&ReportError, callback, INTERNAL_ERROR));
144 cryptohome_client_->TpmAttestationIsEnrolled(dbus_callback); 155 cryptohome_client_->TpmAttestationIsEnrolled(dbus_callback);
145 } 156 }
146 157
147 void PlatformVerificationFlow::CheckConsent(content::WebContents* web_contents, 158 void PlatformVerificationFlow::CheckConsent(const ChallengeContext& context,
148 const std::string& service_id,
149 const std::string& challenge,
150 const ChallengeCallback& callback,
151 bool attestation_enrolled) { 159 bool attestation_enrolled) {
152 PrefService* pref_service = GetPrefs(web_contents); 160 PrefService* pref_service = GetPrefs(context.web_contents);
153 if (!pref_service) { 161 if (!pref_service) {
154 LOG(ERROR) << "Failed to get user prefs."; 162 LOG(ERROR) << "Failed to get user prefs.";
155 ReportError(callback, INTERNAL_ERROR); 163 ReportError(context.callback, INTERNAL_ERROR);
156 return; 164 return;
157 } 165 }
158 bool consent_required = ( 166 bool consent_required = (
159 // Consent required if attestation has never been enrolled on this device. 167 // Consent required if attestation has never been enrolled on this device.
160 !attestation_enrolled || 168 !attestation_enrolled ||
161 // Consent required if this is the first use of attestation for content 169 // Consent required if this is the first use of attestation for content
162 // protection on this device. 170 // protection on this device.
163 !pref_service->GetBoolean(prefs::kRAConsentFirstTime) || 171 !pref_service->GetBoolean(prefs::kRAConsentFirstTime) ||
164 // Consent required if consent has never been given for this domain. 172 // Consent required if consent has never been given for this domain.
165 !GetDomainPref(GetContentSettings(web_contents), 173 !GetDomainPref(GetContentSettings(context.web_contents),
166 GetURL(web_contents), 174 GetURL(context.web_contents),
167 NULL)); 175 NULL));
168 176
169 Delegate::ConsentCallback consent_callback = base::Bind( 177 Delegate::ConsentCallback consent_callback = base::Bind(
170 &PlatformVerificationFlow::OnConsentResponse, 178 &PlatformVerificationFlow::OnConsentResponse,
171 weak_factory_.GetWeakPtr(), 179 this,
172 web_contents, 180 context,
173 service_id,
174 challenge,
175 callback,
176 consent_required); 181 consent_required);
177 if (consent_required) 182 if (consent_required)
178 delegate_->ShowConsentPrompt(web_contents, consent_callback); 183 delegate_->ShowConsentPrompt(context.web_contents, consent_callback);
179 else 184 else
180 consent_callback.Run(CONSENT_RESPONSE_NONE); 185 consent_callback.Run(CONSENT_RESPONSE_NONE);
181 } 186 }
182 187
183 void PlatformVerificationFlow::RegisterProfilePrefs( 188 void PlatformVerificationFlow::RegisterProfilePrefs(
184 user_prefs::PrefRegistrySyncable* prefs) { 189 user_prefs::PrefRegistrySyncable* prefs) {
185 prefs->RegisterBooleanPref(prefs::kRAConsentFirstTime, 190 prefs->RegisterBooleanPref(prefs::kRAConsentFirstTime,
186 false, 191 false,
187 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 192 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
188 } 193 }
189 194
190 void PlatformVerificationFlow::OnConsentResponse( 195 void PlatformVerificationFlow::OnConsentResponse(
191 content::WebContents* web_contents, 196 const ChallengeContext& context,
192 const std::string& service_id,
193 const std::string& challenge,
194 const ChallengeCallback& callback,
195 bool consent_required, 197 bool consent_required,
196 ConsentResponse consent_response) { 198 ConsentResponse consent_response) {
197 if (consent_required) { 199 if (consent_required) {
198 if (consent_response == CONSENT_RESPONSE_NONE) { 200 if (consent_response == CONSENT_RESPONSE_NONE) {
199 // No user response - do not proceed and do not modify any settings. 201 // No user response - do not proceed and do not modify any settings.
200 LOG(WARNING) << "PlatformVerificationFlow: No response from user."; 202 LOG(WARNING) << "PlatformVerificationFlow: No response from user.";
201 ReportError(callback, USER_REJECTED); 203 ReportError(context.callback, USER_REJECTED);
202 return; 204 return;
203 } 205 }
204 if (!UpdateSettings(web_contents, consent_response)) { 206 if (!UpdateSettings(context.web_contents, consent_response)) {
205 ReportError(callback, INTERNAL_ERROR); 207 ReportError(context.callback, INTERNAL_ERROR);
206 return; 208 return;
207 } 209 }
208 if (consent_response == CONSENT_RESPONSE_DENY) { 210 if (consent_response == CONSENT_RESPONSE_DENY) {
209 LOG(INFO) << "PlatformVerificationFlow: User rejected request."; 211 LOG(INFO) << "PlatformVerificationFlow: User rejected request.";
210 content::RecordAction( 212 content::RecordAction(
211 content::UserMetricsAction("PlatformVerificationRejected")); 213 content::UserMetricsAction("PlatformVerificationRejected"));
212 ReportError(callback, USER_REJECTED); 214 ReportError(context.callback, USER_REJECTED);
213 return; 215 return;
214 } else if (consent_response == CONSENT_RESPONSE_ALLOW) { 216 } else if (consent_response == CONSENT_RESPONSE_ALLOW) {
215 content::RecordAction( 217 content::RecordAction(
216 content::UserMetricsAction("PlatformVerificationAccepted")); 218 content::UserMetricsAction("PlatformVerificationAccepted"));
217 } 219 }
218 } 220 }
219 221
220 // At this point all user interaction is complete and we can proceed with the 222 // At this point all user interaction is complete and we can proceed with the
221 // certificate request. 223 // certificate request.
222 chromeos::User* user = GetUser(web_contents); 224 chromeos::User* user = GetUser(context.web_contents);
223 if (!user) { 225 if (!user) {
224 ReportError(callback, INTERNAL_ERROR); 226 ReportError(context.callback, INTERNAL_ERROR);
225 LOG(ERROR) << "Profile does not map to a valid user."; 227 LOG(ERROR) << "Profile does not map to a valid user.";
226 return; 228 return;
227 } 229 }
230
231 scoped_ptr<base::Timer> timer(new base::Timer(false, // Don't retain.
232 false)); // Don't repeat.
233 base::Closure timeout_callback = base::Bind(
234 &PlatformVerificationFlow::OnCertificateTimeout,
235 this,
236 context);
237 timer->Start(FROM_HERE, timeout_delay_, timeout_callback);
238
228 AttestationFlow::CertificateCallback certificate_callback = base::Bind( 239 AttestationFlow::CertificateCallback certificate_callback = base::Bind(
229 &PlatformVerificationFlow::OnCertificateReady, 240 &PlatformVerificationFlow::OnCertificateReady,
230 weak_factory_.GetWeakPtr(), 241 this,
242 context,
231 user->email(), 243 user->email(),
232 service_id, 244 base::Passed(&timer));
233 challenge,
234 callback);
235 attestation_flow_->GetCertificate( 245 attestation_flow_->GetCertificate(
236 PROFILE_CONTENT_PROTECTION_CERTIFICATE, 246 PROFILE_CONTENT_PROTECTION_CERTIFICATE,
237 user->email(), 247 user->email(),
238 service_id, 248 context.service_id,
239 false, // Don't force a new key. 249 false, // Don't force a new key.
240 certificate_callback); 250 certificate_callback);
241 } 251 }
242 252
243 void PlatformVerificationFlow::OnCertificateReady( 253 void PlatformVerificationFlow::OnCertificateReady(
254 const ChallengeContext& context,
244 const std::string& user_id, 255 const std::string& user_id,
245 const std::string& service_id, 256 scoped_ptr<base::Timer> timer,
246 const std::string& challenge,
247 const ChallengeCallback& callback,
248 bool operation_success, 257 bool operation_success,
249 const std::string& certificate) { 258 const std::string& certificate) {
259 // Log failure before checking the timer so all failures are logged, even if
260 // they took too long.
250 if (!operation_success) { 261 if (!operation_success) {
251 LOG(WARNING) << "PlatformVerificationFlow: Failed to certify platform."; 262 LOG(WARNING) << "PlatformVerificationFlow: Failed to certify platform.";
252 ReportError(callback, PLATFORM_NOT_VERIFIED); 263 }
264 if (!timer->IsRunning()) {
265 LOG(WARNING) << "PlatformVerificationFlow: Certificate ready but call has "
266 << "already timed out.";
267 return;
268 }
269 timer->Stop();
270 if (!operation_success) {
271 ReportError(context.callback, PLATFORM_NOT_VERIFIED);
253 return; 272 return;
254 } 273 }
255 cryptohome::AsyncMethodCaller::DataCallback cryptohome_callback = base::Bind( 274 cryptohome::AsyncMethodCaller::DataCallback cryptohome_callback = base::Bind(
256 &PlatformVerificationFlow::OnChallengeReady, 275 &PlatformVerificationFlow::OnChallengeReady,
257 weak_factory_.GetWeakPtr(), 276 this,
258 certificate, 277 context,
259 challenge, 278 certificate);
260 callback);
261 std::string key_name = kContentProtectionKeyPrefix; 279 std::string key_name = kContentProtectionKeyPrefix;
262 key_name += service_id; 280 key_name += context.service_id;
263 async_caller_->TpmAttestationSignSimpleChallenge(KEY_USER, 281 async_caller_->TpmAttestationSignSimpleChallenge(KEY_USER,
264 user_id, 282 user_id,
265 key_name, 283 key_name,
266 challenge, 284 context.challenge,
267 cryptohome_callback); 285 cryptohome_callback);
268 } 286 }
269 287
288 void PlatformVerificationFlow::OnCertificateTimeout(
289 const ChallengeContext& context) {
290 LOG(WARNING) << "PlatformVerificationFlow: Timing out.";
291 ReportError(context.callback, TIMEOUT);
292 }
293
270 void PlatformVerificationFlow::OnChallengeReady( 294 void PlatformVerificationFlow::OnChallengeReady(
295 const ChallengeContext& context,
271 const std::string& certificate, 296 const std::string& certificate,
272 const std::string& challenge,
273 const ChallengeCallback& callback,
274 bool operation_success, 297 bool operation_success,
275 const std::string& response_data) { 298 const std::string& response_data) {
276 if (!operation_success) { 299 if (!operation_success) {
277 LOG(ERROR) << "PlatformVerificationFlow: Failed to sign challenge."; 300 LOG(ERROR) << "PlatformVerificationFlow: Failed to sign challenge.";
278 ReportError(callback, INTERNAL_ERROR); 301 ReportError(context.callback, INTERNAL_ERROR);
279 return; 302 return;
280 } 303 }
281 SignedData signed_data_pb; 304 SignedData signed_data_pb;
282 if (response_data.empty() || !signed_data_pb.ParseFromString(response_data)) { 305 if (response_data.empty() || !signed_data_pb.ParseFromString(response_data)) {
283 LOG(ERROR) << "PlatformVerificationFlow: Failed to parse response data."; 306 LOG(ERROR) << "PlatformVerificationFlow: Failed to parse response data.";
284 ReportError(callback, INTERNAL_ERROR); 307 ReportError(context.callback, INTERNAL_ERROR);
285 return; 308 return;
286 } 309 }
287 callback.Run(SUCCESS, 310 context.callback.Run(SUCCESS,
288 signed_data_pb.data(), 311 signed_data_pb.data(),
289 signed_data_pb.signature(), 312 signed_data_pb.signature(),
290 certificate); 313 certificate);
291 LOG(INFO) << "PlatformVerificationFlow: Platform successfully verified."; 314 LOG(INFO) << "PlatformVerificationFlow: Platform successfully verified.";
292 } 315 }
293 316
294 PrefService* PlatformVerificationFlow::GetPrefs( 317 PrefService* PlatformVerificationFlow::GetPrefs(
295 content::WebContents* web_contents) { 318 content::WebContents* web_contents) {
296 if (testing_prefs_) 319 if (testing_prefs_)
297 return testing_prefs_; 320 return testing_prefs_;
298 return user_prefs::UserPrefs::Get(web_contents->GetBrowserContext()); 321 return user_prefs::UserPrefs::Get(web_contents->GetBrowserContext());
299 } 322 }
300 323
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after
414 CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER, 437 CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER,
415 std::string(), 438 std::string(),
416 setting); 439 setting);
417 } else { 440 } else {
418 LOG(WARNING) << "Not recording action: invalid URL pattern"; 441 LOG(WARNING) << "Not recording action: invalid URL pattern";
419 } 442 }
420 } 443 }
421 444
422 } // namespace attestation 445 } // namespace attestation
423 } // namespace chromeos 446 } // namespace chromeos
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698