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/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 
     19 namespace em = enterprise_management;
     20 
     21 namespace policy {
     22 
     23 namespace {
     24 
     25 // Retry for InstallAttrs initialization every 500ms.
     26 const int kLockRetryIntervalMs = 500;
     27 // Maximum time to retry InstallAttrs initialization before we give up.
     28 const int kLockRetryTimeoutMs = 10 * 60 * 1000;  // 10 minutes.
     29 
     30 }  // namespace
     31 
     32 EnrollmentHandlerChromeOS::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 
     63 EnrollmentHandlerChromeOS::~EnrollmentHandlerChromeOS() {
     64   Stop();
     65   store_->RemoveObserver(this);
     66 }
     67 
     68 void EnrollmentHandlerChromeOS::StartEnrollment() {
     69   CHECK_EQ(STEP_PENDING, enrollment_step_);
     70   enrollment_step_ = STEP_LOADING_STORE;
     71   AttemptRegistration();
     72 }
     73 
     74 scoped_ptr<CloudPolicyClient> EnrollmentHandlerChromeOS::ReleaseClient() {
     75   Stop();
     76   return client_.Pass();
     77 }
     78 
     79 void 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 
    113 void 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 
    135 void 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 
    152 void 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 
    173 void EnrollmentHandlerChromeOS::OnStoreError(CloudPolicyStore* store) {
    174   DCHECK_EQ(store_, store);
    175   ReportResult(EnrollmentStatus::ForStoreError(store_->status(),
    176                                                store_->validation_status()));
    177 }
    178 
    179 void 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 
    189 void 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 
    204 void 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.
    227 void 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 
    238 void EnrollmentHandlerChromeOS::DoLockDeviceStep() {
    239   enrollment_step_ = STEP_LOCK_DEVICE,
    240   StartLockDevice(username_, device_mode_, device_id_);
    241 }
    242 
    243 // GaiaOAuthClient::Delegate
    244 void 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.
    252 void EnrollmentHandlerChromeOS::OnOAuthError() {
    253   CHECK_EQ(STEP_ROBOT_AUTH_REFRESH, enrollment_step_);
    254   DoLockDeviceStep();
    255 }
    256 
    257 // GaiaOAuthClient::Delegate network error when fetching refresh token.
    258 void 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 
    265 void 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 
    282 void 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 
    329 void 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 
    337 void 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