]> git.saurik.com Git - apple/shell_cmds.git/blame - su/su.c
shell_cmds-207.11.1.tar.gz
[apple/shell_cmds.git] / su / su.c
CommitLineData
44bd5ea7 1/*
ddb4a88b
A
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/*-
c0fcf4e1
A
32 * Copyright (c) 1988, 1993, 1994
33 * The Regents of the University of California. All rights reserved.
44bd5ea7
A
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
44bd5ea7 64#ifndef lint
c0fcf4e1
A
65static const char copyright[] =
66"@(#) Copyright (c) 1988, 1993, 1994\n\
67 The Regents of the University of California. All rights reserved.\n";
44bd5ea7
A
68#endif /* not lint */
69
44bd5ea7 70#if 0
ddb4a88b 71#ifndef lint
c0fcf4e1 72static char sccsid[] = "@(#)su.c 8.3 (Berkeley) 4/2/94";
44bd5ea7 73#endif /* not lint */
ddb4a88b
A
74#endif
75
76#include <sys/cdefs.h>
f14763b6 77__FBSDID("$FreeBSD: src/usr.bin/su/su.c,v 1.91 2009/12/13 03:14:06 delphij Exp $");
44bd5ea7
A
78
79#include <sys/param.h>
80#include <sys/time.h>
81#include <sys/resource.h>
c0fcf4e1
A
82#include <sys/wait.h>
83
ddb4a88b
A
84#ifdef USE_BSM_AUDIT
85#include <bsm/libbsm.h>
86#include <bsm/audit_uevents.h>
87#endif
88
44bd5ea7
A
89#include <err.h>
90#include <errno.h>
91#include <grp.h>
ddb4a88b
A
92#ifndef __APPLE__
93#include <login_cap.h>
94#endif /* !__APPLE__ */
44bd5ea7
A
95#include <paths.h>
96#include <pwd.h>
c0fcf4e1 97#include <signal.h>
44bd5ea7 98#include <stdio.h>
44bd5ea7
A
99#include <stdlib.h>
100#include <string.h>
101#include <syslog.h>
44bd5ea7 102#include <unistd.h>
ddb4a88b
A
103#include <stdarg.h>
104
105#include <security/pam_appl.h>
106#include <security/openpam.h>
107
f14763b6 108#ifdef __APPLE__
1e9ba8f2 109#include <bsm/audit_session.h>
f14763b6
A
110#endif /* __APPLE__ */
111
ddb4a88b
A
112#define PAM_END() do { \
113 int local_ret; \
114 if (pamh != NULL) { \
115 local_ret = pam_setcred(pamh, PAM_DELETE_CRED); \
116 if (local_ret != PAM_SUCCESS) \
117 syslog(LOG_ERR, "pam_setcred: %s", \
118 pam_strerror(pamh, local_ret)); \
119 if (asthem) { \
120 local_ret = pam_close_session(pamh, 0); \
121 if (local_ret != PAM_SUCCESS) \
122 syslog(LOG_ERR, "pam_close_session: %s",\
123 pam_strerror(pamh, local_ret)); \
124 } \
125 local_ret = pam_end(pamh, local_ret); \
126 if (local_ret != PAM_SUCCESS) \
127 syslog(LOG_ERR, "pam_end: %s", \
128 pam_strerror(pamh, local_ret)); \
129 } \
c0fcf4e1
A
130} while (0)
131
132
ddb4a88b
A
133#define PAM_SET_ITEM(what, item) do { \
134 int local_ret; \
135 local_ret = pam_set_item(pamh, what, item); \
136 if (local_ret != PAM_SUCCESS) { \
137 syslog(LOG_ERR, "pam_set_item(" #what "): %s", \
138 pam_strerror(pamh, local_ret)); \
139 errx(1, "pam_set_item(" #what "): %s", \
140 pam_strerror(pamh, local_ret)); \
141 /* NOTREACHED */ \
142 } \
c0fcf4e1
A
143} while (0)
144
145enum tristate { UNSET, YES, NO };
146
147static pam_handle_t *pamh = NULL;
c0fcf4e1
A
148static char **environ_pam;
149
150static char *ontty(void);
ddb4a88b
A
151static int chshell(const char *);
152static void usage(void) __dead2;
153static void export_pam_environment(void);
c0fcf4e1
A
154static int ok_to_export(const char *);
155
156extern char **environ;
44bd5ea7
A
157
158int
c0fcf4e1 159main(int argc, char *argv[])
44bd5ea7 160{
e1a085ba 161 static char *cleanenv;
c0fcf4e1 162 struct passwd *pwd;
ddb4a88b 163 struct pam_conv conv = { openpam_ttyconv, NULL };
c0fcf4e1 164 enum tristate iscsh;
ddb4a88b
A
165#ifndef __APPLE__
166 login_cap_t *lc;
167#endif /* !__APPLE__ */
c0fcf4e1
A
168 union {
169 const char **a;
170 char * const *b;
ddb4a88b 171 } np;
c0fcf4e1 172 uid_t ruid;
ddb4a88b
A
173 pid_t child_pid, child_pgrp, pid;
174 int asme, ch, asthem, fastlogin, prio, i, retcode,
175 statusp, setmaclabel;
176#ifndef __APPLE__
177 u_int setwhat;
178#endif /* !__APPLE__ */
e1a085ba 179 char *username, *class, shellbuf[MAXPATHLEN];
c0fcf4e1 180 const char *p, *user, *shell, *mytty, **nargv;
f14763b6 181 const void *v;
ddb4a88b
A
182 struct sigaction sa, sa_int, sa_quit, sa_pipe;
183 int temp, fds[2];
184#ifdef USE_BSM_AUDIT
185 const char *aerr;
186 au_id_t auid;
187#endif
188#ifdef __APPLE__
189 /* 4043304 */
e1a085ba
A
190 const char *avshell;
191 char avshellbuf[MAXPATHLEN];
ddb4a88b 192#endif /* __APPLE__ */
c0fcf4e1
A
193
194 shell = class = cleanenv = NULL;
195 asme = asthem = fastlogin = statusp = 0;
196 user = "root";
197 iscsh = UNSET;
ddb4a88b 198 setmaclabel = 0;
c0fcf4e1 199
e1a085ba
A
200#ifdef __APPLE__
201 while ((ch = getopt(argc, argv, "-flm")) != -1)
202#else
ddb4a88b 203 while ((ch = getopt(argc, argv, "-flmsc:")) != -1)
e1a085ba 204#endif /* __APPLE__ */
c0fcf4e1 205 switch ((char)ch) {
44bd5ea7
A
206 case 'f':
207 fastlogin = 1;
208 break;
209 case '-':
210 case 'l':
211 asme = 0;
212 asthem = 1;
213 break;
214 case 'm':
215 asme = 1;
216 asthem = 0;
217 break;
e1a085ba 218#ifndef __APPLE__
ddb4a88b
A
219 case 's':
220 setmaclabel = 1;
221 break;
c0fcf4e1
A
222 case 'c':
223 class = optarg;
224 break;
e1a085ba 225#endif /* !__APPLE__ */
44bd5ea7
A
226 case '?':
227 default:
c0fcf4e1 228 usage();
ddb4a88b 229 /* NOTREACHED */
44bd5ea7 230 }
c0fcf4e1
A
231
232 if (optind < argc)
233 user = argv[optind++];
234
235 if (user == NULL)
236 usage();
ddb4a88b 237 /* NOTREACHED */
c0fcf4e1 238
ddb4a88b
A
239 /*
240 * Try to provide more helpful debugging output if su(1) is running
241 * non-setuid, or was run from a file system not mounted setuid.
242 */
243 if (geteuid() != 0)
244 errx(1, "not running setuid");
245
246#ifdef USE_BSM_AUDIT
247 if (getauid(&auid) < 0 && errno != ENOSYS) {
248 syslog(LOG_AUTH | LOG_ERR, "getauid: %s", strerror(errno));
249 errx(1, "Permission denied");
250 }
251#endif
252 if (strlen(user) > MAXLOGNAME - 1) {
253#ifdef USE_BSM_AUDIT
254 if (audit_submit(AUE_su, auid,
f14763b6 255 EPERM, 1, "username too long: '%s'", user))
ddb4a88b
A
256 errx(1, "Permission denied");
257#endif
c0fcf4e1 258 errx(1, "username too long");
ddb4a88b 259 }
c0fcf4e1 260
ddb4a88b 261 nargv = malloc(sizeof(char *) * (size_t)(argc + 4));
1c4c78a5 262 if (nargv == NULL)
c0fcf4e1
A
263 errx(1, "malloc failure");
264
265 nargv[argc + 3] = NULL;
266 for (i = argc; i >= optind; i--)
267 nargv[i + 3] = argv[i];
268 np.a = &nargv[i + 3];
269
44bd5ea7
A
270 argv += optind;
271
272 errno = 0;
273 prio = getpriority(PRIO_PROCESS, 0);
274 if (errno)
275 prio = 0;
44bd5ea7 276
c0fcf4e1
A
277 setpriority(PRIO_PROCESS, 0, -2);
278 openlog("su", LOG_CONS, LOG_AUTH);
279
280 /* get current login name, real uid and shell */
44bd5ea7
A
281 ruid = getuid();
282 username = getlogin();
c0fcf4e1
A
283 pwd = getpwnam(username);
284 if (username == NULL || pwd == NULL || pwd->pw_uid != ruid)
44bd5ea7 285 pwd = getpwuid(ruid);
ddb4a88b
A
286 if (pwd == NULL) {
287#ifdef USE_BSM_AUDIT
f14763b6 288 if (audit_submit(AUE_su, auid, EPERM, 1,
ddb4a88b
A
289 "unable to determine invoking subject: '%s'", username))
290 errx(1, "Permission denied");
291#endif
44bd5ea7 292 errx(1, "who are you?");
ddb4a88b 293 }
c0fcf4e1 294
44bd5ea7 295 username = strdup(pwd->pw_name);
1c4c78a5 296 if (username == NULL)
c0fcf4e1 297 err(1, "strdup failure");
44bd5ea7
A
298
299 if (asme) {
c0fcf4e1
A
300 if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') {
301 /* must copy - pwd memory is recycled */
44bd5ea7 302 shell = strncpy(shellbuf, pwd->pw_shell,
c0fcf4e1 303 sizeof(shellbuf));
44bd5ea7 304 shellbuf[sizeof(shellbuf) - 1] = '\0';
c0fcf4e1
A
305 }
306 else {
44bd5ea7
A
307 shell = _PATH_BSHELL;
308 iscsh = NO;
309 }
310 }
44bd5ea7 311
c0fcf4e1
A
312 /* Do the whole PAM startup thing */
313 retcode = pam_start("su", user, &conv, &pamh);
314 if (retcode != PAM_SUCCESS) {
315 syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, retcode));
316 errx(1, "pam_start: %s", pam_strerror(pamh, retcode));
317 }
44bd5ea7 318
ddb4a88b 319 PAM_SET_ITEM(PAM_RUSER, username);
c0fcf4e1
A
320
321 mytty = ttyname(STDERR_FILENO);
322 if (!mytty)
323 mytty = "tty";
324 PAM_SET_ITEM(PAM_TTY, mytty);
325
326 retcode = pam_authenticate(pamh, 0);
327 if (retcode != PAM_SUCCESS) {
ddb4a88b 328#ifdef USE_BSM_AUDIT
f14763b6 329 if (audit_submit(AUE_su, auid, EPERM, 1, "bad su %s to %s on %s",
ddb4a88b
A
330 username, user, mytty))
331 errx(1, "Permission denied");
332#endif
333 syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s on %s",
334 username, user, mytty);
c0fcf4e1
A
335 errx(1, "Sorry");
336 }
ddb4a88b
A
337#ifdef USE_BSM_AUDIT
338 if (audit_submit(AUE_su, auid, 0, 0, "successful authentication"))
339 errx(1, "Permission denied");
340#endif
f14763b6 341 retcode = pam_get_item(pamh, PAM_USER, &v);
c0fcf4e1 342 if (retcode == PAM_SUCCESS)
f14763b6 343 user = v;
c0fcf4e1
A
344 else
345 syslog(LOG_ERR, "pam_get_item(PAM_USER): %s",
346 pam_strerror(pamh, retcode));
ddb4a88b
A
347 pwd = getpwnam(user);
348 if (pwd == NULL) {
349#ifdef USE_BSM_AUDIT
f14763b6 350 if (audit_submit(AUE_su, auid, EPERM, 1,
ddb4a88b
A
351 "unknown subject: %s", user))
352 errx(1, "Permission denied");
353#endif
354 errx(1, "unknown login: %s", user);
355 }
c0fcf4e1
A
356
357 retcode = pam_acct_mgmt(pamh, 0);
358 if (retcode == PAM_NEW_AUTHTOK_REQD) {
359 retcode = pam_chauthtok(pamh,
360 PAM_CHANGE_EXPIRED_AUTHTOK);
361 if (retcode != PAM_SUCCESS) {
ddb4a88b
A
362#ifdef USE_BSM_AUDIT
363 aerr = pam_strerror(pamh, retcode);
364 if (aerr == NULL)
365 aerr = "Unknown PAM error";
f14763b6 366 if (audit_submit(AUE_su, auid, EPERM, 1,
ddb4a88b
A
367 "pam_chauthtok: %s", aerr))
368 errx(1, "Permission denied");
369#endif
c0fcf4e1
A
370 syslog(LOG_ERR, "pam_chauthtok: %s",
371 pam_strerror(pamh, retcode));
372 errx(1, "Sorry");
44bd5ea7
A
373 }
374 }
c0fcf4e1 375 if (retcode != PAM_SUCCESS) {
ddb4a88b 376#ifdef USE_BSM_AUDIT
f14763b6 377 if (audit_submit(AUE_su, auid, EPERM, 1, "pam_acct_mgmt: %s",
ddb4a88b
A
378 pam_strerror(pamh, retcode)))
379 errx(1, "Permission denied");
380#endif
c0fcf4e1
A
381 syslog(LOG_ERR, "pam_acct_mgmt: %s",
382 pam_strerror(pamh, retcode));
383 errx(1, "Sorry");
384 }
44bd5ea7 385
ddb4a88b
A
386#ifndef __APPLE__
387 /* get target login information */
388 if (class == NULL)
389 lc = login_getpwclass(pwd);
390 else {
391 if (ruid != 0) {
392#ifdef USE_BSM_AUDIT
f14763b6 393 if (audit_submit(AUE_su, auid, EPERM, 1,
ddb4a88b
A
394 "only root may use -c"))
395 errx(1, "Permission denied");
396#endif
397 errx(1, "only root may use -c");
398 }
399 lc = login_getclass(class);
400 if (lc == NULL)
401 errx(1, "unknown class: %s", class);
402 }
403#endif /* !__APPLE__ */
c0fcf4e1
A
404
405 /* if asme and non-standard target shell, must be root */
44bd5ea7 406 if (asme) {
1c4c78a5 407 if (ruid != 0 && !chshell(pwd->pw_shell))
ddb4a88b 408 errx(1, "permission denied (shell)");
c0fcf4e1
A
409 }
410 else if (pwd->pw_shell && *pwd->pw_shell) {
ddb4a88b
A
411#ifdef __APPLE__
412 /* 3825554 */
1c4c78a5
A
413 shell = strncpy(shellbuf, pwd->pw_shell, sizeof(shellbuf));
414 shellbuf[sizeof(shellbuf) - 1] = '\0';
ddb4a88b
A
415#else
416 shell = pwd->pw_shell;
417#endif /* __APPLE__ */
44bd5ea7 418 iscsh = UNSET;
c0fcf4e1
A
419 }
420 else {
44bd5ea7
A
421 shell = _PATH_BSHELL;
422 iscsh = NO;
423 }
424
44bd5ea7 425 /* if we're forking a csh, we want to slightly muck the args */
c0fcf4e1 426 if (iscsh == UNSET) {
ddb4a88b
A
427 p = strrchr(shell, '/');
428 if (p)
429 ++p;
430 else
431 p = shell;
432 iscsh = strcmp(p, "csh") ? (strcmp(p, "tcsh") ? NO : YES) : YES;
44bd5ea7 433 }
c0fcf4e1 434 setpriority(PRIO_PROCESS, 0, prio);
44bd5ea7 435
c0fcf4e1
A
436 /*
437 * PAM modules might add supplementary groups in pam_setcred(), so
438 * initialize them first.
439 */
ddb4a88b
A
440#ifdef __APPLE__
441 if (initgroups(user, pwd->pw_gid))
442 err(1, "initgroups");
443#else
444 if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0)
445 err(1, "setusercontext");
446#endif /* __APPLE__ */
44bd5ea7 447
f14763b6
A
448#ifdef __APPLE__
449 /* 8530846 */
450 if (asthem) {
1e9ba8f2
A
451 auditinfo_addr_t auinfo = {
452 .ai_termid = { .at_type = AU_IPv4 },
453 .ai_asid = AU_ASSIGN_ASID,
454 .ai_auid = getuid(),
455 .ai_flags = 0,
456 };
457 if (setaudit_addr(&auinfo, sizeof(auinfo)) == 0) {
458 char session[16];
459 snprintf(session, sizeof(session), "%x", auinfo.ai_asid);
460 setenv("SECURITYSESSIONID", session, 1);
461 } else {
f14763b6 462 errx(1, "failed to create session.");
1e9ba8f2 463 }
f14763b6
A
464 }
465#endif /* __APPLE__ */
466
c0fcf4e1 467 retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
ddb4a88b
A
468 if (retcode != PAM_SUCCESS) {
469 syslog(LOG_ERR, "pam_setcred: %s",
c0fcf4e1 470 pam_strerror(pamh, retcode));
ddb4a88b
A
471 errx(1, "failed to establish credentials.");
472 }
473 if (asthem) {
474 retcode = pam_open_session(pamh, 0);
475 if (retcode != PAM_SUCCESS) {
476 syslog(LOG_ERR, "pam_open_session: %s",
477 pam_strerror(pamh, retcode));
478 errx(1, "failed to open session.");
479 }
480 }
44bd5ea7 481
c0fcf4e1
A
482 /*
483 * We must fork() before setuid() because we need to call
484 * pam_setcred(pamh, PAM_DELETE_CRED) as root.
485 */
ddb4a88b
A
486 sa.sa_flags = SA_RESTART;
487 sa.sa_handler = SIG_IGN;
488 sigemptyset(&sa.sa_mask);
489 sigaction(SIGINT, &sa, &sa_int);
490 sigaction(SIGQUIT, &sa, &sa_quit);
491 sigaction(SIGPIPE, &sa, &sa_pipe);
492 sa.sa_handler = SIG_DFL;
493 sigaction(SIGTSTP, &sa, NULL);
c0fcf4e1 494 statusp = 1;
ddb4a88b
A
495 if (pipe(fds) == -1) {
496 PAM_END();
497 err(1, "pipe");
498 }
c0fcf4e1
A
499 child_pid = fork();
500 switch (child_pid) {
501 default:
ddb4a88b
A
502 sa.sa_handler = SIG_IGN;
503 sigaction(SIGTTOU, &sa, NULL);
504 close(fds[0]);
505 setpgid(child_pid, child_pid);
506 if (tcgetpgrp(STDERR_FILENO) == getpgrp())
507 tcsetpgrp(STDERR_FILENO, child_pid);
508 close(fds[1]);
509 sigaction(SIGPIPE, &sa_pipe, NULL);
510 while ((pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) {
c0fcf4e1 511 if (WIFSTOPPED(statusp)) {
ddb4a88b
A
512 child_pgrp = getpgid(child_pid);
513 if (tcgetpgrp(STDERR_FILENO) == child_pgrp)
514 tcsetpgrp(STDERR_FILENO, getpgrp());
c0fcf4e1 515 kill(getpid(), SIGSTOP);
ddb4a88b
A
516 if (tcgetpgrp(STDERR_FILENO) == getpgrp()) {
517 child_pgrp = getpgid(child_pid);
518 tcsetpgrp(STDERR_FILENO, child_pgrp);
519 }
520 kill(child_pid, SIGCONT);
c0fcf4e1
A
521 statusp = 1;
522 continue;
523 }
524 break;
525 }
ddb4a88b
A
526 tcsetpgrp(STDERR_FILENO, getpgrp());
527 if (pid == -1)
c0fcf4e1
A
528 err(1, "waitpid");
529 PAM_END();
530 exit(WEXITSTATUS(statusp));
531 case -1:
c0fcf4e1 532 PAM_END();
ddb4a88b 533 err(1, "fork");
c0fcf4e1 534 case 0:
ddb4a88b
A
535 close(fds[1]);
536 read(fds[0], &temp, 1);
537 close(fds[0]);
538 sigaction(SIGPIPE, &sa_pipe, NULL);
539 sigaction(SIGINT, &sa_int, NULL);
540 sigaction(SIGQUIT, &sa_quit, NULL);
541
542#ifdef __APPLE__
543 if (setgid(pwd->pw_gid))
c0fcf4e1 544 err(1, "setgid");
e1a085ba 545 /* Call initgroups(2) after setgid(2) to re-establish memberd */
ddb4a88b 546 if (initgroups(user, pwd->pw_gid))
e1a085ba 547 err(1, "initgroups");
ddb4a88b 548 if (setuid(pwd->pw_uid))
c0fcf4e1 549 err(1, "setuid");
ddb4a88b
A
550#else
551 /*
552 * Set all user context except for: Environmental variables
553 * Umask Login records (wtmp, etc) Path
554 */
555 setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK |
556 LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP |
557 LOGIN_SETMAC);
558 /*
559 * If -s is present, also set the MAC label.
560 */
561 if (setmaclabel)
562 setwhat |= LOGIN_SETMAC;
563 /*
564 * Don't touch resource/priority settings if -m has been used
565 * or -l and -c hasn't, and we're not su'ing to root.
566 */
567 if ((asme || (!asthem && class == NULL)) && pwd->pw_uid)
568 setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES);
569 if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0)
570 err(1, "setusercontext");
571#endif /* __APPLE__ */
c0fcf4e1
A
572
573 if (!asme) {
574 if (asthem) {
575 p = getenv("TERM");
e1a085ba
A
576 environ = &cleanenv;
577 }
c0fcf4e1 578
e1a085ba
A
579 if (asthem || pwd->pw_uid)
580 setenv("USER", pwd->pw_name, 1);
581 setenv("HOME", pwd->pw_dir, 1);
582 setenv("SHELL", shell, 1);
ddb4a88b
A
583#ifdef __APPLE__
584 unsetenv("TMPDIR");
585#endif /* __APPLE__ */
e1a085ba
A
586
587 if (asthem) {
c0fcf4e1
A
588 /*
589 * Add any environmental variables that the
590 * PAM modules may have set.
591 */
592 environ_pam = pam_getenvlist(pamh);
593 if (environ_pam)
594 export_pam_environment();
595
e1a085ba
A
596#ifdef __APPLE__
597 /* 5276965: As documented, set $PATH. */
598 setenv("PATH", "/bin:/usr/bin", 1);
599#else
600 /* set the su'd user's environment & umask */
601 setusercontext(lc, pwd, pwd->pw_uid,
602 LOGIN_SETPATH | LOGIN_SETUMASK |
603 LOGIN_SETENV);
ddb4a88b 604#endif /* __APPLE__ */
c0fcf4e1
A
605 if (p)
606 setenv("TERM", p, 1);
e1a085ba
A
607
608 p = pam_getenv(pamh, "HOME");
609 if (chdir(p ? p : pwd->pw_dir) < 0)
c0fcf4e1
A
610 errx(1, "no directory");
611 }
c0fcf4e1 612 }
ddb4a88b
A
613#ifndef __APPLE__
614 login_close(lc);
615#endif /* !__APPLE__ */
44bd5ea7 616
c0fcf4e1
A
617 if (iscsh == YES) {
618 if (fastlogin)
619 *np.a-- = "-f";
620 if (asme)
621 *np.a-- = "-m";
622 }
ddb4a88b
A
623#ifdef __APPLE__
624 /* 4043304 */
625 if ((p = strrchr(shell, '/')) != NULL)
626 avshell = p + 1;
627 else
628 avshell = shell;
e1a085ba
A
629
630 if (asthem) {
631 avshellbuf[0] = '-';
632 strlcpy(avshellbuf+1, avshell, sizeof(avshellbuf) - 1);
633 avshell = avshellbuf;
634 }
635
9bafe280 636 /* csh *no longer* strips the first character... */
e1a085ba 637 *np.a = avshell;
ddb4a88b
A
638#else
639 /* csh strips the first character... */
640 *np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su";
641#endif /* __APPLE__ */
c0fcf4e1
A
642
643 if (ruid != 0)
644 syslog(LOG_NOTICE, "%s to %s%s", username, user,
645 ontty());
646
647 execv(shell, np.b);
648 err(1, "%s", shell);
649 }
44bd5ea7
A
650}
651
ddb4a88b 652static void
c0fcf4e1 653export_pam_environment(void)
44bd5ea7 654{
c0fcf4e1 655 char **pp;
ddb4a88b 656 char *p;
44bd5ea7 657
c0fcf4e1 658 for (pp = environ_pam; *pp != NULL; pp++) {
ddb4a88b
A
659 if (ok_to_export(*pp)) {
660 p = strchr(*pp, '=');
661 *p = '\0';
662 setenv(*pp, p + 1, 1);
663 }
c0fcf4e1 664 free(*pp);
44bd5ea7 665 }
c0fcf4e1 666}
44bd5ea7 667
c0fcf4e1
A
668/*
669 * Sanity checks on PAM environmental variables:
670 * - Make sure there is an '=' in the string.
671 * - Make sure the string doesn't run on too long.
672 * - Do not export certain variables. This list was taken from the
673 * Solaris pam_putenv(3) man page.
ddb4a88b
A
674 * Note that if the user is chrooted, PAM may have a better idea than we
675 * do of where her home directory is.
c0fcf4e1
A
676 */
677static int
678ok_to_export(const char *s)
679{
680 static const char *noexport[] = {
ddb4a88b 681 "SHELL", /* "HOME", */ "LOGNAME", "MAIL", "CDPATH",
c0fcf4e1
A
682 "IFS", "PATH", NULL
683 };
684 const char **pp;
685 size_t n;
686
687 if (strlen(s) > 1024 || strchr(s, '=') == NULL)
688 return 0;
689 if (strncmp(s, "LD_", 3) == 0)
690 return 0;
691 for (pp = noexport; *pp != NULL; pp++) {
692 n = strlen(*pp);
693 if (s[n] == '=' && strncmp(s, *pp, n) == 0)
694 return 0;
44bd5ea7 695 }
c0fcf4e1
A
696 return 1;
697}
44bd5ea7 698
c0fcf4e1
A
699static void
700usage(void)
701{
44bd5ea7 702
e1a085ba
A
703#ifdef __APPLE__
704 fprintf(stderr, "usage: su [-] [-flm] [login [args]]\n");
705#else
ddb4a88b 706 fprintf(stderr, "usage: su [-] [-flms] [-c class] [login [args]]\n");
e1a085ba 707#endif /* __APPLE__ */
c0fcf4e1 708 exit(1);
ddb4a88b 709 /* NOTREACHED */
44bd5ea7
A
710}
711
712static int
ddb4a88b 713chshell(const char *sh)
44bd5ea7 714{
c0fcf4e1
A
715 int r;
716 char *cp;
717
718 r = 0;
719 setusershell();
ddb4a88b
A
720 while ((cp = getusershell()) != NULL && !r)
721 r = (strcmp(cp, sh) == 0);
c0fcf4e1
A
722 endusershell();
723 return r;
724}
725
726static char *
727ontty(void)
728{
729 char *p;
730 static char buf[MAXPATHLEN + 4];
731
732 buf[0] = 0;
733 p = ttyname(STDERR_FILENO);
734 if (p)
735 snprintf(buf, sizeof(buf), " on %s", p);
736 return buf;
44bd5ea7 737}