]> git.saurik.com Git - apple/shell_cmds.git/blob - su/su.c
10a36360e9f39d21eb9a44e5fa5ae543cc6cc678
[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 <Security/AuthSession.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 retcode = SessionCreate(0, 0);
452 if (retcode != noErr) {
453 syslog(LOG_ERR, "SessionCreate: %d", retcode);
454 errx(1, "failed to create session.");
455 }
456 }
457 #endif /* __APPLE__ */
458
459 retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
460 if (retcode != PAM_SUCCESS) {
461 syslog(LOG_ERR, "pam_setcred: %s",
462 pam_strerror(pamh, retcode));
463 errx(1, "failed to establish credentials.");
464 }
465 if (asthem) {
466 retcode = pam_open_session(pamh, 0);
467 if (retcode != PAM_SUCCESS) {
468 syslog(LOG_ERR, "pam_open_session: %s",
469 pam_strerror(pamh, retcode));
470 errx(1, "failed to open session.");
471 }
472 }
473
474 /*
475 * We must fork() before setuid() because we need to call
476 * pam_setcred(pamh, PAM_DELETE_CRED) as root.
477 */
478 sa.sa_flags = SA_RESTART;
479 sa.sa_handler = SIG_IGN;
480 sigemptyset(&sa.sa_mask);
481 sigaction(SIGINT, &sa, &sa_int);
482 sigaction(SIGQUIT, &sa, &sa_quit);
483 sigaction(SIGPIPE, &sa, &sa_pipe);
484 sa.sa_handler = SIG_DFL;
485 sigaction(SIGTSTP, &sa, NULL);
486 statusp = 1;
487 if (pipe(fds) == -1) {
488 PAM_END();
489 err(1, "pipe");
490 }
491 child_pid = fork();
492 switch (child_pid) {
493 default:
494 sa.sa_handler = SIG_IGN;
495 sigaction(SIGTTOU, &sa, NULL);
496 close(fds[0]);
497 setpgid(child_pid, child_pid);
498 if (tcgetpgrp(STDERR_FILENO) == getpgrp())
499 tcsetpgrp(STDERR_FILENO, child_pid);
500 close(fds[1]);
501 sigaction(SIGPIPE, &sa_pipe, NULL);
502 while ((pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) {
503 if (WIFSTOPPED(statusp)) {
504 child_pgrp = getpgid(child_pid);
505 if (tcgetpgrp(STDERR_FILENO) == child_pgrp)
506 tcsetpgrp(STDERR_FILENO, getpgrp());
507 kill(getpid(), SIGSTOP);
508 if (tcgetpgrp(STDERR_FILENO) == getpgrp()) {
509 child_pgrp = getpgid(child_pid);
510 tcsetpgrp(STDERR_FILENO, child_pgrp);
511 }
512 kill(child_pid, SIGCONT);
513 statusp = 1;
514 continue;
515 }
516 break;
517 }
518 tcsetpgrp(STDERR_FILENO, getpgrp());
519 if (pid == -1)
520 err(1, "waitpid");
521 PAM_END();
522 exit(WEXITSTATUS(statusp));
523 case -1:
524 PAM_END();
525 err(1, "fork");
526 case 0:
527 close(fds[1]);
528 read(fds[0], &temp, 1);
529 close(fds[0]);
530 sigaction(SIGPIPE, &sa_pipe, NULL);
531 sigaction(SIGINT, &sa_int, NULL);
532 sigaction(SIGQUIT, &sa_quit, NULL);
533
534 #ifdef __APPLE__
535 if (setgid(pwd->pw_gid))
536 err(1, "setgid");
537 /* Call initgroups(2) after setgid(2) to re-establish memberd */
538 if (initgroups(user, pwd->pw_gid))
539 err(1, "initgroups");
540 if (setuid(pwd->pw_uid))
541 err(1, "setuid");
542 #else
543 /*
544 * Set all user context except for: Environmental variables
545 * Umask Login records (wtmp, etc) Path
546 */
547 setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK |
548 LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP |
549 LOGIN_SETMAC);
550 /*
551 * If -s is present, also set the MAC label.
552 */
553 if (setmaclabel)
554 setwhat |= LOGIN_SETMAC;
555 /*
556 * Don't touch resource/priority settings if -m has been used
557 * or -l and -c hasn't, and we're not su'ing to root.
558 */
559 if ((asme || (!asthem && class == NULL)) && pwd->pw_uid)
560 setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES);
561 if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0)
562 err(1, "setusercontext");
563 #endif /* __APPLE__ */
564
565 if (!asme) {
566 if (asthem) {
567 p = getenv("TERM");
568 environ = &cleanenv;
569 }
570
571 if (asthem || pwd->pw_uid)
572 setenv("USER", pwd->pw_name, 1);
573 setenv("HOME", pwd->pw_dir, 1);
574 setenv("SHELL", shell, 1);
575 #ifdef __APPLE__
576 unsetenv("TMPDIR");
577 #endif /* __APPLE__ */
578
579 if (asthem) {
580 /*
581 * Add any environmental variables that the
582 * PAM modules may have set.
583 */
584 environ_pam = pam_getenvlist(pamh);
585 if (environ_pam)
586 export_pam_environment();
587
588 #ifdef __APPLE__
589 /* 5276965: As documented, set $PATH. */
590 setenv("PATH", "/bin:/usr/bin", 1);
591 #else
592 /* set the su'd user's environment & umask */
593 setusercontext(lc, pwd, pwd->pw_uid,
594 LOGIN_SETPATH | LOGIN_SETUMASK |
595 LOGIN_SETENV);
596 #endif /* __APPLE__ */
597 if (p)
598 setenv("TERM", p, 1);
599
600 p = pam_getenv(pamh, "HOME");
601 if (chdir(p ? p : pwd->pw_dir) < 0)
602 errx(1, "no directory");
603 }
604 }
605 #ifndef __APPLE__
606 login_close(lc);
607 #endif /* !__APPLE__ */
608
609 if (iscsh == YES) {
610 if (fastlogin)
611 *np.a-- = "-f";
612 if (asme)
613 *np.a-- = "-m";
614 }
615 #ifdef __APPLE__
616 /* 4043304 */
617 if ((p = strrchr(shell, '/')) != NULL)
618 avshell = p + 1;
619 else
620 avshell = shell;
621
622 if (asthem) {
623 avshellbuf[0] = '-';
624 strlcpy(avshellbuf+1, avshell, sizeof(avshellbuf) - 1);
625 avshell = avshellbuf;
626 }
627
628 /* csh *no longer* strips the first character... */
629 *np.a = avshell;
630 #else
631 /* csh strips the first character... */
632 *np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su";
633 #endif /* __APPLE__ */
634
635 if (ruid != 0)
636 syslog(LOG_NOTICE, "%s to %s%s", username, user,
637 ontty());
638
639 execv(shell, np.b);
640 err(1, "%s", shell);
641 }
642 }
643
644 static void
645 export_pam_environment(void)
646 {
647 char **pp;
648 char *p;
649
650 for (pp = environ_pam; *pp != NULL; pp++) {
651 if (ok_to_export(*pp)) {
652 p = strchr(*pp, '=');
653 *p = '\0';
654 setenv(*pp, p + 1, 1);
655 }
656 free(*pp);
657 }
658 }
659
660 /*
661 * Sanity checks on PAM environmental variables:
662 * - Make sure there is an '=' in the string.
663 * - Make sure the string doesn't run on too long.
664 * - Do not export certain variables. This list was taken from the
665 * Solaris pam_putenv(3) man page.
666 * Note that if the user is chrooted, PAM may have a better idea than we
667 * do of where her home directory is.
668 */
669 static int
670 ok_to_export(const char *s)
671 {
672 static const char *noexport[] = {
673 "SHELL", /* "HOME", */ "LOGNAME", "MAIL", "CDPATH",
674 "IFS", "PATH", NULL
675 };
676 const char **pp;
677 size_t n;
678
679 if (strlen(s) > 1024 || strchr(s, '=') == NULL)
680 return 0;
681 if (strncmp(s, "LD_", 3) == 0)
682 return 0;
683 for (pp = noexport; *pp != NULL; pp++) {
684 n = strlen(*pp);
685 if (s[n] == '=' && strncmp(s, *pp, n) == 0)
686 return 0;
687 }
688 return 1;
689 }
690
691 static void
692 usage(void)
693 {
694
695 #ifdef __APPLE__
696 fprintf(stderr, "usage: su [-] [-flm] [login [args]]\n");
697 #else
698 fprintf(stderr, "usage: su [-] [-flms] [-c class] [login [args]]\n");
699 #endif /* __APPLE__ */
700 exit(1);
701 /* NOTREACHED */
702 }
703
704 static int
705 chshell(const char *sh)
706 {
707 int r;
708 char *cp;
709
710 r = 0;
711 setusershell();
712 while ((cp = getusershell()) != NULL && !r)
713 r = (strcmp(cp, sh) == 0);
714 endusershell();
715 return r;
716 }
717
718 static char *
719 ontty(void)
720 {
721 char *p;
722 static char buf[MAXPATHLEN + 4];
723
724 buf[0] = 0;
725 p = ttyname(STDERR_FILENO);
726 if (p)
727 snprintf(buf, sizeof(buf), " on %s", p);
728 return buf;
729 }