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(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(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 (int retcode
)
235 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];
280 retval
= pam_open_session (pamh
, 0);
281 if (is_pam_failure(retval
))
283 cleanup_pam (retval
);
284 errx (EXIT_FAILURE
, _("cannot open session: %s"),
285 pam_strerror (pamh
, retval
));
288 _pam_session_opened
= 1;
290 memset(oldact
, 0, sizeof(oldact
));
293 if (child
== (pid_t
) -1)
295 cleanup_pam (PAM_ABORT
);
296 err (EXIT_FAILURE
, _("cannot create child process"));
299 /* the child proceeds to run the shell */
303 /* In the parent watch the child. */
305 /* su without pam support does not have a helper that keeps
306 sitting on any directory so let's go to /. */
307 if (chdir ("/") != 0)
308 warn (_("cannot change directory to %s"), "/");
310 sigfillset (&ourset
);
311 if (sigprocmask (SIG_BLOCK
, &ourset
, NULL
))
313 warn (_("cannot block signals"));
314 caught_signal
= true;
318 struct sigaction action
;
319 action
.sa_handler
= su_catch_sig
;
320 sigemptyset (&action
.sa_mask
);
322 sigemptyset (&ourset
);
325 if (sigaddset(&ourset
, SIGINT
) || sigaddset(&ourset
, SIGQUIT
))
327 warn (_("cannot set signal handler"));
328 caught_signal
= true;
331 if (!caught_signal
&& (sigaddset(&ourset
, SIGTERM
)
332 || sigaddset(&ourset
, SIGALRM
)
333 || sigaction(SIGTERM
, &action
, &oldact
[0])
334 || sigprocmask(SIG_UNBLOCK
, &ourset
, NULL
))) {
335 warn (_("cannot set signal handler"));
336 caught_signal
= true;
338 if (!caught_signal
&& !same_session
&& (sigaction(SIGINT
, &action
, &oldact
[1])
339 || sigaction(SIGQUIT
, &action
, &oldact
[2])))
341 warn (_("cannot set signal handler"));
342 caught_signal
= true;
350 pid
= waitpid (child
, &status
, WUNTRACED
);
352 if (pid
!= (pid_t
)-1 && WIFSTOPPED (status
))
354 kill (getpid (), SIGSTOP
);
355 /* once we get here, we must have resumed */
361 if (pid
!= (pid_t
)-1)
363 if (WIFSIGNALED (status
))
365 fprintf (stderr
, "%s%s\n", strsignal (WTERMSIG (status
)),
366 WCOREDUMP (status
) ? _(" (core dumped)") : "");
367 status
= WTERMSIG (status
) + 128;
370 status
= WEXITSTATUS (status
);
372 else if (caught_signal
)
373 status
= caught_signal
+ 128;
382 fprintf (stderr
, _("\nSession terminated, killing shell..."));
383 kill (child
, SIGTERM
);
386 cleanup_pam (PAM_SUCCESS
);
391 kill (child
, SIGKILL
);
392 fprintf (stderr
, _(" ...killed.\n"));
394 /* Let's terminate itself with the received signal.
396 * It seems that shells use WIFSIGNALED() rather than our exit status
397 * value to detect situations when is necessary to cleanup (reset)
398 * terminal settings (kzak -- Jun 2013).
400 switch (caught_signal
) {
402 sigaction(SIGTERM
, &oldact
[0], NULL
);
405 sigaction(SIGINT
, &oldact
[1], NULL
);
408 sigaction(SIGQUIT
, &oldact
[2], NULL
);
411 /* just in case that signal stuff initialization failed and
412 * caught_signal = true */
413 caught_signal
= SIGKILL
;
416 kill(getpid(), caught_signal
);
422 authenticate (const struct passwd
*pw
)
424 const struct passwd
*lpw
= NULL
;
425 const char *cp
, *srvname
= NULL
;
430 srvname
= simulate_login
? PAM_SRVNAME_SU_L
: PAM_SRVNAME_SU
;
433 srvname
= simulate_login
? PAM_SRVNAME_RUNUSER_L
: PAM_SRVNAME_RUNUSER
;
440 retval
= pam_start (srvname
, pw
->pw_name
, &conv
, &pamh
);
441 if (is_pam_failure(retval
))
444 if (isatty (0) && (cp
= ttyname (0)) != NULL
)
448 if (strncmp (cp
, "/dev/", 5) == 0)
452 retval
= pam_set_item (pamh
, PAM_TTY
, tty
);
453 if (is_pam_failure(retval
))
457 lpw
= current_getpwuid ();
458 if (lpw
&& lpw
->pw_name
)
460 retval
= pam_set_item (pamh
, PAM_RUSER
, (const void *) lpw
->pw_name
);
461 if (is_pam_failure(retval
))
465 if (su_mode
== RUNUSER_MODE
)
468 * This is the only difference between runuser(1) and su(1). The command
469 * runuser(1) does not required authentication, because user is root.
472 errx(EXIT_FAILURE
, _("may not be used by non-root users"));
476 retval
= pam_authenticate (pamh
, 0);
477 if (is_pam_failure(retval
))
480 retval
= pam_acct_mgmt (pamh
, 0);
481 if (retval
== PAM_NEW_AUTHTOK_REQD
)
483 /* Password has expired. Offer option to change it. */
484 retval
= pam_chauthtok (pamh
, PAM_CHANGE_EXPIRED_AUTHTOK
);
489 log_syslog(pw
, !is_pam_failure(retval
));
491 if (is_pam_failure(retval
))
497 msg
= pam_strerror(pamh
, retval
);
498 pam_end(pamh
, retval
);
499 sleep (getlogindefs_num ("FAIL_DELAY", 1));
500 errx (EXIT_FAILURE
, "%s", msg
?msg
:_("incorrect password"));
505 set_path(const struct passwd
* pw
)
509 r
= logindefs_setenv("PATH", "ENV_PATH", _PATH_DEFPATH
);
511 else if ((r
= logindefs_setenv("PATH", "ENV_ROOTPATH", NULL
)) != 0)
512 r
= logindefs_setenv("PATH", "ENV_SUPATH", _PATH_DEFPATH_ROOT
);
515 err (EXIT_FAILURE
, _("failed to set the %s environment variable"), "PATH");
518 /* Update `environ' for the new shell based on PW, with SHELL being
519 the value for the SHELL environment variable. */
522 modify_environment (const struct passwd
*pw
, const char *shell
)
526 /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH.
527 Unset all other environment variables. */
528 char *term
= getenv ("TERM");
530 term
= xstrdup (term
);
531 environ
= xmalloc ((6 + !!term
) * sizeof (char *));
534 xsetenv ("TERM", term
, 1);
537 xsetenv ("HOME", pw
->pw_dir
, 1);
539 xsetenv ("SHELL", shell
, 1);
540 xsetenv ("USER", pw
->pw_name
, 1);
541 xsetenv ("LOGNAME", pw
->pw_name
, 1);
546 /* Set HOME, SHELL, and (if not becoming a superuser)
548 if (change_environment
)
550 xsetenv ("HOME", pw
->pw_dir
, 1);
552 xsetenv ("SHELL", shell
, 1);
553 if (getlogindefs_bool ("ALWAYS_SET_PATH", 0))
558 xsetenv ("USER", pw
->pw_name
, 1);
559 xsetenv ("LOGNAME", pw
->pw_name
, 1);
567 /* Become the user and group(s) specified by PW. */
570 init_groups (const struct passwd
*pw
, gid_t
*groups
, size_t num_groups
)
577 retval
= setgroups (num_groups
, groups
);
579 retval
= initgroups (pw
->pw_name
, pw
->pw_gid
);
583 cleanup_pam (PAM_ABORT
);
584 err (EXIT_FAILURE
, _("cannot set groups"));
588 retval
= pam_setcred (pamh
, PAM_ESTABLISH_CRED
);
589 if (is_pam_failure(retval
))
590 errx (EXIT_FAILURE
, "%s", pam_strerror (pamh
, retval
));
592 _pam_cred_established
= 1;
596 change_identity (const struct passwd
*pw
)
598 if (setgid (pw
->pw_gid
))
599 err (EXIT_FAILURE
, _("cannot set group id"));
600 if (setuid (pw
->pw_uid
))
601 err (EXIT_FAILURE
, _("cannot set user id"));
604 /* Run SHELL, or DEFAULT_SHELL if SHELL is empty.
605 If COMMAND is nonzero, pass it to the shell with the -c option.
606 Pass ADDITIONAL_ARGS to the shell as more arguments; there
607 are N_ADDITIONAL_ARGS extra arguments. */
610 run_shell (char const *shell
, char const *command
, char **additional_args
,
611 size_t n_additional_args
)
613 size_t n_args
= 1 + fast_startup
+ 2 * !!command
+ n_additional_args
+ 1;
614 char const **args
= xcalloc (n_args
, sizeof *args
);
620 char *shell_basename
;
622 shell_basename
= basename (shell
);
623 arg0
= xmalloc (strlen (shell_basename
) + 2);
625 strcpy (arg0
+ 1, shell_basename
);
629 args
[0] = basename (shell
);
631 args
[argno
++] = "-f";
634 args
[argno
++] = "-c";
635 args
[argno
++] = command
;
637 memcpy (args
+ argno
, additional_args
, n_additional_args
* sizeof *args
);
638 args
[argno
+ n_additional_args
] = NULL
;
639 execv (shell
, (char **) args
);
642 int exit_status
= (errno
== ENOENT
? EXIT_ENOENT
: EXIT_CANNOT_INVOKE
);
643 warn (_("failed to execute %s"), shell
);
648 /* Return true if SHELL is a restricted shell (one not returned by
649 getusershell), else false, meaning it is a standard shell. */
652 restricted_shell (const char *shell
)
657 while ((line
= getusershell ()) != NULL
)
659 if (*line
!= '#' && !strcmp (line
, shell
))
669 static void __attribute__((__noreturn__
))
672 if (su_mode
== RUNUSER_MODE
) {
673 fputs(USAGE_HEADER
, stdout
);
674 printf (_(" %s [options] -u <user> <command>\n"), program_invocation_short_name
);
675 printf (_(" %s [options] [-] [<user> [<argument>...]]\n"), program_invocation_short_name
);
677 "Run <command> with the effective user ID and group ID of <user>. If -u is\n"
678 "not given, fall back to su(1)-compatible semantics and execute standard shell.\n"
679 "The options -c, -f, -l, and -s are mutually exclusive with -u.\n"), stdout
);
681 fputs(USAGE_OPTIONS
, stdout
);
683 fputs (_(" -u, --user <user> username\n"), stdout
);
686 fputs(USAGE_HEADER
, stdout
);
687 printf (_(" %s [options] [-] [<user> [<argument>...]]\n"), program_invocation_short_name
);
689 "Change the effective user ID and group ID to that of <user>.\n"
690 "A mere - implies -l. If <user> is not given, root is assumed.\n"), stdout
);
692 fputs(USAGE_OPTIONS
, stdout
);
695 fputs (_(" -m, -p, --preserve-environment do not reset environment variables\n"), stdout
);
696 fputs (_(" -g, --group <group> specify the primary group\n"), stdout
);
697 fputs (_(" -G, --supp-group <group> specify a supplemental group\n\n"), stdout
);
699 fputs (_(" -, -l, --login make the shell a login shell\n"), stdout
);
700 fputs (_(" -c, --command <command> pass a single command to the shell with -c\n"), stdout
);
701 fputs (_(" --session-command <command> pass a single command to the shell with -c\n"
702 " and do not create a new session\n"), stdout
);
703 fputs (_(" -f, --fast pass -f to the shell (for csh or tcsh)\n"), stdout
);
704 fputs (_(" -s, --shell <shell> run <shell> if /etc/shells allows it\n"), stdout
);
706 fputs(USAGE_SEPARATOR
, stdout
);
707 fputs(USAGE_HELP
, stdout
);
708 fputs(USAGE_VERSION
, stdout
);
709 printf(USAGE_MAN_TAIL(su_mode
== SU_MODE
? "su(1)" : "runuser(1)"));
714 void load_config(void)
718 logindefs_load_file(_PATH_LOGINDEFS_SU
);
721 logindefs_load_file(_PATH_LOGINDEFS_RUNUSER
);
725 logindefs_load_file(_PATH_LOGINDEFS
);
729 * Returns 1 if the current user is not root
734 uid_t ruid
= getuid();
735 uid_t euid
= geteuid();
737 /* if we're really root and aren't running setuid */
738 return (uid_t
) 0 == ruid
&& ruid
== euid
? 0 : 1;
742 add_supp_group(const char *name
, gid_t
**groups
, size_t *ngroups
)
746 if (*ngroups
>= NGROUPS_MAX
)
748 P_("specifying more than %d supplemental group is not possible",
749 "specifying more than %d supplemental groups is not possible",
750 NGROUPS_MAX
- 1), NGROUPS_MAX
- 1);
754 errx(EXIT_FAILURE
, _("group %s does not exist"), name
);
756 *groups
= xrealloc(*groups
, sizeof(gid_t
) * (*ngroups
+ 1));
757 (*groups
)[*ngroups
] = gr
->gr_gid
;
764 su_main (int argc
, char **argv
, int mode
)
767 const char *new_user
= DEFAULT_USER
, *runuser_user
= NULL
;
768 char *command
= NULL
;
769 int request_same_session
= 0;
772 struct passwd pw_copy
;
774 gid_t
*groups
= NULL
;
776 bool use_supp
= false;
777 bool use_gid
= false;
780 static const struct option longopts
[] = {
781 {"command", required_argument
, NULL
, 'c'},
782 {"session-command", required_argument
, NULL
, 'C'},
783 {"fast", no_argument
, NULL
, 'f'},
784 {"login", no_argument
, NULL
, 'l'},
785 {"preserve-environment", no_argument
, NULL
, 'p'},
786 {"shell", required_argument
, NULL
, 's'},
787 {"group", required_argument
, NULL
, 'g'},
788 {"supp-group", required_argument
, NULL
, 'G'},
789 {"user", required_argument
, NULL
, 'u'}, /* runuser only */
790 {"help", no_argument
, 0, 'h'},
791 {"version", no_argument
, 0, 'V'},
795 setlocale (LC_ALL
, "");
796 bindtextdomain (PACKAGE
, LOCALEDIR
);
797 textdomain (PACKAGE
);
798 atexit(close_stdout
);
801 fast_startup
= false;
802 simulate_login
= false;
803 change_environment
= true;
805 while ((optc
= getopt_long (argc
, argv
, "c:fg:G:lmps:u:hV", longopts
, NULL
)) != -1)
815 request_same_session
= 1;
824 gid
= add_supp_group(optarg
, &groups
, &ngroups
);
829 add_supp_group(optarg
, &groups
, &ngroups
);
833 simulate_login
= true;
838 change_environment
= false;
846 if (su_mode
!= RUNUSER_MODE
)
847 usage (EXIT_FAILURE
);
848 runuser_user
= optarg
;
855 printf(UTIL_LINUX_VERSION
);
859 errtryhelp(EXIT_FAILURE
);
863 restricted
= evaluate_uid ();
865 if (optind
< argc
&& !strcmp (argv
[optind
], "-"))
867 simulate_login
= true;
871 if (simulate_login
&& !change_environment
) {
872 warnx(_("ignoring --preserve-environment, it's mutually exclusive with --login"));
873 change_environment
= true;
879 /* runuser -u <user> <command> */
880 new_user
= runuser_user
;
881 if (shell
|| fast_startup
|| command
|| simulate_login
) {
883 _("options --{shell,fast,command,session-command,login} and "
884 "--user are mutually exclusive"));
887 errx(EXIT_FAILURE
, _("no command was specified"));
891 /* fallthrough if -u <user> is not specified, then follow
892 * traditional su(1) behavior
896 new_user
= argv
[optind
++];
900 if ((use_supp
|| use_gid
) && restricted
)
901 errx(EXIT_FAILURE
, _("only root can specify alternative groups"));
903 logindefs_load_defaults
= load_config
;
905 pw
= getpwnam (new_user
);
906 if (! (pw
&& pw
->pw_name
&& pw
->pw_name
[0] && pw
->pw_dir
&& pw
->pw_dir
[0]
908 errx (EXIT_FAILURE
, _("user %s does not exist"), new_user
);
910 /* Make a copy of the password information and point pw at the local
911 copy instead. Otherwise, some systems (e.g. Linux) would clobber
912 the static data through the getlogin call from log_su.
913 Also, make sure pw->pw_shell is a nonempty string.
914 It may be NULL when NEW_USER is a username that is retrieved via NIS (YP),
915 but that doesn't have a default shell listed. */
918 pw
->pw_name
= xstrdup (pw
->pw_name
);
919 pw
->pw_passwd
= xstrdup (pw
->pw_passwd
);
920 pw
->pw_dir
= xstrdup (pw
->pw_dir
);
921 pw
->pw_shell
= xstrdup (pw
->pw_shell
&& pw
->pw_shell
[0]
926 if (use_supp
&& !use_gid
)
927 pw
->pw_gid
= groups
[0];
933 if (request_same_session
|| !command
|| !pw
->pw_uid
)
936 /* initialize shell variable only if "-u <user>" not specified */
940 if (!shell
&& !change_environment
)
941 shell
= getenv ("SHELL");
942 if (shell
&& getuid () != 0 && restricted_shell (pw
->pw_shell
))
944 /* The user being su'd to has a nonstandard shell, and so is
945 probably a uucp account or has restricted access. Don't
946 compromise the account by allowing access with a standard
948 warnx (_("using restricted shell %s"), pw
->pw_shell
);
951 shell
= xstrdup (shell
? shell
: pw
->pw_shell
);
954 init_groups (pw
, groups
, ngroups
);
956 if (!simulate_login
|| command
)
957 suppress_pam_info
= 1; /* don't print PAM info messages */
959 create_watching_parent ();
960 /* Now we're in the child. */
962 change_identity (pw
);
966 /* Set environment after pam_open_session, which may put KRB5CCNAME
967 into the pam_env, etc. */
969 modify_environment (pw
, shell
);
971 if (simulate_login
&& chdir (pw
->pw_dir
) != 0)
972 warn (_("warning: cannot change directory to %s"), pw
->pw_dir
);
975 run_shell (shell
, command
, argv
+ optind
, max (0, argc
- optind
));
977 execvp(argv
[optind
], &argv
[optind
]);
978 err(EXIT_FAILURE
, _("failed to execute %s"), argv
[optind
]);
982 // vim: sw=2 cinoptions=>4,n-2,{2,^-2,\:2,=2,g0,h2,p5,t0,+2,(0,u0,w1,m1