Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(614)

Side by Side Diff: chrome/browser/policy/auto_enrollment_client.cc

Issue 12189011: Split up chrome/browser/policy subdirectory (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: gyp cleanup Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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/policy/auto_enrollment_client.h"
6
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/guid.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/message_loop_proxy.h"
13 #include "base/metrics/histogram.h"
14 #include "base/prefs/pref_registry_simple.h"
15 #include "base/prefs/pref_service.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/policy/browser_policy_connector.h"
19 #include "chrome/browser/policy/device_cloud_policy_manager_chromeos.h"
20 #include "chrome/browser/policy/device_management_service.h"
21 #include "chrome/common/chrome_switches.h"
22 #include "chrome/common/pref_names.h"
23 #include "crypto/sha2.h"
24
25 namespace em = enterprise_management;
26
27 namespace {
28
29 // The modulus value is sent in an int64 field in the protobuf, whose maximum
30 // value is 2^63-1. So 2^64 and 2^63 can't be represented as moduli and the
31 // max is 2^62 (when the moduli are restricted to powers-of-2).
32 const int kMaximumPower = 62;
33
34 // Returns the int value of the |switch_name| argument, clamped to the [0, 62]
35 // interval. Returns 0 if the argument doesn't exist or isn't an int value.
36 int GetSanitizedArg(const std::string& switch_name) {
37 CommandLine* command_line = CommandLine::ForCurrentProcess();
38 if (!command_line->HasSwitch(switch_name))
39 return 0;
40 std::string value = command_line->GetSwitchValueASCII(switch_name);
41 int int_value;
42 if (!base::StringToInt(value, &int_value)) {
43 LOG(ERROR) << "Switch \"" << switch_name << "\" is not a valid int. "
44 << "Defaulting to 0.";
45 return 0;
46 }
47 if (int_value < 0) {
48 LOG(ERROR) << "Switch \"" << switch_name << "\" can't be negative. "
49 << "Using 0";
50 return 0;
51 }
52 if (int_value > kMaximumPower) {
53 LOG(ERROR) << "Switch \"" << switch_name << "\" can't be greater than "
54 << kMaximumPower << ". Using " << kMaximumPower;
55 return kMaximumPower;
56 }
57 return int_value;
58 }
59
60 // Returns the power of the next power-of-2 starting at |value|.
61 int NextPowerOf2(int64 value) {
62 for (int i = 0; i <= kMaximumPower; ++i) {
63 if ((GG_INT64_C(1) << i) >= value)
64 return i;
65 }
66 // No other value can be represented in an int64.
67 return kMaximumPower + 1;
68 }
69
70 } // namespace
71
72 namespace policy {
73
74 AutoEnrollmentClient::AutoEnrollmentClient(const base::Closure& callback,
75 DeviceManagementService* service,
76 PrefService* local_state,
77 const std::string& serial_number,
78 int power_initial,
79 int power_limit)
80 : completion_callback_(callback),
81 should_auto_enroll_(false),
82 device_id_(base::GenerateGUID()),
83 power_initial_(power_initial),
84 power_limit_(power_limit),
85 requests_sent_(0),
86 device_management_service_(service),
87 local_state_(local_state) {
88 DCHECK_LE(power_initial_, power_limit_);
89 if (!serial_number.empty())
90 serial_number_hash_ = crypto::SHA256HashString(serial_number);
91 }
92
93 AutoEnrollmentClient::~AutoEnrollmentClient() {}
94
95 // static
96 void AutoEnrollmentClient::RegisterPrefs(PrefRegistrySimple* registry) {
97 registry->RegisterBooleanPref(prefs::kShouldAutoEnroll, false);
98 registry->RegisterIntegerPref(prefs::kAutoEnrollmentPowerLimit, -1);
99 }
100
101 // static
102 bool AutoEnrollmentClient::IsDisabled() {
103 CommandLine* command_line = CommandLine::ForCurrentProcess();
104 return
105 !command_line->HasSwitch(switches::kEnterpriseEnrollmentInitialModulus) &&
106 !command_line->HasSwitch(switches::kEnterpriseEnrollmentModulusLimit);
107 }
108
109 // static
110 AutoEnrollmentClient* AutoEnrollmentClient::Create(
111 const base::Closure& completion_callback) {
112 // The client won't do anything if |service| is NULL.
113 DeviceManagementService* service = NULL;
114 if (IsDisabled()) {
115 VLOG(1) << "Auto-enrollment is disabled";
116 } else {
117 std::string url = BrowserPolicyConnector::GetDeviceManagementUrl();
118 if (!url.empty()) {
119 service = new DeviceManagementService(url);
120 service->ScheduleInitialization(0);
121 }
122 }
123
124 int power_initial = GetSanitizedArg(
125 switches::kEnterpriseEnrollmentInitialModulus);
126 int power_limit = GetSanitizedArg(
127 switches::kEnterpriseEnrollmentModulusLimit);
128 if (power_initial > power_limit) {
129 LOG(ERROR) << "Initial auto-enrollment modulus is larger than the limit, "
130 << "clamping to the limit.";
131 power_initial = power_limit;
132 }
133
134 return new AutoEnrollmentClient(
135 completion_callback,
136 service,
137 g_browser_process->local_state(),
138 DeviceCloudPolicyManagerChromeOS::GetMachineID(),
139 power_initial,
140 power_limit);
141 }
142
143 // static
144 void AutoEnrollmentClient::CancelAutoEnrollment() {
145 PrefService* local_state = g_browser_process->local_state();
146 local_state->SetBoolean(prefs::kShouldAutoEnroll, false);
147 local_state->CommitPendingWrite();
148 }
149
150 void AutoEnrollmentClient::Start() {
151 // Drop the previous job and reset state.
152 request_job_.reset();
153 should_auto_enroll_ = false;
154 time_start_ = base::Time(); // reset to null.
155
156 if (GetCachedDecision()) {
157 VLOG(1) << "AutoEnrollmentClient: using cached decision: "
158 << should_auto_enroll_;
159 } else if (device_management_service_.get()) {
160 if (serial_number_hash_.empty()) {
161 LOG(ERROR) << "Failed to get the hash of the serial number, "
162 << "will not attempt to auto-enroll.";
163 } else {
164 time_start_ = base::Time::Now();
165 SendRequest(power_initial_);
166 // Don't invoke the callback now.
167 return;
168 }
169 }
170
171 // Auto-enrollment can't even start, so we're done.
172 OnProtocolDone();
173 }
174
175 void AutoEnrollmentClient::CancelAndDeleteSoon() {
176 if (time_start_.is_null()) {
177 // The client isn't running, just delete it.
178 delete this;
179 } else {
180 // Client still running, but our owner isn't interested in the result
181 // anymore. Wait until the protocol completes to measure the extra time
182 // needed.
183 time_extra_start_ = base::Time::Now();
184 completion_callback_.Reset();
185 }
186 }
187
188 bool AutoEnrollmentClient::GetCachedDecision() {
189 const PrefService::Preference* should_enroll_pref =
190 local_state_->FindPreference(prefs::kShouldAutoEnroll);
191 const PrefService::Preference* previous_limit_pref =
192 local_state_->FindPreference(prefs::kAutoEnrollmentPowerLimit);
193 bool should_auto_enroll = false;
194 int previous_limit = -1;
195
196 if (!should_enroll_pref ||
197 should_enroll_pref->IsDefaultValue() ||
198 !should_enroll_pref->GetValue()->GetAsBoolean(&should_auto_enroll) ||
199 !previous_limit_pref ||
200 previous_limit_pref->IsDefaultValue() ||
201 !previous_limit_pref->GetValue()->GetAsInteger(&previous_limit) ||
202 power_limit_ > previous_limit) {
203 return false;
204 }
205
206 should_auto_enroll_ = should_auto_enroll;
207 return true;
208 }
209
210 void AutoEnrollmentClient::SendRequest(int power) {
211 if (power < 0 || power > power_limit_ || serial_number_hash_.empty()) {
212 NOTREACHED();
213 OnProtocolDone();
214 return;
215 }
216
217 requests_sent_++;
218
219 // Only power-of-2 moduli are supported for now. These are computed by taking
220 // the lower |power| bits of the hash.
221 uint64 remainder = 0;
222 for (int i = 0; 8 * i < power; ++i) {
223 uint64 byte = serial_number_hash_[31 - i] & 0xff;
224 remainder = remainder | (byte << (8 * i));
225 }
226 remainder = remainder & ((GG_UINT64_C(1) << power) - 1);
227
228 request_job_.reset(
229 device_management_service_->CreateJob(
230 DeviceManagementRequestJob::TYPE_AUTO_ENROLLMENT));
231 request_job_->SetClientID(device_id_);
232 em::DeviceAutoEnrollmentRequest* request =
233 request_job_->GetRequest()->mutable_auto_enrollment_request();
234 request->set_remainder(remainder);
235 request->set_modulus(GG_INT64_C(1) << power);
236 request_job_->Start(base::Bind(&AutoEnrollmentClient::OnRequestCompletion,
237 base::Unretained(this)));
238 }
239
240 void AutoEnrollmentClient::OnRequestCompletion(
241 DeviceManagementStatus status,
242 const em::DeviceManagementResponse& response) {
243 if (status != DM_STATUS_SUCCESS || !response.has_auto_enrollment_response()) {
244 LOG(ERROR) << "Auto enrollment error: " << status;
245 OnProtocolDone();
246 return;
247 }
248
249 const em::DeviceAutoEnrollmentResponse& enrollment_response =
250 response.auto_enrollment_response();
251 if (enrollment_response.has_expected_modulus()) {
252 // Server is asking us to retry with a different modulus.
253 int64 modulus = enrollment_response.expected_modulus();
254 int power = NextPowerOf2(modulus);
255 if ((GG_INT64_C(1) << power) != modulus) {
256 LOG(WARNING) << "Auto enrollment: the server didn't ask for a power-of-2 "
257 << "modulus. Using the closest power-of-2 instead "
258 << "(" << modulus << " vs 2^" << power << ")";
259 }
260 if (requests_sent_ >= 2) {
261 LOG(ERROR) << "Auto enrollment error: already retried with an updated "
262 << "modulus but the server asked for a new one again: "
263 << power;
264 } else if (power > power_limit_) {
265 LOG(ERROR) << "Auto enrollment error: the server asked for a larger "
266 << "modulus than the client accepts (" << power << " vs "
267 << power_limit_ << ").";
268 } else {
269 // Retry at most once with the modulus that the server requested.
270 if (power <= power_initial_) {
271 LOG(WARNING) << "Auto enrollment: the server asked to use a modulus ("
272 << power << ") that isn't larger than the first used ("
273 << power_initial_ << "). Retrying anyway.";
274 }
275 SendRequest(power);
276 // Don't invoke the callback yet.
277 return;
278 }
279 } else {
280 // Server should have sent down a list of hashes to try.
281 should_auto_enroll_ = IsSerialInProtobuf(enrollment_response.hash());
282 LOG(INFO) << "Auto enrollment complete, should_auto_enroll = "
283 << should_auto_enroll_;
284 }
285
286 // Auto-enrollment done.
287 OnProtocolDone();
288 }
289
290 bool AutoEnrollmentClient::IsSerialInProtobuf(
291 const google::protobuf::RepeatedPtrField<std::string>& hashes) {
292 for (int i = 0; i < hashes.size(); ++i) {
293 if (hashes.Get(i) == serial_number_hash_)
294 return true;
295 }
296 return false;
297 }
298
299 void AutoEnrollmentClient::OnProtocolDone() {
300 static const char* kProtocolTime = "Enterprise.AutoEnrollmentProtocolTime";
301 static const char* kExtraTime = "Enterprise.AutoEnrollmentExtraTime";
302 // The mininum time can't be 0, must be at least 1.
303 static const base::TimeDelta kMin = base::TimeDelta::FromMilliseconds(1);
304 static const base::TimeDelta kMax = base::TimeDelta::FromMinutes(5);
305 // However, 0 can still be sampled.
306 static const base::TimeDelta kZero = base::TimeDelta::FromMilliseconds(0);
307 static const int kBuckets = 50;
308
309 base::Time now = base::Time::Now();
310 if (!time_start_.is_null()) {
311 base::TimeDelta delta = now - time_start_;
312 UMA_HISTOGRAM_CUSTOM_TIMES(kProtocolTime, delta, kMin, kMax, kBuckets);
313 time_start_ = base::Time();
314
315 // The decision is cached only if the protocol was actually started, which
316 // is the case only if |time_start_| was not null.
317 local_state_->SetBoolean(prefs::kShouldAutoEnroll, should_auto_enroll_);
318 local_state_->SetInteger(prefs::kAutoEnrollmentPowerLimit, power_limit_);
319 local_state_->CommitPendingWrite();
320 }
321 base::TimeDelta delta = kZero;
322 if (!time_extra_start_.is_null()) {
323 // CancelAndDeleteSoon() was invoked before.
324 delta = now - time_extra_start_;
325 base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE, this);
326 time_extra_start_ = base::Time();
327 }
328 // This samples |kZero| when there was no need for extra time, so that we can
329 // measure the ratio of users that succeeded without needing a delay to the
330 // total users going through OOBE.
331 UMA_HISTOGRAM_CUSTOM_TIMES(kExtraTime, delta, kMin, kMax, kBuckets);
332
333 if (!completion_callback_.is_null())
334 completion_callback_.Run();
335 }
336
337 } // namespace policy
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698