]> git.ipfire.org Git - thirdparty/util-linux.git/blob - login-utils/su.c
syslog always
[thirdparty/util-linux.git] / login-utils / su.c
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
4
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)
8 any later version.
9
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.
14
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. */
18
19 /* Run a shell with the real and effective UID and GID and groups
20 of USER, default `root'.
21
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.
25
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.
30
31 If one or more ARGs are given, they are passed as additional
32 arguments to the subshell.
33
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.
37
38 Based on an implemenation by David MacKenzie <djm@gnu.ai.mit.edu>. */
39
40 #ifndef MAX
41 # define MAX(a,b) ((a) > (b) ? (a) : (b))
42 #endif
43
44 /* Exit statuses for programs like 'env' that exec other programs.
45 EXIT_FAILURE might not be 1, so use EXIT_FAIL in such programs. */
46 enum
47 {
48 EXIT_FAIL = 1,
49 EXIT_CANNOT_INVOKE = 126,
50 EXIT_ENOENT = 127
51 };
52
53 #include <config.h>
54 #include <stdio.h>
55 #include <getopt.h>
56 #include <sys/types.h>
57 #include <pwd.h>
58 #include <grp.h>
59 #include <security/pam_appl.h>
60 #include <security/pam_misc.h>
61 #include <signal.h>
62 #include <sys/wait.h>
63 #include <sys/fsuid.h>
64
65 #include <syslog.h>
66
67 #include "error.h"
68
69 #include <stdbool.h>
70 #include "xalloc.h"
71 #include "nls.h"
72
73 /* The official name of this program (e.g., no `g' prefix). */
74 #define PROGRAM_NAME "su"
75
76 /* name of the pam configuration files. separate configs for su and su - */
77 #define PAM_SERVICE_NAME PROGRAM_NAME
78 #define PAM_SERVICE_NAME_L PROGRAM_NAME "-l"
79
80 #include "getdef.h"
81
82 /* The default PATH for simulated logins to non-superuser accounts. */
83 #define DEFAULT_LOGIN_PATH "/usr/local/bin:/bin:/usr/bin"
84
85 /* The default PATH for simulated logins to superuser accounts. */
86 #define DEFAULT_ROOT_LOGIN_PATH "/usr/sbin:/bin:/usr/bin:/sbin"
87
88 /* The shell to run if none is given in the user's passwd entry. */
89 #define DEFAULT_SHELL "/bin/sh"
90
91 /* The user to become if none is specified. */
92 #define DEFAULT_USER "root"
93
94 extern char **environ;
95
96 static void run_shell (char const *, char const *, char **, size_t)
97 __attribute__ ((__noreturn__));
98
99 /* The name this program was run with. */
100 char *program_name;
101
102 /* If true, pass the `-f' option to the subshell. */
103 static bool fast_startup;
104
105 /* If true, simulate a login instead of just starting a shell. */
106 static bool simulate_login;
107
108 /* If true, change some environment vars to indicate the user su'd to. */
109 static bool change_environment;
110
111 /* If true, then don't call setsid() with a command. */
112 int same_session = 0;
113
114 static bool _pam_session_opened;
115 static bool _pam_cred_established;
116 static sig_atomic_t volatile caught_signal = false;
117 static pam_handle_t *pamh = NULL;
118
119
120 static struct option const longopts[] =
121 {
122 {"command", required_argument, NULL, 'c'},
123 {"session-command", required_argument, NULL, 'C'},
124 {"fast", no_argument, NULL, 'f'},
125 {"login", no_argument, NULL, 'l'},
126 {"preserve-environment", no_argument, NULL, 'p'},
127 {"shell", required_argument, NULL, 's'},
128 {"help", no_argument, 0, 'u'},
129 {"version", no_argument, 0, 'v'},
130 {NULL, 0, NULL, 0}
131 };
132
133 /* Add NAME=VAL to the environment, checking for out of memory errors. */
134
135 static void
136 xsetenv (char const *name, char const *val)
137 {
138 size_t namelen = strlen (name);
139 size_t vallen = strlen (val);
140 char *string = xmalloc (namelen + 1 + vallen + 1);
141 strcpy (string, name);
142 string[namelen] = '=';
143 strcpy (string + namelen + 1, val);
144 if (putenv (string) != 0)
145 error (EXIT_FAILURE, 0, _("out of memory"));
146 }
147
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. */
150
151 static void
152 log_su (struct passwd const *pw, bool successful)
153 {
154 const char *new_user, *old_user, *tty;
155
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 ();
160 if (!old_user)
161 {
162 /* getlogin can fail -- usually due to lack of utmp entry.
163 Resort to getpwuid. */
164 struct passwd *pwd = getpwuid (getuid ());
165 old_user = (pwd ? pwd->pw_name : "");
166 }
167 tty = ttyname (STDERR_FILENO);
168 if (!tty)
169 tty = "none";
170
171 openlog (basename (program_name), 0 , LOG_AUTH);
172 syslog (LOG_NOTICE, "%s(to %s) %s on %s",
173 successful ? "" : "FAILED SU ",
174 new_user, old_user, tty);
175 closelog ();
176 }
177
178 static struct pam_conv conv =
179 {
180 misc_conv,
181 NULL
182 };
183
184 # define PAM_BAIL_P(a) \
185 if (retval) \
186 { \
187 pam_end (pamh, retval); \
188 a; \
189 }
190
191 static void
192 cleanup_pam (int retcode)
193 {
194 if (_pam_session_opened)
195 pam_close_session (pamh, 0);
196
197 if (_pam_cred_established)
198 pam_setcred (pamh, PAM_DELETE_CRED | PAM_SILENT);
199
200 pam_end(pamh, retcode);
201 }
202
203 /* Signal handler for parent process. */
204 static void
205 su_catch_sig (int sig)
206 {
207 caught_signal = true;
208 }
209
210 /* Export env variables declared by PAM modules. */
211 static void
212 export_pamenv (void)
213 {
214 char **env;
215
216 /* This is a copy but don't care to free as we exec later anyways. */
217 env = pam_getenvlist (pamh);
218 while (env && *env)
219 {
220 if (putenv (*env) != 0)
221 error (EXIT_FAILURE, 0, _("out of memory"));
222 env++;
223 }
224 }
225
226 static void
227 create_watching_parent (void)
228 {
229 pid_t child;
230 sigset_t ourset;
231 int status = 0;
232 int retval;
233
234 retval = pam_open_session (pamh, 0);
235 if (retval != PAM_SUCCESS)
236 {
237 cleanup_pam (retval);
238 error (EXIT_FAILURE, 0, _("cannot not open session: %s"),
239 pam_strerror (pamh, retval));
240 }
241 else
242 _pam_session_opened = 1;
243
244 child = fork ();
245 if (child == (pid_t) -1)
246 {
247 cleanup_pam (PAM_ABORT);
248 error (EXIT_FAILURE, errno, _("cannot create child process"));
249 }
250
251 /* the child proceeds to run the shell */
252 if (child == 0)
253 return;
254
255 /* In the parent watch the child. */
256
257 /* su without pam support does not have a helper that keeps
258 sitting on any directory so let's go to /. */
259 if (chdir ("/") != 0)
260 error (0, errno, _("warning: cannot change directory to %s"), "/");
261
262 sigfillset (&ourset);
263 if (sigprocmask (SIG_BLOCK, &ourset, NULL))
264 {
265 error (0, errno, _("cannot block signals"));
266 caught_signal = true;
267 }
268 if (!caught_signal)
269 {
270 struct sigaction action;
271 action.sa_handler = su_catch_sig;
272 sigemptyset (&action.sa_mask);
273 action.sa_flags = 0;
274 sigemptyset (&ourset);
275 if (!same_session)
276 {
277 if (sigaddset(&ourset, SIGINT) || sigaddset(&ourset, SIGQUIT))
278 {
279 error (0, errno, _("cannot set signal handler"));
280 caught_signal = true;
281 }
282 }
283 if (!caught_signal && (sigaddset(&ourset, SIGTERM)
284 || sigaddset(&ourset, SIGALRM)
285 || sigaction(SIGTERM, &action, NULL)
286 || sigprocmask(SIG_UNBLOCK, &ourset, NULL))) {
287 error (0, errno, _("cannot set signal handler"));
288 caught_signal = true;
289 }
290 if (!caught_signal && !same_session && (sigaction(SIGINT, &action, NULL)
291 || sigaction(SIGQUIT, &action, NULL)))
292 {
293 error (0, errno, _("cannot set signal handler"));
294 caught_signal = true;
295 }
296 }
297 if (!caught_signal)
298 {
299 pid_t pid;
300 for (;;)
301 {
302 pid = waitpid (child, &status, WUNTRACED);
303
304 if (pid != (pid_t)-1 && WIFSTOPPED (status))
305 {
306 kill (getpid (), SIGSTOP);
307 /* once we get here, we must have resumed */
308 kill (pid, SIGCONT);
309 }
310 else
311 break;
312 }
313 if (pid != (pid_t)-1)
314 if (WIFSIGNALED (status))
315 status = WTERMSIG (status) + 128;
316 else
317 status = WEXITSTATUS (status);
318 else
319 status = 1;
320 }
321 else
322 status = 1;
323
324 if (caught_signal)
325 {
326 fprintf (stderr, _("\nSession terminated, killing shell..."));
327 kill (child, SIGTERM);
328 }
329
330 cleanup_pam (PAM_SUCCESS);
331
332 if (caught_signal)
333 {
334 sleep (2);
335 kill (child, SIGKILL);
336 fprintf (stderr, _(" ...killed.\n"));
337 }
338 exit (status);
339 }
340
341 static bool
342 correct_password (const struct passwd *pw)
343 {
344 const struct passwd *lpw;
345 const char *cp;
346 int retval;
347
348 retval = pam_start (simulate_login ? PAM_SERVICE_NAME_L : PAM_SERVICE_NAME,
349 pw->pw_name, &conv, &pamh);
350 PAM_BAIL_P (return false);
351
352 if (isatty (0) && (cp = ttyname (0)) != NULL)
353 {
354 const char *tty;
355
356 if (strncmp (cp, "/dev/", 5) == 0)
357 tty = cp + 5;
358 else
359 tty = cp;
360 retval = pam_set_item (pamh, PAM_TTY, tty);
361 PAM_BAIL_P (return false);
362 }
363 # if 0 /* Manpage discourages use of getlogin. */
364 cp = getlogin ();
365 if (!(cp && *cp && (lpw = getpwnam (cp)) != NULL && lpw->pw_uid == getuid ()))
366 # endif
367 lpw = getpwuid (getuid ());
368 if (lpw && lpw->pw_name)
369 {
370 retval = pam_set_item (pamh, PAM_RUSER, (const void *) lpw->pw_name);
371 PAM_BAIL_P (return false);
372 }
373 retval = pam_authenticate (pamh, 0);
374 PAM_BAIL_P (return false);
375 retval = pam_acct_mgmt (pamh, 0);
376 if (retval == PAM_NEW_AUTHTOK_REQD)
377 {
378 /* Password has expired. Offer option to change it. */
379 retval = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
380 PAM_BAIL_P (return false);
381 }
382 PAM_BAIL_P (return false);
383 /* Must be authenticated if this point was reached. */
384 return true;
385 }
386
387 /* Add or clear /sbin and /usr/sbin for the su command
388 used without `-'. */
389
390 /* Set if /sbin is found in path. */
391 #define SBIN_MASK 0x01
392 /* Set if /usr/sbin is found in path. */
393 #define USBIN_MASK 0x02
394
395 static char *
396 addsbin (const char *const path)
397 {
398 unsigned char smask = 0;
399 char *ptr, *tmp, *cur, *ret = NULL;
400 size_t len;
401
402 if (!path || *path == 0)
403 return NULL;
404
405 tmp = xstrdup (path);
406 cur = tmp;
407 for (ptr = strsep (&cur, ":"); ptr != NULL; ptr = strsep (&cur, ":"))
408 {
409 if (!strcmp (ptr, "/sbin"))
410 smask |= SBIN_MASK;
411 if (!strcmp (ptr, "/usr/sbin"))
412 smask |= USBIN_MASK;
413 }
414
415 if ((smask & (USBIN_MASK|SBIN_MASK)) == (USBIN_MASK|SBIN_MASK))
416 {
417 free (tmp);
418 return NULL;
419 }
420
421 len = strlen (path);
422 if (!(smask & USBIN_MASK))
423 len += strlen ("/usr/sbin:");
424
425 if (!(smask & SBIN_MASK))
426 len += strlen (":/sbin");
427
428 ret = xmalloc (len + 1);
429 strcpy (tmp, path);
430
431 *ret = 0;
432 cur = tmp;
433 for (ptr = strsep (&cur, ":"); ptr; ptr = strsep (&cur, ":"))
434 {
435 if (!strcmp (ptr, "."))
436 continue;
437 if (*ret)
438 strcat (ret, ":");
439 if (!(smask & USBIN_MASK) && !strcmp (ptr, "/bin"))
440 {
441 strcat (ret, "/usr/sbin:");
442 strcat (ret, ptr);
443 smask |= USBIN_MASK;
444 continue;
445 }
446 if (!(smask & SBIN_MASK) && !strcmp (ptr, "/usr/bin"))
447 {
448 strcat (ret, ptr);
449 strcat (ret, ":/sbin");
450 smask |= SBIN_MASK;
451 continue;
452 }
453 strcat (ret, ptr);
454 }
455 free (tmp);
456
457 if (!(smask & USBIN_MASK))
458 strcat (ret, ":/usr/sbin");
459
460 if (!(smask & SBIN_MASK))
461 strcat (ret, ":/sbin");
462
463 return ret;
464 }
465
466 static char *
467 clearsbin (const char *const path)
468 {
469 char *ptr, *tmp, *cur, *ret = NULL;
470
471 if (!path || *path == 0)
472 return NULL;
473
474 tmp = strdup (path);
475 if (!tmp)
476 return NULL;
477
478 ret = xmalloc (strlen (path) + 1);
479 *ret = 0;
480 cur = tmp;
481 for (ptr = strsep (&cur, ":"); ptr; ptr = strsep (&cur, ":"))
482 {
483 if (!strcmp (ptr, "/sbin"))
484 continue;
485 if (!strcmp (ptr, "/usr/sbin"))
486 continue;
487 if (!strcmp (ptr, "/usr/local/sbin"))
488 continue;
489 if (*ret)
490 strcat (ret, ":");
491 strcat (ret, ptr);
492 }
493 free (tmp);
494
495 return ret;
496 }
497
498 /* Update `environ' for the new shell based on PW, with SHELL being
499 the value for the SHELL environment variable. */
500
501 static void
502 modify_environment (const struct passwd *pw, const char *shell)
503 {
504 if (simulate_login)
505 {
506 /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH.
507 Unset all other environment variables. */
508 char const *term = getenv ("TERM");
509 if (term)
510 term = xstrdup (term);
511 environ = xmalloc ((6 + !!term) * sizeof (char *));
512 environ[0] = NULL;
513 if (term)
514 xsetenv ("TERM", term);
515 xsetenv ("HOME", pw->pw_dir);
516 xsetenv ("SHELL", shell);
517 xsetenv ("USER", pw->pw_name);
518 xsetenv ("LOGNAME", pw->pw_name);
519 xsetenv ("PATH", (pw->pw_uid
520 ? getdef_str ("PATH", DEFAULT_LOGIN_PATH)
521 : getdef_str ("SUPATH", DEFAULT_ROOT_LOGIN_PATH)));
522 }
523 else
524 {
525 /* Set HOME, SHELL, and if not becoming a super-user,
526 USER and LOGNAME. */
527 if (change_environment)
528 {
529 xsetenv ("HOME", pw->pw_dir);
530 xsetenv ("SHELL", shell);
531 if (getdef_bool ("ALWAYS_SET_PATH", 0))
532 xsetenv ("PATH", (pw->pw_uid
533 ? getdef_str ("PATH",
534 DEFAULT_LOGIN_PATH)
535 : getdef_str ("SUPATH",
536 DEFAULT_ROOT_LOGIN_PATH)));
537 else
538 {
539 char const *path = getenv ("PATH");
540 char *new = NULL;
541
542 if (pw->pw_uid)
543 new = clearsbin (path);
544 else
545 new = addsbin (path);
546
547 if (new)
548 {
549 xsetenv ("PATH", new);
550 free (new);
551 }
552 }
553 if (pw->pw_uid)
554 {
555 xsetenv ("USER", pw->pw_name);
556 xsetenv ("LOGNAME", pw->pw_name);
557 }
558 }
559 }
560
561 export_pamenv ();
562 }
563
564 /* Become the user and group(s) specified by PW. */
565
566 static void
567 init_groups (const struct passwd *pw)
568 {
569 int retval;
570 errno = 0;
571 if (initgroups (pw->pw_name, pw->pw_gid) == -1)
572 {
573 cleanup_pam (PAM_ABORT);
574 error (EXIT_FAIL, errno, _("cannot set groups"));
575 }
576 endgrent ();
577
578 retval = pam_setcred (pamh, PAM_ESTABLISH_CRED);
579 if (retval != PAM_SUCCESS)
580 error (EXIT_FAILURE, 0, "%s", pam_strerror (pamh, retval));
581 else
582 _pam_cred_established = 1;
583 }
584
585 static void
586 change_identity (const struct passwd *pw)
587 {
588 if (setgid (pw->pw_gid))
589 error (EXIT_FAIL, errno, _("cannot set group id"));
590 if (setuid (pw->pw_uid))
591 error (EXIT_FAIL, errno, _("cannot set user id"));
592 }
593
594 /* Run SHELL, or DEFAULT_SHELL if SHELL is empty.
595 If COMMAND is nonzero, pass it to the shell with the -c option.
596 Pass ADDITIONAL_ARGS to the shell as more arguments; there
597 are N_ADDITIONAL_ARGS extra arguments. */
598
599 static void
600 run_shell (char const *shell, char const *command, char **additional_args,
601 size_t n_additional_args)
602 {
603 size_t n_args = 1 + fast_startup + 2 * !!command + n_additional_args + 1;
604 char const **args = xcalloc (n_args, sizeof *args);
605 size_t argno = 1;
606
607 if (simulate_login)
608 {
609 char *arg0;
610 char *shell_basename;
611
612 shell_basename = basename (shell);
613 arg0 = xmalloc (strlen (shell_basename) + 2);
614 arg0[0] = '-';
615 strcpy (arg0 + 1, shell_basename);
616 args[0] = arg0;
617 }
618 else
619 args[0] = basename (shell);
620 if (fast_startup)
621 args[argno++] = "-f";
622 if (command)
623 {
624 args[argno++] = "-c";
625 args[argno++] = command;
626 }
627 memcpy (args + argno, additional_args, n_additional_args * sizeof *args);
628 args[argno + n_additional_args] = NULL;
629 execv (shell, (char **) args);
630
631 {
632 int exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
633 error (0, errno, "%s", shell);
634 exit (exit_status);
635 }
636 }
637
638 /* Return true if SHELL is a restricted shell (one not returned by
639 getusershell), else false, meaning it is a standard shell. */
640
641 static bool
642 restricted_shell (const char *shell)
643 {
644 char *line;
645
646 setusershell ();
647 while ((line = getusershell ()) != NULL)
648 {
649 if (*line != '#' && !strcmp (line, shell))
650 {
651 endusershell ();
652 return false;
653 }
654 }
655 endusershell ();
656 return true;
657 }
658
659 void
660 usage (int status)
661 {
662 if (status != EXIT_SUCCESS)
663 fprintf (stderr, _("Try `%s --help' for more information.\n"),
664 program_name);
665 else
666 {
667 printf (_("Usage: %s [OPTION]... [-] [USER [ARG]...]\n"), program_name);
668 fputs (_("\
669 Change the effective user id and group id to that of USER.\n\
670 \n\
671 -, -l, --login make the shell a login shell\n\
672 -c, --command=COMMAND pass a single COMMAND to the shell with -c\n\
673 --session-command=COMMAND pass a single COMMAND to the shell with -c\n\
674 and do not create a new session\n\
675 -f, --fast pass -f to the shell (for csh or tcsh)\n\
676 -m, --preserve-environment do not reset environment variables\n\
677 -p same as -m\n\
678 -s, --shell=SHELL run SHELL if /etc/shells allows it\n\
679 "), stdout);
680 fputs (_(" -u, --help display this help and exit\n"), stdout);
681 fputs (_(" -v, --version output version information and exit\n"), stdout);
682 fputs (_("\
683 \n\
684 A mere - implies -l. If USER not given, assume root.\n\
685 "), stdout);
686 }
687 exit (status);
688 }
689
690 int
691 main (int argc, char **argv)
692 {
693 int optc;
694 const char *new_user = DEFAULT_USER;
695 char *command = NULL;
696 int request_same_session = 0;
697 char *shell = NULL;
698 struct passwd *pw;
699 struct passwd pw_copy;
700
701 program_name = argv[0];
702 setlocale (LC_ALL, "");
703 bindtextdomain (PACKAGE, LOCALEDIR);
704 textdomain (PACKAGE);
705
706 fast_startup = false;
707 simulate_login = false;
708 change_environment = true;
709
710 while ((optc = getopt_long (argc, argv, "c:flmps:", longopts, NULL)) != -1)
711 {
712 switch (optc)
713 {
714 case 'c':
715 command = optarg;
716 break;
717
718 case 'C':
719 command = optarg;
720 request_same_session = 1;
721 break;
722
723 case 'f':
724 fast_startup = true;
725 break;
726
727 case 'l':
728 simulate_login = true;
729 break;
730
731 case 'm':
732 case 'p':
733 change_environment = false;
734 break;
735
736 case 's':
737 shell = optarg;
738 break;
739
740 case 'u':
741 usage(0);
742
743 case 'v':
744 printf(UTIL_LINUX_VERSION);
745 exit(EXIT_SUCCESS);
746
747 default:
748 usage (EXIT_FAIL);
749 }
750 }
751
752 if (optind < argc && !strcmp (argv[optind], "-"))
753 {
754 simulate_login = true;
755 ++optind;
756 }
757 if (optind < argc)
758 new_user = argv[optind++];
759
760 pw = getpwnam (new_user);
761 if (! (pw && pw->pw_name && pw->pw_name[0] && pw->pw_dir && pw->pw_dir[0]
762 && pw->pw_passwd))
763 error (EXIT_FAIL, 0, _("user %s does not exist"), new_user);
764
765 /* Make a copy of the password information and point pw at the local
766 copy instead. Otherwise, some systems (e.g. Linux) would clobber
767 the static data through the getlogin call from log_su.
768 Also, make sure pw->pw_shell is a nonempty string.
769 It may be NULL when NEW_USER is a username that is retrieved via NIS (YP),
770 but that doesn't have a default shell listed. */
771 pw_copy = *pw;
772 pw = &pw_copy;
773 pw->pw_name = xstrdup (pw->pw_name);
774 pw->pw_passwd = xstrdup (pw->pw_passwd);
775 pw->pw_dir = xstrdup (pw->pw_dir);
776 pw->pw_shell = xstrdup (pw->pw_shell && pw->pw_shell[0]
777 ? pw->pw_shell
778 : DEFAULT_SHELL);
779 endpwent ();
780
781 if (!correct_password (pw))
782 {
783 log_su (pw, false);
784 sleep (getdef_num ("FAIL_DELAY", 1));
785 error (EXIT_FAIL, 0, _("incorrect password"));
786 }
787 else
788 {
789 log_su (pw, true);
790 }
791
792 if (request_same_session || !command || !pw->pw_uid)
793 same_session = 1;
794
795 if (!shell && !change_environment)
796 shell = getenv ("SHELL");
797 if (shell && getuid () != 0 && restricted_shell (pw->pw_shell))
798 {
799 /* The user being su'd to has a nonstandard shell, and so is
800 probably a uucp account or has restricted access. Don't
801 compromise the account by allowing access with a standard
802 shell. */
803 error (0, 0, _("using restricted shell %s"), pw->pw_shell);
804 shell = NULL;
805 }
806 shell = xstrdup (shell ? shell : pw->pw_shell);
807
808 init_groups (pw);
809
810 create_watching_parent ();
811 /* Now we're in the child. */
812
813 change_identity (pw);
814 if (!same_session)
815 setsid ();
816
817 /* Set environment after pam_open_session, which may put KRB5CCNAME
818 into the pam_env, etc. */
819
820 modify_environment (pw, shell);
821
822 if (simulate_login && chdir (pw->pw_dir) != 0)
823 error (0, errno, _("warning: cannot change directory to %s"), pw->pw_dir);
824
825 run_shell (shell, command, argv + optind, MAX (0, argc - optind));
826 }
827
828 // vim: sw=2 cinoptions=>4,n-2,{2,^-2,\:2,=2,g0,h2,p5,t0,+2,(0,u0,w1,m1