[packages/util-linux] - added runuser patch (some code from git master), merged runuser pam files from coreutils - added s

qboosh qboosh at pld-linux.org
Sun Oct 14 18:12:50 CEST 2012


commit 034a23bb423faa90084513d192d871f92613975e
Author: Jakub Bogusz <qboosh at pld-linux.org>
Date:   Sun Oct 14 18:12:55 2012 +0200

    - added runuser patch (some code from git master), merged runuser pam files from coreutils
    - added su bcond; when enabled (default), su and runuser are packaged and Conflicts with SysVinit and coreutils added

 runuser-l.pamd           |    4 +
 runuser.pamd             |    3 +
 su-paths.patch           |    4 +-
 util-linux-runuser.patch | 1957 ++++++++++++++++++++++++++++++++++++++++++++++
 util-linux.spec          |   37 +-
 5 files changed, 1995 insertions(+), 10 deletions(-)
---
diff --git a/util-linux.spec b/util-linux.spec
index b4a4dc2..90d1709 100644
--- a/util-linux.spec
+++ b/util-linux.spec
@@ -12,6 +12,7 @@
 %bcond_with	uClibc		# link initrd version with static glibc instead of uClibc
 %bcond_without	dietlibc	# link initrd version with dietlibc instead of uClibc
 %bcond_without	selinux 	# SELinux support
+%bcond_without	su		# su/runuser programs
 %if "%{pld_release}" == "ac"
 %bcond_with	initrd		# don't build initrd version
 %bcond_with	fallocate	# fallocate utility (needs glibc 2.11 to compile)
@@ -50,8 +51,11 @@ Source4:	%{name}-blockdev.sysconfig
 Source5:	blockdev.upstart
 Source6:	su.pamd
 Source7:	su-l.pamd
+Source8:	runuser.pamd
+Source9:	runuser-l.pamd
 Patch0:		%{name}-pl.po-update.patch
 Patch1:		%{name}-ng-union-mount.patch
+Patch2:		%{name}-runuser.patch
 Patch3:		%{name}-fdformat-ide.patch
 Patch4:		%{name}-fhs.patch
 Patch5:		%{name}-hotkeys.patch
@@ -98,8 +102,6 @@ Provides:	eject = %{version}-%{release}
 Provides:	fdisk
 Provides:	linux32
 Provides:	sparc32
-Provides:	coreutils-su
-Obsoletes:	coreutils-su
 Obsoletes:	cramfs
 Obsoletes:	eject
 Obsoletes:	ionice
@@ -114,6 +116,12 @@ Conflicts:	SysVinit < 2.86-26
 Conflicts:	e2fsprogs < 1.41.8-5
 Conflicts:	shadow-extras < 1:4.0.3-6
 Conflicts:	upstart-SysVinit < 2.86-28
+%if %{with su}
+Provides:	coreutils-su
+Obsoletes:	coreutils-su
+Conflicts:	SysVinit-tools < 2.88-9
+Conflicts:	coreutils < 8.19
+%endif
 BuildRoot:	%{tmpdir}/%{name}-%{version}-root-%(id -u -n)
 
 %define		debugcflags	-O1 -g
@@ -640,6 +648,7 @@ etykietę lub UUID - statycznie skonsolidowane na potrzeby initrd.
 %setup -q -a1
 #%patch0 -p1
 %patch1 -p1
+%patch2 -p1
 %patch3 -p1
 %patch4 -p1
 %patch5 -p1
@@ -689,6 +698,7 @@ export CPPFLAGS="%{rpmcppflags} -I/usr/include/ncurses -DHAVE_LSEEK64_PROTOTYPE
 	--disable-newgrp \
 	--disable-partx \
 	--disable-raw \
+	--disable-runuser \
 	--disable-schedutils \
 	--disable-setarch \
 	--disable-silent-rules \
@@ -738,7 +748,8 @@ export CPPFLAGS="%{rpmcppflags} -I/usr/include/ncurses -DHAVE_LSEEK64_PROTOTYPE
 	--enable-login-chown-vcs \
 	--enable-newgrp \
 	--enable-partx \
-	--enable-su \
+	--enable-runuser%{!?with_su:=no} \
+	--enable-su%{!?with_su:=no} \
 	--enable-sulogin \
 	--enable-utmpdump \
 	--enable-vipw \
@@ -764,8 +775,12 @@ cp -p %{SOURCE2} $RPM_BUILD_ROOT/etc/pam.d/login
 install -p %{SOURCE3} $RPM_BUILD_ROOT/etc/rc.d/init.d/blockdev
 cp -p %{SOURCE4} $RPM_BUILD_ROOT/etc/sysconfig/blockdev
 cp -p %{SOURCE5} $RPM_BUILD_ROOT/etc/init/blockdev.conf
+%if %{with su}
 cp -p %{SOURCE6} $RPM_BUILD_ROOT/etc/pam.d/su
 cp -p %{SOURCE7} $RPM_BUILD_ROOT/etc/pam.d/su-l
+cp -p %{SOURCE8} $RPM_BUILD_ROOT/etc/pam.d/runuser
+cp -p %{SOURCE9} $RPM_BUILD_ROOT/etc/pam.d/runuser-l
+%endif
 
 :> $RPM_BUILD_ROOT/etc/security/blacklist.login
 :> $RPM_BUILD_ROOT/var/lock/wtmpxlock
@@ -922,10 +937,6 @@ fi
 %attr(755,root,root) /bin/kill
 %attr(755,root,root) /bin/more
 
-%attr(4755,root,root) /bin/su
-%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) /etc/pam.d/su
-%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) /etc/pam.d/su-l
-
 %attr(755,root,root) /bin/wdctl
 
 %attr(755,root,root) /sbin/chcpu
@@ -1027,7 +1038,6 @@ fi
 %{_mandir}/man1/script.1*
 %{_mandir}/man1/scriptreplay.1*
 %{_mandir}/man1/setterm.1*
-%{_mandir}/man1/su.1*
 %{_mandir}/man1/tailf.1*
 %{_mandir}/man1/taskset.1*
 %{_mandir}/man1/ul.1*
@@ -1287,6 +1297,17 @@ fi
 %attr(755,root,root) /sbin/mkfs.cramfs
 %attr(755,root,root) /sbin/mkfs.bfs
 
+%if %{with su}
+%attr(755,root,root) /bin/runuser
+%attr(4755,root,root) /bin/su
+%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) /etc/pam.d/runuser
+%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) /etc/pam.d/runuser-l
+%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) /etc/pam.d/su
+%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) /etc/pam.d/su-l
+%{_mandir}/man1/runuser.1*
+%{_mandir}/man1/su.1*
+%endif
+
 %ghost /var/lock/wtmpxlock
 
 %files -n blockdev
diff --git a/runuser-l.pamd b/runuser-l.pamd
new file mode 100644
index 0000000..fa1e4d8
--- /dev/null
+++ b/runuser-l.pamd
@@ -0,0 +1,4 @@
+#%PAM-1.0
+auth		include		runuser
+session		optional	pam_keyinit.so force revoke
+session		include		runuser
diff --git a/runuser.pamd b/runuser.pamd
new file mode 100644
index 0000000..5b66ff1
--- /dev/null
+++ b/runuser.pamd
@@ -0,0 +1,3 @@
+#%PAM-1.0
+auth		sufficient	pam_rootok.so
+session		include		system-auth
diff --git a/su-paths.patch b/su-paths.patch
index bcf5b2d..42705e5 100644
--- a/su-paths.patch
+++ b/su-paths.patch
@@ -1,5 +1,5 @@
---- coreutils-4.5.3/login-utils/su.c.orig	Sun Oct 27 21:57:03 2002
-+++ coreutils-4.5.3/login-utils/su.c	Sun Oct 27 22:15:05 2002
+--- coreutils-4.5.3/login-utils/su-common.c.orig	Sun Oct 27 21:57:03 2002
++++ coreutils-4.5.3/login-utils/su-common.c	Sun Oct 27 22:15:05 2002
 @@ -154,6 +154,15 @@
  # define DEFAULT_ROOT_LOGIN_PATH "/usr/ucb:/bin:/usr/bin:/etc"
  #endif
diff --git a/util-linux-runuser.patch b/util-linux-runuser.patch
new file mode 100644
index 0000000..7b885d0
--- /dev/null
+++ b/util-linux-runuser.patch
@@ -0,0 +1,1957 @@
+diff --git a/configure.ac b/configure.ac
+index 87e85fa..ead559c 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1149,6 +1154,15 @@ UL_REQUIRES_HAVE([su], [security_pam_misc_h], [PAM header file])
+ AM_CONDITIONAL(BUILD_SU, test "x$build_su" = xyes)
+ 
+ 
++AC_ARG_ENABLE([runuser],
++  AS_HELP_STRING([--disable-runuser], [do not build runuser]),
++  [], enable_runuser=yes
++)
++UL_BUILD_INIT([runuser])
++UL_REQUIRES_HAVE([runuser], [security_pam_misc_h], [PAM header file])
++AM_CONDITIONAL(BUILD_RUNUSER, test "x$build_runuser" = xyes)
++
++
+ AC_ARG_ENABLE([schedutils],
+   AS_HELP_STRING([--disable-schedutils], [do not build chrt, ionice, teskset]),
+   [], enable_schedutils=yes
+diff --git a/login-utils/Makemodule.am b/login-utils/Makemodule.am
+index e10da46..755a361 100644
+--- a/login-utils/Makemodule.am
++++ b/login-utils/Makemodule.am
+@@ -83,6 +83,8 @@
+ dist_man_MANS += login-utils/su.1
+ su_SOURCES = \
+ 	login-utils/su.c \
++	login-utils/su-common.c \
++	login-utils/su-common.h \
+ 	login-utils/logindefs.c \
+ 	login-utils/logindefs.h
+ su_CFLAGS = $(SUID_CFLAGS) $(AM_CFLAGS)
+@@ -91,6 +93,19 @@
+ endif
+ 
+ 
++if BUILD_RUNUSER
++bin_PROGRAMS += runuser
++dist_man_MANS += login-utils/runuser.1
++runuser_SOURCES = \
++	login-utils/runuser.c \
++	login-utils/su-common.c \
++	login-utils/su-common.h \
++	login-utils/logindefs.c \
++	login-utils/logindefs.h
++runuser_LDADD = $(LDADD) -lpam -lpam_misc
++endif
++
++
+ if BUILD_NEWGRP
+ usrbin_exec_PROGRAMS += newgrp
+ dist_man_MANS += login-utils/newgrp.1
+diff --git a/login-utils/runuser.1 b/login-utils/runuser.1
+new file mode 100644
+index 0000000..66ad1c4
+--- /dev/null
++++ b/login-utils/runuser.1
+@@ -0,0 +1,230 @@
++.TH RUNUSER "1" "August 2012" "util-linux" "User Commands"
++.SH NAME
++runuser \- run a command with substitute user and group ID
++.SH SYNOPSIS
++.B runuser
++[options...] [\-] [user [args...]]
++.SH DESCRIPTION
++.B runuser
++allows to run commands with substitute user and group ID.
++The difference between the commands
++.B runuser
++and
++.B su
++is that
++.B runuser
++does not ask for password, because it may be executed by root user only.
++The command
++.B runuser
++does not have to be installed with suid permissions.
++.PP
++When called without arguments
++.B runuser
++defaults to running an interactive shell as
++.IR root .
++.PP
++For backward compatibility
++.B runuser
++defaults to not change the current directory and to only set the
++environment variables
++.B HOME
++and
++.B SHELL
++(plus
++.B USER
++and
++.B LOGNAME
++if the target
++.I user
++is not root).  It is recommended to always use the
++.B \-\-login
++option (instead it's shortcut
++.BR \- )
++to avoid side effects caused by mixing environments.
++.PP
++This version of
++.B runuser
++uses PAM for session management.
++.SH OPTIONS
++.TP
++\fB\-c\fR \fIcommand\fR, \fB\-\-command\fR=\fIcommand\fR
++Pass
++.I command
++to the shell with the
++.B \-c
++option.
++.TP
++\fB\-\-session\-command\fR=\fIcommand\fR
++Same as
++.B \-c
++but do not create a new session (discouraged).
++.TP
++\fB\-f\fR, \fB\-\-fast\fR
++Pass
++.B \-f
++to the shell which may or may not be useful depending on the
++shell.
++.TP
++\fB\-g\fR, \fB\-\-group\fR=\fIgroup\fR\fR
++specify the primary group, this option is allowed for root user only
++.TP
++\fB\-G\fR, \fB\-\-supp-group\fR=\fIgroup\fR\fR
++specify a supplemental group, this option is allowed for root user only
++.TP
++\fB\-\fR, \fB\-l\fR, \fB\-\-login\fR
++Starts the shell as login shell with an environment similar to a real
++login:
++.RS 10
++.TP
++o
++clears all environment variables except for
++.B TERM
++.TP
++o
++initializes the environment variables
++.BR HOME ,
++.BR SHELL ,
++.BR USER ,
++.BR LOGNAME ,
++.B PATH
++.TP
++o
++changes to the target user's home directory
++.TP
++o
++sets argv[0] of the shell to
++.RB ' \- '
++in order to make the shell a login shell
++.RE
++.TP
++\fB\-m\fR, \fB\-p\fR, \fB\-\-preserve-environment\fR
++Preserves the whole environment, ie does not set
++.BR HOME ,
++.BR SHELL ,
++.B USER
++nor
++.BR LOGNAME .
++.TP
++\fB\-s\fR \fISHELL\fR, \fB\-\-shell\fR=\fISHELL\fR
++Runs the specified shell instead of the default.  The shell to run is
++selected according to the following rules in order:
++.RS 10
++.TP
++o
++the shell specified with
++.B \-\-shell
++.TP
++o
++The shell specified in the environment variable
++.B SHELL
++if the
++.B \-\-preserve-environment
++option is used.
++.TP
++o
++the shell listed in the passwd entry of the target user
++.TP
++o
++/bin/sh
++.RE
++.IP
++If the target user has a restricted shell (i.e. not listed in
++/etc/shells) the
++.B \-\-shell
++option and the
++.B SHELL
++environment variables are ignored unless the calling user is root.
++.TP
++\fB\-\-help\fR
++Display help text and exit.
++.TP
++\fB\-\-version\fR
++Display version information and exit.
++.SH CONFIG FILES
++.B runuser
++reads the
++.I /etc/default/runuser
++and
++.I /etc/login.defs
++configuration files.  The following configuration items are relevant
++for
++.BR runuser :
++.PP
++.B ENV_PATH
++(string)
++.RS 4
++Defines the PATH environment variable for a regular user.  The
++default value is
++.IR /usr/local/bin:\:/bin:\:/usr/bin .
++.RE
++.PP
++.B ENV_ROOTPATH
++(string)
++.br
++.B ENV_SUPATH
++(string)
++.RS 4
++Defines the PATH environment variable for root. The default value is
++.IR /usr/local/sbin:\:/usr/local/bin:\:/sbin:\:/bin:\:/usr/sbin:\:/usr/bin .
++.RE
++.PP
++.B ALWAYS_SET_PATH
++(boolean)
++.RS 4
++If set to
++.I yes
++and \-\-login and \-\-preserve\-environment were not specified
++.B runuser
++initializes
++.BR PATH .
++.RE
++.SH EXIT STATUS
++.B runuser
++normally returns the exit status of the command it executed.  If the
++command was killed by a signal,
++.B runuser
++returns the number of the signal plus 128.
++.PP
++Exit status generated by
++.B runuser
++itself:
++.RS 10
++.TP
++1
++Generic error before executing the requested command
++.TP
++126
++The requested command could not be executed
++.TP
++127
++The requested command could was not found
++.RE
++.SH FILES
++.PD 0
++.TP 17
++/etc/pam.d/runuser
++default PAM configuration file
++.TP
++/etc/pam.d/runuser-l
++PAM configuration file if \-\-login is specified
++.TP
++/etc/default/runuser
++runuser specific logindef config file
++.TP
++/etc/login.defs
++global logindef config file
++.PD 1
++.SH "SEE ALSO"
++.BR pam (8),
++.BR shells (5),
++.BR login.defs (5),
++.BR su (1)
++.SH AUTHOR
++Derived from coreutils' su which was based on an implemenation from
++David MacKenzie and Fedora runuser command from Dan Walsh.
++.SH AVAILABILITY
++The runuser command is part of the util-linux package and is
++available from
++.UR ftp://\:ftp.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
++Linux Kernel Archive
++.UE .
+diff --git a/login-utils/runuser.c b/login-utils/runuser.c
+new file mode 100644
+index 0000000..d761a14
+--- /dev/null
++++ b/login-utils/runuser.c
+@@ -0,0 +1,7 @@
++
++#include "su-common.h"
++
++int main(int argv, char **argc)
++{
++	return su_main(argv, argc, RUNUSER_MODE);
++}
+diff --git a/login-utils/su-common.c b/login-utils/su-common.c
+new file mode 100644
+index 0000000..d1fecd7
+--- /dev/null
++++ b/login-utils/su-common.c
+@@ -0,0 +1,918 @@
++/* su for Linux.  Run a shell with substitute user and group IDs.
++   Copyright (C) 1992-2006 Free Software Foundation, Inc.
++   Copyright (C) 2012 SUSE Linux Products GmbH, Nuernberg
++
++   This program is free software; you can redistribute it and/or modify
++   it under the terms of the GNU General Public License as published by
++   the Free Software Foundation; either version 2, or (at your option)
++   any later version.
++
++   This program is distributed in the hope that it will be useful,
++   but WITHOUT ANY WARRANTY; without even the implied warranty of
++   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++   GNU General Public License for more details.
++
++   You should have received a copy of the GNU General Public License
++   along with this program; if not, write to the Free Software Foundation,
++   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
++
++/* Run a shell with the real and effective UID and GID and groups
++   of USER, default `root'.
++
++   The shell run is taken from USER's password entry, /bin/sh if
++   none is specified there.  If the account has a password, su
++   prompts for a password unless run by a user with real UID 0.
++
++   Does not change the current directory.
++   Sets `HOME' and `SHELL' from the password entry for USER, and if
++   USER is not root, sets `USER' and `LOGNAME' to USER.
++   The subshell is not a login shell.
++
++   If one or more ARGs are given, they are passed as additional
++   arguments to the subshell.
++
++   Does not handle /bin/sh or other shells specially
++   (setting argv[0] to "-su", passing -c only to certain shells, etc.).
++   I don't see the point in doing that, and it's ugly.
++
++   Based on an implemenation by David MacKenzie <djm at gnu.ai.mit.edu>.  */
++
++enum
++{
++  EXIT_CANNOT_INVOKE = 126,
++  EXIT_ENOENT = 127
++};
++
++#include <config.h>
++#include <stdio.h>
++#include <getopt.h>
++#include <sys/types.h>
++#include <pwd.h>
++#include <grp.h>
++#include <security/pam_appl.h>
++#include <security/pam_misc.h>
++#include <signal.h>
++#include <sys/wait.h>
++#include <syslog.h>
++
++#include "err.h"
++
++#include <stdbool.h>
++#include "c.h"
++#include "xalloc.h"
++#include "nls.h"
++#include "pathnames.h"
++#include "env.h"
++
++/* name of the pam configuration files. separate configs for su and su -  */
++#define PAM_SRVNAME_SU "su"
++#define PAM_SRVNAME_SU_L "su-l"
++
++#define PAM_SRVNAME_RUNUSER "runuser"
++#define PAM_SRVNAME_RUNUSER_L "runuser-l"
++
++#define _PATH_LOGINDEFS_SU	"/etc/defaults/su"
++#define _PATH_LOGINDEFS_RUNUSER "/etc/defaults/runuser"
++
++#define is_pam_failure(_rc)	((_rc) != PAM_SUCCESS)
++
++#include "logindefs.h"
++#include "su-common.h"
++
++/* 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"
++
++#ifndef HAVE_ENVIRON_DECL
++extern char **environ;
++#endif
++
++static void run_shell (char const *, char const *, char **, size_t)
++     __attribute__ ((__noreturn__));
++
++/* If true, pass the `-f' option to the subshell.  */
++static bool fast_startup;
++
++/* If true, simulate a login instead of just starting a shell.  */
++static bool simulate_login;
++
++/* If true, change some environment vars to indicate the user su'd to.  */
++static bool change_environment;
++
++/* If true, then don't call setsid() with a command. */
++static int same_session = 0;
++
++/* SU_MODE_{RUNUSER,SU} */
++static int su_mode;
++
++static bool _pam_session_opened;
++static bool _pam_cred_established;
++static sig_atomic_t volatile caught_signal = false;
++static pam_handle_t *pamh = NULL;
++
++static int restricted = 1;	/* zero for root user */
++
++static struct option const longopts[] =
++{
++  {"command", required_argument, NULL, 'c'},
++  {"session-command", required_argument, NULL, 'C'},
++  {"fast", no_argument, NULL, 'f'},
++  {"login", no_argument, NULL, 'l'},
++  {"preserve-environment", no_argument, NULL, 'p'},
++  {"shell", required_argument, NULL, 's'},
++  {"group", required_argument, NULL, 'g'},
++  {"supp-group", required_argument, NULL, 'G'},
++  {"help", no_argument, 0, 'h'},
++  {"version", no_argument, 0, 'V'},
++  {NULL, 0, NULL, 0}
++};
++
++/* Log the fact that someone has run su to the user given by PW;
++   if SUCCESSFUL is true, they gave the correct password, etc.  */
++
++static void
++log_su (struct passwd const *pw, bool successful)
++{
++  const char *new_user, *old_user, *tty;
++
++  new_user = pw->pw_name;
++  /* The utmp entry (via getlogin) is probably the best way to identify
++     the user, especially if someone su's from a su-shell.  */
++  old_user = getlogin ();
++  if (!old_user)
++    {
++      /* getlogin can fail -- usually due to lack of utmp entry.
++	 Resort to getpwuid.  */
++      struct passwd *pwd = getpwuid (getuid ());
++      old_user = (pwd ? pwd->pw_name : "");
++    }
++  tty = ttyname (STDERR_FILENO);
++  if (!tty)
++    tty = "none";
++
++  openlog (program_invocation_short_name, 0 , LOG_AUTH);
++  syslog (LOG_NOTICE, "%s(to %s) %s on %s",
++	  successful ? "" :
++	  su_mode == RUNUSER_MODE ? "FAILED RUNUSER " : "FAILED SU ",
++	  new_user, old_user, tty);
++  closelog ();
++}
++
++static struct pam_conv conv =
++{
++  misc_conv,
++  NULL
++};
++
++static void
++cleanup_pam (int retcode)
++{
++  int saved_errno = errno;
++
++  if (_pam_session_opened)
++    pam_close_session (pamh, 0);
++
++  if (_pam_cred_established)
++    pam_setcred (pamh, PAM_DELETE_CRED | PAM_SILENT);
++
++  pam_end(pamh, retcode);
++
++  errno = saved_errno;
++}
++
++/* Signal handler for parent process.  */
++static void
++su_catch_sig (int sig __attribute__((__unused__)))
++{
++  caught_signal = true;
++}
++
++/* Export env variables declared by PAM modules.  */
++static void
++export_pamenv (void)
++{
++  char **env;
++
++  /* This is a copy but don't care to free as we exec later anyways.  */
++  env = pam_getenvlist (pamh);
++  while (env && *env)
++    {
++      if (putenv (*env) != 0)
++	err (EXIT_FAILURE, NULL);
++      env++;
++    }
++}
++
++static void
++create_watching_parent (void)
++{
++  pid_t child;
++  sigset_t ourset;
++  int status = 0;
++  int retval;
++
++  retval = pam_open_session (pamh, 0);
++  if (is_pam_failure(retval))
++    {
++      cleanup_pam (retval);
++      errx (EXIT_FAILURE, _("cannot not open session: %s"),
++	     pam_strerror (pamh, retval));
++    }
++  else
++    _pam_session_opened = 1;
++
++  child = fork ();
++  if (child == (pid_t) -1)
++    {
++      cleanup_pam (PAM_ABORT);
++      err (EXIT_FAILURE, _("cannot create child process"));
++    }
++
++  /* the child proceeds to run the shell */
++  if (child == 0)
++    return;
++
++  /* In the parent watch the child.  */
++
++  /* su without pam support does not have a helper that keeps
++     sitting on any directory so let's go to /.  */
++  if (chdir ("/") != 0)
++    warn (_("cannot change directory to %s"), "/");
++
++  sigfillset (&ourset);
++  if (sigprocmask (SIG_BLOCK, &ourset, NULL))
++    {
++      warn (_("cannot block signals"));
++      caught_signal = true;
++    }
++  if (!caught_signal)
++    {
++      struct sigaction action;
++      action.sa_handler = su_catch_sig;
++      sigemptyset (&action.sa_mask);
++      action.sa_flags = 0;
++      sigemptyset (&ourset);
++    if (!same_session)
++      {
++        if (sigaddset(&ourset, SIGINT) || sigaddset(&ourset, SIGQUIT))
++          {
++            warn (_("cannot set signal handler"));
++            caught_signal = true;
++          }
++      }
++    if (!caught_signal && (sigaddset(&ourset, SIGTERM)
++                    || sigaddset(&ourset, SIGALRM)
++                    || sigaction(SIGTERM, &action, NULL)
++                    || sigprocmask(SIG_UNBLOCK, &ourset, NULL))) {
++	  warn (_("cannot set signal handler"));
++	  caught_signal = true;
++	}
++    if (!caught_signal && !same_session && (sigaction(SIGINT, &action, NULL)
++                                     || sigaction(SIGQUIT, &action, NULL)))
++      {
++        warn (_("cannot set signal handler"));
++        caught_signal = true;
++      }
++    }
++  if (!caught_signal)
++    {
++      pid_t pid;
++      for (;;)
++	{
++	  pid = waitpid (child, &status, WUNTRACED);
++
++	  if (pid != (pid_t)-1 && WIFSTOPPED (status))
++	    {
++	      kill (getpid (), SIGSTOP);
++	      /* once we get here, we must have resumed */
++	      kill (pid, SIGCONT);
++	    }
++	  else
++	    break;
++	}
++      if (pid != (pid_t)-1)
++	if (WIFSIGNALED (status))
++	  status = WTERMSIG (status) + 128;
++	else
++	  status = WEXITSTATUS (status);
++      else
++	status = 1;
++    }
++  else
++    status = 1;
++
++  if (caught_signal)
++    {
++      fprintf (stderr, _("\nSession terminated, killing shell..."));
++      kill (child, SIGTERM);
++    }
++
++  cleanup_pam (PAM_SUCCESS);
++
++  if (caught_signal)
++    {
++      sleep (2);
++      kill (child, SIGKILL);
++      fprintf (stderr, _(" ...killed.\n"));
++    }
++  exit (status);
++}
++
++static void
++authenticate (const struct passwd *pw)
++{
++  const struct passwd *lpw;
++  const char *cp, *srvname = NULL;
++  int retval;
++
++  switch (su_mode) {
++  case SU_MODE:
++    srvname = simulate_login ? PAM_SRVNAME_SU_L : PAM_SRVNAME_SU;
++    break;
++  case RUNUSER_MODE:
++    srvname = simulate_login ? PAM_SRVNAME_RUNUSER_L : PAM_SRVNAME_RUNUSER;
++    break;
++  }
++
++  retval = pam_start (srvname, pw->pw_name, &conv, &pamh);
++  if (is_pam_failure(retval))
++    goto done;
++
++  if (isatty (0) && (cp = ttyname (0)) != NULL)
++    {
++      const char *tty;
++
++      if (strncmp (cp, "/dev/", 5) == 0)
++	tty = cp + 5;
++      else
++	tty = cp;
++      retval = pam_set_item (pamh, PAM_TTY, tty);
++      if (is_pam_failure(retval))
++	goto done;
++    }
++
++  lpw = getpwuid (getuid ());
++  if (lpw && lpw->pw_name)
++    {
++      retval = pam_set_item (pamh, PAM_RUSER, (const void *) lpw->pw_name);
++      if (is_pam_failure(retval))
++	goto done;
++    }
++
++  if (su_mode == RUNUSER_MODE)
++    {
++      /*
++       * This is the only difference between runuser(1) and su(1). The command
++       * runuser(1) does not required authentication, because user is root.
++       */
++      if (restricted)
++	errx(EXIT_FAILURE, _("may not be used by non-root users"));
++      return;
++    }
++
++  retval = pam_authenticate (pamh, 0);
++  if (is_pam_failure(retval))
++    goto done;
++
++  retval = pam_acct_mgmt (pamh, 0);
++  if (retval == PAM_NEW_AUTHTOK_REQD)
++    {
++      /* Password has expired.  Offer option to change it.  */
++      retval = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
++    }
++
++done:
++
++  log_su (pw, !is_pam_failure(retval));
++
++  if (is_pam_failure(retval))
++    {
++      const char *msg = pam_strerror(pamh, retval);
++      pam_end(pamh, retval);
++      sleep (getlogindefs_num ("FAIL_DELAY", 1));
++      errx (EXIT_FAILURE, "%s", msg?msg:_("incorrect password"));
++    }
++}
++
++/* Add or clear /sbin and /usr/sbin for the su command
++   used without `-'.  */
++
++/* Set if /sbin is found in path.  */
++#define SBIN_MASK	0x01
++/* Set if /usr/sbin is found in path.  */
++#define USBIN_MASK	0x02
++
++static char *
++addsbin (const char *const path)
++{
++  unsigned char smask = 0;
++  char *ptr, *tmp, *cur, *ret = NULL;
++  size_t len;
++
++  if (!path || *path == 0)
++    return NULL;
++
++  tmp = xstrdup (path);
++  cur = tmp;
++  for (ptr = strsep (&cur, ":"); ptr != NULL; ptr = strsep (&cur, ":"))
++    {
++      if (!strcmp (ptr, "/sbin"))
++	smask |= SBIN_MASK;
++      if (!strcmp (ptr, "/usr/sbin"))
++	smask |= USBIN_MASK;
++    }
++
++  if ((smask & (USBIN_MASK|SBIN_MASK)) == (USBIN_MASK|SBIN_MASK))
++    {
++      free (tmp);
++      return NULL;
++    }
++
++  len = strlen (path);
++  if (!(smask & USBIN_MASK))
++    len += strlen ("/usr/sbin:");
++
++  if (!(smask & SBIN_MASK))
++    len += strlen (":/sbin");
++
++  ret = xmalloc (len + 1);
++  strcpy (tmp, path);
++
++  *ret = 0;
++  cur = tmp;
++  for (ptr = strsep (&cur, ":"); ptr; ptr = strsep (&cur, ":"))
++    {
++      if (!strcmp (ptr, "."))
++	continue;
++      if (*ret)
++	strcat (ret, ":");
++      if (!(smask & USBIN_MASK) && !strcmp (ptr, "/bin"))
++	{
++	  strcat (ret, "/usr/sbin:");
++	  strcat (ret, ptr);
++	  smask |= USBIN_MASK;
++	  continue;
++	}
++      if (!(smask & SBIN_MASK) && !strcmp (ptr, "/usr/bin"))
++	{
++	  strcat (ret, ptr);
++	  strcat (ret, ":/sbin");
++	  smask |= SBIN_MASK;
++	  continue;
++	}
++      strcat (ret, ptr);
++    }
++  free (tmp);
++
++  if (!(smask & USBIN_MASK))
++    strcat (ret, ":/usr/sbin");
++
++  if (!(smask & SBIN_MASK))
++    strcat (ret, ":/sbin");
++
++  return ret;
++}
++
++static char *
++clearsbin (const char *const path)
++{
++  char *ptr, *tmp, *cur, *ret = NULL;
++
++  if (!path || *path == 0)
++    return NULL;
++
++  tmp = xstrdup (path);
++
++  ret = xmalloc (strlen (path) + 1);
++  *ret = 0;
++  cur = tmp;
++  for (ptr = strsep (&cur, ":"); ptr; ptr = strsep (&cur, ":"))
++    {
++      if (!strcmp (ptr, "/sbin"))
++	continue;
++      if (!strcmp (ptr, "/usr/sbin"))
++	continue;
++      if (!strcmp (ptr, "/usr/local/sbin"))
++	continue;
++      if (*ret)
++	strcat (ret, ":");
++      strcat (ret, ptr);
++    }
++  free (tmp);
++
++  return ret;
++}
++
++static void
++set_path(const struct passwd* pw)
++{
++  int r;
++  if (pw->pw_uid)
++    r = logindefs_setenv("PATH", "ENV_PATH", _PATH_DEFPATH);
++
++  else if ((r = logindefs_setenv("PATH", "ENV_ROOTPATH", NULL)) != 0)
++    r = logindefs_setenv("PATH", "ENV_SUPATH", _PATH_DEFPATH_ROOT);
++
++  if (r != 0)
++    err (EXIT_FAILURE,  _("failed to set PATH"));
++}
++
++/* Update `environ' for the new shell based on PW, with SHELL being
++   the value for the SHELL environment variable.  */
++
++static void
++modify_environment (const struct passwd *pw, const char *shell)
++{
++  if (simulate_login)
++    {
++      /* Leave TERM unchanged.  Set HOME, SHELL, USER, LOGNAME, PATH.
++         Unset all other environment variables.  */
++      char const *term = getenv ("TERM");
++      if (term)
++	term = xstrdup (term);
++      environ = xmalloc ((6 + !!term) * sizeof (char *));
++      environ[0] = NULL;
++      if (term)
++	xsetenv ("TERM", term, 1);
++      xsetenv ("HOME", pw->pw_dir, 1);
++      xsetenv ("SHELL", shell, 1);
++      xsetenv ("USER", pw->pw_name, 1);
++      xsetenv ("LOGNAME", pw->pw_name, 1);
++      set_path(pw);
++    }
++  else
++    {
++      /* Set HOME, SHELL, and if not becoming a super-user,
++	 USER and LOGNAME.  */
++      if (change_environment)
++        {
++          xsetenv ("HOME", pw->pw_dir, 1);
++          xsetenv ("SHELL", shell, 1);
++	  if (getlogindefs_bool ("ALWAYS_SET_PATH", 0))
++	    set_path(pw);
++	  else
++	    {
++	      char const *path = getenv ("PATH");
++	      char *new = NULL;
++
++	      if (pw->pw_uid)
++		new = clearsbin (path);
++	      else
++		new = addsbin (path);
++
++	      if (new)
++		{
++		  xsetenv ("PATH", new, 1);
++		  free (new);
++		}
++	    }
++          if (pw->pw_uid)
++            {
++              xsetenv ("USER", pw->pw_name, 1);
++              xsetenv ("LOGNAME", pw->pw_name, 1);
++            }
++        }
++    }
++
++  export_pamenv ();
++}
++
++/* Become the user and group(s) specified by PW.  */
++
++static void
++init_groups (const struct passwd *pw, gid_t *groups, int num_groups)
++{
++  int retval;
++
++  errno = 0;
++
++  if (num_groups)
++    retval = setgroups (num_groups, groups);
++  else
++    retval = initgroups (pw->pw_name, pw->pw_gid);
++
++  if (retval == -1)
++    {
++      cleanup_pam (PAM_ABORT);
++      err (EXIT_FAILURE, _("cannot set groups"));
++    }
++  endgrent ();
++
++  retval = pam_setcred (pamh, PAM_ESTABLISH_CRED);
++  if (is_pam_failure(retval))
++    errx (EXIT_FAILURE, "%s", pam_strerror (pamh, retval));
++  else
++    _pam_cred_established = 1;
++}
++
++static void
++change_identity (const struct passwd *pw)
++{
++  if (setgid (pw->pw_gid))
++    err (EXIT_FAILURE,  _("cannot set group id"));
++  if (setuid (pw->pw_uid))
++    err (EXIT_FAILURE,  _("cannot set user id"));
++}
++
++/* Run SHELL, or DEFAULT_SHELL if SHELL is empty.
++   If COMMAND is nonzero, pass it to the shell with the -c option.
++   Pass ADDITIONAL_ARGS to the shell as more arguments; there
++   are N_ADDITIONAL_ARGS extra arguments.  */
++
++static void
++run_shell (char const *shell, char const *command, char **additional_args,
++	   size_t n_additional_args)
++{
++  size_t n_args = 1 + fast_startup + 2 * !!command + n_additional_args + 1;
++  char const **args = xcalloc (n_args, sizeof *args);
++  size_t argno = 1;
++
++  if (simulate_login)
++    {
++      char *arg0;
++      char *shell_basename;
++
++      shell_basename = basename (shell);
++      arg0 = xmalloc (strlen (shell_basename) + 2);
++      arg0[0] = '-';
++      strcpy (arg0 + 1, shell_basename);
++      args[0] = arg0;
++    }
++  else
++    args[0] = basename (shell);
++  if (fast_startup)
++    args[argno++] = "-f";
++  if (command)
++    {
++      args[argno++] = "-c";
++      args[argno++] = command;
++    }
++  memcpy (args + argno, additional_args, n_additional_args * sizeof *args);
++  args[argno + n_additional_args] = NULL;
++  execv (shell, (char **) args);
++
++  {
++    int exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
++    warn ("%s", shell);
++    exit (exit_status);
++  }
++}
++
++/* Return true if SHELL is a restricted shell (one not returned by
++   getusershell), else false, meaning it is a standard shell.  */
++
++static bool
++restricted_shell (const char *shell)
++{
++  char *line;
++
++  setusershell ();
++  while ((line = getusershell ()) != NULL)
++    {
++      if (*line != '#' && !strcmp (line, shell))
++	{
++	  endusershell ();
++	  return false;
++	}
++    }
++  endusershell ();
++  return true;
++}
++
++static void __attribute__((__noreturn__))
++usage (int status)
++{
++  if (status != EXIT_SUCCESS)
++    fprintf (stderr, _("Try `%s --help' for more information.\n"),
++	     program_invocation_short_name);
++  else
++    {
++      fputs(USAGE_HEADER, stdout);
++      printf (_(" %s [options] [-] [USER [arg]...]\n"), program_invocation_short_name);
++      fputs (_("\n\
++ Change the effective user id and group id to that of USER.\n\
++ A mere - implies -l.   If USER not given, assume root.\n"), stdout);
++      fputs(USAGE_OPTIONS, stdout);
++      fputs (_("\
++ -, -l, --login               make the shell a login shell\n\
++ -c, --command <command>      pass a single command to the shell with -c\n\
++ --session-command <command>  pass a single command to the shell with -c\n\
++                              and do not create a new session\n\
++ -g --group=group             specify the primary group\n\
++ -G --supp-group=group        specify a supplemental group\n\
++ -f, --fast                   pass -f to the shell (for csh or tcsh)\n\
++ -m, --preserve-environment   do not reset environment variables\n\
++ -p                           same as -m\n\
++ -s, --shell <shell>          run shell if /etc/shells allows it\n\
++"), stdout);
++
++      fputs(USAGE_SEPARATOR, stdout);
++      fputs(USAGE_HELP, stdout);
++      fputs(USAGE_VERSION, stdout);
++      printf(USAGE_MAN_TAIL("su(1)"));
++    }
++  exit (status);
++}
++
++static
++void load_config(void)
++{
++  switch (su_mode) {
++  case SU_MODE:
++    logindefs_load_file(_PATH_LOGINDEFS_SU);
++    break;
++  case RUNUSER_MODE:
++    logindefs_load_file(_PATH_LOGINDEFS_RUNUSER);
++    break;
++  }
++
++  logindefs_load_file(_PATH_LOGINDEFS);
++}
++
++/*
++ * Returns 1 if the current user is not root
++ */
++static int
++evaluate_uid(void)
++{
++  uid_t ruid = getuid();
++  uid_t euid = geteuid();
++
++  /* if we're really root and aren't running setuid */
++  return (uid_t) 0 == ruid && ruid == euid ? 0 : 1;
++}
++
++int
++su_main (int argc, char **argv, int mode)
++{
++  int optc;
++  const char *new_user = DEFAULT_USER;
++  char *command = NULL;
++  int request_same_session = 0;
++  char *shell = NULL;
++  struct passwd *pw;
++  struct passwd pw_copy;
++  struct group *gr;
++  gid_t groups[NGROUPS_MAX];
++  int num_supp_groups = 0;
++  int use_gid = 0;
++
++  setlocale (LC_ALL, "");
++  bindtextdomain (PACKAGE, LOCALEDIR);
++  textdomain (PACKAGE);
++
++  su_mode = mode;
++  fast_startup = false;
++  simulate_login = false;
++  change_environment = true;
++
++  while ((optc = getopt_long (argc, argv, "c:fg:G:lmps:hV", longopts, NULL)) != -1)
++    {
++      switch (optc)
++	{
++	case 'c':
++	  command = optarg;
++	  break;
++
++        case 'C':
++          command = optarg;
++          request_same_session = 1;
++          break;
++
++	case 'f':
++	  fast_startup = true;
++	  break;
++
++	case 'g':
++	  gr = getgrnam(optarg);
++	  if (!gr)
++	    errx(EXIT_FAILURE, _("group %s does not exist"), optarg);
++	  use_gid = 1;
++	  groups[0] = gr->gr_gid;
++	  break;
++
++	case 'G':
++	  num_supp_groups++;
++	  if (num_supp_groups >= NGROUPS_MAX)
++	     errx(EXIT_FAILURE,
++		  _("can't specify more than %d supplemental groups"),
++		  NGROUPS_MAX - 1);
++	  gr = getgrnam(optarg);
++	  if (!gr)
++	    errx(EXIT_FAILURE, _("group %s does not exist"), optarg);
++	  groups[num_supp_groups] = gr->gr_gid;
++	  break;
++
++	case 'l':
++	  simulate_login = true;
++	  break;
++
++	case 'm':
++	case 'p':
++	  change_environment = false;
++	  break;
++
++	case 's':
++	  shell = optarg;
++	  break;
++
++	case 'h':
++	  usage(0);
++
++	case 'V':
++	  printf(UTIL_LINUX_VERSION);
++	  exit(EXIT_SUCCESS);
++
++	default:
++	  usage (EXIT_FAILURE);
++	}
++    }
++
++  restricted = evaluate_uid ();
++
++  if (optind < argc && !strcmp (argv[optind], "-"))
++    {
++      simulate_login = true;
++      ++optind;
++    }
++  if (optind < argc)
++    new_user = argv[optind++];
++
++  if ((num_supp_groups || use_gid) && restricted)
++    errx(EXIT_FAILURE, _("only root can specify alternative groups"));
++
++  logindefs_load_defaults = load_config;
++
++  pw = getpwnam (new_user);
++  if (! (pw && pw->pw_name && pw->pw_name[0] && pw->pw_dir && pw->pw_dir[0]
++	 && pw->pw_passwd))
++    errx (EXIT_FAILURE, _("user %s does not exist"), new_user);
++
++  /* Make a copy of the password information and point pw at the local
++     copy instead.  Otherwise, some systems (e.g. Linux) would clobber
++     the static data through the getlogin call from log_su.
++     Also, make sure pw->pw_shell is a nonempty string.
++     It may be NULL when NEW_USER is a username that is retrieved via NIS (YP),
++     but that doesn't have a default shell listed.  */
++  pw_copy = *pw;
++  pw = &pw_copy;
++  pw->pw_name = xstrdup (pw->pw_name);
++  pw->pw_passwd = xstrdup (pw->pw_passwd);
++  pw->pw_dir = xstrdup (pw->pw_dir);
++  pw->pw_shell = xstrdup (pw->pw_shell && pw->pw_shell[0]
++			  ? pw->pw_shell
++			  : DEFAULT_SHELL);
++  endpwent ();
++
++  if (num_supp_groups && !use_gid)
++  {
++    pw->pw_gid = groups[1];
++    memmove (groups, groups + 1, sizeof(gid_t) * num_supp_groups);
++  }
++  else if (use_gid)
++  {
++    pw->pw_gid = groups[0];
++    num_supp_groups++;
++  }
++
++  authenticate (pw);
++
++  if (request_same_session || !command || !pw->pw_uid)
++    same_session = 1;
++
++  if (!shell && !change_environment)
++    shell = getenv ("SHELL");
++  if (shell && getuid () != 0 && restricted_shell (pw->pw_shell))
++    {
++      /* The user being su'd to has a nonstandard shell, and so is
++	 probably a uucp account or has restricted access.  Don't
++	 compromise the account by allowing access with a standard
++	 shell.  */
++      warnx (_("using restricted shell %s"), pw->pw_shell);
++      shell = NULL;
++    }
++  shell = xstrdup (shell ? shell : pw->pw_shell);
++
++  init_groups (pw, groups, num_supp_groups);
++
++  create_watching_parent ();
++  /* Now we're in the child.  */
++
++  change_identity (pw);
++  if (!same_session)
++    setsid ();
++
++  /* Set environment after pam_open_session, which may put KRB5CCNAME
++     into the pam_env, etc.  */
++
++  modify_environment (pw, shell);
++
++  if (simulate_login && chdir (pw->pw_dir) != 0)
++    warn (_("warning: cannot change directory to %s"), pw->pw_dir);
++
++  run_shell (shell, command, argv + optind, max (0, argc - optind));
++}
++
++// vim: sw=2 cinoptions=>4,n-2,{2,^-2,\:2,=2,g0,h2,p5,t0,+2,(0,u0,w1,m1
+diff --git a/login-utils/su-common.h b/login-utils/su-common.h
+new file mode 100644
+index 0000000..7cf3769
+--- /dev/null
++++ b/login-utils/su-common.h
+@@ -0,0 +1,11 @@
++#ifndef UTIL_LINUX_SU_COMMON_H
++#define UTIL_LINUX_SU_COMMON_H
++
++enum {
++	SU_MODE,
++	RUNUSER_MODE
++};
++
++extern int su_main(int argc, char **argv, int mode);
++
++#endif /* UTIL_LINUX_SU_COMMON */
+diff --git a/login-utils/su.1 b/login-utils/su.1
+index 598cebd..59e1731 100644
+--- a/login-utils/su.1
++++ b/login-utils/su.1
+@@ -59,6 +59,12 @@ Pass
+ to the shell which may or may not be useful depending on the
+ shell.
+ .TP
++\fB\-g\fR, \fB\-\-group\fR=\fIgroup\fR\fR
++specify the primary group, this option is allowed for root user only
++.TP
++\fB\-G\fR, \fB\-\-supp-group\fR=\fIgroup\fR\fR
++specify a supplemental group, this option is allowed for root user only
++.TP
+ \fB\-\fR, \fB\-l\fR, \fB\-\-login\fR
+ Starts the shell as login shell with an environment similar to a real
+ login:
+diff --git a/login-utils/su.c b/login-utils/su.c
+index c6b8bce..29c10f0 100644
+--- a/login-utils/su.c
++++ b/login-utils/su.c
+@@ -1,689 +1,8 @@
+-/* su for Linux.  Run a shell with substitute user and group IDs.
+-   Copyright (C) 1992-2006 Free Software Foundation, Inc.
+-   Copyright (C) 2012 SUSE Linux Products GmbH, Nuernberg
+ 
+-   This program is free software; you can redistribute it and/or modify
+-   it under the terms of the GNU General Public License as published by
+-   the Free Software Foundation; either version 2, or (at your option)
+-   any later version.
++#include "su-common.h"
+ 
+-   This program is distributed in the hope that it will be useful,
+-   but WITHOUT ANY WARRANTY; without even the implied warranty of
+-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-   GNU General Public License for more details.
+-
+-   You should have received a copy of the GNU General Public License
+-   along with this program; if not, write to the Free Software Foundation,
+-   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+-
+-/* Run a shell with the real and effective UID and GID and groups
+-   of USER, default `root'.
+-
+-   The shell run is taken from USER's password entry, /bin/sh if
+-   none is specified there.  If the account has a password, su
+-   prompts for a password unless run by a user with real UID 0.
+-
+-   Does not change the current directory.
+-   Sets `HOME' and `SHELL' from the password entry for USER, and if
+-   USER is not root, sets `USER' and `LOGNAME' to USER.
+-   The subshell is not a login shell.
+-
+-   If one or more ARGs are given, they are passed as additional
+-   arguments to the subshell.
+-
+-   Does not handle /bin/sh or other shells specially
+-   (setting argv[0] to "-su", passing -c only to certain shells, etc.).
+-   I don't see the point in doing that, and it's ugly.
+-
+-   Based on an implemenation by David MacKenzie <djm at gnu.ai.mit.edu>.  */
+-
+-enum
+-{
+-  EXIT_CANNOT_INVOKE = 126,
+-  EXIT_ENOENT = 127
+-};
+-
+-#include <config.h>
+-#include <stdio.h>
+-#include <getopt.h>
+-#include <sys/types.h>
+-#include <pwd.h>
+-#include <grp.h>
+-#include <security/pam_appl.h>
+-#include <security/pam_misc.h>
+-#include <signal.h>
+-#include <sys/wait.h>
+-#include <syslog.h>
+-
+-#include "err.h"
+-
+-#include <stdbool.h>
+-#include "c.h"
+-#include "xalloc.h"
+-#include "nls.h"
+-#include "pathnames.h"
+-#include "env.h"
+-
+-/* name of the pam configuration files. separate configs for su and su -  */
+-#define PAM_SERVICE_NAME "su"
+-#define PAM_SERVICE_NAME_L "su-l"
+-
+-#define is_pam_failure(_rc)	((_rc) != PAM_SUCCESS)
+-
+-#include "logindefs.h"
+-
+-/* 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"
+-
+-#ifndef HAVE_ENVIRON_DECL
+-extern char **environ;
+-#endif
+-
+-static void run_shell (char const *, char const *, char **, size_t)
+-     __attribute__ ((__noreturn__));
+-
+-/* If true, pass the `-f' option to the subshell.  */
+-static bool fast_startup;
+-
+-/* If true, simulate a login instead of just starting a shell.  */
+-static bool simulate_login;
+-
+-/* If true, change some environment vars to indicate the user su'd to.  */
+-static bool change_environment;
+-
+-/* If true, then don't call setsid() with a command. */
+-int same_session = 0;
+-
+-static bool _pam_session_opened;
+-static bool _pam_cred_established;
+-static sig_atomic_t volatile caught_signal = false;
+-static pam_handle_t *pamh = NULL;
+-
+-static struct option const longopts[] =
+-{
+-  {"command", required_argument, NULL, 'c'},
+-  {"session-command", required_argument, NULL, 'C'},
+-  {"fast", no_argument, NULL, 'f'},
+-  {"login", no_argument, NULL, 'l'},
+-  {"preserve-environment", no_argument, NULL, 'p'},
+-  {"shell", required_argument, NULL, 's'},
+-  {"help", no_argument, 0, 'h'},
+-  {"version", no_argument, 0, 'V'},
+-  {NULL, 0, NULL, 0}
+-};
+-
+-/* Log the fact that someone has run su to the user given by PW;
+-   if SUCCESSFUL is true, they gave the correct password, etc.  */
+-
+-static void
+-log_su (struct passwd const *pw, bool successful)
+-{
+-  const char *new_user, *old_user, *tty;
+-
+-  new_user = pw->pw_name;
+-  /* The utmp entry (via getlogin) is probably the best way to identify
+-     the user, especially if someone su's from a su-shell.  */
+-  old_user = getlogin ();
+-  if (!old_user)
+-    {
+-      /* getlogin can fail -- usually due to lack of utmp entry.
+-	 Resort to getpwuid.  */
+-      struct passwd *pwd = getpwuid (getuid ());
+-      old_user = (pwd ? pwd->pw_name : "");
+-    }
+-  tty = ttyname (STDERR_FILENO);
+-  if (!tty)
+-    tty = "none";
+-
+-  openlog (program_invocation_short_name, 0 , LOG_AUTH);
+-  syslog (LOG_NOTICE, "%s(to %s) %s on %s",
+-	  successful ? "" : "FAILED SU ",
+-	  new_user, old_user, tty);
+-  closelog ();
+-}
+-
+-static struct pam_conv conv =
+-{
+-  misc_conv,
+-  NULL
+-};
+-
+-static void
+-cleanup_pam (int retcode)
++int main(int argv, char **argc)
+ {
+-  int saved_errno = errno;
+-
+-  if (_pam_session_opened)
+-    pam_close_session (pamh, 0);
+-
+-  if (_pam_cred_established)
+-    pam_setcred (pamh, PAM_DELETE_CRED | PAM_SILENT);
+-
+-  pam_end(pamh, retcode);
+-
+-  errno = saved_errno;
+-}
+-
+-/* Signal handler for parent process.  */
+-static void
+-su_catch_sig (int sig __attribute__((__unused__)))
+-{
+-  caught_signal = true;
+-}
+-
+-/* Export env variables declared by PAM modules.  */
+-static void
+-export_pamenv (void)
+-{
+-  char **env;
+-
+-  /* This is a copy but don't care to free as we exec later anyways.  */
+-  env = pam_getenvlist (pamh);
+-  while (env && *env)
+-    {
+-      if (putenv (*env) != 0)
+-	err (EXIT_FAILURE, NULL);
+-      env++;
+-    }
+-}
+-
+-static void
+-create_watching_parent (void)
+-{
+-  pid_t child;
+-  sigset_t ourset;
+-  int status = 0;
+-  int retval;
+-
+-  retval = pam_open_session (pamh, 0);
+-  if (is_pam_failure(retval))
+-    {
+-      cleanup_pam (retval);
+-      errx (EXIT_FAILURE, _("cannot not open session: %s"),
+-	     pam_strerror (pamh, retval));
+-    }
+-  else
+-    _pam_session_opened = 1;
+-
+-  child = fork ();
+-  if (child == (pid_t) -1)
+-    {
+-      cleanup_pam (PAM_ABORT);
+-      err (EXIT_FAILURE, _("cannot create child process"));
+-    }
+-
+-  /* the child proceeds to run the shell */
+-  if (child == 0)
+-    return;
+-
+-  /* In the parent watch the child.  */
+-
+-  /* su without pam support does not have a helper that keeps
+-     sitting on any directory so let's go to /.  */
+-  if (chdir ("/") != 0)
+-    warn (_("cannot change directory to %s"), "/");
+-
+-  sigfillset (&ourset);
+-  if (sigprocmask (SIG_BLOCK, &ourset, NULL))
+-    {
+-      warn (_("cannot block signals"));
+-      caught_signal = true;
+-    }
+-  if (!caught_signal)
+-    {
+-      struct sigaction action;
+-      action.sa_handler = su_catch_sig;
+-      sigemptyset (&action.sa_mask);
+-      action.sa_flags = 0;
+-      sigemptyset (&ourset);
+-    if (!same_session)
+-      {
+-        if (sigaddset(&ourset, SIGINT) || sigaddset(&ourset, SIGQUIT))
+-          {
+-            warn (_("cannot set signal handler"));
+-            caught_signal = true;
+-          }
+-      }
+-    if (!caught_signal && (sigaddset(&ourset, SIGTERM)
+-                    || sigaddset(&ourset, SIGALRM)
+-                    || sigaction(SIGTERM, &action, NULL)
+-                    || sigprocmask(SIG_UNBLOCK, &ourset, NULL))) {
+-	  warn (_("cannot set signal handler"));
+-	  caught_signal = true;
+-	}
+-    if (!caught_signal && !same_session && (sigaction(SIGINT, &action, NULL)
+-                                     || sigaction(SIGQUIT, &action, NULL)))
+-      {
+-        warn (_("cannot set signal handler"));
+-        caught_signal = true;
+-      }
+-    }
+-  if (!caught_signal)
+-    {
+-      pid_t pid;
+-      for (;;)
+-	{
+-	  pid = waitpid (child, &status, WUNTRACED);
+-
+-	  if (pid != (pid_t)-1 && WIFSTOPPED (status))
+-	    {
+-	      kill (getpid (), SIGSTOP);
+-	      /* once we get here, we must have resumed */
+-	      kill (pid, SIGCONT);
+-	    }
+-	  else
+-	    break;
+-	}
+-      if (pid != (pid_t)-1)
+-	if (WIFSIGNALED (status))
+-	  status = WTERMSIG (status) + 128;
+-	else
+-	  status = WEXITSTATUS (status);
+-      else
+-	status = 1;
+-    }
+-  else
+-    status = 1;
+-
+-  if (caught_signal)
+-    {
+-      fprintf (stderr, _("\nSession terminated, killing shell..."));
+-      kill (child, SIGTERM);
+-    }
+-
+-  cleanup_pam (PAM_SUCCESS);
+-
+-  if (caught_signal)
+-    {
+-      sleep (2);
+-      kill (child, SIGKILL);
+-      fprintf (stderr, _(" ...killed.\n"));
+-    }
+-  exit (status);
+-}
+-
+-static void
+-authenticate (const struct passwd *pw)
+-{
+-  const struct passwd *lpw;
+-  const char *cp;
+-  int retval;
+-
+-  retval = pam_start (simulate_login ? PAM_SERVICE_NAME_L : PAM_SERVICE_NAME,
+-		      pw->pw_name, &conv, &pamh);
+-  if (is_pam_failure(retval))
+-    goto done;
+-
+-  if (isatty (0) && (cp = ttyname (0)) != NULL)
+-    {
+-      const char *tty;
+-
+-      if (strncmp (cp, "/dev/", 5) == 0)
+-	tty = cp + 5;
+-      else
+-	tty = cp;
+-      retval = pam_set_item (pamh, PAM_TTY, tty);
+-      if (is_pam_failure(retval))
+-	goto done;
+-    }
+-
+-  lpw = getpwuid (getuid ());
+-  if (lpw && lpw->pw_name)
+-    {
+-      retval = pam_set_item (pamh, PAM_RUSER, (const void *) lpw->pw_name);
+-      if (is_pam_failure(retval))
+-	goto done;
+-    }
+-
+-  retval = pam_authenticate (pamh, 0);
+-  if (is_pam_failure(retval))
+-    goto done;
+-
+-  retval = pam_acct_mgmt (pamh, 0);
+-  if (retval == PAM_NEW_AUTHTOK_REQD)
+-    {
+-      /* Password has expired.  Offer option to change it.  */
+-      retval = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
+-    }
+-
+-done:
+-
+-  log_su (pw, !is_pam_failure(retval));
+-
+-  if (is_pam_failure(retval))
+-    {
+-      const char *msg = pam_strerror(pamh, retval);
+-      pam_end(pamh, retval);
+-      sleep (getlogindefs_num ("FAIL_DELAY", 1));
+-      errx (EXIT_FAILURE, "%s", msg?msg:_("incorrect password"));
+-    }
+-}
+-
+-static void
+-set_path(const struct passwd* pw)
+-{
+-  int r;
+-  if (pw->pw_uid)
+-    r = logindefs_setenv("PATH", "ENV_PATH", _PATH_DEFPATH);
+-
+-  else if ((r = logindefs_setenv("PATH", "ENV_ROOTPATH", NULL)) != 0)
+-    r = logindefs_setenv("PATH", "ENV_SUPATH", _PATH_DEFPATH_ROOT);
+-
+-  if (r != 0)
+-    err (EXIT_FAILURE,  _("failed to set PATH"));
+-}
+-
+-/* Update `environ' for the new shell based on PW, with SHELL being
+-   the value for the SHELL environment variable.  */
+-
+-static void
+-modify_environment (const struct passwd *pw, const char *shell)
+-{
+-  if (simulate_login)
+-    {
+-      /* Leave TERM unchanged.  Set HOME, SHELL, USER, LOGNAME, PATH.
+-         Unset all other environment variables.  */
+-      char const *term = getenv ("TERM");
+-      if (term)
+-	term = xstrdup (term);
+-      environ = xmalloc ((6 + !!term) * sizeof (char *));
+-      environ[0] = NULL;
+-      if (term)
+-	xsetenv ("TERM", term, 1);
+-      xsetenv ("HOME", pw->pw_dir, 1);
+-      xsetenv ("SHELL", shell, 1);
+-      xsetenv ("USER", pw->pw_name, 1);
+-      xsetenv ("LOGNAME", pw->pw_name, 1);
+-      set_path(pw);
+-    }
+-  else
+-    {
+-      /* Set HOME, SHELL, and if not becoming a super-user,
+-	 USER and LOGNAME.  */
+-      if (change_environment)
+-        {
+-          xsetenv ("HOME", pw->pw_dir, 1);
+-          xsetenv ("SHELL", shell, 1);
+-	  if (getlogindefs_bool ("ALWAYS_SET_PATH", 0))
+-	    set_path(pw);
+-
+-          if (pw->pw_uid)
+-            {
+-              xsetenv ("USER", pw->pw_name, 1);
+-              xsetenv ("LOGNAME", pw->pw_name, 1);
+-            }
+-        }
+-    }
+-
+-  export_pamenv ();
+-}
+-
+-/* Become the user and group(s) specified by PW.  */
+-
+-static void
+-init_groups (const struct passwd *pw)
+-{
+-  int retval;
+-  errno = 0;
+-  if (initgroups (pw->pw_name, pw->pw_gid) == -1)
+-    {
+-      cleanup_pam (PAM_ABORT);
+-      err (EXIT_FAILURE, _("cannot set groups"));
+-    }
+-  endgrent ();
+-
+-  retval = pam_setcred (pamh, PAM_ESTABLISH_CRED);
+-  if (is_pam_failure(retval))
+-    errx (EXIT_FAILURE, "%s", pam_strerror (pamh, retval));
+-  else
+-    _pam_cred_established = 1;
+-}
+-
+-static void
+-change_identity (const struct passwd *pw)
+-{
+-  if (setgid (pw->pw_gid))
+-    err (EXIT_FAILURE,  _("cannot set group id"));
+-  if (setuid (pw->pw_uid))
+-    err (EXIT_FAILURE,  _("cannot set user id"));
+-}
+-
+-/* Run SHELL, or DEFAULT_SHELL if SHELL is empty.
+-   If COMMAND is nonzero, pass it to the shell with the -c option.
+-   Pass ADDITIONAL_ARGS to the shell as more arguments; there
+-   are N_ADDITIONAL_ARGS extra arguments.  */
+-
+-static void
+-run_shell (char const *shell, char const *command, char **additional_args,
+-	   size_t n_additional_args)
+-{
+-  size_t n_args = 1 + fast_startup + 2 * !!command + n_additional_args + 1;
+-  char const **args = xcalloc (n_args, sizeof *args);
+-  size_t argno = 1;
+-
+-  if (simulate_login)
+-    {
+-      char *arg0;
+-      char *shell_basename;
+-
+-      shell_basename = basename (shell);
+-      arg0 = xmalloc (strlen (shell_basename) + 2);
+-      arg0[0] = '-';
+-      strcpy (arg0 + 1, shell_basename);
+-      args[0] = arg0;
+-    }
+-  else
+-    args[0] = basename (shell);
+-  if (fast_startup)
+-    args[argno++] = "-f";
+-  if (command)
+-    {
+-      args[argno++] = "-c";
+-      args[argno++] = command;
+-    }
+-  memcpy (args + argno, additional_args, n_additional_args * sizeof *args);
+-  args[argno + n_additional_args] = NULL;
+-  execv (shell, (char **) args);
+-
+-  {
+-    int exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
+-    warn ("%s", shell);
+-    exit (exit_status);
+-  }
+-}
+-
+-/* Return true if SHELL is a restricted shell (one not returned by
+-   getusershell), else false, meaning it is a standard shell.  */
+-
+-static bool
+-restricted_shell (const char *shell)
+-{
+-  char *line;
+-
+-  setusershell ();
+-  while ((line = getusershell ()) != NULL)
+-    {
+-      if (*line != '#' && !strcmp (line, shell))
+-	{
+-	  endusershell ();
+-	  return false;
+-	}
+-    }
+-  endusershell ();
+-  return true;
+-}
+-
+-static void __attribute__((__noreturn__))
+-usage (int status)
+-{
+-  if (status != EXIT_SUCCESS)
+-    fprintf (stderr, _("Try `%s --help' for more information.\n"),
+-	     program_invocation_short_name);
+-  else
+-    {
+-      fputs(USAGE_HEADER, stdout);
+-      printf (_(" %s [options] [-] [USER [arg]...]\n"), program_invocation_short_name);
+-      fputs (_("\n\
+- Change the effective user id and group id to that of USER.\n\
+- A mere - implies -l.   If USER not given, assume root.\n"), stdout);
+-      fputs(USAGE_OPTIONS, stdout);
+-      fputs (_("\
+- -, -l, --login               make the shell a login shell\n\
+- -c, --command <command>      pass a single command to the shell with -c\n\
+- --session-command <command>  pass a single command to the shell with -c\n\
+-                              and do not create a new session\n\
+- -f, --fast                   pass -f to the shell (for csh or tcsh)\n\
+- -m, --preserve-environment   do not reset environment variables\n\
+- -p                           same as -m\n\
+- -s, --shell <shell>          run shell if /etc/shells allows it\n\
+-"), stdout);
+-
+-      fputs(USAGE_SEPARATOR, stdout);
+-      fputs(USAGE_HELP, stdout);
+-      fputs(USAGE_VERSION, stdout);
+-      printf(USAGE_MAN_TAIL("su(1)"));
+-    }
+-  exit (status);
+-}
+-
+-static
+-void load_config(void)
+-{
+-  logindefs_load_file("/etc/default/su");
+-  logindefs_load_file(_PATH_LOGINDEFS);
+-}
+-
+-int
+-main (int argc, char **argv)
+-{
+-  int optc;
+-  const char *new_user = DEFAULT_USER;
+-  char *command = NULL;
+-  int request_same_session = 0;
+-  char *shell = NULL;
+-  struct passwd *pw;
+-  struct passwd pw_copy;
+-
+-  setlocale (LC_ALL, "");
+-  bindtextdomain (PACKAGE, LOCALEDIR);
+-  textdomain (PACKAGE);
+-
+-  fast_startup = false;
+-  simulate_login = false;
+-  change_environment = true;
+-
+-  while ((optc = getopt_long (argc, argv, "c:flmps:hV", longopts, NULL)) != -1)
+-    {
+-      switch (optc)
+-	{
+-	case 'c':
+-	  command = optarg;
+-	  break;
+-
+-        case 'C':
+-          command = optarg;
+-          request_same_session = 1;
+-          break;
+-
+-	case 'f':
+-	  fast_startup = true;
+-	  break;
+-
+-	case 'l':
+-	  simulate_login = true;
+-	  break;
+-
+-	case 'm':
+-	case 'p':
+-	  change_environment = false;
+-	  break;
+-
+-	case 's':
+-	  shell = optarg;
+-	  break;
+-
+-	case 'h':
+-	  usage(0);
+-
+-	case 'V':
+-	  printf(UTIL_LINUX_VERSION);
+-	  exit(EXIT_SUCCESS);
+-
+-	default:
+-	  usage (EXIT_FAILURE);
+-	}
+-    }
+-
+-  if (optind < argc && !strcmp (argv[optind], "-"))
+-    {
+-      simulate_login = true;
+-      ++optind;
+-    }
+-  if (optind < argc)
+-    new_user = argv[optind++];
+-
+-  logindefs_load_defaults = load_config;
+-
+-  pw = getpwnam (new_user);
+-  if (! (pw && pw->pw_name && pw->pw_name[0] && pw->pw_dir && pw->pw_dir[0]
+-	 && pw->pw_passwd))
+-    errx (EXIT_FAILURE, _("user %s does not exist"), new_user);
+-
+-  /* Make a copy of the password information and point pw at the local
+-     copy instead.  Otherwise, some systems (e.g. Linux) would clobber
+-     the static data through the getlogin call from log_su.
+-     Also, make sure pw->pw_shell is a nonempty string.
+-     It may be NULL when NEW_USER is a username that is retrieved via NIS (YP),
+-     but that doesn't have a default shell listed.  */
+-  pw_copy = *pw;
+-  pw = &pw_copy;
+-  pw->pw_name = xstrdup (pw->pw_name);
+-  pw->pw_passwd = xstrdup (pw->pw_passwd);
+-  pw->pw_dir = xstrdup (pw->pw_dir);
+-  pw->pw_shell = xstrdup (pw->pw_shell && pw->pw_shell[0]
+-			  ? pw->pw_shell
+-			  : DEFAULT_SHELL);
+-  endpwent ();
+-
+-  authenticate (pw);
+-
+-  if (request_same_session || !command || !pw->pw_uid)
+-    same_session = 1;
+-
+-  if (!shell && !change_environment)
+-    shell = getenv ("SHELL");
+-  if (shell && getuid () != 0 && restricted_shell (pw->pw_shell))
+-    {
+-      /* The user being su'd to has a nonstandard shell, and so is
+-	 probably a uucp account or has restricted access.  Don't
+-	 compromise the account by allowing access with a standard
+-	 shell.  */
+-      warnx (_("using restricted shell %s"), pw->pw_shell);
+-      shell = NULL;
+-    }
+-  shell = xstrdup (shell ? shell : pw->pw_shell);
+-
+-  init_groups (pw);
+-
+-  create_watching_parent ();
+-  /* Now we're in the child.  */
+-
+-  change_identity (pw);
+-  if (!same_session)
+-    setsid ();
+-
+-  /* Set environment after pam_open_session, which may put KRB5CCNAME
+-     into the pam_env, etc.  */
+-
+-  modify_environment (pw, shell);
+-
+-  if (simulate_login && chdir (pw->pw_dir) != 0)
+-    warn (_("warning: cannot change directory to %s"), pw->pw_dir);
+-
+-  run_shell (shell, command, argv + optind, max (0, argc - optind));
++	return su_main(argv, argc, SU_MODE);
+ }
+ 
+-// vim: sw=2 cinoptions=>4,n-2,{2,^-2,\:2,=2,g0,h2,p5,t0,+2,(0,u0,w1,m1
================================================================

---- gitweb:

http://git.pld-linux.org/gitweb.cgi/packages/util-linux.git/commitdiff/034a23bb423faa90084513d192d871f92613975e



More information about the pld-cvs-commit mailing list