util-linux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] Consistent shell resolution across util-linux
@ 2025-11-23 15:32 Alessandro Ratti
  2025-11-23 15:32 ` [PATCH 1/3] lib: introduce ul_default_shell() for consistent shell resolution Alessandro Ratti
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Alessandro Ratti @ 2025-11-23 15:32 UTC (permalink / raw)
  To: util-linux; +Cc: kzak, thomas, Alessandro Ratti

This patch series addresses inconsistent default shell handling across
util-linux tools, which caused user-reported data loss when script(1)
defaulted to /bin/sh without respecting the user's configured shell.

The series:
1. Introduces ul_default_shell() for consistent shell resolution
2. Updates interactive tools to use the new function
3. Standardizes _PATH_BSHELL usage in security-sensitive tools

This implements the solution discussed in:
https://github.com/util-linux/util-linux/issues/3865

Alessandro Ratti (3):
  lib: introduce ul_default_shell() for consistent shell resolution
  *: use ul_default_shell() for interactive shell spawning
  login-utils, sys-utils: use _PATH_BSHELL consistently

 include/shells.h        |  6 ++++++
 lib/Makemodule.am       |  1 +
 lib/exec_shell.c        |  8 ++------
 lib/shells.c            | 26 ++++++++++++++++++++++++++
 login-utils/su-common.c |  5 +----
 login-utils/sulogin.c   |  9 +++++----
 sys-utils/flock.c       |  8 +++++---
 sys-utils/setpriv.c     |  6 ++----
 term-utils/script.c     |  5 ++---
 term-utils/scriptlive.c |  5 ++---
 text-utils/more.c       |  4 ++--
 11 files changed, 54 insertions(+), 29 deletions(-)

-- 
2.51.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/3] lib: introduce ul_default_shell() for consistent shell resolution
  2025-11-23 15:32 [PATCH 0/3] Consistent shell resolution across util-linux Alessandro Ratti
@ 2025-11-23 15:32 ` Alessandro Ratti
  2025-11-23 15:32 ` [PATCH 2/3] *: use ul_default_shell() for interactive shell spawning Alessandro Ratti
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: Alessandro Ratti @ 2025-11-23 15:32 UTC (permalink / raw)
  To: util-linux; +Cc: kzak, thomas, Alessandro Ratti

Add a new ul_default_shell() function to provide consistent shell
resolution across util-linux tools. The function follows a priority
order: $SHELL environment variable, user's shell from passwd database,
and finally _PATH_BSHELL as fallback.

The function supports flags to control its behavior:
- UL_SHELL_NOENV: skip $SHELL environment variable check
- UL_SHELL_NOPWD: skip passwd database lookup

This addresses the issue where tools like script(1) would default to
/bin/sh without respecting the user's configured shell, potentially
causing data loss.

Addresses: https://github.com/util-linux/util-linux/issues/3865
Suggested-by: Karel Zak <kzak@redhat.com>
Suggested-by: Thomas Weißschuh <thomas@t-8ch.de>
Signed-off-by: Alessandro Ratti <alessandro@0x65c.net>
---
 include/shells.h  |  6 ++++++
 lib/Makemodule.am |  1 +
 lib/shells.c      | 26 ++++++++++++++++++++++++++
 3 files changed, 33 insertions(+)

diff --git a/include/shells.h b/include/shells.h
index c770a13ba..eca2b4187 100644
--- a/include/shells.h
+++ b/include/shells.h
@@ -4,7 +4,13 @@
 #ifndef UTIL_LINUX_SHELLS_H
 #define UTIL_LINUX_SHELLS_H
 
+#define UL_SHELL_NOENV  (1 << 0)
+#define UL_SHELL_NOPWD  (1 << 1)
+
 extern void print_shells(FILE *out, const char *format);
 extern int is_known_shell(const char *shell_name);
 
+struct passwd;
+const char *ul_default_shell(int flags, const struct passwd *pw);
+
 #endif /* UTIL_LINUX_SHELLS_H */
diff --git a/lib/Makemodule.am b/lib/Makemodule.am
index a9da57734..1d598faa2 100644
--- a/lib/Makemodule.am
+++ b/lib/Makemodule.am
@@ -49,6 +49,7 @@ libcommon_la_SOURCES = \
 if LINUX
 libcommon_la_SOURCES += \
 	lib/linux_version.c \
+	lib/shells.c \
 	lib/loopdev.c
 endif
 
diff --git a/lib/shells.c b/lib/shells.c
index 13f293c5e..ef2aecd0f 100644
--- a/lib/shells.c
+++ b/lib/shells.c
@@ -1,6 +1,11 @@
 /*
  * SPDX-License-Identifier: GPL-2.0-or-later
  */
+#include <sys/types.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <paths.h>
+#include <unistd.h>
 #include <sys/syslog.h>
 #if defined (HAVE_LIBECONF) && defined (USE_VENDORDIR)
 #include <libeconf.h>
@@ -116,3 +121,24 @@ extern int is_known_shell(const char *shell_name)
 #endif
 	return ret;
 }
+
+const char *ul_default_shell(int flags, const struct passwd *pw)
+{
+	const char *shell = NULL;
+
+	if (!(flags & UL_SHELL_NOENV)) {
+		shell = getenv("SHELL");
+		if (shell && *shell)
+			return shell;
+	}
+	if (!(flags & UL_SHELL_NOPWD)) {
+		if (!pw)
+			pw = getpwuid(getuid());
+		if (pw)
+			shell = pw->pw_shell;
+		if (shell && *shell)
+			return shell;
+	}
+
+	return _PATH_BSHELL;
+}
-- 
2.51.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/3] *: use ul_default_shell() for interactive shell spawning
  2025-11-23 15:32 [PATCH 0/3] Consistent shell resolution across util-linux Alessandro Ratti
  2025-11-23 15:32 ` [PATCH 1/3] lib: introduce ul_default_shell() for consistent shell resolution Alessandro Ratti
@ 2025-11-23 15:32 ` Alessandro Ratti
  2025-11-23 15:32 ` [PATCH 3/3] login-utils, sys-utils: use _PATH_BSHELL consistently Alessandro Ratti
  2025-11-26 11:51 ` [PATCH 0/3] Consistent shell resolution across util-linux Karel Zak
  3 siblings, 0 replies; 6+ messages in thread
From: Alessandro Ratti @ 2025-11-23 15:32 UTC (permalink / raw)
  To: util-linux; +Cc: kzak, thomas, Alessandro Ratti

Update tools that spawn interactive shells to use ul_default_shell()
for consistent shell resolution. This ensures these tools respect both
$SHELL and the user's configured shell from the passwd database before
falling back to _PATH_BSHELL.

Affected tools:
- script(1): fixes history truncation when invoked without $SHELL
- scriptlive(1): consistent with script(1) behavior
- flock(1): for -c command execution
- more(1): for shell escape feature
- exec_shell (used by unshare(1) and nsenter(1))

This change addresses user reports of data loss due to tools defaulting
to /bin/sh instead of the user's configured shell, particularly affecting
command history with different HISTSIZE configurations.

Addresses: https://github.com/util-linux/util-linux/issues/3865
Signed-off-by: Alessandro Ratti <alessandro@0x65c.net>
---
 lib/exec_shell.c        | 8 ++------
 sys-utils/flock.c       | 8 +++++---
 term-utils/script.c     | 5 ++---
 term-utils/scriptlive.c | 5 ++---
 text-utils/more.c       | 4 ++--
 5 files changed, 13 insertions(+), 17 deletions(-)

diff --git a/lib/exec_shell.c b/lib/exec_shell.c
index ffe65f006..8f10ea4df 100644
--- a/lib/exec_shell.c
+++ b/lib/exec_shell.c
@@ -26,19 +26,15 @@
 #include "xalloc.h"
 
 #include "exec_shell.h"
-
-#define DEFAULT_SHELL "/bin/sh"
+#include "shells.h"
 
 void __attribute__((__noreturn__)) exec_shell(void)
 {
-	const char *shell = getenv("SHELL");
+	const char *shell = ul_default_shell(0, NULL);
 	char *shellc;
 	const char *shell_basename;
 	char *arg0;
 
-	if (!shell)
-		shell = DEFAULT_SHELL;
-
 	shellc = xstrdup(shell);
 	shell_basename = basename(shellc);
 	xasprintf(&arg0, "-%s", shell_basename);
diff --git a/sys-utils/flock.c b/sys-utils/flock.c
index fe1a71f72..a6883ac77 100644
--- a/sys-utils/flock.c
+++ b/sys-utils/flock.c
@@ -47,6 +47,7 @@
 #include "closestream.h"
 #include "monotonic.h"
 #include "timer.h"
+#include "shells.h"
 
 #ifndef F_OFD_GETLK
 #define F_OFD_GETLK	36
@@ -207,6 +208,8 @@ int main(int argc, char *argv[])
 	int conflict_exit_code = 1;
 	char **cmd_argv = NULL, *sh_c_argv[4];
 	const char *filename = NULL;
+	const char *shell = NULL;
+
 	enum {
 		OPT_VERBOSE = CHAR_MAX + 1,
 		OPT_FCNTL,
@@ -237,6 +240,7 @@ int main(int argc, char *argv[])
 	bindtextdomain(PACKAGE, LOCALEDIR);
 	textdomain(PACKAGE);
 	close_stdout_atexit();
+	shell = ul_default_shell(0, NULL); /* Used for -c command execution */
 
 	strutils_set_exitcode(EX_USAGE);
 
@@ -327,9 +331,7 @@ int main(int argc, char *argv[])
 				     _("%s requires exactly one command argument"),
 				     argv[optind + 1]);
 			cmd_argv = sh_c_argv;
-			cmd_argv[0] = getenv("SHELL");
-			if (!cmd_argv[0] || !*cmd_argv[0])
-				cmd_argv[0] = _PATH_BSHELL;
+			cmd_argv[0] = (char *)shell;
 			cmd_argv[1] = "-c";
 			cmd_argv[2] = argv[optind + 2];
 			cmd_argv[3] = NULL;
diff --git a/term-utils/script.c b/term-utils/script.c
index ff7f4409f..4e302347f 100644
--- a/term-utils/script.c
+++ b/term-utils/script.c
@@ -70,6 +70,7 @@
 #include "signames.h"
 #include "pty-session.h"
 #include "debug.h"
+#include "shells.h"
 
 static UL_DEBUG_DEFINE_MASK(script);
 UL_DEBUG_DEFINE_MASKNAMES(script) = UL_DEBUG_EMPTY_MASKNAMES;
@@ -966,9 +967,7 @@ int main(int argc, char **argv)
 			log_associate(&ctl, &ctl.in, timingfile, format);
 	}
 
-	shell = getenv("SHELL");
-	if (!shell)
-		shell = _PATH_BSHELL;
+	shell = ul_default_shell(0, NULL);
 
 	ctl.pty = ul_new_pty(ctl.isterm);
 	if (!ctl.pty)
diff --git a/term-utils/scriptlive.c b/term-utils/scriptlive.c
index e4a3434ed..6ac685506 100644
--- a/term-utils/scriptlive.c
+++ b/term-utils/scriptlive.c
@@ -38,6 +38,7 @@
 #include "pty-session.h"
 #include "script-playutils.h"
 #include "monotonic.h"
+#include "shells.h"
 
 
 #define SCRIPT_MIN_DELAY 0.0001		/* from original scriptreplay.pl */
@@ -281,9 +282,7 @@ main(int argc, char *argv[])
 		replay_set_delay_max(ss.setup, &maxdelay);
 	replay_set_delay_min(ss.setup, &mindelay);
 
-	shell = getenv("SHELL");
-	if (shell == NULL)
-		shell = _PATH_BSHELL;
+	shell = ul_default_shell(0, NULL);
 
 	fprintf(stdout, _(">>> scriptlive: Starting your typescript execution by %s.\n"),
 			command ? command : shell);
diff --git a/text-utils/more.c b/text-utils/more.c
index 4980aef4c..4e3cae565 100644
--- a/text-utils/more.c
+++ b/text-utils/more.c
@@ -89,6 +89,7 @@
 #include "widechar.h"
 #include "closestream.h"
 #include "env.h"
+#include "shells.h"
 
 #ifdef TEST_PROGRAM
 # define NON_INTERACTIVE_MORE 1
@@ -2110,8 +2111,7 @@ static void initterm(struct more_control *ctl)
 	if ((ctl->backspace_ch = tigetstr(TERM_BACKSPACE)) == NULL)
 		ctl->backspace_ch = BACKSPACE;
 
-	if ((ctl->shell = getenv("SHELL")) == NULL)
-		ctl->shell = _PATH_BSHELL;
+	ctl->shell = (char *)ul_default_shell(0, NULL);
 }
 
 int main(int argc, char **argv)
-- 
2.51.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/3] login-utils, sys-utils: use _PATH_BSHELL consistently
  2025-11-23 15:32 [PATCH 0/3] Consistent shell resolution across util-linux Alessandro Ratti
  2025-11-23 15:32 ` [PATCH 1/3] lib: introduce ul_default_shell() for consistent shell resolution Alessandro Ratti
  2025-11-23 15:32 ` [PATCH 2/3] *: use ul_default_shell() for interactive shell spawning Alessandro Ratti
@ 2025-11-23 15:32 ` Alessandro Ratti
  2025-11-26 11:51 ` [PATCH 0/3] Consistent shell resolution across util-linux Karel Zak
  3 siblings, 0 replies; 6+ messages in thread
From: Alessandro Ratti @ 2025-11-23 15:32 UTC (permalink / raw)
  To: util-linux; +Cc: kzak, thomas, Alessandro Ratti

Remove local DEFAULT_SHELL definitions and hardcoded "/bin/sh" strings
in favor of the standard _PATH_BSHELL macro from <paths.h>. This
provides consistency across the codebase while following libc
conventions.

These tools already perform their own passwd lookups and only need a
fallback value, so they don't require the full ul_default_shell()
resolution logic.

Affected tools:
- su(1): already checks pw_shell validity
- sulogin(8): emergency login with explicit shell handling
- setpriv(1): already has passwd entry for environment setup

Signed-off-by: Alessandro Ratti <alessandro@0x65c.net>
---
 login-utils/su-common.c | 5 +----
 login-utils/sulogin.c   | 9 +++++----
 sys-utils/setpriv.c     | 6 ++----
 3 files changed, 8 insertions(+), 12 deletions(-)

diff --git a/login-utils/su-common.c b/login-utils/su-common.c
index 4d54eab31..c6232ce7a 100644
--- a/login-utils/su-common.c
+++ b/login-utils/su-common.c
@@ -105,9 +105,6 @@ UL_DEBUG_DEFINE_MASKNAMES(su) = UL_DEBUG_EMPTY_MASKNAMES;
 
 #define is_pam_failure(_rc)	((_rc) != PAM_SUCCESS)
 
-/* The shell to run if none is given in the user's passwd entry.  */
-#define DEFAULT_SHELL "/bin/sh"
-
 /* The user to become if none is specified.  */
 #define DEFAULT_USER "root"
 
@@ -1167,7 +1164,7 @@ int su_main(int argc, char **argv, int mode)
 	su->old_user = xgetlogin();
 
 	if (!su->pwd->pw_shell || !*su->pwd->pw_shell)
-		su->pwd->pw_shell = DEFAULT_SHELL;
+		su->pwd->pw_shell = _PATH_BSHELL;
 
 	if (use_supp && !use_gid)
 		su->pwd->pw_gid = groups[0];
diff --git a/login-utils/sulogin.c b/login-utils/sulogin.c
index eb4609db6..c546cc7c1 100644
--- a/login-utils/sulogin.c
+++ b/login-utils/sulogin.c
@@ -34,6 +34,7 @@
 #include <fcntl.h>
 #include <signal.h>
 #include <pwd.h>
+#include <paths.h>
 #include <shadow.h>
 #include <termios.h>
 #include <errno.h>
@@ -892,7 +893,7 @@ static void sushell(struct passwd *pwd, struct console *con)
 		if (pwd->pw_shell[0])
 			su_shell = pwd->pw_shell;
 		else
-			su_shell = "/bin/sh";
+			su_shell = _PATH_BSHELL;
 	}
 	if ((p = strrchr(su_shell, '/')) == NULL)
 		p = su_shell;
@@ -941,9 +942,9 @@ static void sushell(struct passwd *pwd, struct console *con)
 	execl(su_shell, shell, (char *)NULL);
 	warn(_("failed to execute %s"), su_shell);
 
-	xsetenv("SHELL", "/bin/sh", 1);
-	execl("/bin/sh", profile ? "-sh" : "sh", (char *)NULL);
-	warn(_("failed to execute %s"), "/bin/sh");
+	xsetenv("SHELL", _PATH_BSHELL, 1);
+	execl(_PATH_BSHELL, profile ? "-sh" : "sh", (char *)NULL);
+	warn(_("failed to execute %s"), _PATH_BSHELL);
 }
 
 #ifdef HAVE_LIBSELINUX
diff --git a/sys-utils/setpriv.c b/sys-utils/setpriv.c
index c218be8e5..505d1ee5b 100644
--- a/sys-utils/setpriv.c
+++ b/sys-utils/setpriv.c
@@ -30,6 +30,7 @@
 #include <sys/prctl.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <paths.h>
 
 #include "all-io.h"
 #include "c.h"
@@ -56,9 +57,6 @@
 
 #define SETPRIV_EXIT_PRIVERR 127	/* how we exit when we fail to set privs */
 
-/* The shell to set SHELL env.variable if none is given in the user's passwd entry.  */
-#define DEFAULT_SHELL "/bin/sh"
-
 static gid_t get_group(const char *s, const char *err);
 
 enum cap_type {
@@ -741,7 +739,7 @@ static void do_reset_environ(struct passwd *pw)
 	if (pw->pw_shell && *pw->pw_shell)
 		xsetenv("SHELL", pw->pw_shell, 1);
 	else
-		xsetenv("SHELL", DEFAULT_SHELL, 1);
+		xsetenv("SHELL", _PATH_BSHELL, 1);
 
 	xsetenv("HOME", pw->pw_dir, 1);
 	xsetenv("USER", pw->pw_name, 1);
-- 
2.51.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 0/3] Consistent shell resolution across util-linux
  2025-11-23 15:32 [PATCH 0/3] Consistent shell resolution across util-linux Alessandro Ratti
                   ` (2 preceding siblings ...)
  2025-11-23 15:32 ` [PATCH 3/3] login-utils, sys-utils: use _PATH_BSHELL consistently Alessandro Ratti
@ 2025-11-26 11:51 ` Karel Zak
  2025-11-26 12:40   ` Alessandro Ratti
  3 siblings, 1 reply; 6+ messages in thread
From: Karel Zak @ 2025-11-26 11:51 UTC (permalink / raw)
  To: Alessandro Ratti; +Cc: util-linux, thomas


 Hi Alessandro,

 thanks!

On Sun, Nov 23, 2025 at 04:32:43PM +0100, Alessandro Ratti wrote:
> This patch series addresses inconsistent default shell handling across
> util-linux tools, which caused user-reported data loss when script(1)
> defaulted to /bin/sh without respecting the user's configured shell.
> 
> The series:
> 1. Introduces ul_default_shell() for consistent shell resolution
> 2. Updates interactive tools to use the new function
> 3. Standardizes _PATH_BSHELL usage in security-sensitive tools
> 
> This implements the solution discussed in:
> https://github.com/util-linux/util-linux/issues/3865

How about creating a pull request on GitHub? It will help us with the
review, etc.

(I can do it if, for some reason, you don't want to use GitHub, but
it's always better if the author does it themselves.)

    Karel

-- 
 Karel Zak  <kzak@redhat.com>
 http://karelzak.blogspot.com


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 0/3] Consistent shell resolution across util-linux
  2025-11-26 11:51 ` [PATCH 0/3] Consistent shell resolution across util-linux Karel Zak
@ 2025-11-26 12:40   ` Alessandro Ratti
  0 siblings, 0 replies; 6+ messages in thread
From: Alessandro Ratti @ 2025-11-26 12:40 UTC (permalink / raw)
  To: Karel Zak; +Cc: util-linux, thomas, Alessandro Ratti

On Wed, 26 Nov 2025 at 12:51, Karel Zak <kzak@redhat.com> wrote:
>
>
>  Hi Alessandro,
>
>  thanks!
>
> On Sun, Nov 23, 2025 at 04:32:43PM +0100, Alessandro Ratti wrote:
> > This patch series addresses inconsistent default shell handling across
> > util-linux tools, which caused user-reported data loss when script(1)
> > defaulted to /bin/sh without respecting the user's configured shell.
> >
> > The series:
> > 1. Introduces ul_default_shell() for consistent shell resolution
> > 2. Updates interactive tools to use the new function
> > 3. Standardizes _PATH_BSHELL usage in security-sensitive tools
> >
> > This implements the solution discussed in:
> > https://github.com/util-linux/util-linux/issues/3865
>
> How about creating a pull request on GitHub? It will help us with the
> review, etc.

Thanks. I opened a PR on GitHub [1] as you suggested.
I’d be grateful for any feedback or pointers on the review.

Thank you for your time and consideration.

Best regards
Alessandro

[1]: https://github.com/util-linux/util-linux/pull/3876

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2025-11-26 12:40 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-23 15:32 [PATCH 0/3] Consistent shell resolution across util-linux Alessandro Ratti
2025-11-23 15:32 ` [PATCH 1/3] lib: introduce ul_default_shell() for consistent shell resolution Alessandro Ratti
2025-11-23 15:32 ` [PATCH 2/3] *: use ul_default_shell() for interactive shell spawning Alessandro Ratti
2025-11-23 15:32 ` [PATCH 3/3] login-utils, sys-utils: use _PATH_BSHELL consistently Alessandro Ratti
2025-11-26 11:51 ` [PATCH 0/3] Consistent shell resolution across util-linux Karel Zak
2025-11-26 12:40   ` Alessandro Ratti

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).