[Pkg-shadow-devel] su: patch for option --shell

Nicolas François nicolas.francois at centraliens.net
Fri Dec 9 23:33:28 UTC 2005


Hello Tomasz,

Here is a patch extracted from the 437_ patches, which adds a --shell (or
-s) option.



BTW, for the 437_ patches, I will make:
 * one patch for --shell (-s <shell>)
 * one patch for --preserve-environment (-m, -p)
 * one patch for --command (-c <command>)
   Maybe this one will be splitted in multiple patches to prove it is safe
   (because it will touch more parts)

I hope to solve this for the end of the week-end.


Note: for the Usage messages (not only in su), the tabulations are
difficult to handle (when writting the code, or when translating). Could
you switch to spaces (or would you accept patches for that)?

Best Regards,
-- 
Nekral
-------------- next part --------------
--- su-upstream.c	2005-12-09 12:44:33.000000000 +0100
+++ su-shell.c	2005-12-10 00:15:53.000000000 +0100
@@ -111,6 +111,22 @@
 }
 #endif				/* !USE_PAM */
 
+/* borrowed from GNU sh-utils' "su.c" */
+static int
+restricted_shell (const char *shell)
+{
+	char *line;
+
+	setusershell ();
+	while ((line = getusershell ()) != NULL) {
+		if (*line != '#' && strcmp (line, shell) == 0) {
+			endusershell ();
+			return 0;
+		}
+	}
+	endusershell ();
+	return 1;
+}
 
 static void su_failure (const char *tty)
 {
@@ -160,7 +176,7 @@
 		}
 	} else if (child == -1) {
 		(void) fprintf (stderr, "%s: Cannot fork user shell\n", Prog);
-		SYSLOG ((LOG_WARN, "Cannot execute %s", pwent.pw_shell));
+		SYSLOG ((LOG_WARN, "Cannot execute %s", shellstr));
 		closelog ();
 		exit (1);
 	}
@@ -235,11 +251,13 @@
   */
 static void usage (void)
 {
+// tabs seems dangerous here
 	fprintf (stderr, _("Usage: su [options] [login]\n"
 			   "\n"
 			   "Options:\n"
 			   "  -h, --help			display this help message and exit\n"
-			   "  -, -l, --login		make the shell a login shell\n"));
+			   "  -, -l, --login		make the shell a login shell\n"
+			   "  -s, --shell=<shell>		use shell instead of the default in /etc/passwd\n"));
 	exit (E_USAGE);
 }
 
@@ -267,6 +285,7 @@
 	uid_t my_uid;
 	struct passwd *pw = 0;
 	char **envp = environ;
+	char *shell = 0;
 
 #ifdef USE_PAM
 	int ret;
@@ -308,11 +327,12 @@
 		static struct option long_options[] = {
 			{"help", no_argument, NULL, 'h'},
 			{"login", required_argument, NULL, 'l'},
+			{"shell", required_argument, NULL, 's'},
 			{NULL, 0, NULL, '\0'}
 		};
 
 		while ((c =
-			getopt_long (argc, argv, "hl:", long_options,
+			getopt_long (argc, argv, "hl:s:", long_options,
 				     &option_index)) != -1) {
 			switch (c) {
 			case 'h':
@@ -320,6 +340,9 @@
 				break;
 			case 'l':
 				break;
+			case 's':
+				shell = optarg;
+				break;
 			default:
 				usage ();
 			}
@@ -517,11 +540,22 @@
 	}
 #endif				/* !USE_PAM */
 
+	/* For users whith non null UID, if this user has a restricted
+	 * shell, the shell must be the one specified in /etc/passwd
+	 */
+	if (shell != NULL && getuid () && restricted_shell (pwent.pw_shell))
+		shell = NULL;
+	/* If the shell is not set at this time, use the shell specified
+	 * in /etc/passwd.
+	 */
+	if (shell == NULL)
+		shell = (char *) strdup (pwent.pw_shell);
+
 	/*
 	 * Set the default shell.
 	 */
-	if (pwent.pw_shell[0] == '\0')
-		pwent.pw_shell = "/bin/sh";	/* XXX warning: const */
+	if (shell == NULL || shell[0] == '\0')
+		shell = "/bin/sh";
 
 	signal (SIGINT, SIG_IGN);
 	signal (SIGQUIT, SIG_IGN);
@@ -632,6 +666,10 @@
 	if (getenv ("IFS"))	/* don't export user IFS ... */
 		addenv ("IFS= \t\n", NULL);	/* ... instead, set a safe IFS */
 
+	/*
+	 * Even if --shell is specified, the subsystem login test is based on
+	 * the shell specified in /etc/passwd.
+	 */
 	if (pwent.pw_shell[0] == '*') {	/* subsystem root required */
 		pwent.pw_shell++;	/* skip the '*' */
 		subsystem (&pwent);	/* figure out what to execute */
@@ -712,7 +750,7 @@
 	else {
 		addenv ("HOME", pwent.pw_dir);
 		addenv ("USER", pwent.pw_name);
-		addenv ("SHELL", pwent.pw_shell);
+		addenv ("SHELL", shell);
 	}
 #endif
 
@@ -734,35 +772,35 @@
 
 		cp = getdef_str ("SU_NAME");
 		if (!cp)
-			cp = Basename (pwent.pw_shell);
+			cp = Basename (shell);
 
 		arg0 = xmalloc (strlen (cp) + 2);
 		arg0[0] = '-';
 		strcpy (arg0 + 1, cp);
 		cp = arg0;
 	} else
-		cp = Basename (pwent.pw_shell);
+		cp = Basename (shell);
 
 	if (!doshell) {
 		/*
 		 * Use new user's shell from /etc/passwd and create an argv
 		 * with the rest of the command line included.
 		 */
-		argv[-1] = pwent.pw_shell;
+		argv[-1] = shell;
 #ifndef USE_PAM
-		(void) execv (pwent.pw_shell, &argv[-1]);
+		(void) execv (shell, &argv[-1]);
 #else
-		run_shell (pwent.pw_shell, &argv[-1], 0);
+		run_shell (shell, &argv[-1], 0);
 #endif
 		(void) fprintf (stderr, _("No shell\n"));
-		SYSLOG ((LOG_WARN, "Cannot execute %s", pwent.pw_shell));
+		SYSLOG ((LOG_WARN, "Cannot execute %s", shell));
 		closelog ();
 		exit (1);
 	}
 #ifndef USE_PAM
-	shell (pwent.pw_shell, cp);
+	shell (shell, cp);
 #else
-	run_shell (pwent.pw_shell, &cp, 1);
+	run_shell (shell, &cp, 1);
 #endif
 	/* NOT REACHED */
 	exit (1);


More information about the Pkg-shadow-devel mailing list