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