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