X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/0a7de7458d150b5d4dffc935ba399be265ef0a1a..a991bd8d3e7fe02dbca0644054bab73c5b75324a:/bsd/kern/kern_persona.c diff --git a/bsd/kern/kern_persona.c b/bsd/kern/kern_persona.c index c9c846717..b3470216a 100644 --- a/bsd/kern/kern_persona.c +++ b/bsd/kern/kern_persona.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Apple Inc. All rights reserved. + * Copyright (c) 2015-2020 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -28,12 +28,17 @@ #include #include #include +#include #if CONFIG_PERSONAS +#include + #include #include #include #include +#include +#include #include #include @@ -52,9 +57,6 @@ #define FIRST_PERSONA_ID 501 #define PERSONA_ID_STEP 10 -#define PERSONA_SYSTEM_UID ((uid_t)99) -#define PERSONA_SYSTEM_LOGIN "system" - #define PERSONA_ALLOC_TOKEN (0x7a0000ae) #define PERSONA_INIT_TOKEN (0x7500005e) #define PERSONA_MAGIC (0x0aa55aa0) @@ -64,33 +66,42 @@ static LIST_HEAD(personalist, persona) all_personas; static uint32_t g_total_personas; -uint32_t g_max_personas = MAX_PERSONAS; - -struct persona *g_system_persona = NULL; +const uint32_t g_max_personas = MAX_PERSONAS; +struct persona *system_persona = NULL; +struct persona *proxy_system_persona = NULL; +#if !defined(XNU_TARGET_OS_OSX) +int unique_persona = 1; +#else +int unique_persona = 0; +#endif static uid_t g_next_persona_id; -lck_mtx_t all_personas_lock; -lck_attr_t *persona_lck_attr; -lck_grp_t *persona_lck_grp; -lck_grp_attr_t *persona_lck_grp_attr; +LCK_GRP_DECLARE(persona_lck_grp, "personas"); +LCK_MTX_DECLARE(all_personas_lock, &persona_lck_grp); os_refgrp_decl(static, persona_refgrp, "persona", NULL); -static zone_t persona_zone; +static ZONE_DECLARE(persona_zone, "personas", sizeof(struct persona), ZC_ZFREE_CLEARMEM); kauth_cred_t g_default_persona_cred; +extern struct auditinfo_addr * const audit_default_aia_p; #define lock_personas() lck_mtx_lock(&all_personas_lock) #define unlock_personas() lck_mtx_unlock(&all_personas_lock) - extern void mach_kauth_cred_uthread_update(void); +extern kern_return_t bank_get_bank_ledger_thread_group_and_persona(void *voucher, + void *bankledger, void **banktg, uint32_t *persona_id); +void +ipc_voucher_release(void *voucher); + void personas_bootstrap(void) { struct posix_cred pcred; + int unique_persona_bootarg; persona_dbg("Initializing persona subsystem"); LIST_INIT(&all_personas); @@ -98,18 +109,6 @@ personas_bootstrap(void) g_next_persona_id = FIRST_PERSONA_ID; - persona_lck_grp_attr = lck_grp_attr_alloc_init(); - - persona_lck_grp = lck_grp_alloc_init("personas", persona_lck_grp_attr); - persona_lck_attr = lck_attr_alloc_init(); - - lck_mtx_init(&all_personas_lock, persona_lck_grp, persona_lck_attr); - - persona_zone = zinit(sizeof(struct persona), - MAX_PERSONAS * sizeof(struct persona), - MAX_PERSONAS, "personas"); - assert(persona_zone != NULL); - /* * setup the default credentials that a persona temporarily * inherits (to work around kauth APIs) @@ -126,20 +125,17 @@ personas_bootstrap(void) if (!g_default_persona_cred) { panic("couldn't create default persona credentials!"); } - - g_system_persona = persona_alloc(PERSONA_SYSTEM_UID, - PERSONA_SYSTEM_LOGIN, - PERSONA_SYSTEM, NULL); - int err = persona_init_begin(g_system_persona); - assert(err == 0); - - persona_init_end(g_system_persona, err); - - assert(g_system_persona != NULL); +#if CONFIG_AUDIT + /* posix_cred_create() sets this value to NULL */ + g_default_persona_cred->cr_audit.as_aia_p = audit_default_aia_p; +#endif + if (PE_parse_boot_argn("unique_persona", &unique_persona_bootarg, sizeof(unique_persona_bootarg))) { + unique_persona = !!unique_persona_bootarg; + } } struct persona * -persona_alloc(uid_t id, const char *login, int type, int *error) +persona_alloc(uid_t id, const char *login, persona_type_t type, char *path, int *error) { struct persona *persona; int err = 0; @@ -170,7 +166,7 @@ persona_alloc(uid_t id, const char *login, int type, int *error) bzero(persona, sizeof(*persona)); - if (hw_atomic_add(&g_total_personas, 1) > MAX_PERSONAS) { + if (os_atomic_inc(&g_total_personas, relaxed) > MAX_PERSONAS) { /* too many personas! */ pna_err("too many active personas!"); err = EBUSY; @@ -181,7 +177,7 @@ persona_alloc(uid_t id, const char *login, int type, int *error) persona_dbg("Starting persona allocation for: '%s'", persona->pna_login); LIST_INIT(&persona->pna_members); - lck_mtx_init(&persona->pna_lock, persona_lck_grp, persona_lck_attr); + lck_mtx_init(&persona->pna_lock, &persona_lck_grp, LCK_ATTR_NULL); os_ref_init(&persona->pna_refcount, &persona_refgrp); /* @@ -199,6 +195,7 @@ persona_alloc(uid_t id, const char *login, int type, int *error) persona->pna_type = type; persona->pna_id = id; persona->pna_valid = PERSONA_ALLOC_TOKEN; + persona->pna_path = path; /* * NOTE: this persona has not been fully initialized. A subsequent @@ -211,7 +208,7 @@ persona_alloc(uid_t id, const char *login, int type, int *error) return persona; out_error: - (void)hw_atomic_add(&g_total_personas, -1); + os_atomic_dec(&g_total_personas, relaxed); zfree(persona_zone, persona); if (error) { *error = err; @@ -375,7 +372,7 @@ persona_init_end(struct persona *persona, int error) if (error != 0 || persona->pna_valid == PERSONA_ALLOC_TOKEN) { persona_dbg("ERROR:%d after initialization of %d (%s)", error, persona->pna_id, persona->pna_login); /* remove this persona from the global count */ - (void)hw_atomic_add(&g_total_personas, -1); + os_atomic_dec(&g_total_personas, relaxed); } else if (error == 0 && persona->pna_valid == PERSONA_INIT_TOKEN) { persona->pna_valid = PERSONA_MAGIC; @@ -386,6 +383,76 @@ persona_init_end(struct persona *persona, int error) unlock_personas(); } +/** + * persona_verify_and_set_uniqueness + * + * This function checks the persona, if the one being spawned is of type + * PERSONA_SYSTEM or PERSONA_SYSTEM_PROXY, is unique. + * + * Conditions: + * global persona list is locked on entry and return. + * + * Returns: + * EEXIST: if persona is system/system-proxy and is not unique. + * 0: Otherwise. + */ +int +persona_verify_and_set_uniqueness(struct persona *persona) +{ + if (persona == NULL) { + return EINVAL; + } + + if (!unique_persona) { + return 0; + } + + if (persona->pna_type == PERSONA_SYSTEM) { + if (system_persona != NULL) { + return EEXIST; + } + system_persona = persona; + return 0; + } + + if (persona->pna_type == PERSONA_SYSTEM_PROXY) { + if (proxy_system_persona != NULL) { + return EEXIST; + } + proxy_system_persona = persona; + return 0; + } + return 0; +} + +/** + * persona_is_unique + * + * This function checks if the persona spawned is unique. + * + * Returns: + * TRUE: if unique. + * FALSE: otherwise. + */ +boolean_t +persona_is_unique(struct persona *persona) +{ + if (persona == NULL) { + return FALSE; + } + + if (!unique_persona) { + return FALSE; + } + + if (persona->pna_type == PERSONA_SYSTEM || + persona->pna_type == PERSONA_SYSTEM_PROXY) { + return TRUE; + } + + return FALSE; +} + static struct persona * persona_get_locked(struct persona *persona) { @@ -438,11 +505,14 @@ persona_put(struct persona *persona) persona_lock(persona); if (persona_valid(persona)) { LIST_REMOVE(persona, pna_list); - if (hw_atomic_add(&g_total_personas, -1) == UINT_MAX) { + if (os_atomic_dec_orig(&g_total_personas, relaxed) == 0) { panic("persona count underflow!\n"); } persona_mkinvalid(persona); } + if (persona->pna_path != NULL) { + zfree(ZV_NAMEI, persona->pna_path); + } persona_unlock(persona); unlock_personas(); @@ -497,11 +567,11 @@ persona_lookup_and_invalidate(uid_t id) LIST_FOREACH_SAFE(entry, &all_personas, pna_list, tmp) { persona_lock(entry); if (entry->pna_id == id) { - if (persona_valid(entry)) { + if (persona_valid(entry) && !persona_is_unique(entry)) { persona = persona_get_locked(entry); assert(persona != NULL); LIST_REMOVE(persona, pna_list); - if (hw_atomic_add(&g_total_personas, -1) == UINT_MAX) { + if (os_atomic_dec_orig(&g_total_personas, relaxed) == 0) { panic("persona ref count underflow!\n"); } persona_mkinvalid(persona); @@ -516,9 +586,22 @@ persona_lookup_and_invalidate(uid_t id) return persona; } +int +persona_find_by_type(persona_type_t persona_type, struct persona **persona, size_t *plen) +{ + return persona_find_all(NULL, PERSONA_ID_NONE, persona_type, persona, plen); +} + int persona_find(const char *login, uid_t uid, struct persona **persona, size_t *plen) +{ + return persona_find_all(login, uid, PERSONA_INVALID, persona, plen); +} + +int +persona_find_all(const char *login, uid_t uid, persona_type_t persona_type, + struct persona **persona, size_t *plen) { struct persona *tmp; int match = 0; @@ -530,6 +613,11 @@ persona_find(const char *login, uid_t uid, if (uid != PERSONA_ID_NONE) { match++; } + if ((persona_type > PERSONA_INVALID) && (persona_type <= PERSONA_TYPE_MAX)) { + match++; + } else if (persona_type != PERSONA_INVALID) { + return EINVAL; + } if (match == 0) { return EINVAL; @@ -548,6 +636,9 @@ persona_find(const char *login, uid_t uid, if (uid != PERSONA_ID_NONE && uid == tmp->pna_id) { m++; } + if (persona_type != PERSONA_INVALID && persona_type == tmp->pna_type) { + m++; + } if (m == match) { if (persona && *plen > found) { persona[found] = persona_get_locked(tmp); @@ -593,13 +684,29 @@ persona_proc_get(pid_t pid) struct persona * current_persona_get(void) { - proc_t p = current_proc(); - struct persona *persona; - - proc_lock(p); - persona = persona_get(p->p_persona); - proc_unlock(p); + struct persona *persona = NULL; + uid_t current_persona_id = PERSONA_ID_NONE; + ipc_voucher_t voucher; + thread_get_mach_voucher(current_thread(), 0, &voucher); + /* returns a voucher ref */ + if (voucher != IPC_VOUCHER_NULL) { + /* + * If the voucher doesn't contain a bank attribute, it uses + * the default bank task value to determine the persona id + * which is the same as the proc's persona id + */ + bank_get_bank_ledger_thread_group_and_persona(voucher, NULL, + NULL, ¤t_persona_id); + ipc_voucher_release(voucher); + persona = persona_lookup(current_persona_id); + } else { + /* Fallback - get the proc's persona */ + proc_t p = current_proc(); + proc_lock(p); + persona = persona_get(p->p_persona); + proc_unlock(p); + } return persona; } @@ -664,7 +771,7 @@ proc_reset_persona_internal(proc_t p, persona_reset_op_t op, switch (op) { case PROC_REMOVE_PERSONA: old_persona = p->p_persona; - /* fall through */ + OS_FALLTHROUGH; case PROC_RESET_OLD_PERSONA: break; default: @@ -675,6 +782,7 @@ proc_reset_persona_internal(proc_t p, persona_reset_op_t op, /* unlock the new persona (locked on entry) */ persona_unlock(new_persona); /* lock the old persona and the process */ + assert(old_persona != NULL); persona_lock(old_persona); proc_lock(p); @@ -710,7 +818,8 @@ proc_set_cred_internal(proc_t p, struct persona *persona, struct persona *old_persona = NULL; kauth_cred_t my_cred, my_new_cred; uid_t old_uid, new_uid; - int count; + size_t count; + rlim_t nproc = proc_limitgetcur(p, RLIMIT_NPROC, TRUE); /* * This operation must be done under the proc trans lock @@ -757,12 +866,14 @@ proc_set_cred_internal(proc_t p, struct persona *persona, * the process or changing its credentials. */ if (new_uid != 0 && - (rlim_t)chgproccnt(new_uid, 0) > p->p_rlimit[RLIMIT_NPROC].rlim_cur) { + (rlim_t)chgproccnt(new_uid, 0) > nproc) { pna_err("PID:%d hit proc rlimit in new persona(%d): %s", p->p_pid, new_uid, persona_desc(persona, 1)); *rlim_error = EACCES; - (void)proc_reset_persona_internal(p, PROC_RESET_OLD_PERSONA, - old_persona, persona); + if (old_persona) { + (void)proc_reset_persona_internal(p, PROC_RESET_OLD_PERSONA, + old_persona, persona); + } kauth_cred_unref(&my_new_cred); return NULL; } @@ -822,7 +933,7 @@ set_proc_cred: if (new_uid != old_uid) { count = chgproccnt(old_uid, -1); - persona_dbg("Decrement %s:%d proc_count to: %d", + persona_dbg("Decrement %s:%d proc_count to: %lu", old_persona ? "Persona" : "UID", old_uid, count); /* @@ -831,7 +942,7 @@ set_proc_cred: * as in fork1() */ count = chgproccnt(new_uid, 1); - persona_dbg("Increment Persona:%d (UID:%d) proc_count to: %d", + persona_dbg("Increment Persona:%d (UID:%d) proc_count to: %lu", new_uid, kauth_cred_getuid(my_new_cred), count); } @@ -852,7 +963,6 @@ persona_proc_adopt(proc_t p, struct persona *persona, kauth_cred_t auth_override { int error; struct persona *old_persona; - struct session * sessp; if (!persona) { return EINVAL; @@ -886,15 +996,21 @@ persona_proc_adopt(proc_t p, struct persona *persona, kauth_cred_t auth_override enterpgrp(p, persona->pna_pgid, persona->pna_pgid == uid); } + /* Only Multiuser Mode needs to update the session login name to the persona name */ +#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) + volatile uint32_t *multiuser_flag_address = (volatile uint32_t *)(uintptr_t)(_COMM_PAGE_MULTIUSER_CONFIG); + uint32_t multiuser_flags = *multiuser_flag_address; /* set the login name of the session */ - sessp = proc_session(p); - if (sessp != SESSION_NULL) { - session_lock(sessp); - bcopy(persona->pna_login, sessp->s_login, MAXLOGNAME); - session_unlock(sessp); - session_rele(sessp); + if (multiuser_flags) { + struct session * sessp = proc_session(p); + if (sessp != SESSION_NULL) { + session_lock(sessp); + bcopy(persona->pna_login, sessp->s_login, MAXLOGNAME); + session_unlock(sessp); + session_rele(sessp); + } } - +#endif persona_unlock(persona); set_security_token(p); @@ -1162,7 +1278,7 @@ persona_get_gid(struct persona *persona) } int -persona_set_groups(struct persona *persona, gid_t *groups, unsigned ngroups, uid_t gmuid) +persona_set_groups(struct persona *persona, gid_t *groups, size_t ngroups, uid_t gmuid) { int ret = 0; kauth_cred_t my_cred, new_cred; @@ -1186,7 +1302,7 @@ persona_set_groups(struct persona *persona, gid_t *groups, unsigned ngroups, uid my_cred = persona->pna_cred; kauth_cred_ref(my_cred); - new_cred = kauth_cred_setgroups(my_cred, groups, (int)ngroups, gmuid); + new_cred = kauth_cred_setgroups(my_cred, groups, ngroups, gmuid); if (new_cred != my_cred) { persona->pna_cred = new_cred; } @@ -1198,7 +1314,7 @@ out_unlock: } int -persona_get_groups(struct persona *persona, unsigned *ngroups, gid_t *groups, unsigned groups_sz) +persona_get_groups(struct persona *persona, size_t *ngroups, gid_t *groups, size_t groups_sz) { int ret = EINVAL; if (!persona || !persona->pna_cred || !groups || !ngroups || groups_sz > NGROUPS) { @@ -1209,9 +1325,9 @@ persona_get_groups(struct persona *persona, unsigned *ngroups, gid_t *groups, un persona_lock(persona); if (persona_valid(persona)) { - int kauth_ngroups = (int)groups_sz; + size_t kauth_ngroups = groups_sz; kauth_cred_getgroups(persona->pna_cred, groups, &kauth_ngroups); - *ngroups = (unsigned)kauth_ngroups; + *ngroups = (uint32_t)kauth_ngroups; ret = 0; } persona_unlock(persona); @@ -1259,8 +1375,6 @@ persona_get_login(struct persona *persona, char login[MAXLOGNAME + 1]) out_unlock: persona_unlock(persona); - login[MAXLOGNAME] = 0; - return ret; } @@ -1270,6 +1384,10 @@ out_unlock: * symbol exports for kext compatibility */ +struct persona *system_persona = NULL; +struct persona *proxy_system_persona = NULL; +int unique_persona = 0; + uid_t persona_get_id(__unused struct persona *persona) { @@ -1303,6 +1421,20 @@ persona_find(__unused const char *login, return ENOTSUP; } +int +persona_find_by_type(__unused int persona_type, + __unused struct persona **persona, + __unused size_t *plen) +{ + return ENOTSUP; +} + +struct persona * +persona_proc_get(__unused pid_t pid) +{ + return NULL; +} + struct persona * current_persona_get(void) {