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