Home | History | Annotate | Download | only in cloud
      1 // Copyright (c) 2012 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/policy/cloud/cloud_policy_validator.h"
      6 
      7 #include "base/bind_helpers.h"
      8 #include "base/message_loop/message_loop.h"
      9 #include "base/stl_util.h"
     10 #include "chrome/browser/policy/cloud/cloud_policy_constants.h"
     11 #include "chrome/browser/policy/proto/cloud/device_management_backend.pb.h"
     12 #include "content/public/browser/browser_thread.h"
     13 #include "crypto/signature_verifier.h"
     14 #include "google_apis/gaia/gaia_auth_util.h"
     15 
     16 namespace em = enterprise_management;
     17 
     18 namespace policy {
     19 
     20 namespace {
     21 
     22 // Grace interval for policy timestamp checks, in seconds.
     23 const int kTimestampGraceIntervalSeconds = 60;
     24 
     25 // DER-encoded ASN.1 object identifier for the SHA1-RSA signature algorithm.
     26 const uint8 kSignatureAlgorithm[] = {
     27     0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
     28     0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00
     29 };
     30 
     31 }  // namespace
     32 
     33 CloudPolicyValidatorBase::~CloudPolicyValidatorBase() {}
     34 
     35 void CloudPolicyValidatorBase::ValidateTimestamp(
     36     base::Time not_before,
     37     base::Time now,
     38     ValidateTimestampOption timestamp_option) {
     39   // Timestamp should be from the past. We allow for a 1-minute grace interval
     40   // to cover clock drift.
     41   validation_flags_ |= VALIDATE_TIMESTAMP;
     42   timestamp_not_before_ =
     43       (not_before - base::Time::UnixEpoch()).InMilliseconds();
     44   timestamp_not_after_ =
     45       ((now + base::TimeDelta::FromSeconds(kTimestampGraceIntervalSeconds)) -
     46           base::Time::UnixEpoch()).InMillisecondsRoundedUp();
     47   timestamp_option_ = timestamp_option;
     48 }
     49 
     50 void CloudPolicyValidatorBase::ValidateUsername(
     51     const std::string& expected_user) {
     52   validation_flags_ |= VALIDATE_USERNAME;
     53   user_ = gaia::CanonicalizeEmail(expected_user);
     54 }
     55 
     56 void CloudPolicyValidatorBase::ValidateDomain(
     57     const std::string& expected_domain) {
     58   validation_flags_ |= VALIDATE_DOMAIN;
     59   domain_ = gaia::CanonicalizeDomain(expected_domain);
     60 }
     61 
     62 void CloudPolicyValidatorBase::ValidateDMToken(
     63     const std::string& token,
     64     ValidateDMTokenOption dm_token_option) {
     65   validation_flags_ |= VALIDATE_TOKEN;
     66   token_ = token;
     67   dm_token_option_ = dm_token_option;
     68 }
     69 
     70 void CloudPolicyValidatorBase::ValidatePolicyType(
     71     const std::string& policy_type) {
     72   validation_flags_ |= VALIDATE_POLICY_TYPE;
     73   policy_type_ = policy_type;
     74 }
     75 
     76 void CloudPolicyValidatorBase::ValidateSettingsEntityId(
     77     const std::string& settings_entity_id) {
     78   validation_flags_ |= VALIDATE_ENTITY_ID;
     79   settings_entity_id_ = settings_entity_id;
     80 }
     81 
     82 void CloudPolicyValidatorBase::ValidatePayload() {
     83   validation_flags_ |= VALIDATE_PAYLOAD;
     84 }
     85 
     86 void CloudPolicyValidatorBase::ValidateSignature(const std::vector<uint8>& key,
     87                                                  bool allow_key_rotation) {
     88   validation_flags_ |= VALIDATE_SIGNATURE;
     89   key_ = std::string(reinterpret_cast<const char*>(vector_as_array(&key)),
     90                      key.size());
     91   allow_key_rotation_ = allow_key_rotation;
     92 }
     93 
     94 void CloudPolicyValidatorBase::ValidateInitialKey() {
     95   validation_flags_ |= VALIDATE_INITIAL_KEY;
     96 }
     97 
     98 void CloudPolicyValidatorBase::ValidateAgainstCurrentPolicy(
     99     const em::PolicyData* policy_data,
    100     ValidateTimestampOption timestamp_option,
    101     ValidateDMTokenOption dm_token_option) {
    102   base::Time last_policy_timestamp;
    103   std::string expected_dm_token;
    104   if (policy_data) {
    105     last_policy_timestamp =
    106         base::Time::UnixEpoch() +
    107         base::TimeDelta::FromMilliseconds(policy_data->timestamp());
    108     expected_dm_token = policy_data->request_token();
    109   }
    110   ValidateTimestamp(last_policy_timestamp, base::Time::NowFromSystemTime(),
    111                     timestamp_option);
    112   ValidateDMToken(expected_dm_token, dm_token_option);
    113 }
    114 
    115 CloudPolicyValidatorBase::CloudPolicyValidatorBase(
    116     scoped_ptr<em::PolicyFetchResponse> policy_response,
    117     google::protobuf::MessageLite* payload)
    118     : status_(VALIDATION_OK),
    119       policy_(policy_response.Pass()),
    120       payload_(payload),
    121       validation_flags_(0),
    122       timestamp_not_before_(0),
    123       timestamp_not_after_(0),
    124       timestamp_option_(TIMESTAMP_REQUIRED),
    125       dm_token_option_(DM_TOKEN_REQUIRED),
    126       allow_key_rotation_(false) {}
    127 
    128 void CloudPolicyValidatorBase::PostValidationTask(
    129     const base::Closure& completion_callback) {
    130   content::BrowserThread::PostTask(
    131       content::BrowserThread::FILE, FROM_HERE,
    132       base::Bind(&CloudPolicyValidatorBase::PerformValidation,
    133                  base::Passed(scoped_ptr<CloudPolicyValidatorBase>(this)),
    134                  base::MessageLoop::current()->message_loop_proxy(),
    135                  completion_callback));
    136 }
    137 
    138 // static
    139 void CloudPolicyValidatorBase::PerformValidation(
    140     scoped_ptr<CloudPolicyValidatorBase> self,
    141     scoped_refptr<base::MessageLoopProxy> message_loop,
    142     const base::Closure& completion_callback) {
    143   // Run the validation activities on this thread.
    144   self->RunValidation();
    145 
    146   // Report completion on |message_loop|.
    147   message_loop->PostTask(
    148       FROM_HERE,
    149       base::Bind(&CloudPolicyValidatorBase::ReportCompletion,
    150                  base::Passed(&self),
    151                  completion_callback));
    152 }
    153 
    154 // static
    155 void CloudPolicyValidatorBase::ReportCompletion(
    156     scoped_ptr<CloudPolicyValidatorBase> self,
    157     const base::Closure& completion_callback) {
    158   completion_callback.Run();
    159 }
    160 
    161 void CloudPolicyValidatorBase::RunValidation() {
    162   policy_data_.reset(new em::PolicyData());
    163   RunChecks();
    164 }
    165 
    166 void CloudPolicyValidatorBase::RunChecks() {
    167   status_ = VALIDATION_OK;
    168   if ((policy_->has_error_code() && policy_->error_code() != 200) ||
    169       (policy_->has_error_message() && !policy_->error_message().empty())) {
    170     LOG(ERROR) << "Error in policy blob."
    171                << " code: " << policy_->error_code()
    172                << " message: " << policy_->error_message();
    173     status_ = VALIDATION_ERROR_CODE_PRESENT;
    174     return;
    175   }
    176 
    177   // Parse policy data.
    178   if (!policy_data_->ParseFromString(policy_->policy_data()) ||
    179       !policy_data_->IsInitialized()) {
    180     LOG(ERROR) << "Failed to parse policy response";
    181     status_ = VALIDATION_PAYLOAD_PARSE_ERROR;
    182     return;
    183   }
    184 
    185   // Table of checks we run. These are sorted by descending severity of the
    186   // error, s.t. the most severe check will determine the validation status.
    187   static const struct {
    188     int flag;
    189     Status (CloudPolicyValidatorBase::* checkFunction)();
    190   } kCheckFunctions[] = {
    191     { VALIDATE_SIGNATURE,   &CloudPolicyValidatorBase::CheckSignature },
    192     { VALIDATE_INITIAL_KEY, &CloudPolicyValidatorBase::CheckInitialKey },
    193     { VALIDATE_POLICY_TYPE, &CloudPolicyValidatorBase::CheckPolicyType },
    194     { VALIDATE_ENTITY_ID,   &CloudPolicyValidatorBase::CheckEntityId },
    195     { VALIDATE_TOKEN,       &CloudPolicyValidatorBase::CheckToken },
    196     { VALIDATE_USERNAME,    &CloudPolicyValidatorBase::CheckUsername },
    197     { VALIDATE_DOMAIN,      &CloudPolicyValidatorBase::CheckDomain },
    198     { VALIDATE_TIMESTAMP,   &CloudPolicyValidatorBase::CheckTimestamp },
    199     { VALIDATE_PAYLOAD,     &CloudPolicyValidatorBase::CheckPayload },
    200   };
    201 
    202   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kCheckFunctions); ++i) {
    203     if (validation_flags_ & kCheckFunctions[i].flag) {
    204       status_ = (this->*(kCheckFunctions[i].checkFunction))();
    205       if (status_ != VALIDATION_OK)
    206         break;
    207     }
    208   }
    209 }
    210 
    211 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckSignature() {
    212   const std::string* signature_key = &key_;
    213   if (policy_->has_new_public_key() && allow_key_rotation_) {
    214     signature_key = &policy_->new_public_key();
    215     if (!policy_->has_new_public_key_signature() ||
    216         !VerifySignature(policy_->new_public_key(), key_,
    217                          policy_->new_public_key_signature())) {
    218       LOG(ERROR) << "New public key signature verification failed";
    219       return VALIDATION_BAD_SIGNATURE;
    220     }
    221   }
    222 
    223   if (!policy_->has_policy_data_signature() ||
    224       !VerifySignature(policy_->policy_data(), *signature_key,
    225                        policy_->policy_data_signature())) {
    226     LOG(ERROR) << "Policy signature validation failed";
    227     return VALIDATION_BAD_SIGNATURE;
    228   }
    229 
    230   return VALIDATION_OK;
    231 }
    232 
    233 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckInitialKey() {
    234   if (!policy_->has_new_public_key() ||
    235       !policy_->has_policy_data_signature() ||
    236       !VerifySignature(policy_->policy_data(), policy_->new_public_key(),
    237                        policy_->policy_data_signature())) {
    238     LOG(ERROR) << "Initial policy signature validation failed";
    239     return VALIDATION_BAD_INITIAL_SIGNATURE;
    240   }
    241 
    242   return VALIDATION_OK;
    243 }
    244 
    245 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckPolicyType() {
    246   if (!policy_data_->has_policy_type() ||
    247        policy_data_->policy_type() != policy_type_) {
    248     LOG(ERROR) << "Wrong policy type " << policy_data_->policy_type();
    249     return VALIDATION_WRONG_POLICY_TYPE;
    250   }
    251 
    252   return VALIDATION_OK;
    253 }
    254 
    255 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckEntityId() {
    256   if (!policy_data_->has_settings_entity_id() ||
    257       policy_data_->settings_entity_id() != settings_entity_id_) {
    258     LOG(ERROR) << "Wrong settings_entity_id "
    259                << policy_data_->settings_entity_id() << ", expected "
    260                << settings_entity_id_;
    261     return VALIDATION_WRONG_SETTINGS_ENTITY_ID;
    262   }
    263 
    264   return VALIDATION_OK;
    265 }
    266 
    267 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckTimestamp() {
    268   if (!policy_data_->has_timestamp()) {
    269     if (timestamp_option_ == TIMESTAMP_NOT_REQUIRED) {
    270       return VALIDATION_OK;
    271     } else {
    272       LOG(ERROR) << "Policy timestamp missing";
    273       return VALIDATION_BAD_TIMESTAMP;
    274     }
    275   }
    276 
    277   if (policy_data_->timestamp() < timestamp_not_before_) {
    278     LOG(ERROR) << "Policy too old: " << policy_data_->timestamp();
    279     return VALIDATION_BAD_TIMESTAMP;
    280   }
    281   if (policy_data_->timestamp() > timestamp_not_after_) {
    282     LOG(ERROR) << "Policy from the future: " << policy_data_->timestamp();
    283     return VALIDATION_BAD_TIMESTAMP;
    284   }
    285 
    286   return VALIDATION_OK;
    287 }
    288 
    289 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckToken() {
    290   // Make sure the token matches the expected token (if any) and also
    291   // make sure the token itself is valid (non-empty if DM_TOKEN_REQUIRED).
    292   if (dm_token_option_ == DM_TOKEN_REQUIRED &&
    293       (!policy_data_->has_request_token() ||
    294        policy_data_->request_token().empty())) {
    295     LOG(ERROR) << "Empty DM token encountered - expected: " << token_;
    296     return VALIDATION_WRONG_TOKEN;
    297   }
    298   if (!token_.empty() && policy_data_->request_token() != token_) {
    299     LOG(ERROR) << "Invalid DM token: " << policy_data_->request_token()
    300                << " - expected: " << token_;
    301     return VALIDATION_WRONG_TOKEN;
    302   }
    303 
    304   return VALIDATION_OK;
    305 }
    306 
    307 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckUsername() {
    308   if (!policy_data_->has_username()) {
    309     LOG(ERROR) << "Policy is missing user name";
    310     return VALIDATION_BAD_USERNAME;
    311   }
    312 
    313   std::string policy_username =
    314       gaia::CanonicalizeEmail(gaia::SanitizeEmail(policy_data_->username()));
    315 
    316   if (user_ != policy_username) {
    317     LOG(ERROR) << "Invalid user name " << policy_data_->username();
    318     return VALIDATION_BAD_USERNAME;
    319   }
    320 
    321   return VALIDATION_OK;
    322 }
    323 
    324 
    325 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckDomain() {
    326   if (!policy_data_->has_username()) {
    327     LOG(ERROR) << "Policy is missing user name";
    328     return VALIDATION_BAD_USERNAME;
    329   }
    330 
    331   std::string policy_domain =
    332       gaia::ExtractDomainName(
    333           gaia::CanonicalizeEmail(
    334               gaia::SanitizeEmail(policy_data_->username())));
    335 
    336   if (domain_ != policy_domain) {
    337     LOG(ERROR) << "Invalid user name " << policy_data_->username();
    338     return VALIDATION_BAD_USERNAME;
    339   }
    340 
    341   return VALIDATION_OK;
    342 }
    343 
    344 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckPayload() {
    345   if (!policy_data_->has_policy_value() ||
    346       !payload_->ParseFromString(policy_data_->policy_value()) ||
    347       !payload_->IsInitialized()) {
    348     LOG(ERROR) << "Failed to decode policy payload protobuf";
    349     return VALIDATION_POLICY_PARSE_ERROR;
    350   }
    351 
    352   return VALIDATION_OK;
    353 }
    354 
    355 // static
    356 bool CloudPolicyValidatorBase::VerifySignature(const std::string& data,
    357                                                const std::string& key,
    358                                                const std::string& signature) {
    359   crypto::SignatureVerifier verifier;
    360 
    361   if (!verifier.VerifyInit(kSignatureAlgorithm, sizeof(kSignatureAlgorithm),
    362                            reinterpret_cast<const uint8*>(signature.c_str()),
    363                            signature.size(),
    364                            reinterpret_cast<const uint8*>(key.c_str()),
    365                            key.size())) {
    366     return false;
    367   }
    368   verifier.VerifyUpdate(reinterpret_cast<const uint8*>(data.c_str()),
    369                         data.size());
    370   return verifier.VerifyFinal();
    371 }
    372 
    373 template class CloudPolicyValidator<em::CloudPolicySettings>;
    374 template class CloudPolicyValidator<em::ExternalPolicyData>;
    375 
    376 }  // namespace policy
    377