xref: /xnu-12377.1.9/osfmk/ipc/ipc_service_port.c (revision f6217f891ac0bb64f3d375211650a4c1ff8ca1ea) !
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 <kern/block_hint.h>
30 #include <kern/kalloc.h>
31 #include <kern/mach_filter.h>
32 #include <kern/task.h>
33 #include <ipc/ipc_service_port.h>
34 #include <security/mac_mach_internal.h>
35 
36 #define XPC_DOMAIN_PORT 7 /* This value should match what is in <xpc/launch_private.h> */
37 
38 ZONE_DEFINE_TYPE(ipc_service_port_label_zone, "ipc_service_port_label",
39     struct ipc_service_port_label, ZC_ZFREE_CLEARMEM | ZC_NOCACHING);
40 
41 #if CONFIG_SERVICE_PORT_INFO
42 const bool kdp_ipc_have_splabel = true;
43 #else
44 const bool kdp_ipc_have_splabel = false;
45 #endif
46 
47 void
kdp_ipc_splabel_size(size_t * ispl_size,size_t * maxnamelen)48 kdp_ipc_splabel_size(size_t *ispl_size, size_t *maxnamelen)
49 {
50 	*ispl_size = sizeof(struct ipc_service_port_label);
51 	*maxnamelen = MACH_SERVICE_PORT_INFO_STRING_NAME_MAX_BUF_LEN + 1;
52 }
53 
54 void
kdp_ipc_fill_splabel(struct ipc_service_port_label * ispl,struct portlabel_info * spl,const char ** namep)55 kdp_ipc_fill_splabel(struct ipc_service_port_label *ispl,
56     struct portlabel_info *spl, const char **namep)
57 {
58 #pragma unused(ispl, spl, namep)
59 
60 	/* validate that ispl is in our zone */
61 #if CONFIG_SERVICE_PORT_INFO
62 	*namep = ispl->ispl_service_name;
63 	spl->portlabel_domain = ispl->ispl_domain;
64 	if (ispl->ispl_throttled) {
65 		spl->portlabel_flags |= STACKSHOT_PORTLABEL_THROTTLED;
66 	}
67 #endif
68 }
69 
70 /*
71  * Name: ipc_service_port_label_alloc
72  *
73  * Description: Allocates the service port label
74  *
75  * Args:
76  *   sp_info: service port string name, length, domain information
77  *   send_side_filtering: indicates if the messages should be filtered during mach_msg_send
78  *   port_label_ptr: used to return the allocated service_port_label
79  *
80  * Returns:
81  *   KERN_SUCCESS
82  */
83 kern_return_t
ipc_service_port_label_alloc(mach_service_port_info_t sp_info,ipc_object_label_t * label)84 ipc_service_port_label_alloc(
85 	mach_service_port_info_t sp_info,
86 	ipc_object_label_t     *label)
87 {
88 	ipc_service_port_label_t sp_label = NULL;
89 	kern_return_t ret;
90 	void *sblabel = NULL;
91 
92 	sp_label = zalloc(ipc_service_port_label_zone);
93 
94 	if (mach_msg_filter_alloc_service_port_sblabel_callback) {
95 		ret = mach_msg_filter_alloc_service_port_sblabel_callback(sp_info, &sblabel);
96 		if (ret) {
97 			zfree(ipc_service_port_label_zone, sp_label);
98 			return ret;
99 		}
100 	}
101 
102 	sp_label->ispl_sblabel = sblabel;
103 #if CONFIG_SERVICE_PORT_INFO
104 	size_t sp_string_name_len = strlen(sp_info->mspi_string_name);
105 	/* We could investigate compressing the names, but it doesn't seem worth it */
106 	sp_label->ispl_service_name = kalloc_data(sp_string_name_len + 1, Z_WAITOK);
107 	strlcpy(sp_label->ispl_service_name, sp_info->mspi_string_name, sp_string_name_len + 1);
108 	sp_label->ispl_domain = sp_info->mspi_domain_type;
109 #endif /* CONFIG_SERVICE_PORT_INFO */
110 
111 	if (sp_info->mspi_domain_type == XPC_DOMAIN_PORT) {
112 		sp_label->ispl_bootstrap_port = true;
113 	}
114 
115 	label->iol_service = sp_label;
116 	if (sblabel) {
117 		/* always filter service ports with a label */
118 		label->io_filtered = true;
119 	}
120 	if (sp_label->ispl_bootstrap_port) {
121 		/* bootstrap ports are completely immovable thank you */
122 		label->io_state = IO_STATE_IN_SPACE_IMMOVABLE;
123 	}
124 	return KERN_SUCCESS;
125 }
126 
127 void
ipc_connection_port_label_dealloc(ipc_object_label_t label)128 ipc_connection_port_label_dealloc(ipc_object_label_t label)
129 {
130 	mach_msg_filter_dealloc_service_port_sblabel_callback(label.iol_connection);
131 }
132 
133 /*
134  * Name: ipc_service_port_dealloc
135  *
136  * Description: Deallocates the service port label
137  *
138  * Args:
139  *   ip_splabel: port's ip_splabel
140  *
141  * Returns: None
142  *
143  * Should not be called with the port lock held.
144  */
145 void
ipc_service_port_label_dealloc(ipc_object_label_t label)146 ipc_service_port_label_dealloc(ipc_object_label_t label)
147 {
148 	ipc_service_port_label_t sp_label = label.iol_service;
149 	void *sblabel = sp_label->ispl_sblabel;
150 
151 #if CONFIG_SERVICE_PORT_INFO
152 	kfree_data(sp_label->ispl_service_name, strlen(sp_label->ispl_service_name) + 1);
153 #endif /* CONFIG_SERVICE_PORT_INFO */
154 	zfree(ipc_service_port_label_zone, sp_label);
155 
156 	if (sblabel) {
157 		mach_msg_filter_dealloc_service_port_sblabel_callback(sblabel);
158 	}
159 }
160 
161 /*
162  * Name: ipc_service_port_derive_sblabel
163  *
164  * Description: Derive the port's sandbox label using info from the service port's label
165  *
166  * Args:
167  *   service_port_name: send right to a service port
168  *   sblabel_ptr: used to return the allocated sblabel
169  *
170  * Returns:
171  *   KERN_SUCCESS
172  *   KERN_INVALID_NAME: service_port_name is mach_port_null or mach_port_dead
173  *   KERN_INVALID_RIGHT: service_port_name is not a send right
174  *   KERN_INVALID_CAPABILITY: service_port_name is not a right to a service port
175  */
176 kern_return_t
ipc_service_port_derive_sblabel(mach_port_name_t service_port_name,bool force,ipc_object_label_t * label)177 ipc_service_port_derive_sblabel(
178 	mach_port_name_t        service_port_name,
179 	bool                    force,
180 	ipc_object_label_t     *label)
181 {
182 	ipc_port_t port;
183 	kern_return_t kr;
184 #if CONFIG_MACF && XNU_TARGET_OS_OSX
185 	struct mach_service_port_info sp_info = {};
186 #endif
187 
188 	if (!MACH_PORT_VALID(service_port_name)) {
189 		return KERN_INVALID_NAME;
190 	}
191 
192 	if (mach_msg_filter_at_least(MACH_MSG_FILTER_CALLBACKS_VERSION_1)) {
193 		ipc_object_label_t sp_label;
194 		boolean_t send_side_filtering;
195 		void *sblabel = NULL;
196 
197 		kr = ipc_port_translate_send(current_space(), service_port_name, &port);
198 		if (kr != KERN_SUCCESS) {
199 			return kr;
200 		}
201 		/* port is locked and active */
202 
203 		sp_label = ip_label_get(port);
204 		if (!ip_is_any_service_port_type(sp_label.io_type)) {
205 			ip_mq_unlock_label_put(port, &sp_label);
206 			return KERN_INVALID_CAPABILITY;
207 		}
208 
209 #if CONFIG_MACF && XNU_TARGET_OS_OSX
210 		ipc_service_port_label_get_info(sp_label.iol_service, &sp_info);
211 #endif
212 
213 		sblabel = sp_label.iol_service->ispl_sblabel;
214 		if (sblabel) {
215 			mach_msg_filter_retain_sblabel_callback(sblabel);
216 		}
217 		ip_mq_unlock_label_put(port, &sp_label);
218 
219 		if (sblabel) {
220 			/* This callback will release the reference on sblabel */
221 			label->iol_connection = mach_msg_filter_derive_sblabel_from_service_port_callback(sblabel,
222 			    &send_side_filtering);
223 			if (label->iol_connection && (send_side_filtering || force)) {
224 				label->io_filtered = true;
225 			}
226 		}
227 
228 #if CONFIG_MACF && XNU_TARGET_OS_OSX
229 		if (sp_info.mspi_string_name[0] != '\0') {
230 			mac_proc_notify_service_port_derive(&sp_info);
231 		}
232 #endif
233 	}
234 
235 	return KERN_SUCCESS;
236 }
237 
238 #if CONFIG_SERVICE_PORT_INFO
239 void
ipc_service_port_label_get_info(ipc_service_port_label_t port_splabel,mach_service_port_info_t info)240 ipc_service_port_label_get_info(ipc_service_port_label_t port_splabel, mach_service_port_info_t info)
241 {
242 	info->mspi_domain_type = (uint8_t)port_splabel->ispl_domain;
243 	size_t sp_string_name_len = strlen(port_splabel->ispl_service_name);
244 	strlcpy(info->mspi_string_name, port_splabel->ispl_service_name, sp_string_name_len + 1);
245 }
246 #endif /* CONFIG_SERVICE_PORT_INFO */
247