enrollment_handler_chromeos.cc revision 9ab5563a3196760eb381d102cbb2bc0f7abc6a50
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/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      g_browser_process->system_request_context()));
220  gaia_oauth_client_->GetTokensFromAuthCode(client_info,
221                                            client->robot_api_auth_code(),
222                                            0 /* max_retries */,
223                                            this);
224}
225
226// GaiaOAuthClient::Delegate callback for OAuth2 refresh token fetched.
227void EnrollmentHandlerChromeOS::OnGetTokensResponse(
228    const std::string& refresh_token,
229    const std::string& access_token,
230    int expires_in_seconds) {
231  CHECK_EQ(STEP_ROBOT_AUTH_REFRESH, enrollment_step_);
232
233  robot_refresh_token_ = refresh_token;
234
235  DoLockDeviceStep();
236}
237
238void EnrollmentHandlerChromeOS::DoLockDeviceStep() {
239  enrollment_step_ = STEP_LOCK_DEVICE,
240  StartLockDevice(username_, device_mode_, device_id_);
241}
242
243// GaiaOAuthClient::Delegate
244void EnrollmentHandlerChromeOS::OnRefreshTokenResponse(
245    const std::string& access_token,
246    int expires_in_seconds) {
247  // We never use the code that should trigger this callback.
248  LOG(FATAL) << "Unexpected callback invoked";
249}
250
251// GaiaOAuthClient::Delegate OAuth2 error when fetching refresh token request.
252void EnrollmentHandlerChromeOS::OnOAuthError() {
253  CHECK_EQ(STEP_ROBOT_AUTH_REFRESH, enrollment_step_);
254  DoLockDeviceStep();
255}
256
257// GaiaOAuthClient::Delegate network error when fetching refresh token.
258void EnrollmentHandlerChromeOS::OnNetworkError(int response_code) {
259  LOG(ERROR) << "Network error while fetching API refresh token: "
260             << response_code;
261  CHECK_EQ(STEP_ROBOT_AUTH_REFRESH, enrollment_step_);
262  DoLockDeviceStep();
263}
264
265void EnrollmentHandlerChromeOS::StartLockDevice(
266    const std::string& user,
267    DeviceMode device_mode,
268    const std::string& device_id) {
269  CHECK_EQ(STEP_LOCK_DEVICE, enrollment_step_);
270  // Since this method is also called directly.
271  weak_factory_.InvalidateWeakPtrs();
272
273  install_attributes_->LockDevice(
274      user, device_mode, device_id,
275      base::Bind(&EnrollmentHandlerChromeOS::HandleLockDeviceResult,
276                 weak_factory_.GetWeakPtr(),
277                 user,
278                 device_mode,
279                 device_id));
280}
281
282void EnrollmentHandlerChromeOS::HandleLockDeviceResult(
283    const std::string& user,
284    DeviceMode device_mode,
285    const std::string& device_id,
286    EnterpriseInstallAttributes::LockResult lock_result) {
287  CHECK_EQ(STEP_LOCK_DEVICE, enrollment_step_);
288  switch (lock_result) {
289    case EnterpriseInstallAttributes::LOCK_SUCCESS:
290      enrollment_step_ = STEP_STORE_POLICY;
291      store_->InstallInitialPolicy(*policy_);
292      return;
293    case EnterpriseInstallAttributes::LOCK_NOT_READY:
294      // We wait up to |kLockRetryTimeoutMs| milliseconds and if it hasn't
295      // succeeded by then show an error to the user and stop the enrollment.
296      if (lockbox_init_duration_ < kLockRetryTimeoutMs) {
297        // InstallAttributes not ready yet, retry later.
298        LOG(WARNING) << "Install Attributes not ready yet will retry in "
299                     << kLockRetryIntervalMs << "ms.";
300        base::MessageLoop::current()->PostDelayedTask(
301            FROM_HERE,
302            base::Bind(&EnrollmentHandlerChromeOS::StartLockDevice,
303                       weak_factory_.GetWeakPtr(),
304                       user, device_mode, device_id),
305            base::TimeDelta::FromMilliseconds(kLockRetryIntervalMs));
306        lockbox_init_duration_ += kLockRetryIntervalMs;
307      } else {
308        ReportResult(EnrollmentStatus::ForStatus(
309            EnrollmentStatus::STATUS_LOCK_TIMEOUT));
310      }
311      return;
312    case EnterpriseInstallAttributes::LOCK_BACKEND_ERROR:
313      ReportResult(EnrollmentStatus::ForStatus(
314          EnrollmentStatus::STATUS_LOCK_ERROR));
315      return;
316    case EnterpriseInstallAttributes::LOCK_WRONG_USER:
317      LOG(ERROR) << "Enrollment cannot proceed because the InstallAttrs "
318                 << "has been locked already!";
319      ReportResult(EnrollmentStatus::ForStatus(
320          EnrollmentStatus::STATUS_LOCK_WRONG_USER));
321      return;
322  }
323
324  NOTREACHED() << "Invalid lock result " << lock_result;
325  ReportResult(EnrollmentStatus::ForStatus(
326      EnrollmentStatus::STATUS_LOCK_ERROR));
327}
328
329void EnrollmentHandlerChromeOS::Stop() {
330  if (client_.get())
331    client_->RemoveObserver(this);
332  enrollment_step_ = STEP_FINISHED;
333  weak_factory_.InvalidateWeakPtrs();
334  completion_callback_.Reset();
335}
336
337void EnrollmentHandlerChromeOS::ReportResult(EnrollmentStatus status) {
338  EnrollmentCallback callback = completion_callback_;
339  Stop();
340
341  if (status.status() != EnrollmentStatus::STATUS_SUCCESS) {
342    LOG(WARNING) << "Enrollment failed: " << status.status()
343                 << " " << status.client_status()
344                 << " " << status.validation_status()
345                 << " " << status.store_status();
346  }
347
348  if (!callback.is_null())
349    callback.Run(status);
350}
351
352}  // namespace policy
353