blob: da5c87f16105ae4e153d8f1e9c980a0c24ba8f9b [file] [log] [blame]
// Copyright (c) 2014 The Chromium OS 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 "login_manager/session_manager_dbus_adaptor.h"
#include <stdint.h>
#include <map>
#include <string>
#include <vector>
#include <base/bind.h>
#include <base/callback.h>
#include <base/files/file_util.h>
#include <base/memory/scoped_ptr.h>
#include <base/stl_util.h>
#include <chromeos/dbus/service_constants.h>
#include <dbus/exported_object.h>
#include <dbus/file_descriptor.h>
#include <dbus/message.h>
#include "login_manager/dbus_error_types.h"
#include "login_manager/policy_service.h"
#include "login_manager/session_manager_impl.h"
namespace login_manager {
namespace {
const char kBindingsPath[] =
"/usr/share/dbus-1/interfaces/org.chromium.SessionManagerInterface.xml";
const char kDBusIntrospectableInterface[] =
"org.freedesktop.DBus.Introspectable";
const char kDBusIntrospectMethod[] = "Introspect";
// Passes |method_call| to |handler| and passes the response to
// |response_sender|. If |handler| returns NULL, an empty response is created
// and sent.
void HandleSynchronousDBusMethodCall(
base::Callback<scoped_ptr<dbus::Response>(dbus::MethodCall*)> handler,
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
scoped_ptr<dbus::Response> response = handler.Run(method_call);
if (!response)
response = dbus::Response::FromMethodCall(method_call);
response_sender.Run(response.Pass());
}
scoped_ptr<dbus::Response> CreateError(dbus::MethodCall* call,
const std::string& name,
const std::string& message) {
return dbus::ErrorResponse::FromMethodCall(call, name, message).Pass();
}
// Creates a new "invalid args" reply to call.
scoped_ptr<dbus::Response> CreateInvalidArgsError(dbus::MethodCall* call,
std::string message) {
return CreateError(call, DBUS_ERROR_INVALID_ARGS, "Signature is: " + message);
}
// Craft a Response to call that is appropriate, given the contents of error.
// If error is set, this will be an ErrorResponse. Otherwise, it will be a
// Response containing payload.
scoped_ptr<dbus::Response> CraftAppropriateResponseWithBool(
dbus::MethodCall* call,
const SessionManagerImpl::Error& error,
bool payload) {
scoped_ptr<dbus::Response> response;
if (error.is_set()) {
response = CreateError(call, error.name(), error.message());
} else {
response = dbus::Response::FromMethodCall(call);
dbus::MessageWriter writer(response.get());
writer.AppendBool(payload);
}
return response.Pass();
}
scoped_ptr<dbus::Response> CraftAppropriateResponseWithString(
dbus::MethodCall* call,
const SessionManagerImpl::Error& error,
const std::string& payload) {
scoped_ptr<dbus::Response> response;
if (error.is_set()) {
response = CreateError(call, error.name(), error.message());
} else {
response = dbus::Response::FromMethodCall(call);
dbus::MessageWriter writer(response.get());
writer.AppendString(payload);
}
return response.Pass();
}
scoped_ptr<dbus::Response> CraftAppropriateResponseWithBytes(
dbus::MethodCall* call,
const SessionManagerImpl::Error& error,
const std::vector<uint8_t>& payload) {
scoped_ptr<dbus::Response> response;
if (error.is_set()) {
response = CreateError(call, error.name(), error.message());
} else {
response = dbus::Response::FromMethodCall(call);
dbus::MessageWriter writer(response.get());
writer.AppendArrayOfBytes(vector_as_array(&payload), payload.size());
}
return response.Pass();
}
// Handles completion of a server-backed state key retrieval operation and
// passes the response back to the waiting DBus invocation context.
void HandleGetServerBackedStateKeysCompletion(
dbus::MethodCall* call,
const dbus::ExportedObject::ResponseSender& sender,
const std::vector<std::vector<uint8_t>>& state_keys) {
scoped_ptr<dbus::Response> response(dbus::Response::FromMethodCall(call));
dbus::MessageWriter writer(response.get());
dbus::MessageWriter array_writer(NULL);
writer.OpenArray("ay", &array_writer);
for (std::vector<std::vector<uint8_t>>::const_iterator state_key(
state_keys.begin());
state_key != state_keys.end();
++state_key) {
array_writer.AppendArrayOfBytes(state_key->data(), state_key->size());
}
writer.CloseContainer(&array_writer);
sender.Run(response.Pass());
}
} // namespace
// Callback that forwards a result to a DBus invocation context.
class DBusMethodCompletion {
public:
static PolicyService::Completion CreateCallback(
dbus::MethodCall* call,
const dbus::ExportedObject::ResponseSender& sender);
virtual ~DBusMethodCompletion();
private:
DBusMethodCompletion() : call_(nullptr) {}
DBusMethodCompletion(dbus::MethodCall* call,
const dbus::ExportedObject::ResponseSender& sender);
void HandleResult(const PolicyService::Error& error);
dbus::MethodCall* call_ = nullptr;
dbus::ExportedObject::ResponseSender sender_;
DISALLOW_COPY_AND_ASSIGN(DBusMethodCompletion);
};
// static
PolicyService::Completion DBusMethodCompletion::CreateCallback(
dbus::MethodCall* call,
const dbus::ExportedObject::ResponseSender& sender) {
return base::Bind(&DBusMethodCompletion::HandleResult,
base::Owned(new DBusMethodCompletion(call, sender)));
}
// Apparently, call is owned by sender, so it's safe to hang on to it.
DBusMethodCompletion::DBusMethodCompletion(
dbus::MethodCall* call,
const dbus::ExportedObject::ResponseSender& sender)
: call_(call), sender_(sender) {
}
DBusMethodCompletion::~DBusMethodCompletion() {
if (call_) {
NOTREACHED() << "Unfinished DBUS call!";
sender_.Run(dbus::Response::FromMethodCall(call_));
}
}
void DBusMethodCompletion::HandleResult(const PolicyService::Error& error) {
if (error.code() == dbus_error::kNone) {
scoped_ptr<dbus::Response> response(dbus::Response::FromMethodCall(call_));
dbus::MessageWriter writer(response.get());
writer.AppendBool(true);
sender_.Run(response.Pass());
call_ = nullptr;
} else {
sender_.Run(
dbus::ErrorResponse::FromMethodCall(call_,
error.code(), error.message())
.Pass());
call_ = nullptr;
}
}
SessionManagerDBusAdaptor::SessionManagerDBusAdaptor(SessionManagerImpl* impl)
: impl_(impl) {
CHECK(impl_);
}
SessionManagerDBusAdaptor::~SessionManagerDBusAdaptor() {
}
void SessionManagerDBusAdaptor::ExportDBusMethods(
dbus::ExportedObject* object) {
ExportSyncDBusMethod(object,
kSessionManagerEmitLoginPromptVisible,
&SessionManagerDBusAdaptor::EmitLoginPromptVisible);
ExportSyncDBusMethod(object,
"EnableChromeTesting",
&SessionManagerDBusAdaptor::EnableChromeTesting);
ExportSyncDBusMethod(object,
kSessionManagerStartSession,
&SessionManagerDBusAdaptor::StartSession);
ExportSyncDBusMethod(object,
kSessionManagerStopSession,
&SessionManagerDBusAdaptor::StopSession);
ExportAsyncDBusMethod(object,
kSessionManagerStorePolicy,
&SessionManagerDBusAdaptor::StorePolicy);
ExportSyncDBusMethod(object,
kSessionManagerRetrievePolicy,
&SessionManagerDBusAdaptor::RetrievePolicy);
ExportAsyncDBusMethod(object,
kSessionManagerStorePolicyForUser,
&SessionManagerDBusAdaptor::StorePolicyForUser);
ExportSyncDBusMethod(object,
kSessionManagerRetrievePolicyForUser,
&SessionManagerDBusAdaptor::RetrievePolicyForUser);
ExportAsyncDBusMethod(
object,
kSessionManagerStoreDeviceLocalAccountPolicy,
&SessionManagerDBusAdaptor::StoreDeviceLocalAccountPolicy);
ExportSyncDBusMethod(
object,
kSessionManagerRetrieveDeviceLocalAccountPolicy,
&SessionManagerDBusAdaptor::RetrieveDeviceLocalAccountPolicy);
ExportSyncDBusMethod(object,
kSessionManagerRetrieveSessionState,
&SessionManagerDBusAdaptor::RetrieveSessionState);
ExportSyncDBusMethod(object,
kSessionManagerRetrieveActiveSessions,
&SessionManagerDBusAdaptor::RetrieveActiveSessions);
ExportSyncDBusMethod(
object,
kSessionManagerHandleSupervisedUserCreationStarting,
&SessionManagerDBusAdaptor::HandleSupervisedUserCreationStarting);
ExportSyncDBusMethod(
object,
kSessionManagerHandleSupervisedUserCreationFinished,
&SessionManagerDBusAdaptor::HandleSupervisedUserCreationFinished);
ExportSyncDBusMethod(object,
kSessionManagerLockScreen,
&SessionManagerDBusAdaptor::LockScreen);
ExportSyncDBusMethod(object,
kSessionManagerHandleLockScreenShown,
&SessionManagerDBusAdaptor::HandleLockScreenShown);
ExportSyncDBusMethod(object,
kSessionManagerHandleLockScreenDismissed,
&SessionManagerDBusAdaptor::HandleLockScreenDismissed);
ExportSyncDBusMethod(object,
kSessionManagerRestartJob,
&SessionManagerDBusAdaptor::RestartJob);
ExportSyncDBusMethod(object,
kSessionManagerRestartJobWithAuth,
&SessionManagerDBusAdaptor::RestartJobWithAuth);
ExportSyncDBusMethod(object,
kSessionManagerStartDeviceWipe,
&SessionManagerDBusAdaptor::StartDeviceWipe);
ExportSyncDBusMethod(object,
kSessionManagerSetFlagsForUser,
&SessionManagerDBusAdaptor::SetFlagsForUser);
ExportAsyncDBusMethod(object,
kSessionManagerGetServerBackedStateKeys,
&SessionManagerDBusAdaptor::GetServerBackedStateKeys);
ExportSyncDBusMethod(object,
kSessionManagerInitMachineInfo,
&SessionManagerDBusAdaptor::InitMachineInfo);
CHECK(object->ExportMethodAndBlock(
kDBusIntrospectableInterface,
kDBusIntrospectMethod,
base::Bind(&HandleSynchronousDBusMethodCall,
base::Bind(&SessionManagerDBusAdaptor::Introspect,
base::Unretained(this)))));
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::EmitLoginPromptVisible(
dbus::MethodCall* call) {
SessionManagerImpl::Error error;
impl_->EmitLoginPromptVisible(&error);
if (error.is_set())
return CreateError(call, error.name(), error.message());
return scoped_ptr<dbus::Response>(dbus::Response::FromMethodCall(call));
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::EnableChromeTesting(
dbus::MethodCall* call) {
dbus::MessageReader reader(call);
bool relaunch;
std::vector<std::string> extra_args;
if (!reader.PopBool(&relaunch) || !reader.PopArrayOfStrings(&extra_args))
return CreateInvalidArgsError(call, call->GetSignature());
SessionManagerImpl::Error error;
std::string testing_path =
impl_->EnableChromeTesting(relaunch, extra_args, &error);
return CraftAppropriateResponseWithString(call, error, testing_path);
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::StartSession(
dbus::MethodCall* call) {
dbus::MessageReader reader(call);
std::string email, unique_id;
if (!reader.PopString(&email) || !reader.PopString(&unique_id))
return CreateInvalidArgsError(call, call->GetSignature());
SessionManagerImpl::Error error;
bool success = impl_->StartSession(email, unique_id, &error);
return CraftAppropriateResponseWithBool(call, error, success);
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::StopSession(
dbus::MethodCall* call) {
// Though this takes a string (unique_id), it is ignored.
bool success = impl_->StopSession();
scoped_ptr<dbus::Response> response(dbus::Response::FromMethodCall(call));
dbus::MessageWriter writer(response.get());
writer.AppendBool(success);
return response.Pass();
}
void SessionManagerDBusAdaptor::StorePolicy(
dbus::MethodCall* call,
dbus::ExportedObject::ResponseSender sender) {
const uint8_t* policy_blob = NULL;
size_t policy_blob_len = 0;
dbus::MessageReader reader(call);
// policy_blob points into reader after pop.
if (!reader.PopArrayOfBytes(&policy_blob, &policy_blob_len)) {
sender.Run(CreateInvalidArgsError(call, call->GetSignature()).Pass());
} else {
impl_->StorePolicy(policy_blob, policy_blob_len,
DBusMethodCompletion::CreateCallback(call, sender));
// Response will be sent asynchronously.
}
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::RetrievePolicy(
dbus::MethodCall* call) {
std::vector<uint8_t> policy_data;
SessionManagerImpl::Error error;
impl_->RetrievePolicy(&policy_data, &error);
return CraftAppropriateResponseWithBytes(call, error, policy_data);
}
void SessionManagerDBusAdaptor::StorePolicyForUser(
dbus::MethodCall* call,
dbus::ExportedObject::ResponseSender sender) {
std::string user_email;
const uint8_t* policy_blob = NULL;
size_t policy_blob_len = 0;
dbus::MessageReader reader(call);
// policy_blob points into reader after pop.
if (!reader.PopString(&user_email) ||
!reader.PopArrayOfBytes(&policy_blob, &policy_blob_len)) {
sender.Run(CreateInvalidArgsError(call, call->GetSignature()).Pass());
} else {
impl_->StorePolicyForUser(user_email,
policy_blob,
policy_blob_len,
DBusMethodCompletion::CreateCallback(call,
sender));
// Response will normally be sent asynchronously.
}
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::RetrievePolicyForUser(
dbus::MethodCall* call) {
std::string user_email;
dbus::MessageReader reader(call);
if (!reader.PopString(&user_email))
return CreateInvalidArgsError(call, call->GetSignature());
std::vector<uint8_t> policy_data;
SessionManagerImpl::Error error;
impl_->RetrievePolicyForUser(user_email, &policy_data, &error);
return CraftAppropriateResponseWithBytes(call, error, policy_data);
}
void SessionManagerDBusAdaptor::StoreDeviceLocalAccountPolicy(
dbus::MethodCall* call,
dbus::ExportedObject::ResponseSender sender) {
std::string account_id;
const uint8_t* policy_blob = NULL;
size_t policy_blob_len = 0;
dbus::MessageReader reader(call);
// policy_blob points into reader after pop.
if (!reader.PopString(&account_id) ||
!reader.PopArrayOfBytes(&policy_blob, &policy_blob_len)) {
sender.Run(CreateInvalidArgsError(call, call->GetSignature()).Pass());
} else {
impl_->StoreDeviceLocalAccountPolicy(
account_id,
policy_blob,
policy_blob_len,
DBusMethodCompletion::CreateCallback(call, sender));
// Response will be sent asynchronously.
}
}
scoped_ptr<dbus::Response>
SessionManagerDBusAdaptor::RetrieveDeviceLocalAccountPolicy(
dbus::MethodCall* call) {
std::string account_id;
dbus::MessageReader reader(call);
if (!reader.PopString(&account_id))
return CreateInvalidArgsError(call, call->GetSignature());
std::vector<uint8_t> policy_data;
SessionManagerImpl::Error error;
impl_->RetrieveDeviceLocalAccountPolicy(account_id, &policy_data, &error);
return CraftAppropriateResponseWithBytes(call, error, policy_data);
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::RetrieveSessionState(
dbus::MethodCall* call) {
scoped_ptr<dbus::Response> response(dbus::Response::FromMethodCall(call));
dbus::MessageWriter writer(response.get());
writer.AppendString(impl_->RetrieveSessionState());
return response.Pass();
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::RetrieveActiveSessions(
dbus::MethodCall* call) {
std::map<std::string, std::string> sessions;
impl_->RetrieveActiveSessions(&sessions);
scoped_ptr<dbus::Response> response(dbus::Response::FromMethodCall(call));
dbus::MessageWriter writer(response.get());
dbus::MessageWriter array_writer(NULL);
writer.OpenArray("{ss}", &array_writer);
for (std::map<std::string, std::string>::const_iterator it = sessions.begin();
it != sessions.end();
++it) {
dbus::MessageWriter entry_writer(NULL);
array_writer.OpenDictEntry(&entry_writer);
entry_writer.AppendString(it->first);
entry_writer.AppendString(it->second);
array_writer.CloseContainer(&entry_writer);
}
writer.CloseContainer(&array_writer);
return response.Pass();
}
scoped_ptr<dbus::Response>
SessionManagerDBusAdaptor::HandleSupervisedUserCreationStarting(
dbus::MethodCall* call) {
impl_->HandleSupervisedUserCreationStarting();
return scoped_ptr<dbus::Response>(dbus::Response::FromMethodCall(call));
}
scoped_ptr<dbus::Response>
SessionManagerDBusAdaptor::HandleSupervisedUserCreationFinished(
dbus::MethodCall* call) {
impl_->HandleSupervisedUserCreationFinished();
return scoped_ptr<dbus::Response>(dbus::Response::FromMethodCall(call));
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::LockScreen(
dbus::MethodCall* call) {
SessionManagerImpl::Error error;
impl_->LockScreen(&error);
if (error.is_set())
return CreateError(call, error.name(), error.message());
return scoped_ptr<dbus::Response>(dbus::Response::FromMethodCall(call));
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::HandleLockScreenShown(
dbus::MethodCall* call) {
impl_->HandleLockScreenShown();
return scoped_ptr<dbus::Response>(dbus::Response::FromMethodCall(call));
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::HandleLockScreenDismissed(
dbus::MethodCall* call) {
impl_->HandleLockScreenDismissed();
return scoped_ptr<dbus::Response>(dbus::Response::FromMethodCall(call));
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::RestartJob(
dbus::MethodCall* call) {
int pid;
std::string arguments;
dbus::MessageReader reader(call);
if (!reader.PopInt32(&pid) || !reader.PopString(&arguments))
return CreateInvalidArgsError(call, call->GetSignature());
SessionManagerImpl::Error error;
bool success = impl_->RestartJob(static_cast<pid_t>(pid), arguments, &error);
return CraftAppropriateResponseWithBool(call, error, success);
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::RestartJobWithAuth(
dbus::MethodCall* call) {
dbus::FileDescriptor fd;
std::string arguments;
dbus::MessageReader reader(call);
if (!reader.PopFileDescriptor(&fd) || !reader.PopString(&arguments))
return CreateInvalidArgsError(call, call->GetSignature());
fd.CheckValidity();
CHECK(fd.is_valid());
SessionManagerImpl::Error error;
if (impl_->RestartJobWithAuth(fd.value(), arguments, &error))
return dbus::Response::FromMethodCall(call);
return CreateError(call, error.name(), error.message());
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::StartDeviceWipe(
dbus::MethodCall* call) {
SessionManagerImpl::Error error;
impl_->StartDeviceWipe("session_manager_dbus_request", &error);
return CraftAppropriateResponseWithBool(call, error, true);
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::SetFlagsForUser(
dbus::MethodCall* call) {
dbus::MessageReader reader(call);
std::string user_email;
std::vector<std::string> session_user_flags;
if (!reader.PopString(&user_email) ||
!reader.PopArrayOfStrings(&session_user_flags)) {
return CreateInvalidArgsError(call, call->GetSignature());
}
impl_->SetFlagsForUser(user_email, session_user_flags);
return scoped_ptr<dbus::Response>(dbus::Response::FromMethodCall(call));
}
void SessionManagerDBusAdaptor::GetServerBackedStateKeys(
dbus::MethodCall* call,
dbus::ExportedObject::ResponseSender sender) {
std::vector<std::vector<uint8_t>> state_keys;
impl_->RequestServerBackedStateKeys(
base::Bind(&HandleGetServerBackedStateKeysCompletion, call, sender));
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::InitMachineInfo(
dbus::MethodCall* call) {
dbus::MessageReader reader(call);
std::string data;
if (!reader.PopString(&data))
return CreateInvalidArgsError(call, call->GetSignature());
SessionManagerImpl::Error error;
impl_->InitMachineInfo(data, &error);
if (error.is_set())
return CreateError(call, error.name(), error.message());
return scoped_ptr<dbus::Response>(dbus::Response::FromMethodCall(call));
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::Introspect(
dbus::MethodCall* call) {
std::string output;
if (!base::ReadFileToString(base::FilePath(kBindingsPath), &output)) {
PLOG(ERROR) << "Can't read XML bindings from disk:";
return CreateError(call, "Can't read XML bindings from disk.", "");
}
scoped_ptr<dbus::Response> response(dbus::Response::FromMethodCall(call));
dbus::MessageWriter writer(response.get());
writer.AppendString(output);
return response.Pass();
}
void SessionManagerDBusAdaptor::ExportSyncDBusMethod(
dbus::ExportedObject* object,
const std::string& method_name,
SyncDBusMethodCallMemberFunction member) {
DCHECK(object);
CHECK(object->ExportMethodAndBlock(
kSessionManagerInterface,
method_name,
base::Bind(&HandleSynchronousDBusMethodCall,
base::Bind(member, base::Unretained(this)))));
}
void SessionManagerDBusAdaptor::ExportAsyncDBusMethod(
dbus::ExportedObject* object,
const std::string& method_name,
AsyncDBusMethodCallMemberFunction member) {
DCHECK(object);
CHECK(
object->ExportMethodAndBlock(kSessionManagerInterface,
method_name,
base::Bind(member, base::Unretained(this))));
}
} // namespace login_manager