]> git.pld-linux.org Git - packages/util-linux.git/blame - util-linux-runuser.patch
fix wipefs not understanding -f option
[packages/util-linux.git] / util-linux-runuser.patch
CommitLineData
034a23bb
JB
1diff --git a/configure.ac b/configure.ac
2index 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
21diff --git a/login-utils/Makemodule.am b/login-utils/Makemodule.am
22index 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
54diff --git a/login-utils/runuser.1 b/login-utils/runuser.1
55new file mode 100644
56index 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 .
290diff --git a/login-utils/runuser.c b/login-utils/runuser.c
291new file mode 100644
292index 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+}
303diff --git a/login-utils/su-common.c b/login-utils/su-common.c
304new file mode 100644
305index 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
1227diff --git a/login-utils/su-common.h b/login-utils/su-common.h
1228new file mode 100644
1229index 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 */
1244diff --git a/login-utils/su.1 b/login-utils/su.1
1245index 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:
1261diff --git a/login-utils/su.c b/login-utils/su.c
1262index 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.326613 seconds and 4 git commands to generate.