aboutsummaryrefslogtreecommitdiff
path: root/darwin-bootstrap/posixspawn-hook.c
diff options
context:
space:
mode:
authorcomex2015-07-12 16:42:48 -0400
committercomex2015-07-12 16:44:58 -0400
commit41aefc7572abb3c689774354231217aaafe0b01a (patch)
tree610f0f38f5880d4577ca19e9ddb1084dfb7177ec /darwin-bootstrap/posixspawn-hook.c
parenthow did that style violation get in there? must have been tired (diff)
downloadsubstitute-41aefc7572abb3c689774354231217aaafe0b01a.tar.gz
redo crash reporting - untested (but it compiles)
Diffstat (limited to 'darwin-bootstrap/posixspawn-hook.c')
-rw-r--r--darwin-bootstrap/posixspawn-hook.c137
1 files changed, 130 insertions, 7 deletions
diff --git a/darwin-bootstrap/posixspawn-hook.c b/darwin-bootstrap/posixspawn-hook.c
index 7c339f1..b2a7336 100644
--- a/darwin-bootstrap/posixspawn-hook.c
+++ b/darwin-bootstrap/posixspawn-hook.c
@@ -20,6 +20,8 @@
#include "ib-log.h"
#include "substitute.h"
#include "substitute-internal.h"
+#include "darwin/xxpc.h"
+#include "cbit/htab.h"
#include <mach/mach.h>
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
@@ -30,15 +32,35 @@
#include <malloc/malloc.h>
#include <errno.h>
#include <arpa/inet.h>
+#include <pthread.h>
#include <libkern/OSByteOrder.h>
+#define _pid_hash(pidp) (*(pidp))
+#define _pid_eq(pid1p, pid2p) (*(pid1p) == *(pid2p))
+#define _pid_null(pidp) (!*(pidp))
+DECL_STATIC_HTAB_KEY(pid_t, pid_t, _pid_hash, _pid_eq, _pid_null, 0);
+DECL_HTAB(pid_str, pid_t, char *);
+
extern char ***_NSGetEnviron(void);
-static __typeof__(posix_spawn) *old_posix_spawn, *old_posix_spawnp,
- hook_posix_spawn, hook_posix_spawnp;
+struct au_tid;
+extern void audit_token_to_au32(audit_token_t, uid_t *, uid_t *, gid_t *,
+ uid_t *, gid_t *, pid_t *, pid_t *,
+ struct au_tid *);
+
+static typeof(posix_spawn) *old_posix_spawn, *old_posix_spawnp,
+ hook_posix_spawn, hook_posix_spawnp;
+static typeof(wait4) *old_wait4, hook_wait4;
+static typeof(waitpid) *old_waitpid, hook_waitpid;
static int (*old_sandbox_check)(pid_t, const char *, int type, ...);
+static int (*old_xpc_pipe_try_receive)(mach_port_t, xxpc_object_t *,
+ mach_port_t *, void *, size_t, int);
-static bool is_launchd;
+static bool g_is_launchd;
+static xxpc_object_t g_argv0_to_fate;
+static HTAB_STORAGE(pid_str) g_pid_to_argv0 =
+ HTAB_STORAGE_INIT_STATIC(&g_pid_to_argv0, pid_str);
+static pthread_mutex_t g_dicts_lock = PTHREAD_MUTEX_INITIALIZER;
static bool advance(char **strp, const char *template) {
size_t len = strlen(template);
@@ -174,7 +196,7 @@ static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
path,
(flags & POSIX_SPAWN_SETEXEC) ? " (exec)" : "",
(flags & POSIX_SPAWN_START_SUSPENDED) ? " (suspend)" : "",
- is_launchd);
+ g_is_launchd);
for (char *const *ap = argv; *ap; ap++)
ib_log(" %s", *ap);
}
@@ -185,9 +207,11 @@ static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
static const char psh_dylib[] =
"/Library/Substitute/Helpers/posixspawn-hook.dylib";
+ const char *argv0 = argv[0] ?: "";
+
/* which dylib should we add, if any? */
const char *dylib_to_add;
- if (is_launchd) {
+ if (g_is_launchd) {
if (strcmp(path, "/usr/libexec/xpcproxy"))
goto skip;
dylib_to_add = psh_dylib;
@@ -218,7 +242,7 @@ static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
*/
if (!strcmp(path, "/Library/Substitute/Helpers/substituted") ||
!strcmp(path, "/usr/sbin/notifyd") ||
- !strcmp(xbasename(argv[0] ?: ""), "sshd"))
+ !strcmp(xbasename(argv0), "sshd"))
goto skip;
dylib_to_add = bl_dylib;
}
@@ -354,6 +378,11 @@ static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
pid_t pid = *pidp;
if (need_unrestrict)
spawn_unrestrict(pid, !was_suspended, false);
+
+ pthread_mutex_lock(&g_dicts_lock);
+ *htab_setp_pid_str(&g_pid_to_argv0.h, &pid, NULL) = strdup(argv0);
+ pthread_mutex_unlock(&g_dicts_lock);
+
goto cleanup;
crap:
ib_log("posixspawn-hook: weird error - OOM? skipping our stuff");
@@ -382,6 +411,95 @@ int hook_posix_spawnp(pid_t *restrict pid, const char *restrict path,
attrp, argv, envp);
}
+static void after_wait_generic(pid_t pid, int stat) {
+ if (pid == -1)
+ return;
+ pthread_mutex_lock(&g_dicts_lock);
+ struct htab_bucket_pid_str *bucket =
+ htab_getbucket_pid_str(&g_pid_to_argv0.h, &pid);
+ if (!bucket) {
+ /* probably spawned some other way / not a task */
+ if (IB_VERBOSE)
+ ib_log("reaped unknown pid %d", pid);
+ return;
+ }
+ char *argv0 = bucket->value;
+ xxpc_dictionary_set_int64(g_argv0_to_fate, argv0, stat);
+ free(argv0);
+ htab_removeat_pid_str(&g_pid_to_argv0.h, bucket);
+ pthread_mutex_unlock(&g_dicts_lock);
+}
+
+pid_t hook_wait4(pid_t pid, int *stat_loc, int options, struct rusage *rusage) {
+ pid_t ret = old_wait4(pid, stat_loc, options, rusage);
+ after_wait_generic(ret, *stat_loc);
+ return ret;
+}
+
+pid_t hook_waitpid(pid_t pid, int *stat_loc, int options) {
+ pid_t ret = old_waitpid(pid, stat_loc, options);
+ after_wait_generic(ret, *stat_loc);
+ return ret;
+}
+
+int hook_xpc_pipe_try_receive(mach_port_t port_set, xxpc_object_t *requestp,
+ mach_port_t *recvp, void *mig_demux,
+ size_t msg_size, int dunno_ignored) {
+ int res = old_xpc_pipe_try_receive(port_set, requestp, recvp, mig_demux,
+ msg_size, dunno_ignored);
+ if (res)
+ return res;
+ xxpc_object_t request = *requestp;
+ if (!request || /* just to be sure */
+ xxpc_get_type(request) != XXPC_TYPE_DICTIONARY)
+ return res;
+ /* is it for us? - usage of "in"/"out" is to satisfy the public vproc API */
+ xxpc_object_t in = xxpc_dictionary_get_value(request, "in");
+ if (!in || xxpc_get_type(in) != XXPC_TYPE_DICTIONARY)
+ return res;
+ const char *name = xxpc_dictionary_get_string(in,
+ "com.ex.substitute.hook-operation");
+ if (!name)
+ return res;
+ /* is it from someone untrustworthy? */
+ audit_token_t at;
+ xxpc_dictionary_get_audit_token(request, &at);
+ uid_t euid;
+ pid_t pid;
+ audit_token_to_au32(at, NULL, &euid, NULL, NULL, NULL, &pid, NULL, NULL);
+ if (euid != 0) {
+ ib_log("Attempt to perform hook-operation by pid %d with euid %d",
+ euid, pid);
+ return res;
+ }
+ xxpc_object_t reply = NULL;
+ if (!strcmp(name, "argv0-to-fate")) {
+ const char *argv0 = xxpc_dictionary_get_string(in, "argv0");
+ if (!argv0) {
+ ib_log("invalid hook-operation message");
+ return res;
+ }
+ reply = xxpc_dictionary_create_reply(request);
+ xxpc_object_t fate = xxpc_dictionary_get_value(g_argv0_to_fate, argv0);
+ if (fate)
+ xxpc_dictionary_set_value(reply, "out", fate);
+ } else {
+ ib_log("unknown hook-operation '%s'", name);
+ return res;
+ }
+ if (reply) {
+ int reply_res = xxpc_pipe_routine_reply(reply);
+ if (reply_res) {
+ ib_log("xxpc_pipe_routine_reply: %d", reply_res);
+ return res;
+ }
+ xxpc_release(reply);
+ }
+ xxpc_release(request);
+ *requestp = NULL;
+ return 0;
+}
+
int hook_sandbox_check(pid_t pid, const char *op, int type, ...) {
/* Can't easily determine the number of arguments, so just assume there's
* less than 5 pointers' worth. */
@@ -438,9 +556,10 @@ static void init() {
* (it also decreases the amount of library code necessary to load from
* disk...)
*/
+ g_argv0_to_fate = xxpc_dictionary_create(NULL, NULL, 0);
const char *image0 = _dyld_get_image_name(0);
- is_launchd = !!strstr(image0, "launchd");
+ g_is_launchd = !!strstr(image0, "launchd");
struct substitute_image *im = substitute_open_image(image0);
if (!im) {
ib_log("posixspawn-hook: substitute_open_image failed");
@@ -451,6 +570,10 @@ static void init() {
{"_posix_spawn", hook_posix_spawn, &old_posix_spawn},
{"_posix_spawnp", hook_posix_spawnp, &old_posix_spawnp},
{"_sandbox_check", hook_sandbox_check, &old_sandbox_check},
+ {"_waitpid", hook_waitpid, &old_waitpid},
+ {"_wait4", hook_wait4, &old_wait4},
+ {"_xpc_pipe_try_receive", hook_xpc_pipe_try_receive,
+ &old_xpc_pipe_try_receive},
};
int err = substitute_interpose_imports(im, hooks, sizeof(hooks)/sizeof(*hooks),