X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/6601e61aa18bf4f09af135ff61fc7f4771d23b06..0c530ab8987f0ae6a1a3d9284f40182b88852816:/bsd/kern/kern_credential.c?ds=sidebyside diff --git a/bsd/kern/kern_credential.c b/bsd/kern/kern_credential.c index 0a917310f..59c1a24ee 100644 --- a/bsd/kern/kern_credential.c +++ b/bsd/kern/kern_credential.c @@ -56,7 +56,7 @@ #define CRED_DIAGNOSTIC 1 -# define NULLCRED_CHECK(_c) do {if (((_c) == NOCRED) || ((_c) == FSCRED)) panic("bad credential %p", _c);} while(0) +# define NULLCRED_CHECK(_c) do {if (!IS_VALID_CRED(_c)) panic("bad credential %p", _c);} while(0) /* * Interface to external identity resolver. @@ -102,13 +102,14 @@ 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 +#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); +static void kauth_cred_unref_hashlocked(kauth_cred_t *credp); #if KAUTH_CRED_HASH_DEBUG static int kauth_cred_count = 0; @@ -1436,6 +1437,11 @@ kauth_cred_issuser(kauth_cred_t cred) 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); +#if KAUTH_CRED_HASH_DEBUG +#define KAUTH_CRED_HASH_LOCK_ASSERT() _mutex_assert(kauth_cred_hash_mtx, MA_OWNED) +#else /* !KAUTH_CRED_HASH_DEBUG */ +#define KAUTH_CRED_HASH_LOCK_ASSERT() +#endif /* !KAUTH_CRED_HASH_DEBUG */ void kauth_cred_init(void) @@ -1513,9 +1519,7 @@ kauth_cred_get(void) 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); + uthread->uu_ucred = kauth_cred_proc_ref(p); } return(uthread->uu_ucred); } @@ -1541,14 +1545,12 @@ kauth_cred_get_with_ref(void) * If we later inline this function, the code in this block should probably be * called out in a function. */ - proc_lock(procp); if (uthread->uu_ucred == NOCRED) { /* take reference for new cred in thread */ - kauth_cred_ref(uthread->uu_ucred = proc_ucred(procp)); + uthread->uu_ucred = kauth_cred_proc_ref(procp); } /* take a reference for our caller */ kauth_cred_ref(uthread->uu_ucred); - proc_unlock(procp); return(uthread->uu_ucred); } @@ -1575,7 +1577,7 @@ kauth_cred_alloc(void) { kauth_cred_t newcred; - MALLOC(newcred, kauth_cred_t, sizeof(*newcred), M_KAUTH, M_WAITOK | M_ZERO); + MALLOC(newcred, kauth_cred_t, sizeof(*newcred), M_CRED, M_WAITOK | M_ZERO); if (newcred != 0) { newcred->cr_ref = 1; /* must do this, or cred has same group membership as uid 0 */ @@ -1639,7 +1641,7 @@ kauth_cred_create(kauth_cred_t cred) /* retry if kauth_cred_add returns non zero value */ if (err == 0) break; - FREE(new_cred, M_KAUTH); + FREE(new_cred, M_CRED); new_cred = NULL; } } @@ -1741,6 +1743,7 @@ kauth_cred_setgid(kauth_cred_t cred, gid_t gid) return(kauth_cred_update(cred, &temp_cred, TRUE)); } + /* * Update the given credential using the egid argument. The given gid is used * set the effective user ID. We only allocate a new credential when the given @@ -1930,27 +1933,49 @@ kauth_cred_ref(kauth_cred_t cred) /* * Drop a reference from the passed credential, potentially destroying it. + * + * Note: Assumes credential hash is NOT locked */ void -kauth_cred_rele(kauth_cred_t cred) +kauth_cred_unref(kauth_cred_t *credp) { - int old_value; + KAUTH_CRED_HASH_LOCK(); + kauth_cred_unref_hashlocked(credp); + KAUTH_CRED_HASH_UNLOCK(); +} - NULLCRED_CHECK(cred); +/* + * Drop a reference from the passed credential, potentially destroying it. + * + * Note: Assumes credential hash IS locked + */ +static void +kauth_cred_unref_hashlocked(kauth_cred_t *credp) +{ + int old_value; - KAUTH_CRED_HASH_LOCK(); - old_value = OSAddAtomic(-1, &cred->cr_ref); + KAUTH_CRED_HASH_LOCK_ASSERT(); + NULLCRED_CHECK(*credp); + old_value = OSAddAtomic(-1, &(*credp)->cr_ref); #if DIAGNOSTIC if (old_value == 0) - panic("kauth_cred_rele: dropping a reference on a cred with no references"); + panic("%s:0x%08x kauth_cred_rele: dropping a reference on a cred with no references", current_proc()->p_comm, *credp); + if (old_value == 1) + panic("%s:0x%08x kauth_cred_rele: dropping a reference on a cred with no hash entry", current_proc()->p_comm, *credp); #endif if (old_value < 3) { /* the last reference is our credential hash table */ - kauth_cred_remove(cred); + kauth_cred_remove(*credp); } - KAUTH_CRED_HASH_UNLOCK(); + *credp = NOCRED; +} + +void +kauth_cred_rele(kauth_cred_t cred) +{ + kauth_cred_unref(&cred); } /* @@ -1998,7 +2023,10 @@ kauth_cred_copy_real(kauth_cred_t cred) bcopy(cred, &temp_cred, sizeof(temp_cred)); temp_cred.cr_uid = cred->cr_ruid; temp_cred.cr_groups[0] = cred->cr_rgid; - /* if the cred is not opted out, make sure we are using the r/euid for group checks */ + /* + * if the cred is not opted out, make sure we are using the r/euid + * for group checks + */ if (temp_cred.cr_gmuid != KAUTH_UID_NONE) temp_cred.cr_gmuid = cred->cr_ruid; @@ -2013,8 +2041,9 @@ kauth_cred_copy_real(kauth_cred_t cred) return(cred); } if (found_cred != NULL) { - /* found a match so we bump reference count on new one and decrement - * reference count on the old one. + /* + * found a match so we bump reference count on new one. + * we leave the old one alone. */ kauth_cred_ref(found_cred); KAUTH_CRED_HASH_UNLOCK(); @@ -2031,7 +2060,7 @@ kauth_cred_copy_real(kauth_cred_t cred) /* retry if kauth_cred_add returns non zero value */ if (err == 0) break; - FREE(newcred, M_KAUTH); + FREE(newcred, M_CRED); newcred = NULL; } @@ -2039,12 +2068,16 @@ kauth_cred_copy_real(kauth_cred_t cred) } /* - * common code to update a credential. model_cred is a temporary, non reference - * counted credential used only for comparison and modeling purposes. old_cred - * is a live reference counted credential that we intend to update using model_cred - * as our model. + * Common code to update a credential. model_cred is a temporary, non + * reference counted credential used only for comparison and modeling + * purposes. old_cred is a live reference counted credential that we + * intend to update using model_cred as our model. + * + * IMPORTANT: If the old_cred ends up updated by this process, we will, as + * a side effect, drop the reference we held on it going in. */ -static kauth_cred_t kauth_cred_update(kauth_cred_t old_cred, kauth_cred_t model_cred, boolean_t retain_auditinfo) +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; @@ -2065,12 +2098,13 @@ static kauth_cred_t kauth_cred_update(kauth_cred_t old_cred, kauth_cred_t model_ return(old_cred); } if (found_cred != NULL) { - /* found a match so we bump reference count on new one and decrement - * reference count on the old one. + /* + * found a match so we bump reference count on new + * one and decrement reference count on the old one. */ kauth_cred_ref(found_cred); + kauth_cred_unref_hashlocked(&old_cred); KAUTH_CRED_HASH_UNLOCK(); - kauth_cred_rele(old_cred); return(found_cred); } @@ -2084,11 +2118,11 @@ static kauth_cred_t kauth_cred_update(kauth_cred_t old_cred, kauth_cred_t model_ /* retry if kauth_cred_add returns non zero value */ if (err == 0) break; - FREE(new_cred, M_KAUTH); + FREE(new_cred, M_CRED); new_cred = NULL; } - kauth_cred_rele(old_cred); + kauth_cred_unref(&old_cred); return(new_cred); } @@ -2097,10 +2131,13 @@ static kauth_cred_t kauth_cred_update(kauth_cred_t old_cred, kauth_cred_t model_ * reference to account for our use of the credential in the hash table. * NOTE - expects caller to hold KAUTH_CRED_HASH_LOCK! */ -static int kauth_cred_add(kauth_cred_t new_cred) +static int +kauth_cred_add(kauth_cred_t new_cred) { u_long hash_key; - + + KAUTH_CRED_HASH_LOCK_ASSERT(); + hash_key = kauth_cred_get_hashkey(new_cred); hash_key %= kauth_cred_table_size; @@ -2122,30 +2159,35 @@ static int kauth_cred_add(kauth_cred_t new_cred) return(0); } + /* * Remove the given credential from our credential hash table. * NOTE - expects caller to hold KAUTH_CRED_HASH_LOCK! */ -static void kauth_cred_remove(kauth_cred_t cred) +static void +kauth_cred_remove(kauth_cred_t cred) { u_long hash_key; kauth_cred_t found_cred; + KAUTH_CRED_HASH_LOCK_ASSERT(); + hash_key = kauth_cred_get_hashkey(cred); hash_key %= kauth_cred_table_size; /* avoid race */ if (cred->cr_ref < 1) panic("cred reference underflow"); - if (cred->cr_ref > 1) + if (cred->cr_ref > 1) { return; /* someone else got a ref */ - + } + /* find cred in the credential hash table */ TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[hash_key], cr_link) { if (found_cred == cred) { /* found a match, remove it from the hash table */ TAILQ_REMOVE(&kauth_cred_table_anchor[hash_key], found_cred, cr_link); - FREE(cred, M_KAUTH); + FREE(cred, M_CRED); #if KAUTH_CRED_HASH_DEBUG kauth_cred_count--; #endif @@ -2154,7 +2196,7 @@ static void kauth_cred_remove(kauth_cred_t cred) } /* did not find a match. this should not happen! */ - printf("%s - %d - %s - did not find a match \n", __FILE__, __LINE__, __FUNCTION__); + printf("%s:%s - %d - %s - did not find a match for 0x%08x\n", __FILE__, __LINE__, __FUNCTION__, current_proc()->p_comm, cred); return; } @@ -2163,11 +2205,14 @@ static void kauth_cred_remove(kauth_cred_t cred) * table. * NOTE - expects caller to hold KAUTH_CRED_HASH_LOCK! */ -kauth_cred_t kauth_cred_find(kauth_cred_t cred) +kauth_cred_t +kauth_cred_find(kauth_cred_t cred) { u_long hash_key; kauth_cred_t found_cred; + KAUTH_CRED_HASH_LOCK_ASSERT(); + #if KAUTH_CRED_HASH_DEBUG static int test_count = 0; @@ -2194,12 +2239,13 @@ kauth_cred_t kauth_cred_find(kauth_cred_t cred) /* * Generates a hash key using data that makes up a credential. Based on ElfHash. */ -static u_long kauth_cred_get_hashkey(kauth_cred_t cred) +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)), + (sizeof(struct ucred) - offsetof(struct ucred, cr_uid)), hash_key); return(hash_key); }