1 /*
2 * Copyright (c) 2000-2020 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <mach/port.h>
30 #include <mach/kern_return.h>
31 #include <kern/ipc_tt.h>
32 #include <ipc/ipc_port.h>
33 #include <kern/zalloc.h>
34 #include <kern/kalloc.h>
35 #include <kern/mach_param.h>
36 #include <mach/message.h>
37 #include <kern/mach_filter.h>
38 #include <ipc/ipc_service_port.h>
39 #include <security/mac_mach_internal.h>
40
41 #define XPC_DOMAIN_PORT 7 /* This value should match what is in <xpc/launch_private.h> */
42
43 ZONE_DEFINE_TYPE(ipc_service_port_label_zone, "ipc_service_port_label",
44 struct ipc_service_port_label, ZC_ZFREE_CLEARMEM | ZC_NOCACHING);
45
46 #if CONFIG_SERVICE_PORT_INFO
47 const bool kdp_ipc_have_splabel = true;
48 #else
49 const bool kdp_ipc_have_splabel = false;
50 #endif
51
52 void
kdp_ipc_splabel_size(size_t * ispl_size,size_t * maxnamelen)53 kdp_ipc_splabel_size(size_t *ispl_size, size_t *maxnamelen)
54 {
55 *ispl_size = sizeof(struct ipc_service_port_label);
56 *maxnamelen = MACH_SERVICE_PORT_INFO_STRING_NAME_MAX_BUF_LEN + 1;
57 }
58
59 void
kdp_ipc_fill_splabel(struct ipc_service_port_label * ispl,struct portlabel_info * spl,const char ** namep)60 kdp_ipc_fill_splabel(struct ipc_service_port_label *ispl,
61 struct portlabel_info *spl, const char **namep)
62 {
63 #pragma unused(ispl, spl, namep)
64
65 /* validate that ispl is in our zone */
66 #if CONFIG_SERVICE_PORT_INFO
67 *namep = ispl->ispl_service_name;
68 spl->portlabel_domain = ispl->ispl_domain;
69 if (ipc_service_port_label_is_throttled(ispl)) {
70 spl->portlabel_flags |= STACKSHOT_PORTLABEL_THROTTLED;
71 }
72 #endif
73 }
74
75 /*
76 * Name: ipc_service_port_label_alloc
77 *
78 * Description: Allocates the service port label
79 *
80 * Args:
81 * sp_info: service port string name, length, domain information
82 * send_side_filtering: indicates if the messages should be filtered during mach_msg_send
83 * port_label_ptr: used to return the allocated service_port_label
84 *
85 * Returns:
86 * KERN_SUCCESS
87 */
88 kern_return_t
ipc_service_port_label_alloc(mach_service_port_info_t sp_info,void ** port_label_ptr)89 ipc_service_port_label_alloc(mach_service_port_info_t sp_info, void **port_label_ptr)
90 {
91 ipc_service_port_label_t sp_label = IPC_SERVICE_PORT_LABEL_NULL;
92 kern_return_t ret;
93 void *sblabel = NULL;
94
95 sp_label = zalloc(ipc_service_port_label_zone);
96
97 if (mach_msg_filter_alloc_service_port_sblabel_callback) {
98 ret = mach_msg_filter_alloc_service_port_sblabel_callback(sp_info, &sblabel);
99 if (ret) {
100 zfree(ipc_service_port_label_zone, sp_label);
101 return ret;
102 }
103 }
104
105 sp_label->ispl_sblabel = sblabel;
106 #if CONFIG_SERVICE_PORT_INFO
107 size_t sp_string_name_len = strlen(sp_info->mspi_string_name);
108 /* We could investigate compressing the names, but it doesn't seem worth it */
109 sp_label->ispl_service_name = kalloc_data(sp_string_name_len + 1, Z_WAITOK);
110 strlcpy(sp_label->ispl_service_name, sp_info->mspi_string_name, sp_string_name_len + 1);
111 sp_label->ispl_domain = sp_info->mspi_domain_type;
112 #endif /* CONFIG_SERVICE_PORT_INFO */
113
114 if (sp_info->mspi_domain_type == XPC_DOMAIN_PORT) {
115 sp_label->ispl_flags |= ISPL_FLAGS_BOOTSTRAP_PORT;
116 }
117 *port_label_ptr = (void *)sp_label;
118 return KERN_SUCCESS;
119 }
120
121 /*
122 * Name: ipc_service_port_dealloc
123 *
124 * Description: Deallocates the service port label
125 *
126 * Args:
127 * ip_splabel: port's ip_splabel
128 *
129 * Returns: None
130 *
131 * Should not be called with the port lock held.
132 */
133 void
ipc_service_port_label_dealloc(void * ip_splabel,bool service_port)134 ipc_service_port_label_dealloc(void *ip_splabel, bool service_port)
135 {
136 void *sblabel = ip_splabel;
137
138 if (service_port) {
139 ipc_service_port_label_t sp_label = (ipc_service_port_label_t)ip_splabel;
140 sblabel = sp_label->ispl_sblabel;
141 #if CONFIG_SERVICE_PORT_INFO
142 kfree_data(sp_label->ispl_service_name, strlen(sp_label->ispl_service_name) + 1);
143 #endif /* CONFIG_SERVICE_PORT_INFO */
144 zfree(ipc_service_port_label_zone, sp_label);
145 }
146
147 if (sblabel) {
148 assert(mach_msg_filter_dealloc_service_port_sblabel_callback);
149 mach_msg_filter_dealloc_service_port_sblabel_callback(sblabel);
150 }
151 }
152
153 /*
154 * Name: ipc_service_port_derive_sblabel
155 *
156 * Description: Derive the port's sandbox label using info from the service port's label
157 *
158 * Args:
159 * service_port_name: send right to a service port
160 * sblabel_ptr: used to return the allocated sblabel
161 *
162 * Returns:
163 * KERN_SUCCESS
164 * KERN_INVALID_NAME: service_port_name is mach_port_null or mach_port_dead
165 * KERN_INVALID_RIGHT: service_port_name is not a send right
166 * KERN_INVALID_CAPABILITY: service_port_name is not a right to a service port
167 */
168 kern_return_t
ipc_service_port_derive_sblabel(mach_port_name_t service_port_name,void ** sblabel_ptr,bool * filter_msgs)169 ipc_service_port_derive_sblabel(mach_port_name_t service_port_name, void **sblabel_ptr, bool *filter_msgs)
170 {
171 ipc_service_port_label_t port_label;
172 void *derived_sblabel = NULL;
173 void *sblabel = NULL;
174 ipc_port_t port;
175 kern_return_t kr;
176 boolean_t send_side_filtering = FALSE;
177 #if CONFIG_MACF && XNU_TARGET_OS_OSX
178 struct mach_service_port_info sp_info = {};
179 #endif
180
181 if (!MACH_PORT_VALID(service_port_name)) {
182 return KERN_INVALID_NAME;
183 }
184
185 if (mach_msg_filter_at_least(MACH_MSG_FILTER_CALLBACKS_VERSION_1)) {
186 kr = ipc_port_translate_send(current_space(), service_port_name, &port);
187 if (kr != KERN_SUCCESS) {
188 return kr;
189 }
190 /* port is locked and active */
191
192 if (ip_is_kolabeled(port) || !port->ip_service_port) {
193 ip_mq_unlock(port);
194 return KERN_INVALID_CAPABILITY;
195 }
196
197 port_label = (ipc_service_port_label_t)port->ip_splabel;
198 if (!port_label) {
199 ip_mq_unlock(port);
200 return KERN_SUCCESS;
201 }
202
203 #if CONFIG_MACF && XNU_TARGET_OS_OSX
204 ipc_service_port_label_get_info(port_label, &sp_info);
205 #endif
206
207 sblabel = port_label->ispl_sblabel;
208 if (sblabel) {
209 mach_msg_filter_retain_sblabel_callback(sblabel);
210 }
211 ip_mq_unlock(port);
212
213 if (sblabel) {
214 /* This callback will release the reference on sblabel */
215 derived_sblabel = mach_msg_filter_derive_sblabel_from_service_port_callback(sblabel, &send_side_filtering);
216 }
217
218 #if CONFIG_MACF && XNU_TARGET_OS_OSX
219 if (sp_info.mspi_string_name[0] != '\0') {
220 mac_proc_notify_service_port_derive(&sp_info);
221 }
222 #endif
223 }
224
225 *sblabel_ptr = derived_sblabel;
226 *filter_msgs = (bool)send_side_filtering;
227 return KERN_SUCCESS;
228 }
229
230 /*
231 * Name: ipc_service_port_get_sblabel
232 *
233 * Description: Get the port's sandbox label.
234 *
235 * Args:
236 * port
237 *
238 * Conditions:
239 * Should be called on an active port with the lock held.
240 *
241 * Returns:
242 * Sandbox label
243 */
244 void *
ipc_service_port_get_sblabel(ipc_port_t port)245 ipc_service_port_get_sblabel(ipc_port_t port)
246 {
247 void *sblabel = NULL;
248 void *ip_splabel = NULL;
249
250 if (port == IP_NULL) {
251 return NULL;
252 }
253
254 ip_mq_lock_held(port);
255 assert(ip_active(port));
256
257 if (ip_is_kolabeled(port) || !port->ip_splabel) {
258 return NULL;
259 }
260
261 ip_splabel = port->ip_splabel;
262
263 if (!port->ip_service_port) {
264 sblabel = ip_splabel;
265 assert(sblabel != NULL);
266 } else {
267 ipc_service_port_label_t sp_label = (ipc_service_port_label_t)ip_splabel;
268 sblabel = sp_label->ispl_sblabel;
269 }
270
271 return sblabel;
272 }
273
274 /*
275 * Name: ipc_service_port_label_set_attr
276 *
277 * Description: Set the remaining port label attributes after port allocation
278 *
279 * Args:
280 * port_splabel
281 * name : port name in launchd's ipc space
282 * context : launchd's port guard; will be restored after a port destroyed notification if non-zero
283 *
284 * Conditions:
285 * Should be called only once in mach_port_construct on a newly created port with the lock held
286 * The context should be set only if the port is guarded.
287 */
288 void
ipc_service_port_label_set_attr(ipc_service_port_label_t port_splabel,mach_port_name_t name,mach_port_context_t context)289 ipc_service_port_label_set_attr(ipc_service_port_label_t port_splabel, mach_port_name_t name, mach_port_context_t context)
290 {
291 assert(port_splabel->ispl_launchd_name == MACH_PORT_NULL);
292 port_splabel->ispl_launchd_name = name;
293 port_splabel->ispl_launchd_context = context;
294 if (context) {
295 ipc_service_port_label_set_flag(port_splabel, ISPL_FLAGS_SPECIAL_PDREQUEST);
296 }
297 }
298
299 /*
300 * Name: ipc_service_port_label_get_attr
301 *
302 * Description: Get the port label attributes
303 *
304 * Args:
305 * port_splabel
306 * name : port name in launchd's ipc space
307 * context : launchd's port guard
308 *
309 * Conditions:
310 * Should be called with port lock held.
311 */
312 void
ipc_service_port_label_get_attr(ipc_service_port_label_t port_splabel,mach_port_name_t * name,mach_port_context_t * context)313 ipc_service_port_label_get_attr(ipc_service_port_label_t port_splabel, mach_port_name_t *name, mach_port_context_t *context)
314 {
315 *name = port_splabel->ispl_launchd_name;
316 *context = port_splabel->ispl_launchd_context;
317 }
318
319 #if CONFIG_SERVICE_PORT_INFO
320 void
ipc_service_port_label_get_info(ipc_service_port_label_t port_splabel,mach_service_port_info_t info)321 ipc_service_port_label_get_info(ipc_service_port_label_t port_splabel, mach_service_port_info_t info)
322 {
323 info->mspi_domain_type = (uint8_t)port_splabel->ispl_domain;
324 size_t sp_string_name_len = strlen(port_splabel->ispl_service_name);
325 strlcpy(info->mspi_string_name, port_splabel->ispl_service_name, sp_string_name_len + 1);
326 }
327 #endif /* CONFIG_SERVICE_PORT_INFO */
328