[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