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