1 /* su for Linux. Run a shell with substitute user and group IDs.
2 Copyright (C) 1992-2006 Free Software Foundation, Inc.
3 Copyright (C) 2012 SUSE Linux Products GmbH, Nuernberg
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
19 /* Run a shell with the real and effective UID and GID and groups
20 of USER, default `root'.
22 The shell run is taken from USER's password entry, /bin/sh if
23 none is specified there. If the account has a password, su
24 prompts for a password unless run by a user with real UID 0.
26 Does not change the current directory.
27 Sets `HOME' and `SHELL' from the password entry for USER, and if
28 USER is not root, sets `USER' and `LOGNAME' to USER.
29 The subshell is not a login shell.
31 If one or more ARGs are given, they are passed as additional
32 arguments to the subshell.
34 Does not handle /bin/sh or other shells specially
35 (setting argv[0] to "-su", passing -c only to certain shells, etc.).
36 I don't see the point in doing that, and it's ugly.
38 Based on an implementation by David MacKenzie <djm@gnu.ai.mit.edu>. */
42 EXIT_CANNOT_INVOKE
= 126,
49 #include <sys/types.h>
52 #include <security/pam_appl.h>
53 #ifdef HAVE_SECURITY_PAM_MISC_H
54 # include <security/pam_misc.h>
55 #elif defined(HAVE_SECURITY_OPENPAM_H)
56 # include <security/openpam.h>
69 #include "pathnames.h"
71 #include "closestream.h"
75 /* name of the pam configuration files. separate configs for su and su - */
76 #define PAM_SRVNAME_SU "su"
77 #define PAM_SRVNAME_SU_L "su-l"
79 #define PAM_SRVNAME_RUNUSER "runuser"
80 #define PAM_SRVNAME_RUNUSER_L "runuser-l"
82 #define _PATH_LOGINDEFS_SU "/etc/defaults/su"
83 #define _PATH_LOGINDEFS_RUNUSER "/etc/defaults/runuser"
85 #define is_pam_failure(_rc) ((_rc) != PAM_SUCCESS)
87 #include "logindefs.h"
88 #include "su-common.h"
90 /* The shell to run if none is given in the user's passwd entry. */
91 #define DEFAULT_SHELL "/bin/sh"
93 /* The user to become if none is specified. */
94 #define DEFAULT_USER "root"
96 #ifndef HAVE_ENVIRON_DECL
97 extern char **environ
;
100 static void run_shell (char const *, char const *, char **, size_t)
101 __attribute__ ((__noreturn__
));
103 /* If true, pass the `-f' option to the subshell. */
104 static bool fast_startup
;
106 /* If true, simulate a login instead of just starting a shell. */
107 static bool simulate_login
;
109 /* If true, change some environment vars to indicate the user su'd to. */
110 static bool change_environment
;
112 /* If true, then don't call setsid() with a command. */
113 static int same_session
= 0;
115 /* SU_MODE_{RUNUSER,SU} */
118 /* Don't print PAM info messages (Last login, etc.). */
119 static int suppress_pam_info
;
121 static bool _pam_session_opened
;
122 static bool _pam_cred_established
;
123 static sig_atomic_t volatile caught_signal
= false;
124 static pam_handle_t
*pamh
= NULL
;
126 static int restricted
= 1; /* zero for root user */
129 static struct passwd
*
130 current_getpwuid(void)
134 /* GNU Hurd implementation has an extension where a process can exist in a
135 * non-conforming environment, and thus be outside the realms of POSIX
136 * process identifiers; on this platform, getuid() fails with a status of
137 * (uid_t)(-1) and sets errno if a program is run from a non-conforming
140 * http://austingroupbugs.net/view.php?id=511
145 return errno
== 0 ? getpwuid (ruid
) : NULL
;
148 /* Log the fact that someone has run su to the user given by PW;
149 if SUCCESSFUL is true, they gave the correct password, etc. */
152 log_syslog(struct passwd
const *pw
, bool successful
)
154 const char *new_user
, *old_user
, *tty
;
156 new_user
= pw
->pw_name
;
157 /* The utmp entry (via getlogin) is probably the best way to identify
158 the user, especially if someone su's from a su-shell. */
159 old_user
= getlogin ();
162 /* getlogin can fail -- usually due to lack of utmp entry.
163 Resort to getpwuid. */
164 struct passwd
*pwd
= current_getpwuid();
165 old_user
= pwd
? pwd
->pw_name
: "";
168 if (get_terminal_name(STDERR_FILENO
, NULL
, &tty
, NULL
) != 0 || !tty
)
171 openlog (program_invocation_short_name
, 0 , LOG_AUTH
);
172 syslog (LOG_NOTICE
, "%s(to %s) %s on %s",
174 su_mode
== RUNUSER_MODE
? "FAILED RUNUSER " : "FAILED SU ",
175 new_user
, old_user
, tty
);
181 * Log failed login attempts in _PATH_BTMP if that exists.
183 static void log_btmp(struct passwd
const *pw
)
187 const char *tty_name
, *tty_num
;
189 memset(&ut
, 0, sizeof(ut
));
192 pw
&& pw
->pw_name
? pw
->pw_name
: "(unknown)",
195 get_terminal_name(STDERR_FILENO
, NULL
, &tty_name
, &tty_num
);
197 xstrncpy(ut
.ut_id
, tty_num
, sizeof(ut
.ut_id
));
199 xstrncpy(ut
.ut_line
, tty_name
, sizeof(ut
.ut_line
));
201 #if defined(_HAVE_UT_TV) /* in <utmpbits.h> included by <utmp.h> */
202 gettimeofday(&tv
, NULL
);
203 ut
.ut_tv
.tv_sec
= tv
.tv_sec
;
204 ut
.ut_tv
.tv_usec
= tv
.tv_usec
;
209 ut
.ut_time
= t
; /* ut_time is not always a time_t */
212 ut
.ut_type
= LOGIN_PROCESS
; /* XXX doesn't matter */
213 ut
.ut_pid
= getpid();
215 updwtmp(_PATH_BTMP
, &ut
);
219 static int su_pam_conv(int num_msg
, const struct pam_message
**msg
,
220 struct pam_response
**resp
, void *appdata_ptr
)
222 if (suppress_pam_info
225 && msg
[0]->msg_style
== PAM_TEXT_INFO
)
227 #ifdef HAVE_SECURITY_PAM_MISC_H
228 return misc_conv(num_msg
, msg
, resp
, appdata_ptr
);
229 #elif defined(HAVE_SECURITY_OPENPAM_H)
230 return openpam_ttyconv(num_msg
, msg
, resp
, appdata_ptr
);
234 static struct pam_conv conv
=
241 cleanup_pam (int retcode
)
243 int saved_errno
= errno
;
245 if (_pam_session_opened
)
246 pam_close_session (pamh
, 0);
248 if (_pam_cred_established
)
249 pam_setcred (pamh
, PAM_DELETE_CRED
| PAM_SILENT
);
251 pam_end(pamh
, retcode
);
256 /* Signal handler for parent process. */
258 su_catch_sig (int sig
)
263 /* Export env variables declared by PAM modules. */
269 /* This is a copy but don't care to free as we exec later anyways. */
270 env
= pam_getenvlist (pamh
);
273 if (putenv (*env
) != 0)
274 err (EXIT_FAILURE
, NULL
);
280 create_watching_parent (void)
284 struct sigaction oldact
[3];
288 retval
= pam_open_session (pamh
, 0);
289 if (is_pam_failure(retval
))
291 cleanup_pam (retval
);
292 errx (EXIT_FAILURE
, _("cannot open session: %s"),
293 pam_strerror (pamh
, retval
));
296 _pam_session_opened
= 1;
298 memset(oldact
, 0, sizeof(oldact
));
301 if (child
== (pid_t
) -1)
303 cleanup_pam (PAM_ABORT
);
304 err (EXIT_FAILURE
, _("cannot create child process"));
307 /* the child proceeds to run the shell */
311 /* In the parent watch the child. */
313 /* su without pam support does not have a helper that keeps
314 sitting on any directory so let's go to /. */
315 if (chdir ("/") != 0)
316 warn (_("cannot change directory to %s"), "/");
318 sigfillset (&ourset
);
319 if (sigprocmask (SIG_BLOCK
, &ourset
, NULL
))
321 warn (_("cannot block signals"));
322 caught_signal
= true;
326 struct sigaction action
;
327 action
.sa_handler
= su_catch_sig
;
328 sigemptyset (&action
.sa_mask
);
330 sigemptyset (&ourset
);
333 if (sigaddset(&ourset
, SIGINT
) || sigaddset(&ourset
, SIGQUIT
))
335 warn (_("cannot set signal handler"));
336 caught_signal
= true;
339 if (!caught_signal
&& (sigaddset(&ourset
, SIGTERM
)
340 || sigaddset(&ourset
, SIGALRM
)
341 || sigaction(SIGTERM
, &action
, &oldact
[0])
342 || sigprocmask(SIG_UNBLOCK
, &ourset
, NULL
))) {
343 warn (_("cannot set signal handler"));
344 caught_signal
= true;
346 if (!caught_signal
&& !same_session
&& (sigaction(SIGINT
, &action
, &oldact
[1])
347 || sigaction(SIGQUIT
, &action
, &oldact
[2])))
349 warn (_("cannot set signal handler"));
350 caught_signal
= true;
358 pid
= waitpid (child
, &status
, WUNTRACED
);
360 if (pid
!= (pid_t
)-1 && WIFSTOPPED (status
))
362 kill (getpid (), SIGSTOP
);
363 /* once we get here, we must have resumed */
369 if (pid
!= (pid_t
)-1)
371 if (WIFSIGNALED (status
))
373 fprintf (stderr
, "%s%s\n", strsignal (WTERMSIG (status
)),
374 WCOREDUMP (status
) ? _(" (core dumped)") : "");
375 status
= WTERMSIG (status
) + 128;
378 status
= WEXITSTATUS (status
);
380 else if (caught_signal
)
381 status
= caught_signal
+ 128;
390 fprintf (stderr
, _("\nSession terminated, killing shell..."));
391 kill (child
, SIGTERM
);
394 cleanup_pam (PAM_SUCCESS
);
399 kill (child
, SIGKILL
);
400 fprintf (stderr
, _(" ...killed.\n"));
402 /* Let's terminate itself with the received signal.
404 * It seems that shells use WIFSIGNALED() rather than our exit status
405 * value to detect situations when is necessary to cleanup (reset)
406 * terminal settings (kzak -- Jun 2013).
408 switch (caught_signal
) {
410 sigaction(SIGTERM
, &oldact
[0], NULL
);
413 sigaction(SIGINT
, &oldact
[1], NULL
);
416 sigaction(SIGQUIT
, &oldact
[2], NULL
);
419 /* just in case that signal stuff initialization failed and
420 * caught_signal = true */
421 caught_signal
= SIGKILL
;
424 kill(getpid(), caught_signal
);
430 authenticate (const struct passwd
*pw
)
432 const struct passwd
*lpw
= NULL
;
433 const char *cp
, *srvname
= NULL
;
438 srvname
= simulate_login
? PAM_SRVNAME_SU_L
: PAM_SRVNAME_SU
;
441 srvname
= simulate_login
? PAM_SRVNAME_RUNUSER_L
: PAM_SRVNAME_RUNUSER
;
448 retval
= pam_start (srvname
, pw
->pw_name
, &conv
, &pamh
);
449 if (is_pam_failure(retval
))
452 if (isatty (0) && (cp
= ttyname (0)) != NULL
)
456 if (strncmp (cp
, "/dev/", 5) == 0)
460 retval
= pam_set_item (pamh
, PAM_TTY
, tty
);
461 if (is_pam_failure(retval
))
465 lpw
= current_getpwuid ();
466 if (lpw
&& lpw
->pw_name
)
468 retval
= pam_set_item (pamh
, PAM_RUSER
, (const void *) lpw
->pw_name
);
469 if (is_pam_failure(retval
))
473 if (su_mode
== RUNUSER_MODE
)
476 * This is the only difference between runuser(1) and su(1). The command
477 * runuser(1) does not required authentication, because user is root.
480 errx(EXIT_FAILURE
, _("may not be used by non-root users"));
484 retval
= pam_authenticate (pamh
, 0);
485 if (is_pam_failure(retval
))
488 retval
= pam_acct_mgmt (pamh
, 0);
489 if (retval
== PAM_NEW_AUTHTOK_REQD
)
491 /* Password has expired. Offer option to change it. */
492 retval
= pam_chauthtok (pamh
, PAM_CHANGE_EXPIRED_AUTHTOK
);
497 log_syslog(pw
, !is_pam_failure(retval
));
499 if (is_pam_failure(retval
))
505 msg
= pam_strerror(pamh
, retval
);
506 pam_end(pamh
, retval
);
507 sleep (getlogindefs_num ("FAIL_DELAY", 1));
508 errx (EXIT_FAILURE
, "%s", msg
?msg
:_("incorrect password"));
513 set_path(const struct passwd
* pw
)
517 r
= logindefs_setenv("PATH", "ENV_PATH", _PATH_DEFPATH
);
519 else if ((r
= logindefs_setenv("PATH", "ENV_ROOTPATH", NULL
)) != 0)
520 r
= logindefs_setenv("PATH", "ENV_SUPATH", _PATH_DEFPATH_ROOT
);
523 err (EXIT_FAILURE
, _("failed to set PATH"));
526 /* Update `environ' for the new shell based on PW, with SHELL being
527 the value for the SHELL environment variable. */
530 modify_environment (const struct passwd
*pw
, const char *shell
)
534 /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH.
535 Unset all other environment variables. */
536 char *term
= getenv ("TERM");
538 term
= xstrdup (term
);
539 environ
= xmalloc ((6 + !!term
) * sizeof (char *));
542 xsetenv ("TERM", term
, 1);
545 xsetenv ("HOME", pw
->pw_dir
, 1);
547 xsetenv ("SHELL", shell
, 1);
548 xsetenv ("USER", pw
->pw_name
, 1);
549 xsetenv ("LOGNAME", pw
->pw_name
, 1);
554 /* Set HOME, SHELL, and (if not becoming a superuser)
556 if (change_environment
)
558 xsetenv ("HOME", pw
->pw_dir
, 1);
560 xsetenv ("SHELL", shell
, 1);
561 if (getlogindefs_bool ("ALWAYS_SET_PATH", 0))
566 xsetenv ("USER", pw
->pw_name
, 1);
567 xsetenv ("LOGNAME", pw
->pw_name
, 1);
575 /* Become the user and group(s) specified by PW. */
578 init_groups (const struct passwd
*pw
, gid_t
*groups
, size_t num_groups
)
585 retval
= setgroups (num_groups
, groups
);
587 retval
= initgroups (pw
->pw_name
, pw
->pw_gid
);
591 cleanup_pam (PAM_ABORT
);
592 err (EXIT_FAILURE
, _("cannot set groups"));
596 retval
= pam_setcred (pamh
, PAM_ESTABLISH_CRED
);
597 if (is_pam_failure(retval
))
598 errx (EXIT_FAILURE
, "%s", pam_strerror (pamh
, retval
));
600 _pam_cred_established
= 1;
604 change_identity (const struct passwd
*pw
)
606 if (setgid (pw
->pw_gid
))
607 err (EXIT_FAILURE
, _("cannot set group id"));
608 if (setuid (pw
->pw_uid
))
609 err (EXIT_FAILURE
, _("cannot set user id"));
612 /* Run SHELL, or DEFAULT_SHELL if SHELL is empty.
613 If COMMAND is nonzero, pass it to the shell with the -c option.
614 Pass ADDITIONAL_ARGS to the shell as more arguments; there
615 are N_ADDITIONAL_ARGS extra arguments. */
618 run_shell (char const *shell
, char const *command
, char **additional_args
,
619 size_t n_additional_args
)
621 size_t n_args
= 1 + fast_startup
+ 2 * !!command
+ n_additional_args
+ 1;
622 char const **args
= xcalloc (n_args
, sizeof *args
);
628 char *shell_basename
;
630 shell_basename
= basename (shell
);
631 arg0
= xmalloc (strlen (shell_basename
) + 2);
633 strcpy (arg0
+ 1, shell_basename
);
637 args
[0] = basename (shell
);
639 args
[argno
++] = "-f";
642 args
[argno
++] = "-c";
643 args
[argno
++] = command
;
645 memcpy (args
+ argno
, additional_args
, n_additional_args
* sizeof *args
);
646 args
[argno
+ n_additional_args
] = NULL
;
647 execv (shell
, (char **) args
);
650 int exit_status
= (errno
== ENOENT
? EXIT_ENOENT
: EXIT_CANNOT_INVOKE
);
651 warn (_("failed to execute %s"), shell
);
656 /* Return true if SHELL is a restricted shell (one not returned by
657 getusershell), else false, meaning it is a standard shell. */
660 restricted_shell (const char *shell
)
665 while ((line
= getusershell ()) != NULL
)
667 if (*line
!= '#' && !strcmp (line
, shell
))
677 static void __attribute__((__noreturn__
))
680 if (su_mode
== RUNUSER_MODE
) {
681 fputs(USAGE_HEADER
, stdout
);
682 printf (_(" %s [options] -u <user> <command>\n"), program_invocation_short_name
);
683 printf (_(" %s [options] [-] [<user> [<argument>...]]\n"), program_invocation_short_name
);
685 "Run <command> with the effective user ID and group ID of <user>. If -u is\n"
686 "not given, fall back to su(1)-compatible semantics and execute standard shell.\n"
687 "The options -c, -f, -l, and -s are mutually exclusive with -u.\n"), stdout
);
689 fputs(USAGE_OPTIONS
, stdout
);
691 fputs (_(" -u, --user <user> username\n"), stdout
);
694 fputs(USAGE_HEADER
, stdout
);
695 printf (_(" %s [options] [-] [<user> [<argument>...]]\n"), program_invocation_short_name
);
697 "Change the effective user ID and group ID to that of <user>.\n"
698 "A mere - implies -l. If <user> is not given, root is assumed.\n"), stdout
);
700 fputs(USAGE_OPTIONS
, stdout
);
703 fputs (_(" -m, -p, --preserve-environment do not reset environment variables\n"), stdout
);
704 fputs (_(" -g, --group <group> specify the primary group\n"), stdout
);
705 fputs (_(" -G, --supp-group <group> specify a supplemental group\n\n"), stdout
);
707 fputs (_(" -, -l, --login make the shell a login shell\n"), stdout
);
708 fputs (_(" -c, --command <command> pass a single command to the shell with -c\n"), stdout
);
709 fputs (_(" --session-command <command> pass a single command to the shell with -c\n"
710 " and do not create a new session\n"), stdout
);
711 fputs (_(" -f, --fast pass -f to the shell (for csh or tcsh)\n"), stdout
);
712 fputs (_(" -s, --shell <shell> run <shell> if /etc/shells allows it\n"), stdout
);
714 fputs(USAGE_SEPARATOR
, stdout
);
715 fputs(USAGE_HELP
, stdout
);
716 fputs(USAGE_VERSION
, stdout
);
717 printf(USAGE_MAN_TAIL(su_mode
== SU_MODE
? "su(1)" : "runuser(1)"));
722 void load_config(void)
726 logindefs_load_file(_PATH_LOGINDEFS_SU
);
729 logindefs_load_file(_PATH_LOGINDEFS_RUNUSER
);
733 logindefs_load_file(_PATH_LOGINDEFS
);
737 * Returns 1 if the current user is not root
742 uid_t ruid
= getuid();
743 uid_t euid
= geteuid();
745 /* if we're really root and aren't running setuid */
746 return (uid_t
) 0 == ruid
&& ruid
== euid
? 0 : 1;
750 add_supp_group(const char *name
, gid_t
**groups
, size_t *ngroups
)
754 if (*ngroups
>= NGROUPS_MAX
)
756 P_("specifying more than %d supplemental group is not possible",
757 "specifying more than %d supplemental groups is not possible",
758 NGROUPS_MAX
- 1), NGROUPS_MAX
- 1);
762 errx(EXIT_FAILURE
, _("group %s does not exist"), name
);
764 *groups
= xrealloc(*groups
, sizeof(gid_t
) * (*ngroups
+ 1));
765 (*groups
)[*ngroups
] = gr
->gr_gid
;
772 su_main (int argc
, char **argv
, int mode
)
775 const char *new_user
= DEFAULT_USER
, *runuser_user
= NULL
;
776 char *command
= NULL
;
777 int request_same_session
= 0;
780 struct passwd pw_copy
;
782 gid_t
*groups
= NULL
;
784 bool use_supp
= false;
785 bool use_gid
= false;
788 static const struct option longopts
[] = {
789 {"command", required_argument
, NULL
, 'c'},
790 {"session-command", required_argument
, NULL
, 'C'},
791 {"fast", no_argument
, NULL
, 'f'},
792 {"login", no_argument
, NULL
, 'l'},
793 {"preserve-environment", no_argument
, NULL
, 'p'},
794 {"shell", required_argument
, NULL
, 's'},
795 {"group", required_argument
, NULL
, 'g'},
796 {"supp-group", required_argument
, NULL
, 'G'},
797 {"user", required_argument
, NULL
, 'u'}, /* runuser only */
798 {"help", no_argument
, 0, 'h'},
799 {"version", no_argument
, 0, 'V'},
803 setlocale (LC_ALL
, "");
804 bindtextdomain (PACKAGE
, LOCALEDIR
);
805 textdomain (PACKAGE
);
806 atexit(close_stdout
);
809 fast_startup
= false;
810 simulate_login
= false;
811 change_environment
= true;
813 while ((optc
= getopt_long (argc
, argv
, "c:fg:G:lmps:u:hV", longopts
, NULL
)) != -1)
823 request_same_session
= 1;
832 gid
= add_supp_group(optarg
, &groups
, &ngroups
);
837 add_supp_group(optarg
, &groups
, &ngroups
);
841 simulate_login
= true;
846 change_environment
= false;
854 if (su_mode
!= RUNUSER_MODE
)
855 usage (EXIT_FAILURE
);
856 runuser_user
= optarg
;
863 printf(UTIL_LINUX_VERSION
);
867 usage (EXIT_FAILURE
);
871 restricted
= evaluate_uid ();
873 if (optind
< argc
&& !strcmp (argv
[optind
], "-"))
875 simulate_login
= true;
879 if (simulate_login
&& !change_environment
) {
880 warnx(_("ignoring --preserve-environment, it's mutually exclusive with --login"));
881 change_environment
= true;
887 /* runuser -u <user> <command> */
888 new_user
= runuser_user
;
889 if (shell
|| fast_startup
|| command
|| simulate_login
) {
891 _("options --{shell,fast,command,session-command,login} and "
892 "--user are mutually exclusive"));
895 errx(EXIT_FAILURE
, _("no command was specified"));
899 /* fallthrough if -u <user> is not specified, then follow
900 * traditional su(1) behavior
904 new_user
= argv
[optind
++];
908 if ((use_supp
|| use_gid
) && restricted
)
909 errx(EXIT_FAILURE
, _("only root can specify alternative groups"));
911 logindefs_load_defaults
= load_config
;
913 pw
= getpwnam (new_user
);
914 if (! (pw
&& pw
->pw_name
&& pw
->pw_name
[0] && pw
->pw_dir
&& pw
->pw_dir
[0]
916 errx (EXIT_FAILURE
, _("user %s does not exist"), new_user
);
918 /* Make a copy of the password information and point pw at the local
919 copy instead. Otherwise, some systems (e.g. Linux) would clobber
920 the static data through the getlogin call from log_su.
921 Also, make sure pw->pw_shell is a nonempty string.
922 It may be NULL when NEW_USER is a username that is retrieved via NIS (YP),
923 but that doesn't have a default shell listed. */
926 pw
->pw_name
= xstrdup (pw
->pw_name
);
927 pw
->pw_passwd
= xstrdup (pw
->pw_passwd
);
928 pw
->pw_dir
= xstrdup (pw
->pw_dir
);
929 pw
->pw_shell
= xstrdup (pw
->pw_shell
&& pw
->pw_shell
[0]
934 if (use_supp
&& !use_gid
)
935 pw
->pw_gid
= groups
[0];
941 if (request_same_session
|| !command
|| !pw
->pw_uid
)
944 /* initialize shell variable only if "-u <user>" not specified */
948 if (!shell
&& !change_environment
)
949 shell
= getenv ("SHELL");
950 if (shell
&& getuid () != 0 && restricted_shell (pw
->pw_shell
))
952 /* The user being su'd to has a nonstandard shell, and so is
953 probably a uucp account or has restricted access. Don't
954 compromise the account by allowing access with a standard
956 warnx (_("using restricted shell %s"), pw
->pw_shell
);
959 shell
= xstrdup (shell
? shell
: pw
->pw_shell
);
962 init_groups (pw
, groups
, ngroups
);
964 if (!simulate_login
|| command
)
965 suppress_pam_info
= 1; /* don't print PAM info messages */
967 create_watching_parent ();
968 /* Now we're in the child. */
970 change_identity (pw
);
974 /* Set environment after pam_open_session, which may put KRB5CCNAME
975 into the pam_env, etc. */
977 modify_environment (pw
, shell
);
979 if (simulate_login
&& chdir (pw
->pw_dir
) != 0)
980 warn (_("warning: cannot change directory to %s"), pw
->pw_dir
);
983 run_shell (shell
, command
, argv
+ optind
, max (0, argc
- optind
));
985 execvp(argv
[optind
], &argv
[optind
]);
986 err(EXIT_FAILURE
, _("failed to execute %s"), argv
[optind
]);
990 // vim: sw=2 cinoptions=>4,n-2,{2,^-2,\:2,=2,g0,h2,p5,t0,+2,(0,u0,w1,m1