• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
DBusBoolRedirectCallback(const base::Closure & on_true,const base::Closure & on_false,const base::Closure & on_failure,const tracked_objects::Location & from_here,chromeos::DBusMethodCallStatus status,bool value)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.
DBusStringCallback(const base::Callback<void (const std::string &)> on_success,const base::Closure & on_failure,const tracked_objects::Location & from_here,chromeos::DBusMethodCallStatus status,bool result,const std::string & data)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 
AttestationPolicyObserver(policy::CloudPolicyClient * policy_client)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 
AttestationPolicyObserver(policy::CloudPolicyClient * policy_client,CryptohomeClient * cryptohome_client,AttestationFlow * attestation_flow)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 
~AttestationPolicyObserver()129 AttestationPolicyObserver::~AttestationPolicyObserver() {
130   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
131 }
132 
AttestationSettingChanged()133 void AttestationPolicyObserver::AttestationSettingChanged() {
134   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
135   num_retries_ = 0;
136   Start();
137 }
138 
Start()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 
GetNewCertificate()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 
GetExistingCertificate()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 
CheckCertificateExpiry(const std::string & certificate)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 
UploadCertificate(const std::string & certificate)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 
CheckIfUploaded(const std::string & certificate,const std::string & key_payload)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 
GetKeyPayload(base::Callback<void (const std::string &)> callback)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 
OnUploadComplete(bool status)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 
MarkAsUploaded(const std::string & key_payload)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 
Reschedule()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