]>
Commit | Line | Data |
---|---|---|
034a23bb JB |
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 |