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