[klibc] [PATCH v2 klibc] run-init: Add dry-run mode

Ben Hutchings ben at decadent.org.uk
Sun Jan 17 12:26:32 PST 2016


initramfs-tools wants to validate the real init program before running
it, as there is no way out once it has exec'd run-init.  This is
complicated by the increasing use of symlinks for /sbin/init and for
/sbin itself.  We can't simply resolve them with 'readlink -f' because
any absolute symlinks will be resolved using the wrong root.  Add a
dry-run mode (-n option) to run-init that goes as far as possible to
validate that the given init is executable.

Signed-off-by: Ben Hutchings <ben at decadent.org.uk>
---
v2:
- Don't duplicate close(confd) between dry-run and normal modes
- Fix error messages for init permission check failure

--- a/usr/kinit/run-init/run-init.c
+++ b/usr/kinit/run-init/run-init.c
@@ -39,6 +39,7 @@
  * - Spawns the specified init program (with arguments.)
  */
 
+#include <stdbool.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <unistd.h>
@@ -51,7 +52,7 @@ static const char *program;
 static void __attribute__ ((noreturn)) usage(void)
 {
 	fprintf(stderr,
-		"Usage: exec %s [-d caps] [-c consoledev] /real-root /sbin/init [args]\n",
+		"Usage: exec %s [-d caps] [-c consoledev] [-n] /real-root /sbin/init [args]\n",
 		program);
 	exit(1);
 }
@@ -64,6 +65,7 @@ int main(int argc, char *argv[])
 	const char *init;
 	const char *error;
 	const char *drop_caps = NULL;
+	bool dry_run = false;
 	char **initargs;
 
 	/* Variables... */
@@ -72,11 +74,13 @@ int main(int argc, char *argv[])
 	/* Parse the command line */
 	program = argv[0];
 
-	while ((o = getopt(argc, argv, "c:d:")) != -1) {
+	while ((o = getopt(argc, argv, "c:d:n")) != -1) {
 		if (o == 'c') {
 			console = optarg;
 		} else if (o == 'd') {
 			drop_caps = optarg;
+		} else if (o == 'n') {
+			dry_run = true;
 		} else {
 			usage();
 		}
@@ -89,9 +93,13 @@ int main(int argc, char *argv[])
 	init = argv[optind + 1];
 	initargs = argv + optind + 1;
 
-	error = run_init(realroot, console, drop_caps, init, initargs);
+	error = run_init(realroot, console, drop_caps, dry_run, init, initargs);
 
-	/* If run_init returns, something went wrong */
-	fprintf(stderr, "%s: %s: %s\n", program, error, strerror(errno));
-	return 1;
+	if (error) {
+		fprintf(stderr, "%s: %s: %s\n", program, error, strerror(errno));
+		return 1;
+	} else {
+		/* Must have been a dry run */
+		return 0;
+	}
 }
--- a/usr/kinit/run-init/run-init.h
+++ b/usr/kinit/run-init/run-init.h
@@ -28,7 +28,10 @@
 #ifndef RUN_INIT_H
 #define RUN_INIT_H
 
+#include <stdbool.h>
+
 const char *run_init(const char *realroot, const char *console,
-		     const char *drop_caps, const char *init, char **initargs);
+		     const char *drop_caps, bool dry_run,
+		     const char *init, char **initargs);
 
 #endif
--- a/usr/kinit/run-init/runinitlib.c
+++ b/usr/kinit/run-init/runinitlib.c
@@ -156,10 +156,10 @@ static int nuke(const char *what)
 }
 
 const char *run_init(const char *realroot, const char *console,
-		     const char *drop_caps, const char *init,
+		     const char *drop_caps, bool dry_run, const char *init,
 		     char **initargs)
 {
-	struct stat rst, cst;
+	struct stat rst, cst, ist;
 	struct statfs sfs;
 	int confd;
 
@@ -186,13 +186,15 @@ const char *run_init(const char *realroo
 
 	/* Okay, I think we should be safe... */
 
-	/* Delete rootfs contents */
-	if (nuke_dir("/"))
-		return "nuking initramfs contents";
-
-	/* Overmount the root */
-	if (mount(".", "/", NULL, MS_MOVE, NULL))
-		return "overmounting root";
+	if (!dry_run) {
+		/* Delete rootfs contents */
+		if (nuke_dir("/"))
+			return "nuking initramfs contents";
+
+		/* Overmount the root */
+		if (mount(".", "/", NULL, MS_MOVE, NULL))
+			return "overmounting root";
+	}
 
 	/* chroot, chdir */
 	if (chroot(".") || chdir("/"))
@@ -205,12 +207,24 @@ const char *run_init(const char *realroo
 	/* Open /dev/console */
 	if ((confd = open(console, O_RDWR)) < 0)
 		return "opening console";
-	dup2(confd, 0);
-	dup2(confd, 1);
-	dup2(confd, 2);
+	if (!dry_run) {
+		dup2(confd, 0);
+		dup2(confd, 1);
+		dup2(confd, 2);
+	}
 	close(confd);
 
-	/* Spawn init */
-	execv(init, initargs);
-	return init;		/* Failed to spawn init */
+	if (!dry_run) {
+		/* Spawn init */
+		execv(init, initargs);
+		return init;		/* Failed to spawn init */
+	} else {
+		if (stat(init, &ist))
+			return init;
+		if (!S_ISREG(ist.st_mode) || !(ist.st_mode & S_IXUGO)) {
+			errno = EACCES;
+			return init;
+		}
+		return NULL;		/* Success */
+	}
 }
--- a/usr/kinit/kinit.c
+++ b/usr/kinit/kinit.c
@@ -304,7 +304,7 @@ int main(int argc, char *argv[])
 	init_argv[0] = strrchr(init_path, '/') + 1;
 
 	errmsg = run_init("/root", "/dev/console",
-			  get_arg(cmdc, cmdv, "drop_capabilities="),
+			  get_arg(cmdc, cmdv, "drop_capabilities="), false,
 			  init_path, init_argv);
 
 	/* If run_init returned, something went bad */
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 811 bytes
Desc: Digital signature
URL: <http://www.zytor.com/pipermail/klibc/attachments/20160117/c27ca04d/attachment.sig>


More information about the klibc mailing list