Implement task_ident functions

These are required for `secd` sessions (i.e. for Security).
This commit is contained in:
Ariel Abreu 2023-07-31 19:30:14 -04:00
parent 368a44a217
commit 7dba61bfb8
No known key found for this signature in database
GPG Key ID: C06B805216EDEEED
14 changed files with 233 additions and 31 deletions

View File

@ -297,6 +297,7 @@ add_library(darlingserver_duct_tape
xnu/osfmk/kern/syscall_emulation.c
xnu/osfmk/kern/ux_handler.c
xnu/osfmk/kern/exception.c
xnu/osfmk/kern/task_ident.c
xnu/osfmk/ipc/ipc_entry.c
xnu/osfmk/ipc/ipc_hash.c

View File

@ -38,9 +38,11 @@ typedef void (*dtape_hook_thread_setup_f)(void* thread_context, dtape_thread_con
typedef void (*dtape_hook_thread_set_pending_signal_f)(void* thread_context, int pending_signal);
typedef void (*dtape_hook_thread_set_pending_call_override_f)(void* thread_context, bool pending_call_override);
typedef dtape_thread_t* (*dtape_hook_thread_lookup_f)(int id, bool id_is_nsid, bool retain);
typedef dtape_thread_t* (*dtape_hook_thread_lookup_eternal_f)(dtape_eternal_id_t eid, bool retain);
typedef dtape_thread_state_t (*dtape_hook_thread_get_state_f)(void* thread_context);
typedef int (*dtape_hook_thread_send_signal_f)(void* thread_context, int signal);
typedef void (*dtape_hook_thread_context_dispose_f)(void* thread_context);
typedef dtape_eternal_id_t (*dtape_hook_thread_eternal_id_f)(void* thread_context);
typedef void (*dtape_hook_current_thread_interrupt_disable_f)(void);
typedef void (*dtape_hook_current_thread_interrupt_enable_f)(void);
@ -50,6 +52,7 @@ typedef void (*dtape_hook_current_thread_set_bsd_retval_f)(uint32_t retval);
typedef bool (*dtape_hook_task_read_memory_f)(void* task_context, uintptr_t remote_address, void* local_buffer, size_t length);
typedef bool (*dtape_hook_task_write_memory_f)(void* task_context, uintptr_t remote_address, const void* local_buffer, size_t length);
typedef dtape_task_t* (*dtape_hook_task_lookup_f)(int id, bool id_is_nsid, bool retain);
typedef dtape_task_t* (*dtape_hook_task_lookup_eternal_f)(dtape_eternal_id_t eid, bool retain);
typedef void (*dtape_hook_task_get_memory_info_f)(void* task_context, dtape_memory_info_t* memory_info);
typedef bool (*dtape_hook_task_get_memory_region_info_f)(void* task_context, uintptr_t address, dtape_memory_region_info_t* memory_region_info);
typedef uintptr_t (*dtape_hook_task_allocate_pages_f)(void* task_context, size_t page_count, int protection, uintptr_t address_hint, dtape_memory_flags_t flags);
@ -59,6 +62,7 @@ typedef uintptr_t (*dtape_hook_task_get_next_region_f)(void* task_context, uintp
typedef bool (*dtape_hook_task_change_protection_f)(void* task_context, uintptr_t address, size_t page_count, int protection);
typedef bool (*dtape_hook_task_sync_memory_f)(void* task_context, uintptr_t address, size_t size, int sync_flags);
typedef void (*dtape_hook_task_context_dispose_f)(void* task_context);
typedef dtape_eternal_id_t (*dtape_hook_task_eternal_id_f)(void* thread_context);
#if DSERVER_EXTENDED_DEBUG
typedef void (*dtape_hook_task_register_name_f)(void* task_context, uint32_t name, uintptr_t pointer);
@ -85,9 +89,11 @@ typedef struct dtape_hooks {
dtape_hook_thread_set_pending_signal_f thread_set_pending_signal;
dtape_hook_thread_set_pending_call_override_f thread_set_pending_call_override;
dtape_hook_thread_lookup_f thread_lookup;
dtape_hook_thread_lookup_eternal_f thread_lookup_eternal;
dtape_hook_thread_get_state_f thread_get_state;
dtape_hook_thread_send_signal_f thread_send_signal;
dtape_hook_thread_context_dispose_f thread_context_dispose;
dtape_hook_thread_eternal_id_f thread_eternal_id;
dtape_hook_current_thread_interrupt_disable_f current_thread_interrupt_disable;
dtape_hook_current_thread_interrupt_enable_f current_thread_interrupt_enable;
@ -97,6 +103,7 @@ typedef struct dtape_hooks {
dtape_hook_task_read_memory_f task_read_memory;
dtape_hook_task_write_memory_f task_write_memory;
dtape_hook_task_lookup_f task_lookup;
dtape_hook_task_lookup_eternal_f task_lookup_eternal;
dtape_hook_task_get_memory_info_f task_get_memory_info;
dtape_hook_task_get_memory_region_info_f task_get_memory_region_info;
dtape_hook_task_allocate_pages_f task_allocate_pages;
@ -106,6 +113,7 @@ typedef struct dtape_hooks {
dtape_hook_task_change_protection_f task_change_protection;
dtape_hook_task_sync_memory_f task_sync_memory;
dtape_hook_task_context_dispose_f task_context_dispose;
dtape_hook_task_eternal_id_f task_eternal_id;
#if DSERVER_EXTENDED_DEBUG
dtape_hook_task_register_name_f task_register_name;

View File

@ -12,6 +12,7 @@ typedef struct dtape_thread dtape_thread_t;
typedef struct dtape_task dtape_task_t;
typedef struct dtape_kqchan_mach_port dtape_kqchan_mach_port_t;
typedef struct dtape_semaphore dtape_semaphore_t;
typedef uint64_t dtape_eternal_id_t;
typedef enum dtape_log_level {
dtape_log_level_debug,

View File

@ -5,9 +5,14 @@
#include <darlingserver/rpc.h>
#include <darlingserver/duct-tape/condvar.h>
#include <darlingserver/duct-tape/types.h>
typedef struct dtape_task dtape_task_t;
struct proc_ident {
dtape_eternal_id_t eid;
};
struct dtape_task {
void* context;
uint32_t saved_pid;
@ -18,6 +23,7 @@ struct dtape_task {
uint64_t dyld_info_length;
dtape_mutex_t dyld_info_lock;
dtape_condvar_t dyld_info_condvar;
struct proc_ident p_ident;
struct task xnu_task;
};

View File

@ -14,12 +14,19 @@
#include <kern/sync_sema.h>
#include <kern/ux_handler.h>
struct task_id_token {
struct proc_ident ident;
ipc_port_t port;
os_refcnt_t tidt_refs;
};
const dtape_hooks_t* dtape_hooks;
extern zone_t ipc_importance_inherit_zone;
extern lck_spin_t ipc_importance_lock_data;
extern zone_t ipc_importance_task_zone;
extern zone_t semaphore_zone;
extern zone_t task_id_token_zone;
void ipc_table_init(void);
void ipc_init(void);
@ -50,6 +57,8 @@ void dtape_init(const dtape_hooks_t* hooks) {
ipc_importance_task_zone = zone_create("ipc task importance", sizeof(struct ipc_importance_task), ZC_NOENCRYPT);
ipc_importance_inherit_zone = zone_create("ipc importance inherit", sizeof(struct ipc_importance_inherit), ZC_NOENCRYPT);
task_id_token_zone = zone_create("task_id_token", sizeof(struct task_id_token), ZC_ZFREE_CLEARMEM);
lck_mtx_init(&realhost.lock, LCK_GRP_NULL, LCK_ATTR_NULL);
lck_spin_init(&ipc_importance_lock_data, LCK_GRP_NULL, LCK_ATTR_NULL);

View File

@ -71,6 +71,7 @@ dtape_task_t* dtape_task_create(dtape_task_t* parent_task, uint32_t nsid, void*
task->has_sigexc = false;
task->dyld_info_addr = 0;
task->dyld_info_length = 0;
task->p_ident.eid = dtape_hooks->task_eternal_id(context);
dtape_mutex_init(&task->dyld_info_lock);
dtape_condvar_init(&task->dyld_info_condvar);
memset(&task->xnu_task, 0, sizeof(task->xnu_task));
@ -249,10 +250,6 @@ int task_pid(task_t task) {
return pid_from_task(task);
};
void task_id_token_notify(mach_msg_header_t* msg) {
dtape_stub();
};
void task_policy_update_complete_unlocked(task_t task, task_pend_token_t pend_token) {
dtape_stub();
};
@ -282,26 +279,6 @@ kern_return_t task_create_suid_cred(task_t task, suid_cred_path_t path, suid_cre
dtape_stub_unsafe();
};
kern_return_t task_create_identity_token(task_t task, task_id_token_t* tokenp) {
dtape_stub_unsafe();
};
ipc_port_t convert_task_id_token_to_port(task_id_token_t token) {
dtape_stub_unsafe();
};
task_id_token_t convert_port_to_task_id_token(ipc_port_t port) {
dtape_stub_unsafe();
};
kern_return_t task_identity_token_get_task_port(task_id_token_t token, task_flavor_t flavor, ipc_port_t* portp) {
dtape_stub_unsafe();
};
void task_id_token_release(task_id_token_t token) {
dtape_stub_unsafe();
};
kern_return_t task_dyld_process_info_notify_deregister(task_t task, mach_port_name_t rcv_name) {
dtape_stub_unsafe();
};
@ -750,6 +727,31 @@ void task_wait_locked(task_t task, boolean_t until_not_runnable) {
dtape_stub_safe();
};
//
// for task_ident.c
//
void* proc_find_ident(struct proc_ident const *i) {
return dtape_hooks->task_lookup_eternal(i->eid, true);
};
int proc_rele(void* p) {
dtape_task_release(p);
return 0;
};
task_t proc_task(void* p) {
return &((dtape_task_t*)p)->xnu_task;
};
struct proc_ident proc_ident(void* p) {
return ((dtape_task_t*)p)->p_ident;
};
//
// end for task_ident.c
//
// <copied from="xnu://7195.141.2/osfmk/kern/task_policy.c">
/*

View File

@ -425,6 +425,7 @@ struct proc {
pid_t p_sessionid;
};
#ifndef __DARLING__
/*
* Identify a process uniquely.
* proc_ident's fields match 1-1 with those in struct proc.
@ -434,6 +435,7 @@ struct proc_ident {
pid_t p_pid;
int p_idversion;
};
#endif // __DARLING__
#define PGRPID_DEAD 0xdeaddead

View File

@ -35,11 +35,21 @@
#include <security/mac_mach_internal.h>
#include <kern/task_ident.h>
#ifdef __DARLING__
#include <darlingserver/duct-tape/task.h>
kern_return_t
task_get_special_port(
task_t task,
int which,
ipc_port_t *portp);
#else
struct proc_ident {
uint64_t p_uniqueid;
pid_t p_pid;
int p_idversion;
};
#endif // __DARLING__
extern void* proc_find_ident(struct proc_ident const *i);
extern int proc_rele(void* p);
@ -53,8 +63,12 @@ struct task_id_token {
os_refcnt_t tidt_refs;
};
#ifdef __DARLING__
zone_t task_id_token_zone;
#else
static ZONE_DECLARE(task_id_token_zone, "task_id_token",
sizeof(struct task_id_token), ZC_ZFREE_CLEARMEM);
#endif // __DARLING__
static void
tidt_reference(task_id_token_t token)
@ -124,9 +138,16 @@ task_create_identity_token(
token = zalloc_flags(task_id_token_zone, Z_ZERO | Z_WAITOK | Z_NOFAIL);
task_lock(task);
#ifdef __DARLING__
dtape_task_t* dtape_task = dtape_task_for_xnu_task(task);
if (dtape_task) {
token->port = IP_NULL;
token->ident = proc_ident(dtape_task);
#else
if (task->bsd_info) {
token->port = IP_NULL;
token->ident = proc_ident(task->bsd_info);
#endif // __DARLING__
/* this reference will be donated to no-senders notification */
os_ref_init_count(&token->tidt_refs, NULL, 1);
} else {

View File

@ -33,6 +33,7 @@
#include <darlingserver/kqchan.hpp>
#include <darlingserver/rpc.h>
#include <darlingserver/logging.hpp>
#include <darlingserver/registry.hpp>
struct DTapeHooks;
@ -46,6 +47,7 @@ namespace DarlingServer {
friend class Server;
friend class Call; // HACK; see Call.cpp
friend class Kqchan;
friend class Registry<Process>;
public:
enum class Architecture {
@ -84,6 +86,7 @@ namespace DarlingServer {
private:
pid_t _pid;
pid_t _nspid;
EternalID _eid;
std::shared_ptr<FD> _pidfd;
mutable std::shared_mutex _rwlock;
std::unordered_map<uint64_t, std::weak_ptr<Thread>> _threads;
@ -127,6 +130,8 @@ namespace DarlingServer {
void _dispose();
void _setEternalID(EternalID eid);
public:
using ID = pid_t;
using NSID = ID;
@ -150,6 +155,8 @@ namespace DarlingServer {
*/
NSID nsid() const;
EternalID eternalID() const;
std::vector<std::shared_ptr<Thread>> threads() const;
std::string vchrootPath() const;

View File

@ -20,6 +20,7 @@
#ifndef _DARLINGSERVER_REGISTRY_HPP_
#define _DARLINGSERVER_REGISTRY_HPP_
#include <limits>
#include <vector>
#include <memory>
#include <mutex>
@ -31,15 +32,20 @@
#include <sys/types.h>
#include <darlingserver/message.hpp>
#include <darlingserver/process.hpp>
#include <darlingserver/thread.hpp>
namespace DarlingServer {
// for our purposes, a simple uint64_t is good enough
using EternalID = uint64_t;
static constexpr EternalID EternalIDInvalid = std::numeric_limits<EternalID>::max();
template<class Entry>
class Registry {
private:
std::unordered_map<typename Entry::ID, std::shared_ptr<Entry>> _map;
std::unordered_map<typename Entry::NSID, std::shared_ptr<Entry>> _nsmap;
std::unordered_map<EternalID, std::shared_ptr<Entry>> _emap;
// this is only accessed with the lock held, so no need for atomics
uint64_t _eternalCounter;
mutable std::shared_mutex _rwlock;
// sometimes, the factory used with registerIfAbsent needs to be able to look up other entries
@ -66,8 +72,11 @@ namespace DarlingServer {
return nullptr;
}
entry->_setEternalID(_eternalCounter++);
_map[entry->id()] = entry;
_nsmap[entry->nsid()] = entry;
_emap[entry->eternalID()] = entry;
return entry;
};
@ -79,8 +88,11 @@ namespace DarlingServer {
return false;
}
entry->_setEternalID(_eternalCounter++);
_map[entry->id()] = entry;
_nsmap[entry->nsid()] = entry;
_emap[entry->eternalID()] = entry;
return true;
};
@ -100,8 +112,15 @@ namespace DarlingServer {
return false;
}
auto it3 = _emap.find(entry->eternalID());
if (it3 == _emap.end()) {
return false;
}
_map.erase(it);
_nsmap.erase(it2);
_emap.erase(it3);
return true;
};
@ -121,8 +140,43 @@ namespace DarlingServer {
return false;
}
auto it3 = _emap.find(entry->eternalID());
if (it3 == _emap.end()) {
return false;
}
_map.erase(it);
_nsmap.erase(it2);
_emap.erase(it3);
return true;
};
bool unregistryEntryByEternalID(EternalID eid) {
std::unique_lock lock(_rwlock);
auto it3 = _emap.find(eid);
if (it3 == _emap.end()) {
return false;
}
std::shared_ptr<Entry> entry = (*it3).second;
auto it2 = _nsmap.find(entry->nsid());
if (it2 == _nsmap.end()) {
return false;
}
auto it = _map.find(entry->id());
if (it == _map.end()) {
return false;
}
_map.erase(it);
_nsmap.erase(it2);
_emap.erase(it3);
return true;
};
@ -138,18 +192,20 @@ namespace DarlingServer {
auto it = _map.find(entry->id());
auto it2 = _nsmap.find(entry->nsid());
auto it3 = _emap.find(entry->eternalID());
if (it == _map.end() || it2 == _nsmap.end()) {
if (it == _map.end() || it2 == _nsmap.end() || it3 == _emap.end()) {
return false;
}
// note that we *want* pointer-to-pointer comparison
if ((*it).second != entry || (*it2).second != entry) {
if ((*it).second != entry || (*it2).second != entry || (*it3).second != entry) {
return false;
}
_map.erase(it);
_nsmap.erase(it2);
_emap.erase(it3);
return true;
};
@ -183,6 +239,21 @@ namespace DarlingServer {
return (*it2).second;
};
std::optional<std::shared_ptr<Entry>> lookupEntryByEternalID(EternalID eid) {
std::shared_lock lock(_rwlock, std::defer_lock);
if (!_registeringWithLockHeld) {
lock.lock();
}
auto it3 = _emap.find(eid);
if (it3 == _emap.end()) {
return std::nullopt;
}
return (*it3).second;
};
/**
* Locks the registry, preventing new entries from being added and old ones from being removed.
*
@ -216,12 +287,17 @@ namespace DarlingServer {
return _map.size();
};
};
Registry<Process>& processRegistry();
Registry<Thread>& threadRegistry();
};
template<class T>
thread_local bool DarlingServer::Registry<T>::_registeringWithLockHeld = false;
#include <darlingserver/process.hpp>
#include <darlingserver/thread.hpp>
namespace DarlingServer {
Registry<Process>& processRegistry();
Registry<Thread>& threadRegistry();
};
#endif // _DARLINGSERVER_REGISTRY_HPP_

View File

@ -32,6 +32,7 @@
#include <darlingserver/duct-tape.h>
#include <darlingserver/logging.hpp>
#include <darlingserver/stack-pool.hpp>
#include <darlingserver/registry.hpp>
#include <ucontext.h>
@ -44,6 +45,7 @@ namespace DarlingServer {
class Thread: public std::enable_shared_from_this<Thread>, public Loggable {
friend class Process;
friend class Call; // HACK, see call.cpp
friend class Registry<Thread>;
public:
enum class RunState {
@ -72,6 +74,7 @@ namespace DarlingServer {
pid_t _tid;
pid_t _nstid;
EternalID _eid;
std::shared_ptr<Process> _process;
std::shared_ptr<Call> _pendingCall;
Address _address;
@ -142,6 +145,8 @@ namespace DarlingServer {
static StackPool stackPool;
void _setEternalID(EternalID eid);
public:
using ID = pid_t;
using NSID = ID;
@ -180,6 +185,8 @@ namespace DarlingServer {
*/
NSID nsid() const;
EternalID eternalID() const;
Address address() const;
void setAddress(Address address);

View File

@ -116,6 +116,14 @@ DarlingServer::Process::NSID DarlingServer::Process::nsid() const {
return _nspid;
};
DarlingServer::EternalID DarlingServer::Process::eternalID() const {
return _eid;
};
void DarlingServer::Process::_setEternalID(EternalID eid) {
_eid = eid;
};
std::vector<std::shared_ptr<DarlingServer::Thread>> DarlingServer::Process::threads() const {
std::vector<std::shared_ptr<DarlingServer::Thread>> result;
std::shared_lock lock(_rwlock);

View File

@ -17,6 +17,7 @@
* along with Darling. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstdint>
#include <darlingserver/server.hpp>
#include <sys/socket.h>
#include <stdexcept>
@ -182,6 +183,19 @@ struct DTapeHooks {
return thread->_dtapeThread;
};
static dtape_thread_t* dtape_hook_thread_lookup_eternal(dtape_eternal_id_t eid, bool retain) {
auto& registry = DarlingServer::threadRegistry();
auto maybeThread = registry.lookupEntryByEternalID(eid);
if (!maybeThread) {
return nullptr;
}
auto thread = *maybeThread;
if (retain) {
dtape_thread_retain(thread->_dtapeThread);
}
return thread->_dtapeThread;
};
static dtape_thread_state_t dtape_hook_thread_get_state(void* thread_context) {
return static_cast<dtape_thread_state_t>(static_cast<DarlingServer::Thread*>(thread_context)->getRunState());
};
@ -199,6 +213,13 @@ struct DTapeHooks {
static_cast<DarlingServer::Thread*>(thread_context)->_dispose();
};
static dtape_eternal_id_t dtape_hook_thread_eternal_id(void* thread_context) {
if (!thread_context) {
return DarlingServer::EternalIDInvalid;
}
return static_cast<DarlingServer::Thread*>(thread_context)->eternalID();
};
static void dtape_hook_current_thread_interrupt_disable(void) {
DarlingServer::Thread::interruptDisable();
};
@ -236,6 +257,19 @@ struct DTapeHooks {
return process->_dtapeTask;
};
static dtape_task_t* dtape_hook_task_lookup_eternal(dtape_eternal_id_t eid, bool retain) {
auto& registry = DarlingServer::processRegistry();
auto maybeProcess = registry.lookupEntryByEternalID(eid);
if (!maybeProcess) {
return nullptr;
}
auto process = *maybeProcess;
if (retain) {
dtape_task_retain(process->_dtapeTask);
}
return process->_dtapeTask;
};
static void dtape_hook_task_get_memory_info(void* task_context, dtape_memory_info_t* memory_info) {
auto info = static_cast<DarlingServer::Process*>(task_context)->memoryInfo();
memory_info->virtual_size = info.virtualSize;
@ -322,6 +356,13 @@ struct DTapeHooks {
static_cast<DarlingServer::Process*>(task_context)->_dispose();
};
static dtape_eternal_id_t dtape_hook_task_eternal_id(void* task_context) {
if (!task_context) {
return DarlingServer::EternalIDInvalid;
}
return static_cast<DarlingServer::Process*>(task_context)->eternalID();
};
#if DSERVER_EXTENDED_DEBUG
static void dtape_hook_task_register_name(void* task_context, uint32_t name, uintptr_t pointer) {
static_cast<DarlingServer::Process*>(task_context)->_registerName(name, pointer);
@ -361,9 +402,11 @@ struct DTapeHooks {
.thread_set_pending_signal = dtape_hook_thread_set_pending_signal,
.thread_set_pending_call_override = dtape_hook_thread_set_pending_call_override,
.thread_lookup = dtape_hook_thread_lookup,
.thread_lookup_eternal = dtape_hook_thread_lookup_eternal,
.thread_get_state = dtape_hook_thread_get_state,
.thread_send_signal = dtape_hook_thread_send_signal,
.thread_context_dispose = dtape_hook_thread_context_dispose,
.thread_eternal_id = dtape_hook_thread_eternal_id,
.current_thread_interrupt_disable = dtape_hook_current_thread_interrupt_disable,
.current_thread_interrupt_enable = dtape_hook_current_thread_interrupt_enable,
@ -373,6 +416,7 @@ struct DTapeHooks {
.task_read_memory = dtape_hook_task_read_memory,
.task_write_memory = dtape_hook_task_write_memory,
.task_lookup = dtape_hook_task_lookup,
.task_lookup_eternal = dtape_hook_task_lookup_eternal,
.task_get_memory_info = dtape_hook_task_get_memory_info,
.task_get_memory_region_info = dtape_hook_task_get_memory_region_info,
.task_allocate_pages = dtape_hook_task_allocate_pages,
@ -382,6 +426,7 @@ struct DTapeHooks {
.task_change_protection = dtape_hook_task_change_protection,
.task_sync_memory = dtape_hook_task_sync_memory,
.task_context_dispose = dtape_hook_task_context_dispose,
.task_eternal_id = dtape_hook_task_eternal_id,
#if DSERVER_EXTENDED_DEBUG
.task_register_name = dtape_hook_task_register_name,

View File

@ -17,6 +17,7 @@
* along with Darling. If not, see <http://www.gnu.org/licenses/>.
*/
#include "darlingserver/registry.hpp"
#include <darlingserver/thread.hpp>
#include <darlingserver/process.hpp>
#include <darlingserver/call.hpp>
@ -246,6 +247,14 @@ DarlingServer::Thread::NSID DarlingServer::Thread::nsid() const {
return _nstid;
};
DarlingServer::EternalID DarlingServer::Thread::eternalID() const {
return _eid;
};
void DarlingServer::Thread::_setEternalID(EternalID eid) {
_eid = eid;
};
std::shared_ptr<DarlingServer::Process> DarlingServer::Thread::process() const {
return _process;
};