aboutsummaryrefslogtreecommitdiff
path: root/ios-bootstrap
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--darwin-bootstrap/bundle-loader.m (renamed from ios-bootstrap/bundle-loader.m)0
-rw-r--r--darwin-bootstrap/ib-log.h (renamed from ios-bootstrap/ib-log.h)0
-rw-r--r--darwin-bootstrap/inject-into-launchd.c (renamed from ios-bootstrap/inject-into-launchd.c)3
-rw-r--r--darwin-bootstrap/posixspawn-hook.c (renamed from ios-bootstrap/posixspawn-hook.c)145
-rw-r--r--darwin-bootstrap/safety-dance/AutoGrid.h (renamed from ios-bootstrap/safety-dance/AutoGrid.h)0
-rw-r--r--darwin-bootstrap/safety-dance/AutoGrid.m (renamed from ios-bootstrap/safety-dance/AutoGrid.m)0
-rw-r--r--darwin-bootstrap/safety-dance/Info.plist (renamed from ios-bootstrap/safety-dance/Info.plist)0
-rw-r--r--darwin-bootstrap/safety-dance/main.m (renamed from ios-bootstrap/safety-dance/main.m)0
-rw-r--r--darwin-bootstrap/safety-dance/white.png (renamed from ios-bootstrap/safety-dance/white.png)bin1047 -> 1047 bytes
-rw-r--r--ios-bootstrap/unrestrict.c115
10 files changed, 99 insertions, 164 deletions
diff --git a/ios-bootstrap/bundle-loader.m b/darwin-bootstrap/bundle-loader.m
index fec2513..fec2513 100644
--- a/ios-bootstrap/bundle-loader.m
+++ b/darwin-bootstrap/bundle-loader.m
diff --git a/ios-bootstrap/ib-log.h b/darwin-bootstrap/ib-log.h
index fee70b5..fee70b5 100644
--- a/ios-bootstrap/ib-log.h
+++ b/darwin-bootstrap/ib-log.h
diff --git a/ios-bootstrap/inject-into-launchd.c b/darwin-bootstrap/inject-into-launchd.c
index 4396f14..539b1a2 100644
--- a/ios-bootstrap/inject-into-launchd.c
+++ b/darwin-bootstrap/inject-into-launchd.c
@@ -1,3 +1,6 @@
+/* This is an iOS executable, placed in /etc/rc.d, that injects
+ * posixspawn-hook.dylib into launchd (pid 1). */
+
#define IB_LOG_NAME "iil"
#include "ib-log.h"
#include "substitute.h"
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");
diff --git a/ios-bootstrap/safety-dance/AutoGrid.h b/darwin-bootstrap/safety-dance/AutoGrid.h
index 335381a..335381a 100644
--- a/ios-bootstrap/safety-dance/AutoGrid.h
+++ b/darwin-bootstrap/safety-dance/AutoGrid.h
diff --git a/ios-bootstrap/safety-dance/AutoGrid.m b/darwin-bootstrap/safety-dance/AutoGrid.m
index 2a8d3ed..2a8d3ed 100644
--- a/ios-bootstrap/safety-dance/AutoGrid.m
+++ b/darwin-bootstrap/safety-dance/AutoGrid.m
diff --git a/ios-bootstrap/safety-dance/Info.plist b/darwin-bootstrap/safety-dance/Info.plist
index f2fa520..f2fa520 100644
--- a/ios-bootstrap/safety-dance/Info.plist
+++ b/darwin-bootstrap/safety-dance/Info.plist
diff --git a/ios-bootstrap/safety-dance/main.m b/darwin-bootstrap/safety-dance/main.m
index 0d05e00..0d05e00 100644
--- a/ios-bootstrap/safety-dance/main.m
+++ b/darwin-bootstrap/safety-dance/main.m
diff --git a/ios-bootstrap/safety-dance/white.png b/darwin-bootstrap/safety-dance/white.png
index eab5a56..eab5a56 100644
--- a/ios-bootstrap/safety-dance/white.png
+++ b/darwin-bootstrap/safety-dance/white.png
Binary files differ
diff --git a/ios-bootstrap/unrestrict.c b/ios-bootstrap/unrestrict.c
deleted file mode 100644
index 3c6ff56..0000000
--- a/ios-bootstrap/unrestrict.c
+++ /dev/null
@@ -1,115 +0,0 @@
-#define IB_LOG_NAME "unrestrict"
-#include "ib-log.h"
-#include "substitute.h"
-#include "substitute-internal.h"
-#include <stdlib.h>
-#include <syslog.h>
-#include <signal.h>
-#include <errno.h>
-#include <stdio.h>
-
-#define PROC_PIDFDVNODEINFO 1
-#define PROC_PIDFDVNODEINFO_SIZE 176
-int proc_pidfdinfo(int, int, int, void *, int);
-
-int main(int argc, char **argv) {
- if (argc != 4) {
- ib_log("unrestrict: wrong number of args");
- return 1;
- }
-
- const char *pids = argv[1];
- char *end;
- long pid = strtol(pids, &end, 10);
- if (!pids[0] || *end) {
- ib_log("unrestrict: pid not an integer");
- return 1;
- }
-
- const char *should_resume = argv[2];
- if (strcmp(should_resume, "0") && strcmp(should_resume, "1")) {
- ib_log("unrestrict: should_resume not 0 or 1");
- return 1;
- }
-
- const char *is_exec = argv[3];
- if (strcmp(is_exec, "0") && strcmp(is_exec, "1")) {
- ib_log("unrestrict: is_exec not 0 or 1");
- return 1;
- }
-
- /* double fork to avoid zombies */
- int ret = fork();
- if (ret == -1) {
- ib_log("unrestrict: fork: %s", strerror(errno));
- return 1;
- } else if (ret) {
- return 0;
- }
-
- if (IB_VERBOSE) {
- ib_log("unrestrict: unrestricting %ld (sr=%s, ie=%s)", pid,
- should_resume, is_exec);
- }
-
- int ec = 1;
-
- mach_port_t task;
- kern_return_t kr = task_for_pid(mach_task_self(), (pid_t) pid, &task);
- if (kr) {
- ib_log("unrestrict: TFP fail: %d", kr);
- goto fail;
- }
-
- if (is_exec[0] == '1') {
- int retries = 0;
- int wait_us = 1;
- while (1) {
- /* The process might not have transitioned yet. We set up a dummy fd
- * 255 in the parent process which was marked CLOEXEC, so test if that
- * still exists. AFAICT, Substrate's equivalent to this is not
- * actually correct.
- * TODO cleanup
- */
- char buf[PROC_PIDFDVNODEINFO_SIZE];
- /* A bug in proc_pidfdinfo makes it never return -1. Yuck. */
- errno = 0;
- proc_pidfdinfo(pid, 255, PROC_PIDFDVNODEINFO, buf, sizeof(buf));
- if (errno == EBADF) {
- break;
- } else if (errno) {
- ib_log("proc_pidfdinfo: %s", strerror(errno));
- goto fail;
- }
-
- if (retries++ == 20) {
- ib_log("still in parent process after 20 retries");
- goto fail;
- }
- wait_us *= 2;
- if (wait_us > 200000)
- wait_us = 200000;
- while (usleep(wait_us))
- ;
- }
- }
-
- char *err = NULL;
- int sret = substitute_ios_unrestrict(task, &err);
- if (sret) {
- ib_log("unrestrict: substitute_ios_unrestrict => %d (%s)",
- sret, err);
- ec = 1;
- }
-
- ec = 0;
-fail:
- if (should_resume[0] == '1') {
- if ((kill(pid, SIGCONT))) {
- ib_log("unrestrict: kill SIGCONT: %s", strerror(errno));
- return 1;
- }
- }
-
- return ec;
-}