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
This commit is contained in:
Sebastien Bini 2022-10-11 09:48:04 +02:00 committed by Wojciech Macek
parent 91a84eb5ba
commit f3dba162bd

View File

@ -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 */
}