[Pkg-shadow-devel] patch for su (needed by the switch to getopt in 4.0.14)

Nicolas François nicolas.francois at centraliens.net
Tue Dec 13 22:02:29 UTC 2005


Hello Tomasz,

Thanks for having commited the previous patches.

The attached patch contains:
 * update for the su.1 man page
 * some various fixes for su.c (in the patch chunk order; feel free to
   only apply some of these):
   + Fix a comment about su options
   + add '-' at the first position of the options string.
     This permits to stop getopt at the first non-su option.
   + add a comment to the --preserve-environment option
   + a tag to exit getopt (see above) when the first non-su option is
     encoutered
   + Simplify the --preserve-environment:
     change_environment was always used with:
     "(change_environment || restricted_shell (pwent.pw_shell))"
     so OR it earlier.
     I also permit root to use --preserve-environment, even if the target
     user has a restricted shell (this was already the case at some place,
     but not everywere).
     This way, documenting (and understanding) -p is much easier.

     So there is a little difference with the way Debian currently behave,
     but I think this is better this way.
   + I replaced getuid () by !amroot
     This removes a system call, and is easier to read.
     This getuid () was added by one of my previous patches.
   + Some other change_environment simplifications (see earlier
   + (In the same patch chunk as the previous point) set pwent.pw_shell to
     shellstr because setup_env uses pw_shell to set the SHELL environment
     variable
     (This is a bug in the current version in Debian)
   + (Also in the same patch chunk as the previous point)Add LOGNAME to
     the variables always exported (unless -p).  This mimic setup_env.
     I think this is right. When the user uses "su", or "su -", she
     doesn't want to have USER or LOGNAME to point to her previous login
     name.
   + Fix a comment: the shell is no more always the one specified in
     /etc/passwd
 * Fix a commment in useradd.c


I plan to write another section in the su manpage (maybe login will also
need it): to describe the environment the user will have after calling su.
It is currently very complicated. I will try to make some modification to
simplify it.

Currently:

The environment is clean (sanitize_env; but not in Debian):
 * Some environment variables are removed (see the forbid array in
   libmisc/env.c), and the languages variables (LANG, LANGUAGE, LC_*) wich
   contain a '/' are removed (probably for a security reason)
 * in the case of a login su (su -), a new environment is built with only TERM
   otherwise, all the current sanitized environment is used for the new
   environment
The new environment is later changed this way:
 * PATH is always set/replaced (ENV_PATH, ENV_SUPATH, or defaut)
 * If IFS was set, it is reset to a safe value
   (There may be a difference here between a su compiled with USE_PAM or
   not)
   (I'm not sure it is needed, because IFS is removed by sanitize_env)
 * the PAM environment are copied
 * Some variable are set to sane defaut to reflect the change of user,
   shell etc. (HOME, SHELL, USER, LOGNAME). This is done either by
   setup_env (login su, or separately otherwise; this is the reason I
   added LOGNAME in the attached patch)
Then, if -p was not specified (and the target user do not have a restricted
shell or if su is run by root), this new environment is used (and is
trashed otherwise)

Thus, with -p, the environment is not entirely preserved (sanitize_env).

This is still a little bit confusing. I will try to make just a couple of
sentences.

Cheers,
-- 
Nekral
-------------- next part --------------
Index: man/su.1.xml
===================================================================
RCS file: /cvsroot/shadow/man/su.1.xml,v
retrieving revision 1.16
diff -u -r1.16 su.1.xml
--- man/su.1.xml	5 Nov 2005 17:17:30 -0000	1.16
+++ man/su.1.xml	13 Dec 2005 22:01:49 -0000
@@ -15,6 +15,9 @@
   <refsynopsisdiv id='synopsis'>
     <cmdsynopsis>
       <command>su</command>
+      <arg choice='opt'>
+	<replaceable>options</replaceable>
+      </arg>
       <arg choice='opt'>- </arg>
       <arg choice='opt'>
 	<arg choice='plain'>
@@ -46,6 +49,11 @@
       for the target user.
     </para>
 
+    <para>
+      You can use the <option>--</option> argument to separate
+      <command>su</command> options from the arguments supplied to the shell.
+    </para>
+
     <para>The user will be prompted for a password, if appropriate. Invalid
       passwords will produce an error message. All attempts, both valid and
       invalid, are logged to detect abuses of the system.
@@ -68,6 +76,86 @@
     </para>
   </refsect1>
 
+  <refsect1 id='options'>
+    <title>OPTIONS</title>
+    <para>The options which apply to the <command>su</command> command are:
+    </para>
+    <variablelist remap='IP'>
+      <varlistentry>
+	<term>
+	  <option>-</option>, <option>-l</option>, <option>--login</option>
+	</term>
+	<listitem>
+	  <para>
+	    Provide an environment similar to what the user would expect had
+	    the user logged in directly.
+	  </para>
+	  <para>
+	    When <option>-</option> is used, it must be specified as the last
+	    <command>su</command> option.
+	    The other forms (<option>-l</option> and <option>--login</option>)
+	    do not have this restriction.
+	  </para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term>
+	  <option>-s</option>, <option>--shell</option>
+	</term>
+	<listitem>
+	  <para>The shell that will be invoked.</para>
+	  <para>
+	    The invoked shell is choosen among (higest priority first):
+	    <itemizedlist>
+	      <listitem>
+		<para>The shell specified with --shell</para>
+	      </listitem>
+	      <listitem>
+		<para>
+		  If <option>--preserve-environment</option> is used, the
+		  shell specified by the <envar>SHELL</envar> environment
+		  variable.
+		</para>
+	      </listitem>
+	      <listitem>
+		<para>
+		  The shell indicated in the /etc/passwd entry for the target
+		  user.
+		</para>
+	      </listitem>
+	      <listitem>
+		<para>
+		  /bin/sh if a shell could not be found by any above method.
+		</para>
+	      </listitem>
+	    </itemizedlist>
+	  </para>
+	  <para>
+	    If the target user has a restricted shell (i.e. the shell field of
+	    this user's entry in <filename>/etc/passwd</filename> is not
+	    specified in <filename>/etc/shell</filename>), then the
+	    <option>--shell</option> option or the <envar>SHELL</envar>
+	    environment variable won't be taken into account unless
+	    <command>su</command> is called by the root.
+	  </para>
+	</listitem>
+      </varlistentry>
+      <varlistentry>
+	<term>
+	  <option>-m</option>, <option>-p</option>,
+	  <option>--preserve-environment</option>
+	</term>
+	<listitem>
+	  <para>Preserve the current environment.</para>
+	  <para>
+	    If the target user has a restricted shell, this option has no
+	    effect (unless <command>su</command> is called by root).
+	  </para>
+	</listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
   <refsect1 id='caveats'>
     <title>CAVEATS</title>
     <para>
@@ -104,10 +192,7 @@
       </citerefentry>,
       <citerefentry>
 	<refentrytitle>sh</refentrytitle><manvolnum>1</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-	<refentrytitle>suauth</refentrytitle><manvolnum>5</manvolnum>
-      </citerefentry>.
+      </citerefentry>
     </para>
   </refsect1>
 </refentry>
Index: src/su.c
===================================================================
RCS file: /cvsroot/shadow/src/su.c,v
retrieving revision 1.55
diff -u -r1.55 su.c
--- src/su.c	13 Dec 2005 14:03:05 -0000	1.55
+++ src/su.c	13 Dec 2005 22:01:49 -0000
@@ -271,9 +271,6 @@
  *	su changes the user's ids to the values for the specified user.  if
  *	no new user name is specified, "root" is used by default.
  *
- *	The only valid option is a "-" character, which is interpreted as
- *	requiring a new login session to be simulated.
- *
  *	Any additional arguments are passed to the user's shell. In
  *	particular, the argument "-c" will cause the next argument to be
  *	interpreted as a command by the common shell programs.
@@ -339,9 +336,17 @@
 		};
 
 		while ((c =
-			getopt_long (argc, argv, "hlmps:", long_options,
+			getopt_long (argc, argv, "-hlmps:", long_options,
 				     &option_index)) != -1) {
 			switch (c) {
+			case 1:
+				/* This is not an su option */
+				/* The next arguments are either '-', the target name, or
+				 * arguments to be passed to the shell.
+				 */
+				optind--; /* rewind the current (not yet handled) option */
+				goto end_su_options;
+				break;
 			case 'h':
 				usage ();
 				break;
@@ -350,6 +355,10 @@
 				break;
 			case 'm':
 			case 'p':
+				/* This will only have an effect if the target
+				 * user do not have a restricted shell, or if
+				 * su is called by root.
+				 */
 				change_environment = 0;
 				break;
 			case 's':
@@ -359,6 +368,7 @@
 				usage ();
 			}
 		}
+end_su_options:
 		if (optind < argc && !strcmp (argv[optind], "-")) {
 			fakelogin = 1;
 			optind++;
@@ -473,14 +483,18 @@
 #endif				/* !USE_PAM */
 	pwent = *pw;
 
+	/* If su is not called by root, and the target user has a restricted
+	 * shell, the environment must be changed.
+	 */
+	change_environment |= (restricted_shell(pwent.pw_shell) && !amroot);
+
 	/*
 	 * If a new login is being set up, the old environment will be
 	 * ignored and a new one created later on.
 	 * (note: in the case of a subsystem, the shell will be restricted,
 	 *        and this won't be executed on the first pass)
 	 */
-	if (fakelogin &&
-	    (change_environment || restricted_shell (pwent.pw_shell))) {
+	if (fakelogin && change_environment) {
 		/*
 		 * The terminal type will be left alone if it is present in
 		 * the environment already.
@@ -566,7 +580,7 @@
 	/* For users with non null UID, if this user has a restricted
 	 * shell, the shell must be the one specified in /etc/passwd
 	 */
-	if (shellstr != NULL && getuid () && restricted_shell (pwent.pw_shell))
+	if (shellstr != NULL && !amroot && restricted_shell (pwent.pw_shell))
 		shellstr = NULL;
 	/* If the shell is not set at this time, use the shell specified
 	 * in /etc/passwd.
@@ -739,7 +753,7 @@
 		exit (1);
 	}
 
-	if (change_environment || restricted_shell (pwent.pw_shell)) {
+	if (change_environment) {
 		/* we need to setup the environment *after* pam_open_session(),
 		 * else the UID is changed before stuff like pam_xauth could
 		 * run, and we cannot access /etc/shadow and co
@@ -770,12 +784,15 @@
 		exit (1);
 #endif				/* !USE_PAM */
 
-	if (change_environment || restricted_shell (pwent.pw_shell)) {
-		if (fakelogin)
+	if (change_environment) {
+		/* set some environment variables */
+		if (fakelogin) {
+			pwent.pw_shell = shellstr;
 			setup_env (&pwent);
-		else {
+		} else {
 			addenv ("HOME", pwent.pw_dir);
 			addenv ("USER", pwent.pw_name);
+			addenv ("LOGNAME", pwent.pw_name);
 			addenv ("SHELL", shellstr);
 		}
 	}
@@ -811,7 +828,7 @@
 		/* Position argv to the remaining arguments */
 		argv += optind;
 		/*
-		 * Use new user's shell from /etc/passwd and create an argv
+		 * Use the shell and create an argv
 		 * with the rest of the command line included.
 		 */
 		argv[-1] = shellstr;
Index: src/useradd.c
===================================================================
RCS file: /cvsroot/shadow/src/useradd.c,v
retrieving revision 1.88
diff -u -r1.88 useradd.c
--- src/useradd.c	6 Dec 2005 20:22:00 -0000	1.88
+++ src/useradd.c	13 Dec 2005 22:01:49 -0000
@@ -1400,9 +1400,9 @@
 
 
 /*
- * grp_update - add new group file entries
+ * grp_add - add new group file entries
  *
- *      grp_update() writes the new records to the group files.
+ *      grp_add() writes the new records to the group files.
  */
 
 static void grp_add (void)


More information about the Pkg-shadow-devel mailing list