xref: /xnu-12377.41.6/osfmk/kern/task_ident.c (revision bbb1b6f9e71b8cdde6e5cd6f4841f207dee3d828)
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_with_policy(void* p, uint8_t policy);
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 		ipc_kobject_type_t kotype = ip_type(port);
90 		release_assert(kotype == IKOT_TASK_ID_TOKEN ||
91 		    kotype == IKOT_TASK_FATAL);
92 		ipc_kobject_dealloc_port(port, IPC_KOBJECT_NO_MSCOUNT,
93 		    kotype);
94 #else /* CONFIG_PROC_RESOURCE_LIMITS */
95 		ipc_kobject_dealloc_port(port, IPC_KOBJECT_NO_MSCOUNT,
96 		    IKOT_TASK_ID_TOKEN);
97 #endif /* CONFIG_PROC_RESOURCE_LIMITS */
98 	}
99 
100 	zfree(task_id_token_zone, token);
101 }
102 
103 void
task_id_token_release(task_id_token_t token)104 task_id_token_release(task_id_token_t token)
105 {
106 	tidt_release(token);
107 }
108 
109 static void
task_id_token_no_senders(ipc_port_t port,__unused mach_port_mscount_t mscount)110 task_id_token_no_senders(ipc_port_t port, __unused mach_port_mscount_t mscount)
111 {
112 	task_id_token_t token;
113 
114 	token = ipc_kobject_get_stable(port, IKOT_TASK_ID_TOKEN);
115 	assert(token != NULL);
116 	assert(port->ip_srights == 0);
117 
118 	tidt_release(token); /* consumes ref given by notification */
119 }
120 
121 IPC_KOBJECT_DEFINE(IKOT_TASK_ID_TOKEN,
122     .iko_op_movable_send = true,
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_with_policy(bsd_info, IDENT_VALIDATION_PROC_EXACT);
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 		/* copyout determines immovability, see `should_mark_immovable_send` */
239 		*portp = convert_task_to_port(task); /* consumes task ref */
240 		return KERN_SUCCESS;
241 	}
242 
243 	if (flavor <= TASK_FLAVOR_READ &&
244 	    task_conversion_eval(current_task(), task, flavor)) {
245 		task_deallocate(task);
246 		return KERN_INVALID_ARGUMENT;
247 	}
248 
249 #if CONFIG_MACF
250 
251 	if (task != current_task()) {
252 		if (mac_task_check_task_id_token_get_task(task, flavor)) {
253 			task_deallocate(task);
254 			return KERN_DENIED;
255 		}
256 	}
257 #endif
258 
259 	*portp = convert_task_to_port_with_flavor(task, flavor, TASK_GRP_KERNEL);
260 	/* task ref consumed */
261 
262 	return KERN_SUCCESS;
263 }
264 
265 /* Produces task reference */
266 static kern_return_t
task_id_token_port_name_to_task_grp(mach_port_name_t name,task_t * task,task_grp_t grp)267 task_id_token_port_name_to_task_grp(
268 	mach_port_name_t name,
269 	task_t           *task,
270 	task_grp_t       grp)
271 {
272 	kern_return_t kr;
273 	task_id_token_t token;
274 
275 	token = port_name_to_task_id_token(name); /* produces ref */
276 	kr = task_identity_token_get_task_grp(token, task, grp);
277 
278 	tidt_release(token); /* consumes ref */
279 
280 	return kr;
281 }
282 /* Used by kexts only */
283 kern_return_t
task_id_token_port_name_to_task_external(mach_port_name_t name,task_t * task)284 task_id_token_port_name_to_task_external(
285 	mach_port_name_t name,
286 	task_t           *task)
287 {
288 	return task_id_token_port_name_to_task_grp(name, task, TASK_GRP_EXTERNAL);
289 }
290 /* Used by kernel proper */
291 kern_return_t
task_id_token_port_name_to_task(mach_port_name_t name,task_t * task)292 task_id_token_port_name_to_task(
293 	mach_port_name_t name,
294 	task_t           *task)
295 {
296 	return task_id_token_port_name_to_task_grp(name, task, TASK_GRP_KERNEL);
297 }
298 
299 /* Produces token reference */
300 task_id_token_t
convert_port_to_task_id_token(ipc_port_t port)301 convert_port_to_task_id_token(
302 	ipc_port_t              port)
303 {
304 	task_id_token_t token = TASK_ID_TOKEN_NULL;
305 
306 	if (IP_VALID(port)) {
307 		token = ipc_kobject_get_stable(port, IKOT_TASK_ID_TOKEN);
308 		if (token != TASK_ID_TOKEN_NULL) {
309 			zone_require(task_id_token_zone, token);
310 			tidt_reference(token);
311 		}
312 	}
313 	return token;
314 }
315 
316 /* Consumes token reference */
317 ipc_port_t
convert_task_id_token_to_port(task_id_token_t token)318 convert_task_id_token_to_port(
319 	task_id_token_t token)
320 {
321 	if (token == TASK_ID_TOKEN_NULL) {
322 		return IP_NULL;
323 	}
324 
325 	zone_require(task_id_token_zone, token);
326 
327 	/*
328 	 * make a send right and donate our reference for
329 	 * task_id_token_no_senders if this is the first send right
330 	 */
331 	if (!ipc_kobject_make_send_lazy_alloc_port(&token->port,
332 	    token, IKOT_TASK_ID_TOKEN)) {
333 		tidt_release(token);
334 	}
335 
336 	return token->port;
337 }
338 
339 #if CONFIG_PROC_RESOURCE_LIMITS
340 
341 /* Should be used only by ports of type IKOT_TASK_FATAL at allocation time */
342 void
task_id_token_set_port(task_id_token_t token,ipc_port_t port)343 task_id_token_set_port(
344 	task_id_token_t token,
345 	ipc_port_t port)
346 {
347 	assert(token && port && ip_type(port) == IKOT_TASK_FATAL);
348 	token->port = port;
349 }
350 
351 #endif /* CONFIG_PROC_RESOURCE_LIMITS */
352