blob: 0e8d08f5d75075d4df837ad986329c3b57faa785 [file] [log] [blame]
// 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/os_compatibility.h"
#include <servers/bootstrap.h>
#include <stddef.h>
#include <stdint.h>
#include <unistd.h>
#include "base/logging.h"
#include "base/mac/mac_util.h"
#include "base/memory/ptr_util.h"
#include "sandbox/mac/xpc.h"
namespace sandbox {
namespace {
#pragma pack(push, 4)
// Verified from launchd-329.3.3 (10.6.8).
// look_up2_reply_10_7 is the same as the 10_6 version.
struct look_up2_reply_10_7 {
mach_msg_header_t Head;
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t service_port;
};
// Verified from:
// launchd-392.39 (10.7.5)
// launchd-442.26.2 (10.8.5)
// launchd-842.1.4 (10.9.0)
struct look_up2_request_10_7 {
mach_msg_header_t Head;
NDR_record_t NDR;
name_t servicename;
pid_t targetpid;
uuid_t instanceid;
uint64_t flags;
};
// Verified from:
// launchd-329.3.3 (10.6.8)
// launchd-392.39 (10.7.5)
// launchd-442.26.2 (10.8.5)
// launchd-842.1.4 (10.9.0)
typedef int vproc_gsk_t; // Defined as an enum in liblaunch/vproc_priv.h.
struct swap_integer_request_10_7 {
mach_msg_header_t Head;
NDR_record_t NDR;
vproc_gsk_t inkey;
vproc_gsk_t outkey;
int64_t inval;
};
#pragma pack(pop)
// TODO(rsesek): Libc provides strnlen() starting in 10.7.
size_t strnlen(const char* str, size_t maxlen) {
size_t len = 0;
for (; len < maxlen; ++len, ++str) {
if (*str == '\0')
break;
}
return len;
}
class OSCompatibility_10_7 : public OSCompatibility {
public:
OSCompatibility_10_7() {}
~OSCompatibility_10_7() override {}
uint64_t GetMessageSubsystem(const IPCMessage message) override {
return (message.mach->msgh_id / 100) * 100;
}
uint64_t GetMessageID(const IPCMessage message) override {
return message.mach->msgh_id;
}
bool IsServiceLookUpRequest(const IPCMessage message) override {
return GetMessageID(message) == 404;
}
bool IsVprocSwapInteger(const IPCMessage message) override {
return GetMessageID(message) == 416;
}
bool IsXPCDomainManagement(const IPCMessage message) override {
return false;
}
std::string GetServiceLookupName(const IPCMessage message) override {
return GetRequestName<look_up2_request_10_7>(message);
}
void WriteServiceLookUpReply(IPCMessage message,
mach_port_t service_port) override {
auto reply = reinterpret_cast<look_up2_reply_10_7*>(message.mach);
reply->Head.msgh_size = sizeof(*reply);
reply->Head.msgh_bits =
MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND_ONCE) |
MACH_MSGH_BITS_COMPLEX;
reply->msgh_body.msgh_descriptor_count = 1;
reply->service_port.name = service_port;
reply->service_port.disposition = MACH_MSG_TYPE_COPY_SEND;
reply->service_port.type = MACH_MSG_PORT_DESCRIPTOR;
}
bool IsSwapIntegerReadOnly(const IPCMessage message) override {
auto request =
reinterpret_cast<const swap_integer_request_10_7*>(message.mach);
return request->inkey == 0 && request->inval == 0 && request->outkey != 0;
}
protected:
// The 10.6 and 10.7 implementations are the same except for struct offsets,
// so provide this templatized helper.
template <typename R>
static std::string GetRequestName(const IPCMessage message) {
mach_msg_header_t* header = message.mach;
DCHECK_EQ(sizeof(R), header->msgh_size);
const R* request = reinterpret_cast<const R*>(header);
// Make sure the name is properly NUL-terminated.
const size_t name_length =
strnlen(request->servicename, BOOTSTRAP_MAX_NAME_LEN);
std::string name = std::string(request->servicename, name_length);
return name;
}
};
class OSCompatibility_10_10 : public OSCompatibility {
public:
OSCompatibility_10_10() {}
~OSCompatibility_10_10() override {}
uint64_t GetMessageSubsystem(const IPCMessage message) override {
return xpc_dictionary_get_uint64(message.xpc, "subsystem");
}
uint64_t GetMessageID(const IPCMessage message) override {
return xpc_dictionary_get_uint64(message.xpc, "routine");
}
bool IsServiceLookUpRequest(const IPCMessage message) override {
uint64_t subsystem = GetMessageSubsystem(message);
uint64_t id = GetMessageID(message);
// Lookup requests in XPC can either go through the Mach bootstrap subsytem
// (5) from bootstrap_look_up(), or the XPC domain subsystem (3) for
// xpc_connection_create(). Both use the same message format.
return (subsystem == 5 && id == 207) || (subsystem == 3 && id == 804);
}
bool IsVprocSwapInteger(const IPCMessage message) override {
return GetMessageSubsystem(message) == 6 && GetMessageID(message) == 301;
}
bool IsXPCDomainManagement(const IPCMessage message) override {
return GetMessageSubsystem(message) == 3;
}
std::string GetServiceLookupName(const IPCMessage message) override {
const char* name = xpc_dictionary_get_string(message.xpc, "name");
const size_t name_length = strnlen(name, BOOTSTRAP_MAX_NAME_LEN);
return std::string(name, name_length);
}
void WriteServiceLookUpReply(IPCMessage message,
mach_port_t service_port) override {
xpc_dictionary_set_mach_send(message.xpc, "port", service_port);
}
bool IsSwapIntegerReadOnly(const IPCMessage message) override {
return xpc_dictionary_get_bool(message.xpc, "set") == false &&
xpc_dictionary_get_uint64(message.xpc, "ingsk") == 0 &&
xpc_dictionary_get_int64(message.xpc, "in") == 0;
}
};
} // namespace
// static
std::unique_ptr<OSCompatibility> OSCompatibility::CreateForPlatform() {
if (base::mac::IsOSMavericks())
return base::WrapUnique(new OSCompatibility_10_7());
else
return base::WrapUnique(new OSCompatibility_10_10());
}
OSCompatibility::~OSCompatibility() {}
} // namespace sandbox