blob: 5b6ed4bd4f8642bde7232f45ee6446d300b7861d [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_POLICY_CLOUD_CLOUD_POLICY_VALIDATOR_H_
#define CHROME_BROWSER_POLICY_CLOUD_CLOUD_POLICY_VALIDATOR_H_
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/sequenced_task_runner.h"
#include "base/time/time.h"
#include "chrome/browser/policy/proto/cloud/chrome_extension_policy.pb.h"
#include "policy/proto/cloud_policy.pb.h"
namespace base {
class MessageLoopProxy;
}
namespace google {
namespace protobuf {
class MessageLite;
}
}
namespace enterprise_management {
class PolicyData;
class PolicyFetchResponse;
}
namespace policy {
// Helper class that implements the gory details of validating a policy blob.
// Since signature checks are expensive, validation can happen on a background
// thread. The pattern is to create a validator, configure its behavior through
// the ValidateXYZ() functions, and then call StartValidation(). Alternatively,
// RunValidation() can be used to perform validation on the current thread.
class CloudPolicyValidatorBase {
public:
// Validation result codes. These values are also used for UMA histograms;
// they must stay stable, and the UMA counters must be updated if new elements
// are appended at the end.
enum Status {
// Indicates successful validation.
VALIDATION_OK,
// Bad signature on the initial key.
VALIDATION_BAD_INITIAL_SIGNATURE,
// Bad signature.
VALIDATION_BAD_SIGNATURE,
// Policy blob contains error code.
VALIDATION_ERROR_CODE_PRESENT,
// Policy payload failed to decode.
VALIDATION_PAYLOAD_PARSE_ERROR,
// Unexpected policy type.
VALIDATION_WRONG_POLICY_TYPE,
// Unexpected settings entity id.
VALIDATION_WRONG_SETTINGS_ENTITY_ID,
// Time stamp from the future.
VALIDATION_BAD_TIMESTAMP,
// Token doesn't match.
VALIDATION_WRONG_TOKEN,
// Username doesn't match.
VALIDATION_BAD_USERNAME,
// Policy payload protobuf parse error.
VALIDATION_POLICY_PARSE_ERROR,
};
enum ValidateDMTokenOption {
// The policy must have a non-empty DMToken.
DM_TOKEN_REQUIRED,
// The policy may have an empty or missing DMToken, if the expected token
// is also empty.
DM_TOKEN_NOT_REQUIRED,
};
enum ValidateTimestampOption {
// The policy must have a timestamp field and it should be checked against
// both the start and end times.
TIMESTAMP_REQUIRED,
// The timestamp should only be compared vs the |not_before| value (this
// is appropriate for platforms with unreliable system times, where we want
// to ensure that fresh policy is newer than existing policy, but we can't
// do any other validation).
TIMESTAMP_NOT_BEFORE,
// No timestamp field is required.
TIMESTAMP_NOT_REQUIRED,
};
virtual ~CloudPolicyValidatorBase();
// Validation status which can be read after completion has been signaled.
Status status() const { return status_; }
bool success() const { return status_ == VALIDATION_OK; }
// The policy objects owned by the validator. These are scoped_ptr
// references, so ownership can be passed on once validation is complete.
scoped_ptr<enterprise_management::PolicyFetchResponse>& policy() {
return policy_;
}
scoped_ptr<enterprise_management::PolicyData>& policy_data() {
return policy_data_;
}
// Instructs the validator to check that the policy timestamp is not before
// |not_before| and not after |not_after| + grace interval. If
// |timestamp_option| is set to TIMESTAMP_REQUIRED, then the policy will fail
// validation if it does not have a timestamp field.
void ValidateTimestamp(base::Time not_before,
base::Time not_after,
ValidateTimestampOption timestamp_option);
// Validates the username in the policy blob matches |expected_user|.
void ValidateUsername(const std::string& expected_user);
// Validates the policy blob is addressed to |expected_domain|. This uses the
// domain part of the username field in the policy for the check.
void ValidateDomain(const std::string& expected_domain);
// Makes sure the DM token on the policy matches |expected_token|.
// If |dm_token_option| is DM_TOKEN_REQUIRED, then the policy will fail
// validation if it does not have a non-empty request_token field.
void ValidateDMToken(const std::string& dm_token,
ValidateDMTokenOption dm_token_option);
// Validates the policy type.
void ValidatePolicyType(const std::string& policy_type);
// Validates the settings_entity_id value.
void ValidateSettingsEntityId(const std::string& settings_entity_id);
// Validates that the payload can be decoded successfully.
void ValidatePayload();
// Verifies that the signature on the policy blob verifies against |key|. If |
// |allow_key_rotation| is true and there is a key rotation present in the
// policy blob, this checks the signature on the new key against |key| and the
// policy blob against the new key.
void ValidateSignature(const std::vector<uint8>& key,
bool allow_key_rotation);
// Similar to StartSignatureVerification(), this checks the signature on the
// policy blob. However, this variant expects a new policy key set in the
// policy blob and makes sure the policy is signed using that key. This should
// be called at setup time when there is no existing policy key present to
// check against.
void ValidateInitialKey();
// Convenience helper that configures timestamp and token validation based on
// the current policy blob. |policy_data| may be NULL, in which case the
// timestamp validation will drop the lower bound. |dm_token_option|
// and |timestamp_option| have the same effect as the corresponding
// parameters for ValidateTimestamp() and ValidateDMToken().
void ValidateAgainstCurrentPolicy(
const enterprise_management::PolicyData* policy_data,
ValidateTimestampOption timestamp_option,
ValidateDMTokenOption dm_token_option);
// Immediately performs validation on the current thread.
void RunValidation();
protected:
// Create a new validator that checks |policy_response|. |payload| is the
// message that the policy payload will be parsed to, and it needs to stay
// valid for the lifetime of the validator.
CloudPolicyValidatorBase(
scoped_ptr<enterprise_management::PolicyFetchResponse> policy_response,
google::protobuf::MessageLite* payload,
scoped_refptr<base::SequencedTaskRunner> background_task_runner);
// Posts an asynchronous calls to PerformValidation, which will eventually
// report its result via |completion_callback|.
void PostValidationTask(const base::Closure& completion_callback);
private:
// Internal flags indicating what to check.
enum ValidationFlags {
VALIDATE_TIMESTAMP = 1 << 0,
VALIDATE_USERNAME = 1 << 1,
VALIDATE_DOMAIN = 1 << 2,
VALIDATE_TOKEN = 1 << 3,
VALIDATE_POLICY_TYPE = 1 << 4,
VALIDATE_ENTITY_ID = 1 << 5,
VALIDATE_PAYLOAD = 1 << 6,
VALIDATE_SIGNATURE = 1 << 7,
VALIDATE_INITIAL_KEY = 1 << 8,
};
// Performs validation, called on a background thread.
static void PerformValidation(
scoped_ptr<CloudPolicyValidatorBase> self,
scoped_refptr<base::MessageLoopProxy> message_loop,
const base::Closure& completion_callback);
// Reports completion to the |completion_callback_|.
static void ReportCompletion(scoped_ptr<CloudPolicyValidatorBase> self,
const base::Closure& completion_callback);
// Invokes all the checks and reports the result.
void RunChecks();
// Helper functions implementing individual checks.
Status CheckTimestamp();
Status CheckUsername();
Status CheckDomain();
Status CheckToken();
Status CheckPolicyType();
Status CheckEntityId();
Status CheckPayload();
Status CheckSignature();
Status CheckInitialKey();
// Verifies the SHA1/RSA |signature| on |data| against |key|.
static bool VerifySignature(const std::string& data,
const std::string& key,
const std::string& signature);
Status status_;
scoped_ptr<enterprise_management::PolicyFetchResponse> policy_;
scoped_ptr<enterprise_management::PolicyData> policy_data_;
google::protobuf::MessageLite* payload_;
int validation_flags_;
int64 timestamp_not_before_;
int64 timestamp_not_after_;
ValidateTimestampOption timestamp_option_;
ValidateDMTokenOption dm_token_option_;
std::string user_;
std::string domain_;
std::string token_;
std::string policy_type_;
std::string settings_entity_id_;
std::string key_;
bool allow_key_rotation_;
scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
DISALLOW_COPY_AND_ASSIGN(CloudPolicyValidatorBase);
};
// A simple type-parameterized extension of CloudPolicyValidator that
// facilitates working with the actual protobuf payload type.
template<typename PayloadProto>
class CloudPolicyValidator : public CloudPolicyValidatorBase {
public:
typedef base::Callback<void(CloudPolicyValidator<PayloadProto>*)>
CompletionCallback;
virtual ~CloudPolicyValidator() {}
// Creates a new validator.
// |background_task_runner| is optional; if RunValidation() is used directly
// and StartValidation() is not used then it can be NULL.
static CloudPolicyValidator<PayloadProto>* Create(
scoped_ptr<enterprise_management::PolicyFetchResponse> policy_response,
scoped_refptr<base::SequencedTaskRunner> background_task_runner) {
return new CloudPolicyValidator(
policy_response.Pass(),
scoped_ptr<PayloadProto>(new PayloadProto()),
background_task_runner);
}
scoped_ptr<PayloadProto>& payload() {
return payload_;
}
// Kicks off asynchronous validation. |completion_callback| is invoked when
// done. From this point on, the validator manages its own lifetime - this
// allows callers to provide a WeakPtr in the callback without leaking the
// validator.
void StartValidation(const CompletionCallback& completion_callback) {
PostValidationTask(base::Bind(completion_callback, this));
}
private:
CloudPolicyValidator(
scoped_ptr<enterprise_management::PolicyFetchResponse> policy_response,
scoped_ptr<PayloadProto> payload,
scoped_refptr<base::SequencedTaskRunner> background_task_runner)
: CloudPolicyValidatorBase(policy_response.Pass(),
payload.get(),
background_task_runner),
payload_(payload.Pass()) {}
scoped_ptr<PayloadProto> payload_;
DISALLOW_COPY_AND_ASSIGN(CloudPolicyValidator);
};
typedef CloudPolicyValidator<enterprise_management::CloudPolicySettings>
UserCloudPolicyValidator;
typedef CloudPolicyValidator<enterprise_management::ExternalPolicyData>
ComponentCloudPolicyValidator;
} // namespace policy
#endif // CHROME_BROWSER_POLICY_CLOUD_CLOUD_POLICY_VALIDATOR_H_