gems-kernel/source/THIRDPARTY/xnu/bsd/kern/proc_uuid_policy.c
2024-06-03 11:29:39 -05:00

392 lines
10 KiB
C

/*
* Copyright (c) 2013 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
#include <sys/param.h>
#include <sys/malloc.h>
#include <sys/queue.h>
#include <sys/systm.h>
#include <sys/priv.h>
#include <sys/sysproto.h>
#include <sys/proc_uuid_policy.h>
#include <kern/locks.h>
#include <uuid/uuid.h>
#include <string.h>
#include <libkern/OSAtomic.h>
#define PROC_UUID_POLICY_DEBUG 0
#if PROC_UUID_POLICY_DEBUG
#define dprintf(...) printf(__VA_ARGS__)
#else
#define dprintf(...) do { } while(0)
#endif
static LCK_GRP_DECLARE(proc_uuid_policy_subsys_lck_grp,
"proc_uuid_policy_subsys_lock");
static LCK_MTX_DECLARE(proc_uuid_policy_subsys_mutex,
&proc_uuid_policy_subsys_lck_grp);
#define PROC_UUID_POLICY_SUBSYS_LOCK() lck_mtx_lock(&proc_uuid_policy_subsys_mutex)
#define PROC_UUID_POLICY_SUBSYS_UNLOCK() lck_mtx_unlock(&proc_uuid_policy_subsys_mutex)
#define PROC_UUID_POLICY_HASH_SIZE 64
u_long proc_uuid_policy_hash_mask;
/* Assume first byte of UUIDs are evenly distributed */
#define UUIDHASH(uuid) (&proc_uuid_policy_hashtbl[uuid[0] & proc_uuid_policy_hash_mask])
static LIST_HEAD(proc_uuid_policy_hashhead, proc_uuid_policy_entry) * proc_uuid_policy_hashtbl;
/*
* On modification, invalidate cached lookups by bumping the generation count.
* Other calls will need to take the slowpath of taking
* the subsystem lock.
*/
static volatile int32_t proc_uuid_policy_table_gencount;
#define BUMP_PROC_UUID_POLICY_GENERATION_COUNT() do { \
if (OSIncrementAtomic(&proc_uuid_policy_table_gencount) == (INT32_MAX - 1)) { \
proc_uuid_policy_table_gencount = 1; \
} \
} while (0)
#define MAX_PROC_UUID_POLICY_COUNT 10240
static volatile int32_t proc_uuid_policy_count;
struct proc_uuid_policy_entry {
LIST_ENTRY(proc_uuid_policy_entry) entries;
uuid_t uuid; /* Mach-O executable UUID */
uint32_t flags; /* policy flag for that UUID */
};
static int
proc_uuid_policy_insert(uuid_t uuid, uint32_t flags);
static struct proc_uuid_policy_entry *
proc_uuid_policy_remove_locked(uuid_t uuid, uint32_t flags, int *should_delete);
static int
proc_uuid_policy_remove(uuid_t uuid, uint32_t flags);
static struct proc_uuid_policy_entry *
proc_uuid_policy_lookup_locked(uuid_t uuid);
static int
proc_uuid_policy_clear(uint32_t flags);
void
proc_uuid_policy_init(void)
{
proc_uuid_policy_hashtbl = hashinit(PROC_UUID_POLICY_HASH_SIZE, M_PROC_UUID_POLICY, &proc_uuid_policy_hash_mask);
proc_uuid_policy_table_gencount = 1;
proc_uuid_policy_count = 0;
}
static int
proc_uuid_policy_insert(uuid_t uuid, uint32_t flags)
{
struct proc_uuid_policy_entry *entry, *foundentry = NULL;
int error;
#if PROC_UUID_POLICY_DEBUG
uuid_string_t uuidstr;
uuid_unparse(uuid, uuidstr);
#endif
if (uuid_is_null(uuid)) {
return EINVAL;
}
entry = kalloc_type(struct proc_uuid_policy_entry, Z_WAITOK | Z_ZERO);
memcpy(entry->uuid, uuid, sizeof(uuid_t));
entry->flags = flags;
PROC_UUID_POLICY_SUBSYS_LOCK();
foundentry = proc_uuid_policy_lookup_locked(uuid);
if (foundentry != NULL) {
/* The UUID is already in the list. Update the flags. */
foundentry->flags |= flags;
error = 0;
kfree_type(struct proc_uuid_policy_entry, entry);
entry = NULL;
BUMP_PROC_UUID_POLICY_GENERATION_COUNT();
} else {
/* Our target UUID is not in the list, insert it now */
if (proc_uuid_policy_count < MAX_PROC_UUID_POLICY_COUNT) {
LIST_INSERT_HEAD(UUIDHASH(uuid), entry, entries);
proc_uuid_policy_count++;
error = 0;
BUMP_PROC_UUID_POLICY_GENERATION_COUNT();
} else {
error = ENOMEM;
}
}
PROC_UUID_POLICY_SUBSYS_UNLOCK();
if (error) {
kfree_type(struct proc_uuid_policy_entry, entry);
dprintf("Failed to insert proc uuid policy (%s,0x%08x), table full\n", uuidstr, flags);
} else {
dprintf("Inserted proc uuid policy (%s,0x%08x)\n", uuidstr, flags);
}
return error;
}
static struct proc_uuid_policy_entry *
proc_uuid_policy_remove_locked(uuid_t uuid, uint32_t flags, int *should_delete)
{
struct proc_uuid_policy_entry *foundentry = NULL;
if (should_delete) {
*should_delete = 0;
}
foundentry = proc_uuid_policy_lookup_locked(uuid);
if (foundentry) {
if (foundentry->flags == flags) {
LIST_REMOVE(foundentry, entries);
proc_uuid_policy_count--;
if (should_delete) {
*should_delete = 1;
}
} else {
foundentry->flags &= ~flags;
}
}
return foundentry;
}
static int
proc_uuid_policy_remove(uuid_t uuid, uint32_t flags)
{
struct proc_uuid_policy_entry *delentry = NULL;
int error;
int should_delete = 0;
#if PROC_UUID_POLICY_DEBUG
uuid_string_t uuidstr;
uuid_unparse(uuid, uuidstr);
#endif
if (uuid_is_null(uuid)) {
return EINVAL;
}
PROC_UUID_POLICY_SUBSYS_LOCK();
delentry = proc_uuid_policy_remove_locked(uuid, flags, &should_delete);
if (delentry) {
error = 0;
BUMP_PROC_UUID_POLICY_GENERATION_COUNT();
} else {
error = ENOENT;
}
PROC_UUID_POLICY_SUBSYS_UNLOCK();
/* If we had found a pre-existing entry, deallocate its memory now */
if (delentry && should_delete) {
kfree_type(struct proc_uuid_policy_entry, delentry);
}
if (error) {
dprintf("Failed to remove proc uuid policy (%s), entry not present\n", uuidstr);
} else {
dprintf("Removed proc uuid policy (%s)\n", uuidstr);
}
return error;
}
static struct proc_uuid_policy_entry *
proc_uuid_policy_lookup_locked(uuid_t uuid)
{
struct proc_uuid_policy_entry *tmpentry, *searchentry, *foundentry = NULL;
LIST_FOREACH_SAFE(searchentry, UUIDHASH(uuid), entries, tmpentry) {
if (0 == memcmp(searchentry->uuid, uuid, sizeof(uuid_t))) {
foundentry = searchentry;
break;
}
}
return foundentry;
}
int
proc_uuid_policy_lookup(uuid_t uuid, uint32_t *flags, int32_t *gencount)
{
struct proc_uuid_policy_entry *foundentry = NULL;
int error;
#if PROC_UUID_POLICY_DEBUG
uuid_string_t uuidstr;
uuid_unparse(uuid, uuidstr);
#endif
if (uuid_is_null(uuid) || !flags || !gencount) {
return EINVAL;
}
if (*gencount == proc_uuid_policy_table_gencount) {
/*
* Generation count hasn't changed, so old flags should be valid.
* We avoid taking the lock here by assuming any concurrent modifications
* to the table will invalidate the generation count.
*/
return 0;
}
PROC_UUID_POLICY_SUBSYS_LOCK();
foundentry = proc_uuid_policy_lookup_locked(uuid);
if (foundentry) {
*flags = foundentry->flags;
*gencount = proc_uuid_policy_table_gencount;
error = 0;
} else {
error = ENOENT;
}
PROC_UUID_POLICY_SUBSYS_UNLOCK();
if (error == 0) {
dprintf("Looked up proc uuid policy (%s,0x%08x)\n", uuidstr, *flags);
}
return error;
}
static int
proc_uuid_policy_clear(uint32_t flags)
{
struct proc_uuid_policy_entry *tmpentry, *searchentry;
struct proc_uuid_policy_hashhead deletehead = LIST_HEAD_INITIALIZER(deletehead);
unsigned long hashslot;
/* If clear call includes no flags, infer 'No Cellular' flag */
if (flags == PROC_UUID_POLICY_FLAGS_NONE) {
flags = PROC_UUID_NO_CELLULAR;
}
PROC_UUID_POLICY_SUBSYS_LOCK();
if (proc_uuid_policy_count > 0) {
for (hashslot = 0; hashslot <= proc_uuid_policy_hash_mask; hashslot++) {
struct proc_uuid_policy_hashhead *headp = &proc_uuid_policy_hashtbl[hashslot];
LIST_FOREACH_SAFE(searchentry, headp, entries, tmpentry) {
if ((searchentry->flags & flags) == searchentry->flags) {
/* We are clearing all flags for this entry, move entry to our delete list */
LIST_REMOVE(searchentry, entries);
proc_uuid_policy_count--;
LIST_INSERT_HEAD(&deletehead, searchentry, entries);
} else {
searchentry->flags &= ~flags;
}
}
}
BUMP_PROC_UUID_POLICY_GENERATION_COUNT();
}
PROC_UUID_POLICY_SUBSYS_UNLOCK();
/* Memory deallocation happens after the hash lock is dropped */
LIST_FOREACH_SAFE(searchentry, &deletehead, entries, tmpentry) {
LIST_REMOVE(searchentry, entries);
kfree_type(struct proc_uuid_policy_entry, searchentry);
}
dprintf("Clearing proc uuid policy table\n");
return 0;
}
int
proc_uuid_policy_kernel(uint32_t operation, uuid_t uuid, uint32_t flags)
{
int error = 0;
switch (operation) {
case PROC_UUID_POLICY_OPERATION_CLEAR:
error = proc_uuid_policy_clear(flags);
break;
case PROC_UUID_POLICY_OPERATION_ADD:
error = proc_uuid_policy_insert(uuid, flags);
break;
case PROC_UUID_POLICY_OPERATION_REMOVE:
error = proc_uuid_policy_remove(uuid, flags);
break;
default:
error = EINVAL;
break;
}
return error;
}
int
proc_uuid_policy(struct proc *p __unused, struct proc_uuid_policy_args *uap, int32_t *retval __unused)
{
int error = 0;
uuid_t uuid;
memcpy(uuid, UUID_NULL, sizeof(uuid_t));
/* Need privilege for policy changes */
error = priv_check_cred(kauth_cred_get(), PRIV_PROC_UUID_POLICY, 0);
if (error) {
dprintf("%s failed privilege check for proc_uuid_policy: %d\n", p->p_comm, error);
return error;
} else {
dprintf("%s succeeded privilege check for proc_uuid_policy\n", p->p_comm);
}
if (uap->uuid) {
if (uap->uuidlen != sizeof(uuid_t)) {
return ERANGE;
}
error = copyin(uap->uuid, uuid, sizeof(uuid_t));
if (error) {
return error;
}
}
return proc_uuid_policy_kernel(uap->operation, uuid, uap->flags);
}