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
12 namespace sandbox {
13
14 namespace {
15
16 // Verified from launchd-329.3.3 (10.6.8).
17 struct 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
25 struct 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)
35 struct 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)
51 typedef int vproc_gsk_t; // Defined as an enum in liblaunch/vproc_priv.h.
52 struct 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.
strnlen(const char * str,size_t maxlen)61 size_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
70 template <typename R>
LaunchdLookUp2GetRequestName(const mach_msg_header_t * header)71 std::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
81 template <typename R>
LaunchdLookUp2FillReply(mach_msg_header_t * header,mach_port_t port)82 void 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
94 template <typename R>
LaunchdSwapIntegerIsGetOnly(const mach_msg_header_t * header)95 bool 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
GetLaunchdCompatibilityShim()102 const 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