blob: a4970bc8d06486bfbac125512dd56ec8a20b956d [file] [log] [blame]
Elly Jonescd7a9042011-07-22 13:56:51 -04001/* libminijailpreload.c - preload hack library
2 * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 *
6 * This library is preloaded into every program launched by minijail_run().
7 * DO NOT EXPORT ANY SYMBOLS FROM THIS LIBRARY. They will replace other symbols
8 * in the programs it is preloaded into and cause impossible-to-debug failures.
9 * See the minijail0.1 for a design explanation. */
10
11#include "libminijail.h"
12#include "libminijail-private.h"
13
14#include <dlfcn.h>
Will Drewry2f54b6a2011-09-16 13:45:31 -050015#include <stdio.h>
Elly Jonescd7a9042011-07-22 13:56:51 -040016#include <stdlib.h>
17#include <string.h>
18#include <sys/types.h>
19#include <syslog.h>
20#include <unistd.h>
21
22static int (*real_main)(int, char **, char **) = NULL;
23static void *libc_handle = NULL;
24
25static void die(const char *failed) {
26 syslog(LOG_ERR, "libminijail: %s", failed);
27 abort();
28}
29
30static void unset_in_env(char **envp, const char *name) {
31 int i;
32 for (i = 0; envp[i]; i++)
33 if (!strncmp(envp[i], name, strlen(name)))
34 envp[i][0] = '\0';
35}
36
37static void splitarg(char *str, char **key, char **val) {
38 *key = strsep(&str, "=");
39 *val = strsep(&str, "");
40}
41
42/** @brief Fake main(), spliced in before the real call to main() by
43 * __libc_start_main (see below).
Will Drewry2f54b6a2011-09-16 13:45:31 -050044 * We get serialized commands from our invoking process over an fd specified
45 * by an environment variable (kFdEnvVar). The environment variable is a list
46 * of key=value pairs (see move_commands_to_env); we use them to construct a
47 * jail, then enter it.
Elly Jonescd7a9042011-07-22 13:56:51 -040048 */
49static int fake_main(int argc, char **argv, char **envp) {
Will Drewry2f54b6a2011-09-16 13:45:31 -050050 char *fd_name = getenv(kFdEnvVar);
51 char *arg = NULL;
52 size_t arg_len;
53 int fd = -1;
54 FILE *args;
Elly Jonescd7a9042011-07-22 13:56:51 -040055 struct minijail *j;
56 if (geteuid() != getuid() || getegid() != getgid())
Will Drewry2f54b6a2011-09-16 13:45:31 -050057 /* If we didn't do this check, an attacker could set kFdEnvVar for
Elly Jonescd7a9042011-07-22 13:56:51 -040058 * any setuid program that uses libminijail to cause it to get capabilities
59 * or a uid it did not expect. */
Will Drewry2f54b6a2011-09-16 13:45:31 -050060 /* TODO(wad) why would libminijail interact here? */
Elly Jonescd7a9042011-07-22 13:56:51 -040061 return MINIJAIL_ERR_PRELOAD;
Will Drewry2f54b6a2011-09-16 13:45:31 -050062 if (!fd_name)
63 return MINIJAIL_ERR_PRELOAD;
64 fd = atoi(fd_name);
65 if (fd < 0)
66 return MINIJAIL_ERR_PRELOAD;
67 args = fdopen(fd, "r");
Elly Jonescd7a9042011-07-22 13:56:51 -040068 if (!args)
69 return MINIJAIL_ERR_PRELOAD;
Will Drewry2f54b6a2011-09-16 13:45:31 -050070
Elly Jonescd7a9042011-07-22 13:56:51 -040071 j = minijail_new();
72 if (!j)
73 die("preload: out of memory");
Will Drewry2f54b6a2011-09-16 13:45:31 -050074 while (getline(&arg, &arg_len, args) > 0) {
Elly Jonescd7a9042011-07-22 13:56:51 -040075 char *key, *val;
76 unsigned long v;
77 splitarg(arg, &key, &val);
Will Drewry2f54b6a2011-09-16 13:45:31 -050078 if (!strcmp(arg, "eom\n")) {
79 break;
80 } else if (!strcmp(key, "caps")) {
Elly Jonescd7a9042011-07-22 13:56:51 -040081 v = strtoul(val, NULL, 16);
82 minijail_use_caps(j, v);
Will Drewry2f54b6a2011-09-16 13:45:31 -050083 } else if (!strcmp(key, "ptrace")) {
Elly Jonescd7a9042011-07-22 13:56:51 -040084 minijail_disable_ptrace(j);
Will Drewry2f54b6a2011-09-16 13:45:31 -050085 } else if (!strcmp(key, "uid")) {
Elly Jonescd7a9042011-07-22 13:56:51 -040086 v = atoi(val);
87 minijail_change_uid(j, v);
Will Drewry2f54b6a2011-09-16 13:45:31 -050088 } else if (!strcmp(key, "gid")) {
Ben Chan6f470382011-08-23 12:54:41 -070089 v = atoi(val);
90 minijail_change_gid(j, v);
Will Drewry2f54b6a2011-09-16 13:45:31 -050091 } else if (!strcmp(key, "seccomp")) {
Elly Jonescd7a9042011-07-22 13:56:51 -040092 minijail_use_seccomp(j);
Will Drewry2f54b6a2011-09-16 13:45:31 -050093 }
94 free(arg);
95 arg = NULL;
Elly Jonescd7a9042011-07-22 13:56:51 -040096 }
Will Drewry2f54b6a2011-09-16 13:45:31 -050097 if (!feof(args) && ferror(args))
98 die("preload: unexpected failure during unmarshalling");
99 fclose(args);
Elly Jonescd7a9042011-07-22 13:56:51 -0400100 /* TODO(ellyjones): this trashes existing preloads, so one can't do:
101 * LD_PRELOAD="/tmp/test.so libminijailpreload.so" prog; the descendants of
102 * prog will have no LD_PRELOAD set at all. */
Ben Chan541c7e52011-08-26 14:55:53 -0700103 unset_in_env(envp, kLdPreloadEnvVar);
Elly Jonescd7a9042011-07-22 13:56:51 -0400104 minijail_enter(j);
105 minijail_destroy(j);
Elly Jonescd7a9042011-07-22 13:56:51 -0400106 dlclose(libc_handle);
107 return real_main(argc, argv, envp);
108}
109
110/** @brief LD_PRELOAD override of __libc_start_main.
111 *
112 * It is really best if you do not look too closely at this function.
113 * We need to ensure that some of our code runs before the target program (see
114 * the minijail0.1 file in this directory for high-level details about this), and
115 * the only available place to hook is this function, which is normally
116 * responsible for calling main(). Our LD_PRELOAD will overwrite the real
117 * __libc_start_main with this one, so we have to look up the real one from
118 * libc and invoke it with a pointer to the fake main() we'd like to run before
119 * the real main(). We can't just run our setup code *here* because
120 * __libc_start_main is responsible for setting up the C runtime environment,
121 * so we can't rely on things like malloc() being available yet.
122 */
123
124int __libc_start_main(int (*main) (int, char **, char **),
125 int argc, char ** ubp_av, void (*init) (void),
126 void (*fini) (void), void (*rtld_fini) (void),
127 void (* stack_end)) {
128 void *sym;
129 /* This hack is unfortunately required by C99 - casting directly from void* to
130 * function pointers is left undefined. See POSIX.1-2003, the Rationale for
131 * the specification of dlsym(), and dlsym(3). This deliberately violates
132 * strict-aliasing rules, but gcc can't tell. */
133 union {
134 int (*fn)(int (*main) (int, char **, char **), int argc,
135 char **ubp_av, void (*init) (void), void (*fini) (void),
136 void (*rtld_fini) (void), void (* stack_end));
137 void *symval;
138 } real_libc_start_main;
139
140 /* We hold this handle for the duration of the real __libc_start_main() and
141 * drop it just before calling the real main(). */
142 libc_handle = dlopen("libc.so.6", RTLD_NOW);
143
144 if (!libc_handle) {
145 syslog(LOG_ERR, "can't dlopen() libc");
146 /* We dare not use abort() here because it will run atexit() handlers and
147 * try to flush stdio. */
148 _exit(1);
149 }
150 sym = dlsym(libc_handle, "__libc_start_main");
151 if (!sym) {
152 syslog(LOG_ERR, "can't find the real __libc_start_main()");
153 _exit(1);
154 }
155 real_libc_start_main.symval = sym;
156 real_main = main;
157
158 /* Note that we swap fake_main in for main - fake_main knows that it should
159 * call real_main after it's done. */
160 return real_libc_start_main.fn(fake_main, argc, ubp_av, init, fini, rtld_fini,
161 stack_end);
162}