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.
5 #include "chrome/browser/chromeos/policy/enrollment_handler_chromeos.h"
8 #include "base/logging.h"
9 #include "base/message_loop/message_loop.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.h"
12 #include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h"
13 #include "chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h"
14 #include "chrome/browser/chromeos/policy/enrollment_status_chromeos.h"
15 #include "chrome/browser/chromeos/policy/proto/chrome_device_policy.pb.h"
16 #include "chrome/browser/chromeos/policy/server_backed_state_keys_broker.h"
17 #include "chrome/browser/chromeos/profiles/profile_helper.h"
18 #include "chrome/browser/chromeos/settings/device_oauth2_token_service.h"
19 #include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "google_apis/gaia/gaia_urls.h"
22 #include "net/http/http_status_code.h"
24 namespace em
= enterprise_management
;
30 // Retry for InstallAttrs initialization every 500ms.
31 const int kLockRetryIntervalMs
= 500;
32 // Maximum time to retry InstallAttrs initialization before we give up.
33 const int kLockRetryTimeoutMs
= 10 * 60 * 1000; // 10 minutes.
35 em::DeviceRegisterRequest::Flavor
EnrollmentModeToRegistrationFlavor(
36 policy::EnrollmentConfig::Mode mode
) {
38 case policy::EnrollmentConfig::MODE_NONE
:
40 case policy::EnrollmentConfig::MODE_MANUAL
:
41 return em::DeviceRegisterRequest::FLAVOR_ENROLLMENT_MANUAL
;
42 case policy::EnrollmentConfig::MODE_MANUAL_REENROLLMENT
:
43 return em::DeviceRegisterRequest::FLAVOR_ENROLLMENT_MANUAL_RENEW
;
44 case policy::EnrollmentConfig::MODE_LOCAL_FORCED
:
45 return em::DeviceRegisterRequest::FLAVOR_ENROLLMENT_LOCAL_FORCED
;
46 case policy::EnrollmentConfig::MODE_LOCAL_ADVERTISED
:
47 return em::DeviceRegisterRequest::FLAVOR_ENROLLMENT_LOCAL_ADVERTISED
;
48 case policy::EnrollmentConfig::MODE_SERVER_FORCED
:
49 return em::DeviceRegisterRequest::FLAVOR_ENROLLMENT_SERVER_FORCED
;
50 case policy::EnrollmentConfig::MODE_SERVER_ADVERTISED
:
51 return em::DeviceRegisterRequest::FLAVOR_ENROLLMENT_SERVER_ADVERTISED
;
52 case policy::EnrollmentConfig::MODE_RECOVERY
:
53 return em::DeviceRegisterRequest::FLAVOR_ENROLLMENT_RECOVERY
;
56 NOTREACHED() << "Bad enrollment mode: " << mode
;
57 return em::DeviceRegisterRequest::FLAVOR_ENROLLMENT_MANUAL
;
62 EnrollmentHandlerChromeOS::EnrollmentHandlerChromeOS(
63 DeviceCloudPolicyStoreChromeOS
* store
,
64 EnterpriseInstallAttributes
* install_attributes
,
65 ServerBackedStateKeysBroker
* state_keys_broker
,
66 chromeos::OwnerSettingsServiceChromeOS
* owner_settings_service
,
67 scoped_ptr
<CloudPolicyClient
> client
,
68 scoped_refptr
<base::SequencedTaskRunner
> background_task_runner
,
69 const EnrollmentConfig
& enrollment_config
,
70 const std::string
& auth_token
,
71 const std::string
& client_id
,
72 const std::string
& requisition
,
73 const AllowedDeviceModes
& allowed_device_modes
,
74 ManagementMode management_mode
,
75 const EnrollmentCallback
& completion_callback
)
77 install_attributes_(install_attributes
),
78 state_keys_broker_(state_keys_broker
),
79 owner_settings_service_(owner_settings_service
),
80 client_(client
.Pass()),
81 background_task_runner_(background_task_runner
),
82 enrollment_config_(enrollment_config
),
83 auth_token_(auth_token
),
84 client_id_(client_id
),
85 requisition_(requisition
),
86 allowed_device_modes_(allowed_device_modes
),
87 management_mode_(management_mode
),
88 completion_callback_(completion_callback
),
89 device_mode_(DEVICE_MODE_NOT_SET
),
90 skip_robot_auth_(false),
91 enrollment_step_(STEP_PENDING
),
92 lockbox_init_duration_(0),
93 weak_ptr_factory_(this) {
94 CHECK(!client_
->is_registered());
95 CHECK_EQ(DM_STATUS_SUCCESS
, client_
->status());
96 CHECK(management_mode_
== MANAGEMENT_MODE_ENTERPRISE_MANAGED
||
97 management_mode_
== MANAGEMENT_MODE_CONSUMER_MANAGED
);
98 store_
->AddObserver(this);
99 client_
->AddObserver(this);
100 client_
->AddPolicyTypeToFetch(dm_protocol::kChromeDevicePolicyType
,
104 EnrollmentHandlerChromeOS::~EnrollmentHandlerChromeOS() {
106 store_
->RemoveObserver(this);
109 void EnrollmentHandlerChromeOS::StartEnrollment() {
110 CHECK_EQ(STEP_PENDING
, enrollment_step_
);
111 enrollment_step_
= STEP_STATE_KEYS
;
112 state_keys_broker_
->RequestStateKeys(
113 base::Bind(&EnrollmentHandlerChromeOS::HandleStateKeysResult
,
114 weak_ptr_factory_
.GetWeakPtr()));
117 scoped_ptr
<CloudPolicyClient
> EnrollmentHandlerChromeOS::ReleaseClient() {
119 return client_
.Pass();
122 void EnrollmentHandlerChromeOS::OnPolicyFetched(CloudPolicyClient
* client
) {
123 DCHECK_EQ(client_
.get(), client
);
124 CHECK_EQ(STEP_POLICY_FETCH
, enrollment_step_
);
126 enrollment_step_
= STEP_VALIDATION
;
128 // Validate the policy.
129 const em::PolicyFetchResponse
* policy
= client_
->GetPolicyFor(
130 dm_protocol::kChromeDevicePolicyType
, std::string());
132 ReportResult(EnrollmentStatus::ForFetchError(
133 DM_STATUS_RESPONSE_DECODING_ERROR
));
137 scoped_ptr
<DeviceCloudPolicyValidator
> validator(
138 DeviceCloudPolicyValidator::Create(
139 scoped_ptr
<em::PolicyFetchResponse
>(
140 new em::PolicyFetchResponse(*policy
)),
141 background_task_runner_
));
143 validator
->ValidateTimestamp(base::Time(), base::Time::NowFromSystemTime(),
144 CloudPolicyValidatorBase::TIMESTAMP_REQUIRED
);
146 // If this is re-enrollment, make sure that the new policy matches the
147 // previously-enrolled domain.
149 if (install_attributes_
->IsEnterpriseDevice()) {
150 domain
= install_attributes_
->GetDomain();
151 validator
->ValidateDomain(domain
);
153 validator
->ValidateDMToken(client
->dm_token(),
154 CloudPolicyValidatorBase::DM_TOKEN_REQUIRED
);
155 validator
->ValidatePolicyType(dm_protocol::kChromeDevicePolicyType
);
156 validator
->ValidatePayload();
157 if (management_mode_
== MANAGEMENT_MODE_CONSUMER_MANAGED
) {
158 // For consumer-managed devices, although we don't store the policy, we
159 // still need to verify its integrity since we use the request token in it.
160 // The consumer device management server does not have the verification
161 // key, and we need to skip checking on that by passing an empty key to
162 // ValidateInitialKey(). ValidateInitialKey() still checks that the policy
163 // data is correctly signed by the new public key when the verification key
165 validator
->ValidateInitialKey(std::string(), std::string());
167 // If |domain| is empty here, the policy validation code will just use the
168 // domain from the username field in the policy itself to do key validation.
169 // TODO(mnissler): Plumb the enrolling user's username into this object so
170 // we can validate the username on the resulting policy, and use the domain
171 // from that username to validate the key below (http://crbug.com/343074).
172 validator
->ValidateInitialKey(GetPolicyVerificationKey(), domain
);
174 validator
.release()->StartValidation(
175 base::Bind(&EnrollmentHandlerChromeOS::HandlePolicyValidationResult
,
176 weak_ptr_factory_
.GetWeakPtr()));
179 void EnrollmentHandlerChromeOS::OnRegistrationStateChanged(
180 CloudPolicyClient
* client
) {
181 DCHECK_EQ(client_
.get(), client
);
183 if (enrollment_step_
== STEP_REGISTRATION
&& client_
->is_registered()) {
184 enrollment_step_
= STEP_POLICY_FETCH
,
185 device_mode_
= client_
->device_mode();
186 if (device_mode_
== DEVICE_MODE_NOT_SET
)
187 device_mode_
= DEVICE_MODE_ENTERPRISE
;
188 if (!allowed_device_modes_
.test(device_mode_
)) {
189 LOG(ERROR
) << "Bad device mode " << device_mode_
;
190 ReportResult(EnrollmentStatus::ForStatus(
191 EnrollmentStatus::STATUS_REGISTRATION_BAD_MODE
));
194 client_
->FetchPolicy();
196 LOG(FATAL
) << "Registration state changed to " << client_
->is_registered()
197 << " in step " << enrollment_step_
<< ".";
201 void EnrollmentHandlerChromeOS::OnClientError(CloudPolicyClient
* client
) {
202 DCHECK_EQ(client_
.get(), client
);
204 if (enrollment_step_
== STEP_ROBOT_AUTH_FETCH
) {
205 LOG(ERROR
) << "API authentication code fetch failed: "
206 << client_
->status();
207 ReportResult(EnrollmentStatus::ForRobotAuthFetchError(client_
->status()));
208 } else if (enrollment_step_
< STEP_POLICY_FETCH
) {
209 ReportResult(EnrollmentStatus::ForRegistrationError(client_
->status()));
211 ReportResult(EnrollmentStatus::ForFetchError(client_
->status()));
215 void EnrollmentHandlerChromeOS::OnStoreLoaded(CloudPolicyStore
* store
) {
216 DCHECK_EQ(store_
, store
);
218 if (enrollment_step_
== STEP_LOADING_STORE
) {
219 // If the |store_| wasn't initialized when StartEnrollment() was called,
220 // then StartRegistration() bails silently. This gets registration rolling
221 // again after the store finishes loading.
223 } else if (enrollment_step_
== STEP_STORE_POLICY
) {
224 ReportResult(EnrollmentStatus::ForStatus(EnrollmentStatus::STATUS_SUCCESS
));
228 void EnrollmentHandlerChromeOS::OnStoreError(CloudPolicyStore
* store
) {
229 DCHECK_EQ(store_
, store
);
230 if (enrollment_step_
== STEP_STORE_TOKEN_AND_ID
) {
231 // Calling OwnerSettingsServiceChromeOS::SetManagementSettings()
232 // on a non- enterprise-managed device will fail as
233 // DeviceCloudPolicyStore listens to all changes on device
234 // settings, and it calls OnStoreError() when the device is not
235 // enterprise-managed.
238 ReportResult(EnrollmentStatus::ForStoreError(store_
->status(),
239 store_
->validation_status()));
242 void EnrollmentHandlerChromeOS::HandleStateKeysResult(
243 const std::vector
<std::string
>& state_keys
) {
244 CHECK_EQ(STEP_STATE_KEYS
, enrollment_step_
);
246 // Make sure state keys are available if forced re-enrollment is on.
247 if (chromeos::AutoEnrollmentController::GetMode() ==
248 chromeos::AutoEnrollmentController::MODE_FORCED_RE_ENROLLMENT
) {
249 client_
->SetStateKeysToUpload(state_keys
);
250 current_state_key_
= state_keys_broker_
->current_state_key();
251 if (state_keys
.empty() || current_state_key_
.empty()) {
253 EnrollmentStatus::ForStatus(EnrollmentStatus::STATUS_NO_STATE_KEYS
));
258 enrollment_step_
= STEP_LOADING_STORE
;
262 void EnrollmentHandlerChromeOS::StartRegistration() {
263 CHECK_EQ(STEP_LOADING_STORE
, enrollment_step_
);
264 if (store_
->is_initialized()) {
265 enrollment_step_
= STEP_REGISTRATION
;
267 em::DeviceRegisterRequest::DEVICE
,
268 EnrollmentModeToRegistrationFlavor(enrollment_config_
.mode
),
269 auth_token_
, client_id_
, requisition_
, current_state_key_
);
271 // Do nothing. StartRegistration() will be called again from OnStoreLoaded()
272 // after the CloudPolicyStore has initialized.
276 void EnrollmentHandlerChromeOS::HandlePolicyValidationResult(
277 DeviceCloudPolicyValidator
* validator
) {
278 CHECK_EQ(STEP_VALIDATION
, enrollment_step_
);
279 if (validator
->success()) {
280 policy_
= validator
->policy().Pass();
281 username_
= validator
->policy_data()->username();
282 device_id_
= validator
->policy_data()->device_id();
283 request_token_
= validator
->policy_data()->request_token();
284 enrollment_step_
= STEP_ROBOT_AUTH_FETCH
;
285 client_
->FetchRobotAuthCodes(auth_token_
);
287 ReportResult(EnrollmentStatus::ForValidationError(validator
->status()));
291 void EnrollmentHandlerChromeOS::OnRobotAuthCodesFetched(
292 CloudPolicyClient
* client
) {
293 DCHECK_EQ(client_
.get(), client
);
294 CHECK_EQ(STEP_ROBOT_AUTH_FETCH
, enrollment_step_
);
296 if (client
->robot_api_auth_code().empty()) {
297 // If the server doesn't provide an auth code, skip the robot auth setup.
298 // This allows clients running against the test server to transparently skip
300 skip_robot_auth_
= true;
301 enrollment_step_
= STEP_LOCK_DEVICE
;
306 enrollment_step_
= STEP_ROBOT_AUTH_REFRESH
;
308 gaia::OAuthClientInfo client_info
;
309 client_info
.client_id
= GaiaUrls::GetInstance()->oauth2_chrome_client_id();
310 client_info
.client_secret
=
311 GaiaUrls::GetInstance()->oauth2_chrome_client_secret();
312 client_info
.redirect_uri
= "oob";
314 // Use the system request context to avoid sending user cookies.
315 gaia_oauth_client_
.reset(new gaia::GaiaOAuthClient(
316 g_browser_process
->system_request_context()));
317 gaia_oauth_client_
->GetTokensFromAuthCode(client_info
,
318 client
->robot_api_auth_code(),
323 // GaiaOAuthClient::Delegate callback for OAuth2 refresh token fetched.
324 void EnrollmentHandlerChromeOS::OnGetTokensResponse(
325 const std::string
& refresh_token
,
326 const std::string
& access_token
,
327 int expires_in_seconds
) {
328 CHECK_EQ(STEP_ROBOT_AUTH_REFRESH
, enrollment_step_
);
330 robot_refresh_token_
= refresh_token
;
332 enrollment_step_
= STEP_LOCK_DEVICE
;
336 // GaiaOAuthClient::Delegate
337 void EnrollmentHandlerChromeOS::OnRefreshTokenResponse(
338 const std::string
& access_token
,
339 int expires_in_seconds
) {
340 // We never use the code that should trigger this callback.
341 LOG(FATAL
) << "Unexpected callback invoked.";
344 // GaiaOAuthClient::Delegate OAuth2 error when fetching refresh token request.
345 void EnrollmentHandlerChromeOS::OnOAuthError() {
346 CHECK_EQ(STEP_ROBOT_AUTH_REFRESH
, enrollment_step_
);
347 // OnOAuthError is only called if the request is bad (malformed) or the
348 // response is bad (empty access token returned).
349 LOG(ERROR
) << "OAuth protocol error while fetching API refresh token.";
351 EnrollmentStatus::ForRobotRefreshFetchError(net::HTTP_BAD_REQUEST
));
354 // GaiaOAuthClient::Delegate network error when fetching refresh token.
355 void EnrollmentHandlerChromeOS::OnNetworkError(int response_code
) {
356 CHECK_EQ(STEP_ROBOT_AUTH_REFRESH
, enrollment_step_
);
357 LOG(ERROR
) << "Network error while fetching API refresh token: "
360 EnrollmentStatus::ForRobotRefreshFetchError(response_code
));
363 void EnrollmentHandlerChromeOS::StartLockDevice() {
364 CHECK_EQ(STEP_LOCK_DEVICE
, enrollment_step_
);
365 // Since this method is also called directly.
366 weak_ptr_factory_
.InvalidateWeakPtrs();
368 if (management_mode_
== MANAGEMENT_MODE_CONSUMER_MANAGED
) {
369 CHECK(owner_settings_service_
);
371 // Consumer device enrollment doesn't use install attributes. Instead,
372 // we put the information in the owners settings.
373 enrollment_step_
= STEP_STORE_TOKEN_AND_ID
;
374 chromeos::OwnerSettingsServiceChromeOS::ManagementSettings settings
;
375 settings
.management_mode
= management_mode_
;
376 settings
.request_token
= request_token_
;
377 settings
.device_id
= device_id_
;
378 owner_settings_service_
->SetManagementSettings(
380 base::Bind(&EnrollmentHandlerChromeOS::HandleSetManagementSettingsDone
,
381 weak_ptr_factory_
.GetWeakPtr()));
383 install_attributes_
->LockDevice(
384 username_
, device_mode_
, device_id_
,
385 base::Bind(&EnrollmentHandlerChromeOS::HandleLockDeviceResult
,
386 weak_ptr_factory_
.GetWeakPtr()));
390 void EnrollmentHandlerChromeOS::HandleSetManagementSettingsDone(bool success
) {
391 CHECK_EQ(STEP_STORE_TOKEN_AND_ID
, enrollment_step_
);
393 ReportResult(EnrollmentStatus::ForStatus(
394 EnrollmentStatus::STATUS_STORE_TOKEN_AND_ID_FAILED
));
398 StartStoreRobotAuth();
401 void EnrollmentHandlerChromeOS::HandleLockDeviceResult(
402 EnterpriseInstallAttributes::LockResult lock_result
) {
403 CHECK_EQ(STEP_LOCK_DEVICE
, enrollment_step_
);
404 switch (lock_result
) {
405 case EnterpriseInstallAttributes::LOCK_SUCCESS
:
406 StartStoreRobotAuth();
408 case EnterpriseInstallAttributes::LOCK_NOT_READY
:
409 // We wait up to |kLockRetryTimeoutMs| milliseconds and if it hasn't
410 // succeeded by then show an error to the user and stop the enrollment.
411 if (lockbox_init_duration_
< kLockRetryTimeoutMs
) {
412 // InstallAttributes not ready yet, retry later.
413 LOG(WARNING
) << "Install Attributes not ready yet will retry in "
414 << kLockRetryIntervalMs
<< "ms.";
415 base::MessageLoop::current()->PostDelayedTask(
417 base::Bind(&EnrollmentHandlerChromeOS::StartLockDevice
,
418 weak_ptr_factory_
.GetWeakPtr()),
419 base::TimeDelta::FromMilliseconds(kLockRetryIntervalMs
));
420 lockbox_init_duration_
+= kLockRetryIntervalMs
;
422 HandleLockDeviceResult(EnterpriseInstallAttributes::LOCK_TIMEOUT
);
425 case EnterpriseInstallAttributes::LOCK_TIMEOUT
:
426 case EnterpriseInstallAttributes::LOCK_BACKEND_INVALID
:
427 case EnterpriseInstallAttributes::LOCK_ALREADY_LOCKED
:
428 case EnterpriseInstallAttributes::LOCK_SET_ERROR
:
429 case EnterpriseInstallAttributes::LOCK_FINALIZE_ERROR
:
430 case EnterpriseInstallAttributes::LOCK_READBACK_ERROR
:
431 case EnterpriseInstallAttributes::LOCK_WRONG_DOMAIN
:
432 case EnterpriseInstallAttributes::LOCK_WRONG_MODE
:
433 ReportResult(EnrollmentStatus::ForLockError(lock_result
));
438 void EnrollmentHandlerChromeOS::StartStoreRobotAuth() {
439 enrollment_step_
= STEP_STORE_ROBOT_AUTH
;
441 // Don't store the token if robot auth was skipped.
442 if (skip_robot_auth_
) {
443 HandleStoreRobotAuthTokenResult(true);
447 chromeos::DeviceOAuth2TokenServiceFactory::Get()->SetAndSaveRefreshToken(
448 robot_refresh_token_
,
449 base::Bind(&EnrollmentHandlerChromeOS::HandleStoreRobotAuthTokenResult
,
450 weak_ptr_factory_
.GetWeakPtr()));
453 void EnrollmentHandlerChromeOS::HandleStoreRobotAuthTokenResult(bool result
) {
454 CHECK_EQ(STEP_STORE_ROBOT_AUTH
, enrollment_step_
);
457 LOG(ERROR
) << "Failed to store API refresh token.";
458 ReportResult(EnrollmentStatus::ForStatus(
459 EnrollmentStatus::STATUS_ROBOT_REFRESH_STORE_FAILED
));
463 if (management_mode_
== MANAGEMENT_MODE_CONSUMER_MANAGED
) {
464 // For consumer management enrollment, we don't store the policy.
465 ReportResult(EnrollmentStatus::ForStatus(EnrollmentStatus::STATUS_SUCCESS
));
469 enrollment_step_
= STEP_STORE_POLICY
;
470 store_
->InstallInitialPolicy(*policy_
);
473 void EnrollmentHandlerChromeOS::Stop() {
475 client_
->RemoveObserver(this);
476 enrollment_step_
= STEP_FINISHED
;
477 weak_ptr_factory_
.InvalidateWeakPtrs();
478 completion_callback_
.Reset();
481 void EnrollmentHandlerChromeOS::ReportResult(EnrollmentStatus status
) {
482 EnrollmentCallback callback
= completion_callback_
;
485 if (status
.status() != EnrollmentStatus::STATUS_SUCCESS
) {
486 LOG(WARNING
) << "Enrollment failed: " << status
.status()
487 << ", client: " << status
.client_status()
488 << ", validation: " << status
.validation_status()
489 << ", store: " << status
.store_status()
490 << ", lock: " << status
.lock_status();
493 if (!callback
.is_null())
494 callback
.Run(status
);
497 } // namespace policy