xref: /xnu-8796.101.5/bsd/kern/proc_uuid_policy.c (revision aca3beaa3dfbd42498b42c5e5ce20a938e6554e5)
1 /*
2  * Copyright (c) 2013 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 <sys/param.h>
30 #include <sys/malloc.h>
31 #include <sys/queue.h>
32 #include <sys/systm.h>
33 #include <sys/priv.h>
34 
35 #include <sys/sysproto.h>
36 #include <sys/proc_uuid_policy.h>
37 
38 #include <kern/locks.h>
39 #include <uuid/uuid.h>
40 
41 #include <string.h>
42 #include <libkern/OSAtomic.h>
43 
44 #define PROC_UUID_POLICY_DEBUG 0
45 
46 #if PROC_UUID_POLICY_DEBUG
47 #define dprintf(...) printf(__VA_ARGS__)
48 #else
49 #define dprintf(...) do { } while(0)
50 #endif
51 
52 static LCK_GRP_DECLARE(proc_uuid_policy_subsys_lck_grp,
53     "proc_uuid_policy_subsys_lock");
54 static LCK_MTX_DECLARE(proc_uuid_policy_subsys_mutex,
55     &proc_uuid_policy_subsys_lck_grp);
56 
57 #define PROC_UUID_POLICY_SUBSYS_LOCK() lck_mtx_lock(&proc_uuid_policy_subsys_mutex)
58 #define PROC_UUID_POLICY_SUBSYS_UNLOCK() lck_mtx_unlock(&proc_uuid_policy_subsys_mutex)
59 
60 #define PROC_UUID_POLICY_HASH_SIZE 64
61 u_long proc_uuid_policy_hash_mask;
62 
63 /* Assume first byte of UUIDs are evenly distributed */
64 #define UUIDHASH(uuid) (&proc_uuid_policy_hashtbl[uuid[0] & proc_uuid_policy_hash_mask])
65 static LIST_HEAD(proc_uuid_policy_hashhead, proc_uuid_policy_entry) * proc_uuid_policy_hashtbl;
66 
67 /*
68  * On modification, invalidate cached lookups by bumping the generation count.
69  * Other calls will need to take the slowpath of taking
70  * the subsystem lock.
71  */
72 static volatile int32_t proc_uuid_policy_table_gencount;
73 #define BUMP_PROC_UUID_POLICY_GENERATION_COUNT() do {                                                                   \
74 	        if (OSIncrementAtomic(&proc_uuid_policy_table_gencount) == (INT32_MAX - 1)) {   \
75 	                proc_uuid_policy_table_gencount = 1;                                                                            \
76 	        }                                                                                                                                                               \
77 	} while (0)
78 
79 #define MAX_PROC_UUID_POLICY_COUNT 10240
80 static volatile int32_t proc_uuid_policy_count;
81 
82 struct proc_uuid_policy_entry {
83 	LIST_ENTRY(proc_uuid_policy_entry) entries;
84 	uuid_t          uuid;   /* Mach-O executable UUID */
85 	uint32_t        flags;  /* policy flag for that UUID */
86 };
87 
88 static int
89 proc_uuid_policy_insert(uuid_t uuid, uint32_t flags);
90 
91 static struct proc_uuid_policy_entry *
92 proc_uuid_policy_remove_locked(uuid_t uuid, uint32_t flags, int *should_delete);
93 
94 static int
95 proc_uuid_policy_remove(uuid_t uuid, uint32_t flags);
96 
97 static struct proc_uuid_policy_entry *
98 proc_uuid_policy_lookup_locked(uuid_t uuid);
99 
100 static int
101 proc_uuid_policy_clear(uint32_t flags);
102 
103 void
proc_uuid_policy_init(void)104 proc_uuid_policy_init(void)
105 {
106 	proc_uuid_policy_hashtbl = hashinit(PROC_UUID_POLICY_HASH_SIZE, M_PROC_UUID_POLICY, &proc_uuid_policy_hash_mask);
107 	proc_uuid_policy_table_gencount = 1;
108 	proc_uuid_policy_count = 0;
109 }
110 
111 static int
proc_uuid_policy_insert(uuid_t uuid,uint32_t flags)112 proc_uuid_policy_insert(uuid_t uuid, uint32_t flags)
113 {
114 	struct proc_uuid_policy_entry *entry, *foundentry = NULL;
115 	int error;
116 
117 #if PROC_UUID_POLICY_DEBUG
118 	uuid_string_t uuidstr;
119 	uuid_unparse(uuid, uuidstr);
120 #endif
121 
122 	if (uuid_is_null(uuid)) {
123 		return EINVAL;
124 	}
125 
126 	entry = kalloc_type(struct proc_uuid_policy_entry, Z_WAITOK | Z_ZERO);
127 
128 	memcpy(entry->uuid, uuid, sizeof(uuid_t));
129 	entry->flags = flags;
130 
131 	PROC_UUID_POLICY_SUBSYS_LOCK();
132 
133 	foundentry = proc_uuid_policy_lookup_locked(uuid);
134 	if (foundentry != NULL) {
135 		/* The UUID is already in the list. Update the flags. */
136 		foundentry->flags |= flags;
137 		error = 0;
138 		kfree_type(struct proc_uuid_policy_entry, entry);
139 		entry = NULL;
140 		BUMP_PROC_UUID_POLICY_GENERATION_COUNT();
141 	} else {
142 		/* Our target UUID is not in the list, insert it now */
143 		if (proc_uuid_policy_count < MAX_PROC_UUID_POLICY_COUNT) {
144 			LIST_INSERT_HEAD(UUIDHASH(uuid), entry, entries);
145 			proc_uuid_policy_count++;
146 			error = 0;
147 			BUMP_PROC_UUID_POLICY_GENERATION_COUNT();
148 		} else {
149 			error = ENOMEM;
150 		}
151 	}
152 
153 	PROC_UUID_POLICY_SUBSYS_UNLOCK();
154 
155 	if (error) {
156 		kfree_type(struct proc_uuid_policy_entry, entry);
157 		dprintf("Failed to insert proc uuid policy (%s,0x%08x), table full\n", uuidstr, flags);
158 	} else {
159 		dprintf("Inserted proc uuid policy (%s,0x%08x)\n", uuidstr, flags);
160 	}
161 
162 	return error;
163 }
164 
165 static struct proc_uuid_policy_entry *
proc_uuid_policy_remove_locked(uuid_t uuid,uint32_t flags,int * should_delete)166 proc_uuid_policy_remove_locked(uuid_t uuid, uint32_t flags, int *should_delete)
167 {
168 	struct proc_uuid_policy_entry *foundentry = NULL;
169 	if (should_delete) {
170 		*should_delete = 0;
171 	}
172 
173 	foundentry = proc_uuid_policy_lookup_locked(uuid);
174 	if (foundentry) {
175 		if (foundentry->flags == flags) {
176 			LIST_REMOVE(foundentry, entries);
177 			proc_uuid_policy_count--;
178 			if (should_delete) {
179 				*should_delete = 1;
180 			}
181 		} else {
182 			foundentry->flags &= ~flags;
183 		}
184 	}
185 
186 	return foundentry;
187 }
188 
189 static int
proc_uuid_policy_remove(uuid_t uuid,uint32_t flags)190 proc_uuid_policy_remove(uuid_t uuid, uint32_t flags)
191 {
192 	struct proc_uuid_policy_entry *delentry = NULL;
193 	int error;
194 	int should_delete = 0;
195 
196 #if PROC_UUID_POLICY_DEBUG
197 	uuid_string_t uuidstr;
198 	uuid_unparse(uuid, uuidstr);
199 #endif
200 
201 	if (uuid_is_null(uuid)) {
202 		return EINVAL;
203 	}
204 
205 	PROC_UUID_POLICY_SUBSYS_LOCK();
206 
207 	delentry = proc_uuid_policy_remove_locked(uuid, flags, &should_delete);
208 
209 	if (delentry) {
210 		error = 0;
211 		BUMP_PROC_UUID_POLICY_GENERATION_COUNT();
212 	} else {
213 		error = ENOENT;
214 	}
215 
216 	PROC_UUID_POLICY_SUBSYS_UNLOCK();
217 
218 	/* If we had found a pre-existing entry, deallocate its memory now */
219 	if (delentry && should_delete) {
220 		kfree_type(struct proc_uuid_policy_entry, delentry);
221 	}
222 
223 	if (error) {
224 		dprintf("Failed to remove proc uuid policy (%s), entry not present\n", uuidstr);
225 	} else {
226 		dprintf("Removed proc uuid policy (%s)\n", uuidstr);
227 	}
228 
229 	return error;
230 }
231 
232 static struct proc_uuid_policy_entry *
proc_uuid_policy_lookup_locked(uuid_t uuid)233 proc_uuid_policy_lookup_locked(uuid_t uuid)
234 {
235 	struct proc_uuid_policy_entry *tmpentry, *searchentry, *foundentry = NULL;
236 
237 	LIST_FOREACH_SAFE(searchentry, UUIDHASH(uuid), entries, tmpentry) {
238 		if (0 == memcmp(searchentry->uuid, uuid, sizeof(uuid_t))) {
239 			foundentry = searchentry;
240 			break;
241 		}
242 	}
243 
244 	return foundentry;
245 }
246 
247 int
proc_uuid_policy_lookup(uuid_t uuid,uint32_t * flags,int32_t * gencount)248 proc_uuid_policy_lookup(uuid_t uuid, uint32_t *flags, int32_t *gencount)
249 {
250 	struct proc_uuid_policy_entry *foundentry = NULL;
251 	int error;
252 
253 #if PROC_UUID_POLICY_DEBUG
254 	uuid_string_t uuidstr;
255 	uuid_unparse(uuid, uuidstr);
256 #endif
257 
258 	if (uuid_is_null(uuid) || !flags || !gencount) {
259 		return EINVAL;
260 	}
261 
262 	if (*gencount == proc_uuid_policy_table_gencount) {
263 		/*
264 		 * Generation count hasn't changed, so old flags should be valid.
265 		 * We avoid taking the lock here by assuming any concurrent modifications
266 		 * to the table will invalidate the generation count.
267 		 */
268 		return 0;
269 	}
270 
271 	PROC_UUID_POLICY_SUBSYS_LOCK();
272 
273 	foundentry = proc_uuid_policy_lookup_locked(uuid);
274 
275 	if (foundentry) {
276 		*flags = foundentry->flags;
277 		*gencount = proc_uuid_policy_table_gencount;
278 		error = 0;
279 	} else {
280 		error = ENOENT;
281 	}
282 
283 	PROC_UUID_POLICY_SUBSYS_UNLOCK();
284 
285 	if (error == 0) {
286 		dprintf("Looked up proc uuid policy (%s,0x%08x)\n", uuidstr, *flags);
287 	}
288 
289 	return error;
290 }
291 
292 static int
proc_uuid_policy_clear(uint32_t flags)293 proc_uuid_policy_clear(uint32_t flags)
294 {
295 	struct proc_uuid_policy_entry *tmpentry, *searchentry;
296 	struct proc_uuid_policy_hashhead deletehead = LIST_HEAD_INITIALIZER(deletehead);
297 	unsigned long hashslot;
298 
299 	/* If clear call includes no flags, infer 'No Cellular' flag */
300 	if (flags == PROC_UUID_POLICY_FLAGS_NONE) {
301 		flags = PROC_UUID_NO_CELLULAR;
302 	}
303 
304 	PROC_UUID_POLICY_SUBSYS_LOCK();
305 
306 	if (proc_uuid_policy_count > 0) {
307 		for (hashslot = 0; hashslot <= proc_uuid_policy_hash_mask; hashslot++) {
308 			struct proc_uuid_policy_hashhead *headp = &proc_uuid_policy_hashtbl[hashslot];
309 
310 			LIST_FOREACH_SAFE(searchentry, headp, entries, tmpentry) {
311 				if ((searchentry->flags & flags) == searchentry->flags) {
312 					/* We are clearing all flags for this entry, move entry to our delete list */
313 					LIST_REMOVE(searchentry, entries);
314 					proc_uuid_policy_count--;
315 					LIST_INSERT_HEAD(&deletehead, searchentry, entries);
316 				} else {
317 					searchentry->flags &= ~flags;
318 				}
319 			}
320 		}
321 
322 		BUMP_PROC_UUID_POLICY_GENERATION_COUNT();
323 	}
324 
325 	PROC_UUID_POLICY_SUBSYS_UNLOCK();
326 
327 	/* Memory deallocation happens after the hash lock is dropped */
328 	LIST_FOREACH_SAFE(searchentry, &deletehead, entries, tmpentry) {
329 		LIST_REMOVE(searchentry, entries);
330 		kfree_type(struct proc_uuid_policy_entry, searchentry);
331 	}
332 
333 	dprintf("Clearing proc uuid policy table\n");
334 
335 	return 0;
336 }
337 
338 int
proc_uuid_policy_kernel(uint32_t operation,uuid_t uuid,uint32_t flags)339 proc_uuid_policy_kernel(uint32_t operation, uuid_t uuid, uint32_t flags)
340 {
341 	int error = 0;
342 
343 	switch (operation) {
344 	case PROC_UUID_POLICY_OPERATION_CLEAR:
345 		error = proc_uuid_policy_clear(flags);
346 		break;
347 
348 	case PROC_UUID_POLICY_OPERATION_ADD:
349 		error = proc_uuid_policy_insert(uuid, flags);
350 		break;
351 
352 	case PROC_UUID_POLICY_OPERATION_REMOVE:
353 		error = proc_uuid_policy_remove(uuid, flags);
354 		break;
355 
356 	default:
357 		error = EINVAL;
358 		break;
359 	}
360 
361 	return error;
362 }
363 
364 int
proc_uuid_policy(struct proc * p __unused,struct proc_uuid_policy_args * uap,int32_t * retval __unused)365 proc_uuid_policy(struct proc *p __unused, struct proc_uuid_policy_args *uap, int32_t *retval __unused)
366 {
367 	int error = 0;
368 	uuid_t uuid;
369 	memcpy(uuid, UUID_NULL, sizeof(uuid_t));
370 
371 	/* Need privilege for policy changes */
372 	error = priv_check_cred(kauth_cred_get(), PRIV_PROC_UUID_POLICY, 0);
373 	if (error) {
374 		dprintf("%s failed privilege check for proc_uuid_policy: %d\n", p->p_comm, error);
375 		return error;
376 	} else {
377 		dprintf("%s succeeded privilege check for proc_uuid_policy\n", p->p_comm);
378 	}
379 
380 	if (uap->uuid) {
381 		if (uap->uuidlen != sizeof(uuid_t)) {
382 			return ERANGE;
383 		}
384 
385 		error = copyin(uap->uuid, uuid, sizeof(uuid_t));
386 		if (error) {
387 			return error;
388 		}
389 	}
390 
391 	return proc_uuid_policy_kernel(uap->operation, uuid, uap->flags);
392 }
393