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