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 |
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 |
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 |
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(zone: 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(s: 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(dst: sp_label->ispl_service_name, src: sp_info->mspi_string_name, n: 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 |
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 |
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(), name: service_port_name, portp: &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_splabel: port_label, info: &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: &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 * |
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 |
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, flag: 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 |
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 |
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(s: port_splabel->ispl_service_name); |
325 | strlcpy(dst: info->mspi_string_name, src: port_splabel->ispl_service_name, n: sp_string_name_len + 1); |
326 | } |
327 | #endif /* CONFIG_SERVICE_PORT_INFO */ |
328 | |