Home | History | Annotate | Download | only in policy
      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/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/login/enrollment/auto_enrollment_controller.h"
     13 #include "chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h"
     14 #include "chrome/browser/chromeos/policy/proto/chrome_device_policy.pb.h"
     15 #include "chrome/browser/chromeos/policy/server_backed_state_keys_broker.h"
     16 #include "chrome/browser/chromeos/settings/device_oauth2_token_service.h"
     17 #include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h"
     18 #include "chrome/browser/chromeos/settings/device_settings_service.h"
     19 #include "chromeos/chromeos_switches.h"
     20 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
     21 #include "google_apis/gaia/gaia_urls.h"
     22 #include "net/http/http_status_code.h"
     23 
     24 namespace em = enterprise_management;
     25 
     26 namespace policy {
     27 
     28 namespace {
     29 
     30 // Retry for InstallAttrs initialization every 500ms.
     31 const int kLockRetryIntervalMs = 500;
     32 // Maximum time to retry InstallAttrs initialization before we give up.
     33 const int kLockRetryTimeoutMs = 10 * 60 * 1000;  // 10 minutes.
     34 
     35 // Testing token used when the enrollment-skip-robot-auth is set to skip talking
     36 // to GAIA for an actual token. This is needed to be able to run against the
     37 // testing DMServer implementations.
     38 const char kTestingRobotToken[] = "test-token";
     39 
     40 }  // namespace
     41 
     42 EnrollmentHandlerChromeOS::EnrollmentHandlerChromeOS(
     43     DeviceCloudPolicyStoreChromeOS* store,
     44     EnterpriseInstallAttributes* install_attributes,
     45     ServerBackedStateKeysBroker* state_keys_broker,
     46     chromeos::DeviceSettingsService* device_settings_service,
     47     scoped_ptr<CloudPolicyClient> client,
     48     scoped_refptr<base::SequencedTaskRunner> background_task_runner,
     49     const std::string& auth_token,
     50     const std::string& client_id,
     51     bool is_auto_enrollment,
     52     const std::string& requisition,
     53     const AllowedDeviceModes& allowed_device_modes,
     54     em::PolicyData::ManagementMode management_mode,
     55     const EnrollmentCallback& completion_callback)
     56     : store_(store),
     57       install_attributes_(install_attributes),
     58       state_keys_broker_(state_keys_broker),
     59       device_settings_service_(device_settings_service),
     60       client_(client.Pass()),
     61       background_task_runner_(background_task_runner),
     62       auth_token_(auth_token),
     63       client_id_(client_id),
     64       is_auto_enrollment_(is_auto_enrollment),
     65       requisition_(requisition),
     66       allowed_device_modes_(allowed_device_modes),
     67       management_mode_(management_mode),
     68       completion_callback_(completion_callback),
     69       device_mode_(DEVICE_MODE_NOT_SET),
     70       enrollment_step_(STEP_PENDING),
     71       lockbox_init_duration_(0),
     72       weak_ptr_factory_(this) {
     73   CHECK(!client_->is_registered());
     74   CHECK_EQ(DM_STATUS_SUCCESS, client_->status());
     75   CHECK(management_mode_ == em::PolicyData::ENTERPRISE_MANAGED ||
     76         management_mode_ == em::PolicyData::CONSUMER_MANAGED);
     77   store_->AddObserver(this);
     78   client_->AddObserver(this);
     79   client_->AddNamespaceToFetch(PolicyNamespaceKey(
     80       dm_protocol::kChromeDevicePolicyType, std::string()));
     81 }
     82 
     83 EnrollmentHandlerChromeOS::~EnrollmentHandlerChromeOS() {
     84   Stop();
     85   store_->RemoveObserver(this);
     86 }
     87 
     88 void EnrollmentHandlerChromeOS::StartEnrollment() {
     89   CHECK_EQ(STEP_PENDING, enrollment_step_);
     90   enrollment_step_ = STEP_STATE_KEYS;
     91   state_keys_broker_->RequestStateKeys(
     92       base::Bind(&EnrollmentHandlerChromeOS::HandleStateKeysResult,
     93                  weak_ptr_factory_.GetWeakPtr()));
     94 }
     95 
     96 scoped_ptr<CloudPolicyClient> EnrollmentHandlerChromeOS::ReleaseClient() {
     97   Stop();
     98   return client_.Pass();
     99 }
    100 
    101 void EnrollmentHandlerChromeOS::OnPolicyFetched(CloudPolicyClient* client) {
    102   DCHECK_EQ(client_.get(), client);
    103   CHECK_EQ(STEP_POLICY_FETCH, enrollment_step_);
    104 
    105   enrollment_step_ = STEP_VALIDATION;
    106 
    107   // Validate the policy.
    108   const em::PolicyFetchResponse* policy = client_->GetPolicyFor(
    109       PolicyNamespaceKey(dm_protocol::kChromeDevicePolicyType, std::string()));
    110   if (!policy) {
    111     ReportResult(EnrollmentStatus::ForFetchError(
    112         DM_STATUS_RESPONSE_DECODING_ERROR));
    113     return;
    114   }
    115 
    116   scoped_ptr<DeviceCloudPolicyValidator> validator(
    117       DeviceCloudPolicyValidator::Create(
    118           scoped_ptr<em::PolicyFetchResponse>(
    119               new em::PolicyFetchResponse(*policy)),
    120           background_task_runner_));
    121 
    122   validator->ValidateTimestamp(base::Time(), base::Time::NowFromSystemTime(),
    123                                CloudPolicyValidatorBase::TIMESTAMP_REQUIRED);
    124 
    125   // If this is re-enrollment, make sure that the new policy matches the
    126   // previously-enrolled domain.
    127   std::string domain;
    128   if (install_attributes_->IsEnterpriseDevice()) {
    129     domain = install_attributes_->GetDomain();
    130     validator->ValidateDomain(domain);
    131   }
    132   validator->ValidateDMToken(client->dm_token(),
    133                              CloudPolicyValidatorBase::DM_TOKEN_REQUIRED);
    134   validator->ValidatePolicyType(dm_protocol::kChromeDevicePolicyType);
    135   validator->ValidatePayload();
    136   // If |domain| is empty here, the policy validation code will just use the
    137   // domain from the username field in the policy itself to do key validation.
    138   // TODO(mnissler): Plumb the enrolling user's username into this object so
    139   // we can validate the username on the resulting policy, and use the domain
    140   // from that username to validate the key below (http://crbug.com/343074).
    141   validator->ValidateInitialKey(GetPolicyVerificationKey(), domain);
    142   validator.release()->StartValidation(
    143       base::Bind(&EnrollmentHandlerChromeOS::HandlePolicyValidationResult,
    144                  weak_ptr_factory_.GetWeakPtr()));
    145 }
    146 
    147 void EnrollmentHandlerChromeOS::OnRegistrationStateChanged(
    148     CloudPolicyClient* client) {
    149   DCHECK_EQ(client_.get(), client);
    150 
    151   if (enrollment_step_ == STEP_REGISTRATION && client_->is_registered()) {
    152     enrollment_step_ = STEP_POLICY_FETCH,
    153     device_mode_ = client_->device_mode();
    154     if (device_mode_ == DEVICE_MODE_NOT_SET)
    155       device_mode_ = DEVICE_MODE_ENTERPRISE;
    156     if (!allowed_device_modes_.test(device_mode_)) {
    157       LOG(ERROR) << "Bad device mode " << device_mode_;
    158       ReportResult(EnrollmentStatus::ForStatus(
    159           EnrollmentStatus::STATUS_REGISTRATION_BAD_MODE));
    160       return;
    161     }
    162     client_->FetchPolicy();
    163   } else {
    164     LOG(FATAL) << "Registration state changed to " << client_->is_registered()
    165                << " in step " << enrollment_step_ << ".";
    166   }
    167 }
    168 
    169 void EnrollmentHandlerChromeOS::OnClientError(CloudPolicyClient* client) {
    170   DCHECK_EQ(client_.get(), client);
    171 
    172   if (enrollment_step_ == STEP_ROBOT_AUTH_FETCH) {
    173     LOG(ERROR) << "API authentication code fetch failed: "
    174                << client_->status();
    175     ReportResult(EnrollmentStatus::ForRobotAuthFetchError(client_->status()));
    176   } else if (enrollment_step_ < STEP_POLICY_FETCH) {
    177     ReportResult(EnrollmentStatus::ForRegistrationError(client_->status()));
    178   } else {
    179     ReportResult(EnrollmentStatus::ForFetchError(client_->status()));
    180   }
    181 }
    182 
    183 void EnrollmentHandlerChromeOS::OnStoreLoaded(CloudPolicyStore* store) {
    184   DCHECK_EQ(store_, store);
    185 
    186   if (enrollment_step_ == STEP_LOADING_STORE) {
    187     // If the |store_| wasn't initialized when StartEnrollment() was called,
    188     // then StartRegistration() bails silently. This gets registration rolling
    189     // again after the store finishes loading.
    190     StartRegistration();
    191   } else if (enrollment_step_ == STEP_STORE_POLICY) {
    192     ReportResult(EnrollmentStatus::ForStatus(EnrollmentStatus::STATUS_SUCCESS));
    193   }
    194 }
    195 
    196 void EnrollmentHandlerChromeOS::OnStoreError(CloudPolicyStore* store) {
    197   DCHECK_EQ(store_, store);
    198   if (enrollment_step_ == STEP_STORE_TOKEN_AND_ID) {
    199     // Calling DeviceSettingsService::SetManagementSettings() on a non-
    200     // enterprise-managed device will trigger OnStoreError(), as
    201     // DeviceCloudPolicyStore listens to all changes on DeviceSettingsService,
    202     // and it calls OnStoreError() when the device is not enterprise-managed.
    203     return;
    204   }
    205   ReportResult(EnrollmentStatus::ForStoreError(store_->status(),
    206                                                store_->validation_status()));
    207 }
    208 
    209 void EnrollmentHandlerChromeOS::HandleStateKeysResult(
    210     const std::vector<std::string>& state_keys, bool /* first_boot */) {
    211   CHECK_EQ(STEP_STATE_KEYS, enrollment_step_);
    212 
    213   // Make sure state keys are available if forced re-enrollment is on.
    214   if (chromeos::AutoEnrollmentController::GetMode() ==
    215       chromeos::AutoEnrollmentController::MODE_FORCED_RE_ENROLLMENT) {
    216     client_->SetStateKeysToUpload(state_keys);
    217     current_state_key_ = state_keys_broker_->current_state_key();
    218     if (state_keys.empty() || current_state_key_.empty()) {
    219       ReportResult(
    220           EnrollmentStatus::ForStatus(EnrollmentStatus::STATUS_NO_STATE_KEYS));
    221       return;
    222     }
    223   }
    224 
    225   enrollment_step_ = STEP_LOADING_STORE;
    226   StartRegistration();
    227 }
    228 
    229 void EnrollmentHandlerChromeOS::StartRegistration() {
    230   CHECK_EQ(STEP_LOADING_STORE, enrollment_step_);
    231   if (store_->is_initialized()) {
    232     enrollment_step_ = STEP_REGISTRATION;
    233     client_->Register(em::DeviceRegisterRequest::DEVICE,
    234                       auth_token_, client_id_, is_auto_enrollment_,
    235                       requisition_, current_state_key_);
    236   } else {
    237     // Do nothing. StartRegistration() will be called again from OnStoreLoaded()
    238     // after the CloudPolicyStore has initialized.
    239   }
    240 }
    241 
    242 void EnrollmentHandlerChromeOS::HandlePolicyValidationResult(
    243     DeviceCloudPolicyValidator* validator) {
    244   CHECK_EQ(STEP_VALIDATION, enrollment_step_);
    245   if (validator->success()) {
    246     policy_ = validator->policy().Pass();
    247     username_ = validator->policy_data()->username();
    248     device_id_ = validator->policy_data()->device_id();
    249     request_token_ = validator->policy_data()->request_token();
    250 
    251     if (CommandLine::ForCurrentProcess()->HasSwitch(
    252             chromeos::switches::kEnterpriseEnrollmentSkipRobotAuth)) {
    253       // For test purposes we allow enrollment to succeed without proper robot
    254       // account and use the provided value as a token.
    255       refresh_token_ = kTestingRobotToken;
    256       enrollment_step_ = STEP_LOCK_DEVICE;
    257       StartLockDevice();
    258       return;
    259     }
    260 
    261     enrollment_step_ = STEP_ROBOT_AUTH_FETCH;
    262     client_->FetchRobotAuthCodes(auth_token_);
    263   } else {
    264     ReportResult(EnrollmentStatus::ForValidationError(validator->status()));
    265   }
    266 }
    267 
    268 void EnrollmentHandlerChromeOS::OnRobotAuthCodesFetched(
    269     CloudPolicyClient* client) {
    270   DCHECK_EQ(client_.get(), client);
    271   CHECK_EQ(STEP_ROBOT_AUTH_FETCH, enrollment_step_);
    272 
    273   enrollment_step_ = STEP_ROBOT_AUTH_REFRESH;
    274 
    275   gaia::OAuthClientInfo client_info;
    276   client_info.client_id = GaiaUrls::GetInstance()->oauth2_chrome_client_id();
    277   client_info.client_secret =
    278       GaiaUrls::GetInstance()->oauth2_chrome_client_secret();
    279   client_info.redirect_uri = "oob";
    280 
    281   // Use the system request context to avoid sending user cookies.
    282   gaia_oauth_client_.reset(new gaia::GaiaOAuthClient(
    283       g_browser_process->system_request_context()));
    284   gaia_oauth_client_->GetTokensFromAuthCode(client_info,
    285                                             client->robot_api_auth_code(),
    286                                             0 /* max_retries */,
    287                                             this);
    288 }
    289 
    290 // GaiaOAuthClient::Delegate callback for OAuth2 refresh token fetched.
    291 void EnrollmentHandlerChromeOS::OnGetTokensResponse(
    292     const std::string& refresh_token,
    293     const std::string& access_token,
    294     int expires_in_seconds) {
    295   CHECK_EQ(STEP_ROBOT_AUTH_REFRESH, enrollment_step_);
    296 
    297   refresh_token_ = refresh_token;
    298 
    299   enrollment_step_ = STEP_LOCK_DEVICE;
    300   StartLockDevice();
    301 }
    302 
    303 // GaiaOAuthClient::Delegate
    304 void EnrollmentHandlerChromeOS::OnRefreshTokenResponse(
    305     const std::string& access_token,
    306     int expires_in_seconds) {
    307   // We never use the code that should trigger this callback.
    308   LOG(FATAL) << "Unexpected callback invoked.";
    309 }
    310 
    311 // GaiaOAuthClient::Delegate OAuth2 error when fetching refresh token request.
    312 void EnrollmentHandlerChromeOS::OnOAuthError() {
    313   CHECK_EQ(STEP_ROBOT_AUTH_REFRESH, enrollment_step_);
    314   // OnOAuthError is only called if the request is bad (malformed) or the
    315   // response is bad (empty access token returned).
    316   LOG(ERROR) << "OAuth protocol error while fetching API refresh token.";
    317   ReportResult(
    318       EnrollmentStatus::ForRobotRefreshFetchError(net::HTTP_BAD_REQUEST));
    319 }
    320 
    321 // GaiaOAuthClient::Delegate network error when fetching refresh token.
    322 void EnrollmentHandlerChromeOS::OnNetworkError(int response_code) {
    323   CHECK_EQ(STEP_ROBOT_AUTH_REFRESH, enrollment_step_);
    324   LOG(ERROR) << "Network error while fetching API refresh token: "
    325              << response_code;
    326   ReportResult(
    327       EnrollmentStatus::ForRobotRefreshFetchError(response_code));
    328 }
    329 
    330 void EnrollmentHandlerChromeOS::StartLockDevice() {
    331   CHECK_EQ(STEP_LOCK_DEVICE, enrollment_step_);
    332   // Since this method is also called directly.
    333   weak_ptr_factory_.InvalidateWeakPtrs();
    334 
    335   if (management_mode_ == em::PolicyData::CONSUMER_MANAGED) {
    336     // Consumer device enrollment doesn't use install attributes. Instead,
    337     // we put the information in the owners settings.
    338     enrollment_step_ = STEP_STORE_TOKEN_AND_ID;
    339     device_settings_service_->SetManagementSettings(
    340         management_mode_, request_token_, device_id_,
    341         base::Bind(&EnrollmentHandlerChromeOS::HandleSetManagementSettingsDone,
    342                    weak_ptr_factory_.GetWeakPtr()));
    343   } else {
    344     install_attributes_->LockDevice(
    345         username_, device_mode_, device_id_,
    346         base::Bind(&EnrollmentHandlerChromeOS::HandleLockDeviceResult,
    347                    weak_ptr_factory_.GetWeakPtr()));
    348   }
    349 }
    350 
    351 void EnrollmentHandlerChromeOS::HandleSetManagementSettingsDone() {
    352   CHECK_EQ(STEP_STORE_TOKEN_AND_ID, enrollment_step_);
    353   if (device_settings_service_->status() !=
    354       chromeos::DeviceSettingsService::STORE_SUCCESS) {
    355     ReportResult(EnrollmentStatus::ForStatus(
    356         EnrollmentStatus::STATUS_STORE_TOKEN_AND_ID_FAILED));
    357     return;
    358   }
    359 
    360   StartStoreRobotAuth();
    361 }
    362 
    363 void EnrollmentHandlerChromeOS::HandleLockDeviceResult(
    364     EnterpriseInstallAttributes::LockResult lock_result) {
    365   CHECK_EQ(STEP_LOCK_DEVICE, enrollment_step_);
    366   switch (lock_result) {
    367     case EnterpriseInstallAttributes::LOCK_SUCCESS:
    368       StartStoreRobotAuth();
    369       break;
    370     case EnterpriseInstallAttributes::LOCK_NOT_READY:
    371       // We wait up to |kLockRetryTimeoutMs| milliseconds and if it hasn't
    372       // succeeded by then show an error to the user and stop the enrollment.
    373       if (lockbox_init_duration_ < kLockRetryTimeoutMs) {
    374         // InstallAttributes not ready yet, retry later.
    375         LOG(WARNING) << "Install Attributes not ready yet will retry in "
    376                      << kLockRetryIntervalMs << "ms.";
    377         base::MessageLoop::current()->PostDelayedTask(
    378             FROM_HERE,
    379             base::Bind(&EnrollmentHandlerChromeOS::StartLockDevice,
    380                        weak_ptr_factory_.GetWeakPtr()),
    381             base::TimeDelta::FromMilliseconds(kLockRetryIntervalMs));
    382         lockbox_init_duration_ += kLockRetryIntervalMs;
    383       } else {
    384         ReportResult(EnrollmentStatus::ForStatus(
    385             EnrollmentStatus::STATUS_LOCK_TIMEOUT));
    386       }
    387       break;
    388     case EnterpriseInstallAttributes::LOCK_BACKEND_ERROR:
    389       ReportResult(EnrollmentStatus::ForStatus(
    390           EnrollmentStatus::STATUS_LOCK_ERROR));
    391       break;
    392     case EnterpriseInstallAttributes::LOCK_WRONG_USER:
    393       LOG(ERROR) << "Enrollment cannot proceed because the InstallAttrs "
    394                  << "has been locked already!";
    395       ReportResult(EnrollmentStatus::ForStatus(
    396           EnrollmentStatus::STATUS_LOCK_WRONG_USER));
    397       break;
    398   }
    399 }
    400 
    401 void EnrollmentHandlerChromeOS::StartStoreRobotAuth() {
    402   // Get the token service so we can store our robot refresh token.
    403   enrollment_step_ = STEP_STORE_ROBOT_AUTH;
    404   chromeos::DeviceOAuth2TokenServiceFactory::Get()->SetAndSaveRefreshToken(
    405       refresh_token_,
    406       base::Bind(&EnrollmentHandlerChromeOS::HandleStoreRobotAuthTokenResult,
    407                  weak_ptr_factory_.GetWeakPtr()));
    408 }
    409 
    410 void EnrollmentHandlerChromeOS::HandleStoreRobotAuthTokenResult(bool result) {
    411   CHECK_EQ(STEP_STORE_ROBOT_AUTH, enrollment_step_);
    412 
    413   if (!result) {
    414     LOG(ERROR) << "Failed to store API refresh token.";
    415     ReportResult(EnrollmentStatus::ForStatus(
    416         EnrollmentStatus::STATUS_ROBOT_REFRESH_STORE_FAILED));
    417     return;
    418   }
    419 
    420   if (management_mode_ == em::PolicyData::CONSUMER_MANAGED) {
    421     // For consumer management enrollment, we don't store the policy.
    422     ReportResult(EnrollmentStatus::ForStatus(EnrollmentStatus::STATUS_SUCCESS));
    423     return;
    424   }
    425 
    426   enrollment_step_ = STEP_STORE_POLICY;
    427   store_->InstallInitialPolicy(*policy_);
    428 }
    429 
    430 void EnrollmentHandlerChromeOS::Stop() {
    431   if (client_.get())
    432     client_->RemoveObserver(this);
    433   enrollment_step_ = STEP_FINISHED;
    434   weak_ptr_factory_.InvalidateWeakPtrs();
    435   completion_callback_.Reset();
    436 }
    437 
    438 void EnrollmentHandlerChromeOS::ReportResult(EnrollmentStatus status) {
    439   EnrollmentCallback callback = completion_callback_;
    440   Stop();
    441 
    442   if (status.status() != EnrollmentStatus::STATUS_SUCCESS) {
    443     LOG(WARNING) << "Enrollment failed: " << status.status()
    444                  << ", client: " << status.client_status()
    445                  << ", validation: " << status.validation_status()
    446                  << ", store: " << status.store_status();
    447   }
    448 
    449   if (!callback.is_null())
    450     callback.Run(status);
    451 }
    452 
    453 }  // namespace policy
    454