aboutsummaryrefslogtreecommitdiff
path: root/ios-bootstrap/posixspawn-hook.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--darwin-bootstrap/posixspawn-hook.c (renamed from ios-bootstrap/posixspawn-hook.c)145
1 files changed, 96 insertions, 49 deletions
diff --git a/ios-bootstrap/posixspawn-hook.c b/darwin-bootstrap/posixspawn-hook.c
index 80339e1..5acb30b 100644
--- a/ios-bootstrap/posixspawn-hook.c
+++ b/darwin-bootstrap/posixspawn-hook.c
@@ -1,15 +1,29 @@
+/* This library is loaded into launchd, and from there into xpcproxy, which
+ * launchd (always?) uses as an intermediary to exec its processes; its main
+ * purpose is to ensure that bundle-loader.dylib is specified in
+ * DYLD_INSERT_LIBRARIES when launching such processes. In the interests of
+ * not making ssh really weird (and because it's what Substrate does), this is
+ * separate from bundle-loader itself, so any processes that do their own
+ * spawning won't get the environment override.
+ *
+ * It also handles the sandbox override for substituted. */
+
#define IB_LOG_NAME "posixspawn-hook"
#include "ib-log.h"
#include "substitute.h"
#include "substitute-internal.h"
#include <mach/mach.h>
#include <mach-o/dyld.h>
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
#include <spawn.h>
#include <sys/wait.h>
#include <syslog.h>
#include <malloc/malloc.h>
#include <assert.h>
#include <errno.h>
+#include <arpa/inet.h>
+#include <libkern/OSByteOrder.h>
extern char ***_NSGetEnviron(void);
@@ -47,6 +61,77 @@ static bool spawn_unrestrict(pid_t pid, bool should_resume, bool is_exec) {
return true;
}
+static bool looks_restricted(const char *filename) {
+ int fd = open(filename, O_RDONLY);
+ if (fd == -1) {
+ ib_log("open '%s': %s", filename, strerror(errno));
+ return false;
+ }
+ uint32_t offset = 0;
+ union {
+ uint32_t magic;
+ struct {
+ struct fat_header fh;
+ struct fat_arch fa1;
+ };
+ struct mach_header mh;
+ } u;
+ if (read(fd, &u, sizeof(u)) != sizeof(u)) {
+ ib_log("read header for '%s': %s", filename, strerror(errno));
+ return false;
+ }
+ if (ntohl(u.magic) == FAT_MAGIC) {
+ /* Fat binary - to avoid needing to replicate grade_binary in the
+ * kernel, we assume all architectures have the same restrict-ness. */
+ if (u.fh.nfat_arch == 0)
+ return false;
+ offset = ntohl(u.fa1.offset);
+ if (pread(fd, &u, sizeof(u), offset) != sizeof(u)) {
+ ib_log("read header (inside fat) for '%s': %s",
+ filename, strerror(errno));
+ return false;
+ }
+ }
+ bool swap, is64;
+ switch (u.magic) {
+ case MH_MAGIC:
+ swap = false;
+ is64 = false;
+ break;
+ case MH_MAGIC_64:
+ swap = false;
+ is64 = true;
+ break;
+ case MH_CIGAM:
+ swap = true;
+ is64 = false;
+ break;
+ case MH_CIGAM_64:
+ swap = true;
+ is64 = true;
+ break;
+ default:
+ ib_log("bad mach-o magic for '%s'", filename);
+ return false;
+ }
+ uint32_t sizeofcmds = u.mh.sizeofcmds;
+ if (swap)
+ sizeofcmds = OSSwapInt32(sizeofcmds);
+ offset += is64 ? sizeof(struct mach_header_64) : sizeof(struct mach_header);
+ char *cmds_buf = malloc(sizeofcmds);
+ ssize_t actual = pread(fd, cmds_buf, sizeofcmds, offset);
+ if (actual < 0 || (uint32_t) actual != sizeofcmds) {
+ ib_log("read load cmds for '%s': %s", filename, strerror(errno));
+ free(cmds_buf);
+ return false;
+ }
+ /* overestimation is fine here */
+ const char sectname[] = "__restrict";
+ bool ret = !!memmem(cmds_buf, sizeofcmds, sectname, sizeof(sectname));
+ free(cmds_buf);
+ return ret;
+}
+
static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
pid_t *restrict pidp, const char *restrict path,
const posix_spawn_file_actions_t *file_actions,
@@ -90,25 +175,10 @@ static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
* maximum safety... */
bool safe_mode = false;
- /* If Foundation is loaded into notifyd, the system doesn't boot. I spent
- * some time trying to figure out why, but managed to brick my device
- * instead (no idea how that happened either). I want to solve this before
- * a stable release, but this works for now.
- * n.b. Substrate isn't affected by this because it uses only
- * CoreFoundation, not Foundation. However, CoreFoundation is pretty big
- * itself, and also brings in libobjc, so it's not necessarily that
- * principled to switch. I suppose principled might be to extract the
- * plist code from CF... */
- if (!strcmp(path, "/usr/sbin/notifyd")) {
- /* why? */
- safe_mode = true;
- }
-
-
const char *orig_dyld_insert = "";
- static const char my_dylib_1[] =
+ static const char bl_dylib[] =
"/Library/Substitute/bundle-loader.dylib";
- static const char my_dylib_2[] =
+ static const char psh_dylib[] =
"/Library/Substitute/posixspawn-hook.dylib";
size_t env_count = 0;
for (char *const *ep = my_envp; *ep; ep++) {
@@ -126,7 +196,7 @@ static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
}
}
new = malloc(sizeof("DYLD_INSERT_LIBRARIES=") - 1 +
- sizeof(my_dylib_2) /* not - 1, because : */ +
+ sizeof(psh_dylib) /* not - 1, because : */ +
strlen(orig_dyld_insert) + 1);
char *newp_orig = stpcpy(new, "DYLD_INSERT_LIBRARIES=");
char *newp = newp_orig;
@@ -135,10 +205,10 @@ static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
const char *next = strchr(p, ':') ?: (p + strlen(p));
/* append if it isn't one of ours */
bool is_substitute =
- (next - p == sizeof(my_dylib_1) - 1 &&
- !memcmp(next, my_dylib_1, sizeof(my_dylib_1) - 1)) ||
- (next - p == sizeof(my_dylib_2) - 1 &&
- !memcmp(next, my_dylib_2, sizeof(my_dylib_2) - 1));
+ (next - p == sizeof(bl_dylib) - 1 &&
+ !memcmp(next, bl_dylib, sizeof(bl_dylib) - 1)) ||
+ (next - p == sizeof(psh_dylib) - 1 &&
+ !memcmp(next, psh_dylib, sizeof(psh_dylib) - 1));
if (!is_substitute) {
if (newp != newp_orig)
*newp++ = ':';
@@ -154,8 +224,8 @@ static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
if (newp != newp_orig)
*newp++ = ':';
const char *dylib_to_add = !strcmp(path, "/usr/libexec/xpcproxy")
- ? my_dylib_2
- : my_dylib_1;
+ ? psh_dylib
+ : bl_dylib;
newp = stpcpy(newp, dylib_to_add);
}
if (IB_VERBOSE)
@@ -182,17 +252,10 @@ static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
goto skip;
- /* XXX Even async on a separate thread, task_for_pid from launchd hangs in
- * kernel - AMFI permitUnrestrictedDebugging waiting on some mach port.
- * Normally (from other processes) task_for_pid doesn't even ask amfid
- * because we have the right entitlements, but it doesn't usually *hang*.
- * Probably should do what Substrate does and only launch a process for the
- * few actually restricted executables. I was originally hesitant about
- * this because of complications with fat files. Whatever. */
- bool need_unrestrict = getuid() == 0;
-
/* Deal with the dumb __restrict section. A complication is that this
* could actually be an exec. */
+ bool need_unrestrict = looks_restricted(path);
+
/* TODO skip this if Substrate is doing it anyway */
bool was_suspended;
if (need_unrestrict) {
@@ -224,24 +287,8 @@ static int hook_posix_spawn_generic(__typeof__(posix_spawn) *old,
/* Since it returned, obviously it was not SETEXEC, so we need to
* unrestrict it ourself. */
pid_t pid = *pidp;
-#if 0
- dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
- 0);
- dispatch_async(q, ^{
- char *error;
- ib_log("unrestricting %d", pid);
- int sret = substitute_ios_unrestrict(pid, !was_suspended, &error);
- ib_log("unrestricting done");
- if (sret) {
- ib_log("posixspawn-hook: substitute_ios_unrestrict => %d (%s)",
- sret, error);
- }
- free(error);
- });
-#else
if (need_unrestrict)
spawn_unrestrict(pid, !was_suspended, false);
-#endif
goto cleanup;
crap:
ib_log("posixspawn-hook: weird error - OOM? skipping our stuff");