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