Deleted Added
full compact
su.c (169125) su.c (169177)
1/*
2 * Copyright (c) 2002, 2005 Networks Associates Technologies, Inc.
3 * All rights reserved.
4 *
5 * Portions of this software were developed for the FreeBSD Project by
6 * ThinkSec AS and NAI Labs, the Security Research Division of Network
7 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
8 * ("CBOSS"), as part of the DARPA CHATS research program.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31/*-
32 * Copyright (c) 1988, 1993, 1994
33 * The Regents of the University of California. All rights reserved.
34 *
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
37 * are met:
38 * 1. Redistributions of source code must retain the above copyright
39 * notice, this list of conditions and the following disclaimer.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 * notice, this list of conditions and the following disclaimer in the
42 * documentation and/or other materials provided with the distribution.
43 * 3. All advertising materials mentioning features or use of this software
44 * must display the following acknowledgement:
45 * This product includes software developed by the University of
46 * California, Berkeley and its contributors.
47 * 4. Neither the name of the University nor the names of its contributors
48 * may be used to endorse or promote products derived from this software
49 * without specific prior written permission.
50 *
51 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61 * SUCH DAMAGE.
62 */
63
64#ifndef lint
65static const char copyright[] =
66"@(#) Copyright (c) 1988, 1993, 1994\n\
67 The Regents of the University of California. All rights reserved.\n";
68#endif /* not lint */
69
70#if 0
71#ifndef lint
72static char sccsid[] = "@(#)su.c 8.3 (Berkeley) 4/2/94";
73#endif /* not lint */
74#endif
75
76#include <sys/cdefs.h>
1/*
2 * Copyright (c) 2002, 2005 Networks Associates Technologies, Inc.
3 * All rights reserved.
4 *
5 * Portions of this software were developed for the FreeBSD Project by
6 * ThinkSec AS and NAI Labs, the Security Research Division of Network
7 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
8 * ("CBOSS"), as part of the DARPA CHATS research program.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31/*-
32 * Copyright (c) 1988, 1993, 1994
33 * The Regents of the University of California. All rights reserved.
34 *
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
37 * are met:
38 * 1. Redistributions of source code must retain the above copyright
39 * notice, this list of conditions and the following disclaimer.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 * notice, this list of conditions and the following disclaimer in the
42 * documentation and/or other materials provided with the distribution.
43 * 3. All advertising materials mentioning features or use of this software
44 * must display the following acknowledgement:
45 * This product includes software developed by the University of
46 * California, Berkeley and its contributors.
47 * 4. Neither the name of the University nor the names of its contributors
48 * may be used to endorse or promote products derived from this software
49 * without specific prior written permission.
50 *
51 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61 * SUCH DAMAGE.
62 */
63
64#ifndef lint
65static const char copyright[] =
66"@(#) Copyright (c) 1988, 1993, 1994\n\
67 The Regents of the University of California. All rights reserved.\n";
68#endif /* not lint */
69
70#if 0
71#ifndef lint
72static char sccsid[] = "@(#)su.c 8.3 (Berkeley) 4/2/94";
73#endif /* not lint */
74#endif
75
76#include <sys/cdefs.h>
77__FBSDID("$FreeBSD: head/usr.bin/su/su.c 169125 2007-04-30 12:51:02Z ache $");
77__FBSDID("$FreeBSD: head/usr.bin/su/su.c 169177 2007-05-01 16:02:44Z ache $");
78
79#include <sys/param.h>
80#include <sys/time.h>
81#include <sys/resource.h>
82#include <sys/wait.h>
83
84#ifdef USE_BSM_AUDIT
85#include <bsm/libbsm.h>
86#include <bsm/audit_uevents.h>
87#endif
88
89#include <err.h>
90#include <errno.h>
91#include <grp.h>
92#include <login_cap.h>
93#include <paths.h>
94#include <pwd.h>
95#include <signal.h>
96#include <stdio.h>
97#include <stdlib.h>
98#include <string.h>
99#include <syslog.h>
100#include <unistd.h>
101#include <stdarg.h>
102
103#include <security/pam_appl.h>
104#include <security/openpam.h>
105
106#define PAM_END() do { \
107 int local_ret; \
108 if (pamh != NULL) { \
109 local_ret = pam_setcred(pamh, PAM_DELETE_CRED); \
110 if (local_ret != PAM_SUCCESS) \
111 syslog(LOG_ERR, "pam_setcred: %s", \
112 pam_strerror(pamh, local_ret)); \
113 if (asthem) { \
114 local_ret = pam_close_session(pamh, 0); \
115 if (local_ret != PAM_SUCCESS) \
116 syslog(LOG_ERR, "pam_close_session: %s",\
117 pam_strerror(pamh, local_ret)); \
118 } \
119 local_ret = pam_end(pamh, local_ret); \
120 if (local_ret != PAM_SUCCESS) \
121 syslog(LOG_ERR, "pam_end: %s", \
122 pam_strerror(pamh, local_ret)); \
123 } \
124} while (0)
125
126
127#define PAM_SET_ITEM(what, item) do { \
128 int local_ret; \
129 local_ret = pam_set_item(pamh, what, item); \
130 if (local_ret != PAM_SUCCESS) { \
131 syslog(LOG_ERR, "pam_set_item(" #what "): %s", \
132 pam_strerror(pamh, local_ret)); \
133 errx(1, "pam_set_item(" #what "): %s", \
134 pam_strerror(pamh, local_ret)); \
135 /* NOTREACHED */ \
136 } \
137} while (0)
138
139enum tristate { UNSET, YES, NO };
140
141static pam_handle_t *pamh = NULL;
142static char **environ_pam;
143
144static char *ontty(void);
145static int chshell(const char *);
146static void usage(void) __dead2;
147static void export_pam_environment(void);
148static int ok_to_export(const char *);
149
150extern char **environ;
151
152int
153main(int argc, char *argv[])
154{
155 static char *cleanenv;
156 struct passwd *pwd;
157 struct pam_conv conv = { openpam_ttyconv, NULL };
158 enum tristate iscsh;
159 login_cap_t *lc;
160 union {
161 const char **a;
162 char * const *b;
163 } np;
164 uid_t ruid;
165 pid_t child_pid, child_pgrp, pid;
166 int asme, ch, asthem, fastlogin, prio, i, retcode,
167 statusp, setmaclabel;
168 u_int setwhat;
169 char *username, *class, shellbuf[MAXPATHLEN];
170 const char *p, *user, *shell, *mytty, **nargv;
171 struct sigaction sa, sa_int, sa_quit, sa_pipe;
172 int temp, fds[2];
173#ifdef USE_BSM_AUDIT
174 const char *aerr;
175 au_id_t auid;
176#endif
177
178 shell = class = cleanenv = NULL;
179 asme = asthem = fastlogin = statusp = 0;
180 user = "root";
181 iscsh = UNSET;
182 setmaclabel = 0;
183
184 while ((ch = getopt(argc, argv, "-flmsc:")) != -1)
185 switch ((char)ch) {
186 case 'f':
187 fastlogin = 1;
188 break;
189 case '-':
190 case 'l':
191 asme = 0;
192 asthem = 1;
193 break;
194 case 'm':
195 asme = 1;
196 asthem = 0;
197 break;
198 case 's':
199 setmaclabel = 1;
200 break;
201 case 'c':
202 class = optarg;
203 break;
204 case '?':
205 default:
206 usage();
207 /* NOTREACHED */
208 }
209
210 if (optind < argc)
211 user = argv[optind++];
212
213 if (user == NULL)
214 usage();
215 /* NOTREACHED */
216
217 /*
218 * Try to provide more helpful debugging output if su(1) is running
219 * non-setuid, or was run from a file system not mounted setuid.
220 */
221 if (geteuid() != 0)
222 errx(1, "not running setuid");
223
224#ifdef USE_BSM_AUDIT
225 if (getauid(&auid) < 0 && errno != ENOSYS) {
226 syslog(LOG_AUTH | LOG_ERR, "getauid: %s", strerror(errno));
227 errx(1, "Permission denied");
228 }
229#endif
230 if (strlen(user) > MAXLOGNAME - 1) {
231#ifdef USE_BSM_AUDIT
232 if (audit_submit(AUE_su, auid,
233 1, EPERM, "username too long: '%s'", user))
234 errx(1, "Permission denied");
235#endif
236 errx(1, "username too long");
237 }
238
239 nargv = malloc(sizeof(char *) * (size_t)(argc + 4));
240 if (nargv == NULL)
241 errx(1, "malloc failure");
242
243 nargv[argc + 3] = NULL;
244 for (i = argc; i >= optind; i--)
245 nargv[i + 3] = argv[i];
246 np.a = &nargv[i + 3];
247
248 argv += optind;
249
250 errno = 0;
251 prio = getpriority(PRIO_PROCESS, 0);
252 if (errno)
253 prio = 0;
254
255 setpriority(PRIO_PROCESS, 0, -2);
256 openlog("su", LOG_CONS, LOG_AUTH);
257
258 /* get current login name, real uid and shell */
259 ruid = getuid();
260 username = getlogin();
261 pwd = getpwnam(username);
262 if (username == NULL || pwd == NULL || pwd->pw_uid != ruid)
263 pwd = getpwuid(ruid);
264 if (pwd == NULL) {
265#ifdef USE_BSM_AUDIT
266 if (audit_submit(AUE_su, auid, 1, EPERM,
267 "unable to determine invoking subject: '%s'", username))
268 errx(1, "Permission denied");
269#endif
270 errx(1, "who are you?");
271 }
272
273 username = strdup(pwd->pw_name);
274 if (username == NULL)
275 err(1, "strdup failure");
276
277 if (asme) {
278 if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') {
279 /* must copy - pwd memory is recycled */
280 shell = strncpy(shellbuf, pwd->pw_shell,
281 sizeof(shellbuf));
282 shellbuf[sizeof(shellbuf) - 1] = '\0';
283 }
284 else {
285 shell = _PATH_BSHELL;
286 iscsh = NO;
287 }
288 }
289
290 /* Do the whole PAM startup thing */
291 retcode = pam_start("su", user, &conv, &pamh);
292 if (retcode != PAM_SUCCESS) {
293 syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, retcode));
294 errx(1, "pam_start: %s", pam_strerror(pamh, retcode));
295 }
296
297 PAM_SET_ITEM(PAM_RUSER, username);
298
299 mytty = ttyname(STDERR_FILENO);
300 if (!mytty)
301 mytty = "tty";
302 PAM_SET_ITEM(PAM_TTY, mytty);
303
304 retcode = pam_authenticate(pamh, 0);
305 if (retcode != PAM_SUCCESS) {
306#ifdef USE_BSM_AUDIT
307 if (audit_submit(AUE_su, auid, 1, EPERM, "bad su %s to %s on %s",
308 username, user, mytty))
309 errx(1, "Permission denied");
310#endif
311 syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s on %s",
312 username, user, mytty);
313 errx(1, "Sorry");
314 }
315#ifdef USE_BSM_AUDIT
316 if (audit_submit(AUE_su, auid, 0, 0, "successful authentication"))
317 errx(1, "Permission denied");
318#endif
319 retcode = pam_get_item(pamh, PAM_USER, (const void **)&p);
320 if (retcode == PAM_SUCCESS)
321 user = p;
322 else
323 syslog(LOG_ERR, "pam_get_item(PAM_USER): %s",
324 pam_strerror(pamh, retcode));
325 pwd = getpwnam(user);
326 if (pwd == NULL) {
327#ifdef USE_BSM_AUDIT
328 if (audit_submit(AUE_su, auid, 1, EPERM,
329 "unknown subject: %s", user))
330 errx(1, "Permission denied");
331#endif
332 errx(1, "unknown login: %s", user);
333 }
334
335 retcode = pam_acct_mgmt(pamh, 0);
336 if (retcode == PAM_NEW_AUTHTOK_REQD) {
337 retcode = pam_chauthtok(pamh,
338 PAM_CHANGE_EXPIRED_AUTHTOK);
339 if (retcode != PAM_SUCCESS) {
340#ifdef USE_BSM_AUDIT
341 aerr = pam_strerror(pamh, retcode);
342 if (aerr == NULL)
343 aerr = "Unknown PAM error";
344 if (audit_submit(AUE_su, auid, 1, EPERM,
345 "pam_chauthtok: %s", aerr))
346 errx(1, "Permission denied");
347#endif
348 syslog(LOG_ERR, "pam_chauthtok: %s",
349 pam_strerror(pamh, retcode));
350 errx(1, "Sorry");
351 }
352 }
353 if (retcode != PAM_SUCCESS) {
354#ifdef USE_BSM_AUDIT
355 if (audit_submit(AUE_su, auid, 1, EPERM, "pam_acct_mgmt: %s",
356 pam_strerror(pamh, retcode)))
357 errx(1, "Permission denied");
358#endif
359 syslog(LOG_ERR, "pam_acct_mgmt: %s",
360 pam_strerror(pamh, retcode));
361 errx(1, "Sorry");
362 }
363
364 /* get target login information */
365 if (class == NULL)
366 lc = login_getpwclass(pwd);
367 else {
368 if (ruid != 0) {
369#ifdef USE_BSM_AUDIT
370 if (audit_submit(AUE_su, auid, 1, EPERM,
371 "only root may use -c"))
372 errx(1, "Permission denied");
373#endif
374 errx(1, "only root may use -c");
375 }
376 lc = login_getclass(class);
377 if (lc == NULL)
378 errx(1, "unknown class: %s", class);
379 }
380
381 /* if asme and non-standard target shell, must be root */
382 if (asme) {
383 if (ruid != 0 && !chshell(pwd->pw_shell))
384 errx(1, "permission denied (shell)");
385 }
386 else if (pwd->pw_shell && *pwd->pw_shell) {
387 shell = pwd->pw_shell;
388 iscsh = UNSET;
389 }
390 else {
391 shell = _PATH_BSHELL;
392 iscsh = NO;
393 }
394
395 /* if we're forking a csh, we want to slightly muck the args */
396 if (iscsh == UNSET) {
397 p = strrchr(shell, '/');
398 if (p)
399 ++p;
400 else
401 p = shell;
402 iscsh = strcmp(p, "csh") ? (strcmp(p, "tcsh") ? NO : YES) : YES;
403 }
404 setpriority(PRIO_PROCESS, 0, prio);
405
406 /*
407 * PAM modules might add supplementary groups in pam_setcred(), so
408 * initialize them first.
409 */
410 if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0)
411 err(1, "setusercontext");
412
413 retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
414 if (retcode != PAM_SUCCESS) {
415 syslog(LOG_ERR, "pam_setcred: %s",
416 pam_strerror(pamh, retcode));
417 errx(1, "failed to establish credentials.");
418 }
419 if (asthem) {
420 retcode = pam_open_session(pamh, 0);
421 if (retcode != PAM_SUCCESS) {
422 syslog(LOG_ERR, "pam_open_session: %s",
423 pam_strerror(pamh, retcode));
424 errx(1, "failed to open session.");
425 }
426 }
427
428 /*
429 * We must fork() before setuid() because we need to call
430 * pam_setcred(pamh, PAM_DELETE_CRED) as root.
431 */
432 sa.sa_flags = SA_RESTART;
433 sa.sa_handler = SIG_IGN;
434 sigemptyset(&sa.sa_mask);
435 sigaction(SIGINT, &sa, &sa_int);
436 sigaction(SIGQUIT, &sa, &sa_quit);
437 sigaction(SIGPIPE, &sa, &sa_pipe);
438 sa.sa_handler = SIG_DFL;
439 sigaction(SIGTSTP, &sa, NULL);
440 statusp = 1;
441 if (pipe(fds) == -1) {
442 PAM_END();
443 err(1, "pipe");
444 }
445 child_pid = fork();
446 switch (child_pid) {
447 default:
448 sa.sa_handler = SIG_IGN;
449 sigaction(SIGTTOU, &sa, NULL);
450 close(fds[0]);
451 setpgid(child_pid, child_pid);
452 if (tcgetpgrp(STDERR_FILENO) == getpgrp())
453 tcsetpgrp(STDERR_FILENO, child_pid);
454 close(fds[1]);
455 sigaction(SIGPIPE, &sa_pipe, NULL);
456 while ((pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) {
457 if (WIFSTOPPED(statusp)) {
458 child_pgrp = getpgid(child_pid);
459 if (tcgetpgrp(STDERR_FILENO) == child_pgrp)
460 tcsetpgrp(STDERR_FILENO, getpgrp());
461 kill(getpid(), SIGSTOP);
462 if (tcgetpgrp(STDERR_FILENO) == getpgrp()) {
463 child_pgrp = getpgid(child_pid);
464 tcsetpgrp(STDERR_FILENO, child_pgrp);
465 }
466 kill(child_pid, SIGCONT);
467 statusp = 1;
468 continue;
469 }
470 break;
471 }
472 child_pgrp = getpgid(child_pid);
473 if (tcgetpgrp(STDERR_FILENO) == child_pgrp)
474 tcsetpgrp(STDERR_FILENO, getpgrp());
475 if (pid == -1)
476 err(1, "waitpid");
477 PAM_END();
478 exit(WEXITSTATUS(statusp));
479 case -1:
480 PAM_END();
481 err(1, "fork");
482 case 0:
483 close(fds[1]);
484 read(fds[0], &temp, 1);
485 close(fds[0]);
486 sigaction(SIGPIPE, &sa_pipe, NULL);
487 sigaction(SIGINT, &sa_int, NULL);
488 sigaction(SIGQUIT, &sa_quit, NULL);
489
490 /*
491 * Set all user context except for: Environmental variables
492 * Umask Login records (wtmp, etc) Path
493 */
494 setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK |
495 LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP |
496 LOGIN_SETMAC);
497 /*
498 * If -s is present, also set the MAC label.
499 */
500 if (setmaclabel)
501 setwhat |= LOGIN_SETMAC;
502 /*
503 * Don't touch resource/priority settings if -m has been used
504 * or -l and -c hasn't, and we're not su'ing to root.
505 */
506 if ((asme || (!asthem && class == NULL)) && pwd->pw_uid)
507 setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES);
508 if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0)
509 err(1, "setusercontext");
510
511 if (!asme) {
512 if (asthem) {
513 p = getenv("TERM");
514 environ = &cleanenv;
515 }
516
517 if (asthem || pwd->pw_uid)
518 setenv("USER", pwd->pw_name, 1);
519 setenv("HOME", pwd->pw_dir, 1);
520 setenv("SHELL", shell, 1);
521
522 if (asthem) {
523 /*
524 * Add any environmental variables that the
525 * PAM modules may have set.
526 */
527 environ_pam = pam_getenvlist(pamh);
528 if (environ_pam)
529 export_pam_environment();
530
531 /* set the su'd user's environment & umask */
532 setusercontext(lc, pwd, pwd->pw_uid,
533 LOGIN_SETPATH | LOGIN_SETUMASK |
534 LOGIN_SETENV);
535 if (p)
536 setenv("TERM", p, 1);
537
538 p = pam_getenv(pamh, "HOME");
539 if (chdir(p ? p : pwd->pw_dir) < 0)
540 errx(1, "no directory");
541 }
542 }
543 login_close(lc);
544
545 if (iscsh == YES) {
546 if (fastlogin)
547 *np.a-- = "-f";
548 if (asme)
549 *np.a-- = "-m";
550 }
551 /* csh strips the first character... */
552 *np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su";
553
554 if (ruid != 0)
555 syslog(LOG_NOTICE, "%s to %s%s", username, user,
556 ontty());
557
558 execv(shell, np.b);
559 err(1, "%s", shell);
560 }
561}
562
563static void
564export_pam_environment(void)
565{
566 char **pp;
567
568 for (pp = environ_pam; *pp != NULL; pp++) {
569 if (ok_to_export(*pp))
78
79#include <sys/param.h>
80#include <sys/time.h>
81#include <sys/resource.h>
82#include <sys/wait.h>
83
84#ifdef USE_BSM_AUDIT
85#include <bsm/libbsm.h>
86#include <bsm/audit_uevents.h>
87#endif
88
89#include <err.h>
90#include <errno.h>
91#include <grp.h>
92#include <login_cap.h>
93#include <paths.h>
94#include <pwd.h>
95#include <signal.h>
96#include <stdio.h>
97#include <stdlib.h>
98#include <string.h>
99#include <syslog.h>
100#include <unistd.h>
101#include <stdarg.h>
102
103#include <security/pam_appl.h>
104#include <security/openpam.h>
105
106#define PAM_END() do { \
107 int local_ret; \
108 if (pamh != NULL) { \
109 local_ret = pam_setcred(pamh, PAM_DELETE_CRED); \
110 if (local_ret != PAM_SUCCESS) \
111 syslog(LOG_ERR, "pam_setcred: %s", \
112 pam_strerror(pamh, local_ret)); \
113 if (asthem) { \
114 local_ret = pam_close_session(pamh, 0); \
115 if (local_ret != PAM_SUCCESS) \
116 syslog(LOG_ERR, "pam_close_session: %s",\
117 pam_strerror(pamh, local_ret)); \
118 } \
119 local_ret = pam_end(pamh, local_ret); \
120 if (local_ret != PAM_SUCCESS) \
121 syslog(LOG_ERR, "pam_end: %s", \
122 pam_strerror(pamh, local_ret)); \
123 } \
124} while (0)
125
126
127#define PAM_SET_ITEM(what, item) do { \
128 int local_ret; \
129 local_ret = pam_set_item(pamh, what, item); \
130 if (local_ret != PAM_SUCCESS) { \
131 syslog(LOG_ERR, "pam_set_item(" #what "): %s", \
132 pam_strerror(pamh, local_ret)); \
133 errx(1, "pam_set_item(" #what "): %s", \
134 pam_strerror(pamh, local_ret)); \
135 /* NOTREACHED */ \
136 } \
137} while (0)
138
139enum tristate { UNSET, YES, NO };
140
141static pam_handle_t *pamh = NULL;
142static char **environ_pam;
143
144static char *ontty(void);
145static int chshell(const char *);
146static void usage(void) __dead2;
147static void export_pam_environment(void);
148static int ok_to_export(const char *);
149
150extern char **environ;
151
152int
153main(int argc, char *argv[])
154{
155 static char *cleanenv;
156 struct passwd *pwd;
157 struct pam_conv conv = { openpam_ttyconv, NULL };
158 enum tristate iscsh;
159 login_cap_t *lc;
160 union {
161 const char **a;
162 char * const *b;
163 } np;
164 uid_t ruid;
165 pid_t child_pid, child_pgrp, pid;
166 int asme, ch, asthem, fastlogin, prio, i, retcode,
167 statusp, setmaclabel;
168 u_int setwhat;
169 char *username, *class, shellbuf[MAXPATHLEN];
170 const char *p, *user, *shell, *mytty, **nargv;
171 struct sigaction sa, sa_int, sa_quit, sa_pipe;
172 int temp, fds[2];
173#ifdef USE_BSM_AUDIT
174 const char *aerr;
175 au_id_t auid;
176#endif
177
178 shell = class = cleanenv = NULL;
179 asme = asthem = fastlogin = statusp = 0;
180 user = "root";
181 iscsh = UNSET;
182 setmaclabel = 0;
183
184 while ((ch = getopt(argc, argv, "-flmsc:")) != -1)
185 switch ((char)ch) {
186 case 'f':
187 fastlogin = 1;
188 break;
189 case '-':
190 case 'l':
191 asme = 0;
192 asthem = 1;
193 break;
194 case 'm':
195 asme = 1;
196 asthem = 0;
197 break;
198 case 's':
199 setmaclabel = 1;
200 break;
201 case 'c':
202 class = optarg;
203 break;
204 case '?':
205 default:
206 usage();
207 /* NOTREACHED */
208 }
209
210 if (optind < argc)
211 user = argv[optind++];
212
213 if (user == NULL)
214 usage();
215 /* NOTREACHED */
216
217 /*
218 * Try to provide more helpful debugging output if su(1) is running
219 * non-setuid, or was run from a file system not mounted setuid.
220 */
221 if (geteuid() != 0)
222 errx(1, "not running setuid");
223
224#ifdef USE_BSM_AUDIT
225 if (getauid(&auid) < 0 && errno != ENOSYS) {
226 syslog(LOG_AUTH | LOG_ERR, "getauid: %s", strerror(errno));
227 errx(1, "Permission denied");
228 }
229#endif
230 if (strlen(user) > MAXLOGNAME - 1) {
231#ifdef USE_BSM_AUDIT
232 if (audit_submit(AUE_su, auid,
233 1, EPERM, "username too long: '%s'", user))
234 errx(1, "Permission denied");
235#endif
236 errx(1, "username too long");
237 }
238
239 nargv = malloc(sizeof(char *) * (size_t)(argc + 4));
240 if (nargv == NULL)
241 errx(1, "malloc failure");
242
243 nargv[argc + 3] = NULL;
244 for (i = argc; i >= optind; i--)
245 nargv[i + 3] = argv[i];
246 np.a = &nargv[i + 3];
247
248 argv += optind;
249
250 errno = 0;
251 prio = getpriority(PRIO_PROCESS, 0);
252 if (errno)
253 prio = 0;
254
255 setpriority(PRIO_PROCESS, 0, -2);
256 openlog("su", LOG_CONS, LOG_AUTH);
257
258 /* get current login name, real uid and shell */
259 ruid = getuid();
260 username = getlogin();
261 pwd = getpwnam(username);
262 if (username == NULL || pwd == NULL || pwd->pw_uid != ruid)
263 pwd = getpwuid(ruid);
264 if (pwd == NULL) {
265#ifdef USE_BSM_AUDIT
266 if (audit_submit(AUE_su, auid, 1, EPERM,
267 "unable to determine invoking subject: '%s'", username))
268 errx(1, "Permission denied");
269#endif
270 errx(1, "who are you?");
271 }
272
273 username = strdup(pwd->pw_name);
274 if (username == NULL)
275 err(1, "strdup failure");
276
277 if (asme) {
278 if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') {
279 /* must copy - pwd memory is recycled */
280 shell = strncpy(shellbuf, pwd->pw_shell,
281 sizeof(shellbuf));
282 shellbuf[sizeof(shellbuf) - 1] = '\0';
283 }
284 else {
285 shell = _PATH_BSHELL;
286 iscsh = NO;
287 }
288 }
289
290 /* Do the whole PAM startup thing */
291 retcode = pam_start("su", user, &conv, &pamh);
292 if (retcode != PAM_SUCCESS) {
293 syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, retcode));
294 errx(1, "pam_start: %s", pam_strerror(pamh, retcode));
295 }
296
297 PAM_SET_ITEM(PAM_RUSER, username);
298
299 mytty = ttyname(STDERR_FILENO);
300 if (!mytty)
301 mytty = "tty";
302 PAM_SET_ITEM(PAM_TTY, mytty);
303
304 retcode = pam_authenticate(pamh, 0);
305 if (retcode != PAM_SUCCESS) {
306#ifdef USE_BSM_AUDIT
307 if (audit_submit(AUE_su, auid, 1, EPERM, "bad su %s to %s on %s",
308 username, user, mytty))
309 errx(1, "Permission denied");
310#endif
311 syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s on %s",
312 username, user, mytty);
313 errx(1, "Sorry");
314 }
315#ifdef USE_BSM_AUDIT
316 if (audit_submit(AUE_su, auid, 0, 0, "successful authentication"))
317 errx(1, "Permission denied");
318#endif
319 retcode = pam_get_item(pamh, PAM_USER, (const void **)&p);
320 if (retcode == PAM_SUCCESS)
321 user = p;
322 else
323 syslog(LOG_ERR, "pam_get_item(PAM_USER): %s",
324 pam_strerror(pamh, retcode));
325 pwd = getpwnam(user);
326 if (pwd == NULL) {
327#ifdef USE_BSM_AUDIT
328 if (audit_submit(AUE_su, auid, 1, EPERM,
329 "unknown subject: %s", user))
330 errx(1, "Permission denied");
331#endif
332 errx(1, "unknown login: %s", user);
333 }
334
335 retcode = pam_acct_mgmt(pamh, 0);
336 if (retcode == PAM_NEW_AUTHTOK_REQD) {
337 retcode = pam_chauthtok(pamh,
338 PAM_CHANGE_EXPIRED_AUTHTOK);
339 if (retcode != PAM_SUCCESS) {
340#ifdef USE_BSM_AUDIT
341 aerr = pam_strerror(pamh, retcode);
342 if (aerr == NULL)
343 aerr = "Unknown PAM error";
344 if (audit_submit(AUE_su, auid, 1, EPERM,
345 "pam_chauthtok: %s", aerr))
346 errx(1, "Permission denied");
347#endif
348 syslog(LOG_ERR, "pam_chauthtok: %s",
349 pam_strerror(pamh, retcode));
350 errx(1, "Sorry");
351 }
352 }
353 if (retcode != PAM_SUCCESS) {
354#ifdef USE_BSM_AUDIT
355 if (audit_submit(AUE_su, auid, 1, EPERM, "pam_acct_mgmt: %s",
356 pam_strerror(pamh, retcode)))
357 errx(1, "Permission denied");
358#endif
359 syslog(LOG_ERR, "pam_acct_mgmt: %s",
360 pam_strerror(pamh, retcode));
361 errx(1, "Sorry");
362 }
363
364 /* get target login information */
365 if (class == NULL)
366 lc = login_getpwclass(pwd);
367 else {
368 if (ruid != 0) {
369#ifdef USE_BSM_AUDIT
370 if (audit_submit(AUE_su, auid, 1, EPERM,
371 "only root may use -c"))
372 errx(1, "Permission denied");
373#endif
374 errx(1, "only root may use -c");
375 }
376 lc = login_getclass(class);
377 if (lc == NULL)
378 errx(1, "unknown class: %s", class);
379 }
380
381 /* if asme and non-standard target shell, must be root */
382 if (asme) {
383 if (ruid != 0 && !chshell(pwd->pw_shell))
384 errx(1, "permission denied (shell)");
385 }
386 else if (pwd->pw_shell && *pwd->pw_shell) {
387 shell = pwd->pw_shell;
388 iscsh = UNSET;
389 }
390 else {
391 shell = _PATH_BSHELL;
392 iscsh = NO;
393 }
394
395 /* if we're forking a csh, we want to slightly muck the args */
396 if (iscsh == UNSET) {
397 p = strrchr(shell, '/');
398 if (p)
399 ++p;
400 else
401 p = shell;
402 iscsh = strcmp(p, "csh") ? (strcmp(p, "tcsh") ? NO : YES) : YES;
403 }
404 setpriority(PRIO_PROCESS, 0, prio);
405
406 /*
407 * PAM modules might add supplementary groups in pam_setcred(), so
408 * initialize them first.
409 */
410 if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0)
411 err(1, "setusercontext");
412
413 retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
414 if (retcode != PAM_SUCCESS) {
415 syslog(LOG_ERR, "pam_setcred: %s",
416 pam_strerror(pamh, retcode));
417 errx(1, "failed to establish credentials.");
418 }
419 if (asthem) {
420 retcode = pam_open_session(pamh, 0);
421 if (retcode != PAM_SUCCESS) {
422 syslog(LOG_ERR, "pam_open_session: %s",
423 pam_strerror(pamh, retcode));
424 errx(1, "failed to open session.");
425 }
426 }
427
428 /*
429 * We must fork() before setuid() because we need to call
430 * pam_setcred(pamh, PAM_DELETE_CRED) as root.
431 */
432 sa.sa_flags = SA_RESTART;
433 sa.sa_handler = SIG_IGN;
434 sigemptyset(&sa.sa_mask);
435 sigaction(SIGINT, &sa, &sa_int);
436 sigaction(SIGQUIT, &sa, &sa_quit);
437 sigaction(SIGPIPE, &sa, &sa_pipe);
438 sa.sa_handler = SIG_DFL;
439 sigaction(SIGTSTP, &sa, NULL);
440 statusp = 1;
441 if (pipe(fds) == -1) {
442 PAM_END();
443 err(1, "pipe");
444 }
445 child_pid = fork();
446 switch (child_pid) {
447 default:
448 sa.sa_handler = SIG_IGN;
449 sigaction(SIGTTOU, &sa, NULL);
450 close(fds[0]);
451 setpgid(child_pid, child_pid);
452 if (tcgetpgrp(STDERR_FILENO) == getpgrp())
453 tcsetpgrp(STDERR_FILENO, child_pid);
454 close(fds[1]);
455 sigaction(SIGPIPE, &sa_pipe, NULL);
456 while ((pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) {
457 if (WIFSTOPPED(statusp)) {
458 child_pgrp = getpgid(child_pid);
459 if (tcgetpgrp(STDERR_FILENO) == child_pgrp)
460 tcsetpgrp(STDERR_FILENO, getpgrp());
461 kill(getpid(), SIGSTOP);
462 if (tcgetpgrp(STDERR_FILENO) == getpgrp()) {
463 child_pgrp = getpgid(child_pid);
464 tcsetpgrp(STDERR_FILENO, child_pgrp);
465 }
466 kill(child_pid, SIGCONT);
467 statusp = 1;
468 continue;
469 }
470 break;
471 }
472 child_pgrp = getpgid(child_pid);
473 if (tcgetpgrp(STDERR_FILENO) == child_pgrp)
474 tcsetpgrp(STDERR_FILENO, getpgrp());
475 if (pid == -1)
476 err(1, "waitpid");
477 PAM_END();
478 exit(WEXITSTATUS(statusp));
479 case -1:
480 PAM_END();
481 err(1, "fork");
482 case 0:
483 close(fds[1]);
484 read(fds[0], &temp, 1);
485 close(fds[0]);
486 sigaction(SIGPIPE, &sa_pipe, NULL);
487 sigaction(SIGINT, &sa_int, NULL);
488 sigaction(SIGQUIT, &sa_quit, NULL);
489
490 /*
491 * Set all user context except for: Environmental variables
492 * Umask Login records (wtmp, etc) Path
493 */
494 setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK |
495 LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP |
496 LOGIN_SETMAC);
497 /*
498 * If -s is present, also set the MAC label.
499 */
500 if (setmaclabel)
501 setwhat |= LOGIN_SETMAC;
502 /*
503 * Don't touch resource/priority settings if -m has been used
504 * or -l and -c hasn't, and we're not su'ing to root.
505 */
506 if ((asme || (!asthem && class == NULL)) && pwd->pw_uid)
507 setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES);
508 if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0)
509 err(1, "setusercontext");
510
511 if (!asme) {
512 if (asthem) {
513 p = getenv("TERM");
514 environ = &cleanenv;
515 }
516
517 if (asthem || pwd->pw_uid)
518 setenv("USER", pwd->pw_name, 1);
519 setenv("HOME", pwd->pw_dir, 1);
520 setenv("SHELL", shell, 1);
521
522 if (asthem) {
523 /*
524 * Add any environmental variables that the
525 * PAM modules may have set.
526 */
527 environ_pam = pam_getenvlist(pamh);
528 if (environ_pam)
529 export_pam_environment();
530
531 /* set the su'd user's environment & umask */
532 setusercontext(lc, pwd, pwd->pw_uid,
533 LOGIN_SETPATH | LOGIN_SETUMASK |
534 LOGIN_SETENV);
535 if (p)
536 setenv("TERM", p, 1);
537
538 p = pam_getenv(pamh, "HOME");
539 if (chdir(p ? p : pwd->pw_dir) < 0)
540 errx(1, "no directory");
541 }
542 }
543 login_close(lc);
544
545 if (iscsh == YES) {
546 if (fastlogin)
547 *np.a-- = "-f";
548 if (asme)
549 *np.a-- = "-m";
550 }
551 /* csh strips the first character... */
552 *np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su";
553
554 if (ruid != 0)
555 syslog(LOG_NOTICE, "%s to %s%s", username, user,
556 ontty());
557
558 execv(shell, np.b);
559 err(1, "%s", shell);
560 }
561}
562
563static void
564export_pam_environment(void)
565{
566 char **pp;
567
568 for (pp = environ_pam; *pp != NULL; pp++) {
569 if (ok_to_export(*pp))
570 (void)putenv(*pp);
571 else
572 free(*pp);
570 putenv(*pp);
571 free(*pp);
573 }
574}
575
576/*
577 * Sanity checks on PAM environmental variables:
578 * - Make sure there is an '=' in the string.
579 * - Make sure the string doesn't run on too long.
580 * - Do not export certain variables. This list was taken from the
581 * Solaris pam_putenv(3) man page.
582 * Note that if the user is chrooted, PAM may have a better idea than we
583 * do of where her home directory is.
584 */
585static int
586ok_to_export(const char *s)
587{
588 static const char *noexport[] = {
589 "SHELL", /* "HOME", */ "LOGNAME", "MAIL", "CDPATH",
590 "IFS", "PATH", NULL
591 };
592 const char **pp;
593 size_t n;
594
595 if (strlen(s) > 1024 || strchr(s, '=') == NULL)
596 return 0;
597 if (strncmp(s, "LD_", 3) == 0)
598 return 0;
599 for (pp = noexport; *pp != NULL; pp++) {
600 n = strlen(*pp);
601 if (s[n] == '=' && strncmp(s, *pp, n) == 0)
602 return 0;
603 }
604 return 1;
605}
606
607static void
608usage(void)
609{
610
611 fprintf(stderr, "usage: su [-] [-flms] [-c class] [login [args]]\n");
612 exit(1);
613 /* NOTREACHED */
614}
615
616static int
617chshell(const char *sh)
618{
619 int r;
620 char *cp;
621
622 r = 0;
623 setusershell();
624 while ((cp = getusershell()) != NULL && !r)
625 r = (strcmp(cp, sh) == 0);
626 endusershell();
627 return r;
628}
629
630static char *
631ontty(void)
632{
633 char *p;
634 static char buf[MAXPATHLEN + 4];
635
636 buf[0] = 0;
637 p = ttyname(STDERR_FILENO);
638 if (p)
639 snprintf(buf, sizeof(buf), " on %s", p);
640 return buf;
641}
572 }
573}
574
575/*
576 * Sanity checks on PAM environmental variables:
577 * - Make sure there is an '=' in the string.
578 * - Make sure the string doesn't run on too long.
579 * - Do not export certain variables. This list was taken from the
580 * Solaris pam_putenv(3) man page.
581 * Note that if the user is chrooted, PAM may have a better idea than we
582 * do of where her home directory is.
583 */
584static int
585ok_to_export(const char *s)
586{
587 static const char *noexport[] = {
588 "SHELL", /* "HOME", */ "LOGNAME", "MAIL", "CDPATH",
589 "IFS", "PATH", NULL
590 };
591 const char **pp;
592 size_t n;
593
594 if (strlen(s) > 1024 || strchr(s, '=') == NULL)
595 return 0;
596 if (strncmp(s, "LD_", 3) == 0)
597 return 0;
598 for (pp = noexport; *pp != NULL; pp++) {
599 n = strlen(*pp);
600 if (s[n] == '=' && strncmp(s, *pp, n) == 0)
601 return 0;
602 }
603 return 1;
604}
605
606static void
607usage(void)
608{
609
610 fprintf(stderr, "usage: su [-] [-flms] [-c class] [login [args]]\n");
611 exit(1);
612 /* NOTREACHED */
613}
614
615static int
616chshell(const char *sh)
617{
618 int r;
619 char *cp;
620
621 r = 0;
622 setusershell();
623 while ((cp = getusershell()) != NULL && !r)
624 r = (strcmp(cp, sh) == 0);
625 endusershell();
626 return r;
627}
628
629static char *
630ontty(void)
631{
632 char *p;
633 static char buf[MAXPATHLEN + 4];
634
635 buf[0] = 0;
636 p = ttyname(STDERR_FILENO);
637 if (p)
638 snprintf(buf, sizeof(buf), " on %s", p);
639 return buf;
640}