普通文本  |  239行  |  8.22 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/ownership_service.h"

#include "base/file_path.h"
#include "base/file_util.h"
#include "base/lazy_instance.h"
#include "base/synchronization/lock.h"
#include "chrome/browser/browser_process.h"
#include "content/browser/browser_thread.h"

// We want to use NewRunnableMethod for non-static methods of this class but
// need to disable reference counting since it is singleton.
DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::OwnershipService);

namespace chromeos {

static base::LazyInstance<OwnershipService> g_ownership_service(
    base::LINKER_INITIALIZED);

//  static
OwnershipService* OwnershipService::GetSharedInstance() {
  return g_ownership_service.Pointer();
}

OwnershipService::OwnershipService()
    : manager_(new OwnerManager),
      utils_(OwnerKeyUtils::Create()),
      policy_(NULL),
      ownership_status_(OWNERSHIP_UNKNOWN) {
  notification_registrar_.Add(
      this,
      NotificationType::OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED,
      NotificationService::AllSources());
}

OwnershipService::~OwnershipService() {}

void OwnershipService::Prewarm() {
  // Note that we cannot prewarm in constructor because in current codebase
  // object is created before spawning threads.
  if (g_ownership_service == this) {
    // Start getting ownership status.
    BrowserThread::PostTask(
        BrowserThread::FILE,
        FROM_HERE,
        NewRunnableMethod(this, &OwnershipService::FetchStatus));
  } else {
    // This can happen only for particular test: OwnershipServiceTest. It uses
    // mocks and for that uses OwnershipService not as a regular singleton but
    // as a resurrecting object. This behaviour conflicts with
    // DISABLE_RUNNABLE_METHOD_REFCOUNT.  So avoid posting task in those
    // circumstances in order to avoid accessing already deleted object.
  }
}

void OwnershipService::set_cached_policy(const em::PolicyData& pol) {
  policy_.reset(pol.New());
  policy_->CheckTypeAndMergeFrom(pol);
}

bool OwnershipService::has_cached_policy() {
  return policy_.get();
}

const em::PolicyData& OwnershipService::cached_policy() {
  return *(policy_.get());
}

bool OwnershipService::IsAlreadyOwned() {
  return file_util::PathExists(utils_->GetOwnerKeyFilePath());
}

OwnershipService::Status OwnershipService::GetStatus(bool blocking) {
  Status status = OWNERSHIP_UNKNOWN;
  bool is_owned = false;
  if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    ownership_status_lock_.Acquire();
    status = ownership_status_;
    ownership_status_lock_.Release();
    if (status != OWNERSHIP_UNKNOWN || !blocking)
      return status;
    // Under common usage there is very short lapse of time when ownership
    // status is still unknown after constructing OwnershipService.
    LOG(ERROR) << "Blocking on UI thread in OwnershipService::GetStatus";
    base::ThreadRestrictions::ScopedAllowIO allow_io;
    is_owned = IsAlreadyOwned();
  } else {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    is_owned = IsAlreadyOwned();
  }
  status = is_owned ? OWNERSHIP_TAKEN : OWNERSHIP_NONE;
  SetStatus(status);
  return status;
}

void OwnershipService::StartLoadOwnerKeyAttempt() {
  BrowserThread::PostTask(
      BrowserThread::FILE, FROM_HERE,
      NewRunnableFunction(&TryLoadOwnerKeyAttempt, this));
}

void OwnershipService::StartUpdateOwnerKey(const std::vector<uint8>& new_key,
                                           OwnerManager::KeyUpdateDelegate* d) {
  BrowserThread::ID thread_id;
  if (!BrowserThread::GetCurrentThreadIdentifier(&thread_id))
    thread_id = BrowserThread::UI;
  BrowserThread::PostTask(
      BrowserThread::FILE, FROM_HERE,
      NewRunnableFunction(&OwnershipService::UpdateOwnerKey,
                          this,
                          thread_id,
                          new_key,
                          d));
  return;
}

void OwnershipService::StartSigningAttempt(const std::string& data,
                                           OwnerManager::Delegate* d) {
  BrowserThread::ID thread_id;
  if (!BrowserThread::GetCurrentThreadIdentifier(&thread_id))
    thread_id = BrowserThread::UI;
  BrowserThread::PostTask(
      BrowserThread::FILE, FROM_HERE,
      NewRunnableFunction(&OwnershipService::TrySigningAttempt,
                          this,
                          thread_id,
                          data,
                          d));
  return;
}

void OwnershipService::StartVerifyAttempt(const std::string& data,
                                          const std::vector<uint8>& signature,
                                          OwnerManager::Delegate* d) {
  BrowserThread::ID thread_id;
  if (!BrowserThread::GetCurrentThreadIdentifier(&thread_id))
    thread_id = BrowserThread::UI;
  BrowserThread::PostTask(
      BrowserThread::FILE, FROM_HERE,
      NewRunnableFunction(&OwnershipService::TryVerifyAttempt,
                          this,
                          thread_id,
                          data,
                          signature,
                          d));
  return;
}

void OwnershipService::Observe(NotificationType type,
                               const NotificationSource& source,
                               const NotificationDetails& details) {
  if (type.value == NotificationType::OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED) {
    SetStatus(OWNERSHIP_TAKEN);
    notification_registrar_.RemoveAll();
  } else {
    NOTREACHED();
  }
}

bool OwnershipService::CurrentUserIsOwner() {
  // If this user has the private key associated with the owner's
  // public key, this user is the owner.
  return IsAlreadyOwned() && manager_->EnsurePrivateKey();
}

// static
void OwnershipService::UpdateOwnerKey(OwnershipService* service,
                                      const BrowserThread::ID thread_id,
                                      const std::vector<uint8>& new_key,
                                      OwnerManager::KeyUpdateDelegate* d) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  service->manager()->UpdateOwnerKey(thread_id, new_key, d);
}

// static
void OwnershipService::TryLoadOwnerKeyAttempt(OwnershipService* service) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  if (!service->IsAlreadyOwned()) {
    VLOG(1) << "Device not yet owned";
    return;
  }
  service->manager()->LoadOwnerKey();
}

// static
void OwnershipService::TrySigningAttempt(OwnershipService* service,
                                         const BrowserThread::ID thread_id,
                                         const std::string& data,
                                         OwnerManager::Delegate* d) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  if (!service->IsAlreadyOwned()) {
    LOG(ERROR) << "Device not yet owned";
    BrowserThread::PostTask(
        thread_id, FROM_HERE,
        NewRunnableFunction(&OwnershipService::FailAttempt, d));
    return;
  }
  service->manager()->Sign(thread_id, data, d);
}

// static
void OwnershipService::TryVerifyAttempt(OwnershipService* service,
                                        const BrowserThread::ID thread_id,
                                        const std::string& data,
                                        const std::vector<uint8>& signature,
                                        OwnerManager::Delegate* d) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  if (!service->IsAlreadyOwned()) {
    LOG(ERROR) << "Device not yet owned";
    BrowserThread::PostTask(
        thread_id, FROM_HERE,
        NewRunnableFunction(&OwnershipService::FailAttempt, d));
    return;
  }
  service->manager()->Verify(thread_id, data, signature, d);
}

// static
void OwnershipService::FailAttempt(OwnerManager::Delegate* d) {
  d->OnKeyOpComplete(OwnerManager::KEY_UNAVAILABLE, std::vector<uint8>());
}

void OwnershipService::FetchStatus() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  Status status = IsAlreadyOwned() ? OWNERSHIP_TAKEN : OWNERSHIP_NONE;
  SetStatus(status);
}

void OwnershipService::SetStatus(Status new_status) {
  DCHECK(new_status == OWNERSHIP_TAKEN || new_status == OWNERSHIP_NONE);
  base::AutoLock lk(ownership_status_lock_);
  ownership_status_ = new_status;
}

}  // namespace chromeos