blob: b5a3c75ea14f8d817d3ca0d7202342fd420f7ebd [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.
Elly Jonese1749eb2011-10-07 13:54:59 -04009 * See the minijail0.1 for a design explanation.
10 */
Elly Jonescd7a9042011-07-22 13:56:51 -040011
12#include "libminijail.h"
13#include "libminijail-private.h"
Stéphane Lesimple66bcc8c2022-01-06 13:11:37 +010014#include "util.h"
Elly Jonescd7a9042011-07-22 13:56:51 -040015
16#include <dlfcn.h>
Will Drewry2f54b6a2011-09-16 13:45:31 -050017#include <stdio.h>
Elly Jonescd7a9042011-07-22 13:56:51 -040018#include <stdlib.h>
19#include <string.h>
20#include <sys/types.h>
21#include <syslog.h>
22#include <unistd.h>
23
Elly Jonese1749eb2011-10-07 13:54:59 -040024static int (*real_main) (int, char **, char **);
25static void *libc_handle;
Elly Jonescd7a9042011-07-22 13:56:51 -040026
Stéphane Lesimple66bcc8c2022-01-06 13:11:37 +010027static void truncate_preload_env(char **envp, const char *name)
Elly Jonese1749eb2011-10-07 13:54:59 -040028{
Stéphane Lesimple66bcc8c2022-01-06 13:11:37 +010029 char *env_value = minijail_getenv(envp, name);
30 if (env_value) {
31 /*
32 * if we have more than just libminijailpreload.so in
33 * LD_PRELOAD, cut out libminijailpreload.so from it,
34 * as it is guaranteed to always be last in the
35 * LD_PRELOAD list.
36 */
37 char *last_space = strrchr(env_value, ' ');
38 if (last_space) {
39 *last_space = '\0';
40 } else {
41 /* Only our lib was in LD_PRELOAD, just unset it. */
42 minijail_unsetenv(envp, name);
43 }
44 }
Elly Jonescd7a9042011-07-22 13:56:51 -040045}
46
Elly Jonescd7a9042011-07-22 13:56:51 -040047/** @brief Fake main(), spliced in before the real call to main() by
48 * __libc_start_main (see below).
Will Drewry2f54b6a2011-09-16 13:45:31 -050049 * We get serialized commands from our invoking process over an fd specified
50 * by an environment variable (kFdEnvVar). The environment variable is a list
51 * of key=value pairs (see move_commands_to_env); we use them to construct a
52 * jail, then enter it.
Elly Jonescd7a9042011-07-22 13:56:51 -040053 */
Elly Jonese1749eb2011-10-07 13:54:59 -040054static int fake_main(int argc, char **argv, char **envp)
55{
56 char *fd_name = getenv(kFdEnvVar);
57 int fd = -1;
58 struct minijail *j;
Jorge Lucangeli Obesdb0bc672016-08-03 10:45:21 -040059 if (geteuid() != getuid() || getegid() != getgid()) {
60 /*
61 * If we didn't do this check, an attacker could set kFdEnvVar
Elly Jonese1749eb2011-10-07 13:54:59 -040062 * for any setuid program that uses libminijail to cause it to
63 * get capabilities or a uid it did not expect.
64 */
Jorge Lucangeli Obesdb0bc672016-08-03 10:45:21 -040065 /* TODO(wad): why would libminijail interact here? */
Elly Jonese1749eb2011-10-07 13:54:59 -040066 return MINIJAIL_ERR_PRELOAD;
Jorge Lucangeli Obesdb0bc672016-08-03 10:45:21 -040067 }
Elly Jonese1749eb2011-10-07 13:54:59 -040068 if (!fd_name)
69 return MINIJAIL_ERR_PRELOAD;
70 fd = atoi(fd_name);
71 if (fd < 0)
72 return MINIJAIL_ERR_PRELOAD;
Will Drewry2f54b6a2011-09-16 13:45:31 -050073
Elly Jonese1749eb2011-10-07 13:54:59 -040074 j = minijail_new();
75 if (!j)
76 die("preload: out of memory");
77 if (minijail_from_fd(fd, j))
78 die("preload: failed to parse minijail from parent");
79 close(fd);
Will Drewryfe4a3722011-09-16 14:50:50 -050080
Stéphane Lesimple66bcc8c2022-01-06 13:11:37 +010081 minijail_unsetenv(envp, kFdEnvVar);
82
83 truncate_preload_env(envp, kLdPreloadEnvVar);
84
Elly Jonese1749eb2011-10-07 13:54:59 -040085 /* Strip out flags meant for the parent. */
86 minijail_preenter(j);
87 minijail_enter(j);
88 minijail_destroy(j);
89 dlclose(libc_handle);
90 return real_main(argc, argv, envp);
Elly Jonescd7a9042011-07-22 13:56:51 -040091}
92
93/** @brief LD_PRELOAD override of __libc_start_main.
94 *
Elly Jonese1749eb2011-10-07 13:54:59 -040095 * It is really best if you do not look too closely at this function. We need
96 * to ensure that some of our code runs before the target program (see the
97 * minijail0.1 file in this directory for high-level details about this), and
Elly Jonescd7a9042011-07-22 13:56:51 -040098 * the only available place to hook is this function, which is normally
99 * responsible for calling main(). Our LD_PRELOAD will overwrite the real
100 * __libc_start_main with this one, so we have to look up the real one from
101 * libc and invoke it with a pointer to the fake main() we'd like to run before
102 * the real main(). We can't just run our setup code *here* because
103 * __libc_start_main is responsible for setting up the C runtime environment,
104 * so we can't rely on things like malloc() being available yet.
105 */
106
Jorge Lucangeli Obesdb0bc672016-08-03 10:45:21 -0400107int API __libc_start_main(int (*main)(int, char **, char **), int argc,
108 char **ubp_av, void (*init)(void), void (*fini)(void),
109 void (*rtld_fini)(void), void(*stack_end))
Elly Jonese1749eb2011-10-07 13:54:59 -0400110{
111 void *sym;
Jorge Lucangeli Obesdb0bc672016-08-03 10:45:21 -0400112 /*
113 * This hack is unfortunately required by C99 - casting directly from
Elly Jonese1749eb2011-10-07 13:54:59 -0400114 * void* to function pointers is left undefined. See POSIX.1-2003, the
115 * Rationale for the specification of dlsym(), and dlsym(3). This
116 * deliberately violates strict-aliasing rules, but gcc can't tell.
117 */
118 union {
Jorge Lucangeli Obesdb0bc672016-08-03 10:45:21 -0400119 int (*fn)(int (*main)(int, char **, char **), int argc,
120 char **ubp_av, void (*init)(void), void (*fini)(void),
121 void (*rtld_fini)(void), void(*stack_end));
Elly Jonese1749eb2011-10-07 13:54:59 -0400122 void *symval;
123 } real_libc_start_main;
Elly Jonescd7a9042011-07-22 13:56:51 -0400124
Jorge Lucangeli Obesdb0bc672016-08-03 10:45:21 -0400125 /*
126 * We hold this handle for the duration of the real __libc_start_main()
Elly Jonese1749eb2011-10-07 13:54:59 -0400127 * and drop it just before calling the real main().
128 */
129 libc_handle = dlopen("libc.so.6", RTLD_NOW);
Elly Jonescd7a9042011-07-22 13:56:51 -0400130
Elly Jonese1749eb2011-10-07 13:54:59 -0400131 if (!libc_handle) {
132 syslog(LOG_ERR, "can't dlopen() libc");
Jorge Lucangeli Obesdb0bc672016-08-03 10:45:21 -0400133 /*
134 * We dare not use abort() here because it will run atexit()
Elly Jonese1749eb2011-10-07 13:54:59 -0400135 * handlers and try to flush stdio.
136 */
137 _exit(1);
138 }
139 sym = dlsym(libc_handle, "__libc_start_main");
140 if (!sym) {
141 syslog(LOG_ERR, "can't find the real __libc_start_main()");
142 _exit(1);
143 }
144 real_libc_start_main.symval = sym;
145 real_main = main;
Elly Jonescd7a9042011-07-22 13:56:51 -0400146
Jorge Lucangeli Obesdb0bc672016-08-03 10:45:21 -0400147 /*
148 * Note that we swap fake_main in for main - fake_main knows that it
Elly Jonese1749eb2011-10-07 13:54:59 -0400149 * should call real_main after it's done.
150 */
151 return real_libc_start_main.fn(fake_main, argc, ubp_av, init, fini,
152 rtld_fini, stack_end);
Elly Jonescd7a9042011-07-22 13:56:51 -0400153}