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