Commit 737e70ac authored by rsesek@chromium.org's avatar rsesek@chromium.org

Add an XPC implementation of sandbox::MessageServer.

BUG=382931
R=mark@chromium.org

Review URL: https://codereview.chromium.org/404893002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@284288 0039d316-1c4b-4281-b951-d872f2087c98
parent 2f32cebd
...@@ -17,7 +17,10 @@ component("sandbox") { ...@@ -17,7 +17,10 @@ component("sandbox") {
"os_compatibility.h", "os_compatibility.h",
"policy.cc", "policy.cc",
"policy.h", "policy.h",
"xpc.cc",
"xpc.h", "xpc.h",
"xpc_message_server.cc",
"xpc_message_server.h",
] ]
defines = [ "SANDBOX_IMPLEMENTATION" ] defines = [ "SANDBOX_IMPLEMENTATION" ]
...@@ -51,6 +54,7 @@ action("generate_stubs") { ...@@ -51,6 +54,7 @@ action("generate_stubs") {
"-e", rebase_path(generate_stubs_header, root_build_dir), "-e", rebase_path(generate_stubs_header, root_build_dir),
"-s", generate_stubs_output_stem, "-s", generate_stubs_output_stem,
"-p", generate_stubs_project, "-p", generate_stubs_project,
"-x", "SANDBOX_EXPORT",
] ]
args += rebase_path(sources, root_build_dir) args += rebase_path(sources, root_build_dir)
} }
...@@ -60,6 +64,7 @@ test("sandbox_mac_unittests") { ...@@ -60,6 +64,7 @@ test("sandbox_mac_unittests") {
"bootstrap_sandbox_unittest.mm", "bootstrap_sandbox_unittest.mm",
"dispatch_source_mach_unittest.cc", "dispatch_source_mach_unittest.cc",
"policy_unittest.cc", "policy_unittest.cc",
"xpc_message_server_unittest.cc",
] ]
libs = [ libs = [
......
...@@ -21,7 +21,10 @@ ...@@ -21,7 +21,10 @@
'os_compatibility.h', 'os_compatibility.h',
'policy.cc', 'policy.cc',
'policy.h', 'policy.h',
'xpc.cc',
'xpc.h', 'xpc.h',
'xpc_message_server.cc',
'xpc_message_server.h',
], ],
'dependencies': [ 'dependencies': [
'../base/base.gyp:base', '../base/base.gyp:base',
...@@ -68,6 +71,7 @@ ...@@ -68,6 +71,7 @@
'-e', '<(generate_stubs_header_path)', '-e', '<(generate_stubs_header_path)',
'-s', '<(generate_stubs_output_stem)', '-s', '<(generate_stubs_output_stem)',
'-p', '<(generate_stubs_project)', '-p', '<(generate_stubs_project)',
'-x', 'SANDBOX_EXPORT',
'<(generate_stubs_sig_public_path)', '<(generate_stubs_sig_public_path)',
'<(generate_stubs_sig_private_path)', '<(generate_stubs_sig_private_path)',
], ],
...@@ -83,6 +87,7 @@ ...@@ -83,6 +87,7 @@
'bootstrap_sandbox_unittest.mm', 'bootstrap_sandbox_unittest.mm',
'dispatch_source_mach_unittest.cc', 'dispatch_source_mach_unittest.cc',
'policy_unittest.cc', 'policy_unittest.cc',
'xpc_message_server_unittest.cc',
], ],
'dependencies': [ 'dependencies': [
'sandbox', 'sandbox',
......
// Copyright 2014 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 "sandbox/mac/xpc.h"
namespace sandbox {
bool InitializeXPC() {
std::vector<std::string> path_list;
path_list.push_back("/usr/lib/system/libxpc.dylib");
sandbox_mac::StubPathMap path_map;
path_map[sandbox_mac::kModuleXpc_stubs] = path_list;
path_map[sandbox_mac::kModuleXpc_private_stubs] = path_list;
return sandbox_mac::InitializeStubs(path_map);
}
} // namespace sandbox
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
#include <mach/mach.h> #include <mach/mach.h>
#include "sandbox/sandbox_export.h"
// C++ library loader. // C++ library loader.
#include "sandbox/mac/xpc_stubs.h" #include "sandbox/mac/xpc_stubs.h"
...@@ -37,4 +39,11 @@ extern "C" { ...@@ -37,4 +39,11 @@ extern "C" {
#endif #endif
namespace sandbox {
// Dynamically loads the XPC library.
bool SANDBOX_EXPORT InitializeXPC();
} // namespace sandbox
#endif // SANDBOX_MAC_XPC_H_ #endif // SANDBOX_MAC_XPC_H_
// Copyright 2014 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 "sandbox/mac/xpc_message_server.h"
#include <bsm/libbsm.h>
#include <string>
#include "base/mac/mach_logging.h"
#include "base/strings/stringprintf.h"
#include "sandbox/mac/dispatch_source_mach.h"
#include "sandbox/mac/xpc.h"
namespace sandbox {
XPCMessageServer::XPCMessageServer(MessageDemuxer* demuxer,
mach_port_t server_receive_right)
: demuxer_(demuxer),
server_port_(server_receive_right),
reply_message_(NULL) {
}
XPCMessageServer::~XPCMessageServer() {
}
bool XPCMessageServer::Initialize() {
// Allocate a port for use as a new server port if one was not passed to the
// constructor.
if (!server_port_.is_valid()) {
mach_port_t port;
kern_return_t kr;
if ((kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
&port)) != KERN_SUCCESS) {
MACH_LOG(ERROR, kr) << "Failed to allocate new server port.";
return false;
}
server_port_.reset(port);
}
std::string label = base::StringPrintf(
"org.chromium.sandbox.XPCMessageServer.%p", demuxer_);
dispatch_source_.reset(new DispatchSourceMach(
label.c_str(), server_port_.get(), ^{ ReceiveMessage(); }));
dispatch_source_->Resume();
return true;
}
pid_t XPCMessageServer::GetMessageSenderPID(IPCMessage request) {
audit_token_t token;
xpc_dictionary_get_audit_token(request.xpc, &token);
// TODO(rsesek): In the 10.7 SDK, there's audit_token_to_pid().
pid_t sender_pid;
audit_token_to_au32(token,
NULL, NULL, NULL, NULL, NULL, &sender_pid, NULL, NULL);
return sender_pid;
}
IPCMessage XPCMessageServer::CreateReply(IPCMessage request) {
if (!reply_message_)
reply_message_ = xpc_dictionary_create_reply(request.xpc);
IPCMessage reply;
reply.xpc = reply_message_;
return reply;
}
bool XPCMessageServer::SendReply(IPCMessage reply) {
int rv = xpc_pipe_routine_reply(reply.xpc);
if (rv) {
LOG(ERROR) << "Failed to xpc_pipe_routine_reply(): " << rv;
return false;
}
return true;
}
void XPCMessageServer::ForwardMessage(IPCMessage request,
mach_port_t destination) {
xpc_pipe_t pipe = xpc_pipe_create_from_port(destination, 0);
int rv = xpc_pipe_routine_forward(pipe, request.xpc);
if (rv) {
LOG(ERROR) << "Failed to xpc_pipe_routine_forward(): " << rv;
}
xpc_release(pipe);
}
void XPCMessageServer::RejectMessage(IPCMessage request, int error_code) {
IPCMessage reply = CreateReply(request);
xpc_dictionary_set_int64(reply.xpc, "error", error_code);
SendReply(reply);
}
mach_port_t XPCMessageServer::GetServerPort() const {
return server_port_.get();
}
void XPCMessageServer::ReceiveMessage() {
IPCMessage request;
int rv = xpc_pipe_receive(server_port_, &request.xpc);
if (rv) {
LOG(ERROR) << "Failed to xpc_pipe_receive(): " << rv;
return;
}
demuxer_->DemuxMessage(request);
xpc_release(request.xpc);
if (reply_message_) {
xpc_release(reply_message_);
reply_message_ = NULL;
}
}
} // namespace sandbox
// Copyright 2014 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.
#ifndef SANDBOX_MAC_XPC_MESSAGE_SERVER_H_
#define SANDBOX_MAC_XPC_MESSAGE_SERVER_H_
#include "base/mac/scoped_mach_port.h"
#include "base/memory/scoped_ptr.h"
#include "sandbox/mac/message_server.h"
#include "sandbox/mac/xpc.h"
#include "sandbox/sandbox_export.h"
namespace sandbox {
class DispatchSourceMach;
// An implementation of MessageServer that uses XPC pipes to read and write XPC
// messages from a Mach port.
class SANDBOX_EXPORT XPCMessageServer : public MessageServer {
public:
// Creates a new XPC message server that will send messages to |demuxer|
// for handling. If the |server_receive_right| is non-NULL, this class will
// take ownership of the port and it will be used to receive messages.
// Otherwise the server will create a new receive right on which to listen.
XPCMessageServer(MessageDemuxer* demuxer,
mach_port_t server_receive_right);
virtual ~XPCMessageServer();
// MessageServer:
virtual bool Initialize() OVERRIDE;
virtual pid_t GetMessageSenderPID(IPCMessage request) OVERRIDE;
virtual IPCMessage CreateReply(IPCMessage request) OVERRIDE;
virtual bool SendReply(IPCMessage reply) OVERRIDE;
virtual void ForwardMessage(IPCMessage request,
mach_port_t destination) OVERRIDE;
// Creates an error reply message with a field "error" set to |error_code|.
virtual void RejectMessage(IPCMessage request, int error_code) OVERRIDE;
virtual mach_port_t GetServerPort() const OVERRIDE;
private:
// Reads a message from the XPC pipe.
void ReceiveMessage();
// The demuxer delegate. Weak.
MessageDemuxer* demuxer_;
// The Mach port on which the server is receiving requests.
base::mac::ScopedMachReceiveRight server_port_;
// MACH_RECV dispatch source that handles the |server_port_|.
scoped_ptr<DispatchSourceMach> dispatch_source_;
// The reply message, if one has been created.
xpc_object_t reply_message_;
DISALLOW_COPY_AND_ASSIGN(XPCMessageServer);
};
} // namespace sandbox
#endif // SANDBOX_MAC_XPC_MESSAGE_SERVER_H_
// Copyright 2014 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 "sandbox/mac/xpc_message_server.h"
#include <Block.h>
#include <mach/mach.h>
#include <servers/bootstrap.h>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/mac/mac_util.h"
#include "base/mac/scoped_mach_port.h"
#include "base/process/kill.h"
#include "base/test/multiprocess_test.h"
#include "sandbox/mac/xpc.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"
namespace sandbox {
class XPCMessageServerTest : public testing::Test {
public:
virtual void SetUp() OVERRIDE {
if (!RunXPCTest())
return;
ASSERT_TRUE(InitializeXPC());
}
bool RunXPCTest() {
return base::mac::IsOSLionOrLater();
}
};
// A MessageDemuxer that manages a test server and executes a block for every
// message.
class BlockDemuxer : public MessageDemuxer {
public:
BlockDemuxer()
: demux_block_(NULL),
server_(this, MACH_PORT_NULL),
pipe_(NULL) {
}
virtual ~BlockDemuxer() {
if (pipe_)
xpc_release(pipe_);
if (demux_block_)
Block_release(demux_block_);
}
// Starts running the server, given a block to handle incoming IPC messages.
bool Initialize(void (^demux_block)(IPCMessage request)) {
if (!server_.Initialize())
return false;
// Create a send right on the port so that the XPC pipe can be created.
if (mach_port_insert_right(mach_task_self(), server_.GetServerPort(),
server_.GetServerPort(), MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS) {
return false;
}
scoped_send_right_.reset(server_.GetServerPort());
demux_block_ = Block_copy(demux_block);
pipe_ = xpc_pipe_create_from_port(server_.GetServerPort(), 0);
return true;
}
virtual void DemuxMessage(IPCMessage request) OVERRIDE {
demux_block_(request);
}
xpc_pipe_t pipe() { return pipe_; }
XPCMessageServer* server() { return &server_; }
private:
void (^demux_block_)(IPCMessage request);
XPCMessageServer server_;
base::mac::ScopedMachSendRight scoped_send_right_;
xpc_pipe_t pipe_;
};
#define XPC_TEST_F(name) TEST_F(XPCMessageServerTest, name) { \
if (!RunXPCTest()) \
return; \
XPC_TEST_F(ReceiveMessage) // {
BlockDemuxer fixture;
XPCMessageServer* server = fixture.server();
uint64_t __block value = 0;
ASSERT_TRUE(fixture.Initialize(^(IPCMessage request) {
value = xpc_dictionary_get_uint64(request.xpc, "test_value");
server->SendReply(server->CreateReply(request));
}));
xpc_object_t request = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_uint64(request, "test_value", 42);
xpc_object_t reply;
EXPECT_EQ(0, xpc_pipe_routine(fixture.pipe(), request, &reply));
EXPECT_EQ(42u, value);
xpc_release(request);
xpc_release(reply);
}
XPC_TEST_F(RejectMessage) // {
BlockDemuxer fixture;
XPCMessageServer* server = fixture.server();
ASSERT_TRUE(fixture.Initialize(^(IPCMessage request) {
server->RejectMessage(request, EPERM);
}));
xpc_object_t request = xpc_dictionary_create(NULL, NULL, 0);
xpc_object_t reply;
EXPECT_EQ(0, xpc_pipe_routine(fixture.pipe(), request, &reply));
EXPECT_EQ(EPERM, xpc_dictionary_get_int64(reply, "error"));
xpc_release(request);
xpc_release(reply);
}
char kGetSenderPID[] = "org.chromium.sandbox.test.GetSenderPID";
XPC_TEST_F(GetSenderPID) // {
BlockDemuxer fixture;
XPCMessageServer* server = fixture.server();
pid_t __block sender_pid = 0;
int64_t __block child_pid = 0;
ASSERT_TRUE(fixture.Initialize(^(IPCMessage request) {
sender_pid = server->GetMessageSenderPID(request);
child_pid = xpc_dictionary_get_int64(request.xpc, "child_pid");
}));
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
kern_return_t kr = bootstrap_register(bootstrap_port, kGetSenderPID,
server->GetServerPort());
#pragma GCC diagnostic pop
ASSERT_EQ(KERN_SUCCESS, kr);
base::ProcessHandle child_handle = base::SpawnMultiProcessTestChild(
"GetSenderPID",
base::GetMultiProcessTestChildBaseCommandLine(),
base::LaunchOptions());
ASSERT_NE(base::kNullProcessHandle, child_handle);
int exit_code = -1;
ASSERT_TRUE(base::WaitForExitCode(child_handle, &exit_code));
EXPECT_EQ(0, exit_code);
EXPECT_EQ(base::GetProcId(child_handle), sender_pid);
EXPECT_EQ(base::GetProcId(child_handle), child_pid);
EXPECT_EQ(sender_pid, child_pid);
base::CloseProcessHandle(child_handle);
}
MULTIPROCESS_TEST_MAIN(GetSenderPID) {
CHECK(sandbox::InitializeXPC());
mach_port_t port = MACH_PORT_NULL;
CHECK_EQ(KERN_SUCCESS, bootstrap_look_up(bootstrap_port, kGetSenderPID,
&port));
base::mac::ScopedMachSendRight scoped_port(port);
xpc_pipe_t pipe = xpc_pipe_create_from_port(port, 0);
xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_int64(message, "child_pid", getpid());
CHECK_EQ(0, xpc_pipe_simpleroutine(pipe, message));
xpc_release(message);
xpc_release(pipe);
return 0;
}
XPC_TEST_F(ForwardMessage) // {
BlockDemuxer first;
XPCMessageServer* first_server = first.server();
BlockDemuxer second;
XPCMessageServer* second_server = second.server();
ASSERT_TRUE(first.Initialize(^(IPCMessage request) {
xpc_dictionary_set_int64(request.xpc, "seen_by_first", 1);
first_server->ForwardMessage(request, second_server->GetServerPort());
}));
ASSERT_TRUE(second.Initialize(^(IPCMessage request) {
IPCMessage reply = second_server->CreateReply(request);
xpc_dictionary_set_int64(reply.xpc, "seen_by_first",
xpc_dictionary_get_int64(request.xpc, "seen_by_first"));
xpc_dictionary_set_int64(reply.xpc, "seen_by_second", 2);
second_server->SendReply(reply);
}));
xpc_object_t request = xpc_dictionary_create(NULL, NULL, 0);
xpc_object_t reply;
ASSERT_EQ(0, xpc_pipe_routine(first.pipe(), request, &reply));
EXPECT_EQ(1, xpc_dictionary_get_int64(reply, "seen_by_first"));
EXPECT_EQ(2, xpc_dictionary_get_int64(reply, "seen_by_second"));
xpc_release(request);
xpc_release(reply);
}
} // namespace sandbox
...@@ -7,11 +7,13 @@ ...@@ -7,11 +7,13 @@
// tools/generate_stubs for creating a dynamic library loader. // tools/generate_stubs for creating a dynamic library loader.
// Dictionary manipulation. // Dictionary manipulation.
void xpc_dictionary_set_mach_send(xpc_object_t dict, const char* name, mach_port_t port); void xpc_dictionary_set_mach_send(xpc_object_t dictionary, const char* name, mach_port_t port);
void xpc_dictionary_get_audit_token(xpc_object_t dictionary, audit_token_t* token);
// Pipe methods. // Pipe methods.
xpc_pipe_t xpc_pipe_create_from_port(mach_port_t port, int flags); xpc_pipe_t xpc_pipe_create_from_port(mach_port_t port, int flags);
int xpc_pipe_receive(mach_port_t port, xpc_object_t* message); int xpc_pipe_receive(mach_port_t port, xpc_object_t* message);
int xpc_pipe_routine(xpc_pipe_t pipe, xpc_object_t request, xpc_object_t* reply); int xpc_pipe_routine(xpc_pipe_t pipe, xpc_object_t request, xpc_object_t* reply);
int xpc_pipe_routine_reply(xpc_object_t reply); int xpc_pipe_routine_reply(xpc_object_t reply);
int xpc_pipe_simpleroutine(xpc_pipe_t pipe, xpc_object_t message);
int xpc_pipe_routine_forward(xpc_pipe_t forward_to, xpc_object_t request); int xpc_pipe_routine_forward(xpc_pipe_t forward_to, xpc_object_t request);
...@@ -10,7 +10,10 @@ ...@@ -10,7 +10,10 @@
void xpc_release(xpc_object_t object); void xpc_release(xpc_object_t object);
// Dictionary manipulation. // Dictionary manipulation.
xpc_object_t xpc_dictionary_create(const char* const keys, const xpc_object_t* values, size_t count);
const char* xpc_dictionary_get_string(xpc_object_t dictionary, const char* key); const char* xpc_dictionary_get_string(xpc_object_t dictionary, const char* key);
uint64_t xpc_dictionary_get_uint64(xpc_object_t dictionary, const char* key); uint64_t xpc_dictionary_get_uint64(xpc_object_t dictionary, const char* key);
void xpc_dictionary_set_uint64(xpc_object_t dictionary, const char* key, int64_t value);
int64_t xpc_dictionary_get_int64(xpc_object_t dictionary, const char* key);
void xpc_dictionary_set_int64(xpc_object_t dictionary, const char* key, int64_t value); void xpc_dictionary_set_int64(xpc_object_t dictionary, const char* key, int64_t value);
xpc_object_t xpc_dictionary_create_reply(xpc_object_t request); xpc_object_t xpc_dictionary_create_reply(xpc_object_t request);
...@@ -5,6 +5,10 @@ ...@@ -5,6 +5,10 @@
#ifndef SANDBOX_MAC_XPC_STUBS_HEADER_FRAGMENT_ #ifndef SANDBOX_MAC_XPC_STUBS_HEADER_FRAGMENT_
#define SANDBOX_MAC_XPC_STUBS_HEADER_FRAGMENT_ #define SANDBOX_MAC_XPC_STUBS_HEADER_FRAGMENT_
#include <bsm/libbsm.h>
#include "sandbox/sandbox_export.h"
// Declare or include public types. // Declare or include public types.
#if !defined(MAC_OS_X_VERSION_10_7) || \ #if !defined(MAC_OS_X_VERSION_10_7) || \
MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment