Home | History | Annotate | Download | only in attestation
      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