git: 276d9b88a9e6 - main - jail: avoid leaking jail config fds to exec.* hooks

From: Kyle Evans <kevans_at_FreeBSD.org>
Date: Wed, 06 May 2026 23:29:16 UTC
The branch main has been updated by kevans:

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

commit 276d9b88a9e6fd6fd90e57c36444756ad297d2ab
Author:     Jan Bramkamp <crest+freebsd@rlwinm.de>
AuthorDate: 2026-05-06 23:28:53 +0000
Commit:     Kyle Evans <kevans@FreeBSD.org>
CommitDate: 2026-05-06 23:28:53 +0000

    jail: avoid leaking jail config fds to exec.* hooks
    
    The jail(8) command must not leave parsed configuration files open
    since the file descriptors will be leaked to child processes
    including the untrusted exec.start or exec.stop hooks.
    
    While fopen() doesn't provide direct access to O_CLOEXEC, it does
    provide access to FD_CLOEXEC via "e" in the mode string which
    provides the desired defense in depth against leaking file descriptors
    into exec.* hooks since those always execve() into a shell.
    
    Jail configuration is potentially sensitive and some hooks execute from
    within the jail context, leaving some opening for the jail to exfiltrate
    information about the host environment.
    
    (Commit message wordsmithed by kevans)
    
    PR:             295052
    Reviewed by:    kevans
    MFC after:      3 days
---
 usr.sbin/jail/config.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/usr.sbin/jail/config.c b/usr.sbin/jail/config.c
index 1bad04ccde68..f1e2da215790 100644
--- a/usr.sbin/jail/config.c
+++ b/usr.sbin/jail/config.c
@@ -321,6 +321,7 @@ static void
 parse_config(const char *cfname, int is_stdin)
 {
 	struct cflex cflex = {.cfname = cfname, .error = 0};
+	FILE *yfp = NULL;
 	void *scanner;
 
 	yylex_init_extra(&cflex, &scanner);
@@ -328,7 +329,7 @@ parse_config(const char *cfname, int is_stdin)
 		cflex.cfname = "STDIN";
 		yyset_in(stdin, scanner);
 	} else {
-		FILE *yfp = fopen(cfname, "r");
+		yfp = fopen(cfname, "re");
 		if (!yfp)
 			err(1, "%s", cfname);
 		yyset_in(yfp, scanner);
@@ -336,6 +337,8 @@ parse_config(const char *cfname, int is_stdin)
 	if (yyparse(scanner) || cflex.error)
 		exit(1);
 	yylex_destroy(scanner);
+	if (yfp != NULL)
+		fclose(yfp);
 }
 
 /*