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/default/su"
83 #define _PATH_LOGINDEFS_RUNUSER "/etc/default/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 const 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 * const pw
, const 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 const struct passwd
*pwd
= current_getpwuid();
165 old_user
= pwd
? pwd
->pw_name
: "";
168 if (get_terminal_name(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 * 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(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 gettimeofday(&tv
, NULL
);
202 ut
.ut_tv
.tv_sec
= tv
.tv_sec
;
203 ut
.ut_tv
.tv_usec
= tv
.tv_usec
;
204 ut
.ut_type
= LOGIN_PROCESS
; /* XXX doesn't matter */
205 ut
.ut_pid
= getpid();
207 updwtmpx(_PATH_BTMP
, &ut
);
211 static int su_pam_conv(int num_msg
, const struct pam_message
**msg
,
212 struct pam_response
**resp
, void *appdata_ptr
)
214 if (suppress_pam_info
217 && msg
[0]->msg_style
== PAM_TEXT_INFO
)
219 #ifdef HAVE_SECURITY_PAM_MISC_H
220 return misc_conv(num_msg
, msg
, resp
, appdata_ptr
);
221 #elif defined(HAVE_SECURITY_OPENPAM_H)
222 return openpam_ttyconv(num_msg
, msg
, resp
, appdata_ptr
);
226 static struct pam_conv conv
=
233 cleanup_pam (const int retcode
)
235 const int saved_errno
= errno
;
237 if (_pam_session_opened
)
238 pam_close_session (pamh
, 0);
240 if (_pam_cred_established
)
241 pam_setcred (pamh
, PAM_DELETE_CRED
| PAM_SILENT
);
243 pam_end(pamh
, retcode
);
248 /* Signal handler for parent process. */
250 su_catch_sig (int sig
)
255 /* Export env variables declared by PAM modules. */
261 /* This is a copy but don't care to free as we exec later anyways. */
262 env
= pam_getenvlist (pamh
);
265 if (putenv (*env
) != 0)
266 err (EXIT_FAILURE
, NULL
);
272 create_watching_parent (void)
276 struct sigaction oldact
[3];
278 const int retval
= pam_open_session (pamh
, 0);
280 if (is_pam_failure(retval
))
282 cleanup_pam (retval
);
283 errx (EXIT_FAILURE
, _("cannot open session: %s"),
284 pam_strerror (pamh
, retval
));
287 _pam_session_opened
= 1;
289 memset(oldact
, 0, sizeof(oldact
));
292 if (child
== (pid_t
) -1)
294 cleanup_pam (PAM_ABORT
);
295 err (EXIT_FAILURE
, _("cannot create child process"));
298 /* the child proceeds to run the shell */
302 /* In the parent watch the child. */
304 /* su without pam support does not have a helper that keeps
305 sitting on any directory so let's go to /. */
306 if (chdir ("/") != 0)
307 warn (_("cannot change directory to %s"), "/");
309 sigfillset (&ourset
);
310 if (sigprocmask (SIG_BLOCK
, &ourset
, NULL
))
312 warn (_("cannot block signals"));
313 caught_signal
= true;
317 struct sigaction action
;
318 action
.sa_handler
= su_catch_sig
;
319 sigemptyset (&action
.sa_mask
);
321 sigemptyset (&ourset
);
324 if (sigaddset(&ourset
, SIGINT
) || sigaddset(&ourset
, SIGQUIT
))
326 warn (_("cannot set signal handler"));
327 caught_signal
= true;
330 if (!caught_signal
&& (sigaddset(&ourset
, SIGTERM
)
331 || sigaddset(&ourset
, SIGALRM
)
332 || sigaction(SIGTERM
, &action
, &oldact
[0])
333 || sigprocmask(SIG_UNBLOCK
, &ourset
, NULL
))) {
334 warn (_("cannot set signal handler"));
335 caught_signal
= true;
337 if (!caught_signal
&& !same_session
&& (sigaction(SIGINT
, &action
, &oldact
[1])
338 || sigaction(SIGQUIT
, &action
, &oldact
[2])))
340 warn (_("cannot set signal handler"));
341 caught_signal
= true;
349 pid
= waitpid (child
, &status
, WUNTRACED
);
351 if (pid
!= (pid_t
)-1 && WIFSTOPPED (status
))
353 kill (getpid (), SIGSTOP
);
354 /* once we get here, we must have resumed */
360 if (pid
!= (pid_t
)-1)
362 if (WIFSIGNALED (status
))
364 fprintf (stderr
, "%s%s\n", strsignal (WTERMSIG (status
)),
365 WCOREDUMP (status
) ? _(" (core dumped)") : "");
366 status
= WTERMSIG (status
) + 128;
369 status
= WEXITSTATUS (status
);
371 /* child is gone, don't use the PID anymore */
374 else if (caught_signal
)
375 status
= caught_signal
+ 128;
382 if (caught_signal
&& child
!= (pid_t
)-1)
384 fprintf (stderr
, _("\nSession terminated, killing shell..."));
385 kill (child
, SIGTERM
);
388 cleanup_pam (PAM_SUCCESS
);
392 if (child
!= (pid_t
)-1)
395 kill (child
, SIGKILL
);
396 fprintf (stderr
, _(" ...killed.\n"));
399 /* Let's terminate itself with the received signal.
401 * It seems that shells use WIFSIGNALED() rather than our exit status
402 * value to detect situations when is necessary to cleanup (reset)
403 * terminal settings (kzak -- Jun 2013).
405 switch (caught_signal
) {
407 sigaction(SIGTERM
, &oldact
[0], NULL
);
410 sigaction(SIGINT
, &oldact
[1], NULL
);
413 sigaction(SIGQUIT
, &oldact
[2], NULL
);
416 /* just in case that signal stuff initialization failed and
417 * caught_signal = true */
418 caught_signal
= SIGKILL
;
421 kill(getpid(), caught_signal
);
427 authenticate (const struct passwd
* const pw
)
429 const struct passwd
*lpw
= NULL
;
430 const char *cp
, *srvname
= NULL
;
435 srvname
= simulate_login
? PAM_SRVNAME_SU_L
: PAM_SRVNAME_SU
;
438 srvname
= simulate_login
? PAM_SRVNAME_RUNUSER_L
: PAM_SRVNAME_RUNUSER
;
445 retval
= pam_start (srvname
, pw
->pw_name
, &conv
, &pamh
);
446 if (is_pam_failure(retval
))
449 if (isatty (0) && (cp
= ttyname (0)) != NULL
)
453 if (strncmp (cp
, "/dev/", 5) == 0)
457 retval
= pam_set_item (pamh
, PAM_TTY
, tty
);
458 if (is_pam_failure(retval
))
462 lpw
= current_getpwuid ();
463 if (lpw
&& lpw
->pw_name
)
465 retval
= pam_set_item (pamh
, PAM_RUSER
, (const void *) lpw
->pw_name
);
466 if (is_pam_failure(retval
))
470 if (su_mode
== RUNUSER_MODE
)
473 * This is the only difference between runuser(1) and su(1). The command
474 * runuser(1) does not required authentication, because user is root.
477 errx(EXIT_FAILURE
, _("may not be used by non-root users"));
481 retval
= pam_authenticate (pamh
, 0);
482 if (is_pam_failure(retval
))
485 retval
= pam_acct_mgmt (pamh
, 0);
486 if (retval
== PAM_NEW_AUTHTOK_REQD
)
488 /* Password has expired. Offer option to change it. */
489 retval
= pam_chauthtok (pamh
, PAM_CHANGE_EXPIRED_AUTHTOK
);
494 log_syslog(pw
, !is_pam_failure(retval
));
496 if (is_pam_failure(retval
))
502 msg
= pam_strerror(pamh
, retval
);
503 pam_end(pamh
, retval
);
504 sleep (getlogindefs_num ("FAIL_DELAY", 1));
505 errx (EXIT_FAILURE
, "%s", msg
?msg
:_("incorrect password"));
510 set_path(const struct passwd
* const pw
)
514 r
= logindefs_setenv("PATH", "ENV_PATH", _PATH_DEFPATH
);
516 else if ((r
= logindefs_setenv("PATH", "ENV_ROOTPATH", NULL
)) != 0)
517 r
= logindefs_setenv("PATH", "ENV_SUPATH", _PATH_DEFPATH_ROOT
);
520 err (EXIT_FAILURE
, _("failed to set the %s environment variable"), "PATH");
523 /* Update `environ' for the new shell based on PW, with SHELL being
524 the value for the SHELL environment variable. */
527 modify_environment (const struct passwd
* const pw
, const char * const shell
)
531 /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH.
532 Unset all other environment variables. */
533 char *term
= getenv ("TERM");
535 term
= xstrdup (term
);
536 environ
= xmalloc ((6 + !!term
) * sizeof (char *));
539 xsetenv ("TERM", term
, 1);
542 xsetenv ("HOME", pw
->pw_dir
, 1);
544 xsetenv ("SHELL", shell
, 1);
545 xsetenv ("USER", pw
->pw_name
, 1);
546 xsetenv ("LOGNAME", pw
->pw_name
, 1);
551 /* Set HOME, SHELL, and (if not becoming a superuser)
553 if (change_environment
)
555 xsetenv ("HOME", pw
->pw_dir
, 1);
557 xsetenv ("SHELL", shell
, 1);
558 if (getlogindefs_bool ("ALWAYS_SET_PATH", 0))
563 xsetenv ("USER", pw
->pw_name
, 1);
564 xsetenv ("LOGNAME", pw
->pw_name
, 1);
572 /* Become the user and group(s) specified by PW. */
575 init_groups (const struct passwd
* const pw
, const gid_t
* const groups
, const size_t num_groups
)
582 retval
= setgroups (num_groups
, groups
);
584 retval
= initgroups (pw
->pw_name
, pw
->pw_gid
);
588 cleanup_pam (PAM_ABORT
);
589 err (EXIT_FAILURE
, _("cannot set groups"));
593 retval
= pam_setcred (pamh
, PAM_ESTABLISH_CRED
);
594 if (is_pam_failure(retval
))
595 errx (EXIT_FAILURE
, "%s", pam_strerror (pamh
, retval
));
597 _pam_cred_established
= 1;
601 change_identity (const struct passwd
* const pw
)
603 if (setgid (pw
->pw_gid
))
604 err (EXIT_FAILURE
, _("cannot set group id"));
605 if (setuid (pw
->pw_uid
))
606 err (EXIT_FAILURE
, _("cannot set user id"));
609 /* Run SHELL, or DEFAULT_SHELL if SHELL is empty.
610 If COMMAND is nonzero, pass it to the shell with the -c option.
611 Pass ADDITIONAL_ARGS to the shell as more arguments; there
612 are N_ADDITIONAL_ARGS extra arguments. */
615 run_shell (char const * const shell
, char const * const command
, char ** const additional_args
,
616 const size_t n_additional_args
)
618 const size_t n_args
= 1 + fast_startup
+ 2 * !!command
+ n_additional_args
+ 1;
619 const char **args
= xcalloc (n_args
, sizeof *args
);
625 const char *shell_basename
;
627 shell_basename
= basename (shell
);
628 arg0
= xmalloc (strlen (shell_basename
) + 2);
630 strcpy (arg0
+ 1, shell_basename
);
634 args
[0] = basename (shell
);
636 args
[argno
++] = "-f";
639 args
[argno
++] = "-c";
640 args
[argno
++] = command
;
642 memcpy (args
+ argno
, additional_args
, n_additional_args
* sizeof *args
);
643 args
[argno
+ n_additional_args
] = NULL
;
644 execv (shell
, (char **) args
);
647 int exit_status
= (errno
== ENOENT
? EXIT_ENOENT
: EXIT_CANNOT_INVOKE
);
648 warn (_("failed to execute %s"), shell
);
653 /* Return true if SHELL is a restricted shell (one not returned by
654 getusershell), else false, meaning it is a standard shell. */
657 restricted_shell (const char * const shell
)
662 while ((line
= getusershell ()) != NULL
)
664 if (*line
!= '#' && !strcmp (line
, shell
))
674 static void __attribute__((__noreturn__
))
677 if (su_mode
== RUNUSER_MODE
) {
678 fputs(USAGE_HEADER
, stdout
);
679 printf (_(" %s [options] -u <user> [[--] <command>]\n"), program_invocation_short_name
);
680 printf (_(" %s [options] [-] [<user> [<argument>...]]\n"), program_invocation_short_name
);
682 "Run <command> with the effective user ID and group ID of <user>. If -u is\n"
683 "not given, fall back to su(1)-compatible semantics and execute standard shell.\n"
684 "The options -c, -f, -l, and -s are mutually exclusive with -u.\n"), stdout
);
686 fputs(USAGE_OPTIONS
, stdout
);
688 fputs (_(" -u, --user <user> username\n"), stdout
);
691 fputs(USAGE_HEADER
, stdout
);
692 printf (_(" %s [options] [-] [<user> [<argument>...]]\n"), program_invocation_short_name
);
694 "Change the effective user ID and group ID to that of <user>.\n"
695 "A mere - implies -l. If <user> is not given, root is assumed.\n"), stdout
);
697 fputs(USAGE_OPTIONS
, stdout
);
700 fputs (_(" -m, -p, --preserve-environment do not reset environment variables\n"), stdout
);
701 fputs (_(" -g, --group <group> specify the primary group\n"), stdout
);
702 fputs (_(" -G, --supp-group <group> specify a supplemental group\n\n"), stdout
);
704 fputs (_(" -, -l, --login make the shell a login shell\n"), stdout
);
705 fputs (_(" -c, --command <command> pass a single command to the shell with -c\n"), stdout
);
706 fputs (_(" --session-command <command> pass a single command to the shell with -c\n"
707 " and do not create a new session\n"), stdout
);
708 fputs (_(" -f, --fast pass -f to the shell (for csh or tcsh)\n"), stdout
);
709 fputs (_(" -s, --shell <shell> run <shell> if /etc/shells allows it\n"), stdout
);
711 fputs(USAGE_SEPARATOR
, stdout
);
712 printf(USAGE_HELP_OPTIONS(33));
713 printf(USAGE_MAN_TAIL(su_mode
== SU_MODE
? "su(1)" : "runuser(1)"));
718 void load_config(void)
722 logindefs_load_file(_PATH_LOGINDEFS_SU
);
725 logindefs_load_file(_PATH_LOGINDEFS_RUNUSER
);
732 logindefs_load_file(_PATH_LOGINDEFS
);
736 * Returns 1 if the current user is not root
741 const uid_t ruid
= getuid();
742 const uid_t euid
= geteuid();
744 /* if we're really root and aren't running setuid */
745 return (uid_t
) 0 == ruid
&& ruid
== euid
? 0 : 1;
749 add_supp_group(const char *name
, gid_t
**groups
, size_t *ngroups
)
753 if (*ngroups
>= NGROUPS_MAX
)
755 P_("specifying more than %d supplemental group is not possible",
756 "specifying more than %d supplemental groups is not possible",
757 NGROUPS_MAX
- 1), NGROUPS_MAX
- 1);
761 errx(EXIT_FAILURE
, _("group %s does not exist"), name
);
763 *groups
= xrealloc(*groups
, sizeof(gid_t
) * (*ngroups
+ 1));
764 (*groups
)[*ngroups
] = gr
->gr_gid
;
771 su_main (int argc
, char **argv
, int mode
)
774 const char *new_user
= DEFAULT_USER
, *runuser_user
= NULL
;
775 const char *command
= NULL
;
776 int request_same_session
= 0;
777 const char *shell
= NULL
;
779 struct passwd pw_copy
;
781 gid_t
*groups
= NULL
;
783 bool use_supp
= false;
784 bool use_gid
= false;
787 static const struct option longopts
[] = {
788 {"command", required_argument
, NULL
, 'c'},
789 {"session-command", required_argument
, NULL
, 'C'},
790 {"fast", no_argument
, NULL
, 'f'},
791 {"login", no_argument
, NULL
, 'l'},
792 {"preserve-environment", no_argument
, NULL
, 'p'},
793 {"shell", required_argument
, NULL
, 's'},
794 {"group", required_argument
, NULL
, 'g'},
795 {"supp-group", required_argument
, NULL
, 'G'},
796 {"user", required_argument
, NULL
, 'u'}, /* runuser only */
797 {"help", no_argument
, NULL
, 'h'},
798 {"version", no_argument
, NULL
, 'V'},
802 setlocale (LC_ALL
, "");
803 bindtextdomain (PACKAGE
, LOCALEDIR
);
804 textdomain (PACKAGE
);
805 atexit(close_stdout
);
808 fast_startup
= false;
809 simulate_login
= false;
810 change_environment
= true;
812 while ((optc
= getopt_long (argc
, argv
, "c:fg:G:lmps:u:hV", longopts
, NULL
)) != -1)
822 request_same_session
= 1;
831 gid
= add_supp_group(optarg
, &groups
, &ngroups
);
836 add_supp_group(optarg
, &groups
, &ngroups
);
840 simulate_login
= true;
845 change_environment
= false;
853 if (su_mode
!= RUNUSER_MODE
) {
854 warnx(_("invalid option -- 'u'"));
855 errtryhelp(EXIT_FAILURE
);
857 runuser_user
= optarg
;
864 printf(UTIL_LINUX_VERSION
);
868 errtryhelp(EXIT_FAILURE
);
872 restricted
= evaluate_uid ();
874 if (optind
< argc
&& !strcmp (argv
[optind
], "-"))
876 simulate_login
= true;
880 if (simulate_login
&& !change_environment
) {
881 warnx(_("ignoring --preserve-environment, it's mutually exclusive with --login"));
882 change_environment
= true;
888 /* runuser -u <user> <command> */
889 new_user
= runuser_user
;
890 if (shell
|| fast_startup
|| command
|| simulate_login
) {
892 _("options --{shell,fast,command,session-command,login} and "
893 "--user are mutually exclusive"));
896 errx(EXIT_FAILURE
, _("no command was specified"));
900 /* fallthrough if -u <user> is not specified, then follow
901 * traditional su(1) behavior
906 new_user
= argv
[optind
++];
913 if ((use_supp
|| use_gid
) && restricted
)
914 errx(EXIT_FAILURE
, _("only root can specify alternative groups"));
916 logindefs_load_defaults
= load_config
;
918 pw
= getpwnam (new_user
);
919 if (! (pw
&& pw
->pw_name
&& pw
->pw_name
[0] && pw
->pw_dir
&& pw
->pw_dir
[0]
921 errx (EXIT_FAILURE
, _("user %s does not exist"), new_user
);
923 /* Make a copy of the password information and point pw at the local
924 copy instead. Otherwise, some systems (e.g. Linux) would clobber
925 the static data through the getlogin call from log_su.
926 Also, make sure pw->pw_shell is a nonempty string.
927 It may be NULL when NEW_USER is a username that is retrieved via NIS (YP),
928 but that doesn't have a default shell listed. */
931 pw
->pw_name
= xstrdup (pw
->pw_name
);
932 pw
->pw_passwd
= xstrdup (pw
->pw_passwd
);
933 pw
->pw_dir
= xstrdup (pw
->pw_dir
);
934 pw
->pw_shell
= xstrdup (pw
->pw_shell
&& pw
->pw_shell
[0]
939 if (use_supp
&& !use_gid
)
940 pw
->pw_gid
= groups
[0];
946 if (request_same_session
|| !command
|| !pw
->pw_uid
)
949 /* initialize shell variable only if "-u <user>" not specified */
953 if (!shell
&& !change_environment
)
954 shell
= getenv ("SHELL");
955 if (shell
&& getuid () != 0 && restricted_shell (pw
->pw_shell
))
957 /* The user being su'd to has a nonstandard shell, and so is
958 probably a uucp account or has restricted access. Don't
959 compromise the account by allowing access with a standard
961 warnx (_("using restricted shell %s"), pw
->pw_shell
);
964 shell
= xstrdup (shell
? shell
: pw
->pw_shell
);
967 init_groups (pw
, groups
, ngroups
);
969 if (!simulate_login
|| command
)
970 suppress_pam_info
= 1; /* don't print PAM info messages */
972 create_watching_parent ();
973 /* Now we're in the child. */
975 change_identity (pw
);
979 /* Set environment after pam_open_session, which may put KRB5CCNAME
980 into the pam_env, etc. */
982 modify_environment (pw
, shell
);
984 if (simulate_login
&& chdir (pw
->pw_dir
) != 0)
985 warn (_("warning: cannot change directory to %s"), pw
->pw_dir
);
988 run_shell (shell
, command
, argv
+ optind
, max (0, argc
- optind
));
990 execvp(argv
[optind
], &argv
[optind
]);
991 err(EXIT_FAILURE
, _("failed to execute %s"), argv
[optind
]);
995 // vim: sw=2 cinoptions=>4,n-2,{2,^-2,\:2,=2,g0,h2,p5,t0,+2,(0,u0,w1,m1