// 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/cros/login_library.h"
#include "base/message_loop.h"
#include "base/task.h"
#include "base/timer.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/login/signed_settings.h"
#include "chrome/browser/chromeos/login/signed_settings_temp_storage.h"
#include "chrome/browser/policy/proto/device_management_backend.pb.h"
#include "chrome/browser/prefs/pref_service.h"
#include "content/browser/browser_thread.h"
#include "content/common/notification_service.h"
#include "content/common/notification_type.h"
namespace em = enterprise_management;
namespace chromeos {
class LoginLibraryImpl : public LoginLibrary {
public:
LoginLibraryImpl()
: job_restart_request_(NULL),
set_owner_key_callback_(NULL),
whitelist_op_callback_(NULL),
property_op_callback_(NULL) {
if (CrosLibrary::Get()->EnsureLoaded())
Init();
}
virtual ~LoginLibraryImpl() {
if (session_connection_) {
chromeos::DisconnectSession(session_connection_);
}
}
bool EmitLoginPromptReady() {
return chromeos::EmitLoginPromptReady();
}
bool CheckWhitelist(const std::string& email,
std::vector<uint8>* OUT_signature) {
CryptoBlob* sig = NULL;
if (chromeos::CheckWhitelistSafe(email.c_str(), &sig)) {
OUT_signature->assign(sig->data, sig->data + sig->length);
chromeos::FreeCryptoBlob(sig);
return true;
}
return false;
}
void RequestRetrievePolicy(RetrievePolicyCallback callback, void* delegate) {
DCHECK(callback) << "must provide a callback to RequestRetrievePolicy()";
chromeos::RetrievePolicy(callback, delegate);
}
void RequestRetrieveProperty(const std::string& name,
RetrievePropertyCallback callback,
void* user_data) {
DCHECK(callback) << "must provide a callback to RequestRetrieveProperty()";
chromeos::RequestRetrieveProperty(name.c_str(), callback, user_data);
}
void RequestStorePolicy(const std::string& policy,
StorePolicyCallback callback,
void* delegate) {
DCHECK(callback) << "must provide a callback to StorePolicy()";
chromeos::StorePolicy(policy.c_str(), policy.length(), callback, delegate);
}
bool StorePropertyAsync(const std::string& name,
const std::string& value,
const std::vector<uint8>& signature,
Delegate* callback) {
DCHECK(callback) << "must provide a callback to StorePropertyAsync()";
if (property_op_callback_)
return false;
property_op_callback_ = callback;
Property* prop = chromeos::CreateProperty(name.c_str(),
value.c_str(),
&signature[0],
signature.size());
bool rv = chromeos::StorePropertySafe(prop);
chromeos::FreeProperty(prop);
return rv;
}
bool UnwhitelistAsync(const std::string& email,
const std::vector<uint8>& signature,
Delegate* callback) {
DCHECK(callback) << "must provide a callback to UnwhitelistAsync()";
if (whitelist_op_callback_)
return false;
whitelist_op_callback_ = callback;
CryptoBlob* sig = chromeos::CreateCryptoBlob(&signature[0],
signature.size());
bool rv = chromeos::UnwhitelistSafe(email.c_str(), sig);
chromeos::FreeCryptoBlob(sig);
return rv;
}
bool WhitelistAsync(const std::string& email,
const std::vector<uint8>& signature,
Delegate* callback) {
DCHECK(callback) << "must provide a callback to WhitelistAsync()";
if (whitelist_op_callback_)
return false;
whitelist_op_callback_ = callback;
CryptoBlob* sig = chromeos::CreateCryptoBlob(&signature[0],
signature.size());
bool rv = chromeos::WhitelistSafe(email.c_str(), sig);
chromeos::FreeCryptoBlob(sig);
return rv;
}
// DEPRECATED.
bool EnumerateWhitelisted(std::vector<std::string>* whitelisted) {
NOTREACHED();
UserList* list = NULL;
if (chromeos::EnumerateWhitelistedSafe(&list)) {
for (int i = 0; i < list->num_users; i++)
whitelisted->push_back(std::string(list->users[i]));
chromeos::FreeUserList(list);
return true;
}
return false;
}
bool StartSession(const std::string& user_email,
const std::string& unique_id /* unused */) {
// only pass unique_id through once we use it for something.
return chromeos::StartSession(user_email.c_str(), "");
}
bool StopSession(const std::string& unique_id /* unused */) {
// only pass unique_id through once we use it for something.
return chromeos::StopSession("");
}
bool RestartEntd() {
return chromeos::RestartEntd();
}
bool RestartJob(int pid, const std::string& command_line) {
if (job_restart_request_) {
NOTREACHED();
return false;
}
job_restart_request_ = new JobRestartRequest(pid, command_line);
return true;
}
private:
class JobRestartRequest
: public base::RefCountedThreadSafe<JobRestartRequest> {
public:
JobRestartRequest(int pid, const std::string& command_line)
: pid_(pid),
command_line_(command_line),
local_state_(g_browser_process->local_state()) {
AddRef();
if (local_state_) {
// XXX: normally this call must not be needed, however RestartJob
// just kills us so settings may be lost. See http://crosbug.com/13102
local_state_->CommitPendingWrite();
timer_.Start(
base::TimeDelta::FromSeconds(3), this,
&JobRestartRequest::RestartJob);
// Post task on file thread thus it occurs last on task queue, so it
// would be executed after committing pending write on file thread.
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
NewRunnableMethod(this, &JobRestartRequest::RestartJob));
} else {
RestartJob();
}
}
private:
void RestartJob() {
if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
if (!chromeos::RestartJob(pid_, command_line_.c_str()))
NOTREACHED();
} else {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
NewRunnableMethod(this, &JobRestartRequest::RestartJob));
MessageLoop::current()->AssertIdle();
}
}
int pid_;
std::string command_line_;
PrefService* local_state_;
base::OneShotTimer<JobRestartRequest> timer_;
};
class StubDelegate
: public SignedSettings::Delegate<const em::PolicyFetchResponse&> {
public:
StubDelegate() : polfetcher_(NULL) {}
virtual ~StubDelegate() {}
void set_fetcher(SignedSettings* s) { polfetcher_ = s; }
SignedSettings* fetcher() { return polfetcher_.get(); }
// Implementation of SignedSettings::Delegate
virtual void OnSettingsOpCompleted(SignedSettings::ReturnCode code,
const em::PolicyFetchResponse& value) {
VLOG(2) << "Done Fetching Policy";
delete this;
}
private:
scoped_refptr<SignedSettings> polfetcher_;
DISALLOW_COPY_AND_ASSIGN(StubDelegate);
};
static void Handler(void* object, const OwnershipEvent& event) {
LoginLibraryImpl* self = static_cast<LoginLibraryImpl*>(object);
switch (event) {
case SetKeySuccess:
self->CompleteSetOwnerKey(true);
break;
case SetKeyFailure:
self->CompleteSetOwnerKey(false);
break;
case WhitelistOpSuccess:
self->CompleteWhitelistOp(true);
break;
case WhitelistOpFailure:
self->CompleteWhitelistOp(false);
break;
case PropertyOpSuccess:
self->CompletePropertyOp(true);
break;
case PropertyOpFailure:
self->CompletePropertyOp(false);
break;
default:
NOTREACHED();
break;
}
}
void Init() {
session_connection_ = chromeos::MonitorSession(&Handler, this);
}
void CompleteSetOwnerKey(bool value) {
VLOG(1) << "Owner key generation: " << (value ? "success" : "fail");
NotificationType result =
NotificationType::OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED;
if (!value)
result = NotificationType::OWNER_KEY_FETCH_ATTEMPT_FAILED;
// Whether we exported the public key or not, send a notification indicating
// that we're done with this attempt.
NotificationService::current()->Notify(result,
NotificationService::AllSources(),
NotificationService::NoDetails());
// We stored some settings in transient storage before owner was assigned.
// Now owner is assigned and key is generated and we should persist
// those settings into signed storage.
if (g_browser_process && g_browser_process->local_state()) {
SignedSettingsTempStorage::Finalize(g_browser_process->local_state());
}
}
void CompleteWhitelistOp(bool result) {
if (whitelist_op_callback_) {
whitelist_op_callback_->OnComplete(result);
whitelist_op_callback_ = NULL;
}
}
void CompletePropertyOp(bool result) {
if (result) {
StubDelegate* stub = new StubDelegate(); // Manages its own lifetime.
stub->set_fetcher(SignedSettings::CreateRetrievePolicyOp(stub));
stub->fetcher()->Execute();
}
}
chromeos::SessionConnection session_connection_;
JobRestartRequest* job_restart_request_;
Delegate* set_owner_key_callback_;
Delegate* whitelist_op_callback_;
Delegate* property_op_callback_;
DISALLOW_COPY_AND_ASSIGN(LoginLibraryImpl);
};
class LoginLibraryStubImpl : public LoginLibrary {
public:
LoginLibraryStubImpl() {}
virtual ~LoginLibraryStubImpl() {}
bool EmitLoginPromptReady() { return true; }
bool CheckWhitelist(const std::string& email,
std::vector<uint8>* OUT_signature) {
OUT_signature->assign(2, 0);
return true;
}
void RequestRetrievePolicy(RetrievePolicyCallback callback, void* delegate) {
callback(delegate, "", 0);
}
void RequestRetrieveProperty(const std::string& name,
RetrievePropertyCallback callback,
void* user_data) {
uint8 sig_bytes[] = { 0, 0 };
CryptoBlob sig = { sig_bytes, arraysize(sig_bytes) };
Property prop = {
"prop_name",
"stub",
&sig,
};
callback(user_data, true, &prop);
}
void RequestStorePolicy(const std::string& policy,
StorePolicyCallback callback,
void* delegate) {
callback(delegate, true);
}
bool StorePropertyAsync(const std::string& name,
const std::string& value,
const std::vector<uint8>& signature,
Delegate* callback) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
NewRunnableFunction(&DoStubCallback, callback));
return true;
}
bool UnwhitelistAsync(const std::string& email,
const std::vector<uint8>& signature,
Delegate* callback) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
NewRunnableFunction(&DoStubCallback, callback));
return true;
}
bool WhitelistAsync(const std::string& email,
const std::vector<uint8>& signature,
Delegate* callback) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
NewRunnableFunction(&DoStubCallback, callback));
return true;
}
bool EnumerateWhitelisted(std::vector<std::string>* whitelisted) {
return true;
}
bool StartSession(const std::string& user_email,
const std::string& unique_id /* unused */) { return true; }
bool StopSession(const std::string& unique_id /* unused */) { return true; }
bool RestartJob(int pid, const std::string& command_line) { return true; }
bool RestartEntd() { return true; }
private:
static void DoStubCallback(Delegate* callback) {
callback->OnComplete(true);
}
DISALLOW_COPY_AND_ASSIGN(LoginLibraryStubImpl);
};
// static
LoginLibrary* LoginLibrary::GetImpl(bool stub) {
if (stub)
return new LoginLibraryStubImpl();
else
return new LoginLibraryImpl();
}
} // namespace chromeos