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/policy/device_cloud_policy_store_chromeos.h"
13 #include "chrome/browser/chromeos/policy/proto/chrome_device_policy.pb.h"
14 #include "chrome/browser/chromeos/settings/device_oauth2_token_service.h"
15 #include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h"
16 #include "chromeos/chromeos_switches.h"
17 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
18 #include "google_apis/gaia/gaia_urls.h"
19 #include "net/http/http_status_code.h"
20 #include "policy/proto/device_management_backend.pb.h"
22 namespace em
= enterprise_management
;
28 // Retry for InstallAttrs initialization every 500ms.
29 const int kLockRetryIntervalMs
= 500;
30 // Maximum time to retry InstallAttrs initialization before we give up.
31 const int kLockRetryTimeoutMs
= 10 * 60 * 1000; // 10 minutes.
33 // Testing token used when the enrollment-skip-robot-auth is set to skip talking
34 // to GAIA for an actual token. This is needed to be able to run against the
35 // testing DMServer implementations.
36 const char kTestingRobotToken
[] = "test-token";
40 EnrollmentHandlerChromeOS::EnrollmentHandlerChromeOS(
41 DeviceCloudPolicyStoreChromeOS
* store
,
42 EnterpriseInstallAttributes
* install_attributes
,
43 scoped_ptr
<CloudPolicyClient
> client
,
44 scoped_refptr
<base::SequencedTaskRunner
> background_task_runner
,
45 const std::string
& auth_token
,
46 const std::string
& client_id
,
47 bool is_auto_enrollment
,
48 const std::string
& requisition
,
49 const AllowedDeviceModes
& allowed_device_modes
,
50 const EnrollmentCallback
& completion_callback
)
52 install_attributes_(install_attributes
),
53 client_(client
.Pass()),
54 background_task_runner_(background_task_runner
),
55 auth_token_(auth_token
),
56 client_id_(client_id
),
57 is_auto_enrollment_(is_auto_enrollment
),
58 requisition_(requisition
),
59 allowed_device_modes_(allowed_device_modes
),
60 completion_callback_(completion_callback
),
61 device_mode_(DEVICE_MODE_NOT_SET
),
62 enrollment_step_(STEP_PENDING
),
63 lockbox_init_duration_(0),
64 weak_ptr_factory_(this) {
65 CHECK(!client_
->is_registered());
66 CHECK_EQ(DM_STATUS_SUCCESS
, client_
->status());
67 store_
->AddObserver(this);
68 client_
->AddObserver(this);
69 client_
->AddNamespaceToFetch(PolicyNamespaceKey(
70 dm_protocol::kChromeDevicePolicyType
, std::string()));
73 EnrollmentHandlerChromeOS::~EnrollmentHandlerChromeOS() {
75 store_
->RemoveObserver(this);
78 void EnrollmentHandlerChromeOS::StartEnrollment() {
79 CHECK_EQ(STEP_PENDING
, enrollment_step_
);
80 enrollment_step_
= STEP_LOADING_STORE
;
81 AttemptRegistration();
84 scoped_ptr
<CloudPolicyClient
> EnrollmentHandlerChromeOS::ReleaseClient() {
86 return client_
.Pass();
89 void EnrollmentHandlerChromeOS::OnPolicyFetched(CloudPolicyClient
* client
) {
90 DCHECK_EQ(client_
.get(), client
);
91 CHECK_EQ(STEP_POLICY_FETCH
, enrollment_step_
);
93 enrollment_step_
= STEP_VALIDATION
;
95 // Validate the policy.
96 const em::PolicyFetchResponse
* policy
= client_
->GetPolicyFor(
97 PolicyNamespaceKey(dm_protocol::kChromeDevicePolicyType
, std::string()));
99 ReportResult(EnrollmentStatus::ForFetchError(
100 DM_STATUS_RESPONSE_DECODING_ERROR
));
104 scoped_ptr
<DeviceCloudPolicyValidator
> validator(
105 DeviceCloudPolicyValidator::Create(
106 scoped_ptr
<em::PolicyFetchResponse
>(
107 new em::PolicyFetchResponse(*policy
)),
108 background_task_runner_
));
110 validator
->ValidateTimestamp(base::Time(), base::Time::NowFromSystemTime(),
111 CloudPolicyValidatorBase::TIMESTAMP_REQUIRED
);
112 if (install_attributes_
->IsEnterpriseDevice())
113 validator
->ValidateDomain(install_attributes_
->GetDomain());
114 validator
->ValidateDMToken(client
->dm_token(),
115 CloudPolicyValidatorBase::DM_TOKEN_REQUIRED
);
116 validator
->ValidatePolicyType(dm_protocol::kChromeDevicePolicyType
);
117 validator
->ValidatePayload();
118 validator
->ValidateInitialKey();
119 validator
.release()->StartValidation(
120 base::Bind(&EnrollmentHandlerChromeOS::PolicyValidated
,
121 weak_ptr_factory_
.GetWeakPtr()));
124 void EnrollmentHandlerChromeOS::OnRegistrationStateChanged(
125 CloudPolicyClient
* client
) {
126 DCHECK_EQ(client_
.get(), client
);
128 if (enrollment_step_
== STEP_REGISTRATION
&& client_
->is_registered()) {
129 enrollment_step_
= STEP_POLICY_FETCH
,
130 device_mode_
= client_
->device_mode();
131 if (device_mode_
== DEVICE_MODE_NOT_SET
)
132 device_mode_
= DEVICE_MODE_ENTERPRISE
;
133 if (!allowed_device_modes_
.test(device_mode_
)) {
134 LOG(ERROR
) << "Bad device mode " << device_mode_
;
135 ReportResult(EnrollmentStatus::ForStatus(
136 EnrollmentStatus::STATUS_REGISTRATION_BAD_MODE
));
139 client_
->FetchPolicy();
141 LOG(FATAL
) << "Registration state changed to " << client_
->is_registered()
142 << " in step " << enrollment_step_
;
146 void EnrollmentHandlerChromeOS::OnClientError(CloudPolicyClient
* client
) {
147 DCHECK_EQ(client_
.get(), client
);
149 if (enrollment_step_
== STEP_ROBOT_AUTH_FETCH
) {
150 LOG(ERROR
) << "API authentication code fetch failed: "
151 << client_
->status();
152 ReportResult(EnrollmentStatus::ForRobotAuthFetchError(client_
->status()));
153 } else if (enrollment_step_
< STEP_POLICY_FETCH
) {
154 ReportResult(EnrollmentStatus::ForRegistrationError(client_
->status()));
156 ReportResult(EnrollmentStatus::ForFetchError(client_
->status()));
160 void EnrollmentHandlerChromeOS::OnStoreLoaded(CloudPolicyStore
* store
) {
161 DCHECK_EQ(store_
, store
);
163 if (enrollment_step_
== STEP_LOADING_STORE
) {
164 // If the |store_| wasn't initialized when StartEnrollment() was
165 // called, then AttemptRegistration() bails silently. This gets
166 // registration rolling again after the store finishes loading.
167 AttemptRegistration();
168 } else if (enrollment_step_
== STEP_STORE_POLICY
) {
169 ReportResult(EnrollmentStatus::ForStatus(EnrollmentStatus::STATUS_SUCCESS
));
173 void EnrollmentHandlerChromeOS::OnStoreError(CloudPolicyStore
* store
) {
174 DCHECK_EQ(store_
, store
);
175 ReportResult(EnrollmentStatus::ForStoreError(store_
->status(),
176 store_
->validation_status()));
179 void EnrollmentHandlerChromeOS::AttemptRegistration() {
180 CHECK_EQ(STEP_LOADING_STORE
, enrollment_step_
);
181 if (store_
->is_initialized()) {
182 enrollment_step_
= STEP_REGISTRATION
;
183 client_
->Register(em::DeviceRegisterRequest::DEVICE
,
184 auth_token_
, client_id_
, is_auto_enrollment_
,
189 void EnrollmentHandlerChromeOS::PolicyValidated(
190 DeviceCloudPolicyValidator
* validator
) {
191 CHECK_EQ(STEP_VALIDATION
, enrollment_step_
);
192 if (validator
->success()) {
193 policy_
= validator
->policy().Pass();
194 username_
= validator
->policy_data()->username();
195 device_id_
= validator
->policy_data()->device_id();
197 if (CommandLine::ForCurrentProcess()->HasSwitch(
198 chromeos::switches::kEnterpriseEnrollmentSkipRobotAuth
)) {
199 // For test purposes we allow enrollment to succeed without proper robot
200 // account and use the provided value as a token.
201 refresh_token_
= kTestingRobotToken
;
202 enrollment_step_
= STEP_LOCK_DEVICE
,
203 StartLockDevice(username_
, device_mode_
, device_id_
);
207 enrollment_step_
= STEP_ROBOT_AUTH_FETCH
;
208 client_
->FetchRobotAuthCodes(auth_token_
);
210 ReportResult(EnrollmentStatus::ForValidationError(validator
->status()));
214 void EnrollmentHandlerChromeOS::OnRobotAuthCodesFetched(
215 CloudPolicyClient
* client
) {
216 DCHECK_EQ(client_
.get(), client
);
217 CHECK_EQ(STEP_ROBOT_AUTH_FETCH
, enrollment_step_
);
219 enrollment_step_
= STEP_ROBOT_AUTH_REFRESH
;
221 gaia::OAuthClientInfo client_info
;
222 client_info
.client_id
= GaiaUrls::GetInstance()->oauth2_chrome_client_id();
223 client_info
.client_secret
=
224 GaiaUrls::GetInstance()->oauth2_chrome_client_secret();
225 client_info
.redirect_uri
= "oob";
227 // Use the system request context to avoid sending user cookies.
228 gaia_oauth_client_
.reset(new gaia::GaiaOAuthClient(
229 g_browser_process
->system_request_context()));
230 gaia_oauth_client_
->GetTokensFromAuthCode(client_info
,
231 client
->robot_api_auth_code(),
236 // GaiaOAuthClient::Delegate callback for OAuth2 refresh token fetched.
237 void EnrollmentHandlerChromeOS::OnGetTokensResponse(
238 const std::string
& refresh_token
,
239 const std::string
& access_token
,
240 int expires_in_seconds
) {
241 CHECK_EQ(STEP_ROBOT_AUTH_REFRESH
, enrollment_step_
);
243 refresh_token_
= refresh_token
;
245 enrollment_step_
= STEP_LOCK_DEVICE
,
246 StartLockDevice(username_
, device_mode_
, device_id_
);
249 // GaiaOAuthClient::Delegate
250 void EnrollmentHandlerChromeOS::OnRefreshTokenResponse(
251 const std::string
& access_token
,
252 int expires_in_seconds
) {
253 // We never use the code that should trigger this callback.
254 LOG(FATAL
) << "Unexpected callback invoked";
257 // GaiaOAuthClient::Delegate OAuth2 error when fetching refresh token request.
258 void EnrollmentHandlerChromeOS::OnOAuthError() {
259 CHECK_EQ(STEP_ROBOT_AUTH_REFRESH
, enrollment_step_
);
260 // OnOAuthError is only called if the request is bad (malformed) or the
261 // response is bad (empty access token returned).
262 LOG(ERROR
) << "OAuth protocol error while fetching API refresh token.";
264 EnrollmentStatus::ForRobotRefreshFetchError(net::HTTP_BAD_REQUEST
));
267 // GaiaOAuthClient::Delegate network error when fetching refresh token.
268 void EnrollmentHandlerChromeOS::OnNetworkError(int response_code
) {
269 CHECK_EQ(STEP_ROBOT_AUTH_REFRESH
, enrollment_step_
);
270 LOG(ERROR
) << "Network error while fetching API refresh token: "
273 EnrollmentStatus::ForRobotRefreshFetchError(response_code
));
276 void EnrollmentHandlerChromeOS::StartLockDevice(
277 const std::string
& user
,
278 DeviceMode device_mode
,
279 const std::string
& device_id
) {
280 CHECK_EQ(STEP_LOCK_DEVICE
, enrollment_step_
);
281 // Since this method is also called directly.
282 weak_ptr_factory_
.InvalidateWeakPtrs();
284 install_attributes_
->LockDevice(
285 user
, device_mode
, device_id
,
286 base::Bind(&EnrollmentHandlerChromeOS::HandleLockDeviceResult
,
287 weak_ptr_factory_
.GetWeakPtr(),
293 void EnrollmentHandlerChromeOS::HandleLockDeviceResult(
294 const std::string
& user
,
295 DeviceMode device_mode
,
296 const std::string
& device_id
,
297 EnterpriseInstallAttributes::LockResult lock_result
) {
298 CHECK_EQ(STEP_LOCK_DEVICE
, enrollment_step_
);
299 switch (lock_result
) {
300 case EnterpriseInstallAttributes::LOCK_SUCCESS
:
301 // Get the token service so we can store our robot refresh token.
302 enrollment_step_
= STEP_STORE_ROBOT_AUTH
;
303 chromeos::DeviceOAuth2TokenServiceFactory::Get(
304 base::Bind(&EnrollmentHandlerChromeOS::DidGetTokenService
,
305 weak_ptr_factory_
.GetWeakPtr()));
307 case EnterpriseInstallAttributes::LOCK_NOT_READY
:
308 // We wait up to |kLockRetryTimeoutMs| milliseconds and if it hasn't
309 // succeeded by then show an error to the user and stop the enrollment.
310 if (lockbox_init_duration_
< kLockRetryTimeoutMs
) {
311 // InstallAttributes not ready yet, retry later.
312 LOG(WARNING
) << "Install Attributes not ready yet will retry in "
313 << kLockRetryIntervalMs
<< "ms.";
314 base::MessageLoop::current()->PostDelayedTask(
316 base::Bind(&EnrollmentHandlerChromeOS::StartLockDevice
,
317 weak_ptr_factory_
.GetWeakPtr(),
318 user
, device_mode
, device_id
),
319 base::TimeDelta::FromMilliseconds(kLockRetryIntervalMs
));
320 lockbox_init_duration_
+= kLockRetryIntervalMs
;
322 ReportResult(EnrollmentStatus::ForStatus(
323 EnrollmentStatus::STATUS_LOCK_TIMEOUT
));
326 case EnterpriseInstallAttributes::LOCK_BACKEND_ERROR
:
327 ReportResult(EnrollmentStatus::ForStatus(
328 EnrollmentStatus::STATUS_LOCK_ERROR
));
330 case EnterpriseInstallAttributes::LOCK_WRONG_USER
:
331 LOG(ERROR
) << "Enrollment cannot proceed because the InstallAttrs "
332 << "has been locked already!";
333 ReportResult(EnrollmentStatus::ForStatus(
334 EnrollmentStatus::STATUS_LOCK_WRONG_USER
));
338 NOTREACHED() << "Invalid lock result " << lock_result
;
339 ReportResult(EnrollmentStatus::ForStatus(
340 EnrollmentStatus::STATUS_LOCK_ERROR
));
343 void EnrollmentHandlerChromeOS::DidGetTokenService(
344 chromeos::DeviceOAuth2TokenService
* token_service
) {
345 CHECK_EQ(STEP_STORE_ROBOT_AUTH
, enrollment_step_
);
346 // Store the robot API auth refresh token.
347 if (!token_service
) {
348 LOG(ERROR
) << "Failed to store API refresh token (no token service).";
349 ReportResult(EnrollmentStatus::ForStatus(
350 EnrollmentStatus::STATUS_ROBOT_REFRESH_STORE_FAILED
));
354 if (!token_service
->SetAndSaveRefreshToken(refresh_token_
)) {
355 LOG(ERROR
) << "Failed to store API refresh token.";
356 ReportResult(EnrollmentStatus::ForStatus(
357 EnrollmentStatus::STATUS_ROBOT_REFRESH_STORE_FAILED
));
361 enrollment_step_
= STEP_STORE_POLICY
;
362 store_
->InstallInitialPolicy(*policy_
);
365 void EnrollmentHandlerChromeOS::Stop() {
367 client_
->RemoveObserver(this);
368 enrollment_step_
= STEP_FINISHED
;
369 weak_ptr_factory_
.InvalidateWeakPtrs();
370 completion_callback_
.Reset();
373 void EnrollmentHandlerChromeOS::ReportResult(EnrollmentStatus status
) {
374 EnrollmentCallback callback
= completion_callback_
;
377 if (status
.status() != EnrollmentStatus::STATUS_SUCCESS
) {
378 LOG(WARNING
) << "Enrollment failed: " << status
.status()
379 << " " << status
.client_status()
380 << " " << status
.validation_status()
381 << " " << status
.store_status();
384 if (!callback
.is_null())
385 callback
.Run(status
);
388 } // namespace policy