aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuis Hector Chavez <lhchavez@google.com>2017-07-20 15:12:22 -0700
committerLuis Hector Chavez <lhchavez@google.com>2017-07-28 07:12:32 -0700
commite0ba4cebe680a5076623c7f00e419c7def72931e (patch)
tree3aee2aa5b43da22ecb822f16d2d31b389340fd60
parentdb19bfbfbdce0f2b86f53d36392e49cb8f1645d6 (diff)
downloadplatform_external_minijail-e0ba4cebe680a5076623c7f00e419c7def72931e.tar.gz
platform_external_minijail-e0ba4cebe680a5076623c7f00e419c7def72931e.tar.bz2
platform_external_minijail-e0ba4cebe680a5076623c7f00e419c7def72931e.zip
minijail: Add minijail_add_hook()
This allows callers to add hooks to be invoked at various events during minijail setup. This is useful to e.g. setup SELinux contexts, networking in the new namespace, and install other LSM-related stuff. Bug: 63904978 Test: make tests Change-Id: I3e773715ec1842db8071f5e993ee4bdcbe2d0082
-rw-r--r--libminijail.c87
-rw-r--r--libminijail.h38
-rw-r--r--libminijail_unittest.cc35
3 files changed, 160 insertions, 0 deletions
diff --git a/libminijail.c b/libminijail.c
index 4d61b0d5..ac76faf8 100644
--- a/libminijail.c
+++ b/libminijail.c
@@ -96,6 +96,13 @@ struct mountpoint {
struct mountpoint *next;
};
+struct hook {
+ minijail_hook_t hook;
+ void *payload;
+ minijail_hook_event_t event;
+ struct hook *next;
+};
+
struct minijail {
/*
* WARNING: if you add a flag here you need to make sure it's
@@ -167,6 +174,8 @@ struct minijail {
struct minijail_rlimit rlimits[MAX_RLIMITS];
size_t rlimit_count;
uint64_t securebits_skip_mask;
+ struct hook *hooks_head;
+ struct hook *hooks_tail;
};
/*
@@ -753,6 +762,32 @@ int API minijail_bind(struct minijail *j, const char *src, const char *dest,
return minijail_mount(j, src, dest, "", flags);
}
+int API minijail_add_hook(struct minijail *j, minijail_hook_t hook,
+ void *payload, minijail_hook_event_t event)
+{
+ struct hook *c;
+
+ if (hook == NULL)
+ return -EINVAL;
+ if (event >= MINIJAIL_HOOK_EVENT_MAX)
+ return -EINVAL;
+ c = calloc(1, sizeof(*c));
+ if (!c)
+ return -ENOMEM;
+
+ c->hook = hook;
+ c->payload = payload;
+ c->event = event;
+
+ if (j->hooks_tail)
+ j->hooks_tail->next = c;
+ else
+ j->hooks_head = c;
+ j->hooks_tail = c;
+
+ return 0;
+}
+
static void clear_seccomp_options(struct minijail *j)
{
j->flags.seccomp_filter = 0;
@@ -978,6 +1013,8 @@ int minijail_unmarshal(struct minijail *j, char *serialized, size_t length)
j->mounts_head = NULL;
j->mounts_tail = NULL;
j->filter_prog = NULL;
+ j->hooks_head = NULL;
+ j->hooks_tail = NULL;
if (j->user) { /* stale pointer */
char *user = consumestr(&serialized, &length);
@@ -1632,6 +1669,42 @@ static void install_signal_handlers(void)
}
}
+static const char *lookup_hook_name(minijail_hook_event_t event)
+{
+ switch (event) {
+ case MINIJAIL_HOOK_EVENT_PRE_DROP_CAPS:
+ return "pre-drop-caps";
+ case MINIJAIL_HOOK_EVENT_PRE_EXECVE:
+ return "pre-execve";
+ case MINIJAIL_HOOK_EVENT_MAX:
+ /*
+ * Adding this in favor of a default case to force the
+ * compiler to error out if a new enum value is added.
+ */
+ break;
+ }
+ return "unknown";
+}
+
+static void run_hooks_or_die(const struct minijail *j,
+ minijail_hook_event_t event)
+{
+ int rc;
+ int hook_index = 0;
+ for (struct hook *c = j->hooks_head; c; c = c->next) {
+ if (c->event != event)
+ continue;
+ rc = c->hook(c->payload);
+ if (rc != 0) {
+ errno = -rc;
+ pdie("%s hook (index %d) failed",
+ lookup_hook_name(event), hook_index);
+ }
+ /* Only increase the index within the same hook event type. */
+ ++hook_index;
+ }
+}
+
void API minijail_enter(const struct minijail *j)
{
/*
@@ -1715,6 +1788,8 @@ void API minijail_enter(const struct minijail *j)
if (j->flags.remount_proc_ro && remount_proc_readonly(j))
pdie("remount");
+ run_hooks_or_die(j, MINIJAIL_HOOK_EVENT_PRE_DROP_CAPS);
+
/*
* If we're only dropping capabilities from the bounding set, but not
* from the thread's (permitted|inheritable|effective) sets, do it now.
@@ -2025,6 +2100,10 @@ int minijail_run_internal(struct minijail *j, const char *filename,
}
}
+ if (use_preload && j->hooks_head != NULL) {
+ die("Minijail hooks are not supported with LD_PRELOAD");
+ }
+
/*
* Make the process group ID of this process equal to its PID.
* In the non-interactive case (e.g. when the parent process is started
@@ -2350,6 +2429,8 @@ int minijail_run_internal(struct minijail *j, const char *filename,
}
}
+ run_hooks_or_die(j, MINIJAIL_HOOK_EVENT_PRE_EXECVE);
+
/*
* If we aren't pid-namespaced, or the jailed program asked to be init:
* calling process
@@ -2430,6 +2511,12 @@ void API minijail_destroy(struct minijail *j)
free(m);
}
j->mounts_tail = NULL;
+ while (j->hooks_head) {
+ struct hook *c = j->hooks_head;
+ j->hooks_head = c->next;
+ free(c);
+ }
+ j->hooks_tail = NULL;
if (j->user)
free(j->user);
if (j->suppl_gid_list)
diff --git a/libminijail.h b/libminijail.h
index ff063481..372c1a41 100644
--- a/libminijail.h
+++ b/libminijail.h
@@ -30,6 +30,31 @@ enum {
struct minijail;
+/*
+ * A hook that can be used to execute code at various events during minijail
+ * setup in the forked process. These can only be used if the jailed process is
+ * not going to be invoked with LD_PRELOAD.
+ *
+ * If the return value is non-zero, it will be interpreted as -errno and the
+ * process will abort.
+ */
+typedef int (*minijail_hook_t)(void *context);
+
+/*
+ * The events during minijail setup in which hooks can run. All the events are
+ * run in the new process.
+ */
+typedef enum {
+ /* The hook will run just before dropping capabilities. */
+ MINIJAIL_HOOK_EVENT_PRE_DROP_CAPS,
+
+ /* The hook will run just before calling execve(2). */
+ MINIJAIL_HOOK_EVENT_PRE_EXECVE,
+
+ /* Sentinel for error checking. Must be last. */
+ MINIJAIL_HOOK_EVENT_MAX,
+} minijail_hook_event_t;
+
/* Allocates a new minijail with no restrictions. */
struct minijail *minijail_new(void);
@@ -200,6 +225,19 @@ int minijail_bind(struct minijail *j, const char *src, const char *dest,
int writeable);
/*
+ * minijail_add_hook: adds @hook to the list of hooks that will be
+ * invoked when @event is reached during minijail setup. The caller is
+ * responsible for the lifetime of @payload.
+ * @j minijail to add the hook to
+ * @hook the function that will be invoked
+ * @payload an opaque pointer
+ * @event the event that will trigger the hook
+ */
+int minijail_add_hook(struct minijail *j,
+ minijail_hook_t hook, void *payload,
+ minijail_hook_event_t event);
+
+/*
* Lock this process into the given minijail. Note that this procedure cannot
* fail, since there is no way to undo privilege-dropping; therefore, if any
* part of the privilege-drop fails, minijail_enter() will abort the entire
diff --git a/libminijail_unittest.cc b/libminijail_unittest.cc
index d5ff6a87..caaa1386 100644
--- a/libminijail_unittest.cc
+++ b/libminijail_unittest.cc
@@ -273,6 +273,41 @@ TEST(Test, test_minijail_no_fd_leaks) {
close(dev_null);
}
+static int early_exit(void* payload) {
+ exit(static_cast<int>(reinterpret_cast<intptr_t>(payload)));
+}
+
+TEST(Test, test_minijail_callback) {
+ pid_t pid;
+ int mj_run_ret;
+ int status;
+#if defined(__ANDROID__)
+ char filename[] = "/system/bin/cat";
+#else
+ char filename[] = "/bin/cat";
+#endif
+ char *argv[2];
+ int exit_code = 42;
+
+ struct minijail *j = minijail_new();
+
+ status =
+ minijail_add_hook(j, &early_exit, reinterpret_cast<void *>(exit_code),
+ MINIJAIL_HOOK_EVENT_PRE_DROP_CAPS);
+ EXPECT_EQ(status, 0);
+
+ argv[0] = filename;
+ argv[1] = NULL;
+ mj_run_ret = minijail_run_pid_pipes_no_preload(j, argv[0], argv, &pid, NULL,
+ NULL, NULL);
+ EXPECT_EQ(mj_run_ret, 0);
+
+ status = minijail_wait(j);
+ EXPECT_EQ(status, exit_code);
+
+ minijail_destroy(j);
+}
+
TEST(Test, parse_size) {
size_t size;