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