]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/proc_uuid_policy.c
xnu-4903.270.47.tar.gz
[apple/xnu.git] / bsd / kern / proc_uuid_policy.c
CommitLineData
39236c6e
A
1/*
2 * Copyright (c) 2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
0a7de745 5 *
39236c6e
A
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.
0a7de745 14 *
39236c6e
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
0a7de745 17 *
39236c6e
A
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.
0a7de745 25 *
39236c6e
A
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
52static lck_grp_attr_t *proc_uuid_policy_subsys_lck_grp_attr;
53static lck_grp_t *proc_uuid_policy_subsys_lck_grp;
54static lck_attr_t *proc_uuid_policy_subsys_lck_attr;
55static lck_mtx_t proc_uuid_policy_subsys_mutex;
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
61u_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])
0a7de745 65static LIST_HEAD(proc_uuid_policy_hashhead, proc_uuid_policy_entry) * proc_uuid_policy_hashtbl;
39236c6e
A
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 */
72static volatile int32_t proc_uuid_policy_table_gencount;
0a7de745
A
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 } \
39236c6e
A
77 } while (0)
78
79#define MAX_PROC_UUID_POLICY_COUNT 10240
80static volatile int32_t proc_uuid_policy_count;
81
82struct proc_uuid_policy_entry {
83 LIST_ENTRY(proc_uuid_policy_entry) entries;
0a7de745
A
84 uuid_t uuid; /* Mach-O executable UUID */
85 uint32_t flags; /* policy flag for that UUID */
39236c6e
A
86};
87
88static int
89proc_uuid_policy_insert(uuid_t uuid, uint32_t flags);
90
91static struct proc_uuid_policy_entry *
fe8ab488 92proc_uuid_policy_remove_locked(uuid_t uuid, uint32_t flags, int *should_delete);
39236c6e
A
93
94static int
fe8ab488
A
95proc_uuid_policy_remove(uuid_t uuid, uint32_t flags);
96
97static struct proc_uuid_policy_entry *
98proc_uuid_policy_lookup_locked(uuid_t uuid);
39236c6e
A
99
100static int
fe8ab488 101proc_uuid_policy_clear(uint32_t flags);
39236c6e
A
102
103void
104proc_uuid_policy_init(void)
105{
106 proc_uuid_policy_subsys_lck_grp_attr = lck_grp_attr_alloc_init();
107 proc_uuid_policy_subsys_lck_grp = lck_grp_alloc_init("proc_uuid_policy_subsys_lock", proc_uuid_policy_subsys_lck_grp_attr);
108 proc_uuid_policy_subsys_lck_attr = lck_attr_alloc_init();
109 lck_mtx_init(&proc_uuid_policy_subsys_mutex, proc_uuid_policy_subsys_lck_grp, proc_uuid_policy_subsys_lck_attr);
110
111 proc_uuid_policy_hashtbl = hashinit(PROC_UUID_POLICY_HASH_SIZE, M_PROC_UUID_POLICY, &proc_uuid_policy_hash_mask);
112 proc_uuid_policy_table_gencount = 1;
113 proc_uuid_policy_count = 0;
114}
115
116static int
117proc_uuid_policy_insert(uuid_t uuid, uint32_t flags)
118{
fe8ab488 119 struct proc_uuid_policy_entry *entry, *foundentry = NULL;
39236c6e
A
120 int error;
121
122#if PROC_UUID_POLICY_DEBUG
123 uuid_string_t uuidstr;
124 uuid_unparse(uuid, uuidstr);
125#endif
126
0a7de745 127 if (uuid_is_null(uuid)) {
39236c6e 128 return EINVAL;
0a7de745 129 }
39236c6e 130
0a7de745 131 MALLOC(entry, struct proc_uuid_policy_entry *, sizeof(*entry), M_PROC_UUID_POLICY, M_WAITOK | M_ZERO);
39236c6e
A
132
133 memcpy(entry->uuid, uuid, sizeof(uuid_t));
134 entry->flags = flags;
135
136 PROC_UUID_POLICY_SUBSYS_LOCK();
137
fe8ab488
A
138 foundentry = proc_uuid_policy_lookup_locked(uuid);
139 if (foundentry != NULL) {
140 /* The UUID is already in the list. Update the flags. */
141 foundentry->flags |= flags;
39236c6e 142 error = 0;
fe8ab488
A
143 FREE(entry, M_PROC_UUID_POLICY);
144 entry = NULL;
39236c6e
A
145 BUMP_PROC_UUID_POLICY_GENERATION_COUNT();
146 } else {
fe8ab488
A
147 /* Our target UUID is not in the list, insert it now */
148 if (proc_uuid_policy_count < MAX_PROC_UUID_POLICY_COUNT) {
149 LIST_INSERT_HEAD(UUIDHASH(uuid), entry, entries);
150 proc_uuid_policy_count++;
151 error = 0;
152 BUMP_PROC_UUID_POLICY_GENERATION_COUNT();
153 } else {
154 error = ENOMEM;
155 }
39236c6e
A
156 }
157
158 PROC_UUID_POLICY_SUBSYS_UNLOCK();
159
39236c6e
A
160 if (error) {
161 FREE(entry, M_PROC_UUID_POLICY);
162 dprintf("Failed to insert proc uuid policy (%s,0x%08x), table full\n", uuidstr, flags);
163 } else {
164 dprintf("Inserted proc uuid policy (%s,0x%08x)\n", uuidstr, flags);
165 }
166
167 return error;
168}
169
170static struct proc_uuid_policy_entry *
fe8ab488 171proc_uuid_policy_remove_locked(uuid_t uuid, uint32_t flags, int *should_delete)
39236c6e 172{
fe8ab488
A
173 struct proc_uuid_policy_entry *foundentry = NULL;
174 if (should_delete) {
175 *should_delete = 0;
176 }
0a7de745 177
fe8ab488
A
178 foundentry = proc_uuid_policy_lookup_locked(uuid);
179 if (foundentry) {
180 if (foundentry->flags == flags) {
181 LIST_REMOVE(foundentry, entries);
39236c6e 182 proc_uuid_policy_count--;
fe8ab488
A
183 if (should_delete) {
184 *should_delete = 1;
185 }
186 } else {
187 foundentry->flags &= ~flags;
39236c6e
A
188 }
189 }
0a7de745 190
fe8ab488 191 return foundentry;
39236c6e
A
192}
193
194static int
fe8ab488 195proc_uuid_policy_remove(uuid_t uuid, uint32_t flags)
39236c6e
A
196{
197 struct proc_uuid_policy_entry *delentry = NULL;
198 int error;
fe8ab488 199 int should_delete = 0;
39236c6e
A
200
201#if PROC_UUID_POLICY_DEBUG
202 uuid_string_t uuidstr;
203 uuid_unparse(uuid, uuidstr);
204#endif
205
0a7de745 206 if (uuid_is_null(uuid)) {
39236c6e 207 return EINVAL;
0a7de745 208 }
39236c6e
A
209
210 PROC_UUID_POLICY_SUBSYS_LOCK();
211
fe8ab488 212 delentry = proc_uuid_policy_remove_locked(uuid, flags, &should_delete);
39236c6e
A
213
214 if (delentry) {
215 error = 0;
216 BUMP_PROC_UUID_POLICY_GENERATION_COUNT();
217 } else {
218 error = ENOENT;
219 }
220
221 PROC_UUID_POLICY_SUBSYS_UNLOCK();
222
223 /* If we had found a pre-existing entry, deallocate its memory now */
fe8ab488 224 if (delentry && should_delete) {
39236c6e
A
225 FREE(delentry, M_PROC_UUID_POLICY);
226 }
227
228 if (error) {
229 dprintf("Failed to remove proc uuid policy (%s), entry not present\n", uuidstr);
230 } else {
231 dprintf("Removed proc uuid policy (%s)\n", uuidstr);
232 }
233
234 return error;
235}
236
fe8ab488
A
237static struct proc_uuid_policy_entry *
238proc_uuid_policy_lookup_locked(uuid_t uuid)
239{
240 struct proc_uuid_policy_entry *tmpentry, *searchentry, *foundentry = NULL;
0a7de745 241
fe8ab488
A
242 LIST_FOREACH_SAFE(searchentry, UUIDHASH(uuid), entries, tmpentry) {
243 if (0 == memcmp(searchentry->uuid, uuid, sizeof(uuid_t))) {
244 foundentry = searchentry;
245 break;
246 }
247 }
0a7de745 248
fe8ab488
A
249 return foundentry;
250}
251
39236c6e
A
252int
253proc_uuid_policy_lookup(uuid_t uuid, uint32_t *flags, int32_t *gencount)
254{
fe8ab488 255 struct proc_uuid_policy_entry *foundentry = NULL;
39236c6e
A
256 int error;
257
258#if PROC_UUID_POLICY_DEBUG
259 uuid_string_t uuidstr;
260 uuid_unparse(uuid, uuidstr);
261#endif
262
0a7de745 263 if (uuid_is_null(uuid) || !flags || !gencount) {
39236c6e 264 return EINVAL;
0a7de745 265 }
39236c6e
A
266
267 if (*gencount == proc_uuid_policy_table_gencount) {
268 /*
269 * Generation count hasn't changed, so old flags should be valid.
270 * We avoid taking the lock here by assuming any concurrent modifications
271 * to the table will invalidate the generation count.
272 */
273 return 0;
274 }
275
276 PROC_UUID_POLICY_SUBSYS_LOCK();
277
fe8ab488 278 foundentry = proc_uuid_policy_lookup_locked(uuid);
39236c6e
A
279
280 if (foundentry) {
281 *flags = foundentry->flags;
282 *gencount = proc_uuid_policy_table_gencount;
283 error = 0;
284 } else {
285 error = ENOENT;
286 }
287
288 PROC_UUID_POLICY_SUBSYS_UNLOCK();
289
290 if (error == 0) {
291 dprintf("Looked up proc uuid policy (%s,0x%08x)\n", uuidstr, *flags);
292 }
293
294 return error;
295}
296
297static int
fe8ab488 298proc_uuid_policy_clear(uint32_t flags)
39236c6e
A
299{
300 struct proc_uuid_policy_entry *tmpentry, *searchentry;
301 struct proc_uuid_policy_hashhead deletehead = LIST_HEAD_INITIALIZER(deletehead);
302 unsigned long hashslot;
0a7de745 303
fe8ab488
A
304 /* If clear call includes no flags, infer 'No Cellular' flag */
305 if (flags == PROC_UUID_POLICY_FLAGS_NONE) {
306 flags = PROC_UUID_NO_CELLULAR;
307 }
39236c6e
A
308
309 PROC_UUID_POLICY_SUBSYS_LOCK();
310
311 if (proc_uuid_policy_count > 0) {
0a7de745 312 for (hashslot = 0; hashslot <= proc_uuid_policy_hash_mask; hashslot++) {
39236c6e 313 struct proc_uuid_policy_hashhead *headp = &proc_uuid_policy_hashtbl[hashslot];
0a7de745 314
39236c6e 315 LIST_FOREACH_SAFE(searchentry, headp, entries, tmpentry) {
fe8ab488
A
316 if ((searchentry->flags & flags) == searchentry->flags) {
317 /* We are clearing all flags for this entry, move entry to our delete list */
318 LIST_REMOVE(searchentry, entries);
319 proc_uuid_policy_count--;
320 LIST_INSERT_HEAD(&deletehead, searchentry, entries);
321 } else {
322 searchentry->flags &= ~flags;
323 }
39236c6e
A
324 }
325 }
326
327 BUMP_PROC_UUID_POLICY_GENERATION_COUNT();
328 }
329
330 PROC_UUID_POLICY_SUBSYS_UNLOCK();
331
332 /* Memory deallocation happens after the hash lock is dropped */
333 LIST_FOREACH_SAFE(searchentry, &deletehead, entries, tmpentry) {
334 LIST_REMOVE(searchentry, entries);
335 FREE(searchentry, M_PROC_UUID_POLICY);
336 }
337
338 dprintf("Clearing proc uuid policy table\n");
0a7de745 339
39236c6e
A
340 return 0;
341}
342
0a7de745
A
343int
344proc_uuid_policy_kernel(uint32_t operation, uuid_t uuid, uint32_t flags)
fe8ab488
A
345{
346 int error = 0;
0a7de745 347
fe8ab488 348 switch (operation) {
0a7de745
A
349 case PROC_UUID_POLICY_OPERATION_CLEAR:
350 error = proc_uuid_policy_clear(flags);
351 break;
352
353 case PROC_UUID_POLICY_OPERATION_ADD:
354 error = proc_uuid_policy_insert(uuid, flags);
355 break;
356
357 case PROC_UUID_POLICY_OPERATION_REMOVE:
358 error = proc_uuid_policy_remove(uuid, flags);
359 break;
360
361 default:
362 error = EINVAL;
363 break;
fe8ab488 364 }
0a7de745 365
fe8ab488
A
366 return error;
367}
368
0a7de745
A
369int
370proc_uuid_policy(struct proc *p __unused, struct proc_uuid_policy_args *uap, int32_t *retval __unused)
39236c6e
A
371{
372 int error = 0;
373 uuid_t uuid;
39037602 374 memcpy(uuid, UUID_NULL, sizeof(uuid_t));
39236c6e
A
375
376 /* Need privilege for policy changes */
377 error = priv_check_cred(kauth_cred_get(), PRIV_PROC_UUID_POLICY, 0);
378 if (error) {
379 dprintf("%s failed privilege check for proc_uuid_policy: %d\n", p->p_comm, error);
0a7de745 380 return error;
39236c6e
A
381 } else {
382 dprintf("%s succeeded privilege check for proc_uuid_policy\n", p->p_comm);
383 }
0a7de745 384
fe8ab488 385 if (uap->uuid) {
0a7de745 386 if (uap->uuidlen != sizeof(uuid_t)) {
fe8ab488 387 return ERANGE;
0a7de745
A
388 }
389
fe8ab488 390 error = copyin(uap->uuid, uuid, sizeof(uuid_t));
0a7de745 391 if (error) {
fe8ab488 392 return error;
0a7de745 393 }
39236c6e 394 }
0a7de745 395
fe8ab488 396 return proc_uuid_policy_kernel(uap->operation, uuid, uap->flags);
39236c6e 397}