]> git.pld-linux.org Git - packages/util-linux.git/blob - util-linux-runuser.patch
fix wipefs not understanding -f option
[packages/util-linux.git] / util-linux-runuser.patch
1 diff --git a/configure.ac b/configure.ac
2 index 87e85fa..ead559c 100644
3 --- a/configure.ac
4 +++ b/configure.ac
5 @@ -1149,6 +1154,15 @@ UL_REQUIRES_HAVE([su], [security_pam_misc_h], [PAM header file])
6  AM_CONDITIONAL(BUILD_SU, test "x$build_su" = xyes)
7  
8  
9 +AC_ARG_ENABLE([runuser],
10 +  AS_HELP_STRING([--disable-runuser], [do not build runuser]),
11 +  [], enable_runuser=yes
12 +)
13 +UL_BUILD_INIT([runuser])
14 +UL_REQUIRES_HAVE([runuser], [security_pam_misc_h], [PAM header file])
15 +AM_CONDITIONAL(BUILD_RUNUSER, test "x$build_runuser" = xyes)
16 +
17 +
18  AC_ARG_ENABLE([schedutils],
19    AS_HELP_STRING([--disable-schedutils], [do not build chrt, ionice, teskset]),
20    [], enable_schedutils=yes
21 diff --git a/login-utils/Makemodule.am b/login-utils/Makemodule.am
22 index e10da46..755a361 100644
23 --- a/login-utils/Makemodule.am
24 +++ b/login-utils/Makemodule.am
25 @@ -83,6 +83,8 @@
26  dist_man_MANS += login-utils/su.1
27  su_SOURCES = \
28         login-utils/su.c \
29 +       login-utils/su-common.c \
30 +       login-utils/su-common.h \
31         login-utils/logindefs.c \
32         login-utils/logindefs.h
33  su_CFLAGS = $(SUID_CFLAGS) $(AM_CFLAGS)
34 @@ -91,6 +93,19 @@
35  endif
36  
37  
38 +if BUILD_RUNUSER
39 +bin_PROGRAMS += runuser
40 +dist_man_MANS += login-utils/runuser.1
41 +runuser_SOURCES = \
42 +       login-utils/runuser.c \
43 +       login-utils/su-common.c \
44 +       login-utils/su-common.h \
45 +       login-utils/logindefs.c \
46 +       login-utils/logindefs.h
47 +runuser_LDADD = $(LDADD) -lpam -lpam_misc
48 +endif
49 +
50 +
51  if BUILD_NEWGRP
52  usrbin_exec_PROGRAMS += newgrp
53  dist_man_MANS += login-utils/newgrp.1
54 diff --git a/login-utils/runuser.1 b/login-utils/runuser.1
55 new file mode 100644
56 index 0000000..66ad1c4
57 --- /dev/null
58 +++ b/login-utils/runuser.1
59 @@ -0,0 +1,230 @@
60 +.TH RUNUSER "1" "August 2012" "util-linux" "User Commands"
61 +.SH NAME
62 +runuser \- run a command with substitute user and group ID
63 +.SH SYNOPSIS
64 +.B runuser
65 +[options...] [\-] [user [args...]]
66 +.SH DESCRIPTION
67 +.B runuser
68 +allows to run commands with substitute user and group ID.
69 +The difference between the commands
70 +.B runuser
71 +and
72 +.B su
73 +is that
74 +.B runuser
75 +does not ask for password, because it may be executed by root user only.
76 +The command
77 +.B runuser
78 +does not have to be installed with suid permissions.
79 +.PP
80 +When called without arguments
81 +.B runuser
82 +defaults to running an interactive shell as
83 +.IR root .
84 +.PP
85 +For backward compatibility
86 +.B runuser
87 +defaults to not change the current directory and to only set the
88 +environment variables
89 +.B HOME
90 +and
91 +.B SHELL
92 +(plus
93 +.B USER
94 +and
95 +.B LOGNAME
96 +if the target
97 +.I user
98 +is not root).  It is recommended to always use the
99 +.B \-\-login
100 +option (instead it's shortcut
101 +.BR \- )
102 +to avoid side effects caused by mixing environments.
103 +.PP
104 +This version of
105 +.B runuser
106 +uses PAM for session management.
107 +.SH OPTIONS
108 +.TP
109 +\fB\-c\fR \fIcommand\fR, \fB\-\-command\fR=\fIcommand\fR
110 +Pass
111 +.I command
112 +to the shell with the
113 +.B \-c
114 +option.
115 +.TP
116 +\fB\-\-session\-command\fR=\fIcommand\fR
117 +Same as
118 +.B \-c
119 +but do not create a new session (discouraged).
120 +.TP
121 +\fB\-f\fR, \fB\-\-fast\fR
122 +Pass
123 +.B \-f
124 +to the shell which may or may not be useful depending on the
125 +shell.
126 +.TP
127 +\fB\-g\fR, \fB\-\-group\fR=\fIgroup\fR\fR
128 +specify the primary group, this option is allowed for root user only
129 +.TP
130 +\fB\-G\fR, \fB\-\-supp-group\fR=\fIgroup\fR\fR
131 +specify a supplemental group, this option is allowed for root user only
132 +.TP
133 +\fB\-\fR, \fB\-l\fR, \fB\-\-login\fR
134 +Starts the shell as login shell with an environment similar to a real
135 +login:
136 +.RS 10
137 +.TP
138 +o
139 +clears all environment variables except for
140 +.B TERM
141 +.TP
142 +o
143 +initializes the environment variables
144 +.BR HOME ,
145 +.BR SHELL ,
146 +.BR USER ,
147 +.BR LOGNAME ,
148 +.B PATH
149 +.TP
150 +o
151 +changes to the target user's home directory
152 +.TP
153 +o
154 +sets argv[0] of the shell to
155 +.RB ' \- '
156 +in order to make the shell a login shell
157 +.RE
158 +.TP
159 +\fB\-m\fR, \fB\-p\fR, \fB\-\-preserve-environment\fR
160 +Preserves the whole environment, ie does not set
161 +.BR HOME ,
162 +.BR SHELL ,
163 +.B USER
164 +nor
165 +.BR LOGNAME .
166 +.TP
167 +\fB\-s\fR \fISHELL\fR, \fB\-\-shell\fR=\fISHELL\fR
168 +Runs the specified shell instead of the default.  The shell to run is
169 +selected according to the following rules in order:
170 +.RS 10
171 +.TP
172 +o
173 +the shell specified with
174 +.B \-\-shell
175 +.TP
176 +o
177 +The shell specified in the environment variable
178 +.B SHELL
179 +if the
180 +.B \-\-preserve-environment
181 +option is used.
182 +.TP
183 +o
184 +the shell listed in the passwd entry of the target user
185 +.TP
186 +o
187 +/bin/sh
188 +.RE
189 +.IP
190 +If the target user has a restricted shell (i.e. not listed in
191 +/etc/shells) the
192 +.B \-\-shell
193 +option and the
194 +.B SHELL
195 +environment variables are ignored unless the calling user is root.
196 +.TP
197 +\fB\-\-help\fR
198 +Display help text and exit.
199 +.TP
200 +\fB\-\-version\fR
201 +Display version information and exit.
202 +.SH CONFIG FILES
203 +.B runuser
204 +reads the
205 +.I /etc/default/runuser
206 +and
207 +.I /etc/login.defs
208 +configuration files.  The following configuration items are relevant
209 +for
210 +.BR runuser :
211 +.PP
212 +.B ENV_PATH
213 +(string)
214 +.RS 4
215 +Defines the PATH environment variable for a regular user.  The
216 +default value is
217 +.IR /usr/local/bin:\:/bin:\:/usr/bin .
218 +.RE
219 +.PP
220 +.B ENV_ROOTPATH
221 +(string)
222 +.br
223 +.B ENV_SUPATH
224 +(string)
225 +.RS 4
226 +Defines the PATH environment variable for root. The default value is
227 +.IR /usr/local/sbin:\:/usr/local/bin:\:/sbin:\:/bin:\:/usr/sbin:\:/usr/bin .
228 +.RE
229 +.PP
230 +.B ALWAYS_SET_PATH
231 +(boolean)
232 +.RS 4
233 +If set to
234 +.I yes
235 +and \-\-login and \-\-preserve\-environment were not specified
236 +.B runuser
237 +initializes
238 +.BR PATH .
239 +.RE
240 +.SH EXIT STATUS
241 +.B runuser
242 +normally returns the exit status of the command it executed.  If the
243 +command was killed by a signal,
244 +.B runuser
245 +returns the number of the signal plus 128.
246 +.PP
247 +Exit status generated by
248 +.B runuser
249 +itself:
250 +.RS 10
251 +.TP
252 +1
253 +Generic error before executing the requested command
254 +.TP
255 +126
256 +The requested command could not be executed
257 +.TP
258 +127
259 +The requested command could was not found
260 +.RE
261 +.SH FILES
262 +.PD 0
263 +.TP 17
264 +/etc/pam.d/runuser
265 +default PAM configuration file
266 +.TP
267 +/etc/pam.d/runuser-l
268 +PAM configuration file if \-\-login is specified
269 +.TP
270 +/etc/default/runuser
271 +runuser specific logindef config file
272 +.TP
273 +/etc/login.defs
274 +global logindef config file
275 +.PD 1
276 +.SH "SEE ALSO"
277 +.BR pam (8),
278 +.BR shells (5),
279 +.BR login.defs (5),
280 +.BR su (1)
281 +.SH AUTHOR
282 +Derived from coreutils' su which was based on an implemenation from
283 +David MacKenzie and Fedora runuser command from Dan Walsh.
284 +.SH AVAILABILITY
285 +The runuser command is part of the util-linux package and is
286 +available from
287 +.UR ftp://\:ftp.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
288 +Linux Kernel Archive
289 +.UE .
290 diff --git a/login-utils/runuser.c b/login-utils/runuser.c
291 new file mode 100644
292 index 0000000..d761a14
293 --- /dev/null
294 +++ b/login-utils/runuser.c
295 @@ -0,0 +1,7 @@
296 +
297 +#include "su-common.h"
298 +
299 +int main(int argv, char **argc)
300 +{
301 +       return su_main(argv, argc, RUNUSER_MODE);
302 +}
303 diff --git a/login-utils/su-common.c b/login-utils/su-common.c
304 new file mode 100644
305 index 0000000..d1fecd7
306 --- /dev/null
307 +++ b/login-utils/su-common.c
308 @@ -0,0 +1,918 @@
309 +/* su for Linux.  Run a shell with substitute user and group IDs.
310 +   Copyright (C) 1992-2006 Free Software Foundation, Inc.
311 +   Copyright (C) 2012 SUSE Linux Products GmbH, Nuernberg
312 +
313 +   This program is free software; you can redistribute it and/or modify
314 +   it under the terms of the GNU General Public License as published by
315 +   the Free Software Foundation; either version 2, or (at your option)
316 +   any later version.
317 +
318 +   This program is distributed in the hope that it will be useful,
319 +   but WITHOUT ANY WARRANTY; without even the implied warranty of
320 +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
321 +   GNU General Public License for more details.
322 +
323 +   You should have received a copy of the GNU General Public License
324 +   along with this program; if not, write to the Free Software Foundation,
325 +   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
326 +
327 +/* Run a shell with the real and effective UID and GID and groups
328 +   of USER, default `root'.
329 +
330 +   The shell run is taken from USER's password entry, /bin/sh if
331 +   none is specified there.  If the account has a password, su
332 +   prompts for a password unless run by a user with real UID 0.
333 +
334 +   Does not change the current directory.
335 +   Sets `HOME' and `SHELL' from the password entry for USER, and if
336 +   USER is not root, sets `USER' and `LOGNAME' to USER.
337 +   The subshell is not a login shell.
338 +
339 +   If one or more ARGs are given, they are passed as additional
340 +   arguments to the subshell.
341 +
342 +   Does not handle /bin/sh or other shells specially
343 +   (setting argv[0] to "-su", passing -c only to certain shells, etc.).
344 +   I don't see the point in doing that, and it's ugly.
345 +
346 +   Based on an implemenation by David MacKenzie <djm@gnu.ai.mit.edu>.  */
347 +
348 +enum
349 +{
350 +  EXIT_CANNOT_INVOKE = 126,
351 +  EXIT_ENOENT = 127
352 +};
353 +
354 +#include <config.h>
355 +#include <stdio.h>
356 +#include <getopt.h>
357 +#include <sys/types.h>
358 +#include <pwd.h>
359 +#include <grp.h>
360 +#include <security/pam_appl.h>
361 +#include <security/pam_misc.h>
362 +#include <signal.h>
363 +#include <sys/wait.h>
364 +#include <syslog.h>
365 +
366 +#include "err.h"
367 +
368 +#include <stdbool.h>
369 +#include "c.h"
370 +#include "xalloc.h"
371 +#include "nls.h"
372 +#include "pathnames.h"
373 +#include "env.h"
374 +
375 +/* name of the pam configuration files. separate configs for su and su -  */
376 +#define PAM_SRVNAME_SU "su"
377 +#define PAM_SRVNAME_SU_L "su-l"
378 +
379 +#define PAM_SRVNAME_RUNUSER "runuser"
380 +#define PAM_SRVNAME_RUNUSER_L "runuser-l"
381 +
382 +#define _PATH_LOGINDEFS_SU     "/etc/defaults/su"
383 +#define _PATH_LOGINDEFS_RUNUSER "/etc/defaults/runuser"
384 +
385 +#define is_pam_failure(_rc)    ((_rc) != PAM_SUCCESS)
386 +
387 +#include "logindefs.h"
388 +#include "su-common.h"
389 +
390 +/* The shell to run if none is given in the user's passwd entry.  */
391 +#define DEFAULT_SHELL "/bin/sh"
392 +
393 +/* The user to become if none is specified.  */
394 +#define DEFAULT_USER "root"
395 +
396 +#ifndef HAVE_ENVIRON_DECL
397 +extern char **environ;
398 +#endif
399 +
400 +static void run_shell (char const *, char const *, char **, size_t)
401 +     __attribute__ ((__noreturn__));
402 +
403 +/* If true, pass the `-f' option to the subshell.  */
404 +static bool fast_startup;
405 +
406 +/* If true, simulate a login instead of just starting a shell.  */
407 +static bool simulate_login;
408 +
409 +/* If true, change some environment vars to indicate the user su'd to.  */
410 +static bool change_environment;
411 +
412 +/* If true, then don't call setsid() with a command. */
413 +static int same_session = 0;
414 +
415 +/* SU_MODE_{RUNUSER,SU} */
416 +static int su_mode;
417 +
418 +static bool _pam_session_opened;
419 +static bool _pam_cred_established;
420 +static sig_atomic_t volatile caught_signal = false;
421 +static pam_handle_t *pamh = NULL;
422 +
423 +static int restricted = 1;     /* zero for root user */
424 +
425 +static struct option const longopts[] =
426 +{
427 +  {"command", required_argument, NULL, 'c'},
428 +  {"session-command", required_argument, NULL, 'C'},
429 +  {"fast", no_argument, NULL, 'f'},
430 +  {"login", no_argument, NULL, 'l'},
431 +  {"preserve-environment", no_argument, NULL, 'p'},
432 +  {"shell", required_argument, NULL, 's'},
433 +  {"group", required_argument, NULL, 'g'},
434 +  {"supp-group", required_argument, NULL, 'G'},
435 +  {"help", no_argument, 0, 'h'},
436 +  {"version", no_argument, 0, 'V'},
437 +  {NULL, 0, NULL, 0}
438 +};
439 +
440 +/* Log the fact that someone has run su to the user given by PW;
441 +   if SUCCESSFUL is true, they gave the correct password, etc.  */
442 +
443 +static void
444 +log_su (struct passwd const *pw, bool successful)
445 +{
446 +  const char *new_user, *old_user, *tty;
447 +
448 +  new_user = pw->pw_name;
449 +  /* The utmp entry (via getlogin) is probably the best way to identify
450 +     the user, especially if someone su's from a su-shell.  */
451 +  old_user = getlogin ();
452 +  if (!old_user)
453 +    {
454 +      /* getlogin can fail -- usually due to lack of utmp entry.
455 +        Resort to getpwuid.  */
456 +      struct passwd *pwd = getpwuid (getuid ());
457 +      old_user = (pwd ? pwd->pw_name : "");
458 +    }
459 +  tty = ttyname (STDERR_FILENO);
460 +  if (!tty)
461 +    tty = "none";
462 +
463 +  openlog (program_invocation_short_name, 0 , LOG_AUTH);
464 +  syslog (LOG_NOTICE, "%s(to %s) %s on %s",
465 +         successful ? "" :
466 +         su_mode == RUNUSER_MODE ? "FAILED RUNUSER " : "FAILED SU ",
467 +         new_user, old_user, tty);
468 +  closelog ();
469 +}
470 +
471 +static struct pam_conv conv =
472 +{
473 +  misc_conv,
474 +  NULL
475 +};
476 +
477 +static void
478 +cleanup_pam (int retcode)
479 +{
480 +  int saved_errno = errno;
481 +
482 +  if (_pam_session_opened)
483 +    pam_close_session (pamh, 0);
484 +
485 +  if (_pam_cred_established)
486 +    pam_setcred (pamh, PAM_DELETE_CRED | PAM_SILENT);
487 +
488 +  pam_end(pamh, retcode);
489 +
490 +  errno = saved_errno;
491 +}
492 +
493 +/* Signal handler for parent process.  */
494 +static void
495 +su_catch_sig (int sig __attribute__((__unused__)))
496 +{
497 +  caught_signal = true;
498 +}
499 +
500 +/* Export env variables declared by PAM modules.  */
501 +static void
502 +export_pamenv (void)
503 +{
504 +  char **env;
505 +
506 +  /* This is a copy but don't care to free as we exec later anyways.  */
507 +  env = pam_getenvlist (pamh);
508 +  while (env && *env)
509 +    {
510 +      if (putenv (*env) != 0)
511 +       err (EXIT_FAILURE, NULL);
512 +      env++;
513 +    }
514 +}
515 +
516 +static void
517 +create_watching_parent (void)
518 +{
519 +  pid_t child;
520 +  sigset_t ourset;
521 +  int status = 0;
522 +  int retval;
523 +
524 +  retval = pam_open_session (pamh, 0);
525 +  if (is_pam_failure(retval))
526 +    {
527 +      cleanup_pam (retval);
528 +      errx (EXIT_FAILURE, _("cannot not open session: %s"),
529 +            pam_strerror (pamh, retval));
530 +    }
531 +  else
532 +    _pam_session_opened = 1;
533 +
534 +  child = fork ();
535 +  if (child == (pid_t) -1)
536 +    {
537 +      cleanup_pam (PAM_ABORT);
538 +      err (EXIT_FAILURE, _("cannot create child process"));
539 +    }
540 +
541 +  /* the child proceeds to run the shell */
542 +  if (child == 0)
543 +    return;
544 +
545 +  /* In the parent watch the child.  */
546 +
547 +  /* su without pam support does not have a helper that keeps
548 +     sitting on any directory so let's go to /.  */
549 +  if (chdir ("/") != 0)
550 +    warn (_("cannot change directory to %s"), "/");
551 +
552 +  sigfillset (&ourset);
553 +  if (sigprocmask (SIG_BLOCK, &ourset, NULL))
554 +    {
555 +      warn (_("cannot block signals"));
556 +      caught_signal = true;
557 +    }
558 +  if (!caught_signal)
559 +    {
560 +      struct sigaction action;
561 +      action.sa_handler = su_catch_sig;
562 +      sigemptyset (&action.sa_mask);
563 +      action.sa_flags = 0;
564 +      sigemptyset (&ourset);
565 +    if (!same_session)
566 +      {
567 +        if (sigaddset(&ourset, SIGINT) || sigaddset(&ourset, SIGQUIT))
568 +          {
569 +            warn (_("cannot set signal handler"));
570 +            caught_signal = true;
571 +          }
572 +      }
573 +    if (!caught_signal && (sigaddset(&ourset, SIGTERM)
574 +                    || sigaddset(&ourset, SIGALRM)
575 +                    || sigaction(SIGTERM, &action, NULL)
576 +                    || sigprocmask(SIG_UNBLOCK, &ourset, NULL))) {
577 +         warn (_("cannot set signal handler"));
578 +         caught_signal = true;
579 +       }
580 +    if (!caught_signal && !same_session && (sigaction(SIGINT, &action, NULL)
581 +                                     || sigaction(SIGQUIT, &action, NULL)))
582 +      {
583 +        warn (_("cannot set signal handler"));
584 +        caught_signal = true;
585 +      }
586 +    }
587 +  if (!caught_signal)
588 +    {
589 +      pid_t pid;
590 +      for (;;)
591 +       {
592 +         pid = waitpid (child, &status, WUNTRACED);
593 +
594 +         if (pid != (pid_t)-1 && WIFSTOPPED (status))
595 +           {
596 +             kill (getpid (), SIGSTOP);
597 +             /* once we get here, we must have resumed */
598 +             kill (pid, SIGCONT);
599 +           }
600 +         else
601 +           break;
602 +       }
603 +      if (pid != (pid_t)-1)
604 +       if (WIFSIGNALED (status))
605 +         status = WTERMSIG (status) + 128;
606 +       else
607 +         status = WEXITSTATUS (status);
608 +      else
609 +       status = 1;
610 +    }
611 +  else
612 +    status = 1;
613 +
614 +  if (caught_signal)
615 +    {
616 +      fprintf (stderr, _("\nSession terminated, killing shell..."));
617 +      kill (child, SIGTERM);
618 +    }
619 +
620 +  cleanup_pam (PAM_SUCCESS);
621 +
622 +  if (caught_signal)
623 +    {
624 +      sleep (2);
625 +      kill (child, SIGKILL);
626 +      fprintf (stderr, _(" ...killed.\n"));
627 +    }
628 +  exit (status);
629 +}
630 +
631 +static void
632 +authenticate (const struct passwd *pw)
633 +{
634 +  const struct passwd *lpw;
635 +  const char *cp, *srvname = NULL;
636 +  int retval;
637 +
638 +  switch (su_mode) {
639 +  case SU_MODE:
640 +    srvname = simulate_login ? PAM_SRVNAME_SU_L : PAM_SRVNAME_SU;
641 +    break;
642 +  case RUNUSER_MODE:
643 +    srvname = simulate_login ? PAM_SRVNAME_RUNUSER_L : PAM_SRVNAME_RUNUSER;
644 +    break;
645 +  }
646 +
647 +  retval = pam_start (srvname, pw->pw_name, &conv, &pamh);
648 +  if (is_pam_failure(retval))
649 +    goto done;
650 +
651 +  if (isatty (0) && (cp = ttyname (0)) != NULL)
652 +    {
653 +      const char *tty;
654 +
655 +      if (strncmp (cp, "/dev/", 5) == 0)
656 +       tty = cp + 5;
657 +      else
658 +       tty = cp;
659 +      retval = pam_set_item (pamh, PAM_TTY, tty);
660 +      if (is_pam_failure(retval))
661 +       goto done;
662 +    }
663 +
664 +  lpw = getpwuid (getuid ());
665 +  if (lpw && lpw->pw_name)
666 +    {
667 +      retval = pam_set_item (pamh, PAM_RUSER, (const void *) lpw->pw_name);
668 +      if (is_pam_failure(retval))
669 +       goto done;
670 +    }
671 +
672 +  if (su_mode == RUNUSER_MODE)
673 +    {
674 +      /*
675 +       * This is the only difference between runuser(1) and su(1). The command
676 +       * runuser(1) does not required authentication, because user is root.
677 +       */
678 +      if (restricted)
679 +       errx(EXIT_FAILURE, _("may not be used by non-root users"));
680 +      return;
681 +    }
682 +
683 +  retval = pam_authenticate (pamh, 0);
684 +  if (is_pam_failure(retval))
685 +    goto done;
686 +
687 +  retval = pam_acct_mgmt (pamh, 0);
688 +  if (retval == PAM_NEW_AUTHTOK_REQD)
689 +    {
690 +      /* Password has expired.  Offer option to change it.  */
691 +      retval = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
692 +    }
693 +
694 +done:
695 +
696 +  log_su (pw, !is_pam_failure(retval));
697 +
698 +  if (is_pam_failure(retval))
699 +    {
700 +      const char *msg = pam_strerror(pamh, retval);
701 +      pam_end(pamh, retval);
702 +      sleep (getlogindefs_num ("FAIL_DELAY", 1));
703 +      errx (EXIT_FAILURE, "%s", msg?msg:_("incorrect password"));
704 +    }
705 +}
706 +
707 +/* Add or clear /sbin and /usr/sbin for the su command
708 +   used without `-'.  */
709 +
710 +/* Set if /sbin is found in path.  */
711 +#define SBIN_MASK      0x01
712 +/* Set if /usr/sbin is found in path.  */
713 +#define USBIN_MASK     0x02
714 +
715 +static char *
716 +addsbin (const char *const path)
717 +{
718 +  unsigned char smask = 0;
719 +  char *ptr, *tmp, *cur, *ret = NULL;
720 +  size_t len;
721 +
722 +  if (!path || *path == 0)
723 +    return NULL;
724 +
725 +  tmp = xstrdup (path);
726 +  cur = tmp;
727 +  for (ptr = strsep (&cur, ":"); ptr != NULL; ptr = strsep (&cur, ":"))
728 +    {
729 +      if (!strcmp (ptr, "/sbin"))
730 +       smask |= SBIN_MASK;
731 +      if (!strcmp (ptr, "/usr/sbin"))
732 +       smask |= USBIN_MASK;
733 +    }
734 +
735 +  if ((smask & (USBIN_MASK|SBIN_MASK)) == (USBIN_MASK|SBIN_MASK))
736 +    {
737 +      free (tmp);
738 +      return NULL;
739 +    }
740 +
741 +  len = strlen (path);
742 +  if (!(smask & USBIN_MASK))
743 +    len += strlen ("/usr/sbin:");
744 +
745 +  if (!(smask & SBIN_MASK))
746 +    len += strlen (":/sbin");
747 +
748 +  ret = xmalloc (len + 1);
749 +  strcpy (tmp, path);
750 +
751 +  *ret = 0;
752 +  cur = tmp;
753 +  for (ptr = strsep (&cur, ":"); ptr; ptr = strsep (&cur, ":"))
754 +    {
755 +      if (!strcmp (ptr, "."))
756 +       continue;
757 +      if (*ret)
758 +       strcat (ret, ":");
759 +      if (!(smask & USBIN_MASK) && !strcmp (ptr, "/bin"))
760 +       {
761 +         strcat (ret, "/usr/sbin:");
762 +         strcat (ret, ptr);
763 +         smask |= USBIN_MASK;
764 +         continue;
765 +       }
766 +      if (!(smask & SBIN_MASK) && !strcmp (ptr, "/usr/bin"))
767 +       {
768 +         strcat (ret, ptr);
769 +         strcat (ret, ":/sbin");
770 +         smask |= SBIN_MASK;
771 +         continue;
772 +       }
773 +      strcat (ret, ptr);
774 +    }
775 +  free (tmp);
776 +
777 +  if (!(smask & USBIN_MASK))
778 +    strcat (ret, ":/usr/sbin");
779 +
780 +  if (!(smask & SBIN_MASK))
781 +    strcat (ret, ":/sbin");
782 +
783 +  return ret;
784 +}
785 +
786 +static char *
787 +clearsbin (const char *const path)
788 +{
789 +  char *ptr, *tmp, *cur, *ret = NULL;
790 +
791 +  if (!path || *path == 0)
792 +    return NULL;
793 +
794 +  tmp = xstrdup (path);
795 +
796 +  ret = xmalloc (strlen (path) + 1);
797 +  *ret = 0;
798 +  cur = tmp;
799 +  for (ptr = strsep (&cur, ":"); ptr; ptr = strsep (&cur, ":"))
800 +    {
801 +      if (!strcmp (ptr, "/sbin"))
802 +       continue;
803 +      if (!strcmp (ptr, "/usr/sbin"))
804 +       continue;
805 +      if (!strcmp (ptr, "/usr/local/sbin"))
806 +       continue;
807 +      if (*ret)
808 +       strcat (ret, ":");
809 +      strcat (ret, ptr);
810 +    }
811 +  free (tmp);
812 +
813 +  return ret;
814 +}
815 +
816 +static void
817 +set_path(const struct passwd* pw)
818 +{
819 +  int r;
820 +  if (pw->pw_uid)
821 +    r = logindefs_setenv("PATH", "ENV_PATH", _PATH_DEFPATH);
822 +
823 +  else if ((r = logindefs_setenv("PATH", "ENV_ROOTPATH", NULL)) != 0)
824 +    r = logindefs_setenv("PATH", "ENV_SUPATH", _PATH_DEFPATH_ROOT);
825 +
826 +  if (r != 0)
827 +    err (EXIT_FAILURE,  _("failed to set PATH"));
828 +}
829 +
830 +/* Update `environ' for the new shell based on PW, with SHELL being
831 +   the value for the SHELL environment variable.  */
832 +
833 +static void
834 +modify_environment (const struct passwd *pw, const char *shell)
835 +{
836 +  if (simulate_login)
837 +    {
838 +      /* Leave TERM unchanged.  Set HOME, SHELL, USER, LOGNAME, PATH.
839 +         Unset all other environment variables.  */
840 +      char const *term = getenv ("TERM");
841 +      if (term)
842 +       term = xstrdup (term);
843 +      environ = xmalloc ((6 + !!term) * sizeof (char *));
844 +      environ[0] = NULL;
845 +      if (term)
846 +       xsetenv ("TERM", term, 1);
847 +      xsetenv ("HOME", pw->pw_dir, 1);
848 +      xsetenv ("SHELL", shell, 1);
849 +      xsetenv ("USER", pw->pw_name, 1);
850 +      xsetenv ("LOGNAME", pw->pw_name, 1);
851 +      set_path(pw);
852 +    }
853 +  else
854 +    {
855 +      /* Set HOME, SHELL, and if not becoming a super-user,
856 +        USER and LOGNAME.  */
857 +      if (change_environment)
858 +        {
859 +          xsetenv ("HOME", pw->pw_dir, 1);
860 +          xsetenv ("SHELL", shell, 1);
861 +         if (getlogindefs_bool ("ALWAYS_SET_PATH", 0))
862 +           set_path(pw);
863 +         else
864 +           {
865 +             char const *path = getenv ("PATH");
866 +             char *new = NULL;
867 +
868 +             if (pw->pw_uid)
869 +               new = clearsbin (path);
870 +             else
871 +               new = addsbin (path);
872 +
873 +             if (new)
874 +               {
875 +                 xsetenv ("PATH", new, 1);
876 +                 free (new);
877 +               }
878 +           }
879 +          if (pw->pw_uid)
880 +            {
881 +              xsetenv ("USER", pw->pw_name, 1);
882 +              xsetenv ("LOGNAME", pw->pw_name, 1);
883 +            }
884 +        }
885 +    }
886 +
887 +  export_pamenv ();
888 +}
889 +
890 +/* Become the user and group(s) specified by PW.  */
891 +
892 +static void
893 +init_groups (const struct passwd *pw, gid_t *groups, int num_groups)
894 +{
895 +  int retval;
896 +
897 +  errno = 0;
898 +
899 +  if (num_groups)
900 +    retval = setgroups (num_groups, groups);
901 +  else
902 +    retval = initgroups (pw->pw_name, pw->pw_gid);
903 +
904 +  if (retval == -1)
905 +    {
906 +      cleanup_pam (PAM_ABORT);
907 +      err (EXIT_FAILURE, _("cannot set groups"));
908 +    }
909 +  endgrent ();
910 +
911 +  retval = pam_setcred (pamh, PAM_ESTABLISH_CRED);
912 +  if (is_pam_failure(retval))
913 +    errx (EXIT_FAILURE, "%s", pam_strerror (pamh, retval));
914 +  else
915 +    _pam_cred_established = 1;
916 +}
917 +
918 +static void
919 +change_identity (const struct passwd *pw)
920 +{
921 +  if (setgid (pw->pw_gid))
922 +    err (EXIT_FAILURE,  _("cannot set group id"));
923 +  if (setuid (pw->pw_uid))
924 +    err (EXIT_FAILURE,  _("cannot set user id"));
925 +}
926 +
927 +/* Run SHELL, or DEFAULT_SHELL if SHELL is empty.
928 +   If COMMAND is nonzero, pass it to the shell with the -c option.
929 +   Pass ADDITIONAL_ARGS to the shell as more arguments; there
930 +   are N_ADDITIONAL_ARGS extra arguments.  */
931 +
932 +static void
933 +run_shell (char const *shell, char const *command, char **additional_args,
934 +          size_t n_additional_args)
935 +{
936 +  size_t n_args = 1 + fast_startup + 2 * !!command + n_additional_args + 1;
937 +  char const **args = xcalloc (n_args, sizeof *args);
938 +  size_t argno = 1;
939 +
940 +  if (simulate_login)
941 +    {
942 +      char *arg0;
943 +      char *shell_basename;
944 +
945 +      shell_basename = basename (shell);
946 +      arg0 = xmalloc (strlen (shell_basename) + 2);
947 +      arg0[0] = '-';
948 +      strcpy (arg0 + 1, shell_basename);
949 +      args[0] = arg0;
950 +    }
951 +  else
952 +    args[0] = basename (shell);
953 +  if (fast_startup)
954 +    args[argno++] = "-f";
955 +  if (command)
956 +    {
957 +      args[argno++] = "-c";
958 +      args[argno++] = command;
959 +    }
960 +  memcpy (args + argno, additional_args, n_additional_args * sizeof *args);
961 +  args[argno + n_additional_args] = NULL;
962 +  execv (shell, (char **) args);
963 +
964 +  {
965 +    int exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
966 +    warn ("%s", shell);
967 +    exit (exit_status);
968 +  }
969 +}
970 +
971 +/* Return true if SHELL is a restricted shell (one not returned by
972 +   getusershell), else false, meaning it is a standard shell.  */
973 +
974 +static bool
975 +restricted_shell (const char *shell)
976 +{
977 +  char *line;
978 +
979 +  setusershell ();
980 +  while ((line = getusershell ()) != NULL)
981 +    {
982 +      if (*line != '#' && !strcmp (line, shell))
983 +       {
984 +         endusershell ();
985 +         return false;
986 +       }
987 +    }
988 +  endusershell ();
989 +  return true;
990 +}
991 +
992 +static void __attribute__((__noreturn__))
993 +usage (int status)
994 +{
995 +  if (status != EXIT_SUCCESS)
996 +    fprintf (stderr, _("Try `%s --help' for more information.\n"),
997 +            program_invocation_short_name);
998 +  else
999 +    {
1000 +      fputs(USAGE_HEADER, stdout);
1001 +      printf (_(" %s [options] [-] [USER [arg]...]\n"), program_invocation_short_name);
1002 +      fputs (_("\n\
1003 + Change the effective user id and group id to that of USER.\n\
1004 + A mere - implies -l.   If USER not given, assume root.\n"), stdout);
1005 +      fputs(USAGE_OPTIONS, stdout);
1006 +      fputs (_("\
1007 + -, -l, --login               make the shell a login shell\n\
1008 + -c, --command <command>      pass a single command to the shell with -c\n\
1009 + --session-command <command>  pass a single command to the shell with -c\n\
1010 +                              and do not create a new session\n\
1011 + -g --group=group             specify the primary group\n\
1012 + -G --supp-group=group        specify a supplemental group\n\
1013 + -f, --fast                   pass -f to the shell (for csh or tcsh)\n\
1014 + -m, --preserve-environment   do not reset environment variables\n\
1015 + -p                           same as -m\n\
1016 + -s, --shell <shell>          run shell if /etc/shells allows it\n\
1017 +"), stdout);
1018 +
1019 +      fputs(USAGE_SEPARATOR, stdout);
1020 +      fputs(USAGE_HELP, stdout);
1021 +      fputs(USAGE_VERSION, stdout);
1022 +      printf(USAGE_MAN_TAIL("su(1)"));
1023 +    }
1024 +  exit (status);
1025 +}
1026 +
1027 +static
1028 +void load_config(void)
1029 +{
1030 +  switch (su_mode) {
1031 +  case SU_MODE:
1032 +    logindefs_load_file(_PATH_LOGINDEFS_SU);
1033 +    break;
1034 +  case RUNUSER_MODE:
1035 +    logindefs_load_file(_PATH_LOGINDEFS_RUNUSER);
1036 +    break;
1037 +  }
1038 +
1039 +  logindefs_load_file(_PATH_LOGINDEFS);
1040 +}
1041 +
1042 +/*
1043 + * Returns 1 if the current user is not root
1044 + */
1045 +static int
1046 +evaluate_uid(void)
1047 +{
1048 +  uid_t ruid = getuid();
1049 +  uid_t euid = geteuid();
1050 +
1051 +  /* if we're really root and aren't running setuid */
1052 +  return (uid_t) 0 == ruid && ruid == euid ? 0 : 1;
1053 +}
1054 +
1055 +int
1056 +su_main (int argc, char **argv, int mode)
1057 +{
1058 +  int optc;
1059 +  const char *new_user = DEFAULT_USER;
1060 +  char *command = NULL;
1061 +  int request_same_session = 0;
1062 +  char *shell = NULL;
1063 +  struct passwd *pw;
1064 +  struct passwd pw_copy;
1065 +  struct group *gr;
1066 +  gid_t groups[NGROUPS_MAX];
1067 +  int num_supp_groups = 0;
1068 +  int use_gid = 0;
1069 +
1070 +  setlocale (LC_ALL, "");
1071 +  bindtextdomain (PACKAGE, LOCALEDIR);
1072 +  textdomain (PACKAGE);
1073 +
1074 +  su_mode = mode;
1075 +  fast_startup = false;
1076 +  simulate_login = false;
1077 +  change_environment = true;
1078 +
1079 +  while ((optc = getopt_long (argc, argv, "c:fg:G:lmps:hV", longopts, NULL)) != -1)
1080 +    {
1081 +      switch (optc)
1082 +       {
1083 +       case 'c':
1084 +         command = optarg;
1085 +         break;
1086 +
1087 +        case 'C':
1088 +          command = optarg;
1089 +          request_same_session = 1;
1090 +          break;
1091 +
1092 +       case 'f':
1093 +         fast_startup = true;
1094 +         break;
1095 +
1096 +       case 'g':
1097 +         gr = getgrnam(optarg);
1098 +         if (!gr)
1099 +           errx(EXIT_FAILURE, _("group %s does not exist"), optarg);
1100 +         use_gid = 1;
1101 +         groups[0] = gr->gr_gid;
1102 +         break;
1103 +
1104 +       case 'G':
1105 +         num_supp_groups++;
1106 +         if (num_supp_groups >= NGROUPS_MAX)
1107 +            errx(EXIT_FAILURE,
1108 +                 _("can't specify more than %d supplemental groups"),
1109 +                 NGROUPS_MAX - 1);
1110 +         gr = getgrnam(optarg);
1111 +         if (!gr)
1112 +           errx(EXIT_FAILURE, _("group %s does not exist"), optarg);
1113 +         groups[num_supp_groups] = gr->gr_gid;
1114 +         break;
1115 +
1116 +       case 'l':
1117 +         simulate_login = true;
1118 +         break;
1119 +
1120 +       case 'm':
1121 +       case 'p':
1122 +         change_environment = false;
1123 +         break;
1124 +
1125 +       case 's':
1126 +         shell = optarg;
1127 +         break;
1128 +
1129 +       case 'h':
1130 +         usage(0);
1131 +
1132 +       case 'V':
1133 +         printf(UTIL_LINUX_VERSION);
1134 +         exit(EXIT_SUCCESS);
1135 +
1136 +       default:
1137 +         usage (EXIT_FAILURE);
1138 +       }
1139 +    }
1140 +
1141 +  restricted = evaluate_uid ();
1142 +
1143 +  if (optind < argc && !strcmp (argv[optind], "-"))
1144 +    {
1145 +      simulate_login = true;
1146 +      ++optind;
1147 +    }
1148 +  if (optind < argc)
1149 +    new_user = argv[optind++];
1150 +
1151 +  if ((num_supp_groups || use_gid) && restricted)
1152 +    errx(EXIT_FAILURE, _("only root can specify alternative groups"));
1153 +
1154 +  logindefs_load_defaults = load_config;
1155 +
1156 +  pw = getpwnam (new_user);
1157 +  if (! (pw && pw->pw_name && pw->pw_name[0] && pw->pw_dir && pw->pw_dir[0]
1158 +        && pw->pw_passwd))
1159 +    errx (EXIT_FAILURE, _("user %s does not exist"), new_user);
1160 +
1161 +  /* Make a copy of the password information and point pw at the local
1162 +     copy instead.  Otherwise, some systems (e.g. Linux) would clobber
1163 +     the static data through the getlogin call from log_su.
1164 +     Also, make sure pw->pw_shell is a nonempty string.
1165 +     It may be NULL when NEW_USER is a username that is retrieved via NIS (YP),
1166 +     but that doesn't have a default shell listed.  */
1167 +  pw_copy = *pw;
1168 +  pw = &pw_copy;
1169 +  pw->pw_name = xstrdup (pw->pw_name);
1170 +  pw->pw_passwd = xstrdup (pw->pw_passwd);
1171 +  pw->pw_dir = xstrdup (pw->pw_dir);
1172 +  pw->pw_shell = xstrdup (pw->pw_shell && pw->pw_shell[0]
1173 +                         ? pw->pw_shell
1174 +                         : DEFAULT_SHELL);
1175 +  endpwent ();
1176 +
1177 +  if (num_supp_groups && !use_gid)
1178 +  {
1179 +    pw->pw_gid = groups[1];
1180 +    memmove (groups, groups + 1, sizeof(gid_t) * num_supp_groups);
1181 +  }
1182 +  else if (use_gid)
1183 +  {
1184 +    pw->pw_gid = groups[0];
1185 +    num_supp_groups++;
1186 +  }
1187 +
1188 +  authenticate (pw);
1189 +
1190 +  if (request_same_session || !command || !pw->pw_uid)
1191 +    same_session = 1;
1192 +
1193 +  if (!shell && !change_environment)
1194 +    shell = getenv ("SHELL");
1195 +  if (shell && getuid () != 0 && restricted_shell (pw->pw_shell))
1196 +    {
1197 +      /* The user being su'd to has a nonstandard shell, and so is
1198 +        probably a uucp account or has restricted access.  Don't
1199 +        compromise the account by allowing access with a standard
1200 +        shell.  */
1201 +      warnx (_("using restricted shell %s"), pw->pw_shell);
1202 +      shell = NULL;
1203 +    }
1204 +  shell = xstrdup (shell ? shell : pw->pw_shell);
1205 +
1206 +  init_groups (pw, groups, num_supp_groups);
1207 +
1208 +  create_watching_parent ();
1209 +  /* Now we're in the child.  */
1210 +
1211 +  change_identity (pw);
1212 +  if (!same_session)
1213 +    setsid ();
1214 +
1215 +  /* Set environment after pam_open_session, which may put KRB5CCNAME
1216 +     into the pam_env, etc.  */
1217 +
1218 +  modify_environment (pw, shell);
1219 +
1220 +  if (simulate_login && chdir (pw->pw_dir) != 0)
1221 +    warn (_("warning: cannot change directory to %s"), pw->pw_dir);
1222 +
1223 +  run_shell (shell, command, argv + optind, max (0, argc - optind));
1224 +}
1225 +
1226 +// vim: sw=2 cinoptions=>4,n-2,{2,^-2,\:2,=2,g0,h2,p5,t0,+2,(0,u0,w1,m1
1227 diff --git a/login-utils/su-common.h b/login-utils/su-common.h
1228 new file mode 100644
1229 index 0000000..7cf3769
1230 --- /dev/null
1231 +++ b/login-utils/su-common.h
1232 @@ -0,0 +1,11 @@
1233 +#ifndef UTIL_LINUX_SU_COMMON_H
1234 +#define UTIL_LINUX_SU_COMMON_H
1235 +
1236 +enum {
1237 +       SU_MODE,
1238 +       RUNUSER_MODE
1239 +};
1240 +
1241 +extern int su_main(int argc, char **argv, int mode);
1242 +
1243 +#endif /* UTIL_LINUX_SU_COMMON */
1244 diff --git a/login-utils/su.1 b/login-utils/su.1
1245 index 598cebd..59e1731 100644
1246 --- a/login-utils/su.1
1247 +++ b/login-utils/su.1
1248 @@ -59,6 +59,12 @@ Pass
1249  to the shell which may or may not be useful depending on the
1250  shell.
1251  .TP
1252 +\fB\-g\fR, \fB\-\-group\fR=\fIgroup\fR\fR
1253 +specify the primary group, this option is allowed for root user only
1254 +.TP
1255 +\fB\-G\fR, \fB\-\-supp-group\fR=\fIgroup\fR\fR
1256 +specify a supplemental group, this option is allowed for root user only
1257 +.TP
1258  \fB\-\fR, \fB\-l\fR, \fB\-\-login\fR
1259  Starts the shell as login shell with an environment similar to a real
1260  login:
1261 diff --git a/login-utils/su.c b/login-utils/su.c
1262 index c6b8bce..29c10f0 100644
1263 --- a/login-utils/su.c
1264 +++ b/login-utils/su.c
1265 @@ -1,689 +1,8 @@
1266 -/* su for Linux.  Run a shell with substitute user and group IDs.
1267 -   Copyright (C) 1992-2006 Free Software Foundation, Inc.
1268 -   Copyright (C) 2012 SUSE Linux Products GmbH, Nuernberg
1269  
1270 -   This program is free software; you can redistribute it and/or modify
1271 -   it under the terms of the GNU General Public License as published by
1272 -   the Free Software Foundation; either version 2, or (at your option)
1273 -   any later version.
1274 +#include "su-common.h"
1275  
1276 -   This program is distributed in the hope that it will be useful,
1277 -   but WITHOUT ANY WARRANTY; without even the implied warranty of
1278 -   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1279 -   GNU General Public License for more details.
1280 -
1281 -   You should have received a copy of the GNU General Public License
1282 -   along with this program; if not, write to the Free Software Foundation,
1283 -   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
1284 -
1285 -/* Run a shell with the real and effective UID and GID and groups
1286 -   of USER, default `root'.
1287 -
1288 -   The shell run is taken from USER's password entry, /bin/sh if
1289 -   none is specified there.  If the account has a password, su
1290 -   prompts for a password unless run by a user with real UID 0.
1291 -
1292 -   Does not change the current directory.
1293 -   Sets `HOME' and `SHELL' from the password entry for USER, and if
1294 -   USER is not root, sets `USER' and `LOGNAME' to USER.
1295 -   The subshell is not a login shell.
1296 -
1297 -   If one or more ARGs are given, they are passed as additional
1298 -   arguments to the subshell.
1299 -
1300 -   Does not handle /bin/sh or other shells specially
1301 -   (setting argv[0] to "-su", passing -c only to certain shells, etc.).
1302 -   I don't see the point in doing that, and it's ugly.
1303 -
1304 -   Based on an implemenation by David MacKenzie <djm@gnu.ai.mit.edu>.  */
1305 -
1306 -enum
1307 -{
1308 -  EXIT_CANNOT_INVOKE = 126,
1309 -  EXIT_ENOENT = 127
1310 -};
1311 -
1312 -#include <config.h>
1313 -#include <stdio.h>
1314 -#include <getopt.h>
1315 -#include <sys/types.h>
1316 -#include <pwd.h>
1317 -#include <grp.h>
1318 -#include <security/pam_appl.h>
1319 -#include <security/pam_misc.h>
1320 -#include <signal.h>
1321 -#include <sys/wait.h>
1322 -#include <syslog.h>
1323 -
1324 -#include "err.h"
1325 -
1326 -#include <stdbool.h>
1327 -#include "c.h"
1328 -#include "xalloc.h"
1329 -#include "nls.h"
1330 -#include "pathnames.h"
1331 -#include "env.h"
1332 -
1333 -/* name of the pam configuration files. separate configs for su and su -  */
1334 -#define PAM_SERVICE_NAME "su"
1335 -#define PAM_SERVICE_NAME_L "su-l"
1336 -
1337 -#define is_pam_failure(_rc)    ((_rc) != PAM_SUCCESS)
1338 -
1339 -#include "logindefs.h"
1340 -
1341 -/* The shell to run if none is given in the user's passwd entry.  */
1342 -#define DEFAULT_SHELL "/bin/sh"
1343 -
1344 -/* The user to become if none is specified.  */
1345 -#define DEFAULT_USER "root"
1346 -
1347 -#ifndef HAVE_ENVIRON_DECL
1348 -extern char **environ;
1349 -#endif
1350 -
1351 -static void run_shell (char const *, char const *, char **, size_t)
1352 -     __attribute__ ((__noreturn__));
1353 -
1354 -/* If true, pass the `-f' option to the subshell.  */
1355 -static bool fast_startup;
1356 -
1357 -/* If true, simulate a login instead of just starting a shell.  */
1358 -static bool simulate_login;
1359 -
1360 -/* If true, change some environment vars to indicate the user su'd to.  */
1361 -static bool change_environment;
1362 -
1363 -/* If true, then don't call setsid() with a command. */
1364 -int same_session = 0;
1365 -
1366 -static bool _pam_session_opened;
1367 -static bool _pam_cred_established;
1368 -static sig_atomic_t volatile caught_signal = false;
1369 -static pam_handle_t *pamh = NULL;
1370 -
1371 -static struct option const longopts[] =
1372 -{
1373 -  {"command", required_argument, NULL, 'c'},
1374 -  {"session-command", required_argument, NULL, 'C'},
1375 -  {"fast", no_argument, NULL, 'f'},
1376 -  {"login", no_argument, NULL, 'l'},
1377 -  {"preserve-environment", no_argument, NULL, 'p'},
1378 -  {"shell", required_argument, NULL, 's'},
1379 -  {"help", no_argument, 0, 'h'},
1380 -  {"version", no_argument, 0, 'V'},
1381 -  {NULL, 0, NULL, 0}
1382 -};
1383 -
1384 -/* Log the fact that someone has run su to the user given by PW;
1385 -   if SUCCESSFUL is true, they gave the correct password, etc.  */
1386 -
1387 -static void
1388 -log_su (struct passwd const *pw, bool successful)
1389 -{
1390 -  const char *new_user, *old_user, *tty;
1391 -
1392 -  new_user = pw->pw_name;
1393 -  /* The utmp entry (via getlogin) is probably the best way to identify
1394 -     the user, especially if someone su's from a su-shell.  */
1395 -  old_user = getlogin ();
1396 -  if (!old_user)
1397 -    {
1398 -      /* getlogin can fail -- usually due to lack of utmp entry.
1399 -        Resort to getpwuid.  */
1400 -      struct passwd *pwd = getpwuid (getuid ());
1401 -      old_user = (pwd ? pwd->pw_name : "");
1402 -    }
1403 -  tty = ttyname (STDERR_FILENO);
1404 -  if (!tty)
1405 -    tty = "none";
1406 -
1407 -  openlog (program_invocation_short_name, 0 , LOG_AUTH);
1408 -  syslog (LOG_NOTICE, "%s(to %s) %s on %s",
1409 -         successful ? "" : "FAILED SU ",
1410 -         new_user, old_user, tty);
1411 -  closelog ();
1412 -}
1413 -
1414 -static struct pam_conv conv =
1415 -{
1416 -  misc_conv,
1417 -  NULL
1418 -};
1419 -
1420 -static void
1421 -cleanup_pam (int retcode)
1422 +int main(int argv, char **argc)
1423  {
1424 -  int saved_errno = errno;
1425 -
1426 -  if (_pam_session_opened)
1427 -    pam_close_session (pamh, 0);
1428 -
1429 -  if (_pam_cred_established)
1430 -    pam_setcred (pamh, PAM_DELETE_CRED | PAM_SILENT);
1431 -
1432 -  pam_end(pamh, retcode);
1433 -
1434 -  errno = saved_errno;
1435 -}
1436 -
1437 -/* Signal handler for parent process.  */
1438 -static void
1439 -su_catch_sig (int sig __attribute__((__unused__)))
1440 -{
1441 -  caught_signal = true;
1442 -}
1443 -
1444 -/* Export env variables declared by PAM modules.  */
1445 -static void
1446 -export_pamenv (void)
1447 -{
1448 -  char **env;
1449 -
1450 -  /* This is a copy but don't care to free as we exec later anyways.  */
1451 -  env = pam_getenvlist (pamh);
1452 -  while (env && *env)
1453 -    {
1454 -      if (putenv (*env) != 0)
1455 -       err (EXIT_FAILURE, NULL);
1456 -      env++;
1457 -    }
1458 -}
1459 -
1460 -static void
1461 -create_watching_parent (void)
1462 -{
1463 -  pid_t child;
1464 -  sigset_t ourset;
1465 -  int status = 0;
1466 -  int retval;
1467 -
1468 -  retval = pam_open_session (pamh, 0);
1469 -  if (is_pam_failure(retval))
1470 -    {
1471 -      cleanup_pam (retval);
1472 -      errx (EXIT_FAILURE, _("cannot not open session: %s"),
1473 -            pam_strerror (pamh, retval));
1474 -    }
1475 -  else
1476 -    _pam_session_opened = 1;
1477 -
1478 -  child = fork ();
1479 -  if (child == (pid_t) -1)
1480 -    {
1481 -      cleanup_pam (PAM_ABORT);
1482 -      err (EXIT_FAILURE, _("cannot create child process"));
1483 -    }
1484 -
1485 -  /* the child proceeds to run the shell */
1486 -  if (child == 0)
1487 -    return;
1488 -
1489 -  /* In the parent watch the child.  */
1490 -
1491 -  /* su without pam support does not have a helper that keeps
1492 -     sitting on any directory so let's go to /.  */
1493 -  if (chdir ("/") != 0)
1494 -    warn (_("cannot change directory to %s"), "/");
1495 -
1496 -  sigfillset (&ourset);
1497 -  if (sigprocmask (SIG_BLOCK, &ourset, NULL))
1498 -    {
1499 -      warn (_("cannot block signals"));
1500 -      caught_signal = true;
1501 -    }
1502 -  if (!caught_signal)
1503 -    {
1504 -      struct sigaction action;
1505 -      action.sa_handler = su_catch_sig;
1506 -      sigemptyset (&action.sa_mask);
1507 -      action.sa_flags = 0;
1508 -      sigemptyset (&ourset);
1509 -    if (!same_session)
1510 -      {
1511 -        if (sigaddset(&ourset, SIGINT) || sigaddset(&ourset, SIGQUIT))
1512 -          {
1513 -            warn (_("cannot set signal handler"));
1514 -            caught_signal = true;
1515 -          }
1516 -      }
1517 -    if (!caught_signal && (sigaddset(&ourset, SIGTERM)
1518 -                    || sigaddset(&ourset, SIGALRM)
1519 -                    || sigaction(SIGTERM, &action, NULL)
1520 -                    || sigprocmask(SIG_UNBLOCK, &ourset, NULL))) {
1521 -         warn (_("cannot set signal handler"));
1522 -         caught_signal = true;
1523 -       }
1524 -    if (!caught_signal && !same_session && (sigaction(SIGINT, &action, NULL)
1525 -                                     || sigaction(SIGQUIT, &action, NULL)))
1526 -      {
1527 -        warn (_("cannot set signal handler"));
1528 -        caught_signal = true;
1529 -      }
1530 -    }
1531 -  if (!caught_signal)
1532 -    {
1533 -      pid_t pid;
1534 -      for (;;)
1535 -       {
1536 -         pid = waitpid (child, &status, WUNTRACED);
1537 -
1538 -         if (pid != (pid_t)-1 && WIFSTOPPED (status))
1539 -           {
1540 -             kill (getpid (), SIGSTOP);
1541 -             /* once we get here, we must have resumed */
1542 -             kill (pid, SIGCONT);
1543 -           }
1544 -         else
1545 -           break;
1546 -       }
1547 -      if (pid != (pid_t)-1)
1548 -       if (WIFSIGNALED (status))
1549 -         status = WTERMSIG (status) + 128;
1550 -       else
1551 -         status = WEXITSTATUS (status);
1552 -      else
1553 -       status = 1;
1554 -    }
1555 -  else
1556 -    status = 1;
1557 -
1558 -  if (caught_signal)
1559 -    {
1560 -      fprintf (stderr, _("\nSession terminated, killing shell..."));
1561 -      kill (child, SIGTERM);
1562 -    }
1563 -
1564 -  cleanup_pam (PAM_SUCCESS);
1565 -
1566 -  if (caught_signal)
1567 -    {
1568 -      sleep (2);
1569 -      kill (child, SIGKILL);
1570 -      fprintf (stderr, _(" ...killed.\n"));
1571 -    }
1572 -  exit (status);
1573 -}
1574 -
1575 -static void
1576 -authenticate (const struct passwd *pw)
1577 -{
1578 -  const struct passwd *lpw;
1579 -  const char *cp;
1580 -  int retval;
1581 -
1582 -  retval = pam_start (simulate_login ? PAM_SERVICE_NAME_L : PAM_SERVICE_NAME,
1583 -                     pw->pw_name, &conv, &pamh);
1584 -  if (is_pam_failure(retval))
1585 -    goto done;
1586 -
1587 -  if (isatty (0) && (cp = ttyname (0)) != NULL)
1588 -    {
1589 -      const char *tty;
1590 -
1591 -      if (strncmp (cp, "/dev/", 5) == 0)
1592 -       tty = cp + 5;
1593 -      else
1594 -       tty = cp;
1595 -      retval = pam_set_item (pamh, PAM_TTY, tty);
1596 -      if (is_pam_failure(retval))
1597 -       goto done;
1598 -    }
1599 -
1600 -  lpw = getpwuid (getuid ());
1601 -  if (lpw && lpw->pw_name)
1602 -    {
1603 -      retval = pam_set_item (pamh, PAM_RUSER, (const void *) lpw->pw_name);
1604 -      if (is_pam_failure(retval))
1605 -       goto done;
1606 -    }
1607 -
1608 -  retval = pam_authenticate (pamh, 0);
1609 -  if (is_pam_failure(retval))
1610 -    goto done;
1611 -
1612 -  retval = pam_acct_mgmt (pamh, 0);
1613 -  if (retval == PAM_NEW_AUTHTOK_REQD)
1614 -    {
1615 -      /* Password has expired.  Offer option to change it.  */
1616 -      retval = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
1617 -    }
1618 -
1619 -done:
1620 -
1621 -  log_su (pw, !is_pam_failure(retval));
1622 -
1623 -  if (is_pam_failure(retval))
1624 -    {
1625 -      const char *msg = pam_strerror(pamh, retval);
1626 -      pam_end(pamh, retval);
1627 -      sleep (getlogindefs_num ("FAIL_DELAY", 1));
1628 -      errx (EXIT_FAILURE, "%s", msg?msg:_("incorrect password"));
1629 -    }
1630 -}
1631 -
1632 -static void
1633 -set_path(const struct passwd* pw)
1634 -{
1635 -  int r;
1636 -  if (pw->pw_uid)
1637 -    r = logindefs_setenv("PATH", "ENV_PATH", _PATH_DEFPATH);
1638 -
1639 -  else if ((r = logindefs_setenv("PATH", "ENV_ROOTPATH", NULL)) != 0)
1640 -    r = logindefs_setenv("PATH", "ENV_SUPATH", _PATH_DEFPATH_ROOT);
1641 -
1642 -  if (r != 0)
1643 -    err (EXIT_FAILURE,  _("failed to set PATH"));
1644 -}
1645 -
1646 -/* Update `environ' for the new shell based on PW, with SHELL being
1647 -   the value for the SHELL environment variable.  */
1648 -
1649 -static void
1650 -modify_environment (const struct passwd *pw, const char *shell)
1651 -{
1652 -  if (simulate_login)
1653 -    {
1654 -      /* Leave TERM unchanged.  Set HOME, SHELL, USER, LOGNAME, PATH.
1655 -         Unset all other environment variables.  */
1656 -      char const *term = getenv ("TERM");
1657 -      if (term)
1658 -       term = xstrdup (term);
1659 -      environ = xmalloc ((6 + !!term) * sizeof (char *));
1660 -      environ[0] = NULL;
1661 -      if (term)
1662 -       xsetenv ("TERM", term, 1);
1663 -      xsetenv ("HOME", pw->pw_dir, 1);
1664 -      xsetenv ("SHELL", shell, 1);
1665 -      xsetenv ("USER", pw->pw_name, 1);
1666 -      xsetenv ("LOGNAME", pw->pw_name, 1);
1667 -      set_path(pw);
1668 -    }
1669 -  else
1670 -    {
1671 -      /* Set HOME, SHELL, and if not becoming a super-user,
1672 -        USER and LOGNAME.  */
1673 -      if (change_environment)
1674 -        {
1675 -          xsetenv ("HOME", pw->pw_dir, 1);
1676 -          xsetenv ("SHELL", shell, 1);
1677 -         if (getlogindefs_bool ("ALWAYS_SET_PATH", 0))
1678 -           set_path(pw);
1679 -
1680 -          if (pw->pw_uid)
1681 -            {
1682 -              xsetenv ("USER", pw->pw_name, 1);
1683 -              xsetenv ("LOGNAME", pw->pw_name, 1);
1684 -            }
1685 -        }
1686 -    }
1687 -
1688 -  export_pamenv ();
1689 -}
1690 -
1691 -/* Become the user and group(s) specified by PW.  */
1692 -
1693 -static void
1694 -init_groups (const struct passwd *pw)
1695 -{
1696 -  int retval;
1697 -  errno = 0;
1698 -  if (initgroups (pw->pw_name, pw->pw_gid) == -1)
1699 -    {
1700 -      cleanup_pam (PAM_ABORT);
1701 -      err (EXIT_FAILURE, _("cannot set groups"));
1702 -    }
1703 -  endgrent ();
1704 -
1705 -  retval = pam_setcred (pamh, PAM_ESTABLISH_CRED);
1706 -  if (is_pam_failure(retval))
1707 -    errx (EXIT_FAILURE, "%s", pam_strerror (pamh, retval));
1708 -  else
1709 -    _pam_cred_established = 1;
1710 -}
1711 -
1712 -static void
1713 -change_identity (const struct passwd *pw)
1714 -{
1715 -  if (setgid (pw->pw_gid))
1716 -    err (EXIT_FAILURE,  _("cannot set group id"));
1717 -  if (setuid (pw->pw_uid))
1718 -    err (EXIT_FAILURE,  _("cannot set user id"));
1719 -}
1720 -
1721 -/* Run SHELL, or DEFAULT_SHELL if SHELL is empty.
1722 -   If COMMAND is nonzero, pass it to the shell with the -c option.
1723 -   Pass ADDITIONAL_ARGS to the shell as more arguments; there
1724 -   are N_ADDITIONAL_ARGS extra arguments.  */
1725 -
1726 -static void
1727 -run_shell (char const *shell, char const *command, char **additional_args,
1728 -          size_t n_additional_args)
1729 -{
1730 -  size_t n_args = 1 + fast_startup + 2 * !!command + n_additional_args + 1;
1731 -  char const **args = xcalloc (n_args, sizeof *args);
1732 -  size_t argno = 1;
1733 -
1734 -  if (simulate_login)
1735 -    {
1736 -      char *arg0;
1737 -      char *shell_basename;
1738 -
1739 -      shell_basename = basename (shell);
1740 -      arg0 = xmalloc (strlen (shell_basename) + 2);
1741 -      arg0[0] = '-';
1742 -      strcpy (arg0 + 1, shell_basename);
1743 -      args[0] = arg0;
1744 -    }
1745 -  else
1746 -    args[0] = basename (shell);
1747 -  if (fast_startup)
1748 -    args[argno++] = "-f";
1749 -  if (command)
1750 -    {
1751 -      args[argno++] = "-c";
1752 -      args[argno++] = command;
1753 -    }
1754 -  memcpy (args + argno, additional_args, n_additional_args * sizeof *args);
1755 -  args[argno + n_additional_args] = NULL;
1756 -  execv (shell, (char **) args);
1757 -
1758 -  {
1759 -    int exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
1760 -    warn ("%s", shell);
1761 -    exit (exit_status);
1762 -  }
1763 -}
1764 -
1765 -/* Return true if SHELL is a restricted shell (one not returned by
1766 -   getusershell), else false, meaning it is a standard shell.  */
1767 -
1768 -static bool
1769 -restricted_shell (const char *shell)
1770 -{
1771 -  char *line;
1772 -
1773 -  setusershell ();
1774 -  while ((line = getusershell ()) != NULL)
1775 -    {
1776 -      if (*line != '#' && !strcmp (line, shell))
1777 -       {
1778 -         endusershell ();
1779 -         return false;
1780 -       }
1781 -    }
1782 -  endusershell ();
1783 -  return true;
1784 -}
1785 -
1786 -static void __attribute__((__noreturn__))
1787 -usage (int status)
1788 -{
1789 -  if (status != EXIT_SUCCESS)
1790 -    fprintf (stderr, _("Try `%s --help' for more information.\n"),
1791 -            program_invocation_short_name);
1792 -  else
1793 -    {
1794 -      fputs(USAGE_HEADER, stdout);
1795 -      printf (_(" %s [options] [-] [USER [arg]...]\n"), program_invocation_short_name);
1796 -      fputs (_("\n\
1797 - Change the effective user id and group id to that of USER.\n\
1798 - A mere - implies -l.   If USER not given, assume root.\n"), stdout);
1799 -      fputs(USAGE_OPTIONS, stdout);
1800 -      fputs (_("\
1801 - -, -l, --login               make the shell a login shell\n\
1802 - -c, --command <command>      pass a single command to the shell with -c\n\
1803 - --session-command <command>  pass a single command to the shell with -c\n\
1804 -                              and do not create a new session\n\
1805 - -f, --fast                   pass -f to the shell (for csh or tcsh)\n\
1806 - -m, --preserve-environment   do not reset environment variables\n\
1807 - -p                           same as -m\n\
1808 - -s, --shell <shell>          run shell if /etc/shells allows it\n\
1809 -"), stdout);
1810 -
1811 -      fputs(USAGE_SEPARATOR, stdout);
1812 -      fputs(USAGE_HELP, stdout);
1813 -      fputs(USAGE_VERSION, stdout);
1814 -      printf(USAGE_MAN_TAIL("su(1)"));
1815 -    }
1816 -  exit (status);
1817 -}
1818 -
1819 -static
1820 -void load_config(void)
1821 -{
1822 -  logindefs_load_file("/etc/default/su");
1823 -  logindefs_load_file(_PATH_LOGINDEFS);
1824 -}
1825 -
1826 -int
1827 -main (int argc, char **argv)
1828 -{
1829 -  int optc;
1830 -  const char *new_user = DEFAULT_USER;
1831 -  char *command = NULL;
1832 -  int request_same_session = 0;
1833 -  char *shell = NULL;
1834 -  struct passwd *pw;
1835 -  struct passwd pw_copy;
1836 -
1837 -  setlocale (LC_ALL, "");
1838 -  bindtextdomain (PACKAGE, LOCALEDIR);
1839 -  textdomain (PACKAGE);
1840 -
1841 -  fast_startup = false;
1842 -  simulate_login = false;
1843 -  change_environment = true;
1844 -
1845 -  while ((optc = getopt_long (argc, argv, "c:flmps:hV", longopts, NULL)) != -1)
1846 -    {
1847 -      switch (optc)
1848 -       {
1849 -       case 'c':
1850 -         command = optarg;
1851 -         break;
1852 -
1853 -        case 'C':
1854 -          command = optarg;
1855 -          request_same_session = 1;
1856 -          break;
1857 -
1858 -       case 'f':
1859 -         fast_startup = true;
1860 -         break;
1861 -
1862 -       case 'l':
1863 -         simulate_login = true;
1864 -         break;
1865 -
1866 -       case 'm':
1867 -       case 'p':
1868 -         change_environment = false;
1869 -         break;
1870 -
1871 -       case 's':
1872 -         shell = optarg;
1873 -         break;
1874 -
1875 -       case 'h':
1876 -         usage(0);
1877 -
1878 -       case 'V':
1879 -         printf(UTIL_LINUX_VERSION);
1880 -         exit(EXIT_SUCCESS);
1881 -
1882 -       default:
1883 -         usage (EXIT_FAILURE);
1884 -       }
1885 -    }
1886 -
1887 -  if (optind < argc && !strcmp (argv[optind], "-"))
1888 -    {
1889 -      simulate_login = true;
1890 -      ++optind;
1891 -    }
1892 -  if (optind < argc)
1893 -    new_user = argv[optind++];
1894 -
1895 -  logindefs_load_defaults = load_config;
1896 -
1897 -  pw = getpwnam (new_user);
1898 -  if (! (pw && pw->pw_name && pw->pw_name[0] && pw->pw_dir && pw->pw_dir[0]
1899 -        && pw->pw_passwd))
1900 -    errx (EXIT_FAILURE, _("user %s does not exist"), new_user);
1901 -
1902 -  /* Make a copy of the password information and point pw at the local
1903 -     copy instead.  Otherwise, some systems (e.g. Linux) would clobber
1904 -     the static data through the getlogin call from log_su.
1905 -     Also, make sure pw->pw_shell is a nonempty string.
1906 -     It may be NULL when NEW_USER is a username that is retrieved via NIS (YP),
1907 -     but that doesn't have a default shell listed.  */
1908 -  pw_copy = *pw;
1909 -  pw = &pw_copy;
1910 -  pw->pw_name = xstrdup (pw->pw_name);
1911 -  pw->pw_passwd = xstrdup (pw->pw_passwd);
1912 -  pw->pw_dir = xstrdup (pw->pw_dir);
1913 -  pw->pw_shell = xstrdup (pw->pw_shell && pw->pw_shell[0]
1914 -                         ? pw->pw_shell
1915 -                         : DEFAULT_SHELL);
1916 -  endpwent ();
1917 -
1918 -  authenticate (pw);
1919 -
1920 -  if (request_same_session || !command || !pw->pw_uid)
1921 -    same_session = 1;
1922 -
1923 -  if (!shell && !change_environment)
1924 -    shell = getenv ("SHELL");
1925 -  if (shell && getuid () != 0 && restricted_shell (pw->pw_shell))
1926 -    {
1927 -      /* The user being su'd to has a nonstandard shell, and so is
1928 -        probably a uucp account or has restricted access.  Don't
1929 -        compromise the account by allowing access with a standard
1930 -        shell.  */
1931 -      warnx (_("using restricted shell %s"), pw->pw_shell);
1932 -      shell = NULL;
1933 -    }
1934 -  shell = xstrdup (shell ? shell : pw->pw_shell);
1935 -
1936 -  init_groups (pw);
1937 -
1938 -  create_watching_parent ();
1939 -  /* Now we're in the child.  */
1940 -
1941 -  change_identity (pw);
1942 -  if (!same_session)
1943 -    setsid ();
1944 -
1945 -  /* Set environment after pam_open_session, which may put KRB5CCNAME
1946 -     into the pam_env, etc.  */
1947 -
1948 -  modify_environment (pw, shell);
1949 -
1950 -  if (simulate_login && chdir (pw->pw_dir) != 0)
1951 -    warn (_("warning: cannot change directory to %s"), pw->pw_dir);
1952 -
1953 -  run_shell (shell, command, argv + optind, max (0, argc - optind));
1954 +       return su_main(argv, argc, SU_MODE);
1955  }
1956  
1957 -// vim: sw=2 cinoptions=>4,n-2,{2,^-2,\:2,=2,g0,h2,p5,t0,+2,(0,u0,w1,m1
This page took 0.330764 seconds and 3 git commands to generate.