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

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: 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::Delegate::~Delegate() {}
64
65 AutoEnrollmentClient::AutoEnrollmentClient(Delegate* delegate,
66 DeviceManagementService* service,
67 const std::string& serial_number,
68 int power_initial,
69 int power_limit)
70 : delegate_(delegate),
71 should_auto_enroll_(false),
72 device_id_(guid::GenerateGUID()),
73 power_initial_(power_initial),
74 power_limit_(power_limit),
75 device_management_service_(service) {
76 DCHECK_LE(power_initial_, power_limit_);
77 if (!serial_number.empty())
78 serial_number_hash_ = crypto::SHA256HashString(serial_number);
79 }
80
81 AutoEnrollmentClient::~AutoEnrollmentClient() {}
82
83 // static
84 AutoEnrollmentClient* AutoEnrollmentClient::Create(Delegate* delegate) {
85 CommandLine* command_line = CommandLine::ForCurrentProcess();
86
87 std::string url =
88 command_line->GetSwitchValueASCII(switches::kDeviceManagementUrl);
89 DeviceManagementService* service = NULL;
90 // This client won't do anything if there's no dmserver url.
91 if (!url.empty()) {
92 service = new DeviceManagementService(url);
93 service->ScheduleInitialization(0);
94 }
95
96 int power_initial = GetSanitizedArg(switches::kAutoEnrollmentInitialModulus);
97 int power_limit = GetSanitizedArg(switches::kAutoEnrollmentModulusLimit);
98 if (power_initial > power_limit) {
99 LOG(ERROR) << "Initial auto-enrollment modulus is larger than the limit, "
100 << "clamping to the limit.";
101 power_initial = power_limit;
102 }
103
104 return new AutoEnrollmentClient(delegate,
105 service,
106 BrowserPolicyConnector::GetSerialNumber(),
107 power_initial,
108 power_limit);
109 }
110
111 void AutoEnrollmentClient::Start() {
112 // Drop the previous backend and reset state.
113 device_management_backend_.reset();
114 should_auto_enroll_ = false;
115
116 if (device_management_service_.get()) {
117 if (serial_number_hash_.empty()) {
118 LOG(ERROR) << "Failed to get the hash of the serial number, "
119 << "will not attempt to auto-enroll.";
120 } else {
121 // Create a new backend and fire the initial request.
122 device_management_backend_.reset(
123 device_management_service_->CreateBackend());
124 SendRequest(power_initial_);
125 // Don't notify the delegate now.
126 return;
127 }
128 }
129
130 // Auto-enrollment can't even start, notify the delegate that we're done.
131 if (delegate_)
132 delegate_->OnAutoEnrollmentComplete(this);
133 }
134
135 void AutoEnrollmentClient::SendRequest(int power) {
136 DCHECK(power >= 0 && power <= power_limit_);
137 DCHECK(!serial_number_hash_.empty());
Mattias Nissler (ping if slow) 2011/12/09 15:40:52 These should probably be CHECKs or if (!cond) { NO
Joao da Silva 2011/12/09 17:26:06 Done. Using an "if", since a CHECK would kill a pr
138
139 last_power_used_ = power;
140
141 // Only power-of-2 moduli are supported for now. These are computed by taking
142 // the lower |power| bits of the hash.
143 uint64 remainder = 0;
144 for (int i = 0; 8*i < power; ++i) {
Mattias Nissler (ping if slow) 2011/12/09 15:40:52 space around *
Joao da Silva 2011/12/09 17:26:06 Done.
145 uint64 byte = serial_number_hash_[31 - i] & 0xff;
146 remainder = remainder | (byte << (8 * i));
147 }
148 remainder = remainder & (((uint64) 1 << power) - 1);
Mattias Nissler (ping if slow) 2011/12/09 15:40:52 instead of the cast (which shouldn't be C-style),
Joao da Silva 2011/12/09 17:26:06 Done.
149
150 em::DeviceAutoEnrollmentRequest request;
151 request.set_remainder(remainder);
152 request.set_modulus((int64) 1 << power);
153
154 device_management_backend_->ProcessAutoEnrollmentRequest(device_id_,
155 request,
156 this);
157 }
158
159 void AutoEnrollmentClient::HandleAutoEnrollmentResponse(
160 const em::DeviceAutoEnrollmentResponse& response) {
161 if (response.has_modulus()) {
162 // Server is asking us to retry with a different modulus.
163 int64 modulus = response.modulus();
164 int64 last_modulus_used = 1 << last_power_used_;
165 int power = NextPowerOf2(modulus);
166 if ((1 << power) != modulus) {
167 LOG(WARNING) << "Auto enrollment: the server didn't ask for a power-of-2 "
168 << "modulus. Using the closest power-of-2 instead "
169 << "(" << modulus << " vs 2^" << power << ")";
170 }
171 if (modulus < last_modulus_used) {
172 LOG(ERROR) << "Auto enrollment error: the server asked for a smaller "
173 << "modulus than we used.";
174 } else if (modulus == last_modulus_used) {
175 LOG(ERROR) << "Auto enrollment error: the server asked for the same "
176 << "modulus that was already used.";
177 } else if (last_power_used_ != power_initial_) {
178 LOG(ERROR) << "Auto enrollment error: already retried with an updated "
179 << "modulus but the server asked for a new one.";
180 } else if (power > power_limit_) {
181 LOG(ERROR) << "Auto enrollment error: the server asked for a larger "
182 << "modulus than the client accepts (" << power << " vs "
183 << power_limit_ << ").";
184 } else {
185 // Retry with a larger modulus.
186 SendRequest(power);
187 // Don't notify the delegate yet.
188 return;
189 }
190 } else {
191 // Server should have sent down a list of hashes to try.
192 should_auto_enroll_ = IsSerialInProtobuf(response.hashes());
193 LOG(INFO) << "Auto enrollment complete, should_auto_enroll = "
194 << should_auto_enroll_;
195 }
196
197 // Auto-enrollment done.
198 if (delegate_)
199 delegate_->OnAutoEnrollmentComplete(this);
200 }
201
202 void AutoEnrollmentClient::OnError(DeviceManagementBackend::ErrorCode code) {
203 LOG(ERROR) << "Auto enrollment backend error: " << code;
204 if (delegate_)
205 delegate_->OnAutoEnrollmentComplete(this);
206 }
207
208 bool AutoEnrollmentClient::IsSerialInProtobuf(
209 const google::protobuf::RepeatedPtrField<std::string>& hashes) {
210 for (int i = 0; i < hashes.size(); ++i) {
Mattias Nissler (ping if slow) 2011/12/09 15:40:52 Does RepeatedPtrField have iterators that work wit
Joao da Silva 2011/12/09 17:26:06 Yes, it has iterators that should work with binary
Joao da Silva 2011/12/10 13:17:18 Update from Bin: this data is not sorted
211 if (hashes.Get(i) == serial_number_hash_)
212 return true;
213 }
214 return false;
215 }
216
217 } // namespace policy
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698