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/login/enrollment/enrollment_screen.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/logging.h"
10#include "base/message_loop/message_loop.h"
11#include "base/metrics/histogram.h"
12#include "base/metrics/sparse_histogram.h"
13#include "base/timer/elapsed_timer.h"
14#include "chrome/browser/browser_process.h"
15#include "chrome/browser/browser_process_platform_part.h"
16#include "chrome/browser/chromeos/login/login_utils.h"
17#include "chrome/browser/chromeos/login/screen_manager.h"
18#include "chrome/browser/chromeos/login/screens/screen_observer.h"
19#include "chrome/browser/chromeos/login/startup_utils.h"
20#include "chrome/browser/chromeos/login/wizard_controller.h"
21#include "chrome/browser/chromeos/policy/auto_enrollment_client.h"
22#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
23#include "chrome/browser/chromeos/policy/device_cloud_policy_initializer.h"
24#include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h"
25#include "chromeos/dbus/cryptohome_client.h"
26#include "chromeos/dbus/dbus_method_call_status.h"
27#include "chromeos/dbus/dbus_thread_manager.h"
28#include "chromeos/dbus/session_manager_client.h"
29#include "components/pairing/controller_pairing_controller.h"
30#include "google_apis/gaia/gaia_auth_util.h"
31#include "google_apis/gaia/google_service_auth_error.h"
32#include "policy/proto/device_management_backend.pb.h"
33
34using namespace pairing_chromeos;
35
36// Do not change the UMA histogram parameters without renaming the histograms!
37#define UMA_ENROLLMENT_TIME(histogram_name, elapsed_timer) \
38  do {                                                     \
39    UMA_HISTOGRAM_CUSTOM_TIMES(                            \
40      (histogram_name),                                    \
41      (elapsed_timer)->Elapsed(),                          \
42      base::TimeDelta::FromMilliseconds(100) /* min */,    \
43      base::TimeDelta::FromMinutes(15) /* max */,          \
44      100 /* bucket_count */);                             \
45  } while (0)
46
47namespace chromeos {
48
49// static
50EnrollmentScreen* EnrollmentScreen::Get(ScreenManager* manager) {
51  return static_cast<EnrollmentScreen*>(
52      manager->GetScreen(WizardController::kEnrollmentScreenName));
53}
54
55EnrollmentScreen::EnrollmentScreen(
56    ScreenObserver* observer,
57    EnrollmentScreenActor* actor)
58    : WizardScreen(observer),
59      shark_controller_(NULL),
60      remora_controller_(NULL),
61      actor_(actor),
62      enrollment_mode_(EnrollmentScreenActor::ENROLLMENT_MODE_MANUAL),
63      enrollment_failed_once_(false),
64      remora_token_sent_(false),
65      lockbox_init_duration_(0),
66      weak_ptr_factory_(this) {
67  // Init the TPM if it has not been done until now (in debug build we might
68  // have not done that yet).
69  DBusThreadManager::Get()->GetCryptohomeClient()->TpmCanAttemptOwnership(
70      EmptyVoidDBusMethodCallback());
71}
72
73EnrollmentScreen::~EnrollmentScreen() {
74  if (remora_controller_)
75    remora_controller_->RemoveObserver(this);
76}
77
78void EnrollmentScreen::SetParameters(
79    EnrollmentScreenActor::EnrollmentMode enrollment_mode,
80    const std::string& management_domain,
81    const std::string& user,
82    const std::string& auth_token,
83    pairing_chromeos::ControllerPairingController* shark_controller,
84    pairing_chromeos::HostPairingController* remora_controller) {
85  enrollment_mode_ = enrollment_mode;
86  user_ = user.empty() ? user : gaia::CanonicalizeEmail(user);
87  auth_token_ = auth_token;
88  shark_controller_ = shark_controller;
89  if (remora_controller_)
90    remora_controller_->RemoveObserver(this);
91  remora_controller_ = remora_controller;
92  if (remora_controller_)
93    remora_controller_->AddObserver(this);
94  actor_->SetParameters(this, enrollment_mode_, management_domain);
95}
96
97void EnrollmentScreen::PrepareToShow() {
98  actor_->PrepareToShow();
99}
100
101void EnrollmentScreen::Show() {
102  if (is_auto_enrollment() && !enrollment_failed_once_) {
103    actor_->Show();
104    UMA(policy::kMetricEnrollmentAutoStarted);
105    actor_->ShowEnrollmentSpinnerScreen();
106    actor_->FetchOAuthToken();
107  } else if (auth_token_.empty()) {
108    UMA(policy::kMetricEnrollmentTriggered);
109    actor_->ResetAuth(base::Bind(&EnrollmentScreen::ShowSigninScreen,
110                                 weak_ptr_factory_.GetWeakPtr()));
111  } else {
112    actor_->Show();
113    actor_->ShowEnrollmentSpinnerScreen();
114    OnOAuthTokenAvailable(auth_token_);
115  }
116}
117
118void EnrollmentScreen::Hide() {
119  actor_->Hide();
120  weak_ptr_factory_.InvalidateWeakPtrs();
121}
122
123std::string EnrollmentScreen::GetName() const {
124  return WizardController::kEnrollmentScreenName;
125}
126
127void EnrollmentScreen::PairingStageChanged(Stage new_stage) {
128  DCHECK(remora_controller_);
129  if (new_stage == HostPairingController::STAGE_FINISHED) {
130    remora_controller_->RemoveObserver(this);
131    remora_controller_ = NULL;
132    // TODO(zork): Check that this is the best exit status. crbug.com/412798
133    get_screen_observer()->OnExit(
134        WizardController::ENTERPRISE_AUTO_MAGIC_ENROLLMENT_COMPLETED);
135  }
136}
137
138void EnrollmentScreen::ConfigureHost(bool accepted_eula,
139                                     const std::string& lang,
140                                     const std::string& timezone,
141                                     bool send_reports,
142                                     const std::string& keyboard_layout) {
143}
144
145void EnrollmentScreen::EnrollHost(const std::string& auth_token) {
146}
147
148void EnrollmentScreen::OnLoginDone(const std::string& user) {
149  elapsed_timer_.reset(new base::ElapsedTimer());
150  user_ = gaia::CanonicalizeEmail(user);
151
152  if (is_auto_enrollment())
153    UMA(policy::kMetricEnrollmentAutoRestarted);
154  else if (enrollment_failed_once_)
155    UMA(policy::kMetricEnrollmentRestarted);
156  else
157    UMA(policy::kMetricEnrollmentStarted);
158
159  actor_->ShowEnrollmentSpinnerScreen();
160  actor_->FetchOAuthToken();
161}
162
163void EnrollmentScreen::OnAuthError(const GoogleServiceAuthError& error) {
164  switch (error.state()) {
165    case GoogleServiceAuthError::NONE:
166    case GoogleServiceAuthError::CAPTCHA_REQUIRED:
167    case GoogleServiceAuthError::TWO_FACTOR:
168    case GoogleServiceAuthError::HOSTED_NOT_ALLOWED:
169    case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS:
170    case GoogleServiceAuthError::REQUEST_CANCELED:
171    case GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE:
172    case GoogleServiceAuthError::SERVICE_ERROR:
173      UMAFailure(policy::kMetricEnrollmentLoginFailed);
174      LOG(ERROR) << "Auth error " << error.state();
175      break;
176    case GoogleServiceAuthError::USER_NOT_SIGNED_UP:
177      UMAFailure(policy::kMetricEnrollmentAccountNotSignedUp);
178      LOG(ERROR) << "Account not signed up " << error.state();
179      break;
180    case GoogleServiceAuthError::ACCOUNT_DELETED:
181      UMAFailure(policy::kMetricEnrollmentAccountDeleted);
182      LOG(ERROR) << "Account deleted " << error.state();
183      break;
184    case GoogleServiceAuthError::ACCOUNT_DISABLED:
185      UMAFailure(policy::kMetricEnrollmentAccountDisabled);
186      LOG(ERROR) << "Account disabled " << error.state();
187      break;
188    case GoogleServiceAuthError::CONNECTION_FAILED:
189    case GoogleServiceAuthError::SERVICE_UNAVAILABLE:
190      UMAFailure(policy::kMetricEnrollmentNetworkFailed);
191      LOG(WARNING) << "Network error " << error.state();
192      break;
193    case GoogleServiceAuthError::NUM_STATES:
194      NOTREACHED();
195      break;
196  }
197
198  enrollment_failed_once_ = true;
199  actor_->ShowAuthError(error);
200}
201
202void EnrollmentScreen::OnOAuthTokenAvailable(const std::string& token) {
203  VLOG(1) << "OnOAuthTokenAvailable " << token;
204  const bool is_shark =
205      g_browser_process->platform_part()->browser_policy_connector_chromeos()->
206          GetDeviceCloudPolicyManager()->IsSharkRequisition();
207
208  if (is_shark && !remora_token_sent_) {
209    // Fetch a second token for shark devices.
210    remora_token_sent_ = true;
211    SendEnrollmentAuthToken(token);
212    actor_->FetchOAuthToken();
213  } else {
214    RegisterForDevicePolicy(token);
215  }
216}
217
218void EnrollmentScreen::OnRetry() {
219  actor_->ResetAuth(base::Bind(&EnrollmentScreen::ShowSigninScreen,
220                               weak_ptr_factory_.GetWeakPtr()));
221}
222
223void EnrollmentScreen::OnCancel() {
224  UMA(is_auto_enrollment() ? policy::kMetricEnrollmentAutoCancelled
225                           : policy::kMetricEnrollmentCancelled);
226  if (elapsed_timer_)
227    UMA_ENROLLMENT_TIME("Enterprise.EnrollmentTime.Cancel", elapsed_timer_);
228  if (enrollment_mode_ == EnrollmentScreenActor::ENROLLMENT_MODE_FORCED ||
229      enrollment_mode_ == EnrollmentScreenActor::ENROLLMENT_MODE_RECOVERY) {
230    actor_->ResetAuth(
231        base::Bind(&ScreenObserver::OnExit,
232                   base::Unretained(get_screen_observer()),
233                   ScreenObserver::ENTERPRISE_ENROLLMENT_BACK));
234    return;
235  }
236
237  if (is_auto_enrollment())
238    policy::AutoEnrollmentClient::CancelAutoEnrollment();
239  actor_->ResetAuth(
240      base::Bind(&ScreenObserver::OnExit,
241                 base::Unretained(get_screen_observer()),
242                 ScreenObserver::ENTERPRISE_ENROLLMENT_COMPLETED));
243}
244
245void EnrollmentScreen::OnConfirmationClosed() {
246  // If the machine has been put in KIOSK mode we have to restart the session
247  // here to go in the proper KIOSK mode login screen.
248  policy::BrowserPolicyConnectorChromeOS* connector =
249      g_browser_process->platform_part()->browser_policy_connector_chromeos();
250  if (connector->GetDeviceMode() == policy::DEVICE_MODE_RETAIL_KIOSK) {
251    DBusThreadManager::Get()->GetSessionManagerClient()->StopSession();
252    return;
253  }
254
255  if (is_auto_enrollment() &&
256      !enrollment_failed_once_ &&
257      !user_.empty() &&
258      LoginUtils::IsWhitelisted(user_, NULL)) {
259    actor_->ShowLoginSpinnerScreen();
260    get_screen_observer()->OnExit(
261        ScreenObserver::ENTERPRISE_AUTO_MAGIC_ENROLLMENT_COMPLETED);
262  } else {
263    actor_->ResetAuth(
264        base::Bind(&ScreenObserver::OnExit,
265                   base::Unretained(get_screen_observer()),
266                   ScreenObserver::ENTERPRISE_ENROLLMENT_COMPLETED));
267  }
268}
269
270void EnrollmentScreen::RegisterForDevicePolicy(const std::string& token) {
271  policy::BrowserPolicyConnectorChromeOS* connector =
272      g_browser_process->platform_part()->browser_policy_connector_chromeos();
273  if (connector->IsEnterpriseManaged() &&
274      connector->GetEnterpriseDomain() != gaia::ExtractDomainName(user_)) {
275    LOG(ERROR) << "Trying to re-enroll to a different domain than "
276               << connector->GetEnterpriseDomain();
277    UMAFailure(policy::kMetricEnrollmentPrecheckDomainMismatch);
278    actor_->ShowUIError(
279        EnrollmentScreenActor::UI_ERROR_DOMAIN_MISMATCH);
280    return;
281  }
282
283  policy::DeviceCloudPolicyInitializer::AllowedDeviceModes device_modes;
284  device_modes[policy::DEVICE_MODE_ENTERPRISE] = true;
285  device_modes[policy::DEVICE_MODE_RETAIL_KIOSK] =
286      enrollment_mode_ == EnrollmentScreenActor::ENROLLMENT_MODE_MANUAL;
287  connector->ScheduleServiceInitialization(0);
288
289  policy::DeviceCloudPolicyInitializer* dcp_initializer =
290      connector->GetDeviceCloudPolicyInitializer();
291  CHECK(dcp_initializer);
292  dcp_initializer->StartEnrollment(
293      enterprise_management::PolicyData::ENTERPRISE_MANAGED,
294      connector->device_management_service(),
295      token, is_auto_enrollment(), device_modes,
296      base::Bind(&EnrollmentScreen::ReportEnrollmentStatus,
297                 weak_ptr_factory_.GetWeakPtr()));
298}
299
300void EnrollmentScreen::SendEnrollmentAuthToken(const std::string& token) {
301  // TODO(achuith, zork): Extract and send domain.
302  if (shark_controller_)
303    shark_controller_->OnAuthenticationDone("", token);
304}
305
306void EnrollmentScreen::ShowEnrollmentStatusOnSuccess(
307    const policy::EnrollmentStatus& status) {
308  StartupUtils::MarkOobeCompleted();
309  if (elapsed_timer_)
310    UMA_ENROLLMENT_TIME("Enterprise.EnrollmentTime.Success", elapsed_timer_);
311  actor_->ShowEnrollmentStatus(status);
312}
313
314void EnrollmentScreen::ReportEnrollmentStatus(policy::EnrollmentStatus status) {
315  switch (status.status()) {
316    case policy::EnrollmentStatus::STATUS_SUCCESS:
317      StartupUtils::MarkDeviceRegistered(
318          base::Bind(&EnrollmentScreen::ShowEnrollmentStatusOnSuccess,
319                     weak_ptr_factory_.GetWeakPtr(),
320                     status));
321      UMA(is_auto_enrollment() ? policy::kMetricEnrollmentAutoOK
322                               : policy::kMetricEnrollmentOK);
323      if (remora_controller_)
324        remora_controller_->SetEnrollmentComplete(true);
325      return;
326    case policy::EnrollmentStatus::STATUS_REGISTRATION_FAILED:
327    case policy::EnrollmentStatus::STATUS_POLICY_FETCH_FAILED:
328      switch (status.client_status()) {
329        case policy::DM_STATUS_SUCCESS:
330          NOTREACHED();
331          break;
332        case policy::DM_STATUS_REQUEST_INVALID:
333          UMAFailure(policy::kMetricEnrollmentRegisterPolicyPayloadInvalid);
334          break;
335        case policy::DM_STATUS_SERVICE_DEVICE_NOT_FOUND:
336          UMAFailure(policy::kMetricEnrollmentRegisterPolicyDeviceNotFound);
337          break;
338        case policy::DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID:
339          UMAFailure(policy::kMetricEnrollmentRegisterPolicyDMTokenInvalid);
340          break;
341        case policy::DM_STATUS_SERVICE_ACTIVATION_PENDING:
342          UMAFailure(policy::kMetricEnrollmentRegisterPolicyActivationPending);
343          break;
344        case policy::DM_STATUS_SERVICE_DEVICE_ID_CONFLICT:
345          UMAFailure(policy::kMetricEnrollmentRegisterPolicyDeviceIdConflict);
346          break;
347        case policy::DM_STATUS_SERVICE_POLICY_NOT_FOUND:
348          UMAFailure(policy::kMetricEnrollmentRegisterPolicyNotFound);
349          break;
350        case policy::DM_STATUS_REQUEST_FAILED:
351          UMAFailure(policy::kMetricEnrollmentRegisterPolicyRequestFailed);
352          break;
353        case policy::DM_STATUS_TEMPORARY_UNAVAILABLE:
354          UMAFailure(policy::kMetricEnrollmentRegisterPolicyTempUnavailable);
355          break;
356        case policy::DM_STATUS_HTTP_STATUS_ERROR:
357          UMAFailure(policy::kMetricEnrollmentRegisterPolicyHttpError);
358          break;
359        case policy::DM_STATUS_RESPONSE_DECODING_ERROR:
360          UMAFailure(policy::kMetricEnrollmentRegisterPolicyResponseInvalid);
361          break;
362        case policy::DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED:
363          UMAFailure(policy::kMetricEnrollmentNotSupported);
364          break;
365        case policy::DM_STATUS_SERVICE_INVALID_SERIAL_NUMBER:
366          UMAFailure(policy::kMetricEnrollmentRegisterPolicyInvalidSerial);
367          break;
368        case policy::DM_STATUS_SERVICE_MISSING_LICENSES:
369          UMAFailure(policy::kMetricEnrollmentRegisterPolicyMissingLicenses);
370          break;
371        case policy::DM_STATUS_SERVICE_DEPROVISIONED:
372          UMAFailure(policy::kMetricEnrollmentRegisterPolicyDeprovisioned);
373          break;
374        case policy::DM_STATUS_SERVICE_DOMAIN_MISMATCH:
375          UMAFailure(policy::kMetricEnrollmentRegisterPolicyDomainMismatch);
376          break;
377      }
378      break;
379    case policy::EnrollmentStatus::STATUS_REGISTRATION_BAD_MODE:
380      UMAFailure(policy::kMetricEnrollmentInvalidEnrollmentMode);
381      break;
382    case policy::EnrollmentStatus::STATUS_LOCK_TIMEOUT:
383      UMAFailure(policy::kMetricEnrollmentLockboxTimeoutError);
384      break;
385    case policy::EnrollmentStatus::STATUS_LOCK_WRONG_USER:
386      UMAFailure(policy::kMetricEnrollmentLockDomainMismatch);
387      break;
388    case policy::EnrollmentStatus::STATUS_NO_STATE_KEYS:
389      UMAFailure(policy::kMetricEnrollmentNoStateKeys);
390      break;
391    case policy::EnrollmentStatus::STATUS_VALIDATION_FAILED:
392      UMAFailure(policy::kMetricEnrollmentPolicyValidationFailed);
393      break;
394    case policy::EnrollmentStatus::STATUS_STORE_ERROR:
395      UMAFailure(policy::kMetricEnrollmentCloudPolicyStoreError);
396      break;
397    case policy::EnrollmentStatus::STATUS_LOCK_ERROR:
398      UMAFailure(policy::kMetricEnrollmentLockBackendError);
399      break;
400    case policy::EnrollmentStatus::STATUS_ROBOT_AUTH_FETCH_FAILED:
401      UMAFailure(policy::kMetricEnrollmentRobotAuthCodeFetchFailed);
402      break;
403    case policy::EnrollmentStatus::STATUS_ROBOT_REFRESH_FETCH_FAILED:
404      UMAFailure(policy::kMetricEnrollmentRobotRefreshTokenFetchFailed);
405      break;
406    case policy::EnrollmentStatus::STATUS_ROBOT_REFRESH_STORE_FAILED:
407      UMAFailure(policy::kMetricEnrollmentRobotRefreshTokenStoreFailed);
408      break;
409    case policy::EnrollmentStatus::STATUS_STORE_TOKEN_AND_ID_FAILED:
410      // This error should not happen for enterprise enrollment, it only affects
411      // consumer enrollment.
412      UMAFailure(policy::kMetricEnrollmentStoreTokenAndIdFailed);
413      NOTREACHED();
414      break;
415  }
416
417  if (remora_controller_)
418    remora_controller_->SetEnrollmentComplete(false);
419  enrollment_failed_once_ = true;
420  if (elapsed_timer_)
421    UMA_ENROLLMENT_TIME("Enterprise.EnrollmentTime.Failure", elapsed_timer_);
422  actor_->ShowEnrollmentStatus(status);
423}
424
425void EnrollmentScreen::UMA(policy::MetricEnrollment sample) {
426  switch (enrollment_mode_) {
427    case EnrollmentScreenActor::ENROLLMENT_MODE_MANUAL:
428    case EnrollmentScreenActor::ENROLLMENT_MODE_AUTO:
429      UMA_HISTOGRAM_SPARSE_SLOWLY("Enterprise.Enrollment", sample);
430      break;
431    case EnrollmentScreenActor::ENROLLMENT_MODE_FORCED:
432      UMA_HISTOGRAM_SPARSE_SLOWLY("Enterprise.EnrollmentForced", sample);
433      break;
434    case EnrollmentScreenActor::ENROLLMENT_MODE_RECOVERY:
435      UMA_HISTOGRAM_SPARSE_SLOWLY("Enterprise.EnrollmentRecovery", sample);
436      break;
437    case EnrollmentScreenActor::ENROLLMENT_MODE_COUNT:
438      NOTREACHED();
439      break;
440  }
441}
442
443void EnrollmentScreen::UMAFailure(policy::MetricEnrollment sample) {
444  if (is_auto_enrollment())
445    sample = policy::kMetricEnrollmentAutoFailed;
446  UMA(sample);
447}
448
449void EnrollmentScreen::ShowSigninScreen() {
450  actor_->Show();
451  actor_->ShowSigninScreen();
452}
453
454}  // namespace chromeos
455