blob: 975c335f72060d9ee2ae910b65055e0c3ebea148 [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>
15#include <stdlib.h>
16#include <string.h>
17#include <sys/types.h>
18#include <syslog.h>
19#include <unistd.h>
20
21static int (*real_main)(int, char **, char **) = NULL;
22static void *libc_handle = NULL;
23
24static void die(const char *failed) {
25 syslog(LOG_ERR, "libminijail: %s", failed);
26 abort();
27}
28
29static void unset_in_env(char **envp, const char *name) {
30 int i;
31 for (i = 0; envp[i]; i++)
32 if (!strncmp(envp[i], name, strlen(name)))
33 envp[i][0] = '\0';
34}
35
36static void splitarg(char *str, char **key, char **val) {
37 *key = strsep(&str, "=");
38 *val = strsep(&str, "");
39}
40
41/** @brief Fake main(), spliced in before the real call to main() by
42 * __libc_start_main (see below).
43 * We get serialized commands from our invoking process in an environment
44 * variable (kCommandEnvVar). The environment variable is a list of key=value
45 * pairs (see move_commands_to_env); we use them to construct a jail, then
46 * enter it.
47 */
48static int fake_main(int argc, char **argv, char **envp) {
49 char *args = getenv(kCommandEnvVar);
50 char *copy, *oldcopy;
51 char *arg;
52 struct minijail *j;
53 if (geteuid() != getuid() || getegid() != getgid())
54 /* If we didn't do this check, an attacker could set kCommandEnvVar for
55 * any setuid program that uses libminijail to cause it to get capabilities
56 * or a uid it did not expect. */
57 return MINIJAIL_ERR_PRELOAD;
58 if (!args)
59 return MINIJAIL_ERR_PRELOAD;
60 if (!(copy = strdup(args)))
61 die("preload: out of memory");
62 oldcopy = copy;
63 j = minijail_new();
64 if (!j)
65 die("preload: out of memory");
66 while ((arg = strsep(&copy, " "))) {
67 char *key, *val;
68 unsigned long v;
69 splitarg(arg, &key, &val);
70 if (!strcmp(key, "caps")) {
71 v = strtoul(val, NULL, 16);
72 minijail_use_caps(j, v);
73 }
74 else if (!strcmp(key, "ptrace"))
75 minijail_disable_ptrace(j);
76 else if (!strcmp(key, "uid")) {
77 v = atoi(val);
78 minijail_change_uid(j, v);
79 }
80 else if (!strcmp(key, "seccomp"))
81 minijail_use_seccomp(j);
82 }
83 /* TODO(ellyjones): this trashes existing preloads, so one can't do:
84 * LD_PRELOAD="/tmp/test.so libminijailpreload.so" prog; the descendants of
85 * prog will have no LD_PRELOAD set at all. */
86 unset_in_env(envp, "LD_PRELOAD");
87 minijail_enter(j);
88 minijail_destroy(j);
89 free(oldcopy);
90 dlclose(libc_handle);
91 return real_main(argc, argv, envp);
92}
93
94/** @brief LD_PRELOAD override of __libc_start_main.
95 *
96 * It is really best if you do not look too closely at this function.
97 * We need to ensure that some of our code runs before the target program (see
98 * the minijail0.1 file in this directory for high-level details about this), and
99 * the only available place to hook is this function, which is normally
100 * responsible for calling main(). Our LD_PRELOAD will overwrite the real
101 * __libc_start_main with this one, so we have to look up the real one from
102 * libc and invoke it with a pointer to the fake main() we'd like to run before
103 * the real main(). We can't just run our setup code *here* because
104 * __libc_start_main is responsible for setting up the C runtime environment,
105 * so we can't rely on things like malloc() being available yet.
106 */
107
108int __libc_start_main(int (*main) (int, char **, char **),
109 int argc, char ** ubp_av, void (*init) (void),
110 void (*fini) (void), void (*rtld_fini) (void),
111 void (* stack_end)) {
112 void *sym;
113 /* This hack is unfortunately required by C99 - casting directly from void* to
114 * function pointers is left undefined. See POSIX.1-2003, the Rationale for
115 * the specification of dlsym(), and dlsym(3). This deliberately violates
116 * strict-aliasing rules, but gcc can't tell. */
117 union {
118 int (*fn)(int (*main) (int, char **, char **), int argc,
119 char **ubp_av, void (*init) (void), void (*fini) (void),
120 void (*rtld_fini) (void), void (* stack_end));
121 void *symval;
122 } real_libc_start_main;
123
124 /* We hold this handle for the duration of the real __libc_start_main() and
125 * drop it just before calling the real main(). */
126 libc_handle = dlopen("libc.so.6", RTLD_NOW);
127
128 if (!libc_handle) {
129 syslog(LOG_ERR, "can't dlopen() libc");
130 /* We dare not use abort() here because it will run atexit() handlers and
131 * try to flush stdio. */
132 _exit(1);
133 }
134 sym = dlsym(libc_handle, "__libc_start_main");
135 if (!sym) {
136 syslog(LOG_ERR, "can't find the real __libc_start_main()");
137 _exit(1);
138 }
139 real_libc_start_main.symval = sym;
140 real_main = main;
141
142 /* Note that we swap fake_main in for main - fake_main knows that it should
143 * call real_main after it's done. */
144 return real_libc_start_main.fn(fake_main, argc, ubp_av, init, fini, rtld_fini,
145 stack_end);
146}