普通文本  |  937行  |  31.46 KB

// 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/chromeos/login/signed_settings.h"

#include <string>
#include <vector>

#include "base/memory/ref_counted.h"
#include "base/stringprintf.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/cros/login_library.h"
#include "chrome/browser/chromeos/cros_settings_names.h"
#include "chrome/browser/chromeos/login/authenticator.h"
#include "chrome/browser/chromeos/login/ownership_service.h"
#include "chrome/browser/chromeos/login/signed_settings_temp_storage.h"
#include "chrome/browser/policy/proto/chrome_device_policy.pb.h"
#include "chrome/browser/policy/proto/device_management_backend.pb.h"
#include "content/browser/browser_thread.h"

namespace chromeos {
using google::protobuf::RepeatedPtrField;
using std::string;

// static
const char SignedSettings::kDevicePolicyType[] = "google/chromeos/device";

SignedSettings::Relay::Relay(SignedSettings* s) : settings_(s) {
}

SignedSettings::Relay::~Relay() {}

void SignedSettings::Relay::OnSettingsOpCompleted(
    SignedSettings::ReturnCode code,
    const em::PolicyFetchResponse& value) {
  if (code == SignedSettings::SUCCESS) {
    settings_->Execute();
    return;
  }
  settings_->Fail(code);
}

SignedSettings::SignedSettings()
    : service_(OwnershipService::GetSharedInstance()),
      relay_(NULL),
      polfetcher_(NULL) {
}

SignedSettings::~SignedSettings() {}

void SignedSettings::TryToFetchPolicyAndCallBack() {
  relay_.reset(new Relay(this));
  polfetcher_ = SignedSettings::CreateRetrievePolicyOp(relay_.get());
  polfetcher_->set_service(service_);
  polfetcher_->Execute();
}

// static
bool SignedSettings::PolicyIsSane(const em::PolicyFetchResponse& value,
                                  em::PolicyData* poldata) {
  if (value.has_policy_data()) {
    poldata->ParseFromString(value.policy_data());
    if (poldata->has_policy_type() &&
        poldata->policy_type() == kDevicePolicyType &&
        poldata->has_policy_value()) {
      return true;
    }
  }
  return false;
}

// static
SignedSettings::ReturnCode SignedSettings::MapKeyOpCode(
    OwnerManager::KeyOpCode return_code) {
  return (return_code == OwnerManager::KEY_UNAVAILABLE ?
          KEY_UNAVAILABLE : BAD_SIGNATURE);
}

// static
bool SignedSettings::EnumerateWhitelist(std::vector<std::string>* whitelisted) {
  OwnershipService* service = OwnershipService::GetSharedInstance();
  if (!service->has_cached_policy())
    return false;
  em::ChromeDeviceSettingsProto pol;
  pol.ParseFromString(service->cached_policy().policy_value());
  if (!pol.has_user_whitelist())
    return false;

  const RepeatedPtrField<std::string>& whitelist =
      pol.user_whitelist().user_whitelist();
  for (RepeatedPtrField<std::string>::const_iterator it = whitelist.begin();
       it != whitelist.end();
       ++it) {
    whitelisted->push_back(*it);
  }
  return true;
}

class CheckWhitelistOp : public SignedSettings {
 public:
  CheckWhitelistOp(const std::string& email,
                   SignedSettings::Delegate<bool>* d);
  virtual ~CheckWhitelistOp();
  void Execute();
  void Fail(SignedSettings::ReturnCode code);
  void Succeed(bool value);
  // Implementation of OwnerManager::Delegate
  void OnKeyOpComplete(const OwnerManager::KeyOpCode return_code,
                       const std::vector<uint8>& payload);

 private:
  bool LookUpInPolicy(const std::string& email);
  // Always call d_->OnSettingOpCompleted() via this call.
  // It guarantees that the callback will not be triggered until _after_
  // Execute() returns, which is implicitly assumed by SignedSettingsHelper
  // in some cases.
  void PerformCallback(SignedSettings::ReturnCode code, bool value);

  const std::string email_;
  SignedSettings::Delegate<bool>* d_;
};

class WhitelistOp : public SignedSettings,
                    public SignedSettings::Delegate<bool> {
 public:
  WhitelistOp(const std::string& email,
              bool add_to_whitelist,
              SignedSettings::Delegate<bool>* d);
  virtual ~WhitelistOp();
  void Execute();
  void Fail(SignedSettings::ReturnCode code);
  void Succeed(bool value);
  // Implementation of OwnerManager::Delegate
  void OnKeyOpComplete(const OwnerManager::KeyOpCode return_code,
                       const std::vector<uint8>& payload);
  // Implementation of SignedSettings::Delegate
  void OnSettingsOpCompleted(ReturnCode code, bool value);

 private:
  void ModifyWhitelist(const std::string& email,
                       bool add_to_whitelist,
                       em::UserWhitelistProto* whitelist_proto);
  // Always call d_->OnSettingOpCompleted() via this call.
  // It guarantees that the callback will not be triggered until _after_
  // Execute() returns, which is implicitly assumed by SignedSettingsHelper
  // in some cases.
  void PerformCallback(SignedSettings::ReturnCode code, bool value);

  const std::string email_;
  const bool add_to_whitelist_;
  SignedSettings::Delegate<bool>* d_;
  em::PolicyFetchResponse to_store_;
  scoped_refptr<SignedSettings> store_op_;
};

class StorePropertyOp : public SignedSettings,
                        public SignedSettings::Delegate<bool> {
 public:
  StorePropertyOp(const std::string& name,
                  const std::string& value,
                  SignedSettings::Delegate<bool>* d);
  virtual ~StorePropertyOp();
  void Execute();
  void Fail(SignedSettings::ReturnCode code);
  void Succeed(bool value);
  // Implementation of OwnerManager::Delegate
  void OnKeyOpComplete(const OwnerManager::KeyOpCode return_code,
                       const std::vector<uint8>& payload);
  // Implementation of SignedSettings::Delegate
  void OnSettingsOpCompleted(ReturnCode code, bool value);

 private:
  void SetInPolicy(const std::string& prop,
                   const std::string& value,
                   em::PolicyData* poldata);
  // Always call d_->OnSettingOpCompleted() via this call.
  // It guarantees that the callback will not be triggered until _after_
  // Execute() returns, which is implicitly assumed by SignedSettingsHelper
  // in some cases.
  void PerformCallback(SignedSettings::ReturnCode code, bool value);

  std::string name_;
  std::string value_;
  SignedSettings::Delegate<bool>* d_;
  em::PolicyFetchResponse to_store_;
  scoped_refptr<SignedSettings> store_op_;
};

class RetrievePropertyOp : public SignedSettings {
 public:
  RetrievePropertyOp(const std::string& name,
                     SignedSettings::Delegate<std::string>* d);
  virtual ~RetrievePropertyOp();
  void Execute();
  void Fail(SignedSettings::ReturnCode code);
  void Succeed(const std::string& value);
  // Implementation of OwnerManager::Delegate::OnKeyOpComplete()
  void OnKeyOpComplete(const OwnerManager::KeyOpCode return_code,
                       const std::vector<uint8>& payload);

 private:
  static const char* kVeritas[];

  std::string LookUpInPolicy(const std::string& prop);
  // Always call d_->OnSettingOpCompleted() via this call.
  // It guarantees that the callback will not be triggered until _after_
  // Execute() returns, which is implicitly assumed by SignedSettingsHelper
  // in some cases.
  void PerformCallback(SignedSettings::ReturnCode code,
                       const std::string& value);

  std::string name_;
  std::string value_;
  SignedSettings::Delegate<std::string>* d_;
};

class StorePolicyOp : public SignedSettings {
 public:
  StorePolicyOp(em::PolicyFetchResponse* policy,
                SignedSettings::Delegate<bool>* d);
  virtual ~StorePolicyOp();
  void Execute();
  void Fail(SignedSettings::ReturnCode code);
  void Succeed(bool value);
  // Implementation of OwnerManager::Delegate
  void OnKeyOpComplete(const OwnerManager::KeyOpCode return_code,
                       const std::vector<uint8>& payload);

 private:
  static void OnBoolComplete(void* delegate, bool success);
  // Always call d_->OnSettingOpCompleted() via this call.
  // It guarantees that the callback will not be triggered until _after_
  // Execute() returns, which is implicitly assumed by SignedSettingsHelper
  // in some cases.
  void PerformCallback(SignedSettings::ReturnCode code, bool value);

  em::PolicyFetchResponse* policy_;
  SignedSettings::Delegate<bool>* d_;

  void RequestStorePolicy();
};

class RetrievePolicyOp : public SignedSettings {
 public:
  explicit RetrievePolicyOp(
      SignedSettings::Delegate<const em::PolicyFetchResponse&>* d);
  virtual ~RetrievePolicyOp();
  void Execute();
  void Fail(SignedSettings::ReturnCode code);
  void Succeed(const em::PolicyFetchResponse& value);
  // Implementation of OwnerManager::Delegate
  void OnKeyOpComplete(const OwnerManager::KeyOpCode return_code,
                       const std::vector<uint8>& payload);

 private:
  static void OnStringComplete(void* delegate,
                               const char* policy,
                               const unsigned int len);
  // Always call d_->OnSettingOpCompleted() via this call.
  // It guarantees that the callback will not be triggered until _after_
  // Execute() returns, which is implicitly assumed by SignedSettingsHelper
  // in some cases.
  void PerformCallback(SignedSettings::ReturnCode code,
                       const em::PolicyFetchResponse& value);

  void ProcessPolicy(const char* out, const unsigned int len);

  em::PolicyFetchResponse policy_;
  SignedSettings::Delegate<const em::PolicyFetchResponse&>* d_;
};

// static
SignedSettings* SignedSettings::CreateCheckWhitelistOp(
    const std::string& email,
    SignedSettings::Delegate<bool>* d) {
  DCHECK(d != NULL);
  return new CheckWhitelistOp(Authenticator::Canonicalize(email), d);
}

// static
SignedSettings* SignedSettings::CreateWhitelistOp(
    const std::string& email,
    bool add_to_whitelist,
    SignedSettings::Delegate<bool>* d) {
  DCHECK(d != NULL);
  return new WhitelistOp(Authenticator::Canonicalize(email),
                         add_to_whitelist,
                         d);
}

// static
SignedSettings* SignedSettings::CreateStorePropertyOp(
    const std::string& name,
    const std::string& value,
    SignedSettings::Delegate<bool>* d) {
  DCHECK(d != NULL);
  return new StorePropertyOp(name, value, d);
}

// static
SignedSettings* SignedSettings::CreateRetrievePropertyOp(
    const std::string& name,
    SignedSettings::Delegate<std::string>* d) {
  DCHECK(d != NULL);
  return new RetrievePropertyOp(name, d);
}

// static
SignedSettings* SignedSettings::CreateStorePolicyOp(
    em::PolicyFetchResponse* policy,
    SignedSettings::Delegate<bool>* d) {
  DCHECK(d != NULL);
  DCHECK(policy != NULL);
  return new StorePolicyOp(policy, d);
}

// static
SignedSettings* SignedSettings::CreateRetrievePolicyOp(
    SignedSettings::Delegate<const em::PolicyFetchResponse&>* d) {
  DCHECK(d != NULL);
  return new RetrievePolicyOp(d);
}

CheckWhitelistOp::CheckWhitelistOp(const std::string& email,
                                   SignedSettings::Delegate<bool>* d)
    : email_(email),
      d_(d) {
}

CheckWhitelistOp::~CheckWhitelistOp() {}

void CheckWhitelistOp::Execute() {
  CHECK(chromeos::CrosLibrary::Get()->EnsureLoaded());
  std::vector<uint8> sig;
  std::string email_to_check = email_;
  if (!service_->has_cached_policy()) {
    TryToFetchPolicyAndCallBack();
    return;
  }
  if (LookUpInPolicy(email_to_check)) {
    VLOG(2) << "Whitelist check was successful for " << email_to_check;
    Succeed(true);
    return;
  }
  // If the exact match was not found try to match against a wildcard entry
  // where the domain only matches (e.g. *@example.com). In theory we should
  // always have correctly formated mail address here but a little precaution
  // does no harm.
  if (email_.find('@') != std::string::npos) {
    email_to_check = std::string("*").append(email_.substr(email_.find('@')));
    if (LookUpInPolicy(email_to_check)) {
      VLOG(2) << "Whitelist check was successful for " << email_to_check;
      Succeed(true);
      return;
    }
  }
  Fail(NOT_FOUND);
  return;
}

void CheckWhitelistOp::Fail(SignedSettings::ReturnCode code) {
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(this, &CheckWhitelistOp::PerformCallback, code, false));
}

void CheckWhitelistOp::Succeed(bool value) {
  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
                          NewRunnableMethod(this,
                                            &CheckWhitelistOp::PerformCallback,
                                            SUCCESS, value));
}

void CheckWhitelistOp::OnKeyOpComplete(
    const OwnerManager::KeyOpCode return_code,
    const std::vector<uint8>& payload) {
  NOTREACHED();
  // Ensure we're on the UI thread, due to the need to send DBus traffic.
  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    BrowserThread::PostTask(
        BrowserThread::UI, FROM_HERE,
        NewRunnableMethod(this,
                          &CheckWhitelistOp::OnKeyOpComplete,
                          return_code, payload));
    return;
  }
  if (return_code == OwnerManager::SUCCESS) {
    VLOG(2) << "Whitelist check was successful.";
    Succeed(true);
  } else {
    VLOG(2) << "Whitelist check failed.";
    Fail(SignedSettings::MapKeyOpCode(return_code));
  }
}

bool CheckWhitelistOp::LookUpInPolicy(const std::string& email) {
  em::ChromeDeviceSettingsProto pol;
  pol.ParseFromString(service_->cached_policy().policy_value());
  if (!pol.has_user_whitelist())
    return false;

  const RepeatedPtrField<std::string>& whitelist =
      pol.user_whitelist().user_whitelist();
  for (RepeatedPtrField<std::string>::const_iterator it = whitelist.begin();
       it != whitelist.end();
       ++it) {
    if (email == *it)
      return true;
  }
  return false;
}

void CheckWhitelistOp::PerformCallback(SignedSettings::ReturnCode code,
                                       bool value) {
  d_->OnSettingsOpCompleted(code, value);
}

WhitelistOp::WhitelistOp(const std::string& email,
                         bool add_to_whitelist,
                         SignedSettings::Delegate<bool>* d)
    : email_(email),
      add_to_whitelist_(add_to_whitelist),
      d_(d) {
}

WhitelistOp::~WhitelistOp() {}

void WhitelistOp::Execute() {
  if (!service_->has_cached_policy()) {
    TryToFetchPolicyAndCallBack();
    return;
  }
  em::PolicyData to_sign;
  to_sign.CheckTypeAndMergeFrom(service_->cached_policy());
  em::ChromeDeviceSettingsProto pol;
  pol.ParseFromString(to_sign.policy_value());
  em::UserWhitelistProto* whitelist_proto = pol.mutable_user_whitelist();
  ModifyWhitelist(email_, add_to_whitelist_, whitelist_proto);
  to_sign.set_policy_value(pol.SerializeAsString());
  to_store_.set_policy_data(to_sign.SerializeAsString());
  service_->StartSigningAttempt(to_store_.policy_data(), this);
}

void WhitelistOp::Fail(SignedSettings::ReturnCode code) {
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(this, &WhitelistOp::PerformCallback, code, false));
}

void WhitelistOp::Succeed(bool value) {
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(this, &WhitelistOp::PerformCallback, SUCCESS, value));
}

void WhitelistOp::OnKeyOpComplete(const OwnerManager::KeyOpCode return_code,
                                  const std::vector<uint8>& sig) {
  // Ensure we're on the UI thread, due to the need to send DBus traffic.
  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    BrowserThread::PostTask(
        BrowserThread::UI, FROM_HERE,
        NewRunnableMethod(this,
                          &WhitelistOp::OnKeyOpComplete,
                          return_code, sig));
    return;
  }
  VLOG(2) << "WhitelistOp::OnKeyOpComplete return_code = " << return_code;
  // Now, sure we're on the UI thread.
  if (return_code == OwnerManager::SUCCESS) {
    to_store_.set_policy_data_signature(
        std::string(reinterpret_cast<const char*>(&sig[0]), sig.size()));
    store_op_ = CreateStorePolicyOp(&to_store_, this);
    // d_->OnSettingsOpCompleted() will be called by this call.
    store_op_->Execute();
  } else {
    Fail(SignedSettings::MapKeyOpCode(return_code));
  }
}

void WhitelistOp::OnSettingsOpCompleted(ReturnCode code, bool value) {
  if (value && to_store_.has_policy_data()) {
    em::PolicyData poldata;
    poldata.ParseFromString(to_store_.policy_data());
    service_->set_cached_policy(poldata);
    Succeed(value);
    return;
  }
  Fail(NOT_FOUND);
}

void WhitelistOp::ModifyWhitelist(const std::string& email,
                                  bool add_to_whitelist,
                                  em::UserWhitelistProto* whitelist_proto) {
  int i = 0;
  const RepeatedPtrField<string>& whitelist = whitelist_proto->user_whitelist();
  for (RepeatedPtrField<string>::const_iterator it = whitelist.begin();
       it != whitelist.end();
       ++it, ++i) {
    if (email == *it)
      break;
  }
  // |i| contains the index of |email|, if it is in |whitelist|.
  if (add_to_whitelist) {
    if (i >= whitelist.size())  // |email| was not in |whitelist|, we must add.
      whitelist_proto->add_user_whitelist(email);
    return;
  } else {
    if (i < whitelist.size()) {  // |email| was in |whitelist|, we must remove.
      RepeatedPtrField<string>* change_list =
          whitelist_proto->mutable_user_whitelist();
      change_list->SwapElements(i, whitelist.size() - 1);  // Move to end.
      change_list->RemoveLast();
    }
    return;
  }
  LOG(WARNING) << "Whitelist modification no-op: " << email;
}

void WhitelistOp::PerformCallback(SignedSettings::ReturnCode code, bool value) {
  d_->OnSettingsOpCompleted(code, value);
}

StorePropertyOp::StorePropertyOp(const std::string& name,
                                 const std::string& value,
                                 SignedSettings::Delegate<bool>* d)
    : name_(name),
      value_(value),
      d_(d),
      store_op_(NULL) {
}

StorePropertyOp::~StorePropertyOp() {}

void StorePropertyOp::Execute() {
  if (service_->GetStatus(true) != OwnershipService::OWNERSHIP_TAKEN) {
    if (g_browser_process &&
        g_browser_process->local_state() &&
        SignedSettingsTempStorage::Store(name_, value_,
                                         g_browser_process->local_state())) {
      Succeed(true);
      return;
    }
  }
  if (!service_->has_cached_policy()) {
    TryToFetchPolicyAndCallBack();
    return;
  }
  // Posts a task to the FILE thread to sign policy.
  em::PolicyData to_sign;
  to_sign.CheckTypeAndMergeFrom(service_->cached_policy());
  SetInPolicy(name_, value_, &to_sign);
  to_store_.set_policy_data(to_sign.SerializeAsString());
  service_->StartSigningAttempt(to_store_.policy_data(), this);
}

void StorePropertyOp::Fail(SignedSettings::ReturnCode code) {
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(this, &StorePropertyOp::PerformCallback, code, false));
}

void StorePropertyOp::Succeed(bool value) {
  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
                          NewRunnableMethod(this,
                                            &StorePropertyOp::PerformCallback,
                                            SUCCESS, value));
}

void StorePropertyOp::OnKeyOpComplete(const OwnerManager::KeyOpCode return_code,
                                      const std::vector<uint8>& sig) {
  // Ensure we're on the UI thread, due to the need to send DBus traffic.
  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    BrowserThread::PostTask(
        BrowserThread::UI, FROM_HERE,
        NewRunnableMethod(this,
                          &StorePropertyOp::OnKeyOpComplete,
                          return_code, sig));
    return;
  }
  VLOG(2) << "StorePropertyOp::OnKeyOpComplete return_code = " << return_code;
  // Now, sure we're on the UI thread.
  if (return_code == OwnerManager::SUCCESS) {
    to_store_.set_policy_data_signature(
        std::string(reinterpret_cast<const char*>(&sig[0]), sig.size()));
    store_op_ = CreateStorePolicyOp(&to_store_, this);
    // d_->OnSettingsOpCompleted() will be called by this call.
    store_op_->Execute();
  } else {
    Fail(SignedSettings::MapKeyOpCode(return_code));
  }
}

void StorePropertyOp::OnSettingsOpCompleted(ReturnCode code, bool value) {
  if (value && to_store_.has_policy_data()) {
    em::PolicyData poldata;
    poldata.ParseFromString(to_store_.policy_data());
    service_->set_cached_policy(poldata);
    Succeed(value);
    return;
  }
  Fail(NOT_FOUND);
}

void StorePropertyOp::SetInPolicy(const std::string& prop,
                                  const std::string& value,
                                  em::PolicyData* poldata) {
  em::ChromeDeviceSettingsProto pol;
  pol.ParseFromString(poldata->policy_value());
  if (prop == kAccountsPrefAllowNewUser) {
    em::AllowNewUsersProto* allow = pol.mutable_allow_new_users();
    allow->set_allow_new_users(value == "true");

  } else if (prop == kAccountsPrefAllowGuest) {
    em::GuestModeEnabledProto* guest = pol.mutable_guest_mode_enabled();
    guest->set_guest_mode_enabled(value == "true");

  } else if (prop == kAccountsPrefShowUserNamesOnSignIn) {
    em::ShowUserNamesOnSigninProto* show = pol.mutable_show_user_names();
    show->set_show_user_names(value == "true");

  } else if (prop == kSignedDataRoamingEnabled) {
    em::DataRoamingEnabledProto* roam = pol.mutable_data_roaming_enabled();
    roam->set_data_roaming_enabled(value == "true");

  } else if (prop == kSettingProxyEverywhere) {
    // TODO(cmasone): NOTIMPLEMENTED() once http://crosbug.com/13052 is fixed.
    bool success = pol.mutable_device_proxy_settings()->ParseFromString(value);
    DCHECK(success);

  } else {
    NOTREACHED();
  }
  poldata->set_policy_value(pol.SerializeAsString());
}

void StorePropertyOp::PerformCallback(SignedSettings::ReturnCode code,
                                      bool value) {
  d_->OnSettingsOpCompleted(code, value);
}

// static
const char* RetrievePropertyOp::kVeritas[] = { "false", "true" };

RetrievePropertyOp::RetrievePropertyOp(const std::string& name,
                                       SignedSettings::Delegate<std::string>* d)
    : name_(name),
      d_(d) {
}

RetrievePropertyOp::~RetrievePropertyOp() {}

void RetrievePropertyOp::Execute() {
  CHECK(chromeos::CrosLibrary::Get()->EnsureLoaded());
  // TODO(dilmah): Fix the race:
  // At the moment when device becomes owned there is lapse of time after
  // device has been owned and before temp_storage settings are finally
  // persisted into signed settings.
  // In this lapse of time Retrieve loses access to those settings.
  if (service_->GetStatus(true) != OwnershipService::OWNERSHIP_TAKEN) {
    if (g_browser_process &&
        g_browser_process->local_state() &&
        SignedSettingsTempStorage::Retrieve(
            name_, &value_, g_browser_process->local_state())) {
      Succeed(value_);
      return;
    }
  }

  if (!service_->has_cached_policy()) {
    TryToFetchPolicyAndCallBack();
    return;
  }
  std::string value = LookUpInPolicy(name_);
  if (value.empty())
    Fail(NOT_FOUND);
  else
    Succeed(value);
}

void RetrievePropertyOp::Fail(SignedSettings::ReturnCode code) {
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(this,
                        &RetrievePropertyOp::PerformCallback,
                        code, std::string()));
}

void RetrievePropertyOp::Succeed(const std::string& value) {
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(this,
                        &RetrievePropertyOp::PerformCallback, SUCCESS, value));
}

// DEPRECATED.
void RetrievePropertyOp::OnKeyOpComplete(
    const OwnerManager::KeyOpCode return_code,
    const std::vector<uint8>& sig) {
  NOTREACHED();
}

std::string RetrievePropertyOp::LookUpInPolicy(const std::string& prop) {
  if (prop == kDeviceOwner) {
    const em::PolicyData& data = service_->cached_policy();
    if (data.has_username() && !data.has_request_token())
      return data.username();
    return "";
  }
  VLOG(2) << "Looking up " << prop;
  em::ChromeDeviceSettingsProto pol;
  pol.ParseFromString(service_->cached_policy().policy_value());
  if (prop == kAccountsPrefAllowNewUser) {
    if (pol.has_allow_new_users() &&
        pol.allow_new_users().has_allow_new_users() &&
        pol.allow_new_users().allow_new_users()) {
      return kVeritas[1];  // New users allowed, user_whitelist() ignored.
    }
    // If we have the allow_new_users bool, and it is true, we honor that above.
    // In all other cases (don't have it, have it and it is set to false, etc),
    // We will honor the user_whitelist() if it is there and populated.
    // Otherwise, fail open (to do otherwise could render the device unusable).
    if (!pol.has_user_whitelist())
      return kVeritas[1];  // Default to allowing new users.
    return kVeritas[pol.user_whitelist().user_whitelist_size() == 0];

  } else if (prop == kAccountsPrefAllowGuest) {
    if (!pol.has_guest_mode_enabled() ||
        !pol.guest_mode_enabled().has_guest_mode_enabled())
      return kVeritas[1];  // Default to allowing guests;
    return kVeritas[pol.guest_mode_enabled().guest_mode_enabled()];

  } else if (prop == kAccountsPrefShowUserNamesOnSignIn) {
    if (!pol.has_show_user_names() ||
        !pol.show_user_names().has_show_user_names())
      return kVeritas[1];  // Default to showing pods on the login screen;
    return kVeritas[pol.show_user_names().show_user_names()];

  } else if (prop == kSignedDataRoamingEnabled) {
    if (!pol.has_data_roaming_enabled() ||
        !pol.data_roaming_enabled().has_data_roaming_enabled())
      return kVeritas[0];  // Default to disabling cellular data roaming;
    return kVeritas[pol.data_roaming_enabled().data_roaming_enabled()];

  } else if (prop == kSettingProxyEverywhere) {
    // TODO(cmasone): NOTIMPLEMENTED() once http://crosbug.com/13052 is fixed.
    std::string serialized;
    if (!pol.has_device_proxy_settings() ||
        !pol.device_proxy_settings().SerializeToString(&serialized))
      return "";           // Default to invalid proxy config (will be ignored).
    return serialized;

  }
  return std::string();
}

void RetrievePropertyOp::PerformCallback(SignedSettings::ReturnCode code,
                                         const std::string& value) {
  d_->OnSettingsOpCompleted(code, value);
}

StorePolicyOp::StorePolicyOp(em::PolicyFetchResponse* policy,
                             SignedSettings::Delegate<bool>* d)
    : policy_(policy),
      d_(d) {
}

StorePolicyOp::~StorePolicyOp() {}

// static
void StorePolicyOp::OnBoolComplete(void* delegate, bool success) {
  StorePolicyOp* op = static_cast<StorePolicyOp*>(delegate);
  if (success)
    op->Succeed(true);
  else
    op->Fail(NOT_FOUND);
}

void StorePolicyOp::Execute() {
  // get protobuf contents to sign
  if (!policy_->has_policy_data())
    Fail(OPERATION_FAILED);
  if (!policy_->has_policy_data_signature())
    service_->StartSigningAttempt(policy_->policy_data(), this);
  else
    RequestStorePolicy();
}

void StorePolicyOp::Fail(SignedSettings::ReturnCode code) {
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(this, &StorePolicyOp::PerformCallback, code, false));
}

void StorePolicyOp::Succeed(bool ignored) {
  SignedSettings::ReturnCode code = SUCCESS;
  bool to_ret = true;
  em::PolicyData poldata;
  if (SignedSettings::PolicyIsSane(*policy_, &poldata)) {
    service_->set_cached_policy(poldata);
  } else {
    code = NOT_FOUND;
    to_ret = false;
  }
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(this, &StorePolicyOp::PerformCallback, code, to_ret));
}

void StorePolicyOp::OnKeyOpComplete(const OwnerManager::KeyOpCode return_code,
                                    const std::vector<uint8>& payload) {
  // Ensure we're on the UI thread, due to the need to send DBus traffic.
  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    BrowserThread::PostTask(
        BrowserThread::UI, FROM_HERE,
        NewRunnableMethod(this,
                          &StorePolicyOp::OnKeyOpComplete,
                          return_code, payload));
    return;
  }
  VLOG(2) << "StorePolicyOp::OnKeyOpComplete return_code = " << return_code;
  // Now, sure we're on the UI thread.
  if (return_code == OwnerManager::SUCCESS) {
    policy_->set_policy_data_signature(std::string(payload.begin(),
                                                   payload.end()));
    RequestStorePolicy();
    return;
  }
  Fail(SignedSettings::MapKeyOpCode(return_code));
}

void StorePolicyOp::RequestStorePolicy() {
  std::string serialized;
  if (policy_->SerializeToString(&serialized)) {
    CrosLibrary::Get()->GetLoginLibrary()->RequestStorePolicy(
        serialized,
        &StorePolicyOp::OnBoolComplete,
        this);
  } else {
    Fail(OPERATION_FAILED);
  }
}

void StorePolicyOp::PerformCallback(SignedSettings::ReturnCode code,
                                    bool value) {
  d_->OnSettingsOpCompleted(code, value);
}

RetrievePolicyOp::RetrievePolicyOp(
    SignedSettings::Delegate<const em::PolicyFetchResponse&>* d)
    : d_(d) {
}

RetrievePolicyOp::~RetrievePolicyOp() {}

void RetrievePolicyOp::Execute() {
  CrosLibrary::Get()->GetLoginLibrary()->RequestRetrievePolicy(
      &RetrievePolicyOp::OnStringComplete, this);
}

void RetrievePolicyOp::Fail(SignedSettings::ReturnCode code) {
  VLOG(2) << "RetrievePolicyOp::Execute() failed with " << code;
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(this, &RetrievePolicyOp::PerformCallback, code,
                        em::PolicyFetchResponse()));
}

void RetrievePolicyOp::Succeed(const em::PolicyFetchResponse& value) {
  em::PolicyData poldata;
  if (SignedSettings::PolicyIsSane(value, &poldata)) {
    service_->set_cached_policy(poldata);
    BrowserThread::PostTask(
        BrowserThread::UI, FROM_HERE,
        NewRunnableMethod(this,
                          &RetrievePolicyOp::PerformCallback,
                          SUCCESS, value));
  } else {
    Fail(NOT_FOUND);
  }
}

void RetrievePolicyOp::OnKeyOpComplete(
    const OwnerManager::KeyOpCode return_code,
    const std::vector<uint8>& payload) {
  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    BrowserThread::PostTask(
        BrowserThread::UI, FROM_HERE,
        NewRunnableMethod(this,
                          &RetrievePolicyOp::OnKeyOpComplete,
                          return_code, payload));
    return;
  }
  // Now, sure we're on the UI thread.
  if (return_code == OwnerManager::SUCCESS)
    Succeed(policy_);
  else
    Fail(SignedSettings::MapKeyOpCode(return_code));
}

// static
void RetrievePolicyOp::OnStringComplete(void* delegate,
                                        const char* out,
                                        const unsigned int len) {
  RetrievePolicyOp* op = static_cast<RetrievePolicyOp*>(delegate);
  op->ProcessPolicy(out, len);
}

void RetrievePolicyOp::ProcessPolicy(const char* out, const unsigned int len) {
  if (!out || !policy_.ParseFromString(std::string(out, len)) ||
      (!policy_.has_policy_data() && !policy_.has_policy_data_signature())) {
    Fail(NOT_FOUND);
    return;
  }
  if (!policy_.has_policy_data()) {
    Fail(OPERATION_FAILED);
    return;
  }
  if (!policy_.has_policy_data_signature()) {
    Fail(BAD_SIGNATURE);
    return;
  }
  std::vector<uint8> sig;
  const char* sig_ptr = policy_.policy_data_signature().c_str();
  sig.assign(sig_ptr, sig_ptr + policy_.policy_data_signature().length());
  service_->StartVerifyAttempt(policy_.policy_data(), sig, this);
}

void RetrievePolicyOp::PerformCallback(SignedSettings::ReturnCode code,
                                       const em::PolicyFetchResponse& value) {
  d_->OnSettingsOpCompleted(code, value);
}

}  // namespace chromeos