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