1 // Copyright 2014 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.
5 #include "chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.h"
8 #include "base/bind_helpers.h"
9 #include "base/command_line.h"
10 #include "base/logging.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "chrome/browser/browser_process.h"
13 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
14 #include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h"
15 #include "chrome/browser/chromeos/policy/server_backed_state_keys_broker.h"
16 #include "chromeos/chromeos_switches.h"
17 #include "chromeos/system/statistics_provider.h"
18 #include "components/policy/core/common/cloud/device_management_service.h"
19 #include "net/url_request/url_request_context_getter.h"
25 // Maximum time to wait before forcing a decision. Note that download time for
26 // state key buckets can be non-negligible, especially on 2G connections.
27 const int kSafeguardTimeoutSeconds
= 60;
29 // Returns the int value of the |switch_name| argument, clamped to the [0, 62]
30 // interval. Returns 0 if the argument doesn't exist or isn't an int value.
31 int GetSanitizedArg(const std::string
& switch_name
) {
32 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
33 if (!command_line
->HasSwitch(switch_name
))
35 std::string value
= command_line
->GetSwitchValueASCII(switch_name
);
37 if (!base::StringToInt(value
, &int_value
)) {
38 LOG(ERROR
) << "Switch \"" << switch_name
<< "\" is not a valid int. "
39 << "Defaulting to 0.";
43 LOG(ERROR
) << "Switch \"" << switch_name
<< "\" can't be negative. "
47 if (int_value
> policy::AutoEnrollmentClient::kMaximumPower
) {
48 LOG(ERROR
) << "Switch \"" << switch_name
<< "\" can't be greater than "
49 << policy::AutoEnrollmentClient::kMaximumPower
<< ". Using "
50 << policy::AutoEnrollmentClient::kMaximumPower
;
51 return policy::AutoEnrollmentClient::kMaximumPower
;
56 // Checks whether the device is yet to be set up by the first user in its
57 // lifetime. After first setup, the activation date gets stored in the R/W VPD,
58 // the absence of this key signals the device is factory-fresh. The requirement
59 // for the machine serial number to be present as well is a sanity-check to
60 // ensure that the VPD has actually been read successfully.
61 bool IsFirstDeviceSetup() {
62 std::string activate_date
;
63 return !system::StatisticsProvider::GetInstance()->HasMachineStatistic(
64 system::kActivateDateKey
) &&
65 !policy::DeviceCloudPolicyManagerChromeOS::GetMachineID().empty();
70 const char AutoEnrollmentController::kForcedReEnrollmentAlways
[] = "always";
71 const char AutoEnrollmentController::kForcedReEnrollmentNever
[] = "never";
72 const char AutoEnrollmentController::kForcedReEnrollmentOfficialBuild
[] =
75 AutoEnrollmentController::Mode
AutoEnrollmentController::GetMode() {
76 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
78 std::string command_line_mode
= command_line
->GetSwitchValueASCII(
79 switches::kEnterpriseEnableForcedReEnrollment
);
80 if (command_line_mode
== kForcedReEnrollmentAlways
) {
81 return MODE_FORCED_RE_ENROLLMENT
;
82 } else if (command_line_mode
.empty() ||
83 command_line_mode
== kForcedReEnrollmentOfficialBuild
) {
84 #if defined(OFFICIAL_BUILD)
85 std::string firmware_type
;
86 const bool non_chrome_firmware
=
87 system::StatisticsProvider::GetInstance()->GetMachineStatistic(
88 system::kFirmwareTypeKey
, &firmware_type
) &&
89 firmware_type
== system::kFirmwareTypeValueNonchrome
;
91 std::string write_protect_switch_boot
;
92 const bool write_protect_switch_off
=
93 system::StatisticsProvider::GetInstance()->GetMachineStatistic(
94 system::kWriteProtectSwitchBootKey
, &write_protect_switch_boot
) &&
95 write_protect_switch_boot
== system::kWriteProtectSwitchBootValueOff
;
97 return (non_chrome_firmware
|| write_protect_switch_off
)
99 : MODE_FORCED_RE_ENROLLMENT
;
103 } else if (command_line_mode
== kForcedReEnrollmentNever
) {
107 LOG(FATAL
) << "Unknown auto-enrollment mode " << command_line_mode
;
111 AutoEnrollmentController::AutoEnrollmentController()
112 : state_(policy::AUTO_ENROLLMENT_STATE_IDLE
),
113 safeguard_timer_(false, false),
114 client_start_weak_factory_(this) {}
116 AutoEnrollmentController::~AutoEnrollmentController() {}
118 void AutoEnrollmentController::Start() {
119 // This method is called at the point in the OOBE/login flow at which the
120 // auto-enrollment check can start. This happens either after the EULA is
121 // accepted, or right after a reboot if the EULA has already been accepted.
123 // Do not communicate auto-enrollment data to the server if
124 // 1. we are running telemetry tests.
125 // 2. modulus configuration is not present.
126 // 3. Auto-enrollment is disabled via the command line.
127 // 4. This is the first boot ever, so re-enrollment checks are pointless. This
128 // also enables factories to start full guest sessions for testing, see
129 // http://crbug.com/397354 for more context.
131 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
132 if (command_line
->HasSwitch(chromeos::switches::kDisableGaiaServices
) ||
133 (!command_line
->HasSwitch(
134 chromeos::switches::kEnterpriseEnrollmentInitialModulus
) &&
135 !command_line
->HasSwitch(
136 chromeos::switches::kEnterpriseEnrollmentModulusLimit
)) ||
137 GetMode() == MODE_NONE
||
138 IsFirstDeviceSetup()) {
139 VLOG(1) << "Auto-enrollment disabled.";
140 UpdateState(policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT
);
144 // If a client is being created or already existing, bail out.
145 if (client_start_weak_factory_
.HasWeakPtrs() || client_
)
148 // Arm the belts-and-suspenders timer to avoid hangs.
149 safeguard_timer_
.Start(
150 FROM_HERE
, base::TimeDelta::FromSeconds(kSafeguardTimeoutSeconds
),
151 base::Bind(&AutoEnrollmentController::Timeout
, base::Unretained(this)));
153 // Start by checking if the device has already been owned.
154 UpdateState(policy::AUTO_ENROLLMENT_STATE_PENDING
);
155 DeviceSettingsService::Get()->GetOwnershipStatusAsync(
156 base::Bind(&AutoEnrollmentController::OnOwnershipStatusCheckDone
,
157 client_start_weak_factory_
.GetWeakPtr()));
160 void AutoEnrollmentController::Cancel() {
162 // Cancelling the |client_| allows it to determine whether
163 // its protocol finished before login was complete.
164 client_
.release()->CancelAndDeleteSoon();
167 // Make sure to nuke pending |client_| start sequences.
168 client_start_weak_factory_
.InvalidateWeakPtrs();
170 safeguard_timer_
.Stop();
173 void AutoEnrollmentController::Retry() {
180 scoped_ptr
<AutoEnrollmentController::ProgressCallbackList::Subscription
>
181 AutoEnrollmentController::RegisterProgressCallback(
182 const ProgressCallbackList::CallbackType
& callback
) {
183 return progress_callbacks_
.Add(callback
);
186 void AutoEnrollmentController::OnOwnershipStatusCheckDone(
187 DeviceSettingsService::OwnershipStatus status
) {
188 if (status
!= DeviceSettingsService::OWNERSHIP_NONE
) {
189 // The device is already owned. No need for auto-enrollment checks.
190 VLOG(1) << "Device already owned, skipping auto-enrollment check";
191 UpdateState(policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT
);
195 // Make sure state keys are available.
196 g_browser_process
->platform_part()
197 ->browser_policy_connector_chromeos()
198 ->GetStateKeysBroker()
199 ->RequestStateKeys(base::Bind(&AutoEnrollmentController::StartClient
,
200 client_start_weak_factory_
.GetWeakPtr()));
203 void AutoEnrollmentController::StartClient(
204 const std::vector
<std::string
>& state_keys
) {
205 if (state_keys
.empty()) {
206 LOG(WARNING
) << "No state keys available!";
207 UpdateState(policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT
);
211 policy::BrowserPolicyConnectorChromeOS
* connector
=
212 g_browser_process
->platform_part()->browser_policy_connector_chromeos();
213 policy::DeviceManagementService
* service
=
214 connector
->device_management_service();
215 service
->ScheduleInitialization(0);
217 int power_initial
= GetSanitizedArg(
218 chromeos::switches::kEnterpriseEnrollmentInitialModulus
);
219 int power_limit
= GetSanitizedArg(
220 chromeos::switches::kEnterpriseEnrollmentModulusLimit
);
221 if (power_initial
> power_limit
) {
222 LOG(ERROR
) << "Initial auto-enrollment modulus is larger than the limit, "
223 << "clamping to the limit.";
224 power_initial
= power_limit
;
227 client_
.reset(new policy::AutoEnrollmentClient(
228 base::Bind(&AutoEnrollmentController::UpdateState
,
229 base::Unretained(this)),
231 g_browser_process
->local_state(),
232 g_browser_process
->system_request_context(),
237 VLOG(1) << "Starting auto-enrollment client.";
241 void AutoEnrollmentController::UpdateState(
242 policy::AutoEnrollmentState new_state
) {
243 VLOG(1) << "New state: " << new_state
<< ".";
246 // Stop the safeguard timer once a result comes in.
248 case policy::AUTO_ENROLLMENT_STATE_IDLE
:
249 case policy::AUTO_ENROLLMENT_STATE_PENDING
:
251 case policy::AUTO_ENROLLMENT_STATE_CONNECTION_ERROR
:
252 case policy::AUTO_ENROLLMENT_STATE_SERVER_ERROR
:
253 case policy::AUTO_ENROLLMENT_STATE_TRIGGER_ENROLLMENT
:
254 case policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT
:
255 safeguard_timer_
.Stop();
259 progress_callbacks_
.Notify(state_
);
262 void AutoEnrollmentController::Timeout() {
263 // TODO(mnissler): Add UMA to track results of auto-enrollment checks.
264 if (client_start_weak_factory_
.HasWeakPtrs()) {
265 // If the callbacks to check ownership status or state keys are still
266 // pending, there's a bug in the code running on the device. No use in
267 // retrying anything, need to fix that bug.
268 LOG(ERROR
) << "Failed to start auto-enrollment check, fix the code!";
269 UpdateState(policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT
);
271 // If the AutoEnrollmentClient didn't manage to come back with a result in
272 // time, blame it on the network. This can actually happen in some cases,
273 // for example when the server just doesn't reply and keeps the connection
275 LOG(ERROR
) << "AutoEnrollmentClient didn't complete within time limit.";
276 UpdateState(policy::AUTO_ENROLLMENT_STATE_CONNECTION_ERROR
);
283 } // namespace chromeos