OLD | NEW |
---|---|
(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 | |
OLD | NEW |