Commit a12b78ab authored by Pavol Marko's avatar Pavol Marko Committed by Commit Bot

Perform initial enrollment check for Chrome OS

If FRE is not performed, and initial enrollment is enabled according to
command line flags and device state, performs initial enrollment check.
Specifically, the initial enrollment check is done if all of the
following conditions are true:
- the device was not enterprise enrolled or in consumer mode before
  (according to the check_enrollment VPD variable)
- the RLZ embargo date has passed
  (to ensure not to do initial enrollment exchanges on the factory floor)
- Initial enrollment is enabled usinga  command-line switch
- serial number and RLZ brand code are available.

Bug: 839353
Change-Id: I259c06a5e5274f3bdef54ccc0161e6f642f9499f
Reviewed-on: https://chromium-review.googlesource.com/1052707Reviewed-by: default avatarSteven Bennetts <stevenjb@chromium.org>
Reviewed-by: default avatarRoger Tawa <rogerta@chromium.org>
Reviewed-by: default avatarMaksim Ivanov <emaxx@chromium.org>
Commit-Queue: Pavol Marko <pmarko@chromium.org>
Cr-Commit-Position: refs/heads/master@{#558797}
parent acb53bd0
......@@ -76,9 +76,7 @@ void AutoEnrollmentCheckScreen::ClearState() {
void AutoEnrollmentCheckScreen::Show() {
// If the decision got made already, don't show the screen at all.
if (AutoEnrollmentController::GetMode() !=
AutoEnrollmentController::MODE_FORCED_RE_ENROLLMENT ||
IsCompleted()) {
if (!AutoEnrollmentController::IsEnabled() || IsCompleted()) {
SignalCompletion();
return;
}
......@@ -210,7 +208,7 @@ bool AutoEnrollmentCheckScreen::UpdateAutoEnrollmentState(
return false;
case policy::AUTO_ENROLLMENT_STATE_SERVER_ERROR:
if (auto_enrollment_controller_->GetFRERequirement() !=
AutoEnrollmentController::EXPLICITLY_REQUIRED) {
AutoEnrollmentController::FRERequirement::kExplicitlyRequired) {
return false;
}
// Fall to the same behavior like any connection error if the device is
......@@ -265,7 +263,7 @@ bool AutoEnrollmentCheckScreen::IsCompleted() const {
case policy::AUTO_ENROLLMENT_STATE_SERVER_ERROR:
// Server errors should block OOBE for enrolled devices.
return auto_enrollment_controller_->GetFRERequirement() !=
AutoEnrollmentController::EXPLICITLY_REQUIRED;
AutoEnrollmentController::FRERequirement::kExplicitlyRequired;
case policy::AUTO_ENROLLMENT_STATE_TRIGGER_ENROLLMENT:
case policy::AUTO_ENROLLMENT_STATE_TRIGGER_ZERO_TOUCH:
case policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT:
......
......@@ -19,11 +19,16 @@
#include "chromeos/system/statistics_provider.h"
#include "components/policy/core/common/cloud/device_management_service.h"
#include "net/url_request/url_request_context_getter.h"
#include "rlz/chromeos/lib/rlz_value_store_chromeos.h"
namespace chromeos {
namespace {
// Maximum number of bits of the identifer hash to send during initial
// enrollment check.
const int kInitialEnrollmentModulusPowerLimit = 6;
// Maximum time to wait before forcing a decision. Note that download time for
// state key buckets can be non-negligible, especially on 2G connections.
const int kSafeguardTimeoutSeconds = 90;
......@@ -57,14 +62,15 @@ int GetSanitizedArg(const std::string& switch_name) {
std::string FRERequirementToString(
AutoEnrollmentController::FRERequirement requirement) {
using FRERequirement = AutoEnrollmentController::FRERequirement;
switch (requirement) {
case AutoEnrollmentController::REQUIRED:
case FRERequirement::kRequired:
return "Auto-enrollment required.";
case AutoEnrollmentController::NOT_REQUIRED:
case FRERequirement::kNotRequired:
return "Auto-enrollment disabled: first setup.";
case AutoEnrollmentController::EXPLICITLY_REQUIRED:
case FRERequirement::kExplicitlyRequired:
return "Auto-enrollment required: flag in VPD.";
case AutoEnrollmentController::EXPLICITLY_NOT_REQUIRED:
case FRERequirement::kExplicitlyNotRequired:
return "Auto-enrollment disabled: flag in VPD.";
}
......@@ -72,6 +78,31 @@ std::string FRERequirementToString(
return std::string();
}
// Returns true if this is an official build and the device has chrome firmware.
bool IsOfficialChrome() {
#if defined(OFFICIAL_BUILD)
std::string firmware_type;
const bool non_chrome_firmware =
system::StatisticsProvider::GetInstance()->GetMachineStatistic(
system::kFirmwareTypeKey, &firmware_type) &&
firmware_type == system::kFirmwareTypeValueNonchrome;
return !non_chrome_firmware;
#else
return false;
#endif
}
// Schedules immediate initialization of the |DeviceManagementService| and
// returns it.
policy::DeviceManagementService* InitializeAndGetDeviceManagementService() {
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
policy::DeviceManagementService* service =
connector->device_management_service();
service->ScheduleInitialization(0);
return service;
}
} // namespace
const char AutoEnrollmentController::kForcedReEnrollmentAlways[] = "always";
......@@ -79,32 +110,60 @@ const char AutoEnrollmentController::kForcedReEnrollmentNever[] = "never";
const char AutoEnrollmentController::kForcedReEnrollmentOfficialBuild[] =
"official";
const char AutoEnrollmentController::kInitialEnrollmentAlways[] = "always";
const char AutoEnrollmentController::kInitialEnrollmentNever[] = "never";
const char AutoEnrollmentController::kInitialEnrollmentOfficialBuild[] =
"official";
// static
AutoEnrollmentController::Mode AutoEnrollmentController::GetMode() {
bool AutoEnrollmentController::IsFREEnabled() {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
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)
std::string firmware_type;
const bool non_chrome_firmware =
system::StatisticsProvider::GetInstance()->GetMachineStatistic(
system::kFirmwareTypeKey, &firmware_type) &&
firmware_type == system::kFirmwareTypeValueNonchrome;
return non_chrome_firmware ? MODE_NONE : MODE_FORCED_RE_ENROLLMENT;
#else
return MODE_NONE;
#endif
} else if (command_line_mode == kForcedReEnrollmentNever) {
return MODE_NONE;
if (command_line_mode == kForcedReEnrollmentAlways)
return true;
if (command_line_mode.empty() ||
command_line_mode == kForcedReEnrollmentOfficialBuild) {
return IsOfficialChrome();
}
if (command_line_mode == kForcedReEnrollmentNever)
return false;
LOG(FATAL) << "Unknown auto-enrollment mode for FRE " << command_line_mode;
return false;
}
// static
bool AutoEnrollmentController::IsInitialEnrollmentEnabled() {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (!command_line->HasSwitch(switches::kEnterpriseEnableInitialEnrollment))
return false;
std::string command_line_mode = command_line->GetSwitchValueASCII(
switches::kEnterpriseEnableInitialEnrollment);
if (command_line_mode == kInitialEnrollmentAlways)
return true;
if (command_line_mode.empty() ||
command_line_mode == kInitialEnrollmentOfficialBuild) {
return IsOfficialChrome();
}
LOG(FATAL) << "Unknown auto-enrollment mode " << command_line_mode;
return MODE_NONE;
if (command_line_mode == kInitialEnrollmentNever)
return false;
LOG(FATAL) << "Unknown auto-enrollment mode for initial enrollment "
<< command_line_mode;
return false;
}
// static
bool AutoEnrollmentController::IsEnabled() {
return IsFREEnabled() || IsInitialEnrollmentEnabled();
}
// static
......@@ -118,15 +177,51 @@ AutoEnrollmentController::GetFRERequirement() {
if (fre_flag_found) {
if (check_enrollment_value == "0")
return AutoEnrollmentController::EXPLICITLY_NOT_REQUIRED;
return FRERequirement::kExplicitlyNotRequired;
if (check_enrollment_value == "1")
return AutoEnrollmentController::EXPLICITLY_REQUIRED;
return FRERequirement::kExplicitlyRequired;
}
if (!provider->GetMachineStatistic(system::kActivateDateKey, nullptr) &&
!provider->GetEnterpriseMachineID().empty()) {
return AutoEnrollmentController::NOT_REQUIRED;
return FRERequirement::kNotRequired;
}
return FRERequirement::kRequired;
}
// static
AutoEnrollmentController::InitialEnrollmentRequirement
AutoEnrollmentController::GetInitialEnrollmentRequirement() {
system::StatisticsProvider* provider =
system::StatisticsProvider::GetInstance();
rlz_lib::RlzValueStoreChromeOS::EmbargoState embargo_state =
rlz_lib::RlzValueStoreChromeOS::GetRlzEmbargoState();
if (embargo_state == rlz_lib::RlzValueStoreChromeOS::EmbargoState::kInvalid) {
LOG(WARNING)
<< "Skip Initial Enrollment Check due to invalid embargo date.";
// TODO(pmarko): UMA Stat.
return InitialEnrollmentRequirement::kNotRequired;
}
if (embargo_state ==
rlz_lib::RlzValueStoreChromeOS::EmbargoState::kNotPassed) {
VLOG(1) << "Skip Initial Enrollment Check due to not-passed embargo date.";
return InitialEnrollmentRequirement::kNotRequired;
}
if (provider->GetEnterpriseMachineID().empty()) {
LOG(WARNING)
<< "Skip Initial Enrollment Check due to missing serial number.";
return InitialEnrollmentRequirement::kNotRequired;
}
std::string rlz_brand_code;
const bool rlz_brand_code_found =
provider->GetMachineStatistic(system::kRlzBrandCodeKey, &rlz_brand_code);
if (!rlz_brand_code_found || rlz_brand_code.empty()) {
LOG(WARNING) << "Skip Initial Enrollment Check due to missing brand code.";
return InitialEnrollmentRequirement::kNotRequired;
}
return AutoEnrollmentController::REQUIRED;
return InitialEnrollmentRequirement::kRequired;
}
AutoEnrollmentController::AutoEnrollmentController() {}
......@@ -151,39 +246,18 @@ void AutoEnrollmentController::Start() {
break;
}
// Skip if GAIA is disabled or modulus configuration is not present.
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(chromeos::switches::kDisableGaiaServices) ||
(!command_line->HasSwitch(
chromeos::switches::kEnterpriseEnrollmentInitialModulus) &&
!command_line->HasSwitch(
chromeos::switches::kEnterpriseEnrollmentModulusLimit))) {
VLOG(1) << "Auto-enrollment disabled: command line.";
UpdateState(policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT);
return;
}
// Skip if mode comes up as none.
if (GetMode() == MODE_NONE) {
VLOG(1) << "Auto-enrollment disabled: no mode.";
UpdateState(policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT);
// If a client is being created or already existing, bail out.
if (client_start_weak_factory_.HasWeakPtrs() || client_) {
LOG(ERROR) << "Auto-enrollment client is already running.";
return;
}
fre_requirement_ = GetFRERequirement();
VLOG(1) << FRERequirementToString(fre_requirement_);
if (fre_requirement_ == EXPLICITLY_NOT_REQUIRED ||
fre_requirement_ == NOT_REQUIRED) {
DetermineAutoEnrollmentCheckType();
if (auto_enrollment_check_type_ == AutoEnrollmentCheckType::kNone) {
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_) {
LOG(ERROR) << "Auto-enrollment client is already running.";
return;
}
// Arm the belts-and-suspenders timer to avoid hangs.
safeguard_timer_.Start(FROM_HERE,
base::TimeDelta::FromSeconds(kSafeguardTimeoutSeconds),
......@@ -210,16 +284,111 @@ AutoEnrollmentController::RegisterProgressCallback(
return progress_callbacks_.Add(callback);
}
void AutoEnrollmentController::DetermineAutoEnrollmentCheckType() {
// Skip everything if neither FRE nor Initial Enrollment are enabled.
if (!IsEnabled()) {
VLOG(1) << "Auto-enrollment disabled";
auto_enrollment_check_type_ = AutoEnrollmentCheckType::kNone;
return;
}
// Skip everything if GAIA is disabled.
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kDisableGaiaServices)) {
VLOG(1) << "Auto-enrollment disabled: command line (gaia).";
auto_enrollment_check_type_ = AutoEnrollmentCheckType::kNone;
return;
}
// Skip everything if the device was in consumer mode previously.
fre_requirement_ = GetFRERequirement();
VLOG(1) << FRERequirementToString(fre_requirement_);
if (fre_requirement_ == FRERequirement::kExplicitlyNotRequired) {
VLOG(1) << "Auto-enrollment disabled: VPD";
auto_enrollment_check_type_ = AutoEnrollmentCheckType::kNone;
return;
}
if (ShouldDoFRECheck(command_line, fre_requirement_)) {
// FRE has precedence over Initial Enrollment.
VLOG(1) << "Proceeding with FRE";
auto_enrollment_check_type_ = AutoEnrollmentCheckType::kFRE;
return;
}
if (ShouldDoInitialEnrollmentCheck()) {
VLOG(1) << "Proceeding with Initial Enrollment";
auto_enrollment_check_type_ = AutoEnrollmentCheckType::kInitialEnrollment;
return;
}
auto_enrollment_check_type_ = AutoEnrollmentCheckType::kNone;
}
// static
bool AutoEnrollmentController::ShouldDoFRECheck(
base::CommandLine* command_line,
FRERequirement fre_requirement) {
// Skip FRE check if modulus configuration is not present.
if (!command_line->HasSwitch(switches::kEnterpriseEnrollmentInitialModulus) &&
!command_line->HasSwitch(switches::kEnterpriseEnrollmentModulusLimit)) {
VLOG(1) << "FRE disabled: command line (config)";
return false;
}
// Skip FRE check if it is not enabled by command-line switches.
if (!IsFREEnabled()) {
VLOG(1) << "FRE disabled";
return false;
}
// Skip FRE check if it is not required according to the device state.
if (fre_requirement == FRERequirement::kNotRequired)
return false;
return true;
}
// static
bool AutoEnrollmentController::ShouldDoInitialEnrollmentCheck() {
// Skip Initial Enrollment check if it is not enabled according to
// command-line flags.
if (!IsInitialEnrollmentEnabled())
return false;
// Skip Initial Enrollment check if it is not required according to the
// device state.
if (GetInitialEnrollmentRequirement() ==
InitialEnrollmentRequirement::kNotRequired)
return false;
return true;
}
void AutoEnrollmentController::OnOwnershipStatusCheckDone(
DeviceSettingsService::OwnershipStatus status) {
switch (status) {
case DeviceSettingsService::OWNERSHIP_NONE:
g_browser_process->platform_part()
->browser_policy_connector_chromeos()
->GetStateKeysBroker()
->RequestStateKeys(
base::Bind(&AutoEnrollmentController::StartClient,
client_start_weak_factory_.GetWeakPtr()));
switch (auto_enrollment_check_type_) {
case AutoEnrollmentCheckType::kFRE:
// For FRE, request state keys first.
g_browser_process->platform_part()
->browser_policy_connector_chromeos()
->GetStateKeysBroker()
->RequestStateKeys(
base::BindOnce(&AutoEnrollmentController::StartClientForFRE,
client_start_weak_factory_.GetWeakPtr()));
break;
case AutoEnrollmentCheckType::kInitialEnrollment:
StartClientForInitialEnrollment();
break;
case AutoEnrollmentCheckType::kNone:
// The ownership check is only triggered if
// |auto_enrollment_check_type_| indicates that an auto-enrollment
// check should be done.
NOTREACHED();
break;
}
return;
case DeviceSettingsService::OWNERSHIP_TAKEN:
VLOG(1) << "Device already owned, skipping auto-enrollment check.";
......@@ -232,35 +401,32 @@ void AutoEnrollmentController::OnOwnershipStatusCheckDone(
}
}
void AutoEnrollmentController::StartClient(
void AutoEnrollmentController::StartClientForFRE(
const std::vector<std::string>& state_keys) {
if (state_keys.empty()) {
LOG(ERROR) << "No state keys available";
if (fre_requirement_ == EXPLICITLY_REQUIRED) {
if (fre_requirement_ == FRERequirement::kExplicitlyRequired) {
// Retry to fetch the state keys. For devices where FRE is required to be
// checked, we can't proceed with empty state keys.
g_browser_process->platform_part()
->browser_policy_connector_chromeos()
->GetStateKeysBroker()
->RequestStateKeys(
base::Bind(&AutoEnrollmentController::StartClient,
client_start_weak_factory_.GetWeakPtr()));
base::BindOnce(&AutoEnrollmentController::StartClientForFRE,
client_start_weak_factory_.GetWeakPtr()));
} else {
UpdateState(policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT);
}
return;
}
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
policy::DeviceManagementService* service =
connector->device_management_service();
service->ScheduleInitialization(0);
InitializeAndGetDeviceManagementService();
int power_initial =
GetSanitizedArg(chromeos::switches::kEnterpriseEnrollmentInitialModulus);
GetSanitizedArg(switches::kEnterpriseEnrollmentInitialModulus);
int power_limit =
GetSanitizedArg(chromeos::switches::kEnterpriseEnrollmentModulusLimit);
GetSanitizedArg(switches::kEnterpriseEnrollmentModulusLimit);
if (power_initial > power_limit) {
LOG(ERROR) << "Initial auto-enrollment modulus is larger than the limit, "
"clamping to the limit.";
......@@ -274,7 +440,40 @@ void AutoEnrollmentController::StartClient(
g_browser_process->system_request_context(), state_keys.front(),
power_initial, power_limit);
VLOG(1) << "Starting auto-enrollment client.";
VLOG(1) << "Starting auto-enrollment client for FRE.";
client_->Start();
}
void AutoEnrollmentController::StartClientForInitialEnrollment() {
policy::DeviceManagementService* service =
InitializeAndGetDeviceManagementService();
// Initial Enrollment does not transfer any data in the initial exchange, and
// supports uploading up to |kInitialEnrollmentModulusPowerLimit| bits of the
// identifier hash.
const int power_initial = 0;
const int power_limit = kInitialEnrollmentModulusPowerLimit;
system::StatisticsProvider* provider =
system::StatisticsProvider::GetInstance();
std::string serial_number = provider->GetEnterpriseMachineID();
std::string rlz_brand_code;
const bool rlz_brand_code_found =
provider->GetMachineStatistic(system::kRlzBrandCodeKey, &rlz_brand_code);
// The initial enrollment check should not be started if the serial number or
// brand code are missing. This is ensured in
// |GetInitialEnrollmentRequirement|.
CHECK(!serial_number.empty() && rlz_brand_code_found &&
!rlz_brand_code.empty());
client_ = policy::AutoEnrollmentClient::CreateForInitialEnrollment(
base::BindRepeating(&AutoEnrollmentController::UpdateState,
weak_ptr_factory_.GetWeakPtr()),
service, g_browser_process->local_state(),
g_browser_process->system_request_context(), serial_number,
rlz_brand_code, power_initial, power_limit);
VLOG(1) << "Starting auto-enrollment client for Initial Enrollment.";
client_->Start();
}
......@@ -308,7 +507,7 @@ void AutoEnrollmentController::StartRemoveFirmwareManagementParameters() {
DCHECK_EQ(policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT, state_);
cryptohome::RemoveFirmwareManagementParametersRequest request;
chromeos::DBusThreadManager::Get()
DBusThreadManager::Get()
->GetCryptohomeClient()
->RemoveFirmwareManagementParametersFromTpm(
request,
......@@ -336,7 +535,7 @@ void AutoEnrollmentController::Timeout() {
// REQUIRED case as well.
// TODO(mnissler): Add UMA to track results of auto-enrollment checks.
if (client_start_weak_factory_.HasWeakPtrs() &&
fre_requirement_ != EXPLICITLY_REQUIRED) {
fre_requirement_ != FRERequirement::kExplicitlyRequired) {
// If the callbacks to check ownership status or state keys are still
// pending, there's a bug in the code running on the device. No use in
// retrying anything, need to fix that bug.
......
......@@ -17,6 +17,10 @@
#include "chrome/browser/chromeos/policy/auto_enrollment_client.h"
#include "chrome/browser/chromeos/settings/device_settings_service.h"
namespace base {
class CommandLine;
}
namespace cryptohome {
class BaseReply;
} // namespace cryptohome
......@@ -36,33 +40,55 @@ class AutoEnrollmentController {
static const char kForcedReEnrollmentNever[];
static const char kForcedReEnrollmentOfficialBuild[];
// Auto-enrollment modes.
enum Mode {
// No automatic enrollment.
MODE_NONE,
// Forced re-enrollment.
MODE_FORCED_RE_ENROLLMENT,
};
// Parameter values for the kEnterpriseEnableInitialEnrollment flag.
static const char kInitialEnrollmentAlways[];
static const char kInitialEnrollmentNever[];
static const char kInitialEnrollmentOfficialBuild[];
// Requirement for forced re-enrollment check.
enum FRERequirement {
enum class FRERequirement {
// The device was setup (has kActivateDateKey) but doesn't have the
// kCheckEnrollmentKey entry in VPD, or the VPD is corrupted.
REQUIRED,
kRequired,
// The device doesn't have kActivateDateKey, nor kCheckEnrollmentKey entry
// while the serial number has been successfully read from VPD.
NOT_REQUIRED,
kNotRequired,
// FRE check explicitly required by the flag in VPD.
EXPLICITLY_REQUIRED,
kExplicitlyRequired,
// FRE check to be skipped, explicitly stated by the flag in VPD.
EXPLICITLY_NOT_REQUIRED,
kExplicitlyNotRequired,
};
// Requirement for initial enrollment check.
enum class InitialEnrollmentRequirement {
// Initial enrollment check is not required.
kNotRequired,
// Initial enrollment check is required.
kRequired
};
// Gets the auto-enrollment mode based on command-line flags and official
// build status.
static Mode GetMode();
// Type of auto enrollment check.
enum class AutoEnrollmentCheckType {
kNone,
// Forced Re-Enrollment check.
kFRE,
// Initial enrollment check.
kInitialEnrollment
};
// Returns true if forced re-enrollment is enabled based on command-line flags
// and official build status.
static bool IsFREEnabled();
// Returns true if initial enrollment is enabled based on command-line
// flags and official build status.
static bool IsInitialEnrollmentEnabled();
// Returns whether the auto-enrollment check is required. When
// Returns true if any auto enrollment check is enabled based on command-line
// flags and official build status.
static bool IsEnabled();
// Returns whether the FRE auto-enrollment check is required. When
// kCheckEnrollmentKey VPD entry is present, it is explicitly stating whether
// the forced re-enrollment is required or not. Otherwise, for backward
// compatibility with devices upgrading from an older version of Chrome OS,
......@@ -74,6 +100,15 @@ class AutoEnrollmentController {
// is required.
static FRERequirement GetFRERequirement();
// Returns whether the initial enrollment check is required.
static InitialEnrollmentRequirement GetInitialEnrollmentRequirement();
// Returns the type of auto-enrollment check performed by this client. This
// will be |AutoEnrollmentCheckType::kNone| before |Start()| has been called.
AutoEnrollmentCheckType auto_enrollment_check_type() const {
return auto_enrollment_check_type_;
}
AutoEnrollmentController();
~AutoEnrollmentController();
......@@ -91,12 +126,27 @@ class AutoEnrollmentController {
policy::AutoEnrollmentState state() const { return state_; }
private:
// Determines the type of auto-enrollment check that should be done. Sets
// |auto_enrollment_check_type_| and |fre_requirement_|.
void DetermineAutoEnrollmentCheckType();
// Returns true if the FRE check should be done according to command-line
// switches and device state.
static bool ShouldDoFRECheck(base::CommandLine* command_line,
FRERequirement fre_requirement);
// Returns true if the Initial Enrollment check should be done according to
// command-line switches and device state.
static bool ShouldDoInitialEnrollmentCheck();
// Callback for the ownership status check.
void OnOwnershipStatusCheckDone(
DeviceSettingsService::OwnershipStatus status);
// Starts the auto-enrollment client.
void StartClient(const std::vector<std::string>& state_keys);
// Starts the auto-enrollment client for forced re-enrollment.
void StartClientForFRE(const std::vector<std::string>& state_keys);
// Starts the auto-enrollment client for initial enrollment.
void StartClientForInitialEnrollment();
// Sets |state_| and notifies |progress_callbacks_|.
void UpdateState(policy::AutoEnrollmentState state);
......@@ -136,7 +186,12 @@ class AutoEnrollmentController {
base::Timer safeguard_timer_{false, false};
// Whether the forced re-enrollment check has to be applied.
FRERequirement fre_requirement_ = REQUIRED;
FRERequirement fre_requirement_ = FRERequirement::kRequired;
// Which type of auto-enrollment check is being performed by this
// |AutoEnrollmentClient|.
AutoEnrollmentCheckType auto_enrollment_check_type_ =
AutoEnrollmentCheckType::kNone;
// TODO(igorcov): Merge the two weak_ptr factories in one.
base::WeakPtrFactory<AutoEnrollmentController> client_start_weak_factory_{
......
......@@ -10,6 +10,7 @@
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_shutdown.h"
#include "chrome/browser/chrome_notification_types.h"
......@@ -184,6 +185,33 @@ void QuitLoopOnAutoEnrollmentProgress(
loop->Quit();
}
// Returns a string which can be put into the |kRlzEmbargoEndDateKey| VPD
// variable. If |days_offset| is 0, the return value represents the current day.
// If |days_offset| is positive, the return value represents |days_offset| days
// in the future. If |days_offset| is negative, the return value represents
// |days_offset| days in the past.
std::string GenerateEmbargoEndDate(int days_offset) {
base::Time::Exploded exploded;
base::Time target_time =
base::Time::Now() + base::TimeDelta::FromDays(days_offset);
target_time.UTCExplode(&exploded);
std::string rlz_embargo_end_date_string = base::StringPrintf(
"%04d-%02d-%02d", exploded.year, exploded.month, exploded.day_of_month);
// Sanity check that base::Time::FromUTCString can read back the format used
// here.
base::Time reparsed_time;
EXPECT_TRUE(base::Time::FromUTCString(rlz_embargo_end_date_string.c_str(),
&reparsed_time));
EXPECT_EQ(target_time.ToDeltaSinceWindowsEpoch().InMicroseconds() /
base::Time::kMicrosecondsPerDay,
reparsed_time.ToDeltaSinceWindowsEpoch().InMicroseconds() /
base::Time::kMicrosecondsPerDay);
return rlz_embargo_end_date_string;
}
} // namespace
using ::testing::_;
......@@ -820,15 +848,19 @@ class WizardControllerDeviceStateTest : public WizardControllerFlowTest {
"2000-01");
}
static AutoEnrollmentController* auto_enrollment_controller() {
return WizardController::default_controller()
->GetAutoEnrollmentController();
}
static void WaitForAutoEnrollmentState(policy::AutoEnrollmentState state) {
base::RunLoop loop;
std::unique_ptr<
AutoEnrollmentController::ProgressCallbackList::Subscription>
progress_subscription(
WizardController::default_controller()
->GetAutoEnrollmentController()
->RegisterProgressCallback(base::Bind(
&QuitLoopOnAutoEnrollmentProgress, state, &loop)));
auto_enrollment_controller()->RegisterProgressCallback(
base::BindRepeating(&QuitLoopOnAutoEnrollmentProgress, state,
&loop)));
loop.Run();
}
......@@ -912,9 +944,7 @@ IN_PROC_BROWSER_TEST_F(WizardControllerDeviceStateTest,
ControlFlowNoForcedReEnrollmentOnFirstBoot) {
fake_statistics_provider_.ClearMachineStatistic(system::kActivateDateKey);
EXPECT_NE(policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT,
WizardController::default_controller()
->GetAutoEnrollmentController()
->state());
auto_enrollment_controller()->state());
CheckCurrentScreen(OobeScreen::SCREEN_OOBE_NETWORK);
EXPECT_CALL(*mock_network_screen_, Hide()).Times(1);
......@@ -938,9 +968,7 @@ IN_PROC_BROWSER_TEST_F(WizardControllerDeviceStateTest,
CheckCurrentScreen(OobeScreen::SCREEN_AUTO_ENROLLMENT_CHECK);
mock_auto_enrollment_check_screen_->RealShow();
EXPECT_EQ(policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT,
WizardController::default_controller()
->GetAutoEnrollmentController()
->state());
auto_enrollment_controller()->state());
}
IN_PROC_BROWSER_TEST_F(WizardControllerDeviceStateTest,
......@@ -995,6 +1023,158 @@ IN_PROC_BROWSER_TEST_F(WizardControllerDeviceStateTest,
EXPECT_FALSE(StartupUtils::IsOobeCompleted());
}
class WizardControllerDeviceStateWithInitialEnrollmentTest
: public WizardControllerDeviceStateTest {
protected:
WizardControllerDeviceStateWithInitialEnrollmentTest() {
fake_statistics_provider_.SetMachineStatistic(
system::kSerialNumberKeyForTest, "test");
fake_statistics_provider_.SetMachineStatistic(system::kRlzBrandCodeKey,
"AABC");
}
void SetUpCommandLine(base::CommandLine* command_line) override {
WizardControllerDeviceStateTest::SetUpCommandLine(command_line);
command_line->AppendSwitchASCII(
switches::kEnterpriseEnableInitialEnrollment,
chromeos::AutoEnrollmentController::kInitialEnrollmentAlways);
}
private:
DISALLOW_COPY_AND_ASSIGN(
WizardControllerDeviceStateWithInitialEnrollmentTest);
};
IN_PROC_BROWSER_TEST_F(WizardControllerDeviceStateWithInitialEnrollmentTest,
ControlFlowInitialEnrollment) {
fake_statistics_provider_.ClearMachineStatistic(system::kActivateDateKey);
fake_statistics_provider_.SetMachineStatistic(
system::kRlzEmbargoEndDateKey,
GenerateEmbargoEndDate(-15 /* days_offset */));
CheckCurrentScreen(OobeScreen::SCREEN_OOBE_NETWORK);
EXPECT_CALL(*mock_network_screen_, Hide()).Times(1);
EXPECT_CALL(*mock_eula_screen_, Show()).Times(1);
OnExit(*mock_network_screen_, ScreenExitCode::NETWORK_CONNECTED);
CheckCurrentScreen(OobeScreen::SCREEN_OOBE_EULA);
EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1);
EXPECT_CALL(*mock_update_screen_, StartNetworkCheck()).Times(1);
EXPECT_CALL(*mock_update_screen_, Show()).Times(1);
OnExit(*mock_eula_screen_, ScreenExitCode::EULA_ACCEPTED);
// Let update screen smooth time process (time = 0ms).
base::RunLoop().RunUntilIdle();
CheckCurrentScreen(OobeScreen::SCREEN_OOBE_UPDATE);
EXPECT_CALL(*mock_update_screen_, Hide()).Times(1);
EXPECT_CALL(*mock_auto_enrollment_check_screen_, Show()).Times(1);
OnExit(*mock_update_screen_, ScreenExitCode::UPDATE_INSTALLED);
CheckCurrentScreen(OobeScreen::SCREEN_AUTO_ENROLLMENT_CHECK);
EXPECT_CALL(*mock_auto_enrollment_check_screen_, Hide()).Times(1);
mock_auto_enrollment_check_screen_->RealShow();
// Wait for auto-enrollment controller to encounter the connection error.
WaitForAutoEnrollmentState(policy::AUTO_ENROLLMENT_STATE_CONNECTION_ERROR);
// The error screen shows up if there's no auto-enrollment decision.
EXPECT_FALSE(StartupUtils::IsOobeCompleted());
EXPECT_EQ(GetErrorScreen(),
WizardController::default_controller()->current_screen());
base::DictionaryValue device_state;
device_state.SetString(policy::kDeviceStateRestoreMode,
policy::kDeviceStateRestoreModeReEnrollmentEnforced);
g_browser_process->local_state()->Set(prefs::kServerBackedDeviceState,
device_state);
EXPECT_CALL(*mock_enrollment_screen_, Show()).Times(1);
EXPECT_CALL(*mock_enrollment_screen_->view(),
SetParameters(mock_enrollment_screen_,
EnrollmentModeMatches(
policy::EnrollmentConfig::MODE_SERVER_FORCED)))
.Times(1);
OnExit(*mock_auto_enrollment_check_screen_,
ScreenExitCode::ENTERPRISE_AUTO_ENROLLMENT_CHECK_COMPLETED);
ResetAutoEnrollmentCheckScreen();
// Make sure enterprise enrollment page shows up.
CheckCurrentScreen(OobeScreen::SCREEN_OOBE_ENROLLMENT);
OnExit(*mock_enrollment_screen_,
ScreenExitCode::ENTERPRISE_ENROLLMENT_COMPLETED);
EXPECT_TRUE(StartupUtils::IsOobeCompleted());
}
IN_PROC_BROWSER_TEST_F(WizardControllerDeviceStateWithInitialEnrollmentTest,
ControlFlowNoInitialEnrollmentDuringEmbargoPeriod) {
fake_statistics_provider_.ClearMachineStatistic(system::kActivateDateKey);
fake_statistics_provider_.SetMachineStatistic(
system::kRlzEmbargoEndDateKey,
GenerateEmbargoEndDate(1 /* days_offset */));
EXPECT_NE(policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT,
auto_enrollment_controller()->state());
CheckCurrentScreen(OobeScreen::SCREEN_OOBE_NETWORK);
EXPECT_CALL(*mock_network_screen_, Hide()).Times(1);
EXPECT_CALL(*mock_eula_screen_, Show()).Times(1);
OnExit(*mock_network_screen_, ScreenExitCode::NETWORK_CONNECTED);
CheckCurrentScreen(OobeScreen::SCREEN_OOBE_EULA);
EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1);
EXPECT_CALL(*mock_update_screen_, StartNetworkCheck()).Times(1);
EXPECT_CALL(*mock_update_screen_, Show()).Times(1);
OnExit(*mock_eula_screen_, ScreenExitCode::EULA_ACCEPTED);
// Let update screen smooth time process (time = 0ms).
base::RunLoop().RunUntilIdle();
CheckCurrentScreen(OobeScreen::SCREEN_OOBE_UPDATE);
EXPECT_CALL(*mock_update_screen_, Hide()).Times(1);
EXPECT_CALL(*mock_auto_enrollment_check_screen_, Show()).Times(1);
OnExit(*mock_update_screen_, ScreenExitCode::UPDATE_INSTALLED);
CheckCurrentScreen(OobeScreen::SCREEN_AUTO_ENROLLMENT_CHECK);
mock_auto_enrollment_check_screen_->RealShow();
EXPECT_EQ(policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT,
auto_enrollment_controller()->state());
}
IN_PROC_BROWSER_TEST_F(WizardControllerDeviceStateWithInitialEnrollmentTest,
ControlFlowNoInitialEnrollmentIfEnrolledBefore) {
fake_statistics_provider_.SetMachineStatistic(system::kCheckEnrollmentKey,
"0");
fake_statistics_provider_.SetMachineStatistic(
system::kRlzEmbargoEndDateKey,
GenerateEmbargoEndDate(1 /* days_offset */));
EXPECT_NE(policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT,
auto_enrollment_controller()->state());
CheckCurrentScreen(OobeScreen::SCREEN_OOBE_NETWORK);
EXPECT_CALL(*mock_network_screen_, Hide()).Times(1);
EXPECT_CALL(*mock_eula_screen_, Show()).Times(1);
OnExit(*mock_network_screen_, ScreenExitCode::NETWORK_CONNECTED);
CheckCurrentScreen(OobeScreen::SCREEN_OOBE_EULA);
EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1);
EXPECT_CALL(*mock_update_screen_, StartNetworkCheck()).Times(1);
EXPECT_CALL(*mock_update_screen_, Show()).Times(1);
OnExit(*mock_eula_screen_, ScreenExitCode::EULA_ACCEPTED);
// Let update screen smooth time process (time = 0ms).
base::RunLoop().RunUntilIdle();
CheckCurrentScreen(OobeScreen::SCREEN_OOBE_UPDATE);
EXPECT_CALL(*mock_update_screen_, Hide()).Times(1);
EXPECT_CALL(*mock_auto_enrollment_check_screen_, Show()).Times(1);
OnExit(*mock_update_screen_, ScreenExitCode::UPDATE_INSTALLED);
CheckCurrentScreen(OobeScreen::SCREEN_AUTO_ENROLLMENT_CHECK);
mock_auto_enrollment_check_screen_->RealShow();
EXPECT_EQ(policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT,
auto_enrollment_controller()->state());
}
class WizardControllerBrokenLocalStateTest : public WizardControllerTest {
protected:
WizardControllerBrokenLocalStateTest() : fake_session_manager_client_(NULL) {}
......
......@@ -100,8 +100,7 @@ bool GetMachineFlag(const std::string& key, bool default_value) {
// Checks whether forced re-enrollment is enabled.
bool ForcedReEnrollmentEnabled() {
return chromeos::AutoEnrollmentController::GetMode() ==
chromeos::AutoEnrollmentController::MODE_FORCED_RE_ENROLLMENT;
return chromeos::AutoEnrollmentController::IsFREEnabled();
}
} // namespace
......
......@@ -344,8 +344,7 @@ void EnrollmentHandlerChromeOS::HandleStateKeysResult(
DCHECK_EQ(STEP_STATE_KEYS, enrollment_step_);
// Make sure state keys are available if forced re-enrollment is on.
if (chromeos::AutoEnrollmentController::GetMode() ==
chromeos::AutoEnrollmentController::MODE_FORCED_RE_ENROLLMENT) {
if (chromeos::AutoEnrollmentController::IsFREEnabled()) {
client_->SetStateKeysToUpload(state_keys);
current_state_key_ = state_keys_broker_->current_state_key();
if (state_keys.empty() || current_state_key_.empty()) {
......
......@@ -40,16 +40,15 @@ ServerBackedStateKeysBroker::RegisterUpdateCallback(
return update_callbacks_.Add(callback);
}
void ServerBackedStateKeysBroker::RequestStateKeys(
const StateKeysCallback& callback) {
void ServerBackedStateKeysBroker::RequestStateKeys(StateKeysCallback callback) {
if (pending()) {
request_callbacks_.push_back(callback);
request_callbacks_.push_back(std::move(callback));
FetchStateKeys();
return;
}
if (!callback.is_null())
callback.Run(state_keys_);
std::move(callback).Run(state_keys_);
return;
}
......@@ -87,12 +86,9 @@ void ServerBackedStateKeysBroker::StoreStateKeys(
std::vector<StateKeysCallback> callbacks;
request_callbacks_.swap(callbacks);
for (std::vector<StateKeysCallback>::const_iterator callback(
callbacks.begin());
callback != callbacks.end();
++callback) {
if (!callback->is_null())
callback->Run(state_keys_);
for (auto& callback : callbacks) {
if (!callback.is_null())
std::move(callback).Run(state_keys_);
}
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
......
......@@ -27,7 +27,7 @@ class ServerBackedStateKeysBroker {
public:
typedef std::unique_ptr<base::CallbackList<void()>::Subscription>
Subscription;
typedef base::Callback<void(const std::vector<std::string>&)>
typedef base::OnceCallback<void(const std::vector<std::string>&)>
StateKeysCallback;
ServerBackedStateKeysBroker(
......@@ -46,7 +46,7 @@ class ServerBackedStateKeysBroker {
// empty. If |this| gets destroyed before the callback happens or if the time
// sync fails / the network is not established, then the |callback| is never
// invoked. See http://crbug.com/649422 for more context.
void RequestStateKeys(const StateKeysCallback& callback);
void RequestStateKeys(StateKeysCallback callback);
static base::TimeDelta GetPollIntervalForTesting();
......
......@@ -215,7 +215,8 @@ void ShouldOfferUpdateViaPowerwash(
// get enrolled and the admin allows TPM firmware update via powerwash.
const AutoEnrollmentController::FRERequirement requirement =
AutoEnrollmentController::GetFRERequirement();
if (requirement == AutoEnrollmentController::EXPLICITLY_REQUIRED) {
if (requirement ==
AutoEnrollmentController::FRERequirement::kExplicitlyRequired) {
callback.Run(false);
return;
}
......
......@@ -407,7 +407,8 @@ void CoreOobeHandler::HandleToggleResetScreen() {
// effect of dropping the FRE requirement if it was previously in effect.
const AutoEnrollmentController::FRERequirement requirement =
AutoEnrollmentController::GetFRERequirement();
if (requirement != AutoEnrollmentController::EXPLICITLY_REQUIRED) {
if (requirement !=
AutoEnrollmentController::FRERequirement::kExplicitlyRequired) {
LaunchResetScreen();
}
}
......
......@@ -392,6 +392,10 @@ const char kEnterpriseDisableLicenseTypeSelection[] =
const char kEnterpriseEnableForcedReEnrollment[] =
"enterprise-enable-forced-re-enrollment";
// Whether to enable initial enterprise enrollment.
const char kEnterpriseEnableInitialEnrollment[] =
"enterprise-enable-initial-enrollment";
// Enables the zero-touch enterprise enrollment flow.
const char kEnterpriseEnableZeroTouchEnrollment[] =
"enterprise-enable-zero-touch-enrollment";
......
......@@ -115,6 +115,7 @@ CHROMEOS_EXPORT extern const char kEnableZipArchiverUnpacker[];
CHROMEOS_EXPORT extern const char kEnterpriseDisableArc[];
CHROMEOS_EXPORT extern const char kEnterpriseDisableLicenseTypeSelection[];
CHROMEOS_EXPORT extern const char kEnterpriseEnableForcedReEnrollment[];
CHROMEOS_EXPORT extern const char kEnterpriseEnableInitialEnrollment[];
CHROMEOS_EXPORT extern const char kEnterpriseEnableZeroTouchEnrollment[];
CHROMEOS_EXPORT extern const char kEnterpriseEnrollmentInitialModulus[];
CHROMEOS_EXPORT extern const char kEnterpriseEnrollmentModulusLimit[];
......
......@@ -84,38 +84,6 @@ std::string GetKeyName(const std::string& key, Product product) {
return key + "." + GetProductName(product) + "." + brand;
}
// Returns true if the |rlz_embargo_end_date| present in VPD has passed
// compared to the current time.
bool HasRlzEmbargoEndDatePassed() {
chromeos::system::StatisticsProvider* stats =
chromeos::system::StatisticsProvider::GetInstance();
std::string rlz_embargo_end_date;
if (!stats->GetMachineStatistic(chromeos::system::kRlzEmbargoEndDateKey,
&rlz_embargo_end_date)) {
// |rlz_embargo_end_date| only exists on new devices that have not yet
// launched. When the field doesn't exist, returns true so it's a no-op.
return true;
}
base::Time parsed_time;
if (!base::Time::FromUTCString(rlz_embargo_end_date.c_str(), &parsed_time)) {
LOG(ERROR) << "|rlz_embargo_end_date| exists but cannot be parsed.";
return true;
}
if (parsed_time - base::Time::Now() >=
base::TimeDelta::FromDays(
RlzValueStoreChromeOS::kRlzEmbargoEndDateGarbageDateThresholdDays)) {
// If |rlz_embargo_end_date| is more than this many days in the future,
// ignore it. Because it indicates that the device is not connected to an
// ntp server in the factory, and its internal clock could be off when the
// date is written.
return true;
}
return base::Time::Now() > parsed_time;
}
} // namespace
const int RlzValueStoreChromeOS::kRlzEmbargoEndDateGarbageDateThresholdDays =
......@@ -323,6 +291,45 @@ void RlzValueStoreChromeOS::CollectGarbage() {
NOTIMPLEMENTED();
}
// static
RlzValueStoreChromeOS::EmbargoState
RlzValueStoreChromeOS::GetRlzEmbargoState() {
chromeos::system::StatisticsProvider* stats =
chromeos::system::StatisticsProvider::GetInstance();
std::string rlz_embargo_end_date;
if (!stats->GetMachineStatistic(chromeos::system::kRlzEmbargoEndDateKey,
&rlz_embargo_end_date)) {
// |rlz_embargo_end_date| only exists on new devices that have not yet
// launched.
return EmbargoState::kMissingOrMalformed;
}
base::Time parsed_time;
if (!base::Time::FromUTCString(rlz_embargo_end_date.c_str(), &parsed_time)) {
LOG(ERROR) << "|rlz_embargo_end_date| exists but cannot be parsed.";
return EmbargoState::kMissingOrMalformed;
}
if (parsed_time - base::Time::Now() >=
base::TimeDelta::FromDays(
RlzValueStoreChromeOS::kRlzEmbargoEndDateGarbageDateThresholdDays)) {
// If |rlz_embargo_end_date| is more than this many days in the future,
// ignore it. Because it indicates that the device is not connected to an
// ntp server in the factory, and its internal clock could be off when the
// date is written.
// TODO(pmarko): UMA stat for how often this happens.
return EmbargoState::kInvalid;
}
return base::Time::Now() > parsed_time ? EmbargoState::kPassed
: EmbargoState::kNotPassed;
}
// static
bool RlzValueStoreChromeOS::HasRlzEmbargoEndDatePassed() {
return GetRlzEmbargoState() != EmbargoState::kNotPassed;
}
void RlzValueStoreChromeOS::ReadStore() {
int error_code = 0;
std::string error_msg;
......
......@@ -63,7 +63,20 @@ class RlzValueStoreChromeOS : public RlzValueStore {
void CollectGarbage() override;
enum class EmbargoState {
kMissingOrMalformed,
kInvalid,
kNotPassed,
kPassed
};
static EmbargoState GetRlzEmbargoState();
private:
// Returns true if the |rlz_embargo_end_date| present in VPD has passed
// compared to the current time.
static bool HasRlzEmbargoEndDatePassed();
// Reads RLZ store from file.
void ReadStore();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment