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