git: f3dba162bd46 - main - init: allow to start script executions with sh -o verify

From: Wojciech Macek <wma_at_FreeBSD.org>
Date: Tue, 11 Oct 2022 07:51:54 UTC
The branch main has been updated by wma:

URL: https://cgit.FreeBSD.org/src/commit/?id=f3dba162bd46cd851a5491db680c1e6292c15a39

commit f3dba162bd46cd851a5491db680c1e6292c15a39
Author:     Sebastien Bini <Sebastien.BINI@stormshield.eu>
AuthorDate: 2022-10-11 07:48:04 +0000
Commit:     Wojciech Macek <wma@FreeBSD.org>
CommitDate: 2022-10-11 07:48:04 +0000

    init: allow to start script executions with sh -o verify
    
    On systems where mac_veriexec is enforced, init should run its scripts in verified mode.
    This relies on the verify shell option introduced by D30464. init will detect if the shell
    is /bin/sh, and in which case, add the verify option to the argument vector.
    The verify option propagates to all files sourced by the shell, ensuring a better
    protection than if the script was tested against an open(O_VERIFY) before running it.
    This security can be bypassed with the kenv which overloads the shell to use.
    However we feel confident that on systems running with mac_veriexec, this kenv will be blocked somehow.
    Also, the verify option has no effect on systems where mac_veriexec is not loaded nor enforced.
    
    Differential revision:  https://reviews.freebsd.org/D34622
    Reviewed by:            sjg, wma
---
 sbin/init/init.c | 53 ++++++++++++++++++++++++++++++-----------------------
 1 file changed, 30 insertions(+), 23 deletions(-)

diff --git a/sbin/init/init.c b/sbin/init/init.c
index cf48721faf8d..dd8800d21910 100644
--- a/sbin/init/init.c
+++ b/sbin/init/init.c
@@ -99,6 +99,7 @@ static const char rcsid[] =
 #define	RESOURCE_RC		"daemon"
 #define	RESOURCE_WINDOW		"default"
 #define	RESOURCE_GETTY		"default"
+#define SCRIPT_ARGV_SIZE 3 /* size of argv passed to execute_script, can be increased if needed */
 
 static void handle(sig_t, ...);
 static void delset(sigset_t *, ...);
@@ -1044,8 +1045,9 @@ static void
 execute_script(char *argv[])
 {
 	struct sigaction sa;
+	char* sh_argv[3 + SCRIPT_ARGV_SIZE];
 	const char *shell, *script;
-	int error;
+	int error, sh_argv_len, i;
 
 	bzero(&sa, sizeof(sa));
 	sigemptyset(&sa.sa_mask);
@@ -1066,17 +1068,28 @@ execute_script(char *argv[])
 	 * to sh(1).  Don't complain if it fails because of
 	 * the missing execute bit.
 	 */
-	script = argv[1];
+	script = argv[0];
 	error = access(script, X_OK);
 	if (error == 0) {
-		execv(script, argv + 1);
+		execv(script, argv);
 		warning("can't directly exec %s: %m", script);
 	} else if (errno != EACCES) {
 		warning("can't access %s: %m", script);
 	}
 
 	shell = get_shell();
-	execv(shell, argv);
+	sh_argv[0] = __DECONST(char*, shell);
+	sh_argv_len = 1;
+#ifdef SECURE
+	if (strcmp(shell, _PATH_BSHELL) == 0) {
+		sh_argv[1] = __DECONST(char*, "-o");
+		sh_argv[2] = __DECONST(char*, "verify");
+		sh_argv_len = 3;
+	}
+#endif
+	for (i = 0; i != SCRIPT_ARGV_SIZE; ++i)
+		sh_argv[i + sh_argv_len] = argv[i];
+	execv(shell, sh_argv);
 	stall("can't exec %s for %s: %m", shell, script);
 }
 
@@ -1086,12 +1099,10 @@ execute_script(char *argv[])
 static void
 replace_init(char *path)
 {
-	char *argv[3];
-	char sh[] = "sh";
+	char *argv[SCRIPT_ARGV_SIZE];
 
-	argv[0] = sh;
-	argv[1] = path;
-	argv[2] = NULL;
+	argv[0] = path;
+	argv[1] = NULL;
 
 	execute_script(argv);
 }
@@ -1108,20 +1119,18 @@ run_script(const char *script)
 {
 	pid_t pid, wpid;
 	int status;
-	char *argv[4];
+	char *argv[SCRIPT_ARGV_SIZE];
 	const char *shell;
 
 	shell = get_shell();
 
 	if ((pid = fork()) == 0) {
 
-		char _sh[]		= "sh";
-		char _autoboot[]	= "autoboot";
+		char _autoboot[] = "autoboot";
 
-		argv[0] = _sh;
-		argv[1] = __DECONST(char *, script);
-		argv[2] = runcom_mode == AUTOBOOT ? _autoboot : 0;
-		argv[3] = NULL;
+		argv[0] = __DECONST(char *, script);
+		argv[1] = runcom_mode == AUTOBOOT ? _autoboot : NULL;
+		argv[2] = NULL;
 
 		execute_script(argv);
 		sleep(STALL_TIMEOUT);
@@ -1957,7 +1966,7 @@ runshutdown(void)
 	int status;
 	int shutdowntimeout;
 	size_t len;
-	char *argv[4];
+	char *argv[SCRIPT_ARGV_SIZE];
 	struct stat sb;
 
 	BOOTTRACE("init(8): start rc.shutdown");
@@ -1972,16 +1981,14 @@ runshutdown(void)
 		return 0;
 
 	if ((pid = fork()) == 0) {
-		char _sh[]	= "sh";
 		char _reboot[]	= "reboot";
 		char _single[]	= "single";
 		char _path_rundown[] = _PATH_RUNDOWN;
 
-		argv[0] = _sh;
-		argv[1] = _path_rundown;
-		argv[2] = Reboot ? _reboot : _single;
-		argv[3] = NULL;
-		
+		argv[0] = _path_rundown;
+		argv[1] = Reboot ? _reboot : _single;
+		argv[2] = NULL;
+
 		execute_script(argv);
 		_exit(1);	/* force single user mode */
 	}