1 // Copyright (c) 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 "chrome/browser/chromeos/attestation/attestation_policy_observer.h" 6 7 #include <string> 8 9 #include "base/bind.h" 10 #include "base/callback.h" 11 #include "base/location.h" 12 #include "base/time/time.h" 13 #include "chrome/browser/chrome_notification_types.h" 14 #include "chrome/browser/chromeos/attestation/attestation_ca_client.h" 15 #include "chrome/browser/chromeos/attestation/attestation_key_payload.pb.h" 16 #include "chrome/browser/chromeos/settings/cros_settings.h" 17 #include "chromeos/attestation/attestation_flow.h" 18 #include "chromeos/cryptohome/async_method_caller.h" 19 #include "chromeos/dbus/cryptohome_client.h" 20 #include "chromeos/dbus/dbus_method_call_status.h" 21 #include "chromeos/dbus/dbus_thread_manager.h" 22 #include "components/policy/core/common/cloud/cloud_policy_client.h" 23 #include "components/policy/core/common/cloud/cloud_policy_manager.h" 24 #include "content/public/browser/browser_thread.h" 25 #include "content/public/browser/notification_details.h" 26 #include "net/cert/x509_certificate.h" 27 28 namespace { 29 30 // The number of days before a certificate expires during which it is 31 // considered 'expiring soon' and replacement is initiated. The Chrome OS CA 32 // issues certificates with an expiry of at least two years. This value has 33 // been set large enough so that the majority of users will have gone through 34 // a full sign-in during the period. 35 const int kExpiryThresholdInDays = 30; 36 const int kRetryDelay = 5; // Seconds. 37 const int kRetryLimit = 100; 38 39 // A dbus callback which handles a boolean result. 40 // 41 // Parameters 42 // on_true - Called when status=success and value=true. 43 // on_false - Called when status=success and value=false. 44 // status - The dbus operation status. 45 // value - The value returned by the dbus operation. 46 void DBusBoolRedirectCallback(const base::Closure& on_true, 47 const base::Closure& on_false, 48 const base::Closure& on_failure, 49 const tracked_objects::Location& from_here, 50 chromeos::DBusMethodCallStatus status, 51 bool value) { 52 if (status != chromeos::DBUS_METHOD_CALL_SUCCESS) { 53 LOG(ERROR) << "Cryptohome DBus method failed: " << from_here.ToString() 54 << " - " << status; 55 if (!on_failure.is_null()) 56 on_failure.Run(); 57 return; 58 } 59 const base::Closure& task = value ? on_true : on_false; 60 if (!task.is_null()) 61 task.Run(); 62 } 63 64 // A dbus callback which handles a string result. 65 // 66 // Parameters 67 // on_success - Called when status=success and result=true. 68 // status - The dbus operation status. 69 // result - The result returned by the dbus operation. 70 // data - The data returned by the dbus operation. 71 void DBusStringCallback( 72 const base::Callback<void(const std::string&)> on_success, 73 const base::Closure& on_failure, 74 const tracked_objects::Location& from_here, 75 chromeos::DBusMethodCallStatus status, 76 bool result, 77 const std::string& data) { 78 if (status != chromeos::DBUS_METHOD_CALL_SUCCESS || !result) { 79 LOG(ERROR) << "Cryptohome DBus method failed: " << from_here.ToString() 80 << " - " << status << " - " << result; 81 if (!on_failure.is_null()) 82 on_failure.Run(); 83 return; 84 } 85 on_success.Run(data); 86 } 87 88 } // namespace 89 90 namespace chromeos { 91 namespace attestation { 92 93 AttestationPolicyObserver::AttestationPolicyObserver( 94 policy::CloudPolicyClient* policy_client) 95 : cros_settings_(CrosSettings::Get()), 96 policy_client_(policy_client), 97 cryptohome_client_(NULL), 98 attestation_flow_(NULL), 99 num_retries_(0), 100 retry_delay_(kRetryDelay), 101 weak_factory_(this) { 102 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 103 attestation_subscription_ = cros_settings_->AddSettingsObserver( 104 kDeviceAttestationEnabled, 105 base::Bind(&AttestationPolicyObserver::AttestationSettingChanged, 106 base::Unretained(this))); 107 Start(); 108 } 109 110 AttestationPolicyObserver::AttestationPolicyObserver( 111 policy::CloudPolicyClient* policy_client, 112 CryptohomeClient* cryptohome_client, 113 AttestationFlow* attestation_flow) 114 : cros_settings_(CrosSettings::Get()), 115 policy_client_(policy_client), 116 cryptohome_client_(cryptohome_client), 117 attestation_flow_(attestation_flow), 118 num_retries_(0), 119 retry_delay_(kRetryDelay), 120 weak_factory_(this) { 121 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 122 attestation_subscription_ = cros_settings_->AddSettingsObserver( 123 kDeviceAttestationEnabled, 124 base::Bind(&AttestationPolicyObserver::AttestationSettingChanged, 125 base::Unretained(this))); 126 Start(); 127 } 128 129 AttestationPolicyObserver::~AttestationPolicyObserver() { 130 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 131 } 132 133 void AttestationPolicyObserver::AttestationSettingChanged() { 134 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 135 num_retries_ = 0; 136 Start(); 137 } 138 139 void AttestationPolicyObserver::Start() { 140 // If attestation is not enabled, there is nothing to do. 141 bool enabled = false; 142 if (!cros_settings_->GetBoolean(kDeviceAttestationEnabled, &enabled) || 143 !enabled) 144 return; 145 146 // We expect a registered CloudPolicyClient. 147 if (!policy_client_->is_registered()) { 148 LOG(ERROR) << "AttestationPolicyObserver: Invalid CloudPolicyClient."; 149 return; 150 } 151 152 if (!cryptohome_client_) 153 cryptohome_client_ = DBusThreadManager::Get()->GetCryptohomeClient(); 154 155 if (!attestation_flow_) { 156 scoped_ptr<ServerProxy> attestation_ca_client(new AttestationCAClient()); 157 default_attestation_flow_.reset(new AttestationFlow( 158 cryptohome::AsyncMethodCaller::GetInstance(), 159 cryptohome_client_, 160 attestation_ca_client.Pass())); 161 attestation_flow_ = default_attestation_flow_.get(); 162 } 163 164 // Start a dbus call to check if an Enterprise Machine Key already exists. 165 base::Closure on_does_exist = 166 base::Bind(&AttestationPolicyObserver::GetExistingCertificate, 167 weak_factory_.GetWeakPtr()); 168 base::Closure on_does_not_exist = 169 base::Bind(&AttestationPolicyObserver::GetNewCertificate, 170 weak_factory_.GetWeakPtr()); 171 cryptohome_client_->TpmAttestationDoesKeyExist( 172 KEY_DEVICE, 173 std::string(), // Not used. 174 kEnterpriseMachineKey, 175 base::Bind(DBusBoolRedirectCallback, 176 on_does_exist, 177 on_does_not_exist, 178 base::Bind(&AttestationPolicyObserver::Reschedule, 179 weak_factory_.GetWeakPtr()), 180 FROM_HERE)); 181 } 182 183 void AttestationPolicyObserver::GetNewCertificate() { 184 // We can reuse the dbus callback handler logic. 185 attestation_flow_->GetCertificate( 186 PROFILE_ENTERPRISE_MACHINE_CERTIFICATE, 187 std::string(), // Not used. 188 std::string(), // Not used. 189 true, // Force a new key to be generated. 190 base::Bind(DBusStringCallback, 191 base::Bind(&AttestationPolicyObserver::UploadCertificate, 192 weak_factory_.GetWeakPtr()), 193 base::Bind(&AttestationPolicyObserver::Reschedule, 194 weak_factory_.GetWeakPtr()), 195 FROM_HERE, 196 DBUS_METHOD_CALL_SUCCESS)); 197 } 198 199 void AttestationPolicyObserver::GetExistingCertificate() { 200 cryptohome_client_->TpmAttestationGetCertificate( 201 KEY_DEVICE, 202 std::string(), // Not used. 203 kEnterpriseMachineKey, 204 base::Bind(DBusStringCallback, 205 base::Bind(&AttestationPolicyObserver::CheckCertificateExpiry, 206 weak_factory_.GetWeakPtr()), 207 base::Bind(&AttestationPolicyObserver::Reschedule, 208 weak_factory_.GetWeakPtr()), 209 FROM_HERE)); 210 } 211 212 void AttestationPolicyObserver::CheckCertificateExpiry( 213 const std::string& certificate) { 214 scoped_refptr<net::X509Certificate> x509( 215 net::X509Certificate::CreateFromBytes(certificate.data(), 216 certificate.length())); 217 if (!x509.get() || x509->valid_expiry().is_null()) { 218 LOG(WARNING) << "Failed to parse certificate, cannot check expiry."; 219 } else { 220 const base::TimeDelta threshold = 221 base::TimeDelta::FromDays(kExpiryThresholdInDays); 222 if ((base::Time::Now() + threshold) > x509->valid_expiry()) { 223 // The certificate has expired or will soon, replace it. 224 GetNewCertificate(); 225 return; 226 } 227 } 228 229 // Get the payload and check if the certificate has already been uploaded. 230 GetKeyPayload(base::Bind(&AttestationPolicyObserver::CheckIfUploaded, 231 weak_factory_.GetWeakPtr(), 232 certificate)); 233 } 234 235 void AttestationPolicyObserver::UploadCertificate( 236 const std::string& certificate) { 237 policy_client_->UploadCertificate( 238 certificate, 239 base::Bind(&AttestationPolicyObserver::OnUploadComplete, 240 weak_factory_.GetWeakPtr())); 241 } 242 243 void AttestationPolicyObserver::CheckIfUploaded( 244 const std::string& certificate, 245 const std::string& key_payload) { 246 AttestationKeyPayload payload_pb; 247 if (!key_payload.empty() && 248 payload_pb.ParseFromString(key_payload) && 249 payload_pb.is_certificate_uploaded()) { 250 // Already uploaded... nothing more to do. 251 return; 252 } 253 UploadCertificate(certificate); 254 } 255 256 void AttestationPolicyObserver::GetKeyPayload( 257 base::Callback<void(const std::string&)> callback) { 258 cryptohome_client_->TpmAttestationGetKeyPayload( 259 KEY_DEVICE, 260 std::string(), // Not used. 261 kEnterpriseMachineKey, 262 base::Bind(DBusStringCallback, 263 callback, 264 base::Bind(&AttestationPolicyObserver::Reschedule, 265 weak_factory_.GetWeakPtr()), 266 FROM_HERE)); 267 } 268 269 void AttestationPolicyObserver::OnUploadComplete(bool status) { 270 if (!status) 271 return; 272 VLOG(1) << "Enterprise Machine Certificate uploaded to DMServer."; 273 GetKeyPayload(base::Bind(&AttestationPolicyObserver::MarkAsUploaded, 274 weak_factory_.GetWeakPtr())); 275 } 276 277 void AttestationPolicyObserver::MarkAsUploaded(const std::string& key_payload) { 278 AttestationKeyPayload payload_pb; 279 if (!key_payload.empty()) 280 payload_pb.ParseFromString(key_payload); 281 payload_pb.set_is_certificate_uploaded(true); 282 std::string new_payload; 283 if (!payload_pb.SerializeToString(&new_payload)) { 284 LOG(WARNING) << "Failed to serialize key payload."; 285 return; 286 } 287 cryptohome_client_->TpmAttestationSetKeyPayload( 288 KEY_DEVICE, 289 std::string(), // Not used. 290 kEnterpriseMachineKey, 291 new_payload, 292 base::Bind(DBusBoolRedirectCallback, 293 base::Closure(), 294 base::Closure(), 295 base::Closure(), 296 FROM_HERE)); 297 } 298 299 void AttestationPolicyObserver::Reschedule() { 300 if (++num_retries_ < kRetryLimit) { 301 content::BrowserThread::PostDelayedTask( 302 content::BrowserThread::UI, FROM_HERE, 303 base::Bind(&AttestationPolicyObserver::Start, 304 weak_factory_.GetWeakPtr()), 305 base::TimeDelta::FromSeconds(retry_delay_)); 306 } else { 307 LOG(WARNING) << "AttestationPolicyObserver: Retry limit exceeded."; 308 } 309 } 310 311 } // namespace attestation 312 } // namespace chromeos 313