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