blob: d24d75cdde84f067e0d668feec52bdab1f7e0229 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
#include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h"
#include "chrome/browser/chromeos/policy/server_backed_state_keys_broker.h"
#include "chromeos/chromeos_switches.h"
#include "components/policy/core/common/cloud/device_management_service.h"
#include "net/url_request/url_request_context_getter.h"
namespace chromeos {
namespace {
// Returns the int value of the |switch_name| argument, clamped to the [0, 62]
// interval. Returns 0 if the argument doesn't exist or isn't an int value.
int GetSanitizedArg(const std::string& switch_name) {
CommandLine* command_line = CommandLine::ForCurrentProcess();
if (!command_line->HasSwitch(switch_name))
return 0;
std::string value = command_line->GetSwitchValueASCII(switch_name);
int int_value;
if (!base::StringToInt(value, &int_value)) {
LOG(ERROR) << "Switch \"" << switch_name << "\" is not a valid int. "
<< "Defaulting to 0.";
return 0;
}
if (int_value < 0) {
LOG(ERROR) << "Switch \"" << switch_name << "\" can't be negative. "
<< "Using 0";
return 0;
}
if (int_value > policy::AutoEnrollmentClient::kMaximumPower) {
LOG(ERROR) << "Switch \"" << switch_name << "\" can't be greater than "
<< policy::AutoEnrollmentClient::kMaximumPower << ". Using "
<< policy::AutoEnrollmentClient::kMaximumPower;
return policy::AutoEnrollmentClient::kMaximumPower;
}
return int_value;
}
} // namespace
const char AutoEnrollmentController::kForcedReEnrollmentAlways[] = "always";
const char AutoEnrollmentController::kForcedReEnrollmentLegacy[] = "legacy";
const char AutoEnrollmentController::kForcedReEnrollmentNever[] = "never";
const char AutoEnrollmentController::kForcedReEnrollmentOfficialBuild[] =
"official";
AutoEnrollmentController::Mode AutoEnrollmentController::GetMode() {
CommandLine* command_line = CommandLine::ForCurrentProcess();
if (!command_line->HasSwitch(switches::kEnterpriseEnableForcedReEnrollment))
return MODE_LEGACY_AUTO_ENROLLMENT;
std::string command_line_mode = command_line->GetSwitchValueASCII(
switches::kEnterpriseEnableForcedReEnrollment);
if (command_line_mode == kForcedReEnrollmentAlways) {
return MODE_FORCED_RE_ENROLLMENT;
} else if (command_line_mode.empty() ||
command_line_mode == kForcedReEnrollmentOfficialBuild) {
#if defined(OFFICIAL_BUILD)
return MODE_FORCED_RE_ENROLLMENT;
#else
return MODE_NONE;
#endif
} else if (command_line_mode == kForcedReEnrollmentLegacy) {
return MODE_LEGACY_AUTO_ENROLLMENT;
}
return MODE_NONE;
}
AutoEnrollmentController::AutoEnrollmentController()
: state_(policy::AUTO_ENROLLMENT_STATE_IDLE),
client_start_weak_factory_(this) {}
AutoEnrollmentController::~AutoEnrollmentController() {}
void AutoEnrollmentController::Start() {
// This method is called at the point in the OOBE/login flow at which the
// auto-enrollment check can start. This happens either after the EULA is
// accepted, or right after a reboot if the EULA has already been accepted.
// Do not communicate auto-enrollment data to the server if
// 1. we are running telemetry tests.
// 2. modulus configuration is not present.
// 3. Auto-enrollment is disabled via the command line.
CommandLine* command_line = CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(chromeos::switches::kDisableGaiaServices) ||
(!command_line->HasSwitch(
chromeos::switches::kEnterpriseEnrollmentInitialModulus) &&
!command_line->HasSwitch(
chromeos::switches::kEnterpriseEnrollmentModulusLimit)) ||
GetMode() == MODE_NONE) {
VLOG(1) << "Auto-enrollment disabled.";
UpdateState(policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT);
return;
}
// If a client is being created or already existing, bail out.
if (client_start_weak_factory_.HasWeakPtrs() || client_)
return;
// Start by checking if the device has already been owned.
UpdateState(policy::AUTO_ENROLLMENT_STATE_PENDING);
DeviceSettingsService::Get()->GetOwnershipStatusAsync(
base::Bind(&AutoEnrollmentController::OnOwnershipStatusCheckDone,
client_start_weak_factory_.GetWeakPtr()));
}
void AutoEnrollmentController::Cancel() {
if (client_) {
// Cancelling the |client_| allows it to determine whether
// its protocol finished before login was complete.
client_.release()->CancelAndDeleteSoon();
}
// Make sure to nuke pending |client_| start sequences.
client_start_weak_factory_.InvalidateWeakPtrs();
}
void AutoEnrollmentController::Retry() {
if (client_)
client_->Retry();
}
scoped_ptr<AutoEnrollmentController::ProgressCallbackList::Subscription>
AutoEnrollmentController::RegisterProgressCallback(
const ProgressCallbackList::CallbackType& callback) {
return progress_callbacks_.Add(callback);
}
bool AutoEnrollmentController::ShouldEnrollSilently() {
return state_ == policy::AUTO_ENROLLMENT_STATE_TRIGGER_ENROLLMENT &&
GetMode() == MODE_LEGACY_AUTO_ENROLLMENT;
}
void AutoEnrollmentController::OnOwnershipStatusCheckDone(
DeviceSettingsService::OwnershipStatus status) {
if (status != DeviceSettingsService::OWNERSHIP_NONE) {
// The device is already owned. No need for auto-enrollment checks.
VLOG(1) << "Device already owned, skipping auto-enrollment check";
UpdateState(policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT);
return;
}
// Make sure state keys are available.
g_browser_process->platform_part()
->browser_policy_connector_chromeos()
->GetStateKeysBroker()
->RequestStateKeys(base::Bind(&AutoEnrollmentController::StartClient,
client_start_weak_factory_.GetWeakPtr()));
}
void AutoEnrollmentController::StartClient(
const std::vector<std::string>& state_keys, bool first_boot) {
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
policy::DeviceManagementService* service =
connector->device_management_service();
service->ScheduleInitialization(0);
int power_initial = GetSanitizedArg(
chromeos::switches::kEnterpriseEnrollmentInitialModulus);
int power_limit = GetSanitizedArg(
chromeos::switches::kEnterpriseEnrollmentModulusLimit);
if (power_initial > power_limit) {
LOG(ERROR) << "Initial auto-enrollment modulus is larger than the limit, "
<< "clamping to the limit.";
power_initial = power_limit;
}
bool retrieve_device_state = false;
std::string device_id;
if (GetMode() == MODE_FORCED_RE_ENROLLMENT) {
retrieve_device_state = true;
if (!state_keys.empty() && !first_boot)
device_id = state_keys.front();
} else {
device_id = policy::DeviceCloudPolicyManagerChromeOS::GetMachineID();
}
client_.reset(new policy::AutoEnrollmentClient(
base::Bind(&AutoEnrollmentController::UpdateState,
base::Unretained(this)),
service,
g_browser_process->local_state(),
g_browser_process->system_request_context(),
device_id,
retrieve_device_state,
power_initial,
power_limit));
VLOG(1) << "Starting auto-enrollment client.";
client_->Start();
}
void AutoEnrollmentController::UpdateState(
policy::AutoEnrollmentState new_state) {
VLOG(1) << "New state: " << new_state << ".";
state_ = new_state;
progress_callbacks_.Notify(state_);
}
} // namespace chromeos