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

Unified 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 side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698