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} 362