1 /*
2 * Copyright (c) 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 <os/refcnt.h>
30
31 #include <kern/ipc_kobject.h>
32 #include <kern/ipc_tt.h>
33 #include <kern/task_ident.h>
34
35 #include <mach/mach_types.h>
36 #include <mach/task.h>
37 #include <mach/notify.h>
38 #include <mach/kern_return.h>
39
40 #include <security/mac_mach_internal.h>
41 #include <kern/task_ident.h>
42 #include <corpses/task_corpse.h>
43
44 extern void* proc_find_ident(struct proc_ident const *i);
45 extern int proc_rele(void* p);
46 extern task_t proc_task(void* p);
47 extern struct proc_ident proc_ident(void* p);
48 extern kern_return_t task_conversion_eval(task_t caller, task_t victim, int flavor);
49
50 /* Exported to kexts */
51 extern typeof(task_id_token_port_name_to_task) task_id_token_port_name_to_task_external;
52
53 static ZONE_DEFINE_TYPE(task_id_token_zone, "task_id_token",
54 struct task_id_token, ZC_ZFREE_CLEARMEM);
55
56 void task_id_token_set_port(task_id_token_t token, ipc_port_t port);
57
58 static void
tidt_reference(task_id_token_t token)59 tidt_reference(task_id_token_t token)
60 {
61 if (token == TASK_ID_TOKEN_NULL) {
62 return;
63 }
64 os_ref_retain(&token->tidt_refs);
65 }
66
67 static void
tidt_release(task_id_token_t token)68 tidt_release(task_id_token_t token)
69 {
70 ipc_port_t port;
71
72 if (token == TASK_ID_TOKEN_NULL) {
73 return;
74 }
75
76 if (os_ref_release(&token->tidt_refs) > 0) {
77 return;
78 }
79
80 /* last ref */
81 port = token->port;
82
83 if (IP_VALID(port)) {
84 #if CONFIG_PROC_RESOURCE_LIMITS
85 /*
86 * Ports of type IKOT_TASK_FATAL use task_ident objects to avoid holding a task reference
87 * and are created to send resource limit notifications
88 */
89 int kotype = ip_kotype(port);
90 if (kotype == IKOT_TASK_ID_TOKEN || kotype == IKOT_TASK_FATAL) {
91 ipc_kobject_dealloc_port(port, 0, kotype);
92 } else {
93 panic("%s: unexpected kotype of port %p: got %d",
94 __func__, port, kotype);
95 }
96 #else /* CONFIG_PROC_RESOURCE_LIMITS */
97 ipc_kobject_dealloc_port(port, 0, IKOT_TASK_ID_TOKEN);
98 #endif /* CONFIG_PROC_RESOURCE_LIMITS */
99 }
100
101 zfree(task_id_token_zone, token);
102 }
103
104 void
task_id_token_release(task_id_token_t token)105 task_id_token_release(task_id_token_t token)
106 {
107 tidt_release(token);
108 }
109
110 static void
task_id_token_no_senders(ipc_port_t port,__unused mach_port_mscount_t mscount)111 task_id_token_no_senders(ipc_port_t port, __unused mach_port_mscount_t mscount)
112 {
113 task_id_token_t token;
114
115 token = ipc_kobject_get_stable(port, IKOT_TASK_ID_TOKEN);
116 assert(token != NULL);
117 assert(port->ip_srights == 0);
118
119 tidt_release(token); /* consumes ref given by notification */
120 }
121
122 IPC_KOBJECT_DEFINE(IKOT_TASK_ID_TOKEN,
123 .iko_op_stable = true,
124 .iko_op_no_senders = task_id_token_no_senders);
125
126 kern_return_t
task_create_identity_token(task_t task,task_id_token_t * tokenp)127 task_create_identity_token(
128 task_t task,
129 task_id_token_t *tokenp)
130 {
131 task_id_token_t token;
132 void *bsd_info = NULL;
133
134 if (task == TASK_NULL || task == kernel_task) {
135 return KERN_INVALID_ARGUMENT;
136 }
137
138 token = zalloc_flags(task_id_token_zone, Z_ZERO | Z_WAITOK | Z_NOFAIL);
139
140 task_lock(task);
141
142 bsd_info = get_bsdtask_info(task);
143 if (task_is_a_corpse(task)) {
144 token->task_uniqueid = task->task_uniqueid;
145 } else if (task->active && bsd_info != NULL) {
146 /* must check if the task is active to avoid a UAF - rdar://91431693 */
147 token->ident = proc_ident(bsd_info);
148 } else {
149 task_unlock(task);
150 zfree(task_id_token_zone, token);
151 return KERN_INVALID_ARGUMENT;
152 }
153
154 task_unlock(task);
155
156 token->port = IP_NULL;
157 /* this reference will be donated to no-senders notification */
158 os_ref_init_count(&token->tidt_refs, NULL, 1);
159
160 *tokenp = token;
161
162 return KERN_SUCCESS;
163 }
164
165 /* Produces (corpse) task reference, does not consume token reference */
166 kern_return_t
task_identity_token_get_task_grp(task_id_token_t token,task_t * taskp,task_grp_t grp)167 task_identity_token_get_task_grp(
168 task_id_token_t token,
169 task_t *taskp,
170 task_grp_t grp)
171 {
172 kern_return_t kr;
173 task_t task;
174
175 if (token == TASK_ID_TOKEN_NULL) {
176 return KERN_INVALID_ARGUMENT;
177 }
178
179 if (token->task_uniqueid) {
180 kr = find_corpse_task_by_uniqueid_grp(token->task_uniqueid, &task, grp); /* produces ref */
181 if (kr) {
182 return KERN_NOT_FOUND;
183 }
184 assert(task_is_a_corpse(task));
185 } else {
186 void* p = proc_find_ident(&token->ident);
187 if (p == NULL) {
188 return KERN_NOT_FOUND;
189 }
190 task = proc_task(p);
191 task_reference_grp(task, grp); /* produces ref */
192 proc_rele(p);
193 }
194
195 *taskp = task;
196
197 return KERN_SUCCESS;
198 }
199
200 /* Produces task port send right, does not consume token reference */
201 kern_return_t
task_identity_token_get_task_port(task_id_token_t token,task_flavor_t flavor,mach_port_t * portp)202 task_identity_token_get_task_port(
203 task_id_token_t token,
204 task_flavor_t flavor,
205 mach_port_t *portp)
206 {
207 task_t task;
208 kern_return_t kr;
209
210 if (token == TASK_ID_TOKEN_NULL) {
211 return KERN_INVALID_ARGUMENT;
212 }
213
214 if (flavor > TASK_FLAVOR_MAX) {
215 return KERN_INVALID_ARGUMENT;
216 }
217
218 if (token->task_uniqueid) {
219 /*
220 * For corpses, the control port reference would hold the corpse,
221 * only allow conversion to control port for now.
222 */
223 if (flavor != TASK_FLAVOR_CONTROL) {
224 return KERN_INVALID_ARGUMENT;
225 }
226 }
227
228 if ((kr = task_identity_token_get_task_grp(token, &task, TASK_GRP_KERNEL)) != KERN_SUCCESS) {
229 return kr;
230 }
231
232 assert(task != TASK_NULL);
233 assert(token != TASK_ID_TOKEN_NULL);
234
235 /* holding a ref on (corpse) task */
236
237 if (flavor == TASK_FLAVOR_CONTROL && task == current_task()) {
238 *portp = convert_task_to_port_pinned(task); /* consumes task ref */
239 return KERN_SUCCESS;
240 }
241
242 if (flavor <= TASK_FLAVOR_READ &&
243 task_conversion_eval(current_task(), task, flavor)) {
244 task_deallocate(task);
245 return KERN_INVALID_ARGUMENT;
246 }
247
248 #if CONFIG_MACF
249
250 if (task != current_task()) {
251 if (mac_task_check_task_id_token_get_task(task, flavor)) {
252 task_deallocate(task);
253 return KERN_DENIED;
254 }
255 }
256 #endif
257
258 *portp = convert_task_to_port_with_flavor(task, flavor, TASK_GRP_KERNEL);
259 /* task ref consumed */
260
261 return KERN_SUCCESS;
262 }
263
264 /* Produces task reference */
265 static kern_return_t
task_id_token_port_name_to_task_grp(mach_port_name_t name,task_t * task,task_grp_t grp)266 task_id_token_port_name_to_task_grp(
267 mach_port_name_t name,
268 task_t *task,
269 task_grp_t grp)
270 {
271 kern_return_t kr;
272 task_id_token_t token;
273
274 token = port_name_to_task_id_token(name); /* produces ref */
275 kr = task_identity_token_get_task_grp(token, task, grp);
276
277 tidt_release(token); /* consumes ref */
278
279 return kr;
280 }
281 /* Used by kexts only */
282 kern_return_t
task_id_token_port_name_to_task_external(mach_port_name_t name,task_t * task)283 task_id_token_port_name_to_task_external(
284 mach_port_name_t name,
285 task_t *task)
286 {
287 return task_id_token_port_name_to_task_grp(name, task, TASK_GRP_EXTERNAL);
288 }
289 /* Used by kernel proper */
290 kern_return_t
task_id_token_port_name_to_task(mach_port_name_t name,task_t * task)291 task_id_token_port_name_to_task(
292 mach_port_name_t name,
293 task_t *task)
294 {
295 return task_id_token_port_name_to_task_grp(name, task, TASK_GRP_KERNEL);
296 }
297
298 /* Produces token reference */
299 task_id_token_t
convert_port_to_task_id_token(ipc_port_t port)300 convert_port_to_task_id_token(
301 ipc_port_t port)
302 {
303 task_id_token_t token = TASK_ID_TOKEN_NULL;
304
305 if (IP_VALID(port)) {
306 token = ipc_kobject_get_stable(port, IKOT_TASK_ID_TOKEN);
307 if (token != TASK_ID_TOKEN_NULL) {
308 zone_require(task_id_token_zone, token);
309 tidt_reference(token);
310 }
311 }
312 return token;
313 }
314
315 /* Consumes token reference */
316 ipc_port_t
convert_task_id_token_to_port(task_id_token_t token)317 convert_task_id_token_to_port(
318 task_id_token_t token)
319 {
320 __assert_only bool kr;
321
322 if (token == TASK_ID_TOKEN_NULL) {
323 return IP_NULL;
324 }
325
326 zone_require(task_id_token_zone, token);
327
328 kr = ipc_kobject_make_send_lazy_alloc_port(&token->port,
329 token, IKOT_TASK_ID_TOKEN, IPC_KOBJECT_ALLOC_NONE);
330 assert(kr == TRUE); /* no-senders notification is armed, consumes token ref */
331
332 return token->port;
333 }
334
335 #if CONFIG_PROC_RESOURCE_LIMITS
336
337 /* Should be used only by ports of type IKOT_TASK_FATAL at allocation time */
338 void
task_id_token_set_port(task_id_token_t token,ipc_port_t port)339 task_id_token_set_port(
340 task_id_token_t token,
341 ipc_port_t port)
342 {
343 assert(token && port && (ip_kotype(port) == IKOT_TASK_FATAL));
344 token->port = port;
345 }
346 #endif /* CONFIG_PROC_RESOURCE_LIMITS */
347