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 "components/policy/core/common/cloud/cloud_policy_validator.h"
      6 
      7 #include "base/bind_helpers.h"
      8 #include "base/message_loop/message_loop.h"
      9 #include "base/sequenced_task_runner.h"
     10 #include "base/stl_util.h"
     11 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
     12 #include "crypto/signature_verifier.h"
     13 #include "google_apis/gaia/gaia_auth_util.h"
     14 #include "policy/proto/device_management_backend.pb.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     scoped_refptr<base::SequencedTaskRunner> background_task_runner)
    119     : status_(VALIDATION_OK),
    120       policy_(policy_response.Pass()),
    121       payload_(payload),
    122       validation_flags_(0),
    123       timestamp_not_before_(0),
    124       timestamp_not_after_(0),
    125       timestamp_option_(TIMESTAMP_REQUIRED),
    126       dm_token_option_(DM_TOKEN_REQUIRED),
    127       allow_key_rotation_(false),
    128       background_task_runner_(background_task_runner) {}
    129 
    130 void CloudPolicyValidatorBase::PostValidationTask(
    131     const base::Closure& completion_callback) {
    132   background_task_runner_->PostTask(
    133       FROM_HERE,
    134       base::Bind(&CloudPolicyValidatorBase::PerformValidation,
    135                  base::Passed(scoped_ptr<CloudPolicyValidatorBase>(this)),
    136                  base::MessageLoop::current()->message_loop_proxy(),
    137                  completion_callback));
    138 }
    139 
    140 // static
    141 void CloudPolicyValidatorBase::PerformValidation(
    142     scoped_ptr<CloudPolicyValidatorBase> self,
    143     scoped_refptr<base::MessageLoopProxy> message_loop,
    144     const base::Closure& completion_callback) {
    145   // Run the validation activities on this thread.
    146   self->RunValidation();
    147 
    148   // Report completion on |message_loop|.
    149   message_loop->PostTask(
    150       FROM_HERE,
    151       base::Bind(&CloudPolicyValidatorBase::ReportCompletion,
    152                  base::Passed(&self),
    153                  completion_callback));
    154 }
    155 
    156 // static
    157 void CloudPolicyValidatorBase::ReportCompletion(
    158     scoped_ptr<CloudPolicyValidatorBase> self,
    159     const base::Closure& completion_callback) {
    160   completion_callback.Run();
    161 }
    162 
    163 void CloudPolicyValidatorBase::RunValidation() {
    164   policy_data_.reset(new em::PolicyData());
    165   RunChecks();
    166 }
    167 
    168 void CloudPolicyValidatorBase::RunChecks() {
    169   status_ = VALIDATION_OK;
    170   if ((policy_->has_error_code() && policy_->error_code() != 200) ||
    171       (policy_->has_error_message() && !policy_->error_message().empty())) {
    172     LOG(ERROR) << "Error in policy blob."
    173                << " code: " << policy_->error_code()
    174                << " message: " << policy_->error_message();
    175     status_ = VALIDATION_ERROR_CODE_PRESENT;
    176     return;
    177   }
    178 
    179   // Parse policy data.
    180   if (!policy_data_->ParseFromString(policy_->policy_data()) ||
    181       !policy_data_->IsInitialized()) {
    182     LOG(ERROR) << "Failed to parse policy response";
    183     status_ = VALIDATION_PAYLOAD_PARSE_ERROR;
    184     return;
    185   }
    186 
    187   // Table of checks we run. These are sorted by descending severity of the
    188   // error, s.t. the most severe check will determine the validation status.
    189   static const struct {
    190     int flag;
    191     Status (CloudPolicyValidatorBase::* checkFunction)();
    192   } kCheckFunctions[] = {
    193     { VALIDATE_SIGNATURE,   &CloudPolicyValidatorBase::CheckSignature },
    194     { VALIDATE_INITIAL_KEY, &CloudPolicyValidatorBase::CheckInitialKey },
    195     { VALIDATE_POLICY_TYPE, &CloudPolicyValidatorBase::CheckPolicyType },
    196     { VALIDATE_ENTITY_ID,   &CloudPolicyValidatorBase::CheckEntityId },
    197     { VALIDATE_TOKEN,       &CloudPolicyValidatorBase::CheckToken },
    198     { VALIDATE_USERNAME,    &CloudPolicyValidatorBase::CheckUsername },
    199     { VALIDATE_DOMAIN,      &CloudPolicyValidatorBase::CheckDomain },
    200     { VALIDATE_TIMESTAMP,   &CloudPolicyValidatorBase::CheckTimestamp },
    201     { VALIDATE_PAYLOAD,     &CloudPolicyValidatorBase::CheckPayload },
    202   };
    203 
    204   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kCheckFunctions); ++i) {
    205     if (validation_flags_ & kCheckFunctions[i].flag) {
    206       status_ = (this->*(kCheckFunctions[i].checkFunction))();
    207       if (status_ != VALIDATION_OK)
    208         break;
    209     }
    210   }
    211 }
    212 
    213 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckSignature() {
    214   const std::string* signature_key = &key_;
    215   if (policy_->has_new_public_key() && allow_key_rotation_) {
    216     signature_key = &policy_->new_public_key();
    217     if (!policy_->has_new_public_key_signature() ||
    218         !VerifySignature(policy_->new_public_key(), key_,
    219                          policy_->new_public_key_signature())) {
    220       LOG(ERROR) << "New public key signature verification failed";
    221       return VALIDATION_BAD_SIGNATURE;
    222     }
    223   }
    224 
    225   if (!policy_->has_policy_data_signature() ||
    226       !VerifySignature(policy_->policy_data(), *signature_key,
    227                        policy_->policy_data_signature())) {
    228     LOG(ERROR) << "Policy signature validation failed";
    229     return VALIDATION_BAD_SIGNATURE;
    230   }
    231 
    232   return VALIDATION_OK;
    233 }
    234 
    235 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckInitialKey() {
    236   if (!policy_->has_new_public_key() ||
    237       !policy_->has_policy_data_signature() ||
    238       !VerifySignature(policy_->policy_data(), policy_->new_public_key(),
    239                        policy_->policy_data_signature())) {
    240     LOG(ERROR) << "Initial policy signature validation failed";
    241     return VALIDATION_BAD_INITIAL_SIGNATURE;
    242   }
    243 
    244   return VALIDATION_OK;
    245 }
    246 
    247 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckPolicyType() {
    248   if (!policy_data_->has_policy_type() ||
    249        policy_data_->policy_type() != policy_type_) {
    250     LOG(ERROR) << "Wrong policy type " << policy_data_->policy_type();
    251     return VALIDATION_WRONG_POLICY_TYPE;
    252   }
    253 
    254   return VALIDATION_OK;
    255 }
    256 
    257 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckEntityId() {
    258   if (!policy_data_->has_settings_entity_id() ||
    259       policy_data_->settings_entity_id() != settings_entity_id_) {
    260     LOG(ERROR) << "Wrong settings_entity_id "
    261                << policy_data_->settings_entity_id() << ", expected "
    262                << settings_entity_id_;
    263     return VALIDATION_WRONG_SETTINGS_ENTITY_ID;
    264   }
    265 
    266   return VALIDATION_OK;
    267 }
    268 
    269 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckTimestamp() {
    270   if (!policy_data_->has_timestamp()) {
    271     if (timestamp_option_ == TIMESTAMP_NOT_REQUIRED) {
    272       return VALIDATION_OK;
    273     } else {
    274       LOG(ERROR) << "Policy timestamp missing";
    275       return VALIDATION_BAD_TIMESTAMP;
    276     }
    277   }
    278 
    279   if (timestamp_option_ != TIMESTAMP_NOT_REQUIRED &&
    280       policy_data_->timestamp() < timestamp_not_before_) {
    281     // If |timestamp_option_| is TIMESTAMP_REQUIRED or TIMESTAMP_NOT_BEFORE
    282     // then this is a failure.
    283     LOG(ERROR) << "Policy too old: " << policy_data_->timestamp();
    284     return VALIDATION_BAD_TIMESTAMP;
    285   }
    286   if (timestamp_option_ == TIMESTAMP_REQUIRED &&
    287       policy_data_->timestamp() > timestamp_not_after_) {
    288     LOG(ERROR) << "Policy from the future: " << policy_data_->timestamp();
    289     return VALIDATION_BAD_TIMESTAMP;
    290   }
    291 
    292   return VALIDATION_OK;
    293 }
    294 
    295 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckToken() {
    296   // Make sure the token matches the expected token (if any) and also
    297   // make sure the token itself is valid (non-empty if DM_TOKEN_REQUIRED).
    298   if (dm_token_option_ == DM_TOKEN_REQUIRED &&
    299       (!policy_data_->has_request_token() ||
    300        policy_data_->request_token().empty())) {
    301     LOG(ERROR) << "Empty DM token encountered - expected: " << token_;
    302     return VALIDATION_WRONG_TOKEN;
    303   }
    304   if (!token_.empty() && policy_data_->request_token() != token_) {
    305     LOG(ERROR) << "Invalid DM token: " << policy_data_->request_token()
    306                << " - expected: " << token_;
    307     return VALIDATION_WRONG_TOKEN;
    308   }
    309 
    310   return VALIDATION_OK;
    311 }
    312 
    313 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckUsername() {
    314   if (!policy_data_->has_username()) {
    315     LOG(ERROR) << "Policy is missing user name";
    316     return VALIDATION_BAD_USERNAME;
    317   }
    318 
    319   std::string policy_username =
    320       gaia::CanonicalizeEmail(gaia::SanitizeEmail(policy_data_->username()));
    321 
    322   if (user_ != policy_username) {
    323     LOG(ERROR) << "Invalid user name " << policy_data_->username();
    324     return VALIDATION_BAD_USERNAME;
    325   }
    326 
    327   return VALIDATION_OK;
    328 }
    329 
    330 
    331 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckDomain() {
    332   if (!policy_data_->has_username()) {
    333     LOG(ERROR) << "Policy is missing user name";
    334     return VALIDATION_BAD_USERNAME;
    335   }
    336 
    337   std::string policy_domain =
    338       gaia::ExtractDomainName(
    339           gaia::CanonicalizeEmail(
    340               gaia::SanitizeEmail(policy_data_->username())));
    341 
    342   if (domain_ != policy_domain) {
    343     LOG(ERROR) << "Invalid user name " << policy_data_->username();
    344     return VALIDATION_BAD_USERNAME;
    345   }
    346 
    347   return VALIDATION_OK;
    348 }
    349 
    350 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckPayload() {
    351   if (!policy_data_->has_policy_value() ||
    352       !payload_->ParseFromString(policy_data_->policy_value()) ||
    353       !payload_->IsInitialized()) {
    354     LOG(ERROR) << "Failed to decode policy payload protobuf";
    355     return VALIDATION_POLICY_PARSE_ERROR;
    356   }
    357 
    358   return VALIDATION_OK;
    359 }
    360 
    361 // static
    362 bool CloudPolicyValidatorBase::VerifySignature(const std::string& data,
    363                                                const std::string& key,
    364                                                const std::string& signature) {
    365   crypto::SignatureVerifier verifier;
    366 
    367   if (!verifier.VerifyInit(kSignatureAlgorithm, sizeof(kSignatureAlgorithm),
    368                            reinterpret_cast<const uint8*>(signature.c_str()),
    369                            signature.size(),
    370                            reinterpret_cast<const uint8*>(key.c_str()),
    371                            key.size())) {
    372     return false;
    373   }
    374   verifier.VerifyUpdate(reinterpret_cast<const uint8*>(data.c_str()),
    375                         data.size());
    376   return verifier.VerifyFinal();
    377 }
    378 
    379 template class CloudPolicyValidator<em::CloudPolicySettings>;
    380 
    381 #if !defined(OS_ANDROID)
    382 template class CloudPolicyValidator<em::ExternalPolicyData>;
    383 #endif
    384 
    385 }  // namespace policy
    386