Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 03 Sep 2019 14:07:15 -0000
From:      Kyle Evans <kevans@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org
Subject:   svn commit: r346479 - in stable/11: sbin/init stand/man
Message-ID:  <201904210400.x3L40JGZ031625@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kevans
Date: Sun Apr 21 04:00:19 2019
New Revision: 346479
URL: https://svnweb.freebsd.org/changeset/base/346479

Log:
  MFC r337321, r337435, r337707, r337740, r337834, r337836, r337968
  
  r337321:
  Make it possible for init to execute any executable, not just sh(1)
  scripts. This means one should be able to eg rewrite their /etc/rc
  in Python.
  
  r337435:
  Move description of init_shell, init_script, and init_chroot kenv
  tunables from loader(8) to init(8), since it's init that actually
  uses them.  Add .Xrs at their old place.
  
  r337707:
  Move around text in loader(8), in particular stuff related to ZFS,
  to restore the usual section order.
  
  r337740:
  Add init_exec kenv(1) variable, to make init(8) execute a file
  after opening the console, replacing init as PID 1.
  
  From the user point of view, it makes it possible to run eg the
  shell as PID 1, using 'set init_exec=/bin/sh' at the loader(8)
  prompt.
  
  r337834:
  Add SECURITY section to loader(8).
  
  r337836:
  Improve formatting.
  
  r337968:
  Consistently use NULL to terminate the argv; no functional changes.
  
  Relnotes:	yes	(init_exec kenv(1) variable)

Modified:
  stable/11/sbin/init/init.8
  stable/11/sbin/init/init.c
  stable/11/stand/man/loader.8
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/sbin/init/init.8
==============================================================================
--- stable/11/sbin/init/init.8	Sun Apr 21 03:54:49 2019	(r346478)
+++ stable/11/sbin/init/init.8	Sun Apr 21 04:00:19 2019	(r346479)
@@ -31,7 +31,7 @@
 .\"     @(#)init.8	8.3 (Berkeley) 4/18/94
 .\" $FreeBSD$
 .\"
-.Dd October 3, 2016
+.Dd August 15, 2018
 .Dt INIT 8
 .Os
 .Sh NAME
@@ -86,6 +86,15 @@ The password check is skipped if the
 .Em console
 is marked as
 .Dq secure .
+Note that the password check does not protect from variables
+such as
+.Va init_script
+being set from the
+.Xr loader 8
+command line; see the
+.Sx SECURITY
+section of
+.Xr loader 8 .
 .Pp
 If the system security level (see
 .Xr security 7 )
@@ -293,6 +302,84 @@ as follows:
 .Xr ttys 5
 file
 .El
+.Sh KERNEL ENVIRONMENT VARIABLES
+The following
+.Xr kenv 2
+variables are available as
+.Xr loader 8
+tunables:
+.Bl -tag -width indent
+.It Va init_chroot
+If set to a valid directory in the root file system, it causes
+.Nm
+to perform a
+.Xr chroot 2
+operation on that directory, making it the new root directory.
+That happens before entering single-user mode or multi-user
+mode (but after executing the
+.Va init_script
+if enabled).
+This functionality has generally been eclipsed by rerooting.
+See
+.Xr reboot 8
+.Fl r
+for details.
+.It Va init_exec
+If set to a valid file name in the root file system,
+instructs
+.Nm
+to directly execute that file as the very first action,
+replacing
+.Nm
+as PID 1.
+.It Va init_script
+If set to a valid file name in the root file system,
+instructs
+.Nm
+to run that script as the very first action,
+before doing anything else.
+Signal handling and exit code interpretation is similar to
+running the
+.Pa /etc/rc
+script.
+In particular, single-user operation is enforced
+if the script terminates with a non-zero exit code,
+or if a SIGTERM is delivered to the
+.Nm
+process (PID 1).
+This functionality has generally been eclipsed by rerooting.
+See
+.Xr reboot 8
+.Fl r
+for details.
+.It Va init_shell
+Defines the shell binary to be used for executing the various shell scripts.
+The default is
+.Dq Li /bin/sh .
+It is used for running the
+.Va init_exec
+or
+.Va init_script
+if set, as well as for the
+.Pa /etc/rc
+and
+.Pa /etc/rc.shutdown
+scripts.
+The value of the corresponding
+.Xr kenv 2
+variable is evaluated every time
+.Nm
+calls a shell script, so it can be changed later on using the
+.Xr kenv 1
+utility.
+In particular, if a non-default shell is used for running an
+.Va init_script ,
+it might be desirable to have that script reset the value of
+.Va init_shell
+back to the default, so that the
+.Pa /etc/rc
+script is executed with the standard shell
+.Pa /bin/sh .
 .Sh FILES
 .Bl -tag -width /var/log/init.log -compact
 .It Pa /dev/console

Modified: stable/11/sbin/init/init.c
==============================================================================
--- stable/11/sbin/init/init.c	Sun Apr 21 03:54:49 2019	(r346478)
+++ stable/11/sbin/init/init.c	Sun Apr 21 04:00:19 2019	(r346479)
@@ -146,6 +146,7 @@ static state_t current_state = death_single;
 
 static void open_console(void);
 static const char *get_shell(void);
+static void replace_init(char *path);
 static void write_stderr(const char *message);
 
 typedef struct init_session {
@@ -327,6 +328,11 @@ invalid:
 	close(1);
 	close(2);
 
+	if (kenv(KENV_GET, "init_exec", kenv_value, sizeof(kenv_value)) > 0) {
+		replace_init(kenv_value);
+		_exit(0); /* reboot */
+	}
+
 	if (kenv(KENV_GET, "init_script", kenv_value, sizeof(kenv_value)) > 0) {
 		state_func_t next_transition;
 
@@ -965,7 +971,7 @@ single_user(void)
 		char name[] = "-sh";
 
 		argv[0] = name;
-		argv[1] = 0;
+		argv[1] = NULL;
 		execv(shell, argv);
 		emergency("can't exec %s for single user: %m", shell);
 		execv(_PATH_BSHELL, argv);
@@ -1045,6 +1051,22 @@ runcom(void)
 }
 
 /*
+ * Execute binary, replacing init(8) as PID 1.
+ */
+static void
+replace_init(char *path)
+{
+	char *argv[3];
+	char sh[] = "sh";
+
+	argv[0] = sh;
+	argv[1] = path;
+	argv[2] = NULL;
+
+	execute_script(argv);
+}
+
+/*
  * Run a shell script.
  * Returns 0 on success, otherwise the next transition to enter:
  *  - single_user if fork/execv/waitpid failed, or if the script
@@ -1055,7 +1077,7 @@ static state_func_t
 run_script(const char *script)
 {
 	pid_t pid, wpid;
-	int status;
+	int error, status;
 	char *argv[4];
 	const char *shell;
 	struct sigaction sa;
@@ -1077,13 +1099,28 @@ run_script(const char *script)
 		argv[0] = _sh;
 		argv[1] = __DECONST(char *, script);
 		argv[2] = runcom_mode == AUTOBOOT ? _autoboot : 0;
-		argv[3] = 0;
+		argv[3] = NULL;
 
 		sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0);
 
 #ifdef LOGIN_CAP
 		setprocresources(RESOURCE_RC);
 #endif
+
+		/*
+		 * Try to directly execute the script first.  If it
+		 * fails, try the old method of passing the script path
+		 * to sh(1).  Don't complain if it fails because of
+		 * the missing execute bit.
+		 */
+		error = access(script, X_OK);
+		if (error == 0) {
+			execv(script, argv + 1);
+			warning("can't exec %s: %m", script);
+		} else if (errno != EACCES) {
+			warning("can't access %s: %m", script);
+		}
+
 		execv(shell, argv);
 		stall("can't exec %s for %s: %m", shell, script);
 		_exit(1);	/* force single user mode */
@@ -1426,10 +1463,10 @@ start_window_system(session_t *sp)
 		strcpy(term, "TERM=");
 		strlcat(term, sp->se_type, sizeof(term));
 		env[0] = term;
-		env[1] = 0;
+		env[1] = NULL;
 	}
 	else
-		env[0] = 0;
+		env[0] = NULL;
 	execve(sp->se_window_argv[0], sp->se_window_argv, env);
 	stall("can't exec window system '%s' for port %s: %m",
 		sp->se_window_argv[0], sp->se_device);
@@ -1490,9 +1527,9 @@ start_getty(session_t *sp)
 		strcpy(term, "TERM=");
 		strlcat(term, sp->se_type, sizeof(term));
 		env[0] = term;
-		env[1] = 0;
+		env[1] = NULL;
 	} else
-		env[0] = 0;
+		env[0] = NULL;
 	execve(sp->se_getty_argv[0], sp->se_getty_argv, env);
 	stall("can't exec getty '%s' for port %s: %m",
 		sp->se_getty_argv[0], sp->se_device);
@@ -1851,7 +1888,7 @@ static int
 runshutdown(void)
 {
 	pid_t pid, wpid;
-	int status;
+	int error, status;
 	int shutdowntimeout;
 	size_t len;
 	char *argv[4];
@@ -1887,13 +1924,28 @@ runshutdown(void)
 		argv[0] = _sh;
 		argv[1] = _path_rundown;
 		argv[2] = Reboot ? _reboot : _single;
-		argv[3] = 0;
+		argv[3] = NULL;
 
 		sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0);
 
 #ifdef LOGIN_CAP
 		setprocresources(RESOURCE_RC);
 #endif
+
+		/*
+		 * Try to directly execute the script first.  If it
+		 * fails, try the old method of passing the script path
+		 * to sh(1).  Don't complain if it fails because of
+		 * the missing execute bit.
+		 */
+		error = access(_path_rundown, X_OK);
+		if (error == 0) {
+			execv(_path_rundown, argv + 1);
+			warning("can't exec %s: %m", _path_rundown);
+		} else if (errno != EACCES) {
+			warning("can't access %s: %m", _path_rundown);
+		}
+
 		execv(shell, argv);
 		warning("can't exec %s for %s: %m", shell, _PATH_RUNDOWN);
 		_exit(1);	/* force single user mode */

Modified: stable/11/stand/man/loader.8
==============================================================================
--- stable/11/stand/man/loader.8	Sun Apr 21 03:54:49 2019	(r346478)
+++ stable/11/stand/man/loader.8	Sun Apr 21 04:00:19 2019	(r346479)
@@ -244,10 +244,14 @@ If
 is specified, file sizes will be shown too.
 .Pp
 .It Ic lsdev Op Fl v
-Lists all of the devices from which it may be possible to load modules.
+Lists all of the devices from which it may be possible to load modules,
+as well as ZFS pools.
 If
 .Fl v
-is specified, more details are printed.
+is specified, more details are printed, including ZFS pool information
+in a format that resembles
+.Nm zpool Cm status
+output.
 .Pp
 .It Ic lsmod Op Fl v
 Displays loaded modules.
@@ -255,6 +259,14 @@ If
 .Fl v
 is specified, more details are shown.
 .Pp
+.It Ic lszfs Ar filesystem
+A ZFS extended command that can be used to explore the ZFS filesystem
+hierarchy in a pool.
+Lists the immediate children of the
+.Ar filesystem .
+The filesystem hierarchy is rooted at a filesystem with the same name
+as the pool.
+.Pp
 .It Ic more Ar file Op Ar
 Display the files specified, with a pause at each
 .Va LINES
@@ -478,20 +490,11 @@ directive from
 has been processed, allowing kernel panics that happen during the early stages
 of boot to be captured.
 .It Va init_chroot
-If set to a valid directory in the root file system, it causes
-.Xr init 8
-to perform a
-.Xr chroot 2
-operation on that directory, making it the new root directory.
-That happens before entering single-user mode or multi-user
-mode (but after executing the
-.Va init_script
-if enabled).
-This functionality has generally been eclipsed by rerooting.
 See
-.Xr reboot 8
-.Fl r
-for details.
+.Xr init 8 .
+.It Va init_exec
+See
+.Xr init 8 .
 .It Va init_path
 Sets the list of binaries which the kernel will try to run as the initial
 process.
@@ -499,51 +502,11 @@ The first matching binary is used.
 The default list is
 .Dq Li /sbin/init:/sbin/oinit:/sbin/init.bak:\:/rescue/init .
 .It Va init_script
-If set to a valid file name in the root file system,
-instructs
-.Xr init 8
-to run that script as the very first action,
-before doing anything else.
-Signal handling and exit code interpretation is similar to
-running the
-.Pa /etc/rc
-script.
-In particular, single-user operation is enforced
-if the script terminates with a non-zero exit code,
-or if a SIGTERM is delivered to the
-.Xr init 8
-process (PID 1).
-This functionality has generally been eclipsed by rerooting.
 See
-.Xr reboot 8
-.Fl r
-for details.
+.Xr init 8 .
 .It Va init_shell
-Defines the shell binary to be used for executing the various shell scripts.
-The default is
-.Dq Li /bin/sh .
-It is used for running the
-.Va init_script
-if set, as well as for the
-.Pa /etc/rc
-and
-.Pa /etc/rc.shutdown
-scripts.
-The value of the corresponding
-.Xr kenv 2
-variable is evaluated every time
-.Xr init 8
-calls a shell script, so it can be changed later on using the
-.Xr kenv 1
-utility.
-In particular, if a non-default shell is used for running an
-.Va init_script ,
-it might be desirable to have that script reset the value of
-.Va init_shell
-back to the default, so that the
-.Pa /etc/rc
-script is executed with the standard shell
-.Pa /bin/sh .
+See
+.Xr init 8 .
 .It Va interpret
 Has the value
 .Dq Li OK
@@ -718,6 +681,29 @@ Modifies
 and
 .Dv VM_KMEM_SIZE_MAX .
 .El
+.Ss ZFS FEATURES
+.Nm
+supports the following format for specifying ZFS filesystems which
+can be used wherever
+.Xr loader 8
+refers to a device specification:
+.Pp
+.Ar zfs:pool/filesystem:
+.Pp
+where
+.Pa pool/filesystem
+is a ZFS filesystem name as described in
+.Xr zfs 8 .
+.Pp
+If
+.Pa /etc/fstab
+does not have an entry for the root filesystem and
+.Va vfs.root.mountfrom
+is not set, but
+.Va currdev
+refers to a ZFS filesystem, then
+.Nm
+will instruct kernel to use that filesystem as the root filesystem.
 .Ss BUILTIN PARSER
 When a builtin command is executed, the rest of the line is taken
 by it as arguments, and it is processed by a special parser which
@@ -959,9 +945,44 @@ version at compile time.
 .Nm
 version.
 .El
-.Ss SYSTEM DOCUMENTATION
+.Sh SECURITY
+Access to the
+.Nm
+command line provides several ways of compromising system security,
+including, but not limited to:
+.Pp
+.Bl -bullet
+.It
+Booting from removable storage, by setting the
+.Va currdev
+or
+.Va loaddev
+variables
+.It
+Executing binary of choice, by setting the
+.Va init_path
+or
+.Va init_script
+variables
+.It
+Overriding ACPI DSDT to inject arbitrary code into the ACPI subsystem
+.El
+.Pp
+One can prevent unauthorized access
+to the
+.Nm
+command line by setting the
+.Va password ,
+or setting
+.Va autoboot_delay
+to -1.
+See
+.Xr loader.conf 5
+for details.
+In order for this to be effective, one should also configure the firmware
+(BIOS or UEFI) to prevent booting from unauthorized devices.
 .Sh FILES
-.Bl -tag -width /boot/defaults/loader.conf -compact
+.Bl -tag -width /usr/share/examples/bootforth/ -compact
 .It Pa /boot/loader
 .Nm
 itself.
@@ -970,6 +991,8 @@ Additional
 .Tn FICL
 initialization.
 .It Pa /boot/defaults/loader.conf
+.It Pa /boot/loader.4th
+Extra builtin-like words.
 .It Pa /boot/loader.conf
 .It Pa /boot/loader.conf.local
 .Nm
@@ -982,6 +1005,11 @@ bootstrapping script.
 Loaded by
 .Ic help .
 Contains the help messages.
+.It Pa /boot/support.4th
+.Pa loader.conf
+processing words.
+.It Pa /usr/share/examples/bootforth/
+Assorted examples.
 .El
 .Sh EXAMPLES
 Boot in single user mode:
@@ -1007,16 +1035,11 @@ set root_disk_unit=2
 boot /boot/kernel/kernel
 .Ed
 .Pp
-See also:
-.Bl -tag -width /usr/share/examples/bootforth/X
-.It Pa /boot/loader.4th
-Extra builtin-like words.
-.It Pa /boot/support.4th
-.Pa loader.conf
-processing words.
-.It Pa /usr/share/examples/bootforth/
-Assorted examples.
-.El
+Set the default device used for loading a kernel from a ZFS filesystem:
+.Bd -literal -offset indent
+set currdev=zfs:tank/ROOT/knowngood:
+.Ed
+.Pp
 .Sh ERRORS
 The following values are thrown by
 .Nm :
@@ -1042,52 +1065,6 @@ executed.
 .It -259
 Unspecified error.
 .El
-.Sh ZFS FEATURES
-.Nm
-supports the following format for specifying ZFS filesystems which
-can be used wherever
-.Xr loader 8
-refers to a device specification:
-.Pp
-.Ar zfs:pool/filesystem:
-.Pp
-where
-.Pa pool/filesystem
-is a ZFS filesystem name as described in
-.Xr zfs 8 .
-.Pp
-If
-.Pa /etc/fstab
-does not have an entry for the root filesystem and
-.Va vfs.root.mountfrom
-is not set, but
-.Va currdev
-refers to a ZFS filesystem, then
-.Nm
-will instruct kernel to use that filesystem as the root filesystem.
-.Sh ZFS COMMAND EXTENSIONS
-.Bl -tag -width Ds -compact
-.It Ic lsdev Op Fl v
-Lists ZFS pools in addition to disks and partitions.
-Adding
-.Fl v
-shows more ZFS pool details in a format that resembles
-.Nm zpool Cm status
-output.
-.Pp
-.It Ic lszfs Ar filesystem
-A ZFS extended command that can be used to explore the ZFS filesystem
-hierarchy in a pool.
-Lists the immediate children of the
-.Ar filesystem .
-The filesystem hierarchy is rooted at a filesystem with the same name
-as the pool.
-.El
-.Sh EXAMPLES
-Set the default device used for loading a kernel from a ZFS filesystem:
-.Bd -literal -offset indent
-set currdev=zfs:tank/ROOT/knowngood:
-.Ed
 .Sh SEE ALSO
 .Xr libstand 3 ,
 .Xr loader.conf 5 ,





Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201904210400.x3L40JGZ031625>