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

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

Issue 8872032: Added the AutoEnrollmentClient and unit tests for it. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Use callback instead of Delegate, using InSequence, renamed switches, cleanups Created 9 years 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) 2011 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/command_line.h"
8 #include "base/logging.h"
9 #include "base/string_number_conversions.h"
10 #include "chrome/browser/policy/browser_policy_connector.h"
11 #include "chrome/browser/policy/device_management_service.h"
12 #include "chrome/common/chrome_switches.h"
13 #include "chrome/common/guid.h"
14 #include "crypto/sha2.h"
15
16 namespace {
17
18 // The modulus value is sent in an int64 field in the protobuf, whose maximum
19 // value is 2^63-1. So 2^64 and 2^63 can't be represented as moduli and the
20 // max is 2^62 (when the moduli are restricted to powers-of-2).
21 const int kMaximumPower = 62;
22
23 // Returns the int value of the |switch_name| argument, clamped to the [0, 62]
24 // interval. Returns 0 if the argument doesn't exist or isn't an int value.
25 int GetSanitizedArg(const std::string& switch_name) {
26 CommandLine* command_line = CommandLine::ForCurrentProcess();
27 if (!command_line->HasSwitch(switch_name))
28 return 0;
29 std::string value = command_line->GetSwitchValueASCII(switch_name);
30 int int_value;
31 if (!base::StringToInt(value, &int_value)) {
32 LOG(ERROR) << "Switch \"" << switch_name << "\" is not a valid int. "
33 << "Defaulting to 0.";
34 return 0;
35 }
36 if (int_value < 0) {
37 LOG(ERROR) << "Switch \"" << switch_name << "\" can't be negative. "
38 << "Using 0";
39 return 0;
40 }
41 if (int_value > kMaximumPower) {
42 LOG(ERROR) << "Switch \"" << switch_name << "\" can't be greater than "
43 << kMaximumPower << ". Using " << kMaximumPower;
44 return kMaximumPower;
45 }
46 return int_value;
47 }
48
49 // Returns the power of the next power-of-2 starting at |value|.
50 int NextPowerOf2(int64 value) {
51 for (int i = 0; i <= kMaximumPower; ++i) {
52 if ((1 << i) >= value)
53 return i;
54 }
55 // No other value can be represented in an int64.
56 return kMaximumPower + 1;
57 }
58
59 } // namespace
60
61 namespace policy {
62
63 AutoEnrollmentClient::AutoEnrollmentClient(const base::Closure& callback,
64 DeviceManagementService* service,
65 const std::string& serial_number,
66 int power_initial,
67 int power_limit)
68 : completion_callback_(callback),
69 should_auto_enroll_(false),
70 device_id_(guid::GenerateGUID()),
71 power_initial_(power_initial),
72 power_limit_(power_limit),
73 device_management_service_(service) {
74 DCHECK_LE(power_initial_, power_limit_);
75 if (!serial_number.empty())
76 serial_number_hash_ = crypto::SHA256HashString(serial_number);
77 }
78
79 AutoEnrollmentClient::~AutoEnrollmentClient() {}
80
81 // static
82 AutoEnrollmentClient* AutoEnrollmentClient::Create(
83 const base::Closure& completion_callback) {
84 CommandLine* command_line = CommandLine::ForCurrentProcess();
85
86 std::string url =
87 command_line->GetSwitchValueASCII(switches::kDeviceManagementUrl);
88 DeviceManagementService* service = NULL;
89 // This client won't do anything if there's no device management service url.
90 if (!url.empty()) {
91 service = new DeviceManagementService(url);
92 service->ScheduleInitialization(0);
93 }
94
95 int power_initial = GetSanitizedArg(
96 switches::kEnterpriseEnrollmentInitialModulus);
97 int power_limit = GetSanitizedArg(
98 switches::kEnterpriseEnrollmentModulusLimit);
99 if (power_initial > power_limit) {
100 LOG(ERROR) << "Initial auto-enrollment modulus is larger than the limit, "
101 << "clamping to the limit.";
102 power_initial = power_limit;
103 }
104
105 return new AutoEnrollmentClient(completion_callback,
106 service,
107 BrowserPolicyConnector::GetSerialNumber(),
108 power_initial,
109 power_limit);
110 }
111
112 void AutoEnrollmentClient::Start() {
113 // Drop the previous backend and reset state.
114 device_management_backend_.reset();
115 should_auto_enroll_ = false;
116
117 if (device_management_service_.get()) {
118 if (serial_number_hash_.empty()) {
119 LOG(ERROR) << "Failed to get the hash of the serial number, "
120 << "will not attempt to auto-enroll.";
121 } else {
122 // Create a new backend and fire the initial request.
123 device_management_backend_.reset(
124 device_management_service_->CreateBackend());
125 SendRequest(power_initial_);
126 // Don't invoke the callback now.
127 return;
128 }
129 }
130
131 // Auto-enrollment can't even start, so we're done.
132 completion_callback_.Run();
133 }
134
135 void AutoEnrollmentClient::SendRequest(int power) {
136 if (power < 0 || power > power_limit_ || serial_number_hash_.empty()) {
137 NOTREACHED();
138 completion_callback_.Run();
139 return;
140 }
141
142 last_power_used_ = power;
143
144 // Only power-of-2 moduli are supported for now. These are computed by taking
145 // the lower |power| bits of the hash.
146 uint64 remainder = 0;
147 for (int i = 0; 8 * i < power; ++i) {
148 uint64 byte = serial_number_hash_[31 - i] & 0xff;
149 remainder = remainder | (byte << (8 * i));
150 }
151 remainder = remainder & ((1ULL << power) - 1);
152
153 em::DeviceAutoEnrollmentRequest request;
154 request.set_remainder(remainder);
155 request.set_modulus((int64) 1 << power);
156
157 device_management_backend_->ProcessAutoEnrollmentRequest(device_id_,
158 request,
159 this);
160 }
161
162 void AutoEnrollmentClient::HandleAutoEnrollmentResponse(
163 const em::DeviceAutoEnrollmentResponse& response) {
164 if (response.has_modulus()) {
165 // Server is asking us to retry with a different modulus.
166 int64 modulus = response.modulus();
167 int64 last_modulus_used = 1 << last_power_used_;
168 int power = NextPowerOf2(modulus);
169 if ((1 << power) != modulus) {
170 LOG(WARNING) << "Auto enrollment: the server didn't ask for a power-of-2 "
171 << "modulus. Using the closest power-of-2 instead "
172 << "(" << modulus << " vs 2^" << power << ")";
173 }
174 if (modulus < last_modulus_used) {
175 LOG(ERROR) << "Auto enrollment error: the server asked for a smaller "
176 << "modulus than we used.";
177 } else if (modulus == last_modulus_used) {
178 LOG(ERROR) << "Auto enrollment error: the server asked for the same "
179 << "modulus that was already used.";
180 } else if (last_power_used_ != power_initial_) {
181 LOG(ERROR) << "Auto enrollment error: already retried with an updated "
182 << "modulus but the server asked for a new one.";
183 } else if (power > power_limit_) {
184 LOG(ERROR) << "Auto enrollment error: the server asked for a larger "
185 << "modulus than the client accepts (" << power << " vs "
186 << power_limit_ << ").";
187 } else {
188 // Retry with a larger modulus.
189 SendRequest(power);
190 // Don't invoke the callback yet.
191 return;
192 }
193 } else {
194 // Server should have sent down a list of hashes to try.
195 should_auto_enroll_ = IsSerialInProtobuf(response.hashes());
196 LOG(INFO) << "Auto enrollment complete, should_auto_enroll = "
197 << should_auto_enroll_;
198 }
199
200 // Auto-enrollment done.
201 completion_callback_.Run();
202 }
203
204 void AutoEnrollmentClient::OnError(DeviceManagementBackend::ErrorCode code) {
205 LOG(ERROR) << "Auto enrollment backend error: " << code;
206 completion_callback_.Run();
207 }
208
209 bool AutoEnrollmentClient::IsSerialInProtobuf(
210 const google::protobuf::RepeatedPtrField<std::string>& hashes) {
211 for (int i = 0; i < hashes.size(); ++i) {
212 if (hashes.Get(i) == serial_number_hash_)
213 return true;
214 }
215 return false;
216 }
217
218 } // namespace policy
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698