xref: /xnu-10002.1.13/osfmk/ipc/ipc_service_port.c (revision 1031c584a5e37aff177559b9f69dbd3c8c3fd30a)
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