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 <vector>
      6 
      7 #include "base/bind.h"
      8 #include "base/memory/scoped_ptr.h"
      9 #include "base/message_loop/message_loop.h"
     10 #include "base/message_loop/message_loop_proxy.h"
     11 #include "base/stl_util.h"
     12 #include "base/strings/string_util.h"
     13 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
     14 #include "components/policy/core/common/cloud/cloud_policy_validator.h"
     15 #include "components/policy/core/common/cloud/policy_builder.h"
     16 #include "components/policy/core/common/policy_switches.h"
     17 #include "crypto/rsa_private_key.h"
     18 #include "policy/proto/device_management_backend.pb.h"
     19 #include "testing/gmock/include/gmock/gmock.h"
     20 #include "testing/gtest/include/gtest/gtest.h"
     21 
     22 namespace em = enterprise_management;
     23 
     24 using testing::Invoke;
     25 using testing::Mock;
     26 
     27 namespace policy {
     28 
     29 namespace {
     30 
     31 ACTION_P(CheckStatus, expected_status) {
     32   EXPECT_EQ(expected_status, arg0->status());
     33 };
     34 
     35 class CloudPolicyValidatorTest : public testing::Test {
     36  public:
     37   CloudPolicyValidatorTest()
     38       : timestamp_(base::Time::UnixEpoch() +
     39                    base::TimeDelta::FromMilliseconds(
     40                        PolicyBuilder::kFakeTimestamp)),
     41         timestamp_option_(CloudPolicyValidatorBase::TIMESTAMP_REQUIRED),
     42         ignore_missing_dm_token_(CloudPolicyValidatorBase::DM_TOKEN_REQUIRED),
     43         allow_key_rotation_(true),
     44         existing_dm_token_(PolicyBuilder::kFakeToken),
     45         owning_domain_(PolicyBuilder::kFakeDomain),
     46         cached_key_signature_(PolicyBuilder::GetTestSigningKeySignature()) {
     47     policy_.SetDefaultNewSigningKey();
     48   }
     49 
     50   void Validate(testing::Action<void(UserCloudPolicyValidator*)> check_action) {
     51     policy_.Build();
     52     ValidatePolicy(check_action, policy_.GetCopy());
     53   }
     54 
     55   void ValidatePolicy(
     56       testing::Action<void(UserCloudPolicyValidator*)> check_action,
     57       scoped_ptr<enterprise_management::PolicyFetchResponse> policy_response) {
     58     // Create a validator.
     59     scoped_ptr<UserCloudPolicyValidator> validator = CreateValidator(
     60         policy_response.Pass());
     61 
     62     // Run validation and check the result.
     63     EXPECT_CALL(*this, ValidationCompletion(validator.get())).WillOnce(
     64         check_action);
     65     validator.release()->StartValidation(
     66         base::Bind(&CloudPolicyValidatorTest::ValidationCompletion,
     67                    base::Unretained(this)));
     68     loop_.RunUntilIdle();
     69     Mock::VerifyAndClearExpectations(this);
     70   }
     71 
     72   scoped_ptr<UserCloudPolicyValidator> CreateValidator(
     73       scoped_ptr<enterprise_management::PolicyFetchResponse> policy_response) {
     74     std::vector<uint8> public_key_bytes;
     75     EXPECT_TRUE(
     76         PolicyBuilder::CreateTestSigningKey()->ExportPublicKey(
     77             &public_key_bytes));
     78 
     79     // Convert from bytes to string format (which is what ValidateSignature()
     80     // takes).
     81     std::string public_key = std::string(
     82         reinterpret_cast<const char*>(vector_as_array(&public_key_bytes)),
     83         public_key_bytes.size());
     84 
     85     UserCloudPolicyValidator* validator = UserCloudPolicyValidator::Create(
     86         policy_response.Pass(), base::MessageLoopProxy::current());
     87     validator->ValidateTimestamp(timestamp_, timestamp_,
     88                                  timestamp_option_);
     89     validator->ValidateUsername(PolicyBuilder::kFakeUsername, true);
     90     if (!owning_domain_.empty())
     91       validator->ValidateDomain(owning_domain_);
     92     validator->ValidateDMToken(existing_dm_token_, ignore_missing_dm_token_);
     93     validator->ValidatePolicyType(dm_protocol::kChromeUserPolicyType);
     94     validator->ValidatePayload();
     95     validator->ValidateCachedKey(public_key,
     96                                  cached_key_signature_,
     97                                  GetPolicyVerificationKey(),
     98                                  owning_domain_);
     99     validator->ValidateSignature(public_key,
    100                                  GetPolicyVerificationKey(),
    101                                  owning_domain_,
    102                                  allow_key_rotation_);
    103     if (allow_key_rotation_)
    104       validator->ValidateInitialKey(GetPolicyVerificationKey(), owning_domain_);
    105     return make_scoped_ptr(validator);
    106   }
    107 
    108 
    109   void CheckSuccessfulValidation(UserCloudPolicyValidator* validator) {
    110     EXPECT_TRUE(validator->success());
    111     EXPECT_EQ(policy_.policy().SerializeAsString(),
    112               validator->policy()->SerializeAsString());
    113     EXPECT_EQ(policy_.policy_data().SerializeAsString(),
    114               validator->policy_data()->SerializeAsString());
    115     EXPECT_EQ(policy_.payload().SerializeAsString(),
    116               validator->payload()->SerializeAsString());
    117   }
    118 
    119   base::MessageLoopForUI loop_;
    120   base::Time timestamp_;
    121   CloudPolicyValidatorBase::ValidateTimestampOption timestamp_option_;
    122   CloudPolicyValidatorBase::ValidateDMTokenOption ignore_missing_dm_token_;
    123   std::string signing_key_;
    124   bool allow_key_rotation_;
    125   std::string existing_dm_token_;
    126   std::string owning_domain_;
    127   std::string cached_key_signature_;
    128 
    129   UserPolicyBuilder policy_;
    130 
    131  private:
    132   MOCK_METHOD1(ValidationCompletion, void(UserCloudPolicyValidator* validator));
    133 
    134   DISALLOW_COPY_AND_ASSIGN(CloudPolicyValidatorTest);
    135 };
    136 
    137 TEST_F(CloudPolicyValidatorTest, SuccessfulValidation) {
    138   Validate(Invoke(this, &CloudPolicyValidatorTest::CheckSuccessfulValidation));
    139 }
    140 
    141 TEST_F(CloudPolicyValidatorTest, SuccessfulRunValidation) {
    142   policy_.Build();
    143   scoped_ptr<UserCloudPolicyValidator> validator = CreateValidator(
    144       policy_.GetCopy());
    145   // Run validation immediately (no background tasks).
    146   validator->RunValidation();
    147   CheckSuccessfulValidation(validator.get());
    148 }
    149 
    150 TEST_F(CloudPolicyValidatorTest, SuccessfulRunValidationWithNoExistingDMToken) {
    151   existing_dm_token_.clear();
    152   Validate(Invoke(this, &CloudPolicyValidatorTest::CheckSuccessfulValidation));
    153 }
    154 
    155 TEST_F(CloudPolicyValidatorTest, SuccessfulRunValidationWithNoDMTokens) {
    156   existing_dm_token_.clear();
    157   policy_.policy_data().clear_request_token();
    158   ignore_missing_dm_token_ = CloudPolicyValidatorBase::DM_TOKEN_NOT_REQUIRED;
    159   Validate(Invoke(this, &CloudPolicyValidatorTest::CheckSuccessfulValidation));
    160 }
    161 
    162 TEST_F(CloudPolicyValidatorTest, UsernameCanonicalization) {
    163   policy_.policy_data().set_username(
    164       StringToUpperASCII(std::string(PolicyBuilder::kFakeUsername)));
    165   Validate(CheckStatus(CloudPolicyValidatorBase::VALIDATION_OK));
    166 }
    167 
    168 TEST_F(CloudPolicyValidatorTest, ErrorNoPolicyType) {
    169   policy_.policy_data().clear_policy_type();
    170   Validate(CheckStatus(CloudPolicyValidatorBase::VALIDATION_WRONG_POLICY_TYPE));
    171 }
    172 
    173 TEST_F(CloudPolicyValidatorTest, ErrorWrongPolicyType) {
    174   policy_.policy_data().set_policy_type("invalid");
    175   Validate(CheckStatus(CloudPolicyValidatorBase::VALIDATION_WRONG_POLICY_TYPE));
    176 }
    177 
    178 TEST_F(CloudPolicyValidatorTest, ErrorNoTimestamp) {
    179   policy_.policy_data().clear_timestamp();
    180   Validate(CheckStatus(CloudPolicyValidatorBase::VALIDATION_BAD_TIMESTAMP));
    181 }
    182 
    183 TEST_F(CloudPolicyValidatorTest, IgnoreMissingTimestamp) {
    184   timestamp_option_ = CloudPolicyValidatorBase::TIMESTAMP_NOT_REQUIRED;
    185   policy_.policy_data().clear_timestamp();
    186   Validate(CheckStatus(CloudPolicyValidatorBase::VALIDATION_OK));
    187 }
    188 
    189 TEST_F(CloudPolicyValidatorTest, ErrorOldTimestamp) {
    190   base::Time timestamp(timestamp_ - base::TimeDelta::FromMinutes(5));
    191   policy_.policy_data().set_timestamp(
    192       (timestamp - base::Time::UnixEpoch()).InMilliseconds());
    193   Validate(CheckStatus(CloudPolicyValidatorBase::VALIDATION_BAD_TIMESTAMP));
    194 }
    195 
    196 TEST_F(CloudPolicyValidatorTest, ErrorTimestampFromTheFuture) {
    197   base::Time timestamp(timestamp_ + base::TimeDelta::FromMinutes(5));
    198   policy_.policy_data().set_timestamp(
    199       (timestamp - base::Time::UnixEpoch()).InMilliseconds());
    200   Validate(CheckStatus(CloudPolicyValidatorBase::VALIDATION_BAD_TIMESTAMP));
    201 }
    202 
    203 TEST_F(CloudPolicyValidatorTest, IgnoreErrorTimestampFromTheFuture) {
    204   base::Time timestamp(timestamp_ + base::TimeDelta::FromMinutes(5));
    205   timestamp_option_ =
    206       CloudPolicyValidatorBase::TIMESTAMP_NOT_BEFORE;
    207   policy_.policy_data().set_timestamp(
    208       (timestamp - base::Time::UnixEpoch()).InMilliseconds());
    209   Validate(CheckStatus(CloudPolicyValidatorBase::VALIDATION_OK));
    210 }
    211 
    212 TEST_F(CloudPolicyValidatorTest, ErrorNoRequestToken) {
    213   policy_.policy_data().clear_request_token();
    214   Validate(CheckStatus(CloudPolicyValidatorBase::VALIDATION_WRONG_TOKEN));
    215 }
    216 
    217 TEST_F(CloudPolicyValidatorTest, ErrorNoRequestTokenNotRequired) {
    218   // Even though DMTokens are not required, if the existing policy has a token,
    219   // we should still generate an error if the new policy has none.
    220   policy_.policy_data().clear_request_token();
    221   ignore_missing_dm_token_ = CloudPolicyValidatorBase::DM_TOKEN_NOT_REQUIRED;
    222   Validate(CheckStatus(CloudPolicyValidatorBase::VALIDATION_WRONG_TOKEN));
    223 }
    224 
    225 TEST_F(CloudPolicyValidatorTest, ErrorNoRequestTokenNoTokenPassed) {
    226   // Mimic the first fetch of policy (no existing DM token) - should still
    227   // complain about not having any DMToken.
    228   existing_dm_token_.clear();
    229   policy_.policy_data().clear_request_token();
    230   Validate(CheckStatus(CloudPolicyValidatorBase::VALIDATION_WRONG_TOKEN));
    231 }
    232 
    233 TEST_F(CloudPolicyValidatorTest, ErrorInvalidRequestToken) {
    234   policy_.policy_data().set_request_token("invalid");
    235   Validate(CheckStatus(CloudPolicyValidatorBase::VALIDATION_WRONG_TOKEN));
    236 }
    237 
    238 TEST_F(CloudPolicyValidatorTest, ErrorNoPolicyValue) {
    239   policy_.clear_payload();
    240   Validate(
    241       CheckStatus(CloudPolicyValidatorBase::VALIDATION_POLICY_PARSE_ERROR));
    242 }
    243 
    244 TEST_F(CloudPolicyValidatorTest, ErrorInvalidPolicyValue) {
    245   policy_.clear_payload();
    246   policy_.policy_data().set_policy_value("invalid");
    247   Validate(
    248       CheckStatus(CloudPolicyValidatorBase::VALIDATION_POLICY_PARSE_ERROR));
    249 }
    250 
    251 TEST_F(CloudPolicyValidatorTest, ErrorNoUsername) {
    252   policy_.policy_data().clear_username();
    253   Validate(CheckStatus(CloudPolicyValidatorBase::VALIDATION_BAD_USERNAME));
    254 }
    255 
    256 TEST_F(CloudPolicyValidatorTest, ErrorInvalidUsername) {
    257   policy_.policy_data().set_username("invalid (at) example.com");
    258   Validate(CheckStatus(CloudPolicyValidatorBase::VALIDATION_BAD_USERNAME));
    259 }
    260 
    261 TEST_F(CloudPolicyValidatorTest, ErrorErrorMessage) {
    262   policy_.policy().set_error_message("error");
    263   Validate(
    264       CheckStatus(CloudPolicyValidatorBase::VALIDATION_ERROR_CODE_PRESENT));
    265 }
    266 
    267 TEST_F(CloudPolicyValidatorTest, ErrorErrorCode) {
    268   policy_.policy().set_error_code(42);
    269   Validate(
    270       CheckStatus(CloudPolicyValidatorBase::VALIDATION_ERROR_CODE_PRESENT));
    271 }
    272 
    273 TEST_F(CloudPolicyValidatorTest, ErrorNoSignature) {
    274   policy_.UnsetSigningKey();
    275   policy_.UnsetNewSigningKey();
    276   policy_.policy().clear_policy_data_signature();
    277   Validate(CheckStatus(CloudPolicyValidatorBase::VALIDATION_BAD_SIGNATURE));
    278 }
    279 
    280 TEST_F(CloudPolicyValidatorTest, ErrorInvalidSignature) {
    281   policy_.UnsetSigningKey();
    282   policy_.UnsetNewSigningKey();
    283   policy_.policy().set_policy_data_signature("invalid");
    284   Validate(CheckStatus(CloudPolicyValidatorBase::VALIDATION_BAD_SIGNATURE));
    285 }
    286 
    287 TEST_F(CloudPolicyValidatorTest, ErrorNoPublicKey) {
    288   policy_.UnsetSigningKey();
    289   policy_.UnsetNewSigningKey();
    290   policy_.policy().clear_new_public_key();
    291   Validate(CheckStatus(CloudPolicyValidatorBase::VALIDATION_BAD_SIGNATURE));
    292 }
    293 
    294 TEST_F(CloudPolicyValidatorTest, ErrorInvalidPublicKey) {
    295   policy_.UnsetSigningKey();
    296   policy_.UnsetNewSigningKey();
    297   policy_.policy().set_new_public_key("invalid");
    298   Validate(CheckStatus(CloudPolicyValidatorBase::VALIDATION_BAD_SIGNATURE));
    299 }
    300 
    301 TEST_F(CloudPolicyValidatorTest, ErrorNoPublicKeySignature) {
    302   policy_.UnsetSigningKey();
    303   policy_.UnsetNewSigningKey();
    304   policy_.policy().clear_new_public_key_signature();
    305   Validate(CheckStatus(CloudPolicyValidatorBase::VALIDATION_BAD_SIGNATURE));
    306 }
    307 
    308 TEST_F(CloudPolicyValidatorTest, ErrorInvalidPublicKeySignature) {
    309   policy_.UnsetSigningKey();
    310   policy_.UnsetNewSigningKey();
    311   policy_.policy().set_new_public_key_signature("invalid");
    312   Validate(CheckStatus(CloudPolicyValidatorBase::VALIDATION_BAD_SIGNATURE));
    313 }
    314 
    315 #if !defined(OS_CHROMEOS)
    316 // Validation key is not currently checked on Chrome OS
    317 // (http://crbug.com/328038).
    318 TEST_F(CloudPolicyValidatorTest, ErrorInvalidPublicKeyVerificationSignature) {
    319   policy_.Build();
    320   policy_.policy().set_new_public_key_verification_signature("invalid");
    321   ValidatePolicy(CheckStatus(
    322       CloudPolicyValidatorBase::VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE),
    323                  policy_.GetCopy());
    324 }
    325 
    326 TEST_F(CloudPolicyValidatorTest, ErrorDomainMismatchForKeyVerification) {
    327   policy_.Build();
    328   // Generate a non-matching owning_domain, which should cause a validation
    329   // failure.
    330   owning_domain_ = "invalid.com";
    331   ValidatePolicy(CheckStatus(
    332       CloudPolicyValidatorBase::VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE),
    333                  policy_.GetCopy());
    334 }
    335 
    336 TEST_F(CloudPolicyValidatorTest, ErrorDomainExtractedFromUsernameMismatch) {
    337   // Generate a non-matching username domain, which should cause a validation
    338   // failure when we try to verify the signing key with it.
    339   policy_.policy_data().set_username("wonky (at) invalid.com");
    340   policy_.Build();
    341   // Pass an empty domain to tell validator to extract the domain from the
    342   // policy's |username| field.
    343   owning_domain_ = "";
    344   ValidatePolicy(CheckStatus(
    345       CloudPolicyValidatorBase::VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE),
    346                  policy_.GetCopy());
    347 }
    348 
    349 TEST_F(CloudPolicyValidatorTest, ErrorNoCachedKeySignature) {
    350   // Generate an empty cached_key_signature_ and this should cause a validation
    351   // error when we try to verify the signing key with it.
    352   cached_key_signature_ = "";
    353   Validate(CheckStatus(
    354       CloudPolicyValidatorBase::VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE));
    355 }
    356 
    357 TEST_F(CloudPolicyValidatorTest, ErrorInvalidCachedKeySignature) {
    358   // Generate a key signature for a different key (one that does not match
    359   // the signing key) and this should cause a validation error when we try to
    360   // verify the signing key with it.
    361   cached_key_signature_ = PolicyBuilder::GetTestOtherSigningKeySignature();
    362   Validate(CheckStatus(
    363       CloudPolicyValidatorBase::VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE));
    364 }
    365 #endif
    366 
    367 TEST_F(CloudPolicyValidatorTest, SuccessfulNoDomainValidation) {
    368   // Don't pass in a domain - this tells the validation code to instead
    369   // extract the domain from the username.
    370   owning_domain_ = "";
    371   Validate(Invoke(this, &CloudPolicyValidatorTest::CheckSuccessfulValidation));
    372 }
    373 
    374 TEST_F(CloudPolicyValidatorTest, ErrorNoRotationAllowed) {
    375   allow_key_rotation_ = false;
    376   Validate(CheckStatus(CloudPolicyValidatorBase::VALIDATION_BAD_SIGNATURE));
    377 }
    378 
    379 TEST_F(CloudPolicyValidatorTest, NoRotation) {
    380   allow_key_rotation_ = false;
    381   policy_.UnsetNewSigningKey();
    382   Validate(CheckStatus(CloudPolicyValidatorBase::VALIDATION_OK));
    383 }
    384 
    385 }  // namespace
    386 
    387 }  // namespace policy
    388