]> git.saurik.com Git - apple/shell_cmds.git/blob - su/su.c
shell_cmds-198.tar.gz
[apple/shell_cmds.git] / su / su.c
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
65 static 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
72 static 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: src/usr.bin/su/su.c,v 1.91 2009/12/13 03:14:06 delphij Exp $");
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 #ifndef __APPLE__
93 #include <login_cap.h>
94 #endif /* !__APPLE__ */
95 #include <paths.h>
96 #include <pwd.h>
97 #include <signal.h>
98 #include <stdio.h>
99 #include <stdlib.h>
100 #include <string.h>
101 #include <syslog.h>
102 #include <unistd.h>
103 #include <stdarg.h>
104
105 #include <security/pam_appl.h>
106 #include <security/openpam.h>
107
108 #ifdef __APPLE__
109 #include <bsm/audit_session.h>
110 #endif /* __APPLE__ */
111
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 } \
130 } while (0)
131
132
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 } \
143 } while (0)
144
145 enum tristate { UNSET, YES, NO };
146
147 static pam_handle_t *pamh = NULL;
148 static char **environ_pam;
149
150 static char *ontty(void);
151 static int chshell(const char *);
152 static void usage(void) __dead2;
153 static void export_pam_environment(void);
154 static int ok_to_export(const char *);
155
156 extern char **environ;
157
158 int
159 main(int argc, char *argv[])
160 {
161 static char *cleanenv;
162 struct passwd *pwd;
163 struct pam_conv conv = { openpam_ttyconv, NULL };
164 enum tristate iscsh;
165 #ifndef __APPLE__
166 login_cap_t *lc;
167 #endif /* !__APPLE__ */
168 union {
169 const char **a;
170 char * const *b;
171 } np;
172 uid_t ruid;
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__ */
179 char *username, *class, shellbuf[MAXPATHLEN];
180 const char *p, *user, *shell, *mytty, **nargv;
181 const void *v;
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 */
190 const char *avshell;
191 char avshellbuf[MAXPATHLEN];
192 #endif /* __APPLE__ */
193
194 shell = class = cleanenv = NULL;
195 asme = asthem = fastlogin = statusp = 0;
196 user = "root";
197 iscsh = UNSET;
198 setmaclabel = 0;
199
200 #ifdef __APPLE__
201 while ((ch = getopt(argc, argv, "-flm")) != -1)
202 #else
203 while ((ch = getopt(argc, argv, "-flmsc:")) != -1)
204 #endif /* __APPLE__ */
205 switch ((char)ch) {
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;
218 #ifndef __APPLE__
219 case 's':
220 setmaclabel = 1;
221 break;
222 case 'c':
223 class = optarg;
224 break;
225 #endif /* !__APPLE__ */
226 case '?':
227 default:
228 usage();
229 /* NOTREACHED */
230 }
231
232 if (optind < argc)
233 user = argv[optind++];
234
235 if (user == NULL)
236 usage();
237 /* NOTREACHED */
238
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,
255 EPERM, 1, "username too long: '%s'", user))
256 errx(1, "Permission denied");
257 #endif
258 errx(1, "username too long");
259 }
260
261 nargv = malloc(sizeof(char *) * (size_t)(argc + 4));
262 if (nargv == NULL)
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
270 argv += optind;
271
272 errno = 0;
273 prio = getpriority(PRIO_PROCESS, 0);
274 if (errno)
275 prio = 0;
276
277 setpriority(PRIO_PROCESS, 0, -2);
278 openlog("su", LOG_CONS, LOG_AUTH);
279
280 /* get current login name, real uid and shell */
281 ruid = getuid();
282 username = getlogin();
283 pwd = getpwnam(username);
284 if (username == NULL || pwd == NULL || pwd->pw_uid != ruid)
285 pwd = getpwuid(ruid);
286 if (pwd == NULL) {
287 #ifdef USE_BSM_AUDIT
288 if (audit_submit(AUE_su, auid, EPERM, 1,
289 "unable to determine invoking subject: '%s'", username))
290 errx(1, "Permission denied");
291 #endif
292 errx(1, "who are you?");
293 }
294
295 username = strdup(pwd->pw_name);
296 if (username == NULL)
297 err(1, "strdup failure");
298
299 if (asme) {
300 if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') {
301 /* must copy - pwd memory is recycled */
302 shell = strncpy(shellbuf, pwd->pw_shell,
303 sizeof(shellbuf));
304 shellbuf[sizeof(shellbuf) - 1] = '\0';
305 }
306 else {
307 shell = _PATH_BSHELL;
308 iscsh = NO;
309 }
310 }
311
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 }
318
319 PAM_SET_ITEM(PAM_RUSER, username);
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) {
328 #ifdef USE_BSM_AUDIT
329 if (audit_submit(AUE_su, auid, EPERM, 1, "bad su %s to %s on %s",
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);
335 errx(1, "Sorry");
336 }
337 #ifdef USE_BSM_AUDIT
338 if (audit_submit(AUE_su, auid, 0, 0, "successful authentication"))
339 errx(1, "Permission denied");
340 #endif
341 retcode = pam_get_item(pamh, PAM_USER, &v);
342 if (retcode == PAM_SUCCESS)
343 user = v;
344 else
345 syslog(LOG_ERR, "pam_get_item(PAM_USER): %s",
346 pam_strerror(pamh, retcode));
347 pwd = getpwnam(user);
348 if (pwd == NULL) {
349 #ifdef USE_BSM_AUDIT
350 if (audit_submit(AUE_su, auid, EPERM, 1,
351 "unknown subject: %s", user))
352 errx(1, "Permission denied");
353 #endif
354 errx(1, "unknown login: %s", user);
355 }
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) {
362 #ifdef USE_BSM_AUDIT
363 aerr = pam_strerror(pamh, retcode);
364 if (aerr == NULL)
365 aerr = "Unknown PAM error";
366 if (audit_submit(AUE_su, auid, EPERM, 1,
367 "pam_chauthtok: %s", aerr))
368 errx(1, "Permission denied");
369 #endif
370 syslog(LOG_ERR, "pam_chauthtok: %s",
371 pam_strerror(pamh, retcode));
372 errx(1, "Sorry");
373 }
374 }
375 if (retcode != PAM_SUCCESS) {
376 #ifdef USE_BSM_AUDIT
377 if (audit_submit(AUE_su, auid, EPERM, 1, "pam_acct_mgmt: %s",
378 pam_strerror(pamh, retcode)))
379 errx(1, "Permission denied");
380 #endif
381 syslog(LOG_ERR, "pam_acct_mgmt: %s",
382 pam_strerror(pamh, retcode));
383 errx(1, "Sorry");
384 }
385
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
393 if (audit_submit(AUE_su, auid, EPERM, 1,
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__ */
404
405 /* if asme and non-standard target shell, must be root */
406 if (asme) {
407 if (ruid != 0 && !chshell(pwd->pw_shell))
408 errx(1, "permission denied (shell)");
409 }
410 else if (pwd->pw_shell && *pwd->pw_shell) {
411 #ifdef __APPLE__
412 /* 3825554 */
413 shell = strncpy(shellbuf, pwd->pw_shell, sizeof(shellbuf));
414 shellbuf[sizeof(shellbuf) - 1] = '\0';
415 #else
416 shell = pwd->pw_shell;
417 #endif /* __APPLE__ */
418 iscsh = UNSET;
419 }
420 else {
421 shell = _PATH_BSHELL;
422 iscsh = NO;
423 }
424
425 /* if we're forking a csh, we want to slightly muck the args */
426 if (iscsh == UNSET) {
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;
433 }
434 setpriority(PRIO_PROCESS, 0, prio);
435
436 /*
437 * PAM modules might add supplementary groups in pam_setcred(), so
438 * initialize them first.
439 */
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__ */
447
448 #ifdef __APPLE__
449 /* 8530846 */
450 if (asthem) {
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 {
462 errx(1, "failed to create session.");
463 }
464 }
465 #endif /* __APPLE__ */
466
467 retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
468 if (retcode != PAM_SUCCESS) {
469 syslog(LOG_ERR, "pam_setcred: %s",
470 pam_strerror(pamh, retcode));
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 }
481
482 /*
483 * We must fork() before setuid() because we need to call
484 * pam_setcred(pamh, PAM_DELETE_CRED) as root.
485 */
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);
494 statusp = 1;
495 if (pipe(fds) == -1) {
496 PAM_END();
497 err(1, "pipe");
498 }
499 child_pid = fork();
500 switch (child_pid) {
501 default:
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) {
511 if (WIFSTOPPED(statusp)) {
512 child_pgrp = getpgid(child_pid);
513 if (tcgetpgrp(STDERR_FILENO) == child_pgrp)
514 tcsetpgrp(STDERR_FILENO, getpgrp());
515 kill(getpid(), SIGSTOP);
516 if (tcgetpgrp(STDERR_FILENO) == getpgrp()) {
517 child_pgrp = getpgid(child_pid);
518 tcsetpgrp(STDERR_FILENO, child_pgrp);
519 }
520 kill(child_pid, SIGCONT);
521 statusp = 1;
522 continue;
523 }
524 break;
525 }
526 tcsetpgrp(STDERR_FILENO, getpgrp());
527 if (pid == -1)
528 err(1, "waitpid");
529 PAM_END();
530 exit(WEXITSTATUS(statusp));
531 case -1:
532 PAM_END();
533 err(1, "fork");
534 case 0:
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))
544 err(1, "setgid");
545 /* Call initgroups(2) after setgid(2) to re-establish memberd */
546 if (initgroups(user, pwd->pw_gid))
547 err(1, "initgroups");
548 if (setuid(pwd->pw_uid))
549 err(1, "setuid");
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__ */
572
573 if (!asme) {
574 if (asthem) {
575 p = getenv("TERM");
576 environ = &cleanenv;
577 }
578
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);
583 #ifdef __APPLE__
584 unsetenv("TMPDIR");
585 #endif /* __APPLE__ */
586
587 if (asthem) {
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
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);
604 #endif /* __APPLE__ */
605 if (p)
606 setenv("TERM", p, 1);
607
608 p = pam_getenv(pamh, "HOME");
609 if (chdir(p ? p : pwd->pw_dir) < 0)
610 errx(1, "no directory");
611 }
612 }
613 #ifndef __APPLE__
614 login_close(lc);
615 #endif /* !__APPLE__ */
616
617 if (iscsh == YES) {
618 if (fastlogin)
619 *np.a-- = "-f";
620 if (asme)
621 *np.a-- = "-m";
622 }
623 #ifdef __APPLE__
624 /* 4043304 */
625 if ((p = strrchr(shell, '/')) != NULL)
626 avshell = p + 1;
627 else
628 avshell = shell;
629
630 if (asthem) {
631 avshellbuf[0] = '-';
632 strlcpy(avshellbuf+1, avshell, sizeof(avshellbuf) - 1);
633 avshell = avshellbuf;
634 }
635
636 /* csh *no longer* strips the first character... */
637 *np.a = avshell;
638 #else
639 /* csh strips the first character... */
640 *np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su";
641 #endif /* __APPLE__ */
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 }
650 }
651
652 static void
653 export_pam_environment(void)
654 {
655 char **pp;
656 char *p;
657
658 for (pp = environ_pam; *pp != NULL; pp++) {
659 if (ok_to_export(*pp)) {
660 p = strchr(*pp, '=');
661 *p = '\0';
662 setenv(*pp, p + 1, 1);
663 }
664 free(*pp);
665 }
666 }
667
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.
674 * Note that if the user is chrooted, PAM may have a better idea than we
675 * do of where her home directory is.
676 */
677 static int
678 ok_to_export(const char *s)
679 {
680 static const char *noexport[] = {
681 "SHELL", /* "HOME", */ "LOGNAME", "MAIL", "CDPATH",
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;
695 }
696 return 1;
697 }
698
699 static void
700 usage(void)
701 {
702
703 #ifdef __APPLE__
704 fprintf(stderr, "usage: su [-] [-flm] [login [args]]\n");
705 #else
706 fprintf(stderr, "usage: su [-] [-flms] [-c class] [login [args]]\n");
707 #endif /* __APPLE__ */
708 exit(1);
709 /* NOTREACHED */
710 }
711
712 static int
713 chshell(const char *sh)
714 {
715 int r;
716 char *cp;
717
718 r = 0;
719 setusershell();
720 while ((cp = getusershell()) != NULL && !r)
721 r = (strcmp(cp, sh) == 0);
722 endusershell();
723 return r;
724 }
725
726 static char *
727 ontty(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;
737 }