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/command_line.h"
9 #include "base/logging.h"
10 #include "base/message_loop/message_loop.h"
11 #include "chrome/browser/browser_process.h"
12 #include "chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.h"
13 #include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h"
14 #include "chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h"
15 #include "chrome/browser/chromeos/policy/enrollment_status_chromeos.h"
16 #include "chrome/browser/chromeos/policy/proto/chrome_device_policy.pb.h"
17 #include "chrome/browser/chromeos/policy/server_backed_state_keys_broker.h"
18 #include "chrome/browser/chromeos/profiles/profile_helper.h"
19 #include "chrome/browser/chromeos/settings/device_oauth2_token_service.h"
20 #include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chromeos/chromeos_switches.h"
23 #include "google_apis/gaia/gaia_urls.h"
24 #include "net/http/http_status_code.h"
26 namespace em
= enterprise_management
;
32 // Retry for InstallAttrs initialization every 500ms.
33 const int kLockRetryIntervalMs
= 500;
34 // Maximum time to retry InstallAttrs initialization before we give up.
35 const int kLockRetryTimeoutMs
= 10 * 60 * 1000; // 10 minutes.
37 // Testing token used when the enrollment-skip-robot-auth is set to skip talking
38 // to GAIA for an actual token. This is needed to be able to run against the
39 // testing DMServer implementations.
40 const char kTestingRobotToken
[] = "test-token";
42 em::DeviceRegisterRequest::Flavor
EnrollmentModeToRegistrationFlavor(
43 policy::EnrollmentConfig::Mode mode
) {
45 case policy::EnrollmentConfig::MODE_NONE
:
47 case policy::EnrollmentConfig::MODE_MANUAL
:
48 return em::DeviceRegisterRequest::FLAVOR_ENROLLMENT_MANUAL
;
49 case policy::EnrollmentConfig::MODE_MANUAL_REENROLLMENT
:
50 return em::DeviceRegisterRequest::FLAVOR_ENROLLMENT_MANUAL_RENEW
;
51 case policy::EnrollmentConfig::MODE_LOCAL_FORCED
:
52 return em::DeviceRegisterRequest::FLAVOR_ENROLLMENT_LOCAL_FORCED
;
53 case policy::EnrollmentConfig::MODE_LOCAL_ADVERTISED
:
54 return em::DeviceRegisterRequest::FLAVOR_ENROLLMENT_LOCAL_ADVERTISED
;
55 case policy::EnrollmentConfig::MODE_SERVER_FORCED
:
56 return em::DeviceRegisterRequest::FLAVOR_ENROLLMENT_SERVER_FORCED
;
57 case policy::EnrollmentConfig::MODE_SERVER_ADVERTISED
:
58 return em::DeviceRegisterRequest::FLAVOR_ENROLLMENT_SERVER_ADVERTISED
;
59 case policy::EnrollmentConfig::MODE_RECOVERY
:
60 return em::DeviceRegisterRequest::FLAVOR_ENROLLMENT_RECOVERY
;
63 NOTREACHED() << "Bad enrollment mode: " << mode
;
64 return em::DeviceRegisterRequest::FLAVOR_ENROLLMENT_MANUAL
;
69 EnrollmentHandlerChromeOS::EnrollmentHandlerChromeOS(
70 DeviceCloudPolicyStoreChromeOS
* store
,
71 EnterpriseInstallAttributes
* install_attributes
,
72 ServerBackedStateKeysBroker
* state_keys_broker
,
73 chromeos::OwnerSettingsServiceChromeOS
* owner_settings_service
,
74 scoped_ptr
<CloudPolicyClient
> client
,
75 scoped_refptr
<base::SequencedTaskRunner
> background_task_runner
,
76 const EnrollmentConfig
& enrollment_config
,
77 const std::string
& auth_token
,
78 const std::string
& client_id
,
79 const std::string
& requisition
,
80 const AllowedDeviceModes
& allowed_device_modes
,
81 ManagementMode management_mode
,
82 const EnrollmentCallback
& completion_callback
)
84 install_attributes_(install_attributes
),
85 state_keys_broker_(state_keys_broker
),
86 owner_settings_service_(owner_settings_service
),
87 client_(client
.Pass()),
88 background_task_runner_(background_task_runner
),
89 enrollment_config_(enrollment_config
),
90 auth_token_(auth_token
),
91 client_id_(client_id
),
92 requisition_(requisition
),
93 allowed_device_modes_(allowed_device_modes
),
94 management_mode_(management_mode
),
95 completion_callback_(completion_callback
),
96 device_mode_(DEVICE_MODE_NOT_SET
),
97 enrollment_step_(STEP_PENDING
),
98 lockbox_init_duration_(0),
99 weak_ptr_factory_(this) {
100 CHECK(!client_
->is_registered());
101 CHECK_EQ(DM_STATUS_SUCCESS
, client_
->status());
102 CHECK(management_mode_
== MANAGEMENT_MODE_ENTERPRISE_MANAGED
||
103 management_mode_
== MANAGEMENT_MODE_CONSUMER_MANAGED
);
104 store_
->AddObserver(this);
105 client_
->AddObserver(this);
106 client_
->AddPolicyTypeToFetch(dm_protocol::kChromeDevicePolicyType
,
110 EnrollmentHandlerChromeOS::~EnrollmentHandlerChromeOS() {
112 store_
->RemoveObserver(this);
115 void EnrollmentHandlerChromeOS::StartEnrollment() {
116 CHECK_EQ(STEP_PENDING
, enrollment_step_
);
117 enrollment_step_
= STEP_STATE_KEYS
;
118 state_keys_broker_
->RequestStateKeys(
119 base::Bind(&EnrollmentHandlerChromeOS::HandleStateKeysResult
,
120 weak_ptr_factory_
.GetWeakPtr()));
123 scoped_ptr
<CloudPolicyClient
> EnrollmentHandlerChromeOS::ReleaseClient() {
125 return client_
.Pass();
128 void EnrollmentHandlerChromeOS::OnPolicyFetched(CloudPolicyClient
* client
) {
129 DCHECK_EQ(client_
.get(), client
);
130 CHECK_EQ(STEP_POLICY_FETCH
, enrollment_step_
);
132 enrollment_step_
= STEP_VALIDATION
;
134 // Validate the policy.
135 const em::PolicyFetchResponse
* policy
= client_
->GetPolicyFor(
136 dm_protocol::kChromeDevicePolicyType
, std::string());
138 ReportResult(EnrollmentStatus::ForFetchError(
139 DM_STATUS_RESPONSE_DECODING_ERROR
));
143 scoped_ptr
<DeviceCloudPolicyValidator
> validator(
144 DeviceCloudPolicyValidator::Create(
145 scoped_ptr
<em::PolicyFetchResponse
>(
146 new em::PolicyFetchResponse(*policy
)),
147 background_task_runner_
));
149 validator
->ValidateTimestamp(base::Time(), base::Time::NowFromSystemTime(),
150 CloudPolicyValidatorBase::TIMESTAMP_REQUIRED
);
152 // If this is re-enrollment, make sure that the new policy matches the
153 // previously-enrolled domain.
155 if (install_attributes_
->IsEnterpriseDevice()) {
156 domain
= install_attributes_
->GetDomain();
157 validator
->ValidateDomain(domain
);
159 validator
->ValidateDMToken(client
->dm_token(),
160 CloudPolicyValidatorBase::DM_TOKEN_REQUIRED
);
161 validator
->ValidatePolicyType(dm_protocol::kChromeDevicePolicyType
);
162 validator
->ValidatePayload();
163 if (management_mode_
== MANAGEMENT_MODE_CONSUMER_MANAGED
) {
164 // For consumer-managed devices, although we don't store the policy, we
165 // still need to verify its integrity since we use the request token in it.
166 // The consumer device management server does not have the verification
167 // key, and we need to skip checking on that by passing an empty key to
168 // ValidateInitialKey(). ValidateInitialKey() still checks that the policy
169 // data is correctly signed by the new public key when the verification key
171 validator
->ValidateInitialKey(std::string(), std::string());
173 // If |domain| is empty here, the policy validation code will just use the
174 // domain from the username field in the policy itself to do key validation.
175 // TODO(mnissler): Plumb the enrolling user's username into this object so
176 // we can validate the username on the resulting policy, and use the domain
177 // from that username to validate the key below (http://crbug.com/343074).
178 validator
->ValidateInitialKey(GetPolicyVerificationKey(), domain
);
180 validator
.release()->StartValidation(
181 base::Bind(&EnrollmentHandlerChromeOS::HandlePolicyValidationResult
,
182 weak_ptr_factory_
.GetWeakPtr()));
185 void EnrollmentHandlerChromeOS::OnRegistrationStateChanged(
186 CloudPolicyClient
* client
) {
187 DCHECK_EQ(client_
.get(), client
);
189 if (enrollment_step_
== STEP_REGISTRATION
&& client_
->is_registered()) {
190 enrollment_step_
= STEP_POLICY_FETCH
,
191 device_mode_
= client_
->device_mode();
192 if (device_mode_
== DEVICE_MODE_NOT_SET
)
193 device_mode_
= DEVICE_MODE_ENTERPRISE
;
194 if (!allowed_device_modes_
.test(device_mode_
)) {
195 LOG(ERROR
) << "Bad device mode " << device_mode_
;
196 ReportResult(EnrollmentStatus::ForStatus(
197 EnrollmentStatus::STATUS_REGISTRATION_BAD_MODE
));
200 client_
->FetchPolicy();
202 LOG(FATAL
) << "Registration state changed to " << client_
->is_registered()
203 << " in step " << enrollment_step_
<< ".";
207 void EnrollmentHandlerChromeOS::OnClientError(CloudPolicyClient
* client
) {
208 DCHECK_EQ(client_
.get(), client
);
210 if (enrollment_step_
== STEP_ROBOT_AUTH_FETCH
) {
211 LOG(ERROR
) << "API authentication code fetch failed: "
212 << client_
->status();
213 ReportResult(EnrollmentStatus::ForRobotAuthFetchError(client_
->status()));
214 } else if (enrollment_step_
< STEP_POLICY_FETCH
) {
215 ReportResult(EnrollmentStatus::ForRegistrationError(client_
->status()));
217 ReportResult(EnrollmentStatus::ForFetchError(client_
->status()));
221 void EnrollmentHandlerChromeOS::OnStoreLoaded(CloudPolicyStore
* store
) {
222 DCHECK_EQ(store_
, store
);
224 if (enrollment_step_
== STEP_LOADING_STORE
) {
225 // If the |store_| wasn't initialized when StartEnrollment() was called,
226 // then StartRegistration() bails silently. This gets registration rolling
227 // again after the store finishes loading.
229 } else if (enrollment_step_
== STEP_STORE_POLICY
) {
230 ReportResult(EnrollmentStatus::ForStatus(EnrollmentStatus::STATUS_SUCCESS
));
234 void EnrollmentHandlerChromeOS::OnStoreError(CloudPolicyStore
* store
) {
235 DCHECK_EQ(store_
, store
);
236 if (enrollment_step_
== STEP_STORE_TOKEN_AND_ID
) {
237 // Calling OwnerSettingsServiceChromeOS::SetManagementSettings()
238 // on a non- enterprise-managed device will fail as
239 // DeviceCloudPolicyStore listens to all changes on device
240 // settings, and it calls OnStoreError() when the device is not
241 // enterprise-managed.
244 ReportResult(EnrollmentStatus::ForStoreError(store_
->status(),
245 store_
->validation_status()));
248 void EnrollmentHandlerChromeOS::HandleStateKeysResult(
249 const std::vector
<std::string
>& state_keys
) {
250 CHECK_EQ(STEP_STATE_KEYS
, enrollment_step_
);
252 // Make sure state keys are available if forced re-enrollment is on.
253 if (chromeos::AutoEnrollmentController::GetMode() ==
254 chromeos::AutoEnrollmentController::MODE_FORCED_RE_ENROLLMENT
) {
255 client_
->SetStateKeysToUpload(state_keys
);
256 current_state_key_
= state_keys_broker_
->current_state_key();
257 if (state_keys
.empty() || current_state_key_
.empty()) {
259 EnrollmentStatus::ForStatus(EnrollmentStatus::STATUS_NO_STATE_KEYS
));
264 enrollment_step_
= STEP_LOADING_STORE
;
268 void EnrollmentHandlerChromeOS::StartRegistration() {
269 CHECK_EQ(STEP_LOADING_STORE
, enrollment_step_
);
270 if (store_
->is_initialized()) {
271 enrollment_step_
= STEP_REGISTRATION
;
273 em::DeviceRegisterRequest::DEVICE
,
274 EnrollmentModeToRegistrationFlavor(enrollment_config_
.mode
),
275 auth_token_
, client_id_
, requisition_
, current_state_key_
);
277 // Do nothing. StartRegistration() will be called again from OnStoreLoaded()
278 // after the CloudPolicyStore has initialized.
282 void EnrollmentHandlerChromeOS::HandlePolicyValidationResult(
283 DeviceCloudPolicyValidator
* validator
) {
284 CHECK_EQ(STEP_VALIDATION
, enrollment_step_
);
285 if (validator
->success()) {
286 policy_
= validator
->policy().Pass();
287 username_
= validator
->policy_data()->username();
288 device_id_
= validator
->policy_data()->device_id();
289 request_token_
= validator
->policy_data()->request_token();
291 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
292 chromeos::switches::kEnterpriseEnrollmentSkipRobotAuth
)) {
293 // For test purposes we allow enrollment to succeed without proper robot
294 // account and use the provided value as a token.
295 refresh_token_
= kTestingRobotToken
;
296 enrollment_step_
= STEP_LOCK_DEVICE
;
301 enrollment_step_
= STEP_ROBOT_AUTH_FETCH
;
302 client_
->FetchRobotAuthCodes(auth_token_
);
304 ReportResult(EnrollmentStatus::ForValidationError(validator
->status()));
308 void EnrollmentHandlerChromeOS::OnRobotAuthCodesFetched(
309 CloudPolicyClient
* client
) {
310 DCHECK_EQ(client_
.get(), client
);
311 CHECK_EQ(STEP_ROBOT_AUTH_FETCH
, enrollment_step_
);
313 enrollment_step_
= STEP_ROBOT_AUTH_REFRESH
;
315 gaia::OAuthClientInfo client_info
;
316 client_info
.client_id
= GaiaUrls::GetInstance()->oauth2_chrome_client_id();
317 client_info
.client_secret
=
318 GaiaUrls::GetInstance()->oauth2_chrome_client_secret();
319 client_info
.redirect_uri
= "oob";
321 // Use the system request context to avoid sending user cookies.
322 gaia_oauth_client_
.reset(new gaia::GaiaOAuthClient(
323 g_browser_process
->system_request_context()));
324 gaia_oauth_client_
->GetTokensFromAuthCode(client_info
,
325 client
->robot_api_auth_code(),
330 // GaiaOAuthClient::Delegate callback for OAuth2 refresh token fetched.
331 void EnrollmentHandlerChromeOS::OnGetTokensResponse(
332 const std::string
& refresh_token
,
333 const std::string
& access_token
,
334 int expires_in_seconds
) {
335 CHECK_EQ(STEP_ROBOT_AUTH_REFRESH
, enrollment_step_
);
337 refresh_token_
= refresh_token
;
339 enrollment_step_
= STEP_LOCK_DEVICE
;
343 // GaiaOAuthClient::Delegate
344 void EnrollmentHandlerChromeOS::OnRefreshTokenResponse(
345 const std::string
& access_token
,
346 int expires_in_seconds
) {
347 // We never use the code that should trigger this callback.
348 LOG(FATAL
) << "Unexpected callback invoked.";
351 // GaiaOAuthClient::Delegate OAuth2 error when fetching refresh token request.
352 void EnrollmentHandlerChromeOS::OnOAuthError() {
353 CHECK_EQ(STEP_ROBOT_AUTH_REFRESH
, enrollment_step_
);
354 // OnOAuthError is only called if the request is bad (malformed) or the
355 // response is bad (empty access token returned).
356 LOG(ERROR
) << "OAuth protocol error while fetching API refresh token.";
358 EnrollmentStatus::ForRobotRefreshFetchError(net::HTTP_BAD_REQUEST
));
361 // GaiaOAuthClient::Delegate network error when fetching refresh token.
362 void EnrollmentHandlerChromeOS::OnNetworkError(int response_code
) {
363 CHECK_EQ(STEP_ROBOT_AUTH_REFRESH
, enrollment_step_
);
364 LOG(ERROR
) << "Network error while fetching API refresh token: "
367 EnrollmentStatus::ForRobotRefreshFetchError(response_code
));
370 void EnrollmentHandlerChromeOS::StartLockDevice() {
371 CHECK_EQ(STEP_LOCK_DEVICE
, enrollment_step_
);
372 // Since this method is also called directly.
373 weak_ptr_factory_
.InvalidateWeakPtrs();
375 if (management_mode_
== MANAGEMENT_MODE_CONSUMER_MANAGED
) {
376 CHECK(owner_settings_service_
);
378 // Consumer device enrollment doesn't use install attributes. Instead,
379 // we put the information in the owners settings.
380 enrollment_step_
= STEP_STORE_TOKEN_AND_ID
;
381 chromeos::OwnerSettingsServiceChromeOS::ManagementSettings settings
;
382 settings
.management_mode
= management_mode_
;
383 settings
.request_token
= request_token_
;
384 settings
.device_id
= device_id_
;
385 owner_settings_service_
->SetManagementSettings(
387 base::Bind(&EnrollmentHandlerChromeOS::HandleSetManagementSettingsDone
,
388 weak_ptr_factory_
.GetWeakPtr()));
390 install_attributes_
->LockDevice(
391 username_
, device_mode_
, device_id_
,
392 base::Bind(&EnrollmentHandlerChromeOS::HandleLockDeviceResult
,
393 weak_ptr_factory_
.GetWeakPtr()));
397 void EnrollmentHandlerChromeOS::HandleSetManagementSettingsDone(bool success
) {
398 CHECK_EQ(STEP_STORE_TOKEN_AND_ID
, enrollment_step_
);
400 ReportResult(EnrollmentStatus::ForStatus(
401 EnrollmentStatus::STATUS_STORE_TOKEN_AND_ID_FAILED
));
405 StartStoreRobotAuth();
408 void EnrollmentHandlerChromeOS::HandleLockDeviceResult(
409 EnterpriseInstallAttributes::LockResult lock_result
) {
410 CHECK_EQ(STEP_LOCK_DEVICE
, enrollment_step_
);
411 switch (lock_result
) {
412 case EnterpriseInstallAttributes::LOCK_SUCCESS
:
413 StartStoreRobotAuth();
415 case EnterpriseInstallAttributes::LOCK_NOT_READY
:
416 // We wait up to |kLockRetryTimeoutMs| milliseconds and if it hasn't
417 // succeeded by then show an error to the user and stop the enrollment.
418 if (lockbox_init_duration_
< kLockRetryTimeoutMs
) {
419 // InstallAttributes not ready yet, retry later.
420 LOG(WARNING
) << "Install Attributes not ready yet will retry in "
421 << kLockRetryIntervalMs
<< "ms.";
422 base::MessageLoop::current()->PostDelayedTask(
424 base::Bind(&EnrollmentHandlerChromeOS::StartLockDevice
,
425 weak_ptr_factory_
.GetWeakPtr()),
426 base::TimeDelta::FromMilliseconds(kLockRetryIntervalMs
));
427 lockbox_init_duration_
+= kLockRetryIntervalMs
;
429 HandleLockDeviceResult(EnterpriseInstallAttributes::LOCK_TIMEOUT
);
432 case EnterpriseInstallAttributes::LOCK_TIMEOUT
:
433 case EnterpriseInstallAttributes::LOCK_BACKEND_INVALID
:
434 case EnterpriseInstallAttributes::LOCK_ALREADY_LOCKED
:
435 case EnterpriseInstallAttributes::LOCK_SET_ERROR
:
436 case EnterpriseInstallAttributes::LOCK_FINALIZE_ERROR
:
437 case EnterpriseInstallAttributes::LOCK_READBACK_ERROR
:
438 case EnterpriseInstallAttributes::LOCK_WRONG_DOMAIN
:
439 ReportResult(EnrollmentStatus::ForLockError(lock_result
));
444 void EnrollmentHandlerChromeOS::StartStoreRobotAuth() {
445 // Get the token service so we can store our robot refresh token.
446 enrollment_step_
= STEP_STORE_ROBOT_AUTH
;
447 chromeos::DeviceOAuth2TokenServiceFactory::Get()->SetAndSaveRefreshToken(
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