xref: /xnu-8792.41.9/osfmk/ipc/ipc_service_port.c (revision 5c2921b07a2480ab43ec66f5b9e41cb872bc554f)
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 
40 #define XPC_DOMAIN_PORT 7 /* This value should match what is in <xpc/launch_private.h> */
41 
42 ZONE_DEFINE_TYPE(ipc_service_port_label_zone, "ipc_service_port_label",
43     struct ipc_service_port_label, ZC_ZFREE_CLEARMEM | ZC_NOCACHING);
44 
45 #if CONFIG_SERVICE_PORT_INFO
46 const bool kdp_ipc_have_splabel = true;
47 #else
48 const bool kdp_ipc_have_splabel = false;
49 #endif
50 
51 void
kdp_ipc_splabel_size(size_t * ispl_size,size_t * maxnamelen)52 kdp_ipc_splabel_size(size_t *ispl_size, size_t *maxnamelen)
53 {
54 	*ispl_size = sizeof(struct ipc_service_port_label);
55 	*maxnamelen = MACH_SERVICE_PORT_INFO_STRING_NAME_MAX_BUF_LEN + 1;
56 }
57 
58 void
kdp_ipc_fill_splabel(struct ipc_service_port_label * ispl,struct portlabel_info * spl,const char ** namep)59 kdp_ipc_fill_splabel(struct ipc_service_port_label *ispl,
60     struct portlabel_info *spl, const char **namep)
61 {
62 #pragma unused(ispl, spl, namep)
63 
64 	/* validate that ispl is in our zone */
65 #if CONFIG_SERVICE_PORT_INFO
66 	*namep = ispl->ispl_service_name;
67 	spl->portlabel_domain = ispl->ispl_domain;
68 #endif
69 }
70 
71 /*
72  * Name: ipc_service_port_label_alloc
73  *
74  * Description: Allocates the service port label
75  *
76  * Args:
77  *   sp_info: service port string name, length, domain information
78  *   send_side_filtering: indicates if the messages should be filtered during mach_msg_send
79  *   port_label_ptr: used to return the allocated service_port_label
80  *
81  * Returns:
82  *   KERN_SUCCESS
83  */
84 kern_return_t
ipc_service_port_label_alloc(mach_service_port_info_t sp_info,void ** port_label_ptr)85 ipc_service_port_label_alloc(mach_service_port_info_t sp_info, void **port_label_ptr)
86 {
87 	ipc_service_port_label_t sp_label = IPC_SERVICE_PORT_LABEL_NULL;
88 	kern_return_t ret;
89 	void *sblabel = NULL;
90 
91 	sp_label = zalloc(ipc_service_port_label_zone);
92 
93 	if (mach_msg_filter_alloc_service_port_sblabel_callback) {
94 		ret = mach_msg_filter_alloc_service_port_sblabel_callback(sp_info, &sblabel);
95 		if (ret) {
96 			zfree(ipc_service_port_label_zone, sp_label);
97 			return ret;
98 		}
99 	}
100 
101 	sp_label->ispl_sblabel = sblabel;
102 #if CONFIG_SERVICE_PORT_INFO
103 	size_t sp_string_name_len = strlen(sp_info->mspi_string_name);
104 	/* We could investigate compressing the names, but it doesn't seem worth it */
105 	sp_label->ispl_service_name = kalloc_data(sp_string_name_len + 1, Z_WAITOK);
106 	strlcpy(sp_label->ispl_service_name, sp_info->mspi_string_name, sp_string_name_len + 1);
107 	sp_label->ispl_domain = sp_info->mspi_domain_type;
108 #endif /* CONFIG_SERVICE_PORT_INFO */
109 
110 	if (sp_info->mspi_domain_type == XPC_DOMAIN_PORT) {
111 		sp_label->ispl_flags |= ISPL_FLAGS_BOOTSTRAP_PORT;
112 	}
113 	*port_label_ptr = (void *)sp_label;
114 	return KERN_SUCCESS;
115 }
116 
117 /*
118  * Name: ipc_service_port_dealloc
119  *
120  * Description: Deallocates the service port label
121  *
122  * Args:
123  *   ip_splabel: port's ip_splabel
124  *
125  * Returns: None
126  *
127  * Should not be called with the port lock held.
128  */
129 void
ipc_service_port_label_dealloc(void * ip_splabel,bool service_port)130 ipc_service_port_label_dealloc(void *ip_splabel, bool service_port)
131 {
132 	void *sblabel = ip_splabel;
133 
134 	if (service_port) {
135 		ipc_service_port_label_t sp_label = (ipc_service_port_label_t)ip_splabel;
136 		sblabel = sp_label->ispl_sblabel;
137 #if CONFIG_SERVICE_PORT_INFO
138 		kfree_data(sp_label->ispl_service_name, strlen(sp_label->ispl_service_name) + 1);
139 #endif /* CONFIG_SERVICE_PORT_INFO */
140 		zfree(ipc_service_port_label_zone, sp_label);
141 	}
142 
143 	if (sblabel) {
144 		assert(mach_msg_filter_dealloc_service_port_sblabel_callback);
145 		mach_msg_filter_dealloc_service_port_sblabel_callback(sblabel);
146 	}
147 }
148 
149 /*
150  * Name: ipc_service_port_derive_sblabel
151  *
152  * Description: Derive the port's sandbox label using info from the service port's label
153  *
154  * Args:
155  *   service_port_name: send right to a service port
156  *   sblabel_ptr: used to return the allocated sblabel
157  *
158  * Returns:
159  *   KERN_SUCCESS
160  *   KERN_INVALID_NAME: service_port_name is mach_port_null or mach_port_dead
161  *   KERN_INVALID_RIGHT: service_port_name is not a send right
162  *   KERN_INVALID_CAPABILITY: service_port_name is not a right to a service port
163  */
164 kern_return_t
ipc_service_port_derive_sblabel(mach_port_name_t service_port_name,void ** sblabel_ptr,bool * filter_msgs)165 ipc_service_port_derive_sblabel(mach_port_name_t service_port_name, void **sblabel_ptr, bool *filter_msgs)
166 {
167 	ipc_service_port_label_t port_label;
168 	void *derived_sblabel = NULL;
169 	void *sblabel = NULL;
170 	ipc_port_t port;
171 	kern_return_t kr;
172 	boolean_t send_side_filtering = FALSE;
173 
174 	if (!MACH_PORT_VALID(service_port_name)) {
175 		return KERN_INVALID_NAME;
176 	}
177 
178 	if (mach_msg_filter_at_least(MACH_MSG_FILTER_CALLBACKS_VERSION_1)) {
179 		kr = ipc_port_translate_send(current_space(), service_port_name, &port);
180 		if (kr != KERN_SUCCESS) {
181 			return kr;
182 		}
183 		/* port is locked and active */
184 
185 		if (ip_is_kolabeled(port) || !port->ip_service_port) {
186 			ip_mq_unlock(port);
187 			return KERN_INVALID_CAPABILITY;
188 		}
189 
190 		port_label = (ipc_service_port_label_t)port->ip_splabel;
191 		if (!port_label) {
192 			ip_mq_unlock(port);
193 			return KERN_SUCCESS;
194 		}
195 
196 		sblabel = port_label->ispl_sblabel;
197 		if (sblabel) {
198 			mach_msg_filter_retain_sblabel_callback(sblabel);
199 		}
200 		ip_mq_unlock(port);
201 
202 		if (sblabel) {
203 			/* This callback will release the reference on sblabel */
204 			derived_sblabel = mach_msg_filter_derive_sblabel_from_service_port_callback(sblabel, &send_side_filtering);
205 		}
206 	}
207 
208 	*sblabel_ptr = derived_sblabel;
209 	*filter_msgs = (bool)send_side_filtering;
210 	return KERN_SUCCESS;
211 }
212 
213 /*
214  * Name: ipc_service_port_get_sblabel
215  *
216  * Description: Get the port's sandbox label.
217  *
218  * Args:
219  *   port
220  *
221  * Conditions:
222  *   Should be called on an active port with the lock held.
223  *
224  * Returns:
225  *   Sandbox label
226  */
227 void *
ipc_service_port_get_sblabel(ipc_port_t port)228 ipc_service_port_get_sblabel(ipc_port_t port)
229 {
230 	void *sblabel = NULL;
231 	void *ip_splabel = NULL;
232 
233 	if (port == IP_NULL) {
234 		return NULL;
235 	}
236 
237 	ip_mq_lock_held(port);
238 	assert(ip_active(port));
239 
240 	if (ip_is_kolabeled(port) || !port->ip_splabel) {
241 		return NULL;
242 	}
243 
244 	ip_splabel = port->ip_splabel;
245 
246 	if (!port->ip_service_port) {
247 		sblabel = ip_splabel;
248 		assert(sblabel != NULL);
249 	} else {
250 		ipc_service_port_label_t sp_label = (ipc_service_port_label_t)ip_splabel;
251 		sblabel = sp_label->ispl_sblabel;
252 	}
253 
254 	return sblabel;
255 }
256 
257 /*
258  * Name: ipc_service_port_label_set_attr
259  *
260  * Description: Set the remaining port label attributes after port allocation
261  *
262  * Args:
263  *   port_splabel
264  *   name : port name in launchd's ipc space
265  *   context : launchd's port guard; will be restored after a port destroyed notification if non-zero
266  *
267  * Conditions:
268  *   Should be called only once in mach_port_construct on a newly created port with the lock held
269  *   The context should be set only if the port is guarded.
270  */
271 void
ipc_service_port_label_set_attr(ipc_service_port_label_t port_splabel,mach_port_name_t name,mach_port_context_t context)272 ipc_service_port_label_set_attr(ipc_service_port_label_t port_splabel, mach_port_name_t name, mach_port_context_t context)
273 {
274 	assert(port_splabel->ispl_launchd_name == MACH_PORT_NULL);
275 	port_splabel->ispl_launchd_name = name;
276 	port_splabel->ispl_launchd_context = context;
277 	if (context) {
278 		ipc_service_port_label_set_flag(port_splabel, ISPL_FLAGS_SPECIAL_PDREQUEST);
279 	}
280 }
281 
282 /*
283  * Name: ipc_service_port_label_get_attr
284  *
285  * Description: Get the port label attributes
286  *
287  * Args:
288  *   port_splabel
289  *   name : port name in launchd's ipc space
290  *   context : launchd's port guard
291  *
292  * Conditions:
293  *   Should be called with port lock held.
294  */
295 void
ipc_service_port_label_get_attr(ipc_service_port_label_t port_splabel,mach_port_name_t * name,mach_port_context_t * context)296 ipc_service_port_label_get_attr(ipc_service_port_label_t port_splabel, mach_port_name_t *name, mach_port_context_t *context)
297 {
298 	*name = port_splabel->ispl_launchd_name;
299 	*context = port_splabel->ispl_launchd_context;
300 }
301 
302 #if CONFIG_SERVICE_PORT_INFO
303 void
ipc_service_port_label_get_info(ipc_service_port_label_t port_splabel,mach_service_port_info_t info)304 ipc_service_port_label_get_info(ipc_service_port_label_t port_splabel, mach_service_port_info_t info)
305 {
306 	info->mspi_domain_type = (uint8_t)port_splabel->ispl_domain;
307 	size_t sp_string_name_len = strlen(port_splabel->ispl_service_name);
308 	strlcpy(info->mspi_string_name, port_splabel->ispl_service_name, sp_string_name_len + 1);
309 }
310 #endif /* CONFIG_SERVICE_PORT_INFO */
311