[c5c522c] | 1 | From: Ben Hutchings <ben@decadent.org.uk> |
---|
| 2 | Date: Sun, 17 Jan 2016 19:50:28 +0000 |
---|
| 3 | Subject: run-init: Add dry-run mode |
---|
| 4 | Bug-Debian: https://bugs.debian.org/810965 |
---|
| 5 | |
---|
| 6 | initramfs-tools wants to validate the real init program before running |
---|
| 7 | it, as there is no way out once it has exec'd run-init. This is |
---|
| 8 | complicated by the increasing use of symlinks for /sbin/init and for |
---|
| 9 | /sbin itself. We can't simply resolve them with 'readlink -f' because |
---|
| 10 | any absolute symlinks will be resolved using the wrong root. Add a |
---|
| 11 | dry-run mode (-n option) to run-init that goes as far as possible to |
---|
| 12 | validate that the given init is executable. |
---|
| 13 | |
---|
| 14 | Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
---|
| 15 | --- |
---|
| 16 | --- a/usr/kinit/run-init/run-init.c |
---|
| 17 | +++ b/usr/kinit/run-init/run-init.c |
---|
| 18 | @@ -26,19 +26,23 @@ |
---|
| 19 | * ----------------------------------------------------------------------- */ |
---|
| 20 | |
---|
| 21 | /* |
---|
| 22 | - * Usage: exec run-init [-d caps] [-c /dev/console] /real-root /sbin/init "$@" |
---|
| 23 | + * Usage: exec run-init [-d caps] [-c /dev/console] [-n] /real-root /sbin/init "$@" |
---|
| 24 | * |
---|
| 25 | * This program should be called as the last thing in a shell script |
---|
| 26 | * acting as /init in an initramfs; it does the following: |
---|
| 27 | * |
---|
| 28 | - * - Delete all files in the initramfs; |
---|
| 29 | - * - Remounts /real-root onto the root filesystem; |
---|
| 30 | - * - Drops comma-separated list of capabilities; |
---|
| 31 | - * - Chroots; |
---|
| 32 | - * - Opens /dev/console; |
---|
| 33 | - * - Spawns the specified init program (with arguments.) |
---|
| 34 | + * 1. Delete all files in the initramfs; |
---|
| 35 | + * 2. Remounts /real-root onto the root filesystem; |
---|
| 36 | + * 3. Drops comma-separated list of capabilities; |
---|
| 37 | + * 4. Chroots; |
---|
| 38 | + * 5. Opens /dev/console; |
---|
| 39 | + * 6. Spawns the specified init program (with arguments.) |
---|
| 40 | + * |
---|
| 41 | + * With the -n option, it skips steps 1, 2 and 6 and can be used to check |
---|
| 42 | + * whether the given root and init are likely to work. |
---|
| 43 | */ |
---|
| 44 | |
---|
| 45 | +#include <stdbool.h> |
---|
| 46 | #include <stdlib.h> |
---|
| 47 | #include <stdio.h> |
---|
| 48 | #include <unistd.h> |
---|
| 49 | @@ -51,7 +55,7 @@ static const char *program; |
---|
| 50 | static void __attribute__ ((noreturn)) usage(void) |
---|
| 51 | { |
---|
| 52 | fprintf(stderr, |
---|
| 53 | - "Usage: exec %s [-d caps] [-c consoledev] /real-root /sbin/init [args]\n", |
---|
| 54 | + "Usage: exec %s [-d caps] [-c consoledev] [-n] /real-root /sbin/init [args]\n", |
---|
| 55 | program); |
---|
| 56 | exit(1); |
---|
| 57 | } |
---|
| 58 | @@ -64,6 +68,7 @@ int main(int argc, char *argv[]) |
---|
| 59 | const char *init; |
---|
| 60 | const char *error; |
---|
| 61 | const char *drop_caps = NULL; |
---|
| 62 | + bool dry_run = false; |
---|
| 63 | char **initargs; |
---|
| 64 | |
---|
| 65 | /* Variables... */ |
---|
| 66 | @@ -72,11 +77,13 @@ int main(int argc, char *argv[]) |
---|
| 67 | /* Parse the command line */ |
---|
| 68 | program = argv[0]; |
---|
| 69 | |
---|
| 70 | - while ((o = getopt(argc, argv, "c:d:")) != -1) { |
---|
| 71 | + while ((o = getopt(argc, argv, "c:d:n")) != -1) { |
---|
| 72 | if (o == 'c') { |
---|
| 73 | console = optarg; |
---|
| 74 | } else if (o == 'd') { |
---|
| 75 | drop_caps = optarg; |
---|
| 76 | + } else if (o == 'n') { |
---|
| 77 | + dry_run = true; |
---|
| 78 | } else { |
---|
| 79 | usage(); |
---|
| 80 | } |
---|
| 81 | @@ -89,9 +96,13 @@ int main(int argc, char *argv[]) |
---|
| 82 | init = argv[optind + 1]; |
---|
| 83 | initargs = argv + optind + 1; |
---|
| 84 | |
---|
| 85 | - error = run_init(realroot, console, drop_caps, init, initargs); |
---|
| 86 | + error = run_init(realroot, console, drop_caps, dry_run, init, initargs); |
---|
| 87 | |
---|
| 88 | - /* If run_init returns, something went wrong */ |
---|
| 89 | - fprintf(stderr, "%s: %s: %s\n", program, error, strerror(errno)); |
---|
| 90 | - return 1; |
---|
| 91 | + if (error) { |
---|
| 92 | + fprintf(stderr, "%s: %s: %s\n", program, error, strerror(errno)); |
---|
| 93 | + return 1; |
---|
| 94 | + } else { |
---|
| 95 | + /* Must have been a dry run */ |
---|
| 96 | + return 0; |
---|
| 97 | + } |
---|
| 98 | } |
---|
| 99 | --- a/usr/kinit/run-init/run-init.h |
---|
| 100 | +++ b/usr/kinit/run-init/run-init.h |
---|
| 101 | @@ -28,7 +28,10 @@ |
---|
| 102 | #ifndef RUN_INIT_H |
---|
| 103 | #define RUN_INIT_H |
---|
| 104 | |
---|
| 105 | +#include <stdbool.h> |
---|
| 106 | + |
---|
| 107 | const char *run_init(const char *realroot, const char *console, |
---|
| 108 | - const char *drop_caps, const char *init, char **initargs); |
---|
| 109 | + const char *drop_caps, bool dry_run, |
---|
| 110 | + const char *init, char **initargs); |
---|
| 111 | |
---|
| 112 | #endif |
---|
| 113 | --- a/usr/kinit/run-init/runinitlib.c |
---|
| 114 | +++ b/usr/kinit/run-init/runinitlib.c |
---|
| 115 | @@ -156,10 +156,10 @@ static int nuke(const char *what) |
---|
| 116 | } |
---|
| 117 | |
---|
| 118 | const char *run_init(const char *realroot, const char *console, |
---|
| 119 | - const char *drop_caps, const char *init, |
---|
| 120 | + const char *drop_caps, bool dry_run, const char *init, |
---|
| 121 | char **initargs) |
---|
| 122 | { |
---|
| 123 | - struct stat rst, cst; |
---|
| 124 | + struct stat rst, cst, ist; |
---|
| 125 | struct statfs sfs; |
---|
| 126 | int confd; |
---|
| 127 | |
---|
| 128 | @@ -186,13 +186,15 @@ const char *run_init(const char *realroo |
---|
| 129 | |
---|
| 130 | /* Okay, I think we should be safe... */ |
---|
| 131 | |
---|
| 132 | - /* Delete rootfs contents */ |
---|
| 133 | - if (nuke_dir("/")) |
---|
| 134 | - return "nuking initramfs contents"; |
---|
| 135 | - |
---|
| 136 | - /* Overmount the root */ |
---|
| 137 | - if (mount(".", "/", NULL, MS_MOVE, NULL)) |
---|
| 138 | - return "overmounting root"; |
---|
| 139 | + if (!dry_run) { |
---|
| 140 | + /* Delete rootfs contents */ |
---|
| 141 | + if (nuke_dir("/")) |
---|
| 142 | + return "nuking initramfs contents"; |
---|
| 143 | + |
---|
| 144 | + /* Overmount the root */ |
---|
| 145 | + if (mount(".", "/", NULL, MS_MOVE, NULL)) |
---|
| 146 | + return "overmounting root"; |
---|
| 147 | + } |
---|
| 148 | |
---|
| 149 | /* chroot, chdir */ |
---|
| 150 | if (chroot(".") || chdir("/")) |
---|
| 151 | @@ -205,12 +207,24 @@ const char *run_init(const char *realroo |
---|
| 152 | /* Open /dev/console */ |
---|
| 153 | if ((confd = open(console, O_RDWR)) < 0) |
---|
| 154 | return "opening console"; |
---|
| 155 | - dup2(confd, 0); |
---|
| 156 | - dup2(confd, 1); |
---|
| 157 | - dup2(confd, 2); |
---|
| 158 | + if (!dry_run) { |
---|
| 159 | + dup2(confd, 0); |
---|
| 160 | + dup2(confd, 1); |
---|
| 161 | + dup2(confd, 2); |
---|
| 162 | + } |
---|
| 163 | close(confd); |
---|
| 164 | |
---|
| 165 | - /* Spawn init */ |
---|
| 166 | - execv(init, initargs); |
---|
| 167 | - return init; /* Failed to spawn init */ |
---|
| 168 | + if (!dry_run) { |
---|
| 169 | + /* Spawn init */ |
---|
| 170 | + execv(init, initargs); |
---|
| 171 | + return init; /* Failed to spawn init */ |
---|
| 172 | + } else { |
---|
| 173 | + if (stat(init, &ist)) |
---|
| 174 | + return init; |
---|
| 175 | + if (!S_ISREG(ist.st_mode) || !(ist.st_mode & S_IXUGO)) { |
---|
| 176 | + errno = EACCES; |
---|
| 177 | + return init; |
---|
| 178 | + } |
---|
| 179 | + return NULL; /* Success */ |
---|
| 180 | + } |
---|
| 181 | } |
---|
| 182 | --- a/usr/kinit/kinit.c |
---|
| 183 | +++ b/usr/kinit/kinit.c |
---|
| 184 | @@ -304,7 +304,7 @@ int main(int argc, char *argv[]) |
---|
| 185 | init_argv[0] = strrchr(init_path, '/') + 1; |
---|
| 186 | |
---|
| 187 | errmsg = run_init("/root", "/dev/console", |
---|
| 188 | - get_arg(cmdc, cmdv, "drop_capabilities="), |
---|
| 189 | + get_arg(cmdc, cmdv, "drop_capabilities="), false, |
---|
| 190 | init_path, init_argv); |
---|
| 191 | |
---|
| 192 | /* If run_init returned, something went bad */ |
---|