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