2 * Copyright (c) 2013 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <sys/param.h>
30 #include <sys/malloc.h>
31 #include <sys/queue.h>
32 #include <sys/systm.h>
35 #include <sys/sysproto.h>
36 #include <sys/proc_uuid_policy.h>
38 #include <kern/locks.h>
39 #include <uuid/uuid.h>
42 #include <libkern/OSAtomic.h>
44 #define PROC_UUID_POLICY_DEBUG 0
46 #if PROC_UUID_POLICY_DEBUG
47 #define dprintf(...) printf(__VA_ARGS__)
49 #define dprintf(...) do { } while(0)
52 static lck_grp_attr_t
*proc_uuid_policy_subsys_lck_grp_attr
;
53 static lck_grp_t
*proc_uuid_policy_subsys_lck_grp
;
54 static lck_attr_t
*proc_uuid_policy_subsys_lck_attr
;
55 static lck_mtx_t proc_uuid_policy_subsys_mutex
;
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)
60 #define PROC_UUID_POLICY_HASH_SIZE 64
61 u_long proc_uuid_policy_hash_mask
;
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
;
68 * On modification, invalidate cached lookups by bumping the generation count.
69 * Other calls will need to take the slowpath of taking
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; \
79 #define MAX_PROC_UUID_POLICY_COUNT 10240
80 static volatile int32_t proc_uuid_policy_count
;
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 */
89 proc_uuid_policy_insert(uuid_t uuid
, uint32_t flags
);
91 static struct proc_uuid_policy_entry
*
92 proc_uuid_policy_remove_locked(uuid_t uuid
, uint32_t flags
, int *should_delete
);
95 proc_uuid_policy_remove(uuid_t uuid
, uint32_t flags
);
97 static struct proc_uuid_policy_entry
*
98 proc_uuid_policy_lookup_locked(uuid_t uuid
);
101 proc_uuid_policy_clear(uint32_t flags
);
104 proc_uuid_policy_init(void)
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
);
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;
117 proc_uuid_policy_insert(uuid_t uuid
, uint32_t flags
)
119 struct proc_uuid_policy_entry
*entry
, *foundentry
= NULL
;
122 #if PROC_UUID_POLICY_DEBUG
123 uuid_string_t uuidstr
;
124 uuid_unparse(uuid
, uuidstr
);
127 if (uuid_is_null(uuid
)) {
131 MALLOC(entry
, struct proc_uuid_policy_entry
*, sizeof(*entry
), M_PROC_UUID_POLICY
, M_WAITOK
| M_ZERO
);
133 memcpy(entry
->uuid
, uuid
, sizeof(uuid_t
));
134 entry
->flags
= flags
;
136 PROC_UUID_POLICY_SUBSYS_LOCK();
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
;
143 FREE(entry
, M_PROC_UUID_POLICY
);
145 BUMP_PROC_UUID_POLICY_GENERATION_COUNT();
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
++;
152 BUMP_PROC_UUID_POLICY_GENERATION_COUNT();
158 PROC_UUID_POLICY_SUBSYS_UNLOCK();
161 FREE(entry
, M_PROC_UUID_POLICY
);
162 dprintf("Failed to insert proc uuid policy (%s,0x%08x), table full\n", uuidstr
, flags
);
164 dprintf("Inserted proc uuid policy (%s,0x%08x)\n", uuidstr
, flags
);
170 static struct proc_uuid_policy_entry
*
171 proc_uuid_policy_remove_locked(uuid_t uuid
, uint32_t flags
, int *should_delete
)
173 struct proc_uuid_policy_entry
*foundentry
= NULL
;
178 foundentry
= proc_uuid_policy_lookup_locked(uuid
);
180 if (foundentry
->flags
== flags
) {
181 LIST_REMOVE(foundentry
, entries
);
182 proc_uuid_policy_count
--;
187 foundentry
->flags
&= ~flags
;
195 proc_uuid_policy_remove(uuid_t uuid
, uint32_t flags
)
197 struct proc_uuid_policy_entry
*delentry
= NULL
;
199 int should_delete
= 0;
201 #if PROC_UUID_POLICY_DEBUG
202 uuid_string_t uuidstr
;
203 uuid_unparse(uuid
, uuidstr
);
206 if (uuid_is_null(uuid
)) {
210 PROC_UUID_POLICY_SUBSYS_LOCK();
212 delentry
= proc_uuid_policy_remove_locked(uuid
, flags
, &should_delete
);
216 BUMP_PROC_UUID_POLICY_GENERATION_COUNT();
221 PROC_UUID_POLICY_SUBSYS_UNLOCK();
223 /* If we had found a pre-existing entry, deallocate its memory now */
224 if (delentry
&& should_delete
) {
225 FREE(delentry
, M_PROC_UUID_POLICY
);
229 dprintf("Failed to remove proc uuid policy (%s), entry not present\n", uuidstr
);
231 dprintf("Removed proc uuid policy (%s)\n", uuidstr
);
237 static struct proc_uuid_policy_entry
*
238 proc_uuid_policy_lookup_locked(uuid_t uuid
)
240 struct proc_uuid_policy_entry
*tmpentry
, *searchentry
, *foundentry
= NULL
;
242 LIST_FOREACH_SAFE(searchentry
, UUIDHASH(uuid
), entries
, tmpentry
) {
243 if (0 == memcmp(searchentry
->uuid
, uuid
, sizeof(uuid_t
))) {
244 foundentry
= searchentry
;
253 proc_uuid_policy_lookup(uuid_t uuid
, uint32_t *flags
, int32_t *gencount
)
255 struct proc_uuid_policy_entry
*foundentry
= NULL
;
258 #if PROC_UUID_POLICY_DEBUG
259 uuid_string_t uuidstr
;
260 uuid_unparse(uuid
, uuidstr
);
263 if (uuid_is_null(uuid
) || !flags
|| !gencount
) {
267 if (*gencount
== proc_uuid_policy_table_gencount
) {
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.
276 PROC_UUID_POLICY_SUBSYS_LOCK();
278 foundentry
= proc_uuid_policy_lookup_locked(uuid
);
281 *flags
= foundentry
->flags
;
282 *gencount
= proc_uuid_policy_table_gencount
;
288 PROC_UUID_POLICY_SUBSYS_UNLOCK();
291 dprintf("Looked up proc uuid policy (%s,0x%08x)\n", uuidstr
, *flags
);
298 proc_uuid_policy_clear(uint32_t flags
)
300 struct proc_uuid_policy_entry
*tmpentry
, *searchentry
;
301 struct proc_uuid_policy_hashhead deletehead
= LIST_HEAD_INITIALIZER(deletehead
);
302 unsigned long hashslot
;
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
;
309 PROC_UUID_POLICY_SUBSYS_LOCK();
311 if (proc_uuid_policy_count
> 0) {
312 for (hashslot
= 0; hashslot
<= proc_uuid_policy_hash_mask
; hashslot
++) {
313 struct proc_uuid_policy_hashhead
*headp
= &proc_uuid_policy_hashtbl
[hashslot
];
315 LIST_FOREACH_SAFE(searchentry
, headp
, entries
, tmpentry
) {
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
);
322 searchentry
->flags
&= ~flags
;
327 BUMP_PROC_UUID_POLICY_GENERATION_COUNT();
330 PROC_UUID_POLICY_SUBSYS_UNLOCK();
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
);
338 dprintf("Clearing proc uuid policy table\n");
344 proc_uuid_policy_kernel(uint32_t operation
, uuid_t uuid
, uint32_t flags
)
349 case PROC_UUID_POLICY_OPERATION_CLEAR
:
350 error
= proc_uuid_policy_clear(flags
);
353 case PROC_UUID_POLICY_OPERATION_ADD
:
354 error
= proc_uuid_policy_insert(uuid
, flags
);
357 case PROC_UUID_POLICY_OPERATION_REMOVE
:
358 error
= proc_uuid_policy_remove(uuid
, flags
);
370 proc_uuid_policy(struct proc
*p __unused
, struct proc_uuid_policy_args
*uap
, int32_t *retval __unused
)
374 memcpy(uuid
, UUID_NULL
, sizeof(uuid_t
));
376 /* Need privilege for policy changes */
377 error
= priv_check_cred(kauth_cred_get(), PRIV_PROC_UUID_POLICY
, 0);
379 dprintf("%s failed privilege check for proc_uuid_policy: %d\n", p
->p_comm
, error
);
382 dprintf("%s succeeded privilege check for proc_uuid_policy\n", p
->p_comm
);
386 if (uap
->uuidlen
!= sizeof(uuid_t
)) {
390 error
= copyin(uap
->uuid
, uuid
, sizeof(uuid_t
));
396 return proc_uuid_policy_kernel(uap
->operation
, uuid
, uap
->flags
);