Elly Jones | cd7a904 | 2011-07-22 13:56:51 -0400 | [diff] [blame] | 1 | /* libminijailpreload.c - preload hack library |
Mike Frysinger | 4c33189 | 2022-09-13 05:17:08 -0400 | [diff] [blame] | 2 | * Copyright 2011 The ChromiumOS Authors |
Elly Jones | cd7a904 | 2011-07-22 13:56:51 -0400 | [diff] [blame] | 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 Jones | e1749eb | 2011-10-07 13:54:59 -0400 | [diff] [blame] | 9 | * See the minijail0.1 for a design explanation. |
| 10 | */ |
Elly Jones | cd7a904 | 2011-07-22 13:56:51 -0400 | [diff] [blame] | 11 | |
| 12 | #include "libminijail.h" |
| 13 | #include "libminijail-private.h" |
Stéphane Lesimple | 66bcc8c | 2022-01-06 13:11:37 +0100 | [diff] [blame] | 14 | #include "util.h" |
Elly Jones | cd7a904 | 2011-07-22 13:56:51 -0400 | [diff] [blame] | 15 | |
| 16 | #include <dlfcn.h> |
Will Drewry | 2f54b6a | 2011-09-16 13:45:31 -0500 | [diff] [blame] | 17 | #include <stdio.h> |
Elly Jones | cd7a904 | 2011-07-22 13:56:51 -0400 | [diff] [blame] | 18 | #include <stdlib.h> |
| 19 | #include <string.h> |
| 20 | #include <sys/types.h> |
| 21 | #include <syslog.h> |
| 22 | #include <unistd.h> |
| 23 | |
Elly Jones | e1749eb | 2011-10-07 13:54:59 -0400 | [diff] [blame] | 24 | static int (*real_main) (int, char **, char **); |
| 25 | static void *libc_handle; |
Elly Jones | cd7a904 | 2011-07-22 13:56:51 -0400 | [diff] [blame] | 26 | |
Stéphane Lesimple | 66bcc8c | 2022-01-06 13:11:37 +0100 | [diff] [blame] | 27 | static void truncate_preload_env(char **envp, const char *name) |
Elly Jones | e1749eb | 2011-10-07 13:54:59 -0400 | [diff] [blame] | 28 | { |
Stéphane Lesimple | 66bcc8c | 2022-01-06 13:11:37 +0100 | [diff] [blame] | 29 | 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 Jones | cd7a904 | 2011-07-22 13:56:51 -0400 | [diff] [blame] | 45 | } |
| 46 | |
Elly Jones | cd7a904 | 2011-07-22 13:56:51 -0400 | [diff] [blame] | 47 | /** @brief Fake main(), spliced in before the real call to main() by |
| 48 | * __libc_start_main (see below). |
Will Drewry | 2f54b6a | 2011-09-16 13:45:31 -0500 | [diff] [blame] | 49 | * 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 Jones | cd7a904 | 2011-07-22 13:56:51 -0400 | [diff] [blame] | 53 | */ |
Elly Jones | e1749eb | 2011-10-07 13:54:59 -0400 | [diff] [blame] | 54 | static 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 Obes | db0bc67 | 2016-08-03 10:45:21 -0400 | [diff] [blame] | 59 | if (geteuid() != getuid() || getegid() != getgid()) { |
| 60 | /* |
| 61 | * If we didn't do this check, an attacker could set kFdEnvVar |
Elly Jones | e1749eb | 2011-10-07 13:54:59 -0400 | [diff] [blame] | 62 | * for any setuid program that uses libminijail to cause it to |
| 63 | * get capabilities or a uid it did not expect. |
| 64 | */ |
Jorge Lucangeli Obes | db0bc67 | 2016-08-03 10:45:21 -0400 | [diff] [blame] | 65 | /* TODO(wad): why would libminijail interact here? */ |
Elly Jones | e1749eb | 2011-10-07 13:54:59 -0400 | [diff] [blame] | 66 | return MINIJAIL_ERR_PRELOAD; |
Jorge Lucangeli Obes | db0bc67 | 2016-08-03 10:45:21 -0400 | [diff] [blame] | 67 | } |
Elly Jones | e1749eb | 2011-10-07 13:54:59 -0400 | [diff] [blame] | 68 | if (!fd_name) |
| 69 | return MINIJAIL_ERR_PRELOAD; |
| 70 | fd = atoi(fd_name); |
| 71 | if (fd < 0) |
| 72 | return MINIJAIL_ERR_PRELOAD; |
Will Drewry | 2f54b6a | 2011-09-16 13:45:31 -0500 | [diff] [blame] | 73 | |
Elly Jones | e1749eb | 2011-10-07 13:54:59 -0400 | [diff] [blame] | 74 | 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 Drewry | fe4a372 | 2011-09-16 14:50:50 -0500 | [diff] [blame] | 80 | |
Stéphane Lesimple | 66bcc8c | 2022-01-06 13:11:37 +0100 | [diff] [blame] | 81 | minijail_unsetenv(envp, kFdEnvVar); |
| 82 | |
| 83 | truncate_preload_env(envp, kLdPreloadEnvVar); |
| 84 | |
Elly Jones | e1749eb | 2011-10-07 13:54:59 -0400 | [diff] [blame] | 85 | /* 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 Jones | cd7a904 | 2011-07-22 13:56:51 -0400 | [diff] [blame] | 91 | } |
| 92 | |
| 93 | /** @brief LD_PRELOAD override of __libc_start_main. |
| 94 | * |
Elly Jones | e1749eb | 2011-10-07 13:54:59 -0400 | [diff] [blame] | 95 | * 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 Jones | cd7a904 | 2011-07-22 13:56:51 -0400 | [diff] [blame] | 98 | * 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 Obes | db0bc67 | 2016-08-03 10:45:21 -0400 | [diff] [blame] | 107 | int 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 Jones | e1749eb | 2011-10-07 13:54:59 -0400 | [diff] [blame] | 110 | { |
| 111 | void *sym; |
Jorge Lucangeli Obes | db0bc67 | 2016-08-03 10:45:21 -0400 | [diff] [blame] | 112 | /* |
| 113 | * This hack is unfortunately required by C99 - casting directly from |
Elly Jones | e1749eb | 2011-10-07 13:54:59 -0400 | [diff] [blame] | 114 | * 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 Obes | db0bc67 | 2016-08-03 10:45:21 -0400 | [diff] [blame] | 119 | 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 Jones | e1749eb | 2011-10-07 13:54:59 -0400 | [diff] [blame] | 122 | void *symval; |
| 123 | } real_libc_start_main; |
Elly Jones | cd7a904 | 2011-07-22 13:56:51 -0400 | [diff] [blame] | 124 | |
Jorge Lucangeli Obes | db0bc67 | 2016-08-03 10:45:21 -0400 | [diff] [blame] | 125 | /* |
| 126 | * We hold this handle for the duration of the real __libc_start_main() |
Elly Jones | e1749eb | 2011-10-07 13:54:59 -0400 | [diff] [blame] | 127 | * and drop it just before calling the real main(). |
| 128 | */ |
| 129 | libc_handle = dlopen("libc.so.6", RTLD_NOW); |
Elly Jones | cd7a904 | 2011-07-22 13:56:51 -0400 | [diff] [blame] | 130 | |
Elly Jones | e1749eb | 2011-10-07 13:54:59 -0400 | [diff] [blame] | 131 | if (!libc_handle) { |
| 132 | syslog(LOG_ERR, "can't dlopen() libc"); |
Jorge Lucangeli Obes | db0bc67 | 2016-08-03 10:45:21 -0400 | [diff] [blame] | 133 | /* |
| 134 | * We dare not use abort() here because it will run atexit() |
Elly Jones | e1749eb | 2011-10-07 13:54:59 -0400 | [diff] [blame] | 135 | * 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 Jones | cd7a904 | 2011-07-22 13:56:51 -0400 | [diff] [blame] | 146 | |
Jorge Lucangeli Obes | db0bc67 | 2016-08-03 10:45:21 -0400 | [diff] [blame] | 147 | /* |
| 148 | * Note that we swap fake_main in for main - fake_main knows that it |
Elly Jones | e1749eb | 2011-10-07 13:54:59 -0400 | [diff] [blame] | 149 | * 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 Jones | cd7a904 | 2011-07-22 13:56:51 -0400 | [diff] [blame] | 153 | } |