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.
5 #include "chrome/browser/chromeos/attestation/attestation_policy_observer.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"
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;
39 // A dbus callback which handles a boolean result.
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
,
52 if (status
!= chromeos::DBUS_METHOD_CALL_SUCCESS
) {
53 LOG(ERROR
) << "Cryptohome DBus method failed: " << from_here
.ToString()
55 if (!on_failure
.is_null())
59 const base::Closure
& task
= value
? on_true
: on_false
;
64 // A dbus callback which handles a string result.
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
,
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())
91 namespace attestation
{
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
),
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)));
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
),
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)));
129 AttestationPolicyObserver::~AttestationPolicyObserver() {
130 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
133 void AttestationPolicyObserver::AttestationSettingChanged() {
134 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
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
) ||
146 // We expect a registered CloudPolicyClient.
147 if (!policy_client_
->is_registered()) {
148 LOG(ERROR
) << "AttestationPolicyObserver: Invalid CloudPolicyClient.";
152 if (!cryptohome_client_
)
153 cryptohome_client_
= DBusThreadManager::Get()->GetCryptohomeClient();
155 if (!attestation_flow_
) {
156 scoped_ptr
<ServerProxy
> attestation_ca_client(new AttestationCAClient());
157 default_attestation_flow_
.reset(new AttestationFlow(
158 cryptohome::AsyncMethodCaller::GetInstance(),
160 attestation_ca_client
.Pass()));
161 attestation_flow_
= default_attestation_flow_
.get();
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(
173 std::string(), // Not used.
174 kEnterpriseMachineKey
,
175 base::Bind(DBusBoolRedirectCallback
,
178 base::Bind(&AttestationPolicyObserver::Reschedule
,
179 weak_factory_
.GetWeakPtr()),
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()),
196 DBUS_METHOD_CALL_SUCCESS
));
199 void AttestationPolicyObserver::GetExistingCertificate() {
200 cryptohome_client_
->TpmAttestationGetCertificate(
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()),
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.";
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.
229 // Get the payload and check if the certificate has already been uploaded.
230 GetKeyPayload(base::Bind(&AttestationPolicyObserver::CheckIfUploaded
,
231 weak_factory_
.GetWeakPtr(),
235 void AttestationPolicyObserver::UploadCertificate(
236 const std::string
& certificate
) {
237 policy_client_
->UploadCertificate(
239 base::Bind(&AttestationPolicyObserver::OnUploadComplete
,
240 weak_factory_
.GetWeakPtr()));
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.
253 UploadCertificate(certificate
);
256 void AttestationPolicyObserver::GetKeyPayload(
257 base::Callback
<void(const std::string
&)> callback
) {
258 cryptohome_client_
->TpmAttestationGetKeyPayload(
260 std::string(), // Not used.
261 kEnterpriseMachineKey
,
262 base::Bind(DBusStringCallback
,
264 base::Bind(&AttestationPolicyObserver::Reschedule
,
265 weak_factory_
.GetWeakPtr()),
269 void AttestationPolicyObserver::OnUploadComplete(bool status
) {
272 VLOG(1) << "Enterprise Machine Certificate uploaded to DMServer.";
273 GetKeyPayload(base::Bind(&AttestationPolicyObserver::MarkAsUploaded
,
274 weak_factory_
.GetWeakPtr()));
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.";
287 cryptohome_client_
->TpmAttestationSetKeyPayload(
289 std::string(), // Not used.
290 kEnterpriseMachineKey
,
292 base::Bind(DBusBoolRedirectCallback
,
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_
));
307 LOG(WARNING
) << "AttestationPolicyObserver: Retry limit exceeded.";
311 } // namespace attestation
312 } // namespace chromeos