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