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