#include <sys/param.h>
#include <sys/acct.h>
#include <sys/systm.h>
#include <sys/ucred.h>
#include <sys/proc_internal.h>
#include <sys/user.h>
#include <sys/timeb.h>
#include <sys/times.h>
#include <sys/malloc.h>
#include <sys/kauth.h>
#include <sys/kernel.h>
#include <bsm/audit_kernel.h>
#include <sys/mount.h>
#include <sys/sysproto.h>
#include <mach/message.h>
#include <mach/host_security.h>
#include <libkern/OSAtomic.h>
#include <kern/task.h>
#include <kern/lock.h>
#ifdef MACH_ASSERT
# undef MACH_ASSERT
#endif
#define MACH_ASSERT 1
#include <kern/assert.h>
#define CRED_DIAGNOSTIC 1
# define NULLCRED_CHECK(_c) do {if (((_c) == NOCRED) || ((_c) == FSCRED)) panic("bad credential %p", _c);} while(0)
static lck_mtx_t *kauth_resolver_mtx;
#define KAUTH_RESOLVER_LOCK() lck_mtx_lock(kauth_resolver_mtx);
#define KAUTH_RESOLVER_UNLOCK() lck_mtx_unlock(kauth_resolver_mtx);
static volatile pid_t kauth_resolver_identity;
static int kauth_resolver_registered;
static uint32_t kauth_resolver_sequence;
struct kauth_resolver_work {
TAILQ_ENTRY(kauth_resolver_work) kr_link;
struct kauth_identity_extlookup kr_work;
uint32_t kr_seqno;
int kr_refs;
int kr_flags;
#define KAUTH_REQUEST_UNSUBMITTED (1<<0)
#define KAUTH_REQUEST_SUBMITTED (1<<1)
#define KAUTH_REQUEST_DONE (1<<2)
int kr_result;
};
TAILQ_HEAD(kauth_resolver_unsubmitted_head, kauth_resolver_work) kauth_resolver_unsubmitted;
TAILQ_HEAD(kauth_resolver_submitted_head, kauth_resolver_work) kauth_resolver_submitted;
TAILQ_HEAD(kauth_resolver_done_head, kauth_resolver_work) kauth_resolver_done;
static int kauth_resolver_submit(struct kauth_identity_extlookup *lkp);
static int kauth_resolver_complete(user_addr_t message);
static int kauth_resolver_getwork(user_addr_t message);
#define KAUTH_CRED_PRIMES_COUNT 7
static const int kauth_cred_primes[KAUTH_CRED_PRIMES_COUNT] = {97, 241, 397, 743, 1499, 3989, 7499};
static int kauth_cred_primes_index = 0;
static int kauth_cred_table_size = 0;
TAILQ_HEAD(kauth_cred_entry_head, ucred);
static struct kauth_cred_entry_head * kauth_cred_table_anchor = NULL;
#define KAUTH_CRED_HASH_DEBUG 0
static int kauth_cred_add(kauth_cred_t new_cred);
static void kauth_cred_remove(kauth_cred_t cred);
static inline u_long kauth_cred_hash(const uint8_t *datap, int data_len, u_long start_key);
static u_long kauth_cred_get_hashkey(kauth_cred_t cred);
static kauth_cred_t kauth_cred_update(kauth_cred_t old_cred, kauth_cred_t new_cred, boolean_t retain_auditinfo);
#if KAUTH_CRED_HASH_DEBUG
static int kauth_cred_count = 0;
static void kauth_cred_hash_print(void);
static void kauth_cred_print(kauth_cred_t cred);
#endif
void
kauth_resolver_init(void)
{
TAILQ_INIT(&kauth_resolver_unsubmitted);
TAILQ_INIT(&kauth_resolver_submitted);
TAILQ_INIT(&kauth_resolver_done);
kauth_resolver_sequence = 31337;
kauth_resolver_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0);
}
static int
kauth_resolver_submit(struct kauth_identity_extlookup *lkp)
{
struct kauth_resolver_work *workp, *killp;
struct timespec ts;
int error, shouldfree;
if (kauth_resolver_identity == 0) {
error = tsleep(&ts, PZERO | PCATCH, "kr_submit", hz/2);
if (kauth_resolver_identity == 0) {
return(EWOULDBLOCK);
}
}
MALLOC(workp, struct kauth_resolver_work *, sizeof(*workp), M_KAUTH, M_WAITOK);
if (workp == NULL)
return(ENOMEM);
workp->kr_work = *lkp;
workp->kr_refs = 1;
workp->kr_flags = KAUTH_REQUEST_UNSUBMITTED;
workp->kr_result = 0;
KAUTH_RESOLVER_LOCK();
workp->kr_seqno = workp->kr_work.el_seqno = kauth_resolver_sequence++;
workp->kr_work.el_result = KAUTH_EXTLOOKUP_INPROG;
TAILQ_INSERT_TAIL(&kauth_resolver_unsubmitted, workp, kr_link);
wakeup_one((caddr_t)&kauth_resolver_unsubmitted);
for (;;) {
ts.tv_sec = 30;
ts.tv_nsec = 0;
error = msleep(workp, kauth_resolver_mtx, PCATCH, "kr_submit", &ts);
if ((error == 0) && (workp->kr_flags & KAUTH_REQUEST_DONE))
break;
if (kauth_resolver_identity == 0) {
error = EIO;
break;
}
if (error != 0)
break;
}
if (error == 0)
*lkp = workp->kr_work;
if ((error == EWOULDBLOCK) && (workp->kr_flags & KAUTH_REQUEST_UNSUBMITTED)) {
KAUTH_DEBUG("RESOLVER - request timed out without being collected for processing, resolver dead");
kauth_resolver_identity = 0;
TAILQ_FOREACH(killp, &kauth_resolver_submitted, kr_link)
wakeup(killp);
TAILQ_FOREACH(killp, &kauth_resolver_unsubmitted, kr_link)
wakeup(killp);
}
if (--workp->kr_refs <= 0) {
if (workp->kr_flags & KAUTH_REQUEST_DONE) {
TAILQ_REMOVE(&kauth_resolver_done, workp, kr_link);
} else if (workp->kr_flags & KAUTH_REQUEST_SUBMITTED) {
TAILQ_REMOVE(&kauth_resolver_submitted, workp, kr_link);
} else if (workp->kr_flags & KAUTH_REQUEST_UNSUBMITTED) {
TAILQ_REMOVE(&kauth_resolver_unsubmitted, workp, kr_link);
} else {
KAUTH_DEBUG("RESOLVER - completed request has no valid queue");
}
shouldfree = 1;
} else {
shouldfree = 0;
}
if (error == 0)
error = workp->kr_result;
KAUTH_RESOLVER_UNLOCK();
if (shouldfree)
FREE(workp, M_KAUTH);
KAUTH_DEBUG("RESOLVER - returning %d", error);
return(error);
}
int
identitysvc(__unused struct proc *p, struct identitysvc_args *uap, __unused register_t *retval)
{
int opcode = uap->opcode;
user_addr_t message = uap->message;
struct kauth_resolver_work *workp;
int error;
pid_t new_id;
if (opcode == KAUTH_EXTLOOKUP_REGISTER) {
new_id = current_proc()->p_pid;
if ((error = kauth_authorize_generic(kauth_cred_get(), KAUTH_GENERIC_ISSUSER)) != 0) {
KAUTH_DEBUG("RESOLVER - pid %d refused permission to become identity resolver", new_id);
return(error);
}
KAUTH_RESOLVER_LOCK();
if (kauth_resolver_identity != new_id) {
KAUTH_DEBUG("RESOLVER - new resolver %d taking over from old %d", new_id, kauth_resolver_identity);
while ((workp = TAILQ_LAST(&kauth_resolver_submitted, kauth_resolver_submitted_head)) != NULL) {
TAILQ_REMOVE(&kauth_resolver_submitted, workp, kr_link);
workp->kr_flags &= ~KAUTH_REQUEST_SUBMITTED;
workp->kr_flags |= KAUTH_REQUEST_UNSUBMITTED;
TAILQ_INSERT_HEAD(&kauth_resolver_unsubmitted, workp, kr_link);
}
kauth_resolver_identity = new_id;
kauth_resolver_registered = 1;
wakeup(&kauth_resolver_unsubmitted);
}
KAUTH_RESOLVER_UNLOCK();
return(0);
}
if (current_proc()->p_pid != kauth_resolver_identity) {
KAUTH_DEBUG("RESOLVER - call from bogus resolver %d\n", current_proc()->p_pid);
return(EPERM);
}
if (opcode & KAUTH_EXTLOOKUP_RESULT) {
if ((error = kauth_resolver_complete(message)) != 0)
return(error);
}
if (opcode & KAUTH_EXTLOOKUP_WORKER) {
if ((error = kauth_resolver_getwork(message)) != 0)
return(error);
}
return(0);
}
static int
kauth_resolver_getwork(user_addr_t message)
{
struct kauth_resolver_work *workp;
int error;
KAUTH_RESOLVER_LOCK();
error = 0;
while ((workp = TAILQ_FIRST(&kauth_resolver_unsubmitted)) == NULL) {
error = msleep(&kauth_resolver_unsubmitted, kauth_resolver_mtx, PCATCH, "GRGetWork", 0);
if (error != 0)
break;
}
if (workp != NULL) {
if ((error = copyout(&workp->kr_work, message, sizeof(workp->kr_work))) != 0) {
KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
goto out;
}
TAILQ_REMOVE(&kauth_resolver_unsubmitted, workp, kr_link);
workp->kr_flags &= ~KAUTH_REQUEST_UNSUBMITTED;
workp->kr_flags |= KAUTH_REQUEST_SUBMITTED;
TAILQ_INSERT_TAIL(&kauth_resolver_submitted, workp, kr_link);
}
out:
KAUTH_RESOLVER_UNLOCK();
return(error);
}
static int
kauth_resolver_complete(user_addr_t message)
{
struct kauth_identity_extlookup extl;
struct kauth_resolver_work *workp;
int error, result;
if ((error = copyin(message, &extl, sizeof(extl))) != 0) {
KAUTH_DEBUG("RESOLVER - error getting completed work\n");
return(error);
}
KAUTH_RESOLVER_LOCK();
error = 0;
result = 0;
switch (extl.el_result) {
case KAUTH_EXTLOOKUP_INPROG:
{
static int once = 0;
if (!once) {
printf("kauth_resolver: memberd is not setting valid result codes (assuming always successful)\n");
once = 1;
}
}
case KAUTH_EXTLOOKUP_SUCCESS:
break;
case KAUTH_EXTLOOKUP_FATAL:
KAUTH_DEBUG("RESOLVER - resolver %d died, waiting for a new one", kauth_resolver_identity);
kauth_resolver_identity = 0;
error = EIO;
break;
case KAUTH_EXTLOOKUP_BADRQ:
KAUTH_DEBUG("RESOLVER - resolver reported invalid request %d", extl.el_seqno);
result = EINVAL;
break;
case KAUTH_EXTLOOKUP_FAILURE:
KAUTH_DEBUG("RESOLVER - resolver reported transient failure for request %d", extl.el_seqno);
result = EIO;
break;
default:
KAUTH_DEBUG("RESOLVER - resolver returned unexpected status %d", extl.el_result);
result = EIO;
break;
}
if (extl.el_result != KAUTH_EXTLOOKUP_FATAL) {
TAILQ_FOREACH(workp, &kauth_resolver_submitted, kr_link) {
if (workp->kr_seqno == extl.el_seqno) {
workp->kr_work = extl;
TAILQ_REMOVE(&kauth_resolver_submitted, workp, kr_link);
workp->kr_flags &= ~KAUTH_REQUEST_SUBMITTED;
workp->kr_flags |= KAUTH_REQUEST_DONE;
workp->kr_result = result;
TAILQ_INSERT_TAIL(&kauth_resolver_done, workp, kr_link);
wakeup(workp);
break;
}
}
}
KAUTH_RESOLVER_UNLOCK();
return(error);
}
struct kauth_identity {
TAILQ_ENTRY(kauth_identity) ki_link;
int ki_valid;
#define KI_VALID_UID (1<<0)
#define KI_VALID_GID (1<<1)
#define KI_VALID_GUID (1<<2)
#define KI_VALID_NTSID (1<<3)
uid_t ki_uid;
gid_t ki_gid;
guid_t ki_guid;
ntsid_t ki_ntsid;
time_t ki_guid_expiry;
time_t ki_ntsid_expiry;
};
static TAILQ_HEAD(kauth_identity_head, kauth_identity) kauth_identities;
#define KAUTH_IDENTITY_CACHEMAX 100
static int kauth_identity_count;
static lck_mtx_t *kauth_identity_mtx;
#define KAUTH_IDENTITY_LOCK() lck_mtx_lock(kauth_identity_mtx);
#define KAUTH_IDENTITY_UNLOCK() lck_mtx_unlock(kauth_identity_mtx);
static struct kauth_identity *kauth_identity_alloc(uid_t uid, gid_t gid, guid_t *guidp, time_t guid_expiry,
ntsid_t *ntsidp, time_t ntsid_expiry);
static void kauth_identity_register(struct kauth_identity *kip);
static void kauth_identity_updatecache(struct kauth_identity_extlookup *elp, struct kauth_identity *kip);
static void kauth_identity_lru(struct kauth_identity *kip);
static int kauth_identity_guid_expired(struct kauth_identity *kip);
static int kauth_identity_ntsid_expired(struct kauth_identity *kip);
static int kauth_identity_find_uid(uid_t uid, struct kauth_identity *kir);
static int kauth_identity_find_gid(gid_t gid, struct kauth_identity *kir);
static int kauth_identity_find_guid(guid_t *guidp, struct kauth_identity *kir);
static int kauth_identity_find_ntsid(ntsid_t *ntsid, struct kauth_identity *kir);
void
kauth_identity_init(void)
{
TAILQ_INIT(&kauth_identities);
kauth_identity_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0);
}
static int
kauth_identity_resolve(__unused struct kauth_identity_extlookup *el)
{
return(kauth_resolver_submit(el));
}
static struct kauth_identity *
kauth_identity_alloc(uid_t uid, gid_t gid, guid_t *guidp, time_t guid_expiry, ntsid_t *ntsidp, time_t ntsid_expiry)
{
struct kauth_identity *kip;
MALLOC(kip, struct kauth_identity *, sizeof(*kip), M_KAUTH, M_WAITOK | M_ZERO);
if (kip != NULL) {
if (gid != KAUTH_GID_NONE) {
kip->ki_gid = gid;
kip->ki_valid = KI_VALID_GID;
}
if (uid != KAUTH_UID_NONE) {
if (kip->ki_valid & KI_VALID_GID)
panic("can't allocate kauth identity with both uid and gid");
kip->ki_uid = uid;
kip->ki_valid = KI_VALID_UID;
}
if (guidp != NULL) {
kip->ki_guid = *guidp;
kip->ki_valid |= KI_VALID_GUID;
}
kip->ki_guid_expiry = guid_expiry;
if (ntsidp != NULL) {
kip->ki_ntsid = *ntsidp;
kip->ki_valid |= KI_VALID_NTSID;
}
kip->ki_ntsid_expiry = ntsid_expiry;
}
return(kip);
}
static void
kauth_identity_register(struct kauth_identity *kip)
{
struct kauth_identity *ip;
ip = NULL;
KAUTH_IDENTITY_LOCK();
if (kip->ki_valid & KI_VALID_UID) {
if (kip->ki_valid & KI_VALID_GID)
panic("kauth_identity: can't insert record with both UID and GID as key");
TAILQ_FOREACH(ip, &kauth_identities, ki_link)
if ((ip->ki_valid & KI_VALID_UID) && (ip->ki_uid == kip->ki_uid))
break;
} else if (kip->ki_valid & KI_VALID_GID) {
TAILQ_FOREACH(ip, &kauth_identities, ki_link)
if ((ip->ki_valid & KI_VALID_GID) && (ip->ki_gid == kip->ki_gid))
break;
} else {
panic("kauth_identity: can't insert record without UID or GID as key");
}
if (ip != NULL) {
if (kip->ki_valid & KI_VALID_GUID) {
ip->ki_guid = kip->ki_guid;
ip->ki_valid |= KI_VALID_GUID;
}
ip->ki_guid_expiry = kip->ki_guid_expiry;
if (kip->ki_valid & KI_VALID_NTSID) {
ip->ki_ntsid = kip->ki_ntsid;
ip->ki_valid |= KI_VALID_NTSID;
}
ip->ki_ntsid_expiry = kip->ki_ntsid_expiry;
FREE(kip, M_KAUTH);
ip = NULL;
} else {
TAILQ_INSERT_HEAD(&kauth_identities, kip, ki_link);
if (++kauth_identity_count > KAUTH_IDENTITY_CACHEMAX) {
ip = TAILQ_LAST(&kauth_identities, kauth_identity_head);
TAILQ_REMOVE(&kauth_identities, ip, ki_link);
kauth_identity_count--;
}
}
KAUTH_IDENTITY_UNLOCK();
if (ip != NULL)
FREE(ip, M_KAUTH);
}
static void
kauth_identity_updatecache(struct kauth_identity_extlookup *elp, struct kauth_identity *rkip)
{
struct timeval tv;
struct kauth_identity *kip;
microuptime(&tv);
if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_UID) {
KAUTH_IDENTITY_LOCK();
TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
if ((kip->ki_valid & KI_VALID_UID) && (kip->ki_uid == elp->el_uid)) {
if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_UGUID) {
kip->ki_guid = elp->el_uguid;
kip->ki_valid |= KI_VALID_GUID;
}
kip->ki_guid_expiry = tv.tv_sec + elp->el_uguid_valid;
if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_USID) {
kip->ki_ntsid = elp->el_usid;
kip->ki_valid |= KI_VALID_NTSID;
}
kip->ki_ntsid_expiry = tv.tv_sec + elp->el_usid_valid;
kauth_identity_lru(kip);
if (rkip != NULL)
*rkip = *kip;
KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT, kip->ki_uid, K_UUID_ARG(kip->ki_guid));
break;
}
}
KAUTH_IDENTITY_UNLOCK();
if (kip == NULL) {
kip = kauth_identity_alloc(elp->el_uid, KAUTH_GID_NONE,
(elp->el_flags & KAUTH_EXTLOOKUP_VALID_UGUID) ? &elp->el_uguid : NULL,
tv.tv_sec + elp->el_uguid_valid,
(elp->el_flags & KAUTH_EXTLOOKUP_VALID_USID) ? &elp->el_usid : NULL,
tv.tv_sec + elp->el_usid_valid);
if (kip != NULL) {
if (rkip != NULL)
*rkip = *kip;
KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT, kip->ki_uid, K_UUID_ARG(kip->ki_guid));
kauth_identity_register(kip);
}
}
}
if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GID) {
KAUTH_IDENTITY_LOCK();
TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
if ((kip->ki_valid & KI_VALID_GID) && (kip->ki_gid == elp->el_gid)) {
if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GGUID) {
kip->ki_guid = elp->el_gguid;
kip->ki_valid |= KI_VALID_GUID;
}
kip->ki_guid_expiry = tv.tv_sec + elp->el_gguid_valid;
if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GSID) {
kip->ki_ntsid = elp->el_gsid;
kip->ki_valid |= KI_VALID_NTSID;
}
kip->ki_ntsid_expiry = tv.tv_sec + elp->el_gsid_valid;
kauth_identity_lru(kip);
if (rkip != NULL)
*rkip = *kip;
KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT, kip->ki_uid, K_UUID_ARG(kip->ki_guid));
break;
}
}
KAUTH_IDENTITY_UNLOCK();
if (kip == NULL) {
kip = kauth_identity_alloc(KAUTH_UID_NONE, elp->el_gid,
(elp->el_flags & KAUTH_EXTLOOKUP_VALID_GGUID) ? &elp->el_gguid : NULL,
tv.tv_sec + elp->el_gguid_valid,
(elp->el_flags & KAUTH_EXTLOOKUP_VALID_GSID) ? &elp->el_gsid : NULL,
tv.tv_sec + elp->el_gsid_valid);
if (kip != NULL) {
if (rkip != NULL)
*rkip = *kip;
KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT, kip->ki_uid, K_UUID_ARG(kip->ki_guid));
kauth_identity_register(kip);
}
}
}
}
static void
kauth_identity_lru(struct kauth_identity *kip)
{
if (kip != TAILQ_FIRST(&kauth_identities)) {
TAILQ_REMOVE(&kauth_identities, kip, ki_link);
TAILQ_INSERT_HEAD(&kauth_identities, kip, ki_link);
}
}
static int
kauth_identity_guid_expired(struct kauth_identity *kip)
{
struct timeval tv;
microuptime(&tv);
KAUTH_DEBUG("CACHE - GUID expires @ %d now %d", kip->ki_guid_expiry, tv.tv_sec);
return((kip->ki_guid_expiry <= tv.tv_sec) ? 1 : 0);
}
static int
kauth_identity_ntsid_expired(struct kauth_identity *kip)
{
struct timeval tv;
microuptime(&tv);
KAUTH_DEBUG("CACHE - NTSID expires @ %d now %d", kip->ki_ntsid_expiry, tv.tv_sec);
return((kip->ki_ntsid_expiry <= tv.tv_sec) ? 1 : 0);
}
static int
kauth_identity_find_uid(uid_t uid, struct kauth_identity *kir)
{
struct kauth_identity *kip;
KAUTH_IDENTITY_LOCK();
TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
if ((kip->ki_valid & KI_VALID_UID) && (uid == kip->ki_uid)) {
kauth_identity_lru(kip);
*kir = *kip;
break;
}
}
KAUTH_IDENTITY_UNLOCK();
return((kip == NULL) ? ENOENT : 0);
}
static int
kauth_identity_find_gid(uid_t gid, struct kauth_identity *kir)
{
struct kauth_identity *kip;
KAUTH_IDENTITY_LOCK();
TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
if ((kip->ki_valid & KI_VALID_GID) && (gid == kip->ki_gid)) {
kauth_identity_lru(kip);
*kir = *kip;
break;
}
}
KAUTH_IDENTITY_UNLOCK();
return((kip == NULL) ? ENOENT : 0);
}
static int
kauth_identity_find_guid(guid_t *guidp, struct kauth_identity *kir)
{
struct kauth_identity *kip;
KAUTH_IDENTITY_LOCK();
TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
if ((kip->ki_valid & KI_VALID_GUID) && (kauth_guid_equal(guidp, &kip->ki_guid))) {
kauth_identity_lru(kip);
*kir = *kip;
break;
}
}
KAUTH_IDENTITY_UNLOCK();
return((kip == NULL) ? ENOENT : 0);
}
static int
kauth_identity_find_ntsid(ntsid_t *ntsid, struct kauth_identity *kir)
{
struct kauth_identity *kip;
KAUTH_IDENTITY_LOCK();
TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
if ((kip->ki_valid & KI_VALID_NTSID) && (kauth_ntsid_equal(ntsid, &kip->ki_ntsid))) {
kauth_identity_lru(kip);
*kir = *kip;
break;
}
}
KAUTH_IDENTITY_UNLOCK();
return((kip == NULL) ? ENOENT : 0);
}
guid_t kauth_null_guid;
int
kauth_guid_equal(guid_t *guid1, guid_t *guid2)
{
return(!bcmp(guid1, guid2, sizeof(*guid1)));
}
int
kauth_wellknown_guid(guid_t *guid)
{
static char fingerprint[] = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef};
int code;
if (!bcmp((void *)guid, fingerprint, 12)) {
code = *(u_int32_t *)&guid->g_guid[12];
switch(code) {
case 0x0000000c:
return(KAUTH_WKG_EVERYBODY);
case 0xfffffffe:
return(KAUTH_WKG_NOBODY);
case 0x0000000a:
return(KAUTH_WKG_OWNER);
case 0x00000010:
return(KAUTH_WKG_GROUP);
}
}
return(KAUTH_WKG_NOT);
}
int
kauth_ntsid_equal(ntsid_t *sid1, ntsid_t *sid2)
{
if ((KAUTH_NTSID_SIZE(sid1) == KAUTH_NTSID_SIZE(sid2)) &&
(KAUTH_NTSID_SIZE(sid1) <= sizeof(*sid1)) &&
!bcmp(sid1, sid2, KAUTH_NTSID_SIZE(sid1)))
return(1);
return(0);
}
static int kauth_cred_cache_lookup(int from, int to, void *src, void *dst);
uid_t
kauth_cred_getuid(kauth_cred_t cred)
{
NULLCRED_CHECK(cred);
return(cred->cr_uid);
}
uid_t
kauth_cred_getgid(kauth_cred_t cred)
{
NULLCRED_CHECK(cred);
return(cred->cr_gid);
}
int
kauth_cred_guid2uid(guid_t *guidp, uid_t *uidp)
{
return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_UID, guidp, uidp));
}
int
kauth_cred_guid2gid(guid_t *guidp, gid_t *gidp)
{
return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_GID, guidp, gidp));
}
int
kauth_cred_ntsid2uid(ntsid_t *sidp, uid_t *uidp)
{
return(kauth_cred_cache_lookup(KI_VALID_NTSID, KI_VALID_UID, sidp, uidp));
}
int
kauth_cred_ntsid2gid(ntsid_t *sidp, gid_t *gidp)
{
return(kauth_cred_cache_lookup(KI_VALID_NTSID, KI_VALID_GID, sidp, gidp));
}
int
kauth_cred_ntsid2guid(ntsid_t *sidp, guid_t *guidp)
{
return(kauth_cred_cache_lookup(KI_VALID_NTSID, KI_VALID_GUID, sidp, guidp));
}
int
kauth_cred_uid2guid(uid_t uid, guid_t *guidp)
{
return(kauth_cred_cache_lookup(KI_VALID_UID, KI_VALID_GUID, &uid, guidp));
}
int
kauth_cred_getguid(kauth_cred_t cred, guid_t *guidp)
{
NULLCRED_CHECK(cred);
return(kauth_cred_uid2guid(kauth_cred_getuid(cred), guidp));
}
int
kauth_cred_gid2guid(gid_t gid, guid_t *guidp)
{
return(kauth_cred_cache_lookup(KI_VALID_GID, KI_VALID_GUID, &gid, guidp));
}
int
kauth_cred_uid2ntsid(uid_t uid, ntsid_t *sidp)
{
return(kauth_cred_cache_lookup(KI_VALID_UID, KI_VALID_NTSID, &uid, sidp));
}
int
kauth_cred_getntsid(kauth_cred_t cred, ntsid_t *sidp)
{
NULLCRED_CHECK(cred);
return(kauth_cred_uid2ntsid(kauth_cred_getuid(cred), sidp));
}
int
kauth_cred_gid2ntsid(gid_t gid, ntsid_t *sidp)
{
return(kauth_cred_cache_lookup(KI_VALID_GID, KI_VALID_NTSID, &gid, sidp));
}
int
kauth_cred_guid2ntsid(guid_t *guidp, ntsid_t *sidp)
{
return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_NTSID, guidp, sidp));
}
static int
kauth_cred_cache_lookup(int from, int to, void *src, void *dst)
{
struct kauth_identity ki;
struct kauth_identity_extlookup el;
int error;
int (* expired)(struct kauth_identity *kip);
KAUTH_DEBUG("CACHE - translate %d to %d", from, to);
ki.ki_valid = 0;
switch(from) {
case KI_VALID_UID:
error = kauth_identity_find_uid(*(uid_t *)src, &ki);
break;
case KI_VALID_GID:
error = kauth_identity_find_gid(*(gid_t *)src, &ki);
break;
case KI_VALID_GUID:
error = kauth_identity_find_guid((guid_t *)src, &ki);
break;
case KI_VALID_NTSID:
error = kauth_identity_find_ntsid((ntsid_t *)src, &ki);
break;
default:
return(EINVAL);
}
if (error != 0) {
if (error != ENOENT) {
KAUTH_DEBUG("CACHE - cache search error %d", error);
return(error);
}
} else {
if (ki.ki_valid & to) {
switch(to) {
case KI_VALID_GUID:
expired = kauth_identity_guid_expired;
break;
case KI_VALID_NTSID:
expired = kauth_identity_ntsid_expired;
break;
default:
switch(from) {
case KI_VALID_GUID:
expired = kauth_identity_guid_expired;
break;
case KI_VALID_NTSID:
expired = kauth_identity_ntsid_expired;
break;
default:
expired = NULL;
}
}
KAUTH_DEBUG("CACHE - found matching entry with valid %d", ki.ki_valid);
if (!expired) {
KAUTH_DEBUG("CACHE - no expiry function");
goto found;
}
if (!expired(&ki)) {
KAUTH_DEBUG("CACHE - entry valid, unexpired");
goto found;
}
KAUTH_DEBUG("CACHE - expired entry found");
}
}
switch(from) {
case KI_VALID_UID:
el.el_flags = KAUTH_EXTLOOKUP_VALID_UID;
el.el_uid = *(uid_t *)src;
break;
case KI_VALID_GID:
el.el_flags = KAUTH_EXTLOOKUP_VALID_GID;
el.el_gid = *(gid_t *)src;
break;
case KI_VALID_GUID:
el.el_flags = KAUTH_EXTLOOKUP_VALID_UGUID | KAUTH_EXTLOOKUP_VALID_GGUID;
el.el_uguid = *(guid_t *)src;
el.el_gguid = *(guid_t *)src;
break;
case KI_VALID_NTSID:
el.el_flags = KAUTH_EXTLOOKUP_VALID_USID | KAUTH_EXTLOOKUP_VALID_GSID;
el.el_usid = *(ntsid_t *)src;
el.el_gsid = *(ntsid_t *)src;
break;
default:
return(EINVAL);
}
el.el_flags |= KAUTH_EXTLOOKUP_WANT_UID | KAUTH_EXTLOOKUP_WANT_GID |
KAUTH_EXTLOOKUP_WANT_UGUID | KAUTH_EXTLOOKUP_WANT_GGUID |
KAUTH_EXTLOOKUP_WANT_USID | KAUTH_EXTLOOKUP_WANT_GSID;
KAUTH_DEBUG("CACHE - calling resolver for %x", el.el_flags);
error = kauth_identity_resolve(&el);
KAUTH_DEBUG("CACHE - resolver returned %d", error);
if (error == 0) {
kauth_identity_updatecache(&el, &ki);
}
if (!error && !(ki.ki_valid & to))
error = ENOENT;
if (error)
return(error);
found:
switch(to) {
case KI_VALID_UID:
*(uid_t *)dst = ki.ki_uid;
break;
case KI_VALID_GID:
*(gid_t *)dst = ki.ki_gid;
break;
case KI_VALID_GUID:
*(guid_t *)dst = ki.ki_guid;
break;
case KI_VALID_NTSID:
*(ntsid_t *)dst = ki.ki_ntsid;
break;
default:
return(EINVAL);
}
KAUTH_DEBUG("CACHE - returned successfully");
return(0);
}
struct kauth_group_membership {
TAILQ_ENTRY(kauth_group_membership) gm_link;
uid_t gm_uid;
gid_t gm_gid;
time_t gm_expiry;
int gm_flags;
#define KAUTH_GROUP_ISMEMBER (1<<0)
};
TAILQ_HEAD(kauth_groups_head, kauth_group_membership) kauth_groups;
#define KAUTH_GROUPS_CACHEMAX 100
static int kauth_groups_count;
static lck_mtx_t *kauth_groups_mtx;
#define KAUTH_GROUPS_LOCK() lck_mtx_lock(kauth_groups_mtx);
#define KAUTH_GROUPS_UNLOCK() lck_mtx_unlock(kauth_groups_mtx);
static int kauth_groups_expired(struct kauth_group_membership *gm);
static void kauth_groups_lru(struct kauth_group_membership *gm);
static void kauth_groups_updatecache(struct kauth_identity_extlookup *el);
void
kauth_groups_init(void)
{
TAILQ_INIT(&kauth_groups);
kauth_groups_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0);
}
static int
kauth_groups_expired(struct kauth_group_membership *gm)
{
struct timeval tv;
microuptime(&tv);
return((gm->gm_expiry <= tv.tv_sec) ? 1 : 0);
}
static void
kauth_groups_lru(struct kauth_group_membership *gm)
{
if (gm != TAILQ_FIRST(&kauth_groups)) {
TAILQ_REMOVE(&kauth_groups, gm, gm_link);
TAILQ_INSERT_HEAD(&kauth_groups, gm, gm_link);
}
}
static void
kauth_groups_updatecache(struct kauth_identity_extlookup *el)
{
struct kauth_group_membership *gm;
struct timeval tv;
if ((el->el_flags &
(KAUTH_EXTLOOKUP_VALID_UID | KAUTH_EXTLOOKUP_VALID_GID | KAUTH_EXTLOOKUP_VALID_MEMBERSHIP)) !=
(KAUTH_EXTLOOKUP_VALID_UID | KAUTH_EXTLOOKUP_VALID_GID | KAUTH_EXTLOOKUP_VALID_MEMBERSHIP))
return;
microuptime(&tv);
KAUTH_GROUPS_LOCK();
TAILQ_FOREACH(gm, &kauth_groups, gm_link) {
if ((el->el_uid == gm->gm_uid) &&
(el->el_gid == gm->gm_gid)) {
if (el->el_flags & KAUTH_EXTLOOKUP_ISMEMBER) {
gm->gm_flags |= KAUTH_GROUP_ISMEMBER;
} else {
gm->gm_flags &= ~KAUTH_GROUP_ISMEMBER;
}
gm->gm_expiry = el->el_member_valid + tv.tv_sec;
kauth_groups_lru(gm);
break;
}
}
KAUTH_GROUPS_UNLOCK();
if (gm != NULL)
return;
MALLOC(gm, struct kauth_group_membership *, sizeof(*gm), M_KAUTH, M_WAITOK);
if (gm != NULL) {
gm->gm_uid = el->el_uid;
gm->gm_gid = el->el_gid;
if (el->el_flags & KAUTH_EXTLOOKUP_ISMEMBER) {
gm->gm_flags |= KAUTH_GROUP_ISMEMBER;
} else {
gm->gm_flags &= ~KAUTH_GROUP_ISMEMBER;
}
gm->gm_expiry = el->el_member_valid + tv.tv_sec;
}
KAUTH_GROUPS_LOCK();
TAILQ_INSERT_HEAD(&kauth_groups, gm, gm_link);
if (kauth_groups_count++ > KAUTH_GROUPS_CACHEMAX) {
gm = TAILQ_LAST(&kauth_groups, kauth_groups_head);
TAILQ_REMOVE(&kauth_groups, gm, gm_link);
kauth_groups_count--;
} else {
gm = NULL;
}
KAUTH_GROUPS_UNLOCK();
if (gm != NULL)
FREE(gm, M_KAUTH);
}
int
kauth_cred_ismember_gid(kauth_cred_t cred, gid_t gid, int *resultp)
{
struct kauth_group_membership *gm;
struct kauth_identity_extlookup el;
int i, error;
for (i = 0; i < cred->cr_ngroups; i++) {
if (gid == cred->cr_groups[i]) {
*resultp = 1;
return(0);
}
}
if (cred->cr_gmuid == KAUTH_UID_NONE) {
*resultp = 0;
return(0);
}
if (!kauth_resolver_registered) {
*resultp = 0;
return(0);
}
KAUTH_GROUPS_LOCK();
TAILQ_FOREACH(gm, &kauth_groups, gm_link) {
if ((gm->gm_uid == cred->cr_gmuid) && (gm->gm_gid == gid) && !kauth_groups_expired(gm)) {
kauth_groups_lru(gm);
break;
}
}
if (gm != NULL)
*resultp = (gm->gm_flags & KAUTH_GROUP_ISMEMBER) ? 1 : 0;
KAUTH_GROUPS_UNLOCK();
if (gm != NULL)
return(0);
el.el_flags = KAUTH_EXTLOOKUP_VALID_UID | KAUTH_EXTLOOKUP_VALID_GID | KAUTH_EXTLOOKUP_WANT_MEMBERSHIP;
el.el_uid = cred->cr_gmuid;
el.el_gid = gid;
error = kauth_identity_resolve(&el);
if (error != 0)
return(error);
kauth_groups_updatecache(&el);
if (el.el_flags & KAUTH_EXTLOOKUP_VALID_MEMBERSHIP) {
*resultp = (el.el_flags & KAUTH_EXTLOOKUP_ISMEMBER) ? 1 : 0;
return(0);
}
return(ENOENT);
}
int
kauth_cred_ismember_guid(kauth_cred_t cred, guid_t *guidp, int *resultp)
{
gid_t gid;
int error, wkg;
error = 0;
wkg = kauth_wellknown_guid(guidp);
switch(wkg) {
case KAUTH_WKG_NOBODY:
*resultp = 0;
break;
case KAUTH_WKG_EVERYBODY:
*resultp = 1;
break;
default:
if ((error = kauth_cred_guid2gid(guidp, &gid)) != 0) {
if (error == ENOENT) {
*resultp = 0;
error = 0;
}
} else {
error = kauth_cred_ismember_gid(cred, gid, resultp);
}
}
return(error);
}
int
kauth_cred_issuser(kauth_cred_t cred)
{
return(cred->cr_uid == 0);
}
static lck_mtx_t *kauth_cred_hash_mtx;
#define KAUTH_CRED_HASH_LOCK() lck_mtx_lock(kauth_cred_hash_mtx);
#define KAUTH_CRED_HASH_UNLOCK() lck_mtx_unlock(kauth_cred_hash_mtx);
void
kauth_cred_init(void)
{
int i;
kauth_cred_hash_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0);
kauth_cred_table_size = kauth_cred_primes[kauth_cred_primes_index];
MALLOC(kauth_cred_table_anchor, struct kauth_cred_entry_head *,
(sizeof(struct kauth_cred_entry_head) * kauth_cred_table_size),
M_KAUTH, M_WAITOK | M_ZERO);
for (i = 0; i < kauth_cred_table_size; i++) {
TAILQ_INIT(&kauth_cred_table_anchor[i]);
}
}
uid_t
kauth_getuid(void)
{
return(kauth_cred_get()->cr_uid);
}
uid_t
kauth_getruid(void)
{
return(kauth_cred_get()->cr_ruid);
}
gid_t
kauth_getgid(void)
{
return(kauth_cred_get()->cr_groups[0]);
}
gid_t
kauth_getrgid(void)
{
return(kauth_cred_get()->cr_rgid);
}
kauth_cred_t
kauth_cred_get(void)
{
struct proc *p;
struct uthread *uthread;
uthread = get_bsdthread_info(current_thread());
if (uthread == NULL)
panic("thread wants credential but has no BSD thread info");
if (uthread->uu_ucred == NOCRED) {
if ((p = (proc_t) get_bsdtask_info(get_threadtask(current_thread()))) == NULL)
panic("thread wants credential but has no BSD process");
proc_lock(p);
kauth_cred_ref(uthread->uu_ucred = p->p_ucred);
proc_unlock(p);
}
return(uthread->uu_ucred);
}
kauth_cred_t
kauth_cred_get_with_ref(void)
{
struct proc *procp;
struct uthread *uthread;
uthread = get_bsdthread_info(current_thread());
if (uthread == NULL)
panic("%s - thread wants credential but has no BSD thread info", __FUNCTION__);
if ((procp = (proc_t) get_bsdtask_info(get_threadtask(current_thread()))) == NULL)
panic("%s - thread wants credential but has no BSD process", __FUNCTION__);
proc_lock(procp);
if (uthread->uu_ucred == NOCRED) {
kauth_cred_ref(uthread->uu_ucred = proc_ucred(procp));
}
kauth_cred_ref(uthread->uu_ucred);
proc_unlock(procp);
return(uthread->uu_ucred);
}
kauth_cred_t
kauth_cred_proc_ref(proc_t procp)
{
kauth_cred_t cred;
proc_lock(procp);
cred = proc_ucred(procp);
kauth_cred_ref(cred);
proc_unlock(procp);
return(cred);
}
kauth_cred_t
kauth_cred_alloc(void)
{
kauth_cred_t newcred;
MALLOC(newcred, kauth_cred_t, sizeof(*newcred), M_KAUTH, M_WAITOK | M_ZERO);
if (newcred != 0) {
newcred->cr_ref = 1;
newcred->cr_gmuid = KAUTH_UID_NONE;
#if CRED_DIAGNOSTIC
} else {
panic("kauth_cred_alloc: couldn't allocate credential");
#endif
}
#if KAUTH_CRED_HASH_DEBUG
kauth_cred_count++;
#endif
return(newcred);
}
kauth_cred_t
kauth_cred_create(kauth_cred_t cred)
{
kauth_cred_t found_cred, new_cred = NULL;
cred->cr_gmuid = cred->cr_uid;
for (;;) {
KAUTH_CRED_HASH_LOCK();
found_cred = kauth_cred_find(cred);
if (found_cred != NULL) {
kauth_cred_ref(found_cred);
KAUTH_CRED_HASH_UNLOCK();
return(found_cred);
}
KAUTH_CRED_HASH_UNLOCK();
new_cred = kauth_cred_alloc();
if (new_cred != NULL) {
int err;
new_cred->cr_uid = cred->cr_uid;
new_cred->cr_ruid = cred->cr_ruid;
new_cred->cr_svuid = cred->cr_svuid;
new_cred->cr_rgid = cred->cr_rgid;
new_cred->cr_svgid = cred->cr_svgid;
new_cred->cr_gmuid = cred->cr_gmuid;
new_cred->cr_ngroups = cred->cr_ngroups;
bcopy(&cred->cr_groups[0], &new_cred->cr_groups[0], sizeof(new_cred->cr_groups));
KAUTH_CRED_HASH_LOCK();
err = kauth_cred_add(new_cred);
KAUTH_CRED_HASH_UNLOCK();
if (err == 0)
break;
FREE(new_cred, M_KAUTH);
new_cred = NULL;
}
}
return(new_cred);
}
kauth_cred_t
kauth_cred_setuid(kauth_cred_t cred, uid_t uid)
{
struct ucred temp_cred;
NULLCRED_CHECK(cred);
if (cred->cr_uid == uid && cred->cr_ruid == uid && cred->cr_svuid == uid) {
return(cred);
}
bcopy(cred, &temp_cred, sizeof(temp_cred));
temp_cred.cr_uid = uid;
temp_cred.cr_ruid = uid;
temp_cred.cr_svuid = uid;
temp_cred.cr_gmuid = uid;
return(kauth_cred_update(cred, &temp_cred, TRUE));
}
kauth_cred_t
kauth_cred_seteuid(kauth_cred_t cred, uid_t euid)
{
struct ucred temp_cred;
NULLCRED_CHECK(cred);
if (cred->cr_uid == euid) {
return(cred);
}
bcopy(cred, &temp_cred, sizeof(temp_cred));
temp_cred.cr_uid = euid;
return(kauth_cred_update(cred, &temp_cred, TRUE));
}
kauth_cred_t
kauth_cred_setgid(kauth_cred_t cred, gid_t gid)
{
struct ucred temp_cred;
NULLCRED_CHECK(cred);
if (cred->cr_groups[0] == gid && cred->cr_rgid == gid && cred->cr_svgid == gid) {
return(cred);
}
bcopy(cred, &temp_cred, sizeof(temp_cred));
temp_cred.cr_groups[0] = gid;
temp_cred.cr_rgid = gid;
temp_cred.cr_svgid = gid;
return(kauth_cred_update(cred, &temp_cred, TRUE));
}
kauth_cred_t
kauth_cred_setegid(kauth_cred_t cred, gid_t egid)
{
struct ucred temp_cred;
NULLCRED_CHECK(cred);
if (cred->cr_groups[0] == egid) {
return(cred);
}
bcopy(cred, &temp_cred, sizeof(temp_cred));
temp_cred.cr_groups[0] = egid;
return(kauth_cred_update(cred, &temp_cred, TRUE));
}
kauth_cred_t
kauth_cred_setgroups(kauth_cred_t cred, gid_t *groups, int groupcount, uid_t gmuid)
{
int i;
struct ucred temp_cred;
NULLCRED_CHECK(cred);
if ((cred->cr_gmuid == gmuid) && (cred->cr_ngroups == groupcount)) {
for (i = 0; i < groupcount; i++) {
if (cred->cr_groups[i] != groups[i])
break;
}
if (i == groupcount) {
return(cred);
}
}
bcopy(cred, &temp_cred, sizeof(temp_cred));
temp_cred.cr_ngroups = groupcount;
bcopy(groups, temp_cred.cr_groups, sizeof(temp_cred.cr_groups));
temp_cred.cr_gmuid = gmuid;
return(kauth_cred_update(cred, &temp_cred, TRUE));
}
kauth_cred_t
kauth_cred_setuidgid(kauth_cred_t cred, uid_t uid, gid_t gid)
{
struct ucred temp_cred;
NULLCRED_CHECK(cred);
if (cred->cr_uid == uid && cred->cr_ruid == uid && cred->cr_svuid == uid &&
cred->cr_groups[0] == gid && cred->cr_rgid == gid && cred->cr_svgid == gid) {
return(cred);
}
bzero(&temp_cred, sizeof(temp_cred));
temp_cred.cr_uid = uid;
temp_cred.cr_ruid = uid;
temp_cred.cr_svuid = uid;
temp_cred.cr_gmuid = uid;
temp_cred.cr_ngroups = 1;
temp_cred.cr_groups[0] = gid;
temp_cred.cr_rgid = gid;
temp_cred.cr_svgid = gid;
return(kauth_cred_update(cred, &temp_cred, TRUE));
}
kauth_cred_t
kauth_cred_setsvuidgid(kauth_cred_t cred, uid_t uid, gid_t gid)
{
struct ucred temp_cred;
NULLCRED_CHECK(cred);
if (cred->cr_svuid == uid && cred->cr_svgid == gid) {
return(cred);
}
bcopy(cred, &temp_cred, sizeof(temp_cred));
temp_cred.cr_svuid = uid;
temp_cred.cr_svgid = gid;
return(kauth_cred_update(cred, &temp_cred, TRUE));
}
kauth_cred_t
kauth_cred_setauditinfo(kauth_cred_t cred, auditinfo_t *auditinfo_p)
{
struct ucred temp_cred;
NULLCRED_CHECK(cred);
if (bcmp(&cred->cr_au, auditinfo_p, sizeof(cred->cr_au)) == 0) {
return(cred);
}
bcopy(cred, &temp_cred, sizeof(temp_cred));
bcopy(auditinfo_p, &temp_cred.cr_au, sizeof(temp_cred.cr_au));
return(kauth_cred_update(cred, &temp_cred, FALSE));
}
void
kauth_cred_ref(kauth_cred_t cred)
{
int old_value;
NULLCRED_CHECK(cred);
old_value = OSAddAtomic(1, &cred->cr_ref);
if (old_value < 1)
panic("kauth_cred_ref: trying to take a reference on a cred with no references");
return;
}
void
kauth_cred_rele(kauth_cred_t cred)
{
int old_value;
NULLCRED_CHECK(cred);
KAUTH_CRED_HASH_LOCK();
old_value = OSAddAtomic(-1, &cred->cr_ref);
#if DIAGNOSTIC
if (old_value == 0)
panic("kauth_cred_rele: dropping a reference on a cred with no references");
#endif
if (old_value < 3) {
kauth_cred_remove(cred);
}
KAUTH_CRED_HASH_UNLOCK();
}
kauth_cred_t
kauth_cred_dup(kauth_cred_t cred)
{
kauth_cred_t newcred;
#if CRED_DIAGNOSTIC
if (cred == NOCRED || cred == FSCRED)
panic("kauth_cred_dup: bad credential");
#endif
newcred = kauth_cred_alloc();
if (newcred != NULL) {
bcopy(cred, newcred, sizeof(*newcred));
newcred->cr_ref = 1;
}
return(newcred);
}
kauth_cred_t
kauth_cred_copy_real(kauth_cred_t cred)
{
kauth_cred_t newcred = NULL, found_cred;
struct ucred temp_cred;
if ((cred->cr_ruid == cred->cr_uid) &&
(cred->cr_rgid == cred->cr_gid)) {
kauth_cred_ref(cred);
return(cred);
}
bcopy(cred, &temp_cred, sizeof(temp_cred));
temp_cred.cr_uid = cred->cr_ruid;
temp_cred.cr_groups[0] = cred->cr_rgid;
if (temp_cred.cr_gmuid != KAUTH_UID_NONE)
temp_cred.cr_gmuid = cred->cr_ruid;
for (;;) {
int err;
KAUTH_CRED_HASH_LOCK();
found_cred = kauth_cred_find(&temp_cred);
if (found_cred == cred) {
KAUTH_CRED_HASH_UNLOCK();
return(cred);
}
if (found_cred != NULL) {
kauth_cred_ref(found_cred);
KAUTH_CRED_HASH_UNLOCK();
return(found_cred);
}
newcred = kauth_cred_dup(&temp_cred);
err = kauth_cred_add(newcred);
KAUTH_CRED_HASH_UNLOCK();
if (err == 0)
break;
FREE(newcred, M_KAUTH);
newcred = NULL;
}
return(newcred);
}
static kauth_cred_t kauth_cred_update(kauth_cred_t old_cred, kauth_cred_t model_cred, boolean_t retain_auditinfo)
{
kauth_cred_t found_cred, new_cred = NULL;
if (retain_auditinfo)
bcopy(&old_cred->cr_au, &model_cred->cr_au, sizeof(model_cred->cr_au));
for (;;) {
int err;
KAUTH_CRED_HASH_LOCK();
found_cred = kauth_cred_find(model_cred);
if (found_cred == old_cred) {
KAUTH_CRED_HASH_UNLOCK();
return(old_cred);
}
if (found_cred != NULL) {
kauth_cred_ref(found_cred);
KAUTH_CRED_HASH_UNLOCK();
kauth_cred_rele(old_cred);
return(found_cred);
}
new_cred = kauth_cred_dup(model_cred);
err = kauth_cred_add(new_cred);
KAUTH_CRED_HASH_UNLOCK();
if (err == 0)
break;
FREE(new_cred, M_KAUTH);
new_cred = NULL;
}
kauth_cred_rele(old_cred);
return(new_cred);
}
static int kauth_cred_add(kauth_cred_t new_cred)
{
u_long hash_key;
hash_key = kauth_cred_get_hashkey(new_cred);
hash_key %= kauth_cred_table_size;
if (kauth_cred_find(new_cred) != NULL) {
return(-1);
}
kauth_cred_ref(new_cred);
TAILQ_INSERT_HEAD(&kauth_cred_table_anchor[hash_key], new_cred, cr_link);
return(0);
}
static void kauth_cred_remove(kauth_cred_t cred)
{
u_long hash_key;
kauth_cred_t found_cred;
hash_key = kauth_cred_get_hashkey(cred);
hash_key %= kauth_cred_table_size;
if (cred->cr_ref < 1)
panic("cred reference underflow");
if (cred->cr_ref > 1)
return;
TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[hash_key], cr_link) {
if (found_cred == cred) {
TAILQ_REMOVE(&kauth_cred_table_anchor[hash_key], found_cred, cr_link);
FREE(cred, M_KAUTH);
#if KAUTH_CRED_HASH_DEBUG
kauth_cred_count--;
#endif
return;
}
}
printf("%s - %d - %s - did not find a match \n", __FILE__, __LINE__, __FUNCTION__);
return;
}
kauth_cred_t kauth_cred_find(kauth_cred_t cred)
{
u_long hash_key;
kauth_cred_t found_cred;
#if KAUTH_CRED_HASH_DEBUG
static int test_count = 0;
test_count++;
if ((test_count % 200) == 0) {
kauth_cred_hash_print();
}
#endif
hash_key = kauth_cred_get_hashkey(cred);
hash_key %= kauth_cred_table_size;
TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[hash_key], cr_link) {
if (bcmp(&found_cred->cr_uid, &cred->cr_uid, (sizeof(struct ucred) - offsetof(struct ucred, cr_uid))) == 0) {
return(found_cred);
}
}
return(NULL);
}
static u_long kauth_cred_get_hashkey(kauth_cred_t cred)
{
u_long hash_key = 0;
hash_key = kauth_cred_hash((uint8_t *)&cred->cr_uid,
(sizeof(struct ucred) - offsetof(struct ucred, cr_uid)),
hash_key);
return(hash_key);
}
static inline u_long kauth_cred_hash(const uint8_t *datap, int data_len, u_long start_key)
{
u_long hash_key = start_key;
u_long temp;
while (data_len > 0) {
hash_key = (hash_key << 4) + *datap++;
temp = hash_key & 0xF0000000;
if (temp) {
hash_key ^= temp >> 24;
}
hash_key &= ~temp;
data_len--;
}
return(hash_key);
}
#if KAUTH_CRED_HASH_DEBUG
static void kauth_cred_hash_print(void)
{
int i, j;
kauth_cred_t found_cred;
printf("\n\t kauth credential hash table statistics - current cred count %d \n", kauth_cred_count);
for (i = 0; i < kauth_cred_table_size; i++) {
printf("[%02d] ", i);
j = 0;
TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[i], cr_link) {
if (j > 0) {
printf("---- ");
}
j++;
kauth_cred_print(found_cred);
printf("\n");
}
if (j == 0) {
printf("NOCRED \n");
}
}
}
static void kauth_cred_print(kauth_cred_t cred)
{
int i;
printf("0x%02X - refs %d uids %d %d %d ", cred, cred->cr_ref, cred->cr_uid, cred->cr_ruid, cred->cr_svuid);
printf("group count %d gids ", cred->cr_ngroups);
for (i = 0; i < NGROUPS; i++) {
printf("%d ", cred->cr_groups[i]);
}
printf("%d %d %d ", cred->cr_rgid, cred->cr_svgid, cred->cr_gmuid);
printf("auditinfo %d %d %d %d %d %d ",
cred->cr_au.ai_auid, cred->cr_au.ai_mask.am_success, cred->cr_au.ai_mask.am_failure,
cred->cr_au.ai_termid.port, cred->cr_au.ai_termid.machine, cred->cr_au.ai_asid);
}
#endif