1 diff --git a/configure.ac b/configure.ac
2 index 87e85fa..ead559c 100644
5 @@ -1149,6 +1154,15 @@ UL_REQUIRES_HAVE([su], [security_pam_misc_h], [PAM header file])
6 AM_CONDITIONAL(BUILD_SU, test "x$build_su" = xyes)
9 +AC_ARG_ENABLE([runuser],
10 + AS_HELP_STRING([--disable-runuser], [do not build runuser]),
11 + [], enable_runuser=yes
13 +UL_BUILD_INIT([runuser])
14 +UL_REQUIRES_HAVE([runuser], [security_pam_misc_h], [PAM header file])
15 +AM_CONDITIONAL(BUILD_RUNUSER, test "x$build_runuser" = xyes)
18 AC_ARG_ENABLE([schedutils],
19 AS_HELP_STRING([--disable-schedutils], [do not build chrt, ionice, teskset]),
20 [], enable_schedutils=yes
21 diff --git a/login-utils/Makemodule.am b/login-utils/Makemodule.am
22 index e10da46..755a361 100644
23 --- a/login-utils/Makemodule.am
24 +++ b/login-utils/Makemodule.am
26 dist_man_MANS += login-utils/su.1
29 + login-utils/su-common.c \
30 + login-utils/su-common.h \
31 login-utils/logindefs.c \
32 login-utils/logindefs.h
33 su_CFLAGS = $(SUID_CFLAGS) $(AM_CFLAGS)
39 +bin_PROGRAMS += runuser
40 +dist_man_MANS += login-utils/runuser.1
42 + login-utils/runuser.c \
43 + login-utils/su-common.c \
44 + login-utils/su-common.h \
45 + login-utils/logindefs.c \
46 + login-utils/logindefs.h
47 +runuser_LDADD = $(LDADD) -lpam -lpam_misc
52 usrbin_exec_PROGRAMS += newgrp
53 dist_man_MANS += login-utils/newgrp.1
54 diff --git a/login-utils/runuser.1 b/login-utils/runuser.1
56 index 0000000..66ad1c4
58 +++ b/login-utils/runuser.1
60 +.TH RUNUSER "1" "August 2012" "util-linux" "User Commands"
62 +runuser \- run a command with substitute user and group ID
65 +[options...] [\-] [user [args...]]
68 +allows to run commands with substitute user and group ID.
69 +The difference between the commands
75 +does not ask for password, because it may be executed by root user only.
78 +does not have to be installed with suid permissions.
80 +When called without arguments
82 +defaults to running an interactive shell as
85 +For backward compatibility
87 +defaults to not change the current directory and to only set the
88 +environment variables
98 +is not root). It is recommended to always use the
100 +option (instead it's shortcut
102 +to avoid side effects caused by mixing environments.
106 +uses PAM for session management.
109 +\fB\-c\fR \fIcommand\fR, \fB\-\-command\fR=\fIcommand\fR
112 +to the shell with the
116 +\fB\-\-session\-command\fR=\fIcommand\fR
119 +but do not create a new session (discouraged).
121 +\fB\-f\fR, \fB\-\-fast\fR
124 +to the shell which may or may not be useful depending on the
127 +\fB\-g\fR, \fB\-\-group\fR=\fIgroup\fR\fR
128 +specify the primary group, this option is allowed for root user only
130 +\fB\-G\fR, \fB\-\-supp-group\fR=\fIgroup\fR\fR
131 +specify a supplemental group, this option is allowed for root user only
133 +\fB\-\fR, \fB\-l\fR, \fB\-\-login\fR
134 +Starts the shell as login shell with an environment similar to a real
139 +clears all environment variables except for
143 +initializes the environment variables
151 +changes to the target user's home directory
154 +sets argv[0] of the shell to
156 +in order to make the shell a login shell
159 +\fB\-m\fR, \fB\-p\fR, \fB\-\-preserve-environment\fR
160 +Preserves the whole environment, ie does not set
167 +\fB\-s\fR \fISHELL\fR, \fB\-\-shell\fR=\fISHELL\fR
168 +Runs the specified shell instead of the default. The shell to run is
169 +selected according to the following rules in order:
173 +the shell specified with
177 +The shell specified in the environment variable
180 +.B \-\-preserve-environment
184 +the shell listed in the passwd entry of the target user
190 +If the target user has a restricted shell (i.e. not listed in
195 +environment variables are ignored unless the calling user is root.
198 +Display help text and exit.
201 +Display version information and exit.
205 +.I /etc/default/runuser
208 +configuration files. The following configuration items are relevant
215 +Defines the PATH environment variable for a regular user. The
217 +.IR /usr/local/bin:\:/bin:\:/usr/bin .
226 +Defines the PATH environment variable for root. The default value is
227 +.IR /usr/local/sbin:\:/usr/local/bin:\:/sbin:\:/bin:\:/usr/sbin:\:/usr/bin .
235 +and \-\-login and \-\-preserve\-environment were not specified
242 +normally returns the exit status of the command it executed. If the
243 +command was killed by a signal,
245 +returns the number of the signal plus 128.
247 +Exit status generated by
253 +Generic error before executing the requested command
256 +The requested command could not be executed
259 +The requested command could was not found
265 +default PAM configuration file
267 +/etc/pam.d/runuser-l
268 +PAM configuration file if \-\-login is specified
270 +/etc/default/runuser
271 +runuser specific logindef config file
274 +global logindef config file
282 +Derived from coreutils' su which was based on an implemenation from
283 +David MacKenzie and Fedora runuser command from Dan Walsh.
285 +The runuser command is part of the util-linux package and is
287 +.UR ftp://\:ftp.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
288 +Linux Kernel Archive
290 diff --git a/login-utils/runuser.c b/login-utils/runuser.c
292 index 0000000..d761a14
294 +++ b/login-utils/runuser.c
297 +#include "su-common.h"
299 +int main(int argv, char **argc)
301 + return su_main(argv, argc, RUNUSER_MODE);
303 diff --git a/login-utils/su-common.c b/login-utils/su-common.c
305 index 0000000..d1fecd7
307 +++ b/login-utils/su-common.c
309 +/* su for Linux. Run a shell with substitute user and group IDs.
310 + Copyright (C) 1992-2006 Free Software Foundation, Inc.
311 + Copyright (C) 2012 SUSE Linux Products GmbH, Nuernberg
313 + This program is free software; you can redistribute it and/or modify
314 + it under the terms of the GNU General Public License as published by
315 + the Free Software Foundation; either version 2, or (at your option)
318 + This program is distributed in the hope that it will be useful,
319 + but WITHOUT ANY WARRANTY; without even the implied warranty of
320 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
321 + GNU General Public License for more details.
323 + You should have received a copy of the GNU General Public License
324 + along with this program; if not, write to the Free Software Foundation,
325 + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
327 +/* Run a shell with the real and effective UID and GID and groups
328 + of USER, default `root'.
330 + The shell run is taken from USER's password entry, /bin/sh if
331 + none is specified there. If the account has a password, su
332 + prompts for a password unless run by a user with real UID 0.
334 + Does not change the current directory.
335 + Sets `HOME' and `SHELL' from the password entry for USER, and if
336 + USER is not root, sets `USER' and `LOGNAME' to USER.
337 + The subshell is not a login shell.
339 + If one or more ARGs are given, they are passed as additional
340 + arguments to the subshell.
342 + Does not handle /bin/sh or other shells specially
343 + (setting argv[0] to "-su", passing -c only to certain shells, etc.).
344 + I don't see the point in doing that, and it's ugly.
346 + Based on an implemenation by David MacKenzie <djm@gnu.ai.mit.edu>. */
350 + EXIT_CANNOT_INVOKE = 126,
357 +#include <sys/types.h>
360 +#include <security/pam_appl.h>
361 +#include <security/pam_misc.h>
363 +#include <sys/wait.h>
368 +#include <stdbool.h>
372 +#include "pathnames.h"
375 +/* name of the pam configuration files. separate configs for su and su - */
376 +#define PAM_SRVNAME_SU "su"
377 +#define PAM_SRVNAME_SU_L "su-l"
379 +#define PAM_SRVNAME_RUNUSER "runuser"
380 +#define PAM_SRVNAME_RUNUSER_L "runuser-l"
382 +#define _PATH_LOGINDEFS_SU "/etc/defaults/su"
383 +#define _PATH_LOGINDEFS_RUNUSER "/etc/defaults/runuser"
385 +#define is_pam_failure(_rc) ((_rc) != PAM_SUCCESS)
387 +#include "logindefs.h"
388 +#include "su-common.h"
390 +/* The shell to run if none is given in the user's passwd entry. */
391 +#define DEFAULT_SHELL "/bin/sh"
393 +/* The user to become if none is specified. */
394 +#define DEFAULT_USER "root"
396 +#ifndef HAVE_ENVIRON_DECL
397 +extern char **environ;
400 +static void run_shell (char const *, char const *, char **, size_t)
401 + __attribute__ ((__noreturn__));
403 +/* If true, pass the `-f' option to the subshell. */
404 +static bool fast_startup;
406 +/* If true, simulate a login instead of just starting a shell. */
407 +static bool simulate_login;
409 +/* If true, change some environment vars to indicate the user su'd to. */
410 +static bool change_environment;
412 +/* If true, then don't call setsid() with a command. */
413 +static int same_session = 0;
415 +/* SU_MODE_{RUNUSER,SU} */
418 +static bool _pam_session_opened;
419 +static bool _pam_cred_established;
420 +static sig_atomic_t volatile caught_signal = false;
421 +static pam_handle_t *pamh = NULL;
423 +static int restricted = 1; /* zero for root user */
425 +static struct option const longopts[] =
427 + {"command", required_argument, NULL, 'c'},
428 + {"session-command", required_argument, NULL, 'C'},
429 + {"fast", no_argument, NULL, 'f'},
430 + {"login", no_argument, NULL, 'l'},
431 + {"preserve-environment", no_argument, NULL, 'p'},
432 + {"shell", required_argument, NULL, 's'},
433 + {"group", required_argument, NULL, 'g'},
434 + {"supp-group", required_argument, NULL, 'G'},
435 + {"help", no_argument, 0, 'h'},
436 + {"version", no_argument, 0, 'V'},
440 +/* Log the fact that someone has run su to the user given by PW;
441 + if SUCCESSFUL is true, they gave the correct password, etc. */
444 +log_su (struct passwd const *pw, bool successful)
446 + const char *new_user, *old_user, *tty;
448 + new_user = pw->pw_name;
449 + /* The utmp entry (via getlogin) is probably the best way to identify
450 + the user, especially if someone su's from a su-shell. */
451 + old_user = getlogin ();
454 + /* getlogin can fail -- usually due to lack of utmp entry.
455 + Resort to getpwuid. */
456 + struct passwd *pwd = getpwuid (getuid ());
457 + old_user = (pwd ? pwd->pw_name : "");
459 + tty = ttyname (STDERR_FILENO);
463 + openlog (program_invocation_short_name, 0 , LOG_AUTH);
464 + syslog (LOG_NOTICE, "%s(to %s) %s on %s",
466 + su_mode == RUNUSER_MODE ? "FAILED RUNUSER " : "FAILED SU ",
467 + new_user, old_user, tty);
471 +static struct pam_conv conv =
478 +cleanup_pam (int retcode)
480 + int saved_errno = errno;
482 + if (_pam_session_opened)
483 + pam_close_session (pamh, 0);
485 + if (_pam_cred_established)
486 + pam_setcred (pamh, PAM_DELETE_CRED | PAM_SILENT);
488 + pam_end(pamh, retcode);
490 + errno = saved_errno;
493 +/* Signal handler for parent process. */
495 +su_catch_sig (int sig __attribute__((__unused__)))
497 + caught_signal = true;
500 +/* Export env variables declared by PAM modules. */
502 +export_pamenv (void)
506 + /* This is a copy but don't care to free as we exec later anyways. */
507 + env = pam_getenvlist (pamh);
508 + while (env && *env)
510 + if (putenv (*env) != 0)
511 + err (EXIT_FAILURE, NULL);
517 +create_watching_parent (void)
524 + retval = pam_open_session (pamh, 0);
525 + if (is_pam_failure(retval))
527 + cleanup_pam (retval);
528 + errx (EXIT_FAILURE, _("cannot not open session: %s"),
529 + pam_strerror (pamh, retval));
532 + _pam_session_opened = 1;
535 + if (child == (pid_t) -1)
537 + cleanup_pam (PAM_ABORT);
538 + err (EXIT_FAILURE, _("cannot create child process"));
541 + /* the child proceeds to run the shell */
545 + /* In the parent watch the child. */
547 + /* su without pam support does not have a helper that keeps
548 + sitting on any directory so let's go to /. */
549 + if (chdir ("/") != 0)
550 + warn (_("cannot change directory to %s"), "/");
552 + sigfillset (&ourset);
553 + if (sigprocmask (SIG_BLOCK, &ourset, NULL))
555 + warn (_("cannot block signals"));
556 + caught_signal = true;
558 + if (!caught_signal)
560 + struct sigaction action;
561 + action.sa_handler = su_catch_sig;
562 + sigemptyset (&action.sa_mask);
563 + action.sa_flags = 0;
564 + sigemptyset (&ourset);
567 + if (sigaddset(&ourset, SIGINT) || sigaddset(&ourset, SIGQUIT))
569 + warn (_("cannot set signal handler"));
570 + caught_signal = true;
573 + if (!caught_signal && (sigaddset(&ourset, SIGTERM)
574 + || sigaddset(&ourset, SIGALRM)
575 + || sigaction(SIGTERM, &action, NULL)
576 + || sigprocmask(SIG_UNBLOCK, &ourset, NULL))) {
577 + warn (_("cannot set signal handler"));
578 + caught_signal = true;
580 + if (!caught_signal && !same_session && (sigaction(SIGINT, &action, NULL)
581 + || sigaction(SIGQUIT, &action, NULL)))
583 + warn (_("cannot set signal handler"));
584 + caught_signal = true;
587 + if (!caught_signal)
592 + pid = waitpid (child, &status, WUNTRACED);
594 + if (pid != (pid_t)-1 && WIFSTOPPED (status))
596 + kill (getpid (), SIGSTOP);
597 + /* once we get here, we must have resumed */
598 + kill (pid, SIGCONT);
603 + if (pid != (pid_t)-1)
604 + if (WIFSIGNALED (status))
605 + status = WTERMSIG (status) + 128;
607 + status = WEXITSTATUS (status);
616 + fprintf (stderr, _("\nSession terminated, killing shell..."));
617 + kill (child, SIGTERM);
620 + cleanup_pam (PAM_SUCCESS);
625 + kill (child, SIGKILL);
626 + fprintf (stderr, _(" ...killed.\n"));
632 +authenticate (const struct passwd *pw)
634 + const struct passwd *lpw;
635 + const char *cp, *srvname = NULL;
640 + srvname = simulate_login ? PAM_SRVNAME_SU_L : PAM_SRVNAME_SU;
643 + srvname = simulate_login ? PAM_SRVNAME_RUNUSER_L : PAM_SRVNAME_RUNUSER;
647 + retval = pam_start (srvname, pw->pw_name, &conv, &pamh);
648 + if (is_pam_failure(retval))
651 + if (isatty (0) && (cp = ttyname (0)) != NULL)
655 + if (strncmp (cp, "/dev/", 5) == 0)
659 + retval = pam_set_item (pamh, PAM_TTY, tty);
660 + if (is_pam_failure(retval))
664 + lpw = getpwuid (getuid ());
665 + if (lpw && lpw->pw_name)
667 + retval = pam_set_item (pamh, PAM_RUSER, (const void *) lpw->pw_name);
668 + if (is_pam_failure(retval))
672 + if (su_mode == RUNUSER_MODE)
675 + * This is the only difference between runuser(1) and su(1). The command
676 + * runuser(1) does not required authentication, because user is root.
679 + errx(EXIT_FAILURE, _("may not be used by non-root users"));
683 + retval = pam_authenticate (pamh, 0);
684 + if (is_pam_failure(retval))
687 + retval = pam_acct_mgmt (pamh, 0);
688 + if (retval == PAM_NEW_AUTHTOK_REQD)
690 + /* Password has expired. Offer option to change it. */
691 + retval = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
696 + log_su (pw, !is_pam_failure(retval));
698 + if (is_pam_failure(retval))
700 + const char *msg = pam_strerror(pamh, retval);
701 + pam_end(pamh, retval);
702 + sleep (getlogindefs_num ("FAIL_DELAY", 1));
703 + errx (EXIT_FAILURE, "%s", msg?msg:_("incorrect password"));
707 +/* Add or clear /sbin and /usr/sbin for the su command
708 + used without `-'. */
710 +/* Set if /sbin is found in path. */
711 +#define SBIN_MASK 0x01
712 +/* Set if /usr/sbin is found in path. */
713 +#define USBIN_MASK 0x02
716 +addsbin (const char *const path)
718 + unsigned char smask = 0;
719 + char *ptr, *tmp, *cur, *ret = NULL;
722 + if (!path || *path == 0)
725 + tmp = xstrdup (path);
727 + for (ptr = strsep (&cur, ":"); ptr != NULL; ptr = strsep (&cur, ":"))
729 + if (!strcmp (ptr, "/sbin"))
730 + smask |= SBIN_MASK;
731 + if (!strcmp (ptr, "/usr/sbin"))
732 + smask |= USBIN_MASK;
735 + if ((smask & (USBIN_MASK|SBIN_MASK)) == (USBIN_MASK|SBIN_MASK))
741 + len = strlen (path);
742 + if (!(smask & USBIN_MASK))
743 + len += strlen ("/usr/sbin:");
745 + if (!(smask & SBIN_MASK))
746 + len += strlen (":/sbin");
748 + ret = xmalloc (len + 1);
749 + strcpy (tmp, path);
753 + for (ptr = strsep (&cur, ":"); ptr; ptr = strsep (&cur, ":"))
755 + if (!strcmp (ptr, "."))
759 + if (!(smask & USBIN_MASK) && !strcmp (ptr, "/bin"))
761 + strcat (ret, "/usr/sbin:");
763 + smask |= USBIN_MASK;
766 + if (!(smask & SBIN_MASK) && !strcmp (ptr, "/usr/bin"))
769 + strcat (ret, ":/sbin");
770 + smask |= SBIN_MASK;
777 + if (!(smask & USBIN_MASK))
778 + strcat (ret, ":/usr/sbin");
780 + if (!(smask & SBIN_MASK))
781 + strcat (ret, ":/sbin");
787 +clearsbin (const char *const path)
789 + char *ptr, *tmp, *cur, *ret = NULL;
791 + if (!path || *path == 0)
794 + tmp = xstrdup (path);
796 + ret = xmalloc (strlen (path) + 1);
799 + for (ptr = strsep (&cur, ":"); ptr; ptr = strsep (&cur, ":"))
801 + if (!strcmp (ptr, "/sbin"))
803 + if (!strcmp (ptr, "/usr/sbin"))
805 + if (!strcmp (ptr, "/usr/local/sbin"))
817 +set_path(const struct passwd* pw)
821 + r = logindefs_setenv("PATH", "ENV_PATH", _PATH_DEFPATH);
823 + else if ((r = logindefs_setenv("PATH", "ENV_ROOTPATH", NULL)) != 0)
824 + r = logindefs_setenv("PATH", "ENV_SUPATH", _PATH_DEFPATH_ROOT);
827 + err (EXIT_FAILURE, _("failed to set PATH"));
830 +/* Update `environ' for the new shell based on PW, with SHELL being
831 + the value for the SHELL environment variable. */
834 +modify_environment (const struct passwd *pw, const char *shell)
836 + if (simulate_login)
838 + /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH.
839 + Unset all other environment variables. */
840 + char const *term = getenv ("TERM");
842 + term = xstrdup (term);
843 + environ = xmalloc ((6 + !!term) * sizeof (char *));
846 + xsetenv ("TERM", term, 1);
847 + xsetenv ("HOME", pw->pw_dir, 1);
848 + xsetenv ("SHELL", shell, 1);
849 + xsetenv ("USER", pw->pw_name, 1);
850 + xsetenv ("LOGNAME", pw->pw_name, 1);
855 + /* Set HOME, SHELL, and if not becoming a super-user,
856 + USER and LOGNAME. */
857 + if (change_environment)
859 + xsetenv ("HOME", pw->pw_dir, 1);
860 + xsetenv ("SHELL", shell, 1);
861 + if (getlogindefs_bool ("ALWAYS_SET_PATH", 0))
865 + char const *path = getenv ("PATH");
869 + new = clearsbin (path);
871 + new = addsbin (path);
875 + xsetenv ("PATH", new, 1);
881 + xsetenv ("USER", pw->pw_name, 1);
882 + xsetenv ("LOGNAME", pw->pw_name, 1);
890 +/* Become the user and group(s) specified by PW. */
893 +init_groups (const struct passwd *pw, gid_t *groups, int num_groups)
900 + retval = setgroups (num_groups, groups);
902 + retval = initgroups (pw->pw_name, pw->pw_gid);
906 + cleanup_pam (PAM_ABORT);
907 + err (EXIT_FAILURE, _("cannot set groups"));
911 + retval = pam_setcred (pamh, PAM_ESTABLISH_CRED);
912 + if (is_pam_failure(retval))
913 + errx (EXIT_FAILURE, "%s", pam_strerror (pamh, retval));
915 + _pam_cred_established = 1;
919 +change_identity (const struct passwd *pw)
921 + if (setgid (pw->pw_gid))
922 + err (EXIT_FAILURE, _("cannot set group id"));
923 + if (setuid (pw->pw_uid))
924 + err (EXIT_FAILURE, _("cannot set user id"));
927 +/* Run SHELL, or DEFAULT_SHELL if SHELL is empty.
928 + If COMMAND is nonzero, pass it to the shell with the -c option.
929 + Pass ADDITIONAL_ARGS to the shell as more arguments; there
930 + are N_ADDITIONAL_ARGS extra arguments. */
933 +run_shell (char const *shell, char const *command, char **additional_args,
934 + size_t n_additional_args)
936 + size_t n_args = 1 + fast_startup + 2 * !!command + n_additional_args + 1;
937 + char const **args = xcalloc (n_args, sizeof *args);
940 + if (simulate_login)
943 + char *shell_basename;
945 + shell_basename = basename (shell);
946 + arg0 = xmalloc (strlen (shell_basename) + 2);
948 + strcpy (arg0 + 1, shell_basename);
952 + args[0] = basename (shell);
954 + args[argno++] = "-f";
957 + args[argno++] = "-c";
958 + args[argno++] = command;
960 + memcpy (args + argno, additional_args, n_additional_args * sizeof *args);
961 + args[argno + n_additional_args] = NULL;
962 + execv (shell, (char **) args);
965 + int exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
966 + warn ("%s", shell);
967 + exit (exit_status);
971 +/* Return true if SHELL is a restricted shell (one not returned by
972 + getusershell), else false, meaning it is a standard shell. */
975 +restricted_shell (const char *shell)
980 + while ((line = getusershell ()) != NULL)
982 + if (*line != '#' && !strcmp (line, shell))
992 +static void __attribute__((__noreturn__))
995 + if (status != EXIT_SUCCESS)
996 + fprintf (stderr, _("Try `%s --help' for more information.\n"),
997 + program_invocation_short_name);
1000 + fputs(USAGE_HEADER, stdout);
1001 + printf (_(" %s [options] [-] [USER [arg]...]\n"), program_invocation_short_name);
1003 + Change the effective user id and group id to that of USER.\n\
1004 + A mere - implies -l. If USER not given, assume root.\n"), stdout);
1005 + fputs(USAGE_OPTIONS, stdout);
1007 + -, -l, --login make the shell a login shell\n\
1008 + -c, --command <command> pass a single command to the shell with -c\n\
1009 + --session-command <command> pass a single command to the shell with -c\n\
1010 + and do not create a new session\n\
1011 + -g --group=group specify the primary group\n\
1012 + -G --supp-group=group specify a supplemental group\n\
1013 + -f, --fast pass -f to the shell (for csh or tcsh)\n\
1014 + -m, --preserve-environment do not reset environment variables\n\
1016 + -s, --shell <shell> run shell if /etc/shells allows it\n\
1019 + fputs(USAGE_SEPARATOR, stdout);
1020 + fputs(USAGE_HELP, stdout);
1021 + fputs(USAGE_VERSION, stdout);
1022 + printf(USAGE_MAN_TAIL("su(1)"));
1028 +void load_config(void)
1030 + switch (su_mode) {
1032 + logindefs_load_file(_PATH_LOGINDEFS_SU);
1034 + case RUNUSER_MODE:
1035 + logindefs_load_file(_PATH_LOGINDEFS_RUNUSER);
1039 + logindefs_load_file(_PATH_LOGINDEFS);
1043 + * Returns 1 if the current user is not root
1048 + uid_t ruid = getuid();
1049 + uid_t euid = geteuid();
1051 + /* if we're really root and aren't running setuid */
1052 + return (uid_t) 0 == ruid && ruid == euid ? 0 : 1;
1056 +su_main (int argc, char **argv, int mode)
1059 + const char *new_user = DEFAULT_USER;
1060 + char *command = NULL;
1061 + int request_same_session = 0;
1062 + char *shell = NULL;
1063 + struct passwd *pw;
1064 + struct passwd pw_copy;
1066 + gid_t groups[NGROUPS_MAX];
1067 + int num_supp_groups = 0;
1070 + setlocale (LC_ALL, "");
1071 + bindtextdomain (PACKAGE, LOCALEDIR);
1072 + textdomain (PACKAGE);
1075 + fast_startup = false;
1076 + simulate_login = false;
1077 + change_environment = true;
1079 + while ((optc = getopt_long (argc, argv, "c:fg:G:lmps:hV", longopts, NULL)) != -1)
1089 + request_same_session = 1;
1093 + fast_startup = true;
1097 + gr = getgrnam(optarg);
1099 + errx(EXIT_FAILURE, _("group %s does not exist"), optarg);
1101 + groups[0] = gr->gr_gid;
1105 + num_supp_groups++;
1106 + if (num_supp_groups >= NGROUPS_MAX)
1107 + errx(EXIT_FAILURE,
1108 + _("can't specify more than %d supplemental groups"),
1110 + gr = getgrnam(optarg);
1112 + errx(EXIT_FAILURE, _("group %s does not exist"), optarg);
1113 + groups[num_supp_groups] = gr->gr_gid;
1117 + simulate_login = true;
1122 + change_environment = false;
1133 + printf(UTIL_LINUX_VERSION);
1134 + exit(EXIT_SUCCESS);
1137 + usage (EXIT_FAILURE);
1141 + restricted = evaluate_uid ();
1143 + if (optind < argc && !strcmp (argv[optind], "-"))
1145 + simulate_login = true;
1148 + if (optind < argc)
1149 + new_user = argv[optind++];
1151 + if ((num_supp_groups || use_gid) && restricted)
1152 + errx(EXIT_FAILURE, _("only root can specify alternative groups"));
1154 + logindefs_load_defaults = load_config;
1156 + pw = getpwnam (new_user);
1157 + if (! (pw && pw->pw_name && pw->pw_name[0] && pw->pw_dir && pw->pw_dir[0]
1158 + && pw->pw_passwd))
1159 + errx (EXIT_FAILURE, _("user %s does not exist"), new_user);
1161 + /* Make a copy of the password information and point pw at the local
1162 + copy instead. Otherwise, some systems (e.g. Linux) would clobber
1163 + the static data through the getlogin call from log_su.
1164 + Also, make sure pw->pw_shell is a nonempty string.
1165 + It may be NULL when NEW_USER is a username that is retrieved via NIS (YP),
1166 + but that doesn't have a default shell listed. */
1169 + pw->pw_name = xstrdup (pw->pw_name);
1170 + pw->pw_passwd = xstrdup (pw->pw_passwd);
1171 + pw->pw_dir = xstrdup (pw->pw_dir);
1172 + pw->pw_shell = xstrdup (pw->pw_shell && pw->pw_shell[0]
1177 + if (num_supp_groups && !use_gid)
1179 + pw->pw_gid = groups[1];
1180 + memmove (groups, groups + 1, sizeof(gid_t) * num_supp_groups);
1184 + pw->pw_gid = groups[0];
1185 + num_supp_groups++;
1188 + authenticate (pw);
1190 + if (request_same_session || !command || !pw->pw_uid)
1193 + if (!shell && !change_environment)
1194 + shell = getenv ("SHELL");
1195 + if (shell && getuid () != 0 && restricted_shell (pw->pw_shell))
1197 + /* The user being su'd to has a nonstandard shell, and so is
1198 + probably a uucp account or has restricted access. Don't
1199 + compromise the account by allowing access with a standard
1201 + warnx (_("using restricted shell %s"), pw->pw_shell);
1204 + shell = xstrdup (shell ? shell : pw->pw_shell);
1206 + init_groups (pw, groups, num_supp_groups);
1208 + create_watching_parent ();
1209 + /* Now we're in the child. */
1211 + change_identity (pw);
1212 + if (!same_session)
1215 + /* Set environment after pam_open_session, which may put KRB5CCNAME
1216 + into the pam_env, etc. */
1218 + modify_environment (pw, shell);
1220 + if (simulate_login && chdir (pw->pw_dir) != 0)
1221 + warn (_("warning: cannot change directory to %s"), pw->pw_dir);
1223 + run_shell (shell, command, argv + optind, max (0, argc - optind));
1226 +// vim: sw=2 cinoptions=>4,n-2,{2,^-2,\:2,=2,g0,h2,p5,t0,+2,(0,u0,w1,m1
1227 diff --git a/login-utils/su-common.h b/login-utils/su-common.h
1228 new file mode 100644
1229 index 0000000..7cf3769
1231 +++ b/login-utils/su-common.h
1233 +#ifndef UTIL_LINUX_SU_COMMON_H
1234 +#define UTIL_LINUX_SU_COMMON_H
1241 +extern int su_main(int argc, char **argv, int mode);
1243 +#endif /* UTIL_LINUX_SU_COMMON */
1244 diff --git a/login-utils/su.1 b/login-utils/su.1
1245 index 598cebd..59e1731 100644
1246 --- a/login-utils/su.1
1247 +++ b/login-utils/su.1
1248 @@ -59,6 +59,12 @@ Pass
1249 to the shell which may or may not be useful depending on the
1252 +\fB\-g\fR, \fB\-\-group\fR=\fIgroup\fR\fR
1253 +specify the primary group, this option is allowed for root user only
1255 +\fB\-G\fR, \fB\-\-supp-group\fR=\fIgroup\fR\fR
1256 +specify a supplemental group, this option is allowed for root user only
1258 \fB\-\fR, \fB\-l\fR, \fB\-\-login\fR
1259 Starts the shell as login shell with an environment similar to a real
1261 diff --git a/login-utils/su.c b/login-utils/su.c
1262 index c6b8bce..29c10f0 100644
1263 --- a/login-utils/su.c
1264 +++ b/login-utils/su.c
1266 -/* su for Linux. Run a shell with substitute user and group IDs.
1267 - Copyright (C) 1992-2006 Free Software Foundation, Inc.
1268 - Copyright (C) 2012 SUSE Linux Products GmbH, Nuernberg
1270 - This program is free software; you can redistribute it and/or modify
1271 - it under the terms of the GNU General Public License as published by
1272 - the Free Software Foundation; either version 2, or (at your option)
1273 - any later version.
1274 +#include "su-common.h"
1276 - This program is distributed in the hope that it will be useful,
1277 - but WITHOUT ANY WARRANTY; without even the implied warranty of
1278 - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1279 - GNU General Public License for more details.
1281 - You should have received a copy of the GNU General Public License
1282 - along with this program; if not, write to the Free Software Foundation,
1283 - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
1285 -/* Run a shell with the real and effective UID and GID and groups
1286 - of USER, default `root'.
1288 - The shell run is taken from USER's password entry, /bin/sh if
1289 - none is specified there. If the account has a password, su
1290 - prompts for a password unless run by a user with real UID 0.
1292 - Does not change the current directory.
1293 - Sets `HOME' and `SHELL' from the password entry for USER, and if
1294 - USER is not root, sets `USER' and `LOGNAME' to USER.
1295 - The subshell is not a login shell.
1297 - If one or more ARGs are given, they are passed as additional
1298 - arguments to the subshell.
1300 - Does not handle /bin/sh or other shells specially
1301 - (setting argv[0] to "-su", passing -c only to certain shells, etc.).
1302 - I don't see the point in doing that, and it's ugly.
1304 - Based on an implemenation by David MacKenzie <djm@gnu.ai.mit.edu>. */
1308 - EXIT_CANNOT_INVOKE = 126,
1312 -#include <config.h>
1314 -#include <getopt.h>
1315 -#include <sys/types.h>
1318 -#include <security/pam_appl.h>
1319 -#include <security/pam_misc.h>
1320 -#include <signal.h>
1321 -#include <sys/wait.h>
1322 -#include <syslog.h>
1326 -#include <stdbool.h>
1328 -#include "xalloc.h"
1330 -#include "pathnames.h"
1333 -/* name of the pam configuration files. separate configs for su and su - */
1334 -#define PAM_SERVICE_NAME "su"
1335 -#define PAM_SERVICE_NAME_L "su-l"
1337 -#define is_pam_failure(_rc) ((_rc) != PAM_SUCCESS)
1339 -#include "logindefs.h"
1341 -/* The shell to run if none is given in the user's passwd entry. */
1342 -#define DEFAULT_SHELL "/bin/sh"
1344 -/* The user to become if none is specified. */
1345 -#define DEFAULT_USER "root"
1347 -#ifndef HAVE_ENVIRON_DECL
1348 -extern char **environ;
1351 -static void run_shell (char const *, char const *, char **, size_t)
1352 - __attribute__ ((__noreturn__));
1354 -/* If true, pass the `-f' option to the subshell. */
1355 -static bool fast_startup;
1357 -/* If true, simulate a login instead of just starting a shell. */
1358 -static bool simulate_login;
1360 -/* If true, change some environment vars to indicate the user su'd to. */
1361 -static bool change_environment;
1363 -/* If true, then don't call setsid() with a command. */
1364 -int same_session = 0;
1366 -static bool _pam_session_opened;
1367 -static bool _pam_cred_established;
1368 -static sig_atomic_t volatile caught_signal = false;
1369 -static pam_handle_t *pamh = NULL;
1371 -static struct option const longopts[] =
1373 - {"command", required_argument, NULL, 'c'},
1374 - {"session-command", required_argument, NULL, 'C'},
1375 - {"fast", no_argument, NULL, 'f'},
1376 - {"login", no_argument, NULL, 'l'},
1377 - {"preserve-environment", no_argument, NULL, 'p'},
1378 - {"shell", required_argument, NULL, 's'},
1379 - {"help", no_argument, 0, 'h'},
1380 - {"version", no_argument, 0, 'V'},
1381 - {NULL, 0, NULL, 0}
1384 -/* Log the fact that someone has run su to the user given by PW;
1385 - if SUCCESSFUL is true, they gave the correct password, etc. */
1388 -log_su (struct passwd const *pw, bool successful)
1390 - const char *new_user, *old_user, *tty;
1392 - new_user = pw->pw_name;
1393 - /* The utmp entry (via getlogin) is probably the best way to identify
1394 - the user, especially if someone su's from a su-shell. */
1395 - old_user = getlogin ();
1398 - /* getlogin can fail -- usually due to lack of utmp entry.
1399 - Resort to getpwuid. */
1400 - struct passwd *pwd = getpwuid (getuid ());
1401 - old_user = (pwd ? pwd->pw_name : "");
1403 - tty = ttyname (STDERR_FILENO);
1407 - openlog (program_invocation_short_name, 0 , LOG_AUTH);
1408 - syslog (LOG_NOTICE, "%s(to %s) %s on %s",
1409 - successful ? "" : "FAILED SU ",
1410 - new_user, old_user, tty);
1414 -static struct pam_conv conv =
1421 -cleanup_pam (int retcode)
1422 +int main(int argv, char **argc)
1424 - int saved_errno = errno;
1426 - if (_pam_session_opened)
1427 - pam_close_session (pamh, 0);
1429 - if (_pam_cred_established)
1430 - pam_setcred (pamh, PAM_DELETE_CRED | PAM_SILENT);
1432 - pam_end(pamh, retcode);
1434 - errno = saved_errno;
1437 -/* Signal handler for parent process. */
1439 -su_catch_sig (int sig __attribute__((__unused__)))
1441 - caught_signal = true;
1444 -/* Export env variables declared by PAM modules. */
1446 -export_pamenv (void)
1450 - /* This is a copy but don't care to free as we exec later anyways. */
1451 - env = pam_getenvlist (pamh);
1452 - while (env && *env)
1454 - if (putenv (*env) != 0)
1455 - err (EXIT_FAILURE, NULL);
1461 -create_watching_parent (void)
1468 - retval = pam_open_session (pamh, 0);
1469 - if (is_pam_failure(retval))
1471 - cleanup_pam (retval);
1472 - errx (EXIT_FAILURE, _("cannot not open session: %s"),
1473 - pam_strerror (pamh, retval));
1476 - _pam_session_opened = 1;
1479 - if (child == (pid_t) -1)
1481 - cleanup_pam (PAM_ABORT);
1482 - err (EXIT_FAILURE, _("cannot create child process"));
1485 - /* the child proceeds to run the shell */
1489 - /* In the parent watch the child. */
1491 - /* su without pam support does not have a helper that keeps
1492 - sitting on any directory so let's go to /. */
1493 - if (chdir ("/") != 0)
1494 - warn (_("cannot change directory to %s"), "/");
1496 - sigfillset (&ourset);
1497 - if (sigprocmask (SIG_BLOCK, &ourset, NULL))
1499 - warn (_("cannot block signals"));
1500 - caught_signal = true;
1502 - if (!caught_signal)
1504 - struct sigaction action;
1505 - action.sa_handler = su_catch_sig;
1506 - sigemptyset (&action.sa_mask);
1507 - action.sa_flags = 0;
1508 - sigemptyset (&ourset);
1509 - if (!same_session)
1511 - if (sigaddset(&ourset, SIGINT) || sigaddset(&ourset, SIGQUIT))
1513 - warn (_("cannot set signal handler"));
1514 - caught_signal = true;
1517 - if (!caught_signal && (sigaddset(&ourset, SIGTERM)
1518 - || sigaddset(&ourset, SIGALRM)
1519 - || sigaction(SIGTERM, &action, NULL)
1520 - || sigprocmask(SIG_UNBLOCK, &ourset, NULL))) {
1521 - warn (_("cannot set signal handler"));
1522 - caught_signal = true;
1524 - if (!caught_signal && !same_session && (sigaction(SIGINT, &action, NULL)
1525 - || sigaction(SIGQUIT, &action, NULL)))
1527 - warn (_("cannot set signal handler"));
1528 - caught_signal = true;
1531 - if (!caught_signal)
1536 - pid = waitpid (child, &status, WUNTRACED);
1538 - if (pid != (pid_t)-1 && WIFSTOPPED (status))
1540 - kill (getpid (), SIGSTOP);
1541 - /* once we get here, we must have resumed */
1542 - kill (pid, SIGCONT);
1547 - if (pid != (pid_t)-1)
1548 - if (WIFSIGNALED (status))
1549 - status = WTERMSIG (status) + 128;
1551 - status = WEXITSTATUS (status);
1558 - if (caught_signal)
1560 - fprintf (stderr, _("\nSession terminated, killing shell..."));
1561 - kill (child, SIGTERM);
1564 - cleanup_pam (PAM_SUCCESS);
1566 - if (caught_signal)
1569 - kill (child, SIGKILL);
1570 - fprintf (stderr, _(" ...killed.\n"));
1576 -authenticate (const struct passwd *pw)
1578 - const struct passwd *lpw;
1582 - retval = pam_start (simulate_login ? PAM_SERVICE_NAME_L : PAM_SERVICE_NAME,
1583 - pw->pw_name, &conv, &pamh);
1584 - if (is_pam_failure(retval))
1587 - if (isatty (0) && (cp = ttyname (0)) != NULL)
1591 - if (strncmp (cp, "/dev/", 5) == 0)
1595 - retval = pam_set_item (pamh, PAM_TTY, tty);
1596 - if (is_pam_failure(retval))
1600 - lpw = getpwuid (getuid ());
1601 - if (lpw && lpw->pw_name)
1603 - retval = pam_set_item (pamh, PAM_RUSER, (const void *) lpw->pw_name);
1604 - if (is_pam_failure(retval))
1608 - retval = pam_authenticate (pamh, 0);
1609 - if (is_pam_failure(retval))
1612 - retval = pam_acct_mgmt (pamh, 0);
1613 - if (retval == PAM_NEW_AUTHTOK_REQD)
1615 - /* Password has expired. Offer option to change it. */
1616 - retval = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
1621 - log_su (pw, !is_pam_failure(retval));
1623 - if (is_pam_failure(retval))
1625 - const char *msg = pam_strerror(pamh, retval);
1626 - pam_end(pamh, retval);
1627 - sleep (getlogindefs_num ("FAIL_DELAY", 1));
1628 - errx (EXIT_FAILURE, "%s", msg?msg:_("incorrect password"));
1633 -set_path(const struct passwd* pw)
1637 - r = logindefs_setenv("PATH", "ENV_PATH", _PATH_DEFPATH);
1639 - else if ((r = logindefs_setenv("PATH", "ENV_ROOTPATH", NULL)) != 0)
1640 - r = logindefs_setenv("PATH", "ENV_SUPATH", _PATH_DEFPATH_ROOT);
1643 - err (EXIT_FAILURE, _("failed to set PATH"));
1646 -/* Update `environ' for the new shell based on PW, with SHELL being
1647 - the value for the SHELL environment variable. */
1650 -modify_environment (const struct passwd *pw, const char *shell)
1652 - if (simulate_login)
1654 - /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH.
1655 - Unset all other environment variables. */
1656 - char const *term = getenv ("TERM");
1658 - term = xstrdup (term);
1659 - environ = xmalloc ((6 + !!term) * sizeof (char *));
1660 - environ[0] = NULL;
1662 - xsetenv ("TERM", term, 1);
1663 - xsetenv ("HOME", pw->pw_dir, 1);
1664 - xsetenv ("SHELL", shell, 1);
1665 - xsetenv ("USER", pw->pw_name, 1);
1666 - xsetenv ("LOGNAME", pw->pw_name, 1);
1671 - /* Set HOME, SHELL, and if not becoming a super-user,
1672 - USER and LOGNAME. */
1673 - if (change_environment)
1675 - xsetenv ("HOME", pw->pw_dir, 1);
1676 - xsetenv ("SHELL", shell, 1);
1677 - if (getlogindefs_bool ("ALWAYS_SET_PATH", 0))
1682 - xsetenv ("USER", pw->pw_name, 1);
1683 - xsetenv ("LOGNAME", pw->pw_name, 1);
1691 -/* Become the user and group(s) specified by PW. */
1694 -init_groups (const struct passwd *pw)
1698 - if (initgroups (pw->pw_name, pw->pw_gid) == -1)
1700 - cleanup_pam (PAM_ABORT);
1701 - err (EXIT_FAILURE, _("cannot set groups"));
1705 - retval = pam_setcred (pamh, PAM_ESTABLISH_CRED);
1706 - if (is_pam_failure(retval))
1707 - errx (EXIT_FAILURE, "%s", pam_strerror (pamh, retval));
1709 - _pam_cred_established = 1;
1713 -change_identity (const struct passwd *pw)
1715 - if (setgid (pw->pw_gid))
1716 - err (EXIT_FAILURE, _("cannot set group id"));
1717 - if (setuid (pw->pw_uid))
1718 - err (EXIT_FAILURE, _("cannot set user id"));
1721 -/* Run SHELL, or DEFAULT_SHELL if SHELL is empty.
1722 - If COMMAND is nonzero, pass it to the shell with the -c option.
1723 - Pass ADDITIONAL_ARGS to the shell as more arguments; there
1724 - are N_ADDITIONAL_ARGS extra arguments. */
1727 -run_shell (char const *shell, char const *command, char **additional_args,
1728 - size_t n_additional_args)
1730 - size_t n_args = 1 + fast_startup + 2 * !!command + n_additional_args + 1;
1731 - char const **args = xcalloc (n_args, sizeof *args);
1734 - if (simulate_login)
1737 - char *shell_basename;
1739 - shell_basename = basename (shell);
1740 - arg0 = xmalloc (strlen (shell_basename) + 2);
1742 - strcpy (arg0 + 1, shell_basename);
1746 - args[0] = basename (shell);
1748 - args[argno++] = "-f";
1751 - args[argno++] = "-c";
1752 - args[argno++] = command;
1754 - memcpy (args + argno, additional_args, n_additional_args * sizeof *args);
1755 - args[argno + n_additional_args] = NULL;
1756 - execv (shell, (char **) args);
1759 - int exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
1760 - warn ("%s", shell);
1761 - exit (exit_status);
1765 -/* Return true if SHELL is a restricted shell (one not returned by
1766 - getusershell), else false, meaning it is a standard shell. */
1769 -restricted_shell (const char *shell)
1774 - while ((line = getusershell ()) != NULL)
1776 - if (*line != '#' && !strcmp (line, shell))
1786 -static void __attribute__((__noreturn__))
1789 - if (status != EXIT_SUCCESS)
1790 - fprintf (stderr, _("Try `%s --help' for more information.\n"),
1791 - program_invocation_short_name);
1794 - fputs(USAGE_HEADER, stdout);
1795 - printf (_(" %s [options] [-] [USER [arg]...]\n"), program_invocation_short_name);
1797 - Change the effective user id and group id to that of USER.\n\
1798 - A mere - implies -l. If USER not given, assume root.\n"), stdout);
1799 - fputs(USAGE_OPTIONS, stdout);
1801 - -, -l, --login make the shell a login shell\n\
1802 - -c, --command <command> pass a single command to the shell with -c\n\
1803 - --session-command <command> pass a single command to the shell with -c\n\
1804 - and do not create a new session\n\
1805 - -f, --fast pass -f to the shell (for csh or tcsh)\n\
1806 - -m, --preserve-environment do not reset environment variables\n\
1808 - -s, --shell <shell> run shell if /etc/shells allows it\n\
1811 - fputs(USAGE_SEPARATOR, stdout);
1812 - fputs(USAGE_HELP, stdout);
1813 - fputs(USAGE_VERSION, stdout);
1814 - printf(USAGE_MAN_TAIL("su(1)"));
1820 -void load_config(void)
1822 - logindefs_load_file("/etc/default/su");
1823 - logindefs_load_file(_PATH_LOGINDEFS);
1827 -main (int argc, char **argv)
1830 - const char *new_user = DEFAULT_USER;
1831 - char *command = NULL;
1832 - int request_same_session = 0;
1833 - char *shell = NULL;
1834 - struct passwd *pw;
1835 - struct passwd pw_copy;
1837 - setlocale (LC_ALL, "");
1838 - bindtextdomain (PACKAGE, LOCALEDIR);
1839 - textdomain (PACKAGE);
1841 - fast_startup = false;
1842 - simulate_login = false;
1843 - change_environment = true;
1845 - while ((optc = getopt_long (argc, argv, "c:flmps:hV", longopts, NULL)) != -1)
1855 - request_same_session = 1;
1859 - fast_startup = true;
1863 - simulate_login = true;
1868 - change_environment = false;
1879 - printf(UTIL_LINUX_VERSION);
1880 - exit(EXIT_SUCCESS);
1883 - usage (EXIT_FAILURE);
1887 - if (optind < argc && !strcmp (argv[optind], "-"))
1889 - simulate_login = true;
1892 - if (optind < argc)
1893 - new_user = argv[optind++];
1895 - logindefs_load_defaults = load_config;
1897 - pw = getpwnam (new_user);
1898 - if (! (pw && pw->pw_name && pw->pw_name[0] && pw->pw_dir && pw->pw_dir[0]
1899 - && pw->pw_passwd))
1900 - errx (EXIT_FAILURE, _("user %s does not exist"), new_user);
1902 - /* Make a copy of the password information and point pw at the local
1903 - copy instead. Otherwise, some systems (e.g. Linux) would clobber
1904 - the static data through the getlogin call from log_su.
1905 - Also, make sure pw->pw_shell is a nonempty string.
1906 - It may be NULL when NEW_USER is a username that is retrieved via NIS (YP),
1907 - but that doesn't have a default shell listed. */
1910 - pw->pw_name = xstrdup (pw->pw_name);
1911 - pw->pw_passwd = xstrdup (pw->pw_passwd);
1912 - pw->pw_dir = xstrdup (pw->pw_dir);
1913 - pw->pw_shell = xstrdup (pw->pw_shell && pw->pw_shell[0]
1918 - authenticate (pw);
1920 - if (request_same_session || !command || !pw->pw_uid)
1923 - if (!shell && !change_environment)
1924 - shell = getenv ("SHELL");
1925 - if (shell && getuid () != 0 && restricted_shell (pw->pw_shell))
1927 - /* The user being su'd to has a nonstandard shell, and so is
1928 - probably a uucp account or has restricted access. Don't
1929 - compromise the account by allowing access with a standard
1931 - warnx (_("using restricted shell %s"), pw->pw_shell);
1934 - shell = xstrdup (shell ? shell : pw->pw_shell);
1938 - create_watching_parent ();
1939 - /* Now we're in the child. */
1941 - change_identity (pw);
1942 - if (!same_session)
1945 - /* Set environment after pam_open_session, which may put KRB5CCNAME
1946 - into the pam_env, etc. */
1948 - modify_environment (pw, shell);
1950 - if (simulate_login && chdir (pw->pw_dir) != 0)
1951 - warn (_("warning: cannot change directory to %s"), pw->pw_dir);
1953 - run_shell (shell, command, argv + optind, max (0, argc - optind));
1954 + return su_main(argv, argc, SU_MODE);
1957 -// vim: sw=2 cinoptions=>4,n-2,{2,^-2,\:2,=2,g0,h2,p5,t0,+2,(0,u0,w1,m1