]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/proc_uuid_policy.c
xnu-6153.41.3.tar.gz
[apple/xnu.git] / bsd / kern / proc_uuid_policy.c
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_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;
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
104 proc_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
116 static int
117 proc_uuid_policy_insert(uuid_t uuid, uint32_t flags)
118 {
119 struct proc_uuid_policy_entry *entry, *foundentry = NULL;
120 int error;
121
122 #if PROC_UUID_POLICY_DEBUG
123 uuid_string_t uuidstr;
124 uuid_unparse(uuid, uuidstr);
125 #endif
126
127 if (uuid_is_null(uuid)) {
128 return EINVAL;
129 }
130
131 MALLOC(entry, struct proc_uuid_policy_entry *, sizeof(*entry), M_PROC_UUID_POLICY, M_WAITOK | M_ZERO);
132
133 memcpy(entry->uuid, uuid, sizeof(uuid_t));
134 entry->flags = flags;
135
136 PROC_UUID_POLICY_SUBSYS_LOCK();
137
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;
142 error = 0;
143 FREE(entry, M_PROC_UUID_POLICY);
144 entry = NULL;
145 BUMP_PROC_UUID_POLICY_GENERATION_COUNT();
146 } else {
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 }
156 }
157
158 PROC_UUID_POLICY_SUBSYS_UNLOCK();
159
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
170 static struct proc_uuid_policy_entry *
171 proc_uuid_policy_remove_locked(uuid_t uuid, uint32_t flags, int *should_delete)
172 {
173 struct proc_uuid_policy_entry *foundentry = NULL;
174 if (should_delete) {
175 *should_delete = 0;
176 }
177
178 foundentry = proc_uuid_policy_lookup_locked(uuid);
179 if (foundentry) {
180 if (foundentry->flags == flags) {
181 LIST_REMOVE(foundentry, entries);
182 proc_uuid_policy_count--;
183 if (should_delete) {
184 *should_delete = 1;
185 }
186 } else {
187 foundentry->flags &= ~flags;
188 }
189 }
190
191 return foundentry;
192 }
193
194 static int
195 proc_uuid_policy_remove(uuid_t uuid, uint32_t flags)
196 {
197 struct proc_uuid_policy_entry *delentry = NULL;
198 int error;
199 int should_delete = 0;
200
201 #if PROC_UUID_POLICY_DEBUG
202 uuid_string_t uuidstr;
203 uuid_unparse(uuid, uuidstr);
204 #endif
205
206 if (uuid_is_null(uuid)) {
207 return EINVAL;
208 }
209
210 PROC_UUID_POLICY_SUBSYS_LOCK();
211
212 delentry = proc_uuid_policy_remove_locked(uuid, flags, &should_delete);
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 */
224 if (delentry && should_delete) {
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
237 static struct proc_uuid_policy_entry *
238 proc_uuid_policy_lookup_locked(uuid_t uuid)
239 {
240 struct proc_uuid_policy_entry *tmpentry, *searchentry, *foundentry = NULL;
241
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 }
248
249 return foundentry;
250 }
251
252 int
253 proc_uuid_policy_lookup(uuid_t uuid, uint32_t *flags, int32_t *gencount)
254 {
255 struct proc_uuid_policy_entry *foundentry = NULL;
256 int error;
257
258 #if PROC_UUID_POLICY_DEBUG
259 uuid_string_t uuidstr;
260 uuid_unparse(uuid, uuidstr);
261 #endif
262
263 if (uuid_is_null(uuid) || !flags || !gencount) {
264 return EINVAL;
265 }
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
278 foundentry = proc_uuid_policy_lookup_locked(uuid);
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
297 static int
298 proc_uuid_policy_clear(uint32_t flags)
299 {
300 struct proc_uuid_policy_entry *tmpentry, *searchentry;
301 struct proc_uuid_policy_hashhead deletehead = LIST_HEAD_INITIALIZER(deletehead);
302 unsigned long hashslot;
303
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 }
308
309 PROC_UUID_POLICY_SUBSYS_LOCK();
310
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];
314
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);
321 } else {
322 searchentry->flags &= ~flags;
323 }
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");
339
340 return 0;
341 }
342
343 int
344 proc_uuid_policy_kernel(uint32_t operation, uuid_t uuid, uint32_t flags)
345 {
346 int error = 0;
347
348 switch (operation) {
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;
364 }
365
366 return error;
367 }
368
369 int
370 proc_uuid_policy(struct proc *p __unused, struct proc_uuid_policy_args *uap, int32_t *retval __unused)
371 {
372 int error = 0;
373 uuid_t uuid;
374 memcpy(uuid, UUID_NULL, sizeof(uuid_t));
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);
380 return error;
381 } else {
382 dprintf("%s succeeded privilege check for proc_uuid_policy\n", p->p_comm);
383 }
384
385 if (uap->uuid) {
386 if (uap->uuidlen != sizeof(uuid_t)) {
387 return ERANGE;
388 }
389
390 error = copyin(uap->uuid, uuid, sizeof(uuid_t));
391 if (error) {
392 return error;
393 }
394 }
395
396 return proc_uuid_policy_kernel(uap->operation, uuid, uap->flags);
397 }