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