auto_enrollment_client.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
19720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block// Copyright (c) 2012 The Chromium Authors. All rights reserved.
29720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block// Use of this source code is governed by a BSD-style license that can be
39720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block// found in the LICENSE file.
49720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
59720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block#include "chrome/browser/chromeos/policy/auto_enrollment_client.h"
69720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
79720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block#include "base/bind.h"
89720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block#include "base/command_line.h"
99720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block#include "base/guid.h"
109720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block#include "base/location.h"
119720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block#include "base/logging.h"
129720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block#include "base/message_loop_proxy.h"
139720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block#include "base/metrics/histogram.h"
149720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block#include "base/prefs/pref_registry_simple.h"
159720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block#include "base/prefs/pref_service.h"
169720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block#include "base/strings/string_number_conversions.h"
179720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block#include "chrome/browser/browser_process.h"
189720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block#include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h"
199720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block#include "chrome/browser/policy/browser_policy_connector.h"
209720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block#include "chrome/browser/policy/cloud/device_management_service.h"
219720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block#include "chrome/common/chrome_switches.h"
229720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block#include "chrome/common/pref_names.h"
239720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block#include "crypto/sha2.h"
249720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
259720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Blocknamespace em = enterprise_management;
269720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
279720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Blocknamespace {
289720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
299720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block// The modulus value is sent in an int64 field in the protobuf, whose maximum
309720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block// value is 2^63-1. So 2^64 and 2^63 can't be represented as moduli and the
319720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block// max is 2^62 (when the moduli are restricted to powers-of-2).
329720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Blockconst int kMaximumPower = 62;
339720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
349720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block// Returns the int value of the |switch_name| argument, clamped to the [0, 62]
359720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block// interval. Returns 0 if the argument doesn't exist or isn't an int value.
369720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Blockint GetSanitizedArg(const std::string& switch_name) {
379720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  CommandLine* command_line = CommandLine::ForCurrentProcess();
389720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  if (!command_line->HasSwitch(switch_name))
399720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    return 0;
409720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  std::string value = command_line->GetSwitchValueASCII(switch_name);
419720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  int int_value;
429720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  if (!base::StringToInt(value, &int_value)) {
439720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    LOG(ERROR) << "Switch \"" << switch_name << "\" is not a valid int. "
449720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block               << "Defaulting to 0.";
459720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    return 0;
469720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  }
479720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  if (int_value < 0) {
489720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    LOG(ERROR) << "Switch \"" << switch_name << "\" can't be negative. "
499720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block               << "Using 0";
509720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    return 0;
519720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  }
529720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  if (int_value > kMaximumPower) {
539720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    LOG(ERROR) << "Switch \"" << switch_name << "\" can't be greater than "
549720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block               << kMaximumPower << ". Using " << kMaximumPower;
559720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    return kMaximumPower;
569720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  }
579720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  return int_value;
589720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block}
599720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
609720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block// Returns the power of the next power-of-2 starting at |value|.
619720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Blockint NextPowerOf2(int64 value) {
629720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  for (int i = 0; i <= kMaximumPower; ++i) {
639720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    if ((GG_INT64_C(1) << i) >= value)
649720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      return i;
65a721beb6d7372334152406a36e8db016650a9691Tareq A. Siraj  }
66a721beb6d7372334152406a36e8db016650a9691Tareq A. Siraj  // No other value can be represented in an int64.
67a721beb6d7372334152406a36e8db016650a9691Tareq A. Siraj  return kMaximumPower + 1;
68a721beb6d7372334152406a36e8db016650a9691Tareq A. Siraj}
69a721beb6d7372334152406a36e8db016650a9691Tareq A. Siraj
70a721beb6d7372334152406a36e8db016650a9691Tareq A. Siraj}  // namespace
719720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
729720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Blocknamespace policy {
739720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
749720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve BlockAutoEnrollmentClient::AutoEnrollmentClient(const base::Closure& callback,
759720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block                                           DeviceManagementService* service,
769720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block                                           PrefService* local_state,
779720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block                                           const std::string& serial_number,
789720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block                                           int power_initial,
799720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block                                           int power_limit)
809720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    : completion_callback_(callback),
819720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      should_auto_enroll_(false),
829720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      device_id_(base::GenerateGUID()),
839720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      power_initial_(power_initial),
84e46c9386c4f79aa40185f79a19fc5b2a7ef528b3Patrick Scott      power_limit_(power_limit),
859720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      requests_sent_(0),
869720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      device_management_service_(service),
879720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      local_state_(local_state) {
889720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  DCHECK_LE(power_initial_, power_limit_);
899720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  if (!serial_number.empty())
909720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    serial_number_hash_ = crypto::SHA256HashString(serial_number);
919720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block}
929720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
939720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve BlockAutoEnrollmentClient::~AutoEnrollmentClient() {}
949720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
959720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block// static
969720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Blockvoid AutoEnrollmentClient::RegisterPrefs(PrefRegistrySimple* registry) {
979720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  registry->RegisterBooleanPref(prefs::kShouldAutoEnroll, false);
989720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  registry->RegisterIntegerPref(prefs::kAutoEnrollmentPowerLimit, -1);
999720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block}
1009720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
1019720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block// static
1029720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Blockbool AutoEnrollmentClient::IsDisabled() {
1039720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  CommandLine* command_line = CommandLine::ForCurrentProcess();
1049720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  return
1059720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      !command_line->HasSwitch(switches::kEnterpriseEnrollmentInitialModulus) &&
1069720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      !command_line->HasSwitch(switches::kEnterpriseEnrollmentModulusLimit);
1079720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block}
1089720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
1099720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block// static
1109720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve BlockAutoEnrollmentClient* AutoEnrollmentClient::Create(
1119720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    const base::Closure& completion_callback) {
1129720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  // The client won't do anything if |service| is NULL.
1139720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  DeviceManagementService* service = NULL;
1149720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  if (IsDisabled()) {
1159720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    VLOG(1) << "Auto-enrollment is disabled";
1169720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  } else {
1179720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    std::string url = BrowserPolicyConnector::GetDeviceManagementUrl();
1189720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    if (!url.empty()) {
1199720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      service = new DeviceManagementService(url);
1209720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      service->ScheduleInitialization(0);
1219720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    }
1229720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  }
1239720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
1249720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  int power_initial = GetSanitizedArg(
1259720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      switches::kEnterpriseEnrollmentInitialModulus);
1269720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  int power_limit = GetSanitizedArg(
1279720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      switches::kEnterpriseEnrollmentModulusLimit);
1289720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  if (power_initial > power_limit) {
1299720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    LOG(ERROR) << "Initial auto-enrollment modulus is larger than the limit, "
1309720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block               << "clamping to the limit.";
1319720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    power_initial = power_limit;
1329720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  }
1339720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
1349720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  return new AutoEnrollmentClient(
1359720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      completion_callback,
1369720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      service,
1379720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      g_browser_process->local_state(),
1389720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      DeviceCloudPolicyManagerChromeOS::GetMachineID(),
1399720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      power_initial,
1409720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      power_limit);
1419720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block}
1429720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
1439720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block// static
1449720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Blockvoid AutoEnrollmentClient::CancelAutoEnrollment() {
1459720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  PrefService* local_state = g_browser_process->local_state();
1469720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  local_state->SetBoolean(prefs::kShouldAutoEnroll, false);
1479720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  local_state->CommitPendingWrite();
148e46c9386c4f79aa40185f79a19fc5b2a7ef528b3Patrick Scott}
149e46c9386c4f79aa40185f79a19fc5b2a7ef528b3Patrick Scott
150e46c9386c4f79aa40185f79a19fc5b2a7ef528b3Patrick Scottvoid AutoEnrollmentClient::Start() {
151e46c9386c4f79aa40185f79a19fc5b2a7ef528b3Patrick Scott  // Drop the previous job and reset state.
152e46c9386c4f79aa40185f79a19fc5b2a7ef528b3Patrick Scott  request_job_.reset();
153e46c9386c4f79aa40185f79a19fc5b2a7ef528b3Patrick Scott  should_auto_enroll_ = false;
154e46c9386c4f79aa40185f79a19fc5b2a7ef528b3Patrick Scott  time_start_ = base::Time();  // reset to null.
155e46c9386c4f79aa40185f79a19fc5b2a7ef528b3Patrick Scott
156e46c9386c4f79aa40185f79a19fc5b2a7ef528b3Patrick Scott  if (GetCachedDecision()) {
157e46c9386c4f79aa40185f79a19fc5b2a7ef528b3Patrick Scott    VLOG(1) << "AutoEnrollmentClient: using cached decision: "
1589720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block            << should_auto_enroll_;
1599720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  } else if (device_management_service_.get()) {
1609720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    if (serial_number_hash_.empty()) {
1619720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      LOG(ERROR) << "Failed to get the hash of the serial number, "
1629720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block                 << "will not attempt to auto-enroll.";
1639720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    } else {
1649720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      time_start_ = base::Time::Now();
1659720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      SendRequest(power_initial_);
1669720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      // Don't invoke the callback now.
1679720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      return;
1689720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    }
1699720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  }
1709720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
17189f07d6a92dab765c534266a640933ebd2f6d9e4Chris Dearman  // Auto-enrollment can't even start, so we're done.
1729720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  OnProtocolDone();
1739720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block}
1749720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
1759720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Blockvoid AutoEnrollmentClient::CancelAndDeleteSoon() {
1769720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  if (time_start_.is_null()) {
1779720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    // The client isn't running, just delete it.
1789720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    delete this;
1799720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  } else {
1809720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    // Client still running, but our owner isn't interested in the result
1819720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    // anymore. Wait until the protocol completes to measure the extra time
1829720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    // needed.
1839720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    time_extra_start_ = base::Time::Now();
1849720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    completion_callback_.Reset();
1859720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  }
1869720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block}
1879720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
1889720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Blockbool AutoEnrollmentClient::GetCachedDecision() {
1899720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  const PrefService::Preference* should_enroll_pref =
1909720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      local_state_->FindPreference(prefs::kShouldAutoEnroll);
1919720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  const PrefService::Preference* previous_limit_pref =
1929720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      local_state_->FindPreference(prefs::kAutoEnrollmentPowerLimit);
1939720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  bool should_auto_enroll = false;
1949720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  int previous_limit = -1;
1959720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
1969720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  if (!should_enroll_pref ||
1979720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      should_enroll_pref->IsDefaultValue() ||
1989720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      !should_enroll_pref->GetValue()->GetAsBoolean(&should_auto_enroll) ||
1999720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      !previous_limit_pref ||
2009720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      previous_limit_pref->IsDefaultValue() ||
2019720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      !previous_limit_pref->GetValue()->GetAsInteger(&previous_limit) ||
2029720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      power_limit_ > previous_limit) {
2039720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    return false;
2049720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  }
2059720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
2069720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  should_auto_enroll_ = should_auto_enroll;
2079720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  return true;
2089720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block}
2099720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
2109720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Blockvoid AutoEnrollmentClient::SendRequest(int power) {
211e46c9386c4f79aa40185f79a19fc5b2a7ef528b3Patrick Scott  if (power < 0 || power > power_limit_ || serial_number_hash_.empty()) {
212e46c9386c4f79aa40185f79a19fc5b2a7ef528b3Patrick Scott    NOTREACHED();
2139720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    OnProtocolDone();
2149720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    return;
2159720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  }
2169720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
2179720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  requests_sent_++;
2189720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
2199720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  // Only power-of-2 moduli are supported for now. These are computed by taking
2209720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  // the lower |power| bits of the hash.
2219720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  uint64 remainder = 0;
2229720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  for (int i = 0; 8 * i < power; ++i) {
223e46c9386c4f79aa40185f79a19fc5b2a7ef528b3Patrick Scott    uint64 byte = serial_number_hash_[31 - i] & 0xff;
224e46c9386c4f79aa40185f79a19fc5b2a7ef528b3Patrick Scott    remainder = remainder | (byte << (8 * i));
225e46c9386c4f79aa40185f79a19fc5b2a7ef528b3Patrick Scott  }
2269720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  remainder = remainder & ((GG_UINT64_C(1) << power) - 1);
2279720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
2289720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  request_job_.reset(
2299720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      device_management_service_->CreateJob(
2309720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block          DeviceManagementRequestJob::TYPE_AUTO_ENROLLMENT));
2319720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  request_job_->SetClientID(device_id_);
2329720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  em::DeviceAutoEnrollmentRequest* request =
2339720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      request_job_->GetRequest()->mutable_auto_enrollment_request();
2349720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  request->set_remainder(remainder);
2359720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  request->set_modulus(GG_INT64_C(1) << power);
2369720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  request_job_->Start(base::Bind(&AutoEnrollmentClient::OnRequestCompletion,
2379720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block                                 base::Unretained(this)));
2389720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block}
2399720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
2409720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Blockvoid AutoEnrollmentClient::OnRequestCompletion(
2419720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    DeviceManagementStatus status,
2429720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    const em::DeviceManagementResponse& response) {
2439720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  if (status != DM_STATUS_SUCCESS || !response.has_auto_enrollment_response()) {
2449720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    LOG(ERROR) << "Auto enrollment error: " << status;
2459720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    OnProtocolDone();
2469720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    return;
2479720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  }
2489720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
2499720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  const em::DeviceAutoEnrollmentResponse& enrollment_response =
2509720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      response.auto_enrollment_response();
2519720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  if (enrollment_response.has_expected_modulus()) {
2529720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    // Server is asking us to retry with a different modulus.
2539720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    int64 modulus = enrollment_response.expected_modulus();
2549720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    int power = NextPowerOf2(modulus);
255e46c9386c4f79aa40185f79a19fc5b2a7ef528b3Patrick Scott    if ((GG_INT64_C(1) << power) != modulus) {
2569720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      LOG(WARNING) << "Auto enrollment: the server didn't ask for a power-of-2 "
2579720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block                   << "modulus. Using the closest power-of-2 instead "
2589720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block                   << "(" << modulus << " vs 2^" << power << ")";
2599720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    }
2609720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    if (requests_sent_ >= 2) {
2619720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      LOG(ERROR) << "Auto enrollment error: already retried with an updated "
2629720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block                 << "modulus but the server asked for a new one again: "
2639720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block                 << power;
2649720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    } else if (power > power_limit_) {
2659720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      LOG(ERROR) << "Auto enrollment error: the server asked for a larger "
266e46c9386c4f79aa40185f79a19fc5b2a7ef528b3Patrick Scott                 << "modulus than the client accepts (" << power << " vs "
2679720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block                 << power_limit_ << ").";
2689720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    } else {
2699720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      // Retry at most once with the modulus that the server requested.
2709720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      if (power <= power_initial_) {
2719720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block        LOG(WARNING) << "Auto enrollment: the server asked to use a modulus ("
2729720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block                     << power << ") that isn't larger than the first used ("
2739720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block                     << power_initial_ << "). Retrying anyway.";
2749720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      }
2759720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      SendRequest(power);
2769720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      // Don't invoke the callback yet.
2779720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      return;
2789720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    }
2799720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  } else {
2809720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    // Server should have sent down a list of hashes to try.
2819720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    should_auto_enroll_ = IsSerialInProtobuf(enrollment_response.hash());
2829720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    LOG(INFO) << "Auto enrollment complete, should_auto_enroll = "
2839720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block              << should_auto_enroll_;
2849720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  }
2859720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
2869720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  // Auto-enrollment done.
2879720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  OnProtocolDone();
2889720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block}
2899720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
2909720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Blockbool AutoEnrollmentClient::IsSerialInProtobuf(
2919720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      const google::protobuf::RepeatedPtrField<std::string>& hashes) {
2929720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  for (int i = 0; i < hashes.size(); ++i) {
2939720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    if (hashes.Get(i) == serial_number_hash_)
2949720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block      return true;
2959720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  }
2969720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  return false;
297e46c9386c4f79aa40185f79a19fc5b2a7ef528b3Patrick Scott}
2989720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
2999720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Blockvoid AutoEnrollmentClient::OnProtocolDone() {
3009720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  static const char* kProtocolTime = "Enterprise.AutoEnrollmentProtocolTime";
3019720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  static const char* kExtraTime = "Enterprise.AutoEnrollmentExtraTime";
3029720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  // The mininum time can't be 0, must be at least 1.
3039720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  static const base::TimeDelta kMin = base::TimeDelta::FromMilliseconds(1);
3049720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  static const base::TimeDelta kMax = base::TimeDelta::FromMinutes(5);
3059720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  // However, 0 can still be sampled.
3069720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  static const base::TimeDelta kZero = base::TimeDelta::FromMilliseconds(0);
3079720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  static const int kBuckets = 50;
3089720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
3099720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  base::Time now = base::Time::Now();
3109720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  if (!time_start_.is_null()) {
3119720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    base::TimeDelta delta = now - time_start_;
3129720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    UMA_HISTOGRAM_CUSTOM_TIMES(kProtocolTime, delta, kMin, kMax, kBuckets);
3139720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    time_start_ = base::Time();
3149720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
3159720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    // The decision is cached only if the protocol was actually started, which
3169720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    // is the case only if |time_start_| was not null.
3179720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    local_state_->SetBoolean(prefs::kShouldAutoEnroll, should_auto_enroll_);
3189720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    local_state_->SetInteger(prefs::kAutoEnrollmentPowerLimit, power_limit_);
3199720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    local_state_->CommitPendingWrite();
3209720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  }
3219720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  base::TimeDelta delta = kZero;
3229720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  if (!time_extra_start_.is_null()) {
3239720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    // CancelAndDeleteSoon() was invoked before.
3249720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    delta = now - time_extra_start_;
3259720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE, this);
3269720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    time_extra_start_ = base::Time();
3279720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  }
3289720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  // This samples |kZero| when there was no need for extra time, so that we can
3299720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  // measure the ratio of users that succeeded without needing a delay to the
3309720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  // total users going through OOBE.
3319720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  UMA_HISTOGRAM_CUSTOM_TIMES(kExtraTime, delta, kMin, kMax, kBuckets);
3329720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
3339720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block  if (!completion_callback_.is_null())
3349720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block    completion_callback_.Run();
3359720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block}
3369720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block
3379720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block}  // namespace policy
3389720d5f59b9c1f5d1b9ecbc9173dbcb71bd557beSteve Block