enrollment_handler_chromeos.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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.
4
5#include "chrome/browser/chromeos/policy/enrollment_handler_chromeos.h"
6
7#include "base/bind.h"
8#include "base/logging.h"
9#include "base/message_loop.h"
10#include "chrome/browser/browser_process.h"
11#include "chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h"
12#include "chrome/browser/chromeos/settings/device_oauth2_token_service.h"
13#include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h"
14#include "chrome/browser/policy/cloud/cloud_policy_constants.h"
15#include "chrome/browser/policy/proto/chromeos/chrome_device_policy.pb.h"
16#include "chrome/browser/policy/proto/cloud/device_management_backend.pb.h"
17#include "google_apis/gaia/gaia_urls.h"
18
19namespace em = enterprise_management;
20
21namespace policy {
22
23namespace {
24
25// Retry for InstallAttrs initialization every 500ms.
26const int kLockRetryIntervalMs = 500;
27// Maximum time to retry InstallAttrs initialization before we give up.
28const int kLockRetryTimeoutMs = 10 * 60 * 1000;  // 10 minutes.
29
30}  // namespace
31
32EnrollmentHandlerChromeOS::EnrollmentHandlerChromeOS(
33    DeviceCloudPolicyStoreChromeOS* store,
34    EnterpriseInstallAttributes* install_attributes,
35    scoped_ptr<CloudPolicyClient> client,
36    const std::string& auth_token,
37    const std::string& client_id,
38    bool is_auto_enrollment,
39    const std::string& requisition,
40    const AllowedDeviceModes& allowed_device_modes,
41    const EnrollmentCallback& completion_callback)
42    : store_(store),
43      install_attributes_(install_attributes),
44      client_(client.Pass()),
45      auth_token_(auth_token),
46      client_id_(client_id),
47      is_auto_enrollment_(is_auto_enrollment),
48      requisition_(requisition),
49      allowed_device_modes_(allowed_device_modes),
50      completion_callback_(completion_callback),
51      device_mode_(DEVICE_MODE_NOT_SET),
52      enrollment_step_(STEP_PENDING),
53      lockbox_init_duration_(0),
54      weak_factory_(this) {
55  CHECK(!client_->is_registered());
56  CHECK_EQ(DM_STATUS_SUCCESS, client_->status());
57  store_->AddObserver(this);
58  client_->AddObserver(this);
59  client_->AddNamespaceToFetch(PolicyNamespaceKey(
60      dm_protocol::kChromeDevicePolicyType, std::string()));
61}
62
63EnrollmentHandlerChromeOS::~EnrollmentHandlerChromeOS() {
64  Stop();
65  store_->RemoveObserver(this);
66}
67
68void EnrollmentHandlerChromeOS::StartEnrollment() {
69  CHECK_EQ(STEP_PENDING, enrollment_step_);
70  enrollment_step_ = STEP_LOADING_STORE;
71  AttemptRegistration();
72}
73
74scoped_ptr<CloudPolicyClient> EnrollmentHandlerChromeOS::ReleaseClient() {
75  Stop();
76  return client_.Pass();
77}
78
79void EnrollmentHandlerChromeOS::OnPolicyFetched(CloudPolicyClient* client) {
80  DCHECK_EQ(client_.get(), client);
81  CHECK_EQ(STEP_POLICY_FETCH, enrollment_step_);
82
83  enrollment_step_ = STEP_VALIDATION;
84
85  // Validate the policy.
86  const em::PolicyFetchResponse* policy = client_->GetPolicyFor(
87      PolicyNamespaceKey(dm_protocol::kChromeDevicePolicyType, std::string()));
88  if (!policy) {
89    ReportResult(EnrollmentStatus::ForFetchError(
90        DM_STATUS_RESPONSE_DECODING_ERROR));
91    return;
92  }
93
94  scoped_ptr<DeviceCloudPolicyValidator> validator(
95      DeviceCloudPolicyValidator::Create(
96          scoped_ptr<em::PolicyFetchResponse>(
97              new em::PolicyFetchResponse(*policy))));
98
99  validator->ValidateTimestamp(base::Time(), base::Time::NowFromSystemTime(),
100                               CloudPolicyValidatorBase::TIMESTAMP_REQUIRED);
101  if (install_attributes_->IsEnterpriseDevice())
102    validator->ValidateDomain(install_attributes_->GetDomain());
103  validator->ValidateDMToken(client->dm_token(),
104                             CloudPolicyValidatorBase::DM_TOKEN_REQUIRED);
105  validator->ValidatePolicyType(dm_protocol::kChromeDevicePolicyType);
106  validator->ValidatePayload();
107  validator->ValidateInitialKey();
108  validator.release()->StartValidation(
109      base::Bind(&EnrollmentHandlerChromeOS::PolicyValidated,
110                 weak_factory_.GetWeakPtr()));
111}
112
113void EnrollmentHandlerChromeOS::OnRegistrationStateChanged(
114    CloudPolicyClient* client) {
115  DCHECK_EQ(client_.get(), client);
116
117  if (enrollment_step_ == STEP_REGISTRATION && client_->is_registered()) {
118    enrollment_step_ = STEP_POLICY_FETCH,
119    device_mode_ = client_->device_mode();
120    if (device_mode_ == DEVICE_MODE_NOT_SET)
121      device_mode_ = DEVICE_MODE_ENTERPRISE;
122    if (!allowed_device_modes_.test(device_mode_)) {
123      LOG(ERROR) << "Bad device mode " << device_mode_;
124      ReportResult(EnrollmentStatus::ForStatus(
125          EnrollmentStatus::STATUS_REGISTRATION_BAD_MODE));
126      return;
127    }
128    client_->FetchPolicy();
129  } else {
130    LOG(FATAL) << "Registration state changed to " << client_->is_registered()
131               << " in step " << enrollment_step_;
132  }
133}
134
135void EnrollmentHandlerChromeOS::OnClientError(CloudPolicyClient* client) {
136  DCHECK_EQ(client_.get(), client);
137
138  if (enrollment_step_ == STEP_ROBOT_AUTH_FETCH) {
139    LOG(WARNING) << "API authentication code fetch failed: "
140                 << client_->status();
141    // Robot auth tokens are currently optional.  Skip fetching the refresh
142    // token and jump directly to the lock device step.
143    robot_refresh_token_.clear();
144    DoLockDeviceStep();
145  } else if (enrollment_step_ < STEP_POLICY_FETCH) {
146    ReportResult(EnrollmentStatus::ForRegistrationError(client_->status()));
147  } else {
148    ReportResult(EnrollmentStatus::ForFetchError(client_->status()));
149  }
150}
151
152void EnrollmentHandlerChromeOS::OnStoreLoaded(CloudPolicyStore* store) {
153  DCHECK_EQ(store_, store);
154
155  if (enrollment_step_ == STEP_LOADING_STORE) {
156    // If the |store_| wasn't initialized when StartEnrollment() was
157    // called, then AttemptRegistration() bails silently.  This gets
158    // registration rolling again after the store finishes loading.
159    AttemptRegistration();
160  } else if (enrollment_step_ == STEP_STORE_POLICY) {
161    // Store the robot API auth refresh token.
162    // Currently optional, so always return success.
163    chromeos::DeviceOAuth2TokenService* token_service =
164        chromeos::DeviceOAuth2TokenServiceFactory::Get();
165    if (token_service && !robot_refresh_token_.empty()) {
166      token_service->SetAndSaveRefreshToken(robot_refresh_token_);
167
168    }
169    ReportResult(EnrollmentStatus::ForStatus(EnrollmentStatus::STATUS_SUCCESS));
170  }
171}
172
173void EnrollmentHandlerChromeOS::OnStoreError(CloudPolicyStore* store) {
174  DCHECK_EQ(store_, store);
175  ReportResult(EnrollmentStatus::ForStoreError(store_->status(),
176                                               store_->validation_status()));
177}
178
179void 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_,
185                      requisition_);
186  }
187}
188
189void 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();
196
197    enrollment_step_ = STEP_ROBOT_AUTH_FETCH;
198    client_->FetchRobotAuthCodes(auth_token_);
199  } else {
200    ReportResult(EnrollmentStatus::ForValidationError(validator->status()));
201  }
202}
203
204void EnrollmentHandlerChromeOS::OnRobotAuthCodesFetched(
205    CloudPolicyClient* client) {
206  DCHECK_EQ(client_.get(), client);
207  CHECK_EQ(STEP_ROBOT_AUTH_FETCH, enrollment_step_);
208
209  enrollment_step_ = STEP_ROBOT_AUTH_REFRESH;
210
211  gaia::OAuthClientInfo client_info;
212  client_info.client_id = GaiaUrls::GetInstance()->oauth2_chrome_client_id();
213  client_info.client_secret =
214      GaiaUrls::GetInstance()->oauth2_chrome_client_secret();
215  client_info.redirect_uri = "oob";
216
217  // Use the system request context to avoid sending user cookies.
218  gaia_oauth_client_.reset(new gaia::GaiaOAuthClient(
219      GaiaUrls::GetInstance()->oauth2_token_url(),
220      g_browser_process->system_request_context()));
221  gaia_oauth_client_->GetTokensFromAuthCode(client_info,
222                                            client->robot_api_auth_code(),
223                                            0 /* max_retries */,
224                                            this);
225}
226
227// GaiaOAuthClient::Delegate callback for OAuth2 refresh token fetched.
228void EnrollmentHandlerChromeOS::OnGetTokensResponse(
229    const std::string& refresh_token,
230    const std::string& access_token,
231    int expires_in_seconds) {
232  CHECK_EQ(STEP_ROBOT_AUTH_REFRESH, enrollment_step_);
233
234  robot_refresh_token_ = refresh_token;
235
236  DoLockDeviceStep();
237}
238
239void EnrollmentHandlerChromeOS::DoLockDeviceStep() {
240  enrollment_step_ = STEP_LOCK_DEVICE,
241  StartLockDevice(username_, device_mode_, device_id_);
242}
243
244// GaiaOAuthClient::Delegate
245void EnrollmentHandlerChromeOS::OnRefreshTokenResponse(
246    const std::string& access_token,
247    int expires_in_seconds) {
248  // We never use the code that should trigger this callback.
249  LOG(FATAL) << "Unexpected callback invoked";
250}
251
252// GaiaOAuthClient::Delegate OAuth2 error when fetching refresh token request.
253void EnrollmentHandlerChromeOS::OnOAuthError() {
254  CHECK_EQ(STEP_ROBOT_AUTH_REFRESH, enrollment_step_);
255  DoLockDeviceStep();
256}
257
258// GaiaOAuthClient::Delegate network error when fetching refresh token.
259void EnrollmentHandlerChromeOS::OnNetworkError(int response_code) {
260  LOG(ERROR) << "Network error while fetching API refresh token: "
261             << response_code;
262  CHECK_EQ(STEP_ROBOT_AUTH_REFRESH, enrollment_step_);
263  DoLockDeviceStep();
264}
265
266void EnrollmentHandlerChromeOS::StartLockDevice(
267    const std::string& user,
268    DeviceMode device_mode,
269    const std::string& device_id) {
270  CHECK_EQ(STEP_LOCK_DEVICE, enrollment_step_);
271  // Since this method is also called directly.
272  weak_factory_.InvalidateWeakPtrs();
273
274  install_attributes_->LockDevice(
275      user, device_mode, device_id,
276      base::Bind(&EnrollmentHandlerChromeOS::HandleLockDeviceResult,
277                 weak_factory_.GetWeakPtr(),
278                 user,
279                 device_mode,
280                 device_id));
281}
282
283void EnrollmentHandlerChromeOS::HandleLockDeviceResult(
284    const std::string& user,
285    DeviceMode device_mode,
286    const std::string& device_id,
287    EnterpriseInstallAttributes::LockResult lock_result) {
288  CHECK_EQ(STEP_LOCK_DEVICE, enrollment_step_);
289  switch (lock_result) {
290    case EnterpriseInstallAttributes::LOCK_SUCCESS:
291      enrollment_step_ = STEP_STORE_POLICY;
292      store_->InstallInitialPolicy(*policy_);
293      return;
294    case EnterpriseInstallAttributes::LOCK_NOT_READY:
295      // We wait up to |kLockRetryTimeoutMs| milliseconds and if it hasn't
296      // succeeded by then show an error to the user and stop the enrollment.
297      if (lockbox_init_duration_ < kLockRetryTimeoutMs) {
298        // InstallAttributes not ready yet, retry later.
299        LOG(WARNING) << "Install Attributes not ready yet will retry in "
300                     << kLockRetryIntervalMs << "ms.";
301        base::MessageLoop::current()->PostDelayedTask(
302            FROM_HERE,
303            base::Bind(&EnrollmentHandlerChromeOS::StartLockDevice,
304                       weak_factory_.GetWeakPtr(),
305                       user, device_mode, device_id),
306            base::TimeDelta::FromMilliseconds(kLockRetryIntervalMs));
307        lockbox_init_duration_ += kLockRetryIntervalMs;
308      } else {
309        ReportResult(EnrollmentStatus::ForStatus(
310            EnrollmentStatus::STATUS_LOCK_TIMEOUT));
311      }
312      return;
313    case EnterpriseInstallAttributes::LOCK_BACKEND_ERROR:
314      ReportResult(EnrollmentStatus::ForStatus(
315          EnrollmentStatus::STATUS_LOCK_ERROR));
316      return;
317    case EnterpriseInstallAttributes::LOCK_WRONG_USER:
318      LOG(ERROR) << "Enrollment cannot proceed because the InstallAttrs "
319                 << "has been locked already!";
320      ReportResult(EnrollmentStatus::ForStatus(
321          EnrollmentStatus::STATUS_LOCK_WRONG_USER));
322      return;
323  }
324
325  NOTREACHED() << "Invalid lock result " << lock_result;
326  ReportResult(EnrollmentStatus::ForStatus(
327      EnrollmentStatus::STATUS_LOCK_ERROR));
328}
329
330void EnrollmentHandlerChromeOS::Stop() {
331  if (client_.get())
332    client_->RemoveObserver(this);
333  enrollment_step_ = STEP_FINISHED;
334  weak_factory_.InvalidateWeakPtrs();
335  completion_callback_.Reset();
336}
337
338void EnrollmentHandlerChromeOS::ReportResult(EnrollmentStatus status) {
339  EnrollmentCallback callback = completion_callback_;
340  Stop();
341
342  if (status.status() != EnrollmentStatus::STATUS_SUCCESS) {
343    LOG(WARNING) << "Enrollment failed: " << status.status()
344                 << " " << status.client_status()
345                 << " " << status.validation_status()
346                 << " " << status.store_status();
347  }
348
349  if (!callback.is_null())
350    callback.Run(status);
351}
352
353}  // namespace policy
354