git: a371b008d13f - main - Add boot_setenv

From: Simon J. Gerraty <sjg_at_FreeBSD.org>
Date: Tue, 07 Apr 2026 16:30:00 UTC
The branch main has been updated by sjg:

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

commit a371b008d13f84cf4448bf6d700641ecc15c206b
Author:     Simon J. Gerraty <sjg@FreeBSD.org>
AuthorDate: 2026-04-07 16:29:07 +0000
Commit:     Simon J. Gerraty <sjg@FreeBSD.org>
CommitDate: 2026-04-07 16:29:07 +0000

    Add boot_setenv
    
    Move is_restricted_var() to libsa/environment.c so it can be leveraged
    by boot_setenv called from subr_boot with not truted input.
    
    Also, allow for local tuning via ENV_IS_RESTRICTED_ALLOWED_LIST and
    ENV_IS_RESTRICTED_LIST
    
    Sponsored by:   Hewlett Packard Enterprise Development LP.
    
    Reviewed by:    kevans, imp
    Differential Revision:  https://reviews.freebsd.org/D56287
---
 stand/common/commands.c   | 60 ++-----------------------------------
 stand/efi/loader/main.c   |  5 ++++
 stand/libsa/environment.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++
 stand/libsa/stand.h       |  3 ++
 sys/kern/subr_boot.c      |  2 +-
 5 files changed, 88 insertions(+), 58 deletions(-)

diff --git a/stand/common/commands.c b/stand/common/commands.c
index 19452047a0ca..41687ece42fd 100644
--- a/stand/common/commands.c
+++ b/stand/common/commands.c
@@ -291,63 +291,6 @@ command_show(int argc, char *argv[])
 	return (CMD_OK);
 }
 
-#ifdef LOADER_VERIEXEC
-static int
-is_restricted_var(const char *var)
-{
-	/*
-	 * We impose restrictions if input is not verified
-	 * allowing for exceptions.
-	 * These entries should include the '='
-	 */
-	const char *allowed[] = {
-		"boot_function=",
-		"boot_phase=",
-		"boot_recover_cli=",
-		"boot_recover_volume=",
-		"boot_safe=",
-		"boot_set=",
-		"boot_single=",
-		"boot_verbose=",
-		NULL,
-	};
-	const char *restricted[] = {
-		"boot",
-		"init",
-		"loader.ve.",
-		"rootfs",
-		"secur",
-		"vfs.",
-		NULL,
-	};
-	const char **cp;
-	int ok = -1;
-
-#ifdef LOADER_VERIEXEC_TESTING
-	printf("Checking: %s\n", var);
-#endif
-	for (cp = restricted; *cp; cp++) {
-		if (strncmp(var, *cp, strlen(*cp)) == 0) {
-			ok = 0;
-			break;
-		}
-	}
-	if (!ok) {
-		/*
-		 * Check for exceptions.
-		 * These should match up to '='.
-		 */
-		for (cp = allowed; *cp; cp++) {
-			if (strncmp(var, *cp, strlen(*cp)) == 0) {
-				ok = 1;
-				break;
-			}
-		}
-	}
-	return (ok == 0);
-}
-#endif
-
 COMMAND_SET(set, "set", "set a variable", command_set);
 
 static int
@@ -364,6 +307,9 @@ command_set(int argc, char *argv[])
 
 		ves = ve_status_get(-1);
 		if (ves == VE_UNVERIFIED_OK) {
+#ifdef LOADER_VERIEXEC_TESTING
+			printf("Checking: %s\n", var);
+#endif
 			if (is_restricted_var(argv[1])) {
 				printf("Ignoring restricted variable: %s\n",
 				    argv[1]);
diff --git a/stand/efi/loader/main.c b/stand/efi/loader/main.c
index 1fd6c8d74195..e54f3e1f9f35 100644
--- a/stand/efi/loader/main.c
+++ b/stand/efi/loader/main.c
@@ -1242,6 +1242,11 @@ main(int argc, CHAR16 *argv[])
 	/* Report the RSDP early. */
 	acpi_detect();
 
+#ifdef LOADER_VERIEXEC
+	/* tell boot_setenv to be careful */
+	set_check_restricted(true);
+#endif
+
 	/*
 	 * Chicken-and-egg problem; we want to have console output early, but
 	 * some console attributes may depend on reading from eg. the boot
diff --git a/stand/libsa/environment.c b/stand/libsa/environment.c
index d139249a8e84..3882db6ee9e1 100644
--- a/stand/libsa/environment.c
+++ b/stand/libsa/environment.c
@@ -222,6 +222,82 @@ env_noset(struct env_var *ev __unused, int flags __unused,
 	return (EPERM);
 }
 
+bool
+is_restricted_var(const char *name)
+{
+	/*
+	 * We impose restrictions if input is not verified/trusted
+	 * allowing for exceptions.
+	 * These entries should probably include the '='
+	 */
+	const char *allowed[] = {
+		"boot_function=",
+		"boot_phase=",
+		"boot_recover_cli=",
+		"boot_recover_volume=",
+		"boot_safe=",
+		"boot_set=",
+		"boot_single=",
+		"boot_verbose=",
+#ifdef ENV_IS_RESTRICTED_ALLOWED_LIST
+		ENV_IS_RESTRICTED_ALLOWED_LIST,
+#endif
+		NULL,
+	};
+	/*
+	 * These are prefixes we want to be careful with.
+	 */
+	const char *restricted[] = {
+		"boot",
+		"init",
+		"loader.ve.",
+		"rootfs",
+		"secur",
+		"vfs.",
+#ifdef ENV_IS_RESTRICTED_LIST
+		ENV_IS_RESTRICTED_LIST,
+#endif
+		NULL,
+	};
+	const char **cp;
+	int ok = -1;
+	
+	for (cp = restricted; *cp; cp++) {
+		if (strncmp(name, *cp, strlen(*cp)) == 0) {
+			ok = 0;
+			break;
+		}
+	}
+	if (!ok) {
+		for (cp = allowed; *cp; cp++) {
+			if (strncmp(name, *cp, strlen(*cp)) == 0) {
+				ok = 1;
+				break;
+			}
+		}
+	}
+	return (ok == 0);
+}
+
+static bool check_restricted = false;
+
+void
+set_check_restricted(bool b)
+{
+	check_restricted = b;
+}
+
+/* called from subr_boot with not quite trusted input */
+int
+boot_setenv(const char *name, const char *value)
+{
+	if (check_restricted && is_restricted_var(name)) {
+		errno = EPERM;
+		return -1;
+	}
+	return setenv(name, value, 1);
+}
+
 int
 env_nounset(struct env_var *ev __unused)
 {
diff --git a/stand/libsa/stand.h b/stand/libsa/stand.h
index aaba0aa7fb39..4f7f21867cea 100644
--- a/stand/libsa/stand.h
+++ b/stand/libsa/stand.h
@@ -381,6 +381,9 @@ extern int		setenv(const char *name, const char *value,
 			       int overwrite);
 extern int		putenv(char *string);
 extern int		unsetenv(const char *name);
+extern bool		is_restricted_var(const char *name);
+extern void		set_check_restricted(bool);
+extern int		boot_setenv(const char *name, const char *value);
 
 extern ev_sethook_t	env_noset;		/* refuse set operation */
 extern ev_unsethook_t	env_nounset;		/* refuse unset operation */
diff --git a/sys/kern/subr_boot.c b/sys/kern/subr_boot.c
index b721abf7013c..00c8e66617b8 100644
--- a/sys/kern/subr_boot.c
+++ b/sys/kern/subr_boot.c
@@ -53,7 +53,7 @@
 #define GETENV(k)	kern_getenv(k)
 #define FREE(v)		freeenv(v)
 #else	/* Boot loader */
-#define SETENV(k, v)	setenv(k, v, 1)
+#define SETENV(k, v)	boot_setenv(k, v)
 #define GETENV(k)	getenv(k)
 #define	FREE(v)
 #endif