1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "sandbox/mac/os_compatibility.h"
6
7#include <servers/bootstrap.h>
8#include <unistd.h>
9
10#include "base/mac/mac_util.h"
11
12namespace sandbox {
13
14namespace {
15
16#pragma pack(push, 4)
17// Verified from launchd-329.3.3 (10.6.8).
18struct look_up2_request_10_6 {
19  mach_msg_header_t Head;
20  NDR_record_t NDR;
21  name_t servicename;
22  pid_t targetpid;
23  uint64_t flags;
24};
25
26struct look_up2_reply_10_6 {
27  mach_msg_header_t Head;
28  mach_msg_body_t msgh_body;
29  mach_msg_port_descriptor_t service_port;
30};
31
32// Verified from:
33//   launchd-392.39 (10.7.5)
34//   launchd-442.26.2 (10.8.5)
35//   launchd-842.1.4 (10.9.0)
36struct look_up2_request_10_7 {
37  mach_msg_header_t Head;
38  NDR_record_t NDR;
39  name_t servicename;
40  pid_t targetpid;
41  uuid_t instanceid;
42  uint64_t flags;
43};
44
45// look_up2_reply_10_7 is the same as the 10_6 version.
46
47// Verified from:
48//   launchd-329.3.3 (10.6.8)
49//   launchd-392.39 (10.7.5)
50//   launchd-442.26.2 (10.8.5)
51//   launchd-842.1.4 (10.9.0)
52typedef int vproc_gsk_t;  // Defined as an enum in liblaunch/vproc_priv.h.
53struct swap_integer_request_10_6 {
54  mach_msg_header_t Head;
55  NDR_record_t NDR;
56  vproc_gsk_t inkey;
57  vproc_gsk_t outkey;
58  int64_t inval;
59};
60#pragma pack(pop)
61
62// TODO(rsesek): Libc provides strnlen() starting in 10.7.
63size_t strnlen(const char* str, size_t maxlen) {
64  size_t len = 0;
65  for (; len < maxlen; ++len, ++str) {
66    if (*str == '\0')
67      break;
68  }
69  return len;
70}
71
72uint64_t MachGetMessageID(const IPCMessage message) {
73  return message.mach->msgh_id;
74}
75
76template <typename R>
77std::string LaunchdLookUp2GetRequestName(const IPCMessage message) {
78  mach_msg_header_t* header = message.mach;
79  DCHECK_EQ(sizeof(R), header->msgh_size);
80  const R* request = reinterpret_cast<const R*>(header);
81  // Make sure the name is properly NUL-terminated.
82  const size_t name_length =
83      strnlen(request->servicename, BOOTSTRAP_MAX_NAME_LEN);
84  std::string name = std::string(request->servicename, name_length);
85  return name;
86}
87
88template <typename R>
89void LaunchdLookUp2FillReply(IPCMessage message, mach_port_t port) {
90  R* reply = reinterpret_cast<R*>(message.mach);
91  reply->Head.msgh_size = sizeof(R);
92  reply->Head.msgh_bits =
93      MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND_ONCE) |
94      MACH_MSGH_BITS_COMPLEX;
95  reply->msgh_body.msgh_descriptor_count = 1;
96  reply->service_port.name = port;
97  reply->service_port.disposition = MACH_MSG_TYPE_COPY_SEND;
98  reply->service_port.type = MACH_MSG_PORT_DESCRIPTOR;
99}
100
101template <typename R>
102bool LaunchdSwapIntegerIsGetOnly(const IPCMessage message) {
103  const R* request = reinterpret_cast<const R*>(message.mach);
104  return request->inkey == 0 && request->inval == 0 && request->outkey != 0;
105}
106
107}  // namespace
108
109const LaunchdCompatibilityShim GetLaunchdCompatibilityShim() {
110  LaunchdCompatibilityShim shim = {
111    .ipc_message_get_id = &MachGetMessageID,
112    .msg_id_look_up2 = 404,
113    .msg_id_swap_integer = 416,
114    .look_up2_fill_reply = &LaunchdLookUp2FillReply<look_up2_reply_10_6>,
115    .swap_integer_is_get_only =
116        &LaunchdSwapIntegerIsGetOnly<swap_integer_request_10_6>,
117  };
118
119  if (base::mac::IsOSSnowLeopard()) {
120    shim.look_up2_get_request_name =
121        &LaunchdLookUp2GetRequestName<look_up2_request_10_6>;
122  } else if (base::mac::IsOSLionOrLater() &&
123             !base::mac::IsOSYosemiteOrLater()) {
124    shim.look_up2_get_request_name =
125        &LaunchdLookUp2GetRequestName<look_up2_request_10_7>;
126  } else {
127    DLOG(ERROR) << "Unknown OS, using launchd compatibility shim from 10.7.";
128    shim.look_up2_get_request_name =
129        &LaunchdLookUp2GetRequestName<look_up2_request_10_7>;
130  }
131
132  return shim;
133}
134
135}  // namespace sandbox
136