Chromium Code Reviews| Index: chrome/browser/policy/auto_enrollment_client.cc |
| diff --git a/chrome/browser/policy/auto_enrollment_client.cc b/chrome/browser/policy/auto_enrollment_client.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..ab4764a84ca16d9089a7df8fb3eddc2c55c3585e |
| --- /dev/null |
| +++ b/chrome/browser/policy/auto_enrollment_client.cc |
| @@ -0,0 +1,217 @@ |
| +// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/browser/policy/auto_enrollment_client.h" |
| + |
| +#include "base/command_line.h" |
| +#include "base/logging.h" |
| +#include "base/string_number_conversions.h" |
| +#include "chrome/browser/policy/browser_policy_connector.h" |
| +#include "chrome/browser/policy/device_management_service.h" |
| +#include "chrome/common/chrome_switches.h" |
| +#include "chrome/common/guid.h" |
| +#include "crypto/sha2.h" |
| + |
| +namespace { |
| + |
| +// The modulus value is sent in an int64 field in the protobuf, whose maximum |
| +// value is 2^63-1. So 2^64 and 2^63 can't be represented as moduli and the |
| +// max is 2^62 (when the moduli are restricted to powers-of-2). |
| +const int kMaximumPower = 62; |
| + |
| +// Returns the int value of the |switch_name| argument, clamped to the [0, 62] |
| +// interval. Returns 0 if the argument doesn't exist or isn't an int value. |
| +int GetSanitizedArg(const std::string& switch_name) { |
| + CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| + if (!command_line->HasSwitch(switch_name)) |
| + return 0; |
| + std::string value = command_line->GetSwitchValueASCII(switch_name); |
| + int int_value; |
| + if (!base::StringToInt(value, &int_value)) { |
| + LOG(ERROR) << "Switch \"" << switch_name << "\" is not a valid int. " |
| + << "Defaulting to 0."; |
| + return 0; |
| + } |
| + if (int_value < 0) { |
| + LOG(ERROR) << "Switch \"" << switch_name << "\" can't be negative. " |
| + << "Using 0"; |
| + return 0; |
| + } |
| + if (int_value > kMaximumPower) { |
| + LOG(ERROR) << "Switch \"" << switch_name << "\" can't be greater than " |
| + << kMaximumPower << ". Using " << kMaximumPower; |
| + return kMaximumPower; |
| + } |
| + return int_value; |
| +} |
| + |
| +// Returns the power of the next power-of-2 starting at |value|. |
| +int NextPowerOf2(int64 value) { |
| + for (int i = 0; i <= kMaximumPower; ++i) { |
| + if ((1 << i) >= value) |
| + return i; |
| + } |
| + // No other value can be represented in an int64. |
| + return kMaximumPower + 1; |
| +} |
| + |
| +} // namespace |
| + |
| +namespace policy { |
| + |
| +AutoEnrollmentClient::Delegate::~Delegate() {} |
| + |
| +AutoEnrollmentClient::AutoEnrollmentClient(Delegate* delegate, |
| + DeviceManagementService* service, |
| + const std::string& serial_number, |
| + int power_initial, |
| + int power_limit) |
| + : delegate_(delegate), |
| + should_auto_enroll_(false), |
| + device_id_(guid::GenerateGUID()), |
| + power_initial_(power_initial), |
| + power_limit_(power_limit), |
| + device_management_service_(service) { |
| + DCHECK_LE(power_initial_, power_limit_); |
| + if (!serial_number.empty()) |
| + serial_number_hash_ = crypto::SHA256HashString(serial_number); |
| +} |
| + |
| +AutoEnrollmentClient::~AutoEnrollmentClient() {} |
| + |
| +// static |
| +AutoEnrollmentClient* AutoEnrollmentClient::Create(Delegate* delegate) { |
| + CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| + |
| + std::string url = |
| + command_line->GetSwitchValueASCII(switches::kDeviceManagementUrl); |
| + DeviceManagementService* service = NULL; |
| + // This client won't do anything if there's no dmserver url. |
| + if (!url.empty()) { |
| + service = new DeviceManagementService(url); |
| + service->ScheduleInitialization(0); |
| + } |
| + |
| + int power_initial = GetSanitizedArg(switches::kAutoEnrollmentInitialModulus); |
| + int power_limit = GetSanitizedArg(switches::kAutoEnrollmentModulusLimit); |
| + if (power_initial > power_limit) { |
| + LOG(ERROR) << "Initial auto-enrollment modulus is larger than the limit, " |
| + << "clamping to the limit."; |
| + power_initial = power_limit; |
| + } |
| + |
| + return new AutoEnrollmentClient(delegate, |
| + service, |
| + BrowserPolicyConnector::GetSerialNumber(), |
| + power_initial, |
| + power_limit); |
| +} |
| + |
| +void AutoEnrollmentClient::Start() { |
| + // Drop the previous backend and reset state. |
| + device_management_backend_.reset(); |
| + should_auto_enroll_ = false; |
| + |
| + if (device_management_service_.get()) { |
| + if (serial_number_hash_.empty()) { |
| + LOG(ERROR) << "Failed to get the hash of the serial number, " |
| + << "will not attempt to auto-enroll."; |
| + } else { |
| + // Create a new backend and fire the initial request. |
| + device_management_backend_.reset( |
| + device_management_service_->CreateBackend()); |
| + SendRequest(power_initial_); |
| + // Don't notify the delegate now. |
| + return; |
| + } |
| + } |
| + |
| + // Auto-enrollment can't even start, notify the delegate that we're done. |
| + if (delegate_) |
| + delegate_->OnAutoEnrollmentComplete(this); |
| +} |
| + |
| +void AutoEnrollmentClient::SendRequest(int power) { |
| + DCHECK(power >= 0 && power <= power_limit_); |
| + 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
|
| + |
| + last_power_used_ = power; |
| + |
| + // Only power-of-2 moduli are supported for now. These are computed by taking |
| + // the lower |power| bits of the hash. |
| + uint64 remainder = 0; |
| + 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.
|
| + uint64 byte = serial_number_hash_[31 - i] & 0xff; |
| + remainder = remainder | (byte << (8 * i)); |
| + } |
| + 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.
|
| + |
| + em::DeviceAutoEnrollmentRequest request; |
| + request.set_remainder(remainder); |
| + request.set_modulus((int64) 1 << power); |
| + |
| + device_management_backend_->ProcessAutoEnrollmentRequest(device_id_, |
| + request, |
| + this); |
| +} |
| + |
| +void AutoEnrollmentClient::HandleAutoEnrollmentResponse( |
| + const em::DeviceAutoEnrollmentResponse& response) { |
| + if (response.has_modulus()) { |
| + // Server is asking us to retry with a different modulus. |
| + int64 modulus = response.modulus(); |
| + int64 last_modulus_used = 1 << last_power_used_; |
| + int power = NextPowerOf2(modulus); |
| + if ((1 << power) != modulus) { |
| + LOG(WARNING) << "Auto enrollment: the server didn't ask for a power-of-2 " |
| + << "modulus. Using the closest power-of-2 instead " |
| + << "(" << modulus << " vs 2^" << power << ")"; |
| + } |
| + if (modulus < last_modulus_used) { |
| + LOG(ERROR) << "Auto enrollment error: the server asked for a smaller " |
| + << "modulus than we used."; |
| + } else if (modulus == last_modulus_used) { |
| + LOG(ERROR) << "Auto enrollment error: the server asked for the same " |
| + << "modulus that was already used."; |
| + } else if (last_power_used_ != power_initial_) { |
| + LOG(ERROR) << "Auto enrollment error: already retried with an updated " |
| + << "modulus but the server asked for a new one."; |
| + } else if (power > power_limit_) { |
| + LOG(ERROR) << "Auto enrollment error: the server asked for a larger " |
| + << "modulus than the client accepts (" << power << " vs " |
| + << power_limit_ << ")."; |
| + } else { |
| + // Retry with a larger modulus. |
| + SendRequest(power); |
| + // Don't notify the delegate yet. |
| + return; |
| + } |
| + } else { |
| + // Server should have sent down a list of hashes to try. |
| + should_auto_enroll_ = IsSerialInProtobuf(response.hashes()); |
| + LOG(INFO) << "Auto enrollment complete, should_auto_enroll = " |
| + << should_auto_enroll_; |
| + } |
| + |
| + // Auto-enrollment done. |
| + if (delegate_) |
| + delegate_->OnAutoEnrollmentComplete(this); |
| +} |
| + |
| +void AutoEnrollmentClient::OnError(DeviceManagementBackend::ErrorCode code) { |
| + LOG(ERROR) << "Auto enrollment backend error: " << code; |
| + if (delegate_) |
| + delegate_->OnAutoEnrollmentComplete(this); |
| +} |
| + |
| +bool AutoEnrollmentClient::IsSerialInProtobuf( |
| + const google::protobuf::RepeatedPtrField<std::string>& hashes) { |
| + 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
|
| + if (hashes.Get(i) == serial_number_hash_) |
| + return true; |
| + } |
| + return false; |
| +} |
| + |
| +} // namespace policy |