diff options
author | mnissler@chromium.org <mnissler@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-14 15:22:09 +0000 |
---|---|---|
committer | mnissler@chromium.org <mnissler@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-14 15:22:09 +0000 |
commit | 911807d9bccc8bbfba1fe8033bdc7b0adadb1484 (patch) | |
tree | 295e85549b7d8c48dc8051232824a84ff77b80c6 | |
parent | 614a72e27ebbb8445a354edfc12f170198e869f1 (diff) | |
download | chromium_src-911807d9bccc8bbfba1fe8033bdc7b0adadb1484.zip chromium_src-911807d9bccc8bbfba1fe8033bdc7b0adadb1484.tar.gz chromium_src-911807d9bccc8bbfba1fe8033bdc7b0adadb1484.tar.bz2 |
Retrieve enterprise device state in OOBE.
This extends AutoEnrollmentClient to be able to retrieve server-backed
device state from the cloud during OOBE. This will allow the device to
restore enrollment state after recovery.
BUG=chromium:351663
TEST=Existing unit tests.
R=joaodasilva@chromium.org
Review URL: https://codereview.chromium.org/197043002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@257115 0039d316-1c4b-4281-b951-d872f2087c98
18 files changed, 654 insertions, 251 deletions
diff --git a/chrome/browser/chromeos/policy/auto_enrollment_client.cc b/chrome/browser/chromeos/policy/auto_enrollment_client.cc index a5b9425..2a5fe30 100644 --- a/chrome/browser/chromeos/policy/auto_enrollment_client.cc +++ b/chrome/browser/chromeos/policy/auto_enrollment_client.cc @@ -14,9 +14,11 @@ #include "base/metrics/sparse_histogram.h" #include "base/prefs/pref_registry_simple.h" #include "base/prefs/pref_service.h" +#include "base/prefs/scoped_user_pref_update.h" #include "base/strings/string_number_conversions.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h" +#include "chrome/browser/chromeos/policy/server_backed_device_state.h" #include "chrome/common/chrome_content_client.h" #include "chrome/common/pref_names.h" #include "chromeos/chromeos_switches.h" @@ -26,12 +28,15 @@ #include "content/public/browser/browser_thread.h" #include "crypto/sha2.h" #include "net/url_request/url_request_context_getter.h" +#include "policy/proto/device_management_backend.pb.h" #include "url/gurl.h" using content::BrowserThread; namespace em = enterprise_management; +namespace policy { + namespace { // UMA histogram names. @@ -82,22 +87,70 @@ int NextPowerOf2(int64 value) { return kMaximumPower + 1; } -} // namespace +// Sets or clears a value in a dictionary. +void UpdateDict(base::DictionaryValue* dict, + const char* pref_path, + bool set_or_clear, + base::Value* value) { + scoped_ptr<base::Value> scoped_value(value); + if (set_or_clear) + dict->Set(pref_path, scoped_value.release()); + else + dict->Remove(pref_path, NULL); +} -namespace policy { +// Converts a restore mode enum value from the DM protocol into the +// corresponding prefs string constant. +std::string ConvertRestoreMode( + em::DeviceStateRetrievalResponse::RestoreMode restore_mode) { + switch (restore_mode) { + case em::DeviceStateRetrievalResponse::RESTORE_MODE_NONE: + return std::string(); + case em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_REQUESTED: + return kDeviceStateRestoreModeReEnrollmentRequested; + case em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED: + return kDeviceStateRestoreModeReEnrollmentEnforced; + } + + NOTREACHED() << "Bad restore mode " << restore_mode; + return std::string(); +} + +// Helper that calls |completion_callback| if |state| indicates termination. +void CheckCompletion(const base::Closure& completion_callback, + AutoEnrollmentClient::State state) { + switch (state) { + case AutoEnrollmentClient::STATE_PENDING: + case AutoEnrollmentClient::STATE_CONNECTION_ERROR: + case AutoEnrollmentClient::STATE_SERVER_ERROR: + break; + case AutoEnrollmentClient::STATE_TRIGGER_ENROLLMENT: + case AutoEnrollmentClient::STATE_NO_ENROLLMENT: + if (!completion_callback.is_null()) + completion_callback.Run(); + break; + } +} + +} // namespace AutoEnrollmentClient::AutoEnrollmentClient( - const base::Closure& callback, + const ProgressCallback& callback, DeviceManagementService* service, PrefService* local_state, scoped_refptr<net::URLRequestContextGetter> system_request_context, - const std::string& serial_number, + const std::string& server_backed_state_key, + bool retrieve_device_state, int power_initial, int power_limit) - : completion_callback_(callback), - should_auto_enroll_(false), + : progress_callback_(callback), + state_(STATE_PENDING), + has_server_state_(false), + device_state_available_(false), device_id_(base::GenerateGUID()), - power_initial_(power_initial), + server_backed_state_key_(server_backed_state_key), + retrieve_device_state_(retrieve_device_state), + current_power_(power_initial), power_limit_(power_limit), modulus_updates_received_(0), device_management_service_(service), @@ -105,10 +158,12 @@ AutoEnrollmentClient::AutoEnrollmentClient( request_context_ = new SystemPolicyRequestContext( system_request_context, GetUserAgent()); - DCHECK_LE(power_initial_, power_limit_); - DCHECK(!completion_callback_.is_null()); - if (!serial_number.empty()) - serial_number_hash_ = crypto::SHA256HashString(serial_number); + DCHECK_LE(current_power_, power_limit_); + DCHECK(!progress_callback_.is_null()); + if (!server_backed_state_key_.empty()) { + server_backed_state_key_hash_ = + crypto::SHA256HashString(server_backed_state_key_); + } net::NetworkChangeNotifier::AddNetworkChangeObserver(this); } @@ -128,12 +183,11 @@ bool AutoEnrollmentClient::IsDisabled() { // Do not communicate auto-enrollment data to the server if // 1. we are running integration or perf tests with telemetry. // 2. modulus configuration is not present. - return command_line->HasSwitch( - chromeos::switches::kOobeSkipPostLogin) || + return command_line->HasSwitch(chromeos::switches::kOobeSkipPostLogin) || (!command_line->HasSwitch( - chromeos::switches::kEnterpriseEnrollmentInitialModulus) && - !command_line->HasSwitch( - chromeos::switches::kEnterpriseEnrollmentModulusLimit)); + chromeos::switches::kEnterpriseEnrollmentInitialModulus) && + !command_line->HasSwitch( + chromeos::switches::kEnterpriseEnrollmentModulusLimit)); } // static @@ -160,12 +214,23 @@ AutoEnrollmentClient* AutoEnrollmentClient::Create( power_initial = power_limit; } + bool retrieve_device_state = false; + std::string device_id; + if (CommandLine::ForCurrentProcess()->HasSwitch( + chromeos::switches::kEnterpriseEnableForcedReEnrollment)) { + retrieve_device_state = true; + device_id = DeviceCloudPolicyManagerChromeOS::GetDeviceStateKey(); + } else { + device_id = DeviceCloudPolicyManagerChromeOS::GetMachineID(); + } + return new AutoEnrollmentClient( - completion_callback, + base::Bind(&CheckCompletion, completion_callback), service, g_browser_process->local_state(), g_browser_process->system_request_context(), - DeviceCloudPolicyManagerChromeOS::GetMachineID(), + device_id, + retrieve_device_state, power_initial, power_limit); } @@ -174,36 +239,24 @@ AutoEnrollmentClient* AutoEnrollmentClient::Create( void AutoEnrollmentClient::CancelAutoEnrollment() { PrefService* local_state = g_browser_process->local_state(); local_state->SetBoolean(prefs::kShouldAutoEnroll, false); + local_state->ClearPref(prefs::kServerBackedDeviceState); local_state->CommitPendingWrite(); } void AutoEnrollmentClient::Start() { // Drop the previous job and reset state. request_job_.reset(); - should_auto_enroll_ = false; - time_start_ = base::Time(); // reset to null. - - if (GetCachedDecision()) { - VLOG(1) << "AutoEnrollmentClient: using cached decision: " - << should_auto_enroll_; - } else if (device_management_service_) { - if (serial_number_hash_.empty()) { - LOG(ERROR) << "Failed to get the hash of the serial number, " - << "will not attempt to auto-enroll."; - } else { - time_start_ = base::Time::Now(); - SendRequest(power_initial_); - // Don't invoke the callback now. - return; - } - } + state_ = STATE_PENDING; + time_start_ = base::Time::Now(); + modulus_updates_received_ = 0; + has_server_state_ = false; + device_state_available_ = false; - // Auto-enrollment can't even start, so we're done. - OnProtocolDone(); + NextStep(); } void AutoEnrollmentClient::CancelAndDeleteSoon() { - if (time_start_.is_null()) { + if (time_start_.is_null() || !request_job_) { // The client isn't running, just delete it. delete this; } else { @@ -211,40 +264,29 @@ void AutoEnrollmentClient::CancelAndDeleteSoon() { // anymore. Wait until the protocol completes to measure the extra time // needed. time_extra_start_ = base::Time::Now(); - completion_callback_.Reset(); + progress_callback_.Reset(); } } void AutoEnrollmentClient::OnNetworkChanged( net::NetworkChangeNotifier::ConnectionType type) { - if (GetCachedDecision()) { - // A previous request already obtained a definitive response from the - // server, so there is no point in retrying; it will get the same decision. - return; - } - if (type != net::NetworkChangeNotifier::CONNECTION_NONE && - !completion_callback_.is_null() && - !request_job_ && - device_management_service_ && - !serial_number_hash_.empty()) { - VLOG(1) << "Retrying auto enrollment check after network changed"; - time_start_ = base::Time::Now(); - SendRequest(power_initial_); + !progress_callback_.is_null()) { + RetryStep(); } } bool AutoEnrollmentClient::GetCachedDecision() { - const PrefService::Preference* should_enroll_pref = + const PrefService::Preference* has_server_state_pref = local_state_->FindPreference(prefs::kShouldAutoEnroll); const PrefService::Preference* previous_limit_pref = local_state_->FindPreference(prefs::kAutoEnrollmentPowerLimit); - bool should_auto_enroll = false; + bool has_server_state = false; int previous_limit = -1; - if (!should_enroll_pref || - should_enroll_pref->IsDefaultValue() || - !should_enroll_pref->GetValue()->GetAsBoolean(&should_auto_enroll) || + if (!has_server_state_pref || + has_server_state_pref->IsDefaultValue() || + !has_server_state_pref->GetValue()->GetAsBoolean(&has_server_state) || !previous_limit_pref || previous_limit_pref->IsDefaultValue() || !previous_limit_pref->GetValue()->GetAsInteger(&previous_limit) || @@ -252,25 +294,77 @@ bool AutoEnrollmentClient::GetCachedDecision() { return false; } - should_auto_enroll_ = should_auto_enroll; + has_server_state_ = has_server_state; return true; } -void AutoEnrollmentClient::SendRequest(int power) { - if (power < 0 || power > power_limit_ || serial_number_hash_.empty()) { - NOTREACHED(); - OnRequestDone(); - return; +bool AutoEnrollmentClient::RetryStep() { + // If there is a pending request job, let it finish. + if (request_job_) + return true; + + if (GetCachedDecision()) { + // The bucket download check has completed already. If it came back + // positive, then device state should be (re-)downloaded. + if (has_server_state_) { + if (retrieve_device_state_ && !device_state_available_ && + SendDeviceStateRequest()) { + return true; + } + } + } else { + // Start bucket download. + if (SendBucketDownloadRequest()) + return true; + } + + return false; +} + +void AutoEnrollmentClient::ReportProgress(State state) { + state_ = state; + if (progress_callback_.is_null()) { + base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE, this); + } else { + progress_callback_.Run(state_); + } +} + +void AutoEnrollmentClient::NextStep() { + if (!RetryStep()) { + // Protocol finished successfully, report result. + bool trigger_enrollment = false; + if (retrieve_device_state_) { + const base::DictionaryValue* device_state_dict = + local_state_->GetDictionary(prefs::kServerBackedDeviceState); + std::string restore_mode; + device_state_dict->GetString(kDeviceStateRestoreMode, &restore_mode); + trigger_enrollment = + (restore_mode == kDeviceStateRestoreModeReEnrollmentRequested || + restore_mode == kDeviceStateRestoreModeReEnrollmentEnforced); + } else { + trigger_enrollment = has_server_state_; + } + + ReportProgress(trigger_enrollment ? STATE_TRIGGER_ENROLLMENT + : STATE_NO_ENROLLMENT); } +} + +bool AutoEnrollmentClient::SendBucketDownloadRequest() { + if (server_backed_state_key_hash_.empty()) + return false; // Only power-of-2 moduli are supported for now. These are computed by taking - // the lower |power| bits of the hash. + // the lower |current_power_| bits of the hash. uint64 remainder = 0; - for (int i = 0; 8 * i < power; ++i) { - uint64 byte = serial_number_hash_[31 - i] & 0xff; + for (int i = 0; 8 * i < current_power_; ++i) { + uint64 byte = server_backed_state_key_hash_[31 - i] & 0xff; remainder = remainder | (byte << (8 * i)); } - remainder = remainder & ((GG_UINT64_C(1) << power) - 1); + remainder = remainder & ((GG_UINT64_C(1) << current_power_) - 1); + + ReportProgress(STATE_PENDING); request_job_.reset( device_management_service_->CreateJob( @@ -280,28 +374,72 @@ void AutoEnrollmentClient::SendRequest(int power) { em::DeviceAutoEnrollmentRequest* request = request_job_->GetRequest()->mutable_auto_enrollment_request(); request->set_remainder(remainder); - request->set_modulus(GG_INT64_C(1) << power); - request_job_->Start(base::Bind(&AutoEnrollmentClient::OnRequestCompletion, - base::Unretained(this))); + request->set_modulus(GG_INT64_C(1) << current_power_); + request_job_->Start( + base::Bind(&AutoEnrollmentClient::HandleRequestCompletion, + base::Unretained(this), + &AutoEnrollmentClient::OnBucketDownloadRequestCompletion)); + return true; +} + +bool AutoEnrollmentClient::SendDeviceStateRequest() { + ReportProgress(STATE_PENDING); + + request_job_.reset( + device_management_service_->CreateJob( + DeviceManagementRequestJob::TYPE_DEVICE_STATE_RETRIEVAL, + request_context_.get())); + request_job_->SetClientID(device_id_); + em::DeviceStateRetrievalRequest* request = + request_job_->GetRequest()->mutable_device_state_retrieval_request(); + request->set_server_backed_state_key(server_backed_state_key_); + request_job_->Start( + base::Bind(&AutoEnrollmentClient::HandleRequestCompletion, + base::Unretained(this), + &AutoEnrollmentClient::OnDeviceStateRequestCompletion)); + return true; } -void AutoEnrollmentClient::OnRequestCompletion( +void AutoEnrollmentClient::HandleRequestCompletion( + RequestCompletionHandler handler, DeviceManagementStatus status, int net_error, const em::DeviceManagementResponse& response) { - if (status != DM_STATUS_SUCCESS || !response.has_auto_enrollment_response()) { + UMA_HISTOGRAM_SPARSE_SLOWLY(kUMARequestStatus, status); + if (status != DM_STATUS_SUCCESS) { LOG(ERROR) << "Auto enrollment error: " << status; - UMA_HISTOGRAM_SPARSE_SLOWLY(kUMARequestStatus, status); if (status == DM_STATUS_REQUEST_FAILED) UMA_HISTOGRAM_SPARSE_SLOWLY(kUMANetworkErrorCode, -net_error); - // The client will retry if a network change is detected. - OnRequestDone(); + request_job_.reset(); + + // Abort if CancelAndDeleteSoon has been called meanwhile. + if (progress_callback_.is_null()) { + base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE, this); + } else { + ReportProgress(status == DM_STATUS_REQUEST_FAILED ? STATE_CONNECTION_ERROR + : STATE_SERVER_ERROR); + } return; } + bool progress = (this->*handler)(status, net_error, response); + request_job_.reset(); + if (progress) + NextStep(); + else + ReportProgress(STATE_SERVER_ERROR); +} + +bool AutoEnrollmentClient::OnBucketDownloadRequestCompletion( + DeviceManagementStatus status, + int net_error, + const em::DeviceManagementResponse& response) { + bool progress = false; const em::DeviceAutoEnrollmentResponse& enrollment_response = response.auto_enrollment_response(); - if (enrollment_response.has_expected_modulus()) { + if (!response.has_auto_enrollment_response()) { + LOG(ERROR) << "Server failed to provide auto-enrollment response."; + } else if (enrollment_response.has_expected_modulus()) { // Server is asking us to retry with a different modulus. modulus_updates_received_++; @@ -322,44 +460,76 @@ void AutoEnrollmentClient::OnRequestCompletion( << power_limit_ << ")."; } else { // Retry at most once with the modulus that the server requested. - if (power <= power_initial_) { + if (power <= current_power_) { LOG(WARNING) << "Auto enrollment: the server asked to use a modulus (" << power << ") that isn't larger than the first used (" - << power_initial_ << "). Retrying anyway."; + << current_power_ << "). Retrying anyway."; } // Remember this value, so that eventual retries start with the correct // modulus. - power_initial_ = power; - SendRequest(power); - return; + current_power_ = power; + return true; } } else { // Server should have sent down a list of hashes to try. - should_auto_enroll_ = IsSerialInProtobuf(enrollment_response.hash()); + has_server_state_ = IsIdHashInProtobuf(enrollment_response.hash()); // Cache the current decision in local_state, so that it is reused in case // the device reboots before enrolling. - local_state_->SetBoolean(prefs::kShouldAutoEnroll, should_auto_enroll_); + local_state_->SetBoolean(prefs::kShouldAutoEnroll, has_server_state_); local_state_->SetInteger(prefs::kAutoEnrollmentPowerLimit, power_limit_); local_state_->CommitPendingWrite(); - VLOG(1) << "Auto enrollment complete, should_auto_enroll = " - << should_auto_enroll_; + VLOG(1) << "Auto enrollment check complete, has_server_state_ = " + << has_server_state_; + progress = true; } - // Auto-enrollment done. - UMA_HISTOGRAM_SPARSE_SLOWLY(kUMARequestStatus, DM_STATUS_SUCCESS); - OnProtocolDone(); + // Bucket download done, update UMA. + UpdateBucketDownloadTimingHistograms(); + return progress; } -bool AutoEnrollmentClient::IsSerialInProtobuf( +bool AutoEnrollmentClient::OnDeviceStateRequestCompletion( + DeviceManagementStatus status, + int net_error, + const enterprise_management::DeviceManagementResponse& response) { + bool progress = false; + if (!response.has_device_state_retrieval_response()) { + LOG(ERROR) << "Server failed to provide auto-enrollment response."; + } else { + const em::DeviceStateRetrievalResponse& state_response = + response.device_state_retrieval_response(); + { + DictionaryPrefUpdate dict(local_state_, prefs::kServerBackedDeviceState); + UpdateDict(dict.Get(), + kDeviceStateManagementDomain, + state_response.has_management_domain(), + new base::StringValue(state_response.management_domain())); + + std::string restore_mode = + ConvertRestoreMode(state_response.restore_mode()); + UpdateDict(dict.Get(), + kDeviceStateRestoreMode, + !restore_mode.empty(), + new base::StringValue(restore_mode)); + } + local_state_->CommitPendingWrite(); + device_state_available_ = true; + progress = true; + } + + return progress; +} + +bool AutoEnrollmentClient::IsIdHashInProtobuf( const google::protobuf::RepeatedPtrField<std::string>& hashes) { for (int i = 0; i < hashes.size(); ++i) { - if (hashes.Get(i) == serial_number_hash_) + if (hashes.Get(i) == server_backed_state_key_hash_) return true; } return false; } -void AutoEnrollmentClient::OnProtocolDone() { +void AutoEnrollmentClient::UpdateBucketDownloadTimingHistograms() { // The mininum time can't be 0, must be at least 1. static const base::TimeDelta kMin = base::TimeDelta::FromMilliseconds(1); static const base::TimeDelta kMax = base::TimeDelta::FromMinutes(5); @@ -379,21 +549,6 @@ void AutoEnrollmentClient::OnProtocolDone() { // measure the ratio of users that succeeded without needing a delay to the // total users going through OOBE. UMA_HISTOGRAM_CUSTOM_TIMES(kUMAExtraTime, delta, kMin, kMax, kBuckets); - - if (!completion_callback_.is_null()) - completion_callback_.Run(); - - OnRequestDone(); -} - -void AutoEnrollmentClient::OnRequestDone() { - request_job_.reset(); - time_start_ = base::Time(); - - if (completion_callback_.is_null()) { - // CancelAndDeleteSoon() was invoked before. - base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE, this); - } } } // namespace policy diff --git a/chrome/browser/chromeos/policy/auto_enrollment_client.h b/chrome/browser/chromeos/policy/auto_enrollment_client.h index e27065a..0c8a0e8 100644 --- a/chrome/browser/chromeos/policy/auto_enrollment_client.h +++ b/chrome/browser/chromeos/policy/auto_enrollment_client.h @@ -38,17 +38,35 @@ class DeviceManagementService; class AutoEnrollmentClient : public net::NetworkChangeNotifier::NetworkChangeObserver { public: - // |completion_callback| will be invoked on completion of the protocol, after - // Start() is invoked. + // Indicates the current state of the auto-enrollment check. + enum State { + // Working, another event will be fired eventually. + STATE_PENDING, + // Failed to connect to DMServer. + STATE_CONNECTION_ERROR, + // Connection successful, but the server failed to generate a valid reply. + STATE_SERVER_ERROR, + // Check completed successfully, enrollment should be triggered. + STATE_TRIGGER_ENROLLMENT, + // Check completed successfully, enrollment not applicable. + STATE_NO_ENROLLMENT, + }; + + // Used for signaling progress to a consumer. + typedef base::Callback<void(State)> ProgressCallback; + + // |progress_callback| will be invoked whenever some significant event happens + // as part of the protocol, after Start() is invoked. // The result of the protocol will be cached in |local_state|. // |power_initial| and |power_limit| are exponents of power-of-2 values which // will be the initial modulus and the maximum modulus used by this client. AutoEnrollmentClient( - const base::Closure& completion_callback, + const ProgressCallback& progress_callback, DeviceManagementService* device_management_service, PrefService* local_state, scoped_refptr<net::URLRequestContextGetter> system_request_context, - const std::string& serial_number, + const std::string& server_backed_state_key, + bool retrieve_device_state, int power_initial, int power_limit); virtual ~AutoEnrollmentClient(); @@ -61,7 +79,9 @@ class AutoEnrollmentClient static bool IsDisabled(); // Convenience method to create instances of this class. - static AutoEnrollmentClient* Create(const base::Closure& completion_callback); + // TODO(mnissler): Convert callers to pass a ProgressCallback here. + static AutoEnrollmentClient* Create( + const base::Closure& completion_callback); // Cancels auto-enrollment. // This function does not interrupt a running auto-enrollment check. It only @@ -71,70 +91,111 @@ class AutoEnrollmentClient // Starts the auto-enrollment check protocol with the device management // service. Subsequent calls drop any previous requests. Notice that this - // call can invoke the |completion_callback_| if errors occur. + // call can invoke the |progress_callback_| if errors occur. void Start(); - // Cancels any pending requests. |completion_callback_| will not be invoked. + // Cancels any pending requests. |progress_callback_| will not be invoked. // |this| will delete itself. void CancelAndDeleteSoon(); // Returns true if the protocol completed successfully and determined that // this device should do enterprise enrollment. - bool should_auto_enroll() const { return should_auto_enroll_; } + // TODO(mnissler): Remove once callers have been converted to state(). + bool should_auto_enroll() const { + return state_ == STATE_TRIGGER_ENROLLMENT; + }; // Returns the device_id randomly generated for the auto-enrollment requests. // It can be reused for subsequent requests to the device management service. std::string device_id() const { return device_id_; } + // Current state. + State state() const { return state_; } + // Implementation of net::NetworkChangeNotifier::NetworkChangeObserver: virtual void OnNetworkChanged( net::NetworkChangeNotifier::ConnectionType type) OVERRIDE; private: + typedef bool (AutoEnrollmentClient::*RequestCompletionHandler)( + DeviceManagementStatus, + int, + const enterprise_management::DeviceManagementResponse&); + // Tries to load the result of a previous execution of the protocol from // local state. Returns true if that decision has been made and is valid. bool GetCachedDecision(); + // Kicks protocol processing, restarting the current step if applicable. + // Returns true if progress has been made, false if the protocol is done. + bool RetryStep(); + + // Cleans up and invokes |progress_callback_|. + void ReportProgress(State state); + + // Calls RetryStep() to make progress or determine that all is done. In the + // latter case, calls ReportProgress(). + void NextStep(); + // Sends an auto-enrollment check request to the device management service. - // |power| is the power of the power-of-2 to use as a modulus for this - // request. - void SendRequest(int power); + bool SendBucketDownloadRequest(); + + // Sends a device state download request to the device management service. + bool SendDeviceStateRequest(); + + // Runs the response handler for device management requests and calls + // NextStep(). + void HandleRequestCompletion( + RequestCompletionHandler handler, + DeviceManagementStatus status, + int net_error, + const enterprise_management::DeviceManagementResponse& response); + + // Parses the server response to a bucket download request. + bool OnBucketDownloadRequestCompletion( + DeviceManagementStatus status, + int net_error, + const enterprise_management::DeviceManagementResponse& response); - // Handles auto-enrollment request completion. - void OnRequestCompletion( + // Parses the server response to a device state request. + bool OnDeviceStateRequestCompletion( DeviceManagementStatus status, int net_error, const enterprise_management::DeviceManagementResponse& response); - // Returns true if |serial_number_hash_| is contained in |hashes|. - bool IsSerialInProtobuf( + // Returns true if |server_backed_state_key_hash_| is contained in |hashes|. + bool IsIdHashInProtobuf( const google::protobuf::RepeatedPtrField<std::string>& hashes); - // Invoked when the protocol completes. This invokes the callback and records - // some UMA metrics. - void OnProtocolDone(); + // Updates UMA histograms for bucket download timings. + void UpdateBucketDownloadTimingHistograms(); - // Invoked when a request job completes. Resets the internal state, and - // deletes the client if necessary. - void OnRequestDone(); + // Callback to invoke when the protocol generates a relevant event. This can + // be either successful completion or an error that requires external action. + ProgressCallback progress_callback_; - // Callback to invoke when the protocol completes. - base::Closure completion_callback_; + // Current state. + State state_; - // Whether to auto-enroll or not. This is reset by calls to Start(), and only - // turns true if the protocol and the serial number check succeed. - bool should_auto_enroll_; + // Whether the hash bucket check succeeded, indicating that the server knows + // this device and might have keep state for it. + bool has_server_state_; + + // Whether the download of server-kept device state completed successfully. + bool device_state_available_; // Randomly generated device id for the auto-enrollment requests. std::string device_id_; - // SHA256 hash of the device's serial number. Empty if the serial couldn't be - // retrieved. - std::string serial_number_hash_; + // Stable state key and its SHA-256 digest. + std::string server_backed_state_key_; + std::string server_backed_state_key_hash_; + + // Whether device state should be retrieved from the server. + bool retrieve_device_state_; - // Power of the power-of-2 modulus used in the initial auto-enrollment - // request. - int power_initial_; + // Power-of-2 modulus to try next. + int current_power_; // Power of the maximum power-of-2 modulus that this client will accept from // a retry response from the server. diff --git a/chrome/browser/chromeos/policy/auto_enrollment_client_unittest.cc b/chrome/browser/chromeos/policy/auto_enrollment_client_unittest.cc index 91751eb..cae311e 100644 --- a/chrome/browser/chromeos/policy/auto_enrollment_client_unittest.cc +++ b/chrome/browser/chromeos/policy/auto_enrollment_client_unittest.cc @@ -9,6 +9,8 @@ #include "base/message_loop/message_loop.h" #include "base/prefs/pref_service.h" #include "base/prefs/testing_pref_service.h" +#include "base/run_loop.h" +#include "base/strings/stringprintf.h" #include "base/values.h" #include "chrome/browser/browser_process.h" #include "chrome/common/pref_names.h" @@ -28,10 +30,10 @@ namespace policy { namespace { -const char* kSerial = "serial"; -const char* kSerialHash = - "\x01\x44\xb1\xde\xfc\xf7\x56\x10\x87\x01\x5f\x8d\x83\x0d\x65\xb1" - "\x6f\x02\x4a\xd7\xeb\x92\x45\xfc\xd4\xe4\x37\xa1\x55\x2b\x13\x8a"; +const char kStateKey[] = "state_key"; +const char kStateKeyHash[] = + "\xde\x74\xcd\xf0\x03\x36\x8c\x21\x79\xba\xb1\x5a\xc4\x32\xee\xd6" + "\xb3\x4a\x5e\xff\x73\x7e\x92\xd9\xf8\x6e\x72\x44\xd0\x97\xc3\xe6"; using ::testing::InSequence; using ::testing::Mock; @@ -44,36 +46,42 @@ class AutoEnrollmentClientTest : public testing::Test { : scoped_testing_local_state_( TestingBrowserProcess::GetGlobal()), local_state_(scoped_testing_local_state_.Get()), - completion_callback_count_(0) {} + state_(AutoEnrollmentClient::STATE_PENDING) {} virtual void SetUp() OVERRIDE { - CreateClient(kSerial, 4, 8); + CreateClient(kStateKey, true, 4, 8); ASSERT_FALSE(local_state_->GetUserPref(prefs::kShouldAutoEnroll)); ASSERT_FALSE(local_state_->GetUserPref(prefs::kAutoEnrollmentPowerLimit)); } - void CreateClient(const std::string& serial, + virtual void TearDown() OVERRIDE { + // Flush any deletion tasks. + base::RunLoop().RunUntilIdle(); + } + + void CreateClient(const std::string& state_key, + bool retrieve_device_state, int power_initial, int power_limit) { + state_ = AutoEnrollmentClient::STATE_PENDING; service_.reset(new MockDeviceManagementService()); EXPECT_CALL(*service_, StartJob(_, _, _, _, _, _, _)) .WillRepeatedly(SaveArg<6>(&last_request_)); - base::Closure callback = - base::Bind(&AutoEnrollmentClientTest::CompletionCallback, - base::Unretained(this)); client_.reset(new AutoEnrollmentClient( - callback, + base::Bind(&AutoEnrollmentClientTest::ProgressCallback, + base::Unretained(this)), service_.get(), local_state_, new net::TestURLRequestContextGetter( base::MessageLoop::current()->message_loop_proxy()), - serial, + state_key, + retrieve_device_state, power_initial, power_limit)); } - void CompletionCallback() { - completion_callback_count_++; + void ProgressCallback(AutoEnrollmentClient::State state) { + state_ = state; } void ServerWillFail(DeviceManagementStatus error) { @@ -83,7 +91,7 @@ class AutoEnrollmentClientTest : public testing::Test { .WillOnce(service_->FailJob(error)); } - void ServerWillReply(int64 modulus, bool with_hashes, bool with_serial_hash) { + void ServerWillReply(int64 modulus, bool with_hashes, bool with_id_hash) { em::DeviceManagementResponse response; em::DeviceAutoEnrollmentResponse* enrollment_response = response.mutable_auto_enrollment_response(); @@ -91,21 +99,34 @@ class AutoEnrollmentClientTest : public testing::Test { enrollment_response->set_expected_modulus(modulus); if (with_hashes) { for (size_t i = 0; i < 10; ++i) { - std::string serial = "serial X"; - serial[7] = '0' + i; - std::string hash = crypto::SHA256HashString(serial); + std::string state_key = base::StringPrintf("state_key %lu", i); + std::string hash = crypto::SHA256HashString(state_key); enrollment_response->mutable_hash()->Add()->assign(hash); } } - if (with_serial_hash) { - enrollment_response->mutable_hash()->Add()->assign(kSerialHash, - crypto::kSHA256Length); + if (with_id_hash) { + enrollment_response->mutable_hash()->Add()->assign(kStateKeyHash, + crypto::kSHA256Length); } EXPECT_CALL(*service_, CreateJob(DeviceManagementRequestJob::TYPE_AUTO_ENROLLMENT, _)) .WillOnce(service_->SucceedJob(response)); } + void ServerWillSendState( + const std::string& management_domain, + em::DeviceStateRetrievalResponse::RestoreMode restore_mode) { + em::DeviceManagementResponse response; + em::DeviceStateRetrievalResponse* state_response = + response.mutable_device_state_retrieval_response(); + state_response->set_restore_mode(restore_mode); + state_response->set_management_domain(management_domain); + EXPECT_CALL( + *service_, + CreateJob(DeviceManagementRequestJob::TYPE_DEVICE_STATE_RETRIEVAL, _)) + .WillOnce(service_->SucceedJob(response)); + } + void ServerWillReplyAsync(MockDeviceManagementJob** job) { EXPECT_CALL(*service_, CreateJob(DeviceManagementRequestJob::TYPE_AUTO_ENROLLMENT, _)) @@ -137,7 +158,7 @@ class AutoEnrollmentClientTest : public testing::Test { scoped_ptr<MockDeviceManagementService> service_; scoped_ptr<AutoEnrollmentClient> client_; em::DeviceManagementRequest last_request_; - int completion_callback_count_; + AutoEnrollmentClient::State state_; private: DISALLOW_COPY_AND_ASSIGN(AutoEnrollmentClientTest); @@ -146,29 +167,26 @@ class AutoEnrollmentClientTest : public testing::Test { TEST_F(AutoEnrollmentClientTest, NetworkFailure) { ServerWillFail(DM_STATUS_TEMPORARY_UNAVAILABLE); client_->Start(); - EXPECT_FALSE(client_->should_auto_enroll()); - EXPECT_EQ(0, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_SERVER_ERROR, state_); EXPECT_FALSE(HasCachedDecision()); } TEST_F(AutoEnrollmentClientTest, EmptyReply) { ServerWillReply(-1, false, false); client_->Start(); - EXPECT_FALSE(client_->should_auto_enroll()); - EXPECT_EQ(1, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_NO_ENROLLMENT, state_); VerifyCachedResult(false, 8); } TEST_F(AutoEnrollmentClientTest, ClientUploadsRightBits) { ServerWillReply(-1, false, false); client_->Start(); - EXPECT_FALSE(client_->should_auto_enroll()); - EXPECT_EQ(1, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_NO_ENROLLMENT, state_); EXPECT_TRUE(auto_enrollment_request().has_remainder()); EXPECT_TRUE(auto_enrollment_request().has_modulus()); EXPECT_EQ(16, auto_enrollment_request().modulus()); - EXPECT_EQ(kSerialHash[31] & 0xf, auto_enrollment_request().remainder()); + EXPECT_EQ(kStateKeyHash[31] & 0xf, auto_enrollment_request().remainder()); VerifyCachedResult(false, 8); } @@ -177,8 +195,7 @@ TEST_F(AutoEnrollmentClientTest, AskForMoreThenFail) { ServerWillReply(32, false, false); ServerWillFail(DM_STATUS_TEMPORARY_UNAVAILABLE); client_->Start(); - EXPECT_FALSE(client_->should_auto_enroll()); - EXPECT_EQ(0, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_SERVER_ERROR, state_); EXPECT_FALSE(HasCachedDecision()); } @@ -187,8 +204,7 @@ TEST_F(AutoEnrollmentClientTest, AskForMoreThenEvenMore) { ServerWillReply(32, false, false); ServerWillReply(64, false, false); client_->Start(); - EXPECT_FALSE(client_->should_auto_enroll()); - EXPECT_EQ(1, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_SERVER_ERROR, state_); EXPECT_FALSE(HasCachedDecision()); } @@ -196,9 +212,11 @@ TEST_F(AutoEnrollmentClientTest, AskForLess) { InSequence sequence; ServerWillReply(8, false, false); ServerWillReply(-1, true, true); + ServerWillSendState( + "example.com", + em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED); client_->Start(); - EXPECT_TRUE(client_->should_auto_enroll()); - EXPECT_EQ(1, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_TRIGGER_ENROLLMENT, state_); VerifyCachedResult(true, 8); } @@ -206,9 +224,11 @@ TEST_F(AutoEnrollmentClientTest, AskForSame) { InSequence sequence; ServerWillReply(16, false, false); ServerWillReply(-1, true, true); + ServerWillSendState( + "example.com", + em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED); client_->Start(); - EXPECT_TRUE(client_->should_auto_enroll()); - EXPECT_EQ(1, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_TRIGGER_ENROLLMENT, state_); VerifyCachedResult(true, 8); } @@ -217,16 +237,14 @@ TEST_F(AutoEnrollmentClientTest, AskForSameTwice) { ServerWillReply(16, false, false); ServerWillReply(16, false, false); client_->Start(); - EXPECT_FALSE(client_->should_auto_enroll()); - EXPECT_EQ(1, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_SERVER_ERROR, state_); EXPECT_FALSE(HasCachedDecision()); } TEST_F(AutoEnrollmentClientTest, AskForTooMuch) { ServerWillReply(512, false, false); client_->Start(); - EXPECT_FALSE(client_->should_auto_enroll()); - EXPECT_EQ(1, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_SERVER_ERROR, state_); EXPECT_FALSE(HasCachedDecision()); } @@ -235,55 +253,53 @@ TEST_F(AutoEnrollmentClientTest, AskNonPowerOf2) { ServerWillReply(100, false, false); ServerWillReply(-1, false, false); client_->Start(); - EXPECT_FALSE(client_->should_auto_enroll()); - EXPECT_EQ(1, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_NO_ENROLLMENT, state_); EXPECT_TRUE(auto_enrollment_request().has_remainder()); EXPECT_TRUE(auto_enrollment_request().has_modulus()); EXPECT_EQ(128, auto_enrollment_request().modulus()); - EXPECT_EQ(kSerialHash[31] & 0x7f, auto_enrollment_request().remainder()); + EXPECT_EQ(kStateKeyHash[31] & 0x7f, auto_enrollment_request().remainder()); VerifyCachedResult(false, 8); } TEST_F(AutoEnrollmentClientTest, ConsumerDevice) { ServerWillReply(-1, true, false); client_->Start(); - EXPECT_FALSE(client_->should_auto_enroll()); - EXPECT_EQ(1, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_NO_ENROLLMENT, state_); VerifyCachedResult(false, 8); // Network changes don't trigger retries after obtaining a response from // the server. client_->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_ETHERNET); - EXPECT_EQ(1, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_NO_ENROLLMENT, state_); } TEST_F(AutoEnrollmentClientTest, EnterpriseDevice) { ServerWillReply(-1, true, true); + ServerWillSendState( + "example.com", + em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED); client_->Start(); - EXPECT_TRUE(client_->should_auto_enroll()); - EXPECT_EQ(1, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_TRIGGER_ENROLLMENT, state_); VerifyCachedResult(true, 8); // Network changes don't trigger retries after obtaining a response from // the server. client_->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_ETHERNET); - EXPECT_EQ(1, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_TRIGGER_ENROLLMENT, state_); } TEST_F(AutoEnrollmentClientTest, NoSerial) { - CreateClient("", 4, 8); + CreateClient("", true, 4, 8); client_->Start(); - EXPECT_FALSE(client_->should_auto_enroll()); - EXPECT_EQ(1, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_NO_ENROLLMENT, state_); EXPECT_FALSE(HasCachedDecision()); } TEST_F(AutoEnrollmentClientTest, NoBitsUploaded) { - CreateClient(kSerial, 0, 0); + CreateClient(kStateKey, true, 0, 0); ServerWillReply(-1, false, false); client_->Start(); - EXPECT_FALSE(client_->should_auto_enroll()); - EXPECT_EQ(1, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_NO_ENROLLMENT, state_); EXPECT_TRUE(auto_enrollment_request().has_remainder()); EXPECT_TRUE(auto_enrollment_request().has_modulus()); EXPECT_EQ(1, auto_enrollment_request().modulus()); @@ -292,14 +308,12 @@ TEST_F(AutoEnrollmentClientTest, NoBitsUploaded) { } TEST_F(AutoEnrollmentClientTest, ManyBitsUploaded) { - int64 bottom62 = GG_INT64_C(0x14e437a1552b138a); + int64 bottom62 = GG_INT64_C(0x386e7244d097c3e6); for (int i = 0; i <= 62; ++i) { - completion_callback_count_ = 0; - CreateClient(kSerial, i, i); + CreateClient(kStateKey, true, i, i); ServerWillReply(-1, false, false); client_->Start(); - EXPECT_FALSE(client_->should_auto_enroll()); - EXPECT_EQ(1, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_NO_ENROLLMENT, state_); EXPECT_TRUE(auto_enrollment_request().has_remainder()); EXPECT_TRUE(auto_enrollment_request().has_modulus()); EXPECT_EQ(GG_INT64_C(1) << i, auto_enrollment_request().modulus()); @@ -310,13 +324,15 @@ TEST_F(AutoEnrollmentClientTest, ManyBitsUploaded) { } TEST_F(AutoEnrollmentClientTest, MoreThan32BitsUploaded) { - CreateClient(kSerial, 10, 37); + CreateClient(kStateKey, true, 10, 37); InSequence sequence; ServerWillReply(GG_INT64_C(1) << 37, false, false); ServerWillReply(-1, true, true); + ServerWillSendState( + "example.com", + em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED); client_->Start(); - EXPECT_TRUE(client_->should_auto_enroll()); - EXPECT_EQ(1, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_TRIGGER_ENROLLMENT, state_); VerifyCachedResult(true, 37); } @@ -326,13 +342,14 @@ TEST_F(AutoEnrollmentClientTest, ReuseCachedDecision) { base::Value::CreateBooleanValue(true)); local_state_->SetUserPref(prefs::kAutoEnrollmentPowerLimit, base::Value::CreateIntegerValue(8)); + ServerWillSendState( + "example.com", + em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED); client_->Start(); - EXPECT_TRUE(client_->should_auto_enroll()); - EXPECT_EQ(1, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_TRIGGER_ENROLLMENT, state_); AutoEnrollmentClient::CancelAutoEnrollment(); client_->Start(); - EXPECT_FALSE(client_->should_auto_enroll()); - EXPECT_EQ(2, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_NO_ENROLLMENT, state_); } TEST_F(AutoEnrollmentClientTest, RetryIfPowerLargerThanCached) { @@ -340,39 +357,40 @@ TEST_F(AutoEnrollmentClientTest, RetryIfPowerLargerThanCached) { base::Value::CreateBooleanValue(false)); local_state_->SetUserPref(prefs::kAutoEnrollmentPowerLimit, base::Value::CreateIntegerValue(8)); - CreateClient(kSerial, 5, 10); + CreateClient(kStateKey, true, 5, 10); ServerWillReply(-1, true, true); + ServerWillSendState( + "example.com", + em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED); client_->Start(); - EXPECT_TRUE(client_->should_auto_enroll()); - EXPECT_EQ(1, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_TRIGGER_ENROLLMENT, state_); } TEST_F(AutoEnrollmentClientTest, NetworkChangeRetryAfterErrors) { ServerWillFail(DM_STATUS_TEMPORARY_UNAVAILABLE); client_->Start(); - EXPECT_FALSE(client_->should_auto_enroll()); // Don't invoke the callback if there was a network failure. - EXPECT_EQ(0, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_SERVER_ERROR, state_); EXPECT_FALSE(HasCachedDecision()); // The client doesn't retry if no new connection became available. client_->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_NONE); - EXPECT_FALSE(client_->should_auto_enroll()); - EXPECT_EQ(0, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_SERVER_ERROR, state_); EXPECT_FALSE(HasCachedDecision()); // Retry once the network is back. ServerWillReply(-1, true, true); + ServerWillSendState( + "example.com", + em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED); client_->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_ETHERNET); - EXPECT_TRUE(client_->should_auto_enroll()); - EXPECT_EQ(1, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_TRIGGER_ENROLLMENT, state_); EXPECT_TRUE(HasCachedDecision()); // Subsequent network changes don't trigger retries. client_->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_NONE); client_->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_ETHERNET); - EXPECT_TRUE(client_->should_auto_enroll()); - EXPECT_EQ(1, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_TRIGGER_ENROLLMENT, state_); EXPECT_TRUE(HasCachedDecision()); } @@ -382,7 +400,7 @@ TEST_F(AutoEnrollmentClientTest, CancelAndDeleteSoonWithPendingRequest) { EXPECT_FALSE(job); client_->Start(); ASSERT_TRUE(job); - EXPECT_EQ(0, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_PENDING, state_); // Cancel while a request is in flight. EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting()); @@ -394,7 +412,7 @@ TEST_F(AutoEnrollmentClientTest, CancelAndDeleteSoonWithPendingRequest) { em::DeviceManagementResponse()); // The DeleteSoon task has been posted: EXPECT_FALSE(base::MessageLoop::current()->IsIdleForTesting()); - EXPECT_EQ(0, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_PENDING, state_); } TEST_F(AutoEnrollmentClientTest, NetworkChangedAfterCancelAndDeleteSoon) { @@ -403,7 +421,7 @@ TEST_F(AutoEnrollmentClientTest, NetworkChangedAfterCancelAndDeleteSoon) { EXPECT_FALSE(job); client_->Start(); ASSERT_TRUE(job); - EXPECT_EQ(0, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_PENDING, state_); // Cancel while a request is in flight. EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting()); @@ -413,25 +431,27 @@ TEST_F(AutoEnrollmentClientTest, NetworkChangedAfterCancelAndDeleteSoon) { // Network change events are ignored while a request is pending. client->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_ETHERNET); - EXPECT_EQ(0, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_PENDING, state_); // The client cleans itself up once a reply is received. job->SendResponse(DM_STATUS_TEMPORARY_UNAVAILABLE, em::DeviceManagementResponse()); // The DeleteSoon task has been posted: EXPECT_FALSE(base::MessageLoop::current()->IsIdleForTesting()); - EXPECT_EQ(0, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_PENDING, state_); // Network changes that have been posted before are also ignored: client->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_ETHERNET); - EXPECT_EQ(0, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_PENDING, state_); } TEST_F(AutoEnrollmentClientTest, CancelAndDeleteSoonAfterCompletion) { ServerWillReply(-1, true, true); + ServerWillSendState( + "example.com", + em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED); client_->Start(); - EXPECT_EQ(1, completion_callback_count_); - EXPECT_TRUE(client_->should_auto_enroll()); + EXPECT_EQ(AutoEnrollmentClient::STATE_TRIGGER_ENROLLMENT, state_); // The client will delete itself immediately if there are no pending // requests. @@ -443,8 +463,7 @@ TEST_F(AutoEnrollmentClientTest, CancelAndDeleteSoonAfterCompletion) { TEST_F(AutoEnrollmentClientTest, CancelAndDeleteSoonAfterNetworkFailure) { ServerWillFail(DM_STATUS_TEMPORARY_UNAVAILABLE); client_->Start(); - EXPECT_EQ(0, completion_callback_count_); - EXPECT_FALSE(client_->should_auto_enroll()); + EXPECT_EQ(AutoEnrollmentClient::STATE_SERVER_ERROR, state_); // The client will delete itself immediately if there are no pending // requests. @@ -460,9 +479,8 @@ TEST_F(AutoEnrollmentClientTest, NetworkFailureThenRequireUpdatedModulus) { ServerWillFail(DM_STATUS_REQUEST_FAILED); client_->Start(); - EXPECT_FALSE(client_->should_auto_enroll()); - // Don't invoke the callback if there was a network failure. - EXPECT_EQ(0, completion_callback_count_); + // Callback should signal the connection error. + EXPECT_EQ(AutoEnrollmentClient::STATE_CONNECTION_ERROR, state_); EXPECT_FALSE(HasCachedDecision()); Mock::VerifyAndClearExpectations(service_.get()); @@ -473,15 +491,29 @@ TEST_F(AutoEnrollmentClientTest, NetworkFailureThenRequireUpdatedModulus) { // Then reply with a valid response and include the hash. ServerWillReply(-1, true, true); EXPECT_CALL(*service_, StartJob(_, _, _, _, _, _, _)); + // State download triggers. + ServerWillSendState( + "example.com", + em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED); + EXPECT_CALL(*service_, StartJob(_, _, _, _, _, _, _)); // Trigger a network change event. client_->OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_ETHERNET); - EXPECT_TRUE(client_->should_auto_enroll()); - EXPECT_EQ(1, completion_callback_count_); + EXPECT_EQ(AutoEnrollmentClient::STATE_TRIGGER_ENROLLMENT, state_); EXPECT_TRUE(HasCachedDecision()); Mock::VerifyAndClearExpectations(service_.get()); } -} // namespace +TEST_F(AutoEnrollmentClientTest, NoDeviceStateRetrieval) { + CreateClient(kStateKey, false, 4, 8); + ServerWillReply(-1, true, true); + EXPECT_CALL(*service_, + CreateJob(DeviceManagementRequestJob::TYPE_DEVICE_STATE_RETRIEVAL, + _)).Times(0); + client_->Start(); + EXPECT_EQ(AutoEnrollmentClient::STATE_TRIGGER_ENROLLMENT, state_); + VerifyCachedResult(true, 8); +} +} // namespace } // namespace policy diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc index e6a7514..197b279 100644 --- a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc +++ b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc @@ -14,6 +14,7 @@ #include "chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h" #include "chrome/browser/chromeos/policy/enrollment_handler_chromeos.h" #include "chrome/browser/chromeos/policy/enterprise_install_attributes.h" +#include "chrome/browser/chromeos/policy/server_backed_device_state.h" #include "chrome/common/chrome_content_client.h" #include "chrome/common/pref_names.h" #include "chromeos/chromeos_constants.h" @@ -23,6 +24,7 @@ #include "components/policy/core/common/cloud/device_management_service.h" #include "components/policy/core/common/cloud/system_policy_request_context.h" #include "content/public/browser/browser_thread.h" +#include "crypto/sha2.h" #include "policy/proto/device_management_backend.pb.h" #include "url/gurl.h" @@ -188,6 +190,12 @@ void DeviceCloudPolicyManagerChromeOS::SetDeviceRequisition( } bool DeviceCloudPolicyManagerChromeOS::ShouldAutoStartEnrollment() const { + std::string restore_mode = GetRestoreMode(); + if (restore_mode == kDeviceStateRestoreModeReEnrollmentRequested || + restore_mode == kDeviceStateRestoreModeReEnrollmentEnforced) { + return true; + } + if (local_state_->HasPrefPath(prefs::kDeviceEnrollmentAutoStart)) return local_state_->GetBoolean(prefs::kDeviceEnrollmentAutoStart); @@ -195,6 +203,9 @@ bool DeviceCloudPolicyManagerChromeOS::ShouldAutoStartEnrollment() const { } bool DeviceCloudPolicyManagerChromeOS::CanExitEnrollment() const { + if (GetRestoreMode() == kDeviceStateRestoreModeReEnrollmentEnforced) + return false; + if (local_state_->HasPrefPath(prefs::kDeviceEnrollmentCanExit)) return local_state_->GetBoolean(prefs::kDeviceEnrollmentCanExit); @@ -221,6 +232,7 @@ void DeviceCloudPolicyManagerChromeOS::RegisterPrefs( std::string()); registry->RegisterBooleanPref(prefs::kDeviceEnrollmentAutoStart, false); registry->RegisterBooleanPref(prefs::kDeviceEnrollmentCanExit, true); + registry->RegisterDictionaryPref(prefs::kServerBackedDeviceState); } // static @@ -247,6 +259,15 @@ std::string DeviceCloudPolicyManagerChromeOS::GetMachineModel() { return GetMachineStatistic(kMachineInfoSystemHwqual); } +// static +std::string DeviceCloudPolicyManagerChromeOS::GetDeviceStateKey() { + // TODO(mnissler): Figure out which stable device identifiers should be used + // here and update the code. See http://crbug.com/352599. + std::string group_code_key = + GetMachineStatistic(chromeos::system::kOffersGroupCodeKey); + return crypto::SHA256HashString(group_code_key + GetMachineID()); +} + scoped_ptr<CloudPolicyClient> DeviceCloudPolicyManagerChromeOS::CreateClient() { scoped_refptr<net::URLRequestContextGetter> request_context = new SystemPolicyRequestContext( @@ -326,4 +347,12 @@ void DeviceCloudPolicyManagerChromeOS::InitalizeRequisition() { } } +std::string DeviceCloudPolicyManagerChromeOS::GetRestoreMode() const { + const base::DictionaryValue* device_state_dict = + local_state_->GetDictionary(prefs::kServerBackedDeviceState); + std::string restore_mode; + device_state_dict->GetString(kDeviceStateRestoreMode, &restore_mode); + return restore_mode; +} + } // namespace policy diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h index f5f36ed..970587a 100644 --- a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h +++ b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h @@ -98,6 +98,9 @@ class DeviceCloudPolicyManagerChromeOS : public CloudPolicyManager { // Returns the machine model, or an empty string if not available. static std::string GetMachineModel(); + // Returns the stable device state key. + static std::string GetDeviceStateKey(); + // Returns the robot 'email address' associated with the device robot // account (sometimes called a service account) associated with this device // during enterprise enrollment. @@ -118,6 +121,9 @@ class DeviceCloudPolicyManagerChromeOS : public CloudPolicyManager { // Initializes requisition settings at OOBE with values from VPD. void InitalizeRequisition(); + // Gets the device restore mode as stored in |local_state_|. + std::string GetRestoreMode() const; + // Points to the same object as the base CloudPolicyManager::store(), but with // actual device policy specific type. scoped_ptr<DeviceCloudPolicyStoreChromeOS> device_store_; diff --git a/chrome/browser/chromeos/policy/server_backed_device_state.cc b/chrome/browser/chromeos/policy/server_backed_device_state.cc new file mode 100644 index 0000000..2f023a7 --- /dev/null +++ b/chrome/browser/chromeos/policy/server_backed_device_state.cc @@ -0,0 +1,17 @@ +// Copyright (c) 2014 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. + +#include "chrome/browser/chromeos/policy/server_backed_device_state.h" + +namespace policy { + +const char kDeviceStateManagementDomain[] = "management_domain"; +const char kDeviceStateRestoreMode[] = "device_mode"; + +const char kDeviceStateRestoreModeReEnrollmentRequested[] = + "re-enrollment-requested"; +const char kDeviceStateRestoreModeReEnrollmentEnforced[] = + "re-enrollment-enforced"; + +} // namespace policy diff --git a/chrome/browser/chromeos/policy/server_backed_device_state.h b/chrome/browser/chromeos/policy/server_backed_device_state.h new file mode 100644 index 0000000..bb6d039 --- /dev/null +++ b/chrome/browser/chromeos/policy/server_backed_device_state.h @@ -0,0 +1,20 @@ +// Copyright (c) 2014 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_CHROMEOS_POLICY_SERVER_BACKED_DEVICE_STATE_H_ +#define CHROME_BROWSER_CHROMEOS_POLICY_SERVER_BACKED_DEVICE_STATE_H_ + +namespace policy { + +// Dictionary key constants for prefs::kServerBackedDeviceState. +extern const char kDeviceStateManagementDomain[]; +extern const char kDeviceStateRestoreMode[]; + +// Values for kDeviceStateRestoreMode. +extern const char kDeviceStateRestoreModeReEnrollmentEnforced[]; +extern const char kDeviceStateRestoreModeReEnrollmentRequested[]; + +} // namespace policy + +#endif // CHROME_BROWSER_CHROMEOS_POLICY_SERVER_BACKED_DEVICE_STATE_H_ diff --git a/chrome/browser/policy/test/policy_testserver.py b/chrome/browser/policy/test/policy_testserver.py index a246eaf..19a2971 100644 --- a/chrome/browser/policy/test/policy_testserver.py +++ b/chrome/browser/policy/test/policy_testserver.py @@ -285,18 +285,24 @@ class PolicyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): len(self.GetUniqueParam('agent')) >= 64): return (400, 'Invalid request parameter') if request_type == 'register': - return self.ProcessRegister(rmsg.register_request) - if request_type == 'api_authorization': - return self.ProcessApiAuthorization(rmsg.service_api_access_request) + response = self.ProcessRegister(rmsg.register_request) + elif request_type == 'api_authorization': + response = self.ProcessApiAuthorization(rmsg.service_api_access_request) elif request_type == 'unregister': - return self.ProcessUnregister(rmsg.unregister_request) + response = self.ProcessUnregister(rmsg.unregister_request) elif request_type == 'policy' or request_type == 'ping': - return self.ProcessPolicy(rmsg.policy_request, request_type) + response = self.ProcessPolicy(rmsg.policy_request, request_type) elif request_type == 'enterprise_check': - return self.ProcessAutoEnrollment(rmsg.auto_enrollment_request) + response = self.ProcessAutoEnrollment(rmsg.auto_enrollment_request) + elif request_type == 'device_state_retrieval': + response = self.ProcessDeviceStateRetrievalRequest( + rmsg.device_state_retrieval_request) else: return (400, 'Invalid request parameter') + self.DumpMessage('Response', response[1]) + return (response[0], response[1].SerializeToString()) + def CreatePolicyForExternalPolicyData(self, policy_key): """Returns an ExternalPolicyData protobuf for policy_key. @@ -372,9 +378,7 @@ class PolicyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): response.register_response.machine_name = token_info['machine_name'] response.register_response.enrollment_type = token_info['enrollment_mode'] - self.DumpMessage('Response', response) - - return (200, response.SerializeToString()) + return (200, response) def ProcessApiAuthorization(self, msg): """Handles an API authorization request. @@ -392,9 +396,8 @@ class PolicyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): response = dm.DeviceManagementResponse() response.service_api_access_response.auth_code = policy.get( 'robot_api_auth_code', 'policy_testserver.py-auth_code') - self.DumpMessage('Response', response) - return (200, response.SerializeToString()) + return (200, response) def ProcessUnregister(self, msg): """Handles a register request. @@ -420,9 +423,7 @@ class PolicyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): response = dm.DeviceManagementResponse() response.unregister_response.CopyFrom(dm.DeviceUnregisterResponse()) - self.DumpMessage('Response', response) - - return (200, response.SerializeToString()) + return (200, response) def ProcessPolicy(self, msg, request_type): """Handles a policy request. @@ -460,7 +461,7 @@ class PolicyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): fetch_response.error_code = 400 fetch_response.error_message = 'Invalid policy_type' - return (200, response.SerializeToString()) + return (200, response) def ProcessAutoEnrollment(self, msg): """Handles an auto-enrollment check request. @@ -499,7 +500,30 @@ class PolicyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): response = dm.DeviceManagementResponse() response.auto_enrollment_response.CopyFrom(auto_enrollment_response) - return (200, response.SerializeToString()) + return (200, response) + + def ProcessDeviceStateRetrievalRequest(self, msg): + """Handles a device state retrieval request. + + Response data is taken from server configuration. + + Returns: + A tuple of HTTP status code and response data to send to the client. + """ + device_state_retrieval_response = dm.DeviceStateRetrievalResponse() + state = self.server.GetPolicies().get('device_state', {}) + FIELDS = [ + 'management_domain', + 'device_mode', + ] + for field in FIELDS: + if field in state: + setattr(device_state_retrieval_response, field, state[field]) + + response = dm.DeviceManagementResponse() + response.device_state_retrieval_response.CopyFrom( + device_state_retrieval_response) + return (200, response) def SetProtobufMessageField(self, group_message, field, field_value): '''Sets a field in a protobuf message. @@ -725,8 +749,6 @@ class PolicyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): response.new_public_key_signature = ( req_key.hashAndSign(response.new_public_key).tostring()) - self.DumpMessage('Response', response) - return (200, response.SerializeToString()) def GetSignatureForDomain(self, signatures, username): diff --git a/chrome/chrome_browser_chromeos.gypi b/chrome/chrome_browser_chromeos.gypi index c0d37ce..fb3eb19 100644 --- a/chrome/chrome_browser_chromeos.gypi +++ b/chrome/chrome_browser_chromeos.gypi @@ -754,6 +754,8 @@ 'browser/chromeos/policy/recommendation_restorer.h', 'browser/chromeos/policy/recommendation_restorer_factory.cc', 'browser/chromeos/policy/recommendation_restorer_factory.h', + 'browser/chromeos/policy/server_backed_device_state.cc', + 'browser/chromeos/policy/server_backed_device_state.h', 'browser/chromeos/policy/user_cloud_external_data_manager.cc', 'browser/chromeos/policy/user_cloud_external_data_manager.h', 'browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc', diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc index 90cada9..2d1d044 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc @@ -2248,6 +2248,10 @@ const char kDeviceRegistered[] = "DeviceRegistered"; // This is used to prevent these users from joining multiprofile sessions. const char kUsedPolicyCertificates[] = "policy.used_policy_certificates"; +// A dictionary containing server-provided device state pulled form the cloud +// after recovery. +const char kServerBackedDeviceState[] = "server_backed_device_state"; + #endif // Whether there is a Flash version installed that supports clearing LSO data. diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index d812f60..3536896 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h @@ -769,6 +769,7 @@ extern const char kInitialLocale[]; extern const char kOobeComplete[]; extern const char kDeviceRegistered[]; extern const char kUsedPolicyCertificates[]; +extern const char kServerBackedDeviceState[]; #endif extern const char kClearPluginLSODataEnabled[]; diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc index 405e982..ef49542 100644 --- a/chromeos/chromeos_switches.cc +++ b/chromeos/chromeos_switches.cc @@ -97,6 +97,10 @@ const char kEnableKioskMode[] = "enable-kiosk-mode"; // Enables request of tablet site (via user agent override). const char kEnableRequestTabletSite[] = "enable-request-tablet-site"; +// Whether to enable forced enterprise re-enrollment. +const char kEnterpriseEnableForcedReEnrollment[] = + "enterprise-enable-forced-re-enrollment"; + // Power of the power-of-2 initial modulus that will be used by the // auto-enrollment client. E.g. "4" means the modulus will be 2^4 = 16. const char kEnterpriseEnrollmentInitialModulus[] = diff --git a/chromeos/chromeos_switches.h b/chromeos/chromeos_switches.h index 29c87a3..651e898 100644 --- a/chromeos/chromeos_switches.h +++ b/chromeos/chromeos_switches.h @@ -46,6 +46,7 @@ CHROMEOS_EXPORT extern const char kEnableNetworkPortalNotification[]; CHROMEOS_EXPORT extern const char kEnableRequestTabletSite[]; CHROMEOS_EXPORT extern const char kEnableStubInteractive[]; CHROMEOS_EXPORT extern const char kEnableTouchpadThreeFingerClick[]; +CHROMEOS_EXPORT extern const char kEnterpriseEnableForcedReEnrollment[]; CHROMEOS_EXPORT extern const char kEnterpriseEnrollmentInitialModulus[]; CHROMEOS_EXPORT extern const char kEnterpriseEnrollmentModulusLimit[]; CHROMEOS_EXPORT extern const char kEnterpriseEnrollmentSkipRobotAuth[]; diff --git a/components/policy/core/common/cloud/cloud_policy_constants.cc b/components/policy/core/common/cloud/cloud_policy_constants.cc index 4d2920e..e740677 100644 --- a/components/policy/core/common/cloud/cloud_policy_constants.cc +++ b/components/policy/core/common/cloud/cloud_policy_constants.cc @@ -32,6 +32,7 @@ const char kValueRequestRegister[] = "register"; const char kValueRequestApiAuthorization[] = "api_authorization"; const char kValueRequestUnregister[] = "unregister"; const char kValueRequestUploadCertificate[] = "cert_upload"; +const char kValueRequestDeviceStateRetrieval[] = "device_state_retrieval"; const char kValueUserAffiliationManaged[] = "managed"; const char kValueUserAffiliationNone[] = "none"; diff --git a/components/policy/core/common/cloud/cloud_policy_constants.h b/components/policy/core/common/cloud/cloud_policy_constants.h index bc0f853..4f67f45 100644 --- a/components/policy/core/common/cloud/cloud_policy_constants.h +++ b/components/policy/core/common/cloud/cloud_policy_constants.h @@ -34,6 +34,7 @@ POLICY_EXPORT extern const char kValueRequestRegister[]; POLICY_EXPORT extern const char kValueRequestApiAuthorization[]; POLICY_EXPORT extern const char kValueRequestUnregister[]; POLICY_EXPORT extern const char kValueRequestUploadCertificate[]; +POLICY_EXPORT extern const char kValueRequestDeviceStateRetrieval[]; POLICY_EXPORT extern const char kValueUserAffiliationManaged[]; POLICY_EXPORT extern const char kValueUserAffiliationNone[]; diff --git a/components/policy/core/common/cloud/device_management_service.cc b/components/policy/core/common/cloud/device_management_service.cc index 81cd3e2..95b9c3c 100644 --- a/components/policy/core/common/cloud/device_management_service.cc +++ b/components/policy/core/common/cloud/device_management_service.cc @@ -121,6 +121,8 @@ const char* JobTypeToRequestType(DeviceManagementRequestJob::JobType type) { return dm_protocol::kValueRequestUnregister; case DeviceManagementRequestJob::TYPE_UPLOAD_CERTIFICATE: return dm_protocol::kValueRequestUploadCertificate; + case DeviceManagementRequestJob::TYPE_DEVICE_STATE_RETRIEVAL: + return dm_protocol::kValueRequestDeviceStateRetrieval; } NOTREACHED() << "Invalid job type " << type; return ""; diff --git a/components/policy/core/common/cloud/device_management_service.h b/components/policy/core/common/cloud/device_management_service.h index 9f124b7..ffc7bfa 100644 --- a/components/policy/core/common/cloud/device_management_service.h +++ b/components/policy/core/common/cloud/device_management_service.h @@ -41,6 +41,7 @@ class POLICY_EXPORT DeviceManagementRequestJob { TYPE_POLICY_FETCH, TYPE_UNREGISTRATION, TYPE_UPLOAD_CERTIFICATE, + TYPE_DEVICE_STATE_RETRIEVAL, }; typedef base::Callback< diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto index 7233234..17c7ad4 100644 --- a/components/policy/proto/device_management_backend.proto +++ b/components/policy/proto/device_management_backend.proto @@ -555,6 +555,42 @@ message DeviceAutoEnrollmentResponse { repeated bytes hash = 2; } +// Sent by the client to the server. The device management server keeps a +// mapping of device identifiers to device state. Devices query this table after +// hard reset in order recover state. This request is keyed just by the opaque +// server-backed state key; there is no further authentication. +message DeviceStateRetrievalRequest { + // Opaque, client-determined, unpredictable, stable and unique device + // identifier to retrieve state for. This field contains 32 bytes of data that + // looks essentially random to the server. It may be generated e.g. by running + // a concatenation of suitable device identifiers through a cryptographic hash + // algorithm such as SHA-256. + optional bytes server_backed_state_key = 1; +} + +// Server to client message carrying the device state response. Because the +// request is not authenticated, the only protection against state extraction +// from server is the unpredictability of the server-backed state ID. Thus, the +// response should not contain any sensitive data. If the server doesn't know +// the requested identifier, it just return a message with restore_mode set to +// RESTORE_MODE_NONE. +message DeviceStateRetrievalResponse { + // Restorative action to take after device reset. + enum RestoreMode { + // No state restoration. + RESTORE_MODE_NONE = 0; + // Enterprise enrollment requested, but user may skip. + RESTORE_MODE_REENROLLMENT_REQUESTED = 1; + // Enterprise enrollment is enforced and cannot be skipped. + RESTORE_MODE_REENROLLMENT_ENFORCED = 2; + }; + // The server-indicated restore mode. + optional RestoreMode restore_mode = 1 [default = RESTORE_MODE_NONE]; + + // Primary domain the device is associated with. + optional string management_domain = 2; +} + // Request from the DMAgent on the device to the DMServer. This is // container for all requests from device to server. The overall HTTP // request MUST be in the following format: @@ -565,6 +601,7 @@ message DeviceAutoEnrollmentResponse { // * request: MUST BE one of // * cert_upload // * enterprise_check +// * device_state_retrieval // * ping // * policy // * register @@ -592,6 +629,7 @@ message DeviceAutoEnrollmentResponse { // container will be ignored. // cert_upload: cert_upload_request // enterprise_check: auto_enrollment_request +// device_state_retrieval: device_state_retrieval_request // ping: policy_request // policy: policy_request // register: register_request @@ -621,6 +659,9 @@ message DeviceManagementRequest { // Request for OAuth2 authorization codes to access Google services. optional DeviceServiceApiAccessRequest service_api_access_request = 8; + + // Device-state retrieval. + optional DeviceStateRetrievalRequest device_state_retrieval_request = 9; } // Response from server to device. @@ -666,4 +707,7 @@ message DeviceManagementResponse { // Response to OAuth2 authorization code request. optional DeviceServiceApiAccessResponse service_api_access_response = 10; + + // Device-state retrieval. + optional DeviceStateRetrievalResponse device_state_retrieval_response = 11; } |