su.c revision 200420
1264790Sbapt/* 2264790Sbapt * Copyright (c) 2002, 2005 Networks Associates Technologies, Inc. 3264790Sbapt * All rights reserved. 4264790Sbapt * 5264790Sbapt * Portions of this software were developed for the FreeBSD Project by 6264790Sbapt * ThinkSec AS and NAI Labs, the Security Research Division of Network 7264790Sbapt * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 8264790Sbapt * ("CBOSS"), as part of the DARPA CHATS research program. 9264790Sbapt * 10264790Sbapt * Redistribution and use in source and binary forms, with or without 11264790Sbapt * modification, are permitted provided that the following conditions 12264790Sbapt * are met: 13264790Sbapt * 1. Redistributions of source code must retain the above copyright 14264790Sbapt * notice, this list of conditions and the following disclaimer. 15264790Sbapt * 2. Redistributions in binary form must reproduce the above copyright 16264790Sbapt * notice, this list of conditions and the following disclaimer in the 17264790Sbapt * documentation and/or other materials provided with the distribution. 18264790Sbapt * 19264790Sbapt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20264790Sbapt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21264790Sbapt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22264790Sbapt * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23264790Sbapt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24264790Sbapt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25264790Sbapt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26264790Sbapt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27264790Sbapt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28264790Sbapt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29264790Sbapt * SUCH DAMAGE. 30264790Sbapt */ 31264790Sbapt/*- 32264790Sbapt * Copyright (c) 1988, 1993, 1994 33264790Sbapt * The Regents of the University of California. All rights reserved. 34264790Sbapt * 35264790Sbapt * Redistribution and use in source and binary forms, with or without 36264790Sbapt * modification, are permitted provided that the following conditions 37264790Sbapt * are met: 38264790Sbapt * 1. Redistributions of source code must retain the above copyright 39264790Sbapt * notice, this list of conditions and the following disclaimer. 40264790Sbapt * 2. Redistributions in binary form must reproduce the above copyright 41264790Sbapt * notice, this list of conditions and the following disclaimer in the 42264790Sbapt * documentation and/or other materials provided with the distribution. 43264790Sbapt * 3. All advertising materials mentioning features or use of this software 44264790Sbapt * must display the following acknowledgement: 45264790Sbapt * This product includes software developed by the University of 46264790Sbapt * California, Berkeley and its contributors. 47264790Sbapt * 4. Neither the name of the University nor the names of its contributors 48264790Sbapt * may be used to endorse or promote products derived from this software 49264790Sbapt * without specific prior written permission. 50264790Sbapt * 51264790Sbapt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 52264790Sbapt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 53264790Sbapt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 54264790Sbapt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 55264790Sbapt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 56264790Sbapt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 57264790Sbapt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 58264790Sbapt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 59264790Sbapt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 60264790Sbapt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 61264790Sbapt * SUCH DAMAGE. 62264790Sbapt */ 63264790Sbapt 64264790Sbapt#ifndef lint 65264790Sbaptstatic const char copyright[] = 66264790Sbapt"@(#) Copyright (c) 1988, 1993, 1994\n\ 67264790Sbapt The Regents of the University of California. All rights reserved.\n"; 68264790Sbapt#endif /* not lint */ 69264790Sbapt 70264790Sbapt#if 0 71264790Sbapt#ifndef lint 72264790Sbaptstatic char sccsid[] = "@(#)su.c 8.3 (Berkeley) 4/2/94"; 73264790Sbapt#endif /* not lint */ 74264790Sbapt#endif 75264790Sbapt 76264790Sbapt#include <sys/cdefs.h> 77264790Sbapt__FBSDID("$FreeBSD: head/usr.bin/su/su.c 200420 2009-12-11 23:35:38Z delphij $"); 78264790Sbapt 79264790Sbapt#include <sys/param.h> 80264790Sbapt#include <sys/time.h> 81264790Sbapt#include <sys/resource.h> 82264790Sbapt#include <sys/wait.h> 83264790Sbapt 84264790Sbapt#ifdef USE_BSM_AUDIT 85264790Sbapt#include <bsm/libbsm.h> 86264790Sbapt#include <bsm/audit_uevents.h> 87264790Sbapt#endif 88264790Sbapt 89264790Sbapt#include <err.h> 90264790Sbapt#include <errno.h> 91264790Sbapt#include <login_cap.h> 92264790Sbapt#include <paths.h> 93264790Sbapt#include <pwd.h> 94264790Sbapt#include <signal.h> 95264790Sbapt#include <stdio.h> 96264790Sbapt#include <stdlib.h> 97264790Sbapt#include <string.h> 98264790Sbapt#include <syslog.h> 99264790Sbapt#include <unistd.h> 100264790Sbapt#include <stdarg.h> 101264790Sbapt 102264790Sbapt#include <security/pam_appl.h> 103264790Sbapt#include <security/openpam.h> 104264790Sbapt 105264790Sbapt#define PAM_END() do { \ 106264790Sbapt int local_ret; \ 107264790Sbapt if (pamh != NULL) { \ 108264790Sbapt local_ret = pam_setcred(pamh, PAM_DELETE_CRED); \ 109264790Sbapt if (local_ret != PAM_SUCCESS) \ 110264790Sbapt syslog(LOG_ERR, "pam_setcred: %s", \ 111264790Sbapt pam_strerror(pamh, local_ret)); \ 112264790Sbapt if (asthem) { \ 113264790Sbapt local_ret = pam_close_session(pamh, 0); \ 114264790Sbapt if (local_ret != PAM_SUCCESS) \ 115264790Sbapt syslog(LOG_ERR, "pam_close_session: %s",\ 116264790Sbapt pam_strerror(pamh, local_ret)); \ 117264790Sbapt } \ 118264790Sbapt local_ret = pam_end(pamh, local_ret); \ 119264790Sbapt if (local_ret != PAM_SUCCESS) \ 120264790Sbapt syslog(LOG_ERR, "pam_end: %s", \ 121264790Sbapt pam_strerror(pamh, local_ret)); \ 122264790Sbapt } \ 123264790Sbapt} while (0) 124264790Sbapt 125264790Sbapt 126264790Sbapt#define PAM_SET_ITEM(what, item) do { \ 127264790Sbapt int local_ret; \ 128264790Sbapt local_ret = pam_set_item(pamh, what, item); \ 129264790Sbapt if (local_ret != PAM_SUCCESS) { \ 130264790Sbapt syslog(LOG_ERR, "pam_set_item(" #what "): %s", \ 131264790Sbapt pam_strerror(pamh, local_ret)); \ 132264790Sbapt errx(1, "pam_set_item(" #what "): %s", \ 133264790Sbapt pam_strerror(pamh, local_ret)); \ 134264790Sbapt /* NOTREACHED */ \ 135264790Sbapt } \ 136264790Sbapt} while (0) 137264790Sbapt 138264790Sbaptenum tristate { UNSET, YES, NO }; 139264790Sbapt 140264790Sbaptstatic pam_handle_t *pamh = NULL; 141264790Sbaptstatic char **environ_pam; 142264790Sbapt 143264790Sbaptstatic char *ontty(void); 144264790Sbaptstatic int chshell(const char *); 145264790Sbaptstatic void usage(void) __dead2; 146264790Sbaptstatic void export_pam_environment(void); 147264790Sbaptstatic int ok_to_export(const char *); 148264790Sbapt 149264790Sbaptextern char **environ; 150264790Sbapt 151264790Sbaptint 152264790Sbaptmain(int argc, char *argv[]) 153264790Sbapt{ 154264790Sbapt static char *cleanenv; 155264790Sbapt struct passwd *pwd; 156264790Sbapt struct pam_conv conv = { openpam_ttyconv, NULL }; 157264790Sbapt enum tristate iscsh; 158264790Sbapt login_cap_t *lc; 159264790Sbapt union { 160264790Sbapt const char **a; 161264790Sbapt char * const *b; 162264790Sbapt } np; 163264790Sbapt uid_t ruid; 164264790Sbapt pid_t child_pid, child_pgrp, pid; 165264790Sbapt int asme, ch, asthem, fastlogin, prio, i, retcode, 166264790Sbapt statusp, setmaclabel; 167264790Sbapt u_int setwhat; 168264790Sbapt char *username, *class, shellbuf[MAXPATHLEN]; 169264790Sbapt const char *p, *user, *shell, *mytty, **nargv; 170264790Sbapt const void *v; 171264790Sbapt struct sigaction sa, sa_int, sa_quit, sa_pipe; 172264790Sbapt int temp, fds[2]; 173264790Sbapt#ifdef USE_BSM_AUDIT 174264790Sbapt const char *aerr; 175264790Sbapt au_id_t auid; 176264790Sbapt#endif 177264790Sbapt 178264790Sbapt shell = class = cleanenv = NULL; 179264790Sbapt asme = asthem = fastlogin = statusp = 0; 180264790Sbapt user = "root"; 181264790Sbapt iscsh = UNSET; 182264790Sbapt setmaclabel = 0; 183264790Sbapt 184264790Sbapt while ((ch = getopt(argc, argv, "-flmsc:")) != -1) 185264790Sbapt switch ((char)ch) { 186264790Sbapt case 'f': 187264790Sbapt fastlogin = 1; 188264790Sbapt break; 189264790Sbapt case '-': 190264790Sbapt case 'l': 191264790Sbapt asme = 0; 192264790Sbapt asthem = 1; 193264790Sbapt break; 194264790Sbapt case 'm': 195264790Sbapt asme = 1; 196264790Sbapt asthem = 0; 197264790Sbapt break; 198264790Sbapt case 's': 199264790Sbapt setmaclabel = 1; 200264790Sbapt break; 201264790Sbapt case 'c': 202264790Sbapt class = optarg; 203264790Sbapt break; 204264790Sbapt case '?': 205264790Sbapt default: 206264790Sbapt usage(); 207264790Sbapt /* NOTREACHED */ 208264790Sbapt } 209264790Sbapt 210264790Sbapt if (optind < argc) 211264790Sbapt user = argv[optind++]; 212264790Sbapt 213264790Sbapt if (user == NULL) 214264790Sbapt usage(); 215264790Sbapt /* NOTREACHED */ 216264790Sbapt 217264790Sbapt /* 218264790Sbapt * Try to provide more helpful debugging output if su(1) is running 219264790Sbapt * non-setuid, or was run from a file system not mounted setuid. 220264790Sbapt */ 221264790Sbapt if (geteuid() != 0) 222264790Sbapt errx(1, "not running setuid"); 223264790Sbapt 224264790Sbapt#ifdef USE_BSM_AUDIT 225264790Sbapt if (getauid(&auid) < 0 && errno != ENOSYS) { 226264790Sbapt syslog(LOG_AUTH | LOG_ERR, "getauid: %s", strerror(errno)); 227264790Sbapt errx(1, "Permission denied"); 228264790Sbapt } 229264790Sbapt#endif 230264790Sbapt if (strlen(user) > MAXLOGNAME - 1) { 231264790Sbapt#ifdef USE_BSM_AUDIT 232264790Sbapt if (audit_submit(AUE_su, auid, 233264790Sbapt EPERM, 1, "username too long: '%s'", user)) 234264790Sbapt errx(1, "Permission denied"); 235264790Sbapt#endif 236264790Sbapt errx(1, "username too long"); 237264790Sbapt } 238264790Sbapt 239264790Sbapt nargv = malloc(sizeof(char *) * (size_t)(argc + 4)); 240264790Sbapt if (nargv == NULL) 241264790Sbapt errx(1, "malloc failure"); 242264790Sbapt 243264790Sbapt nargv[argc + 3] = NULL; 244264790Sbapt for (i = argc; i >= optind; i--) 245264790Sbapt nargv[i + 3] = argv[i]; 246264790Sbapt np.a = &nargv[i + 3]; 247264790Sbapt 248264790Sbapt argv += optind; 249264790Sbapt 250264790Sbapt errno = 0; 251264790Sbapt prio = getpriority(PRIO_PROCESS, 0); 252264790Sbapt if (errno) 253264790Sbapt prio = 0; 254264790Sbapt 255264790Sbapt setpriority(PRIO_PROCESS, 0, -2); 256264790Sbapt openlog("su", LOG_CONS, LOG_AUTH); 257264790Sbapt 258264790Sbapt /* get current login name, real uid and shell */ 259264790Sbapt ruid = getuid(); 260264790Sbapt username = getlogin(); 261264790Sbapt pwd = getpwnam(username); 262264790Sbapt if (username == NULL || pwd == NULL || pwd->pw_uid != ruid) 263264790Sbapt pwd = getpwuid(ruid); 264264790Sbapt if (pwd == NULL) { 265264790Sbapt#ifdef USE_BSM_AUDIT 266264790Sbapt if (audit_submit(AUE_su, auid, EPERM, 1, 267264790Sbapt "unable to determine invoking subject: '%s'", username)) 268264790Sbapt errx(1, "Permission denied"); 269264790Sbapt#endif 270264790Sbapt errx(1, "who are you?"); 271264790Sbapt } 272264790Sbapt 273264790Sbapt username = strdup(pwd->pw_name); 274264790Sbapt if (username == NULL) 275264790Sbapt err(1, "strdup failure"); 276264790Sbapt 277264790Sbapt if (asme) { 278264790Sbapt if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') { 279264790Sbapt /* must copy - pwd memory is recycled */ 280264790Sbapt shell = strncpy(shellbuf, pwd->pw_shell, 281264790Sbapt sizeof(shellbuf)); 282264790Sbapt shellbuf[sizeof(shellbuf) - 1] = '\0'; 283264790Sbapt } 284264790Sbapt else { 285264790Sbapt shell = _PATH_BSHELL; 286264790Sbapt iscsh = NO; 287264790Sbapt } 288264790Sbapt } 289264790Sbapt 290264790Sbapt /* Do the whole PAM startup thing */ 291264790Sbapt retcode = pam_start("su", user, &conv, &pamh); 292264790Sbapt if (retcode != PAM_SUCCESS) { 293264790Sbapt syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, retcode)); 294264790Sbapt errx(1, "pam_start: %s", pam_strerror(pamh, retcode)); 295264790Sbapt } 296264790Sbapt 297264790Sbapt PAM_SET_ITEM(PAM_RUSER, username); 298264790Sbapt 299264790Sbapt mytty = ttyname(STDERR_FILENO); 300264790Sbapt if (!mytty) 301264790Sbapt mytty = "tty"; 302264790Sbapt PAM_SET_ITEM(PAM_TTY, mytty); 303264790Sbapt 304264790Sbapt retcode = pam_authenticate(pamh, 0); 305264790Sbapt if (retcode != PAM_SUCCESS) { 306264790Sbapt#ifdef USE_BSM_AUDIT 307264790Sbapt if (audit_submit(AUE_su, auid, EPERM, 1, "bad su %s to %s on %s", 308264790Sbapt username, user, mytty)) 309264790Sbapt errx(1, "Permission denied"); 310264790Sbapt#endif 311264790Sbapt syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s on %s", 312264790Sbapt username, user, mytty); 313264790Sbapt errx(1, "Sorry"); 314264790Sbapt } 315264790Sbapt#ifdef USE_BSM_AUDIT 316264790Sbapt if (audit_submit(AUE_su, auid, 0, 0, "successful authentication")) 317264790Sbapt errx(1, "Permission denied"); 318264790Sbapt#endif 319264790Sbapt retcode = pam_get_item(pamh, PAM_USER, &v); 320264790Sbapt if (retcode == PAM_SUCCESS) 321264790Sbapt user = v; 322264790Sbapt else 323264790Sbapt syslog(LOG_ERR, "pam_get_item(PAM_USER): %s", 324264790Sbapt pam_strerror(pamh, retcode)); 325264790Sbapt pwd = getpwnam(user); 326264790Sbapt if (pwd == NULL) { 327264790Sbapt#ifdef USE_BSM_AUDIT 328264790Sbapt if (audit_submit(AUE_su, auid, EPERM, 1, 329264790Sbapt "unknown subject: %s", user)) 330264790Sbapt errx(1, "Permission denied"); 331264790Sbapt#endif 332264790Sbapt errx(1, "unknown login: %s", user); 333264790Sbapt } 334264790Sbapt 335264790Sbapt retcode = pam_acct_mgmt(pamh, 0); 336264790Sbapt if (retcode == PAM_NEW_AUTHTOK_REQD) { 337264790Sbapt retcode = pam_chauthtok(pamh, 338264790Sbapt PAM_CHANGE_EXPIRED_AUTHTOK); 339264790Sbapt if (retcode != PAM_SUCCESS) { 340264790Sbapt#ifdef USE_BSM_AUDIT 341264790Sbapt aerr = pam_strerror(pamh, retcode); 342264790Sbapt if (aerr == NULL) 343264790Sbapt aerr = "Unknown PAM error"; 344264790Sbapt if (audit_submit(AUE_su, auid, EPERM, 1, 345264790Sbapt "pam_chauthtok: %s", aerr)) 346264790Sbapt errx(1, "Permission denied"); 347264790Sbapt#endif 348264790Sbapt syslog(LOG_ERR, "pam_chauthtok: %s", 349264790Sbapt pam_strerror(pamh, retcode)); 350264790Sbapt errx(1, "Sorry"); 351264790Sbapt } 352264790Sbapt } 353264790Sbapt if (retcode != PAM_SUCCESS) { 354264790Sbapt#ifdef USE_BSM_AUDIT 355264790Sbapt if (audit_submit(AUE_su, auid, EPERM, 1, "pam_acct_mgmt: %s", 356264790Sbapt pam_strerror(pamh, retcode))) 357264790Sbapt errx(1, "Permission denied"); 358264790Sbapt#endif 359264790Sbapt syslog(LOG_ERR, "pam_acct_mgmt: %s", 360264790Sbapt pam_strerror(pamh, retcode)); 361264790Sbapt errx(1, "Sorry"); 362264790Sbapt } 363264790Sbapt 364264790Sbapt /* get target login information */ 365264790Sbapt if (class == NULL) 366264790Sbapt lc = login_getpwclass(pwd); 367264790Sbapt else { 368264790Sbapt if (ruid != 0) { 369264790Sbapt#ifdef USE_BSM_AUDIT 370264790Sbapt if (audit_submit(AUE_su, auid, EPERM, 1, 371264790Sbapt "only root may use -c")) 372264790Sbapt errx(1, "Permission denied"); 373264790Sbapt#endif 374264790Sbapt errx(1, "only root may use -c"); 375264790Sbapt } 376264790Sbapt lc = login_getclass(class); 377264790Sbapt if (lc == NULL) 378264790Sbapt errx(1, "unknown class: %s", class); 379264790Sbapt } 380264790Sbapt 381264790Sbapt /* if asme and non-standard target shell, must be root */ 382264790Sbapt if (asme) { 383264790Sbapt if (ruid != 0 && !chshell(pwd->pw_shell)) 384264790Sbapt errx(1, "permission denied (shell)"); 385264790Sbapt } 386264790Sbapt else if (pwd->pw_shell && *pwd->pw_shell) { 387264790Sbapt shell = pwd->pw_shell; 388264790Sbapt iscsh = UNSET; 389264790Sbapt } 390264790Sbapt else { 391264790Sbapt shell = _PATH_BSHELL; 392264790Sbapt iscsh = NO; 393264790Sbapt } 394264790Sbapt 395264790Sbapt /* if we're forking a csh, we want to slightly muck the args */ 396264790Sbapt if (iscsh == UNSET) { 397264790Sbapt p = strrchr(shell, '/'); 398264790Sbapt if (p) 399264790Sbapt ++p; 400264790Sbapt else 401264790Sbapt p = shell; 402264790Sbapt iscsh = strcmp(p, "csh") ? (strcmp(p, "tcsh") ? NO : YES) : YES; 403264790Sbapt } 404264790Sbapt setpriority(PRIO_PROCESS, 0, prio); 405264790Sbapt 406264790Sbapt /* 407264790Sbapt * PAM modules might add supplementary groups in pam_setcred(), so 408264790Sbapt * initialize them first. 409264790Sbapt */ 410264790Sbapt if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0) 411264790Sbapt err(1, "setusercontext"); 412264790Sbapt 413264790Sbapt retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED); 414264790Sbapt if (retcode != PAM_SUCCESS) { 415264790Sbapt syslog(LOG_ERR, "pam_setcred: %s", 416264790Sbapt pam_strerror(pamh, retcode)); 417264790Sbapt errx(1, "failed to establish credentials."); 418264790Sbapt } 419264790Sbapt if (asthem) { 420264790Sbapt retcode = pam_open_session(pamh, 0); 421264790Sbapt if (retcode != PAM_SUCCESS) { 422264790Sbapt syslog(LOG_ERR, "pam_open_session: %s", 423264790Sbapt pam_strerror(pamh, retcode)); 424264790Sbapt errx(1, "failed to open session."); 425264790Sbapt } 426264790Sbapt } 427264790Sbapt 428264790Sbapt /* 429264790Sbapt * We must fork() before setuid() because we need to call 430264790Sbapt * pam_setcred(pamh, PAM_DELETE_CRED) as root. 431264790Sbapt */ 432264790Sbapt sa.sa_flags = SA_RESTART; 433264790Sbapt sa.sa_handler = SIG_IGN; 434264790Sbapt sigemptyset(&sa.sa_mask); 435264790Sbapt sigaction(SIGINT, &sa, &sa_int); 436264790Sbapt sigaction(SIGQUIT, &sa, &sa_quit); 437264790Sbapt sigaction(SIGPIPE, &sa, &sa_pipe); 438264790Sbapt sa.sa_handler = SIG_DFL; 439264790Sbapt sigaction(SIGTSTP, &sa, NULL); 440264790Sbapt statusp = 1; 441264790Sbapt if (pipe(fds) == -1) { 442264790Sbapt PAM_END(); 443264790Sbapt err(1, "pipe"); 444264790Sbapt } 445264790Sbapt child_pid = fork(); 446264790Sbapt switch (child_pid) { 447264790Sbapt default: 448264790Sbapt sa.sa_handler = SIG_IGN; 449264790Sbapt sigaction(SIGTTOU, &sa, NULL); 450264790Sbapt close(fds[0]); 451264790Sbapt setpgid(child_pid, child_pid); 452264790Sbapt if (tcgetpgrp(STDERR_FILENO) == getpgrp()) 453264790Sbapt tcsetpgrp(STDERR_FILENO, child_pid); 454264790Sbapt close(fds[1]); 455264790Sbapt sigaction(SIGPIPE, &sa_pipe, NULL); 456264790Sbapt while ((pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) { 457264790Sbapt if (WIFSTOPPED(statusp)) { 458264790Sbapt child_pgrp = getpgid(child_pid); 459264790Sbapt if (tcgetpgrp(STDERR_FILENO) == child_pgrp) 460264790Sbapt tcsetpgrp(STDERR_FILENO, getpgrp()); 461264790Sbapt kill(getpid(), SIGSTOP); 462264790Sbapt if (tcgetpgrp(STDERR_FILENO) == getpgrp()) { 463264790Sbapt child_pgrp = getpgid(child_pid); 464264790Sbapt tcsetpgrp(STDERR_FILENO, child_pgrp); 465264790Sbapt } 466264790Sbapt kill(child_pid, SIGCONT); 467264790Sbapt statusp = 1; 468264790Sbapt continue; 469264790Sbapt } 470264790Sbapt break; 471264790Sbapt } 472264790Sbapt tcsetpgrp(STDERR_FILENO, getpgrp()); 473264790Sbapt if (pid == -1) 474264790Sbapt err(1, "waitpid"); 475264790Sbapt PAM_END(); 476264790Sbapt exit(WEXITSTATUS(statusp)); 477264790Sbapt case -1: 478264790Sbapt PAM_END(); 479264790Sbapt err(1, "fork"); 480264790Sbapt case 0: 481264790Sbapt close(fds[1]); 482264790Sbapt read(fds[0], &temp, 1); 483264790Sbapt close(fds[0]); 484264790Sbapt sigaction(SIGPIPE, &sa_pipe, NULL); 485264790Sbapt sigaction(SIGINT, &sa_int, NULL); 486264790Sbapt sigaction(SIGQUIT, &sa_quit, NULL); 487264790Sbapt 488264790Sbapt /* 489264790Sbapt * Set all user context except for: Environmental variables 490264790Sbapt * Umask Login records (wtmp, etc) Path 491264790Sbapt */ 492264790Sbapt setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK | 493264790Sbapt LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP | 494264790Sbapt LOGIN_SETMAC); 495264790Sbapt /* 496264790Sbapt * If -s is present, also set the MAC label. 497264790Sbapt */ 498264790Sbapt if (setmaclabel) 499264790Sbapt setwhat |= LOGIN_SETMAC; 500264790Sbapt /* 501264790Sbapt * Don't touch resource/priority settings if -m has been used 502264790Sbapt * or -l and -c hasn't, and we're not su'ing to root. 503264790Sbapt */ 504264790Sbapt if ((asme || (!asthem && class == NULL)) && pwd->pw_uid) 505264790Sbapt setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES); 506264790Sbapt if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0) 507264790Sbapt err(1, "setusercontext"); 508264790Sbapt 509264790Sbapt if (!asme) { 510264790Sbapt if (asthem) { 511264790Sbapt p = getenv("TERM"); 512264790Sbapt environ = &cleanenv; 513264790Sbapt } 514264790Sbapt 515264790Sbapt if (asthem || pwd->pw_uid) 516264790Sbapt setenv("USER", pwd->pw_name, 1); 517264790Sbapt setenv("HOME", pwd->pw_dir, 1); 518264790Sbapt setenv("SHELL", shell, 1); 519264790Sbapt 520264790Sbapt if (asthem) { 521264790Sbapt /* 522264790Sbapt * Add any environmental variables that the 523264790Sbapt * PAM modules may have set. 524264790Sbapt */ 525264790Sbapt environ_pam = pam_getenvlist(pamh); 526264790Sbapt if (environ_pam) 527264790Sbapt export_pam_environment(); 528264790Sbapt 529264790Sbapt /* set the su'd user's environment & umask */ 530264790Sbapt setusercontext(lc, pwd, pwd->pw_uid, 531264790Sbapt LOGIN_SETPATH | LOGIN_SETUMASK | 532264790Sbapt LOGIN_SETENV); 533264790Sbapt if (p) 534264790Sbapt setenv("TERM", p, 1); 535264790Sbapt 536264790Sbapt p = pam_getenv(pamh, "HOME"); 537264790Sbapt if (chdir(p ? p : pwd->pw_dir) < 0) 538264790Sbapt errx(1, "no directory"); 539264790Sbapt } 540264790Sbapt } 541264790Sbapt login_close(lc); 542264790Sbapt 543264790Sbapt if (iscsh == YES) { 544264790Sbapt if (fastlogin) 545264790Sbapt *np.a-- = "-f"; 546264790Sbapt if (asme) 547264790Sbapt *np.a-- = "-m"; 548264790Sbapt } 549264790Sbapt /* csh strips the first character... */ 550264790Sbapt *np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su"; 551264790Sbapt 552264790Sbapt if (ruid != 0) 553264790Sbapt syslog(LOG_NOTICE, "%s to %s%s", username, user, 554264790Sbapt ontty()); 555264790Sbapt 556264790Sbapt execv(shell, np.b); 557264790Sbapt err(1, "%s", shell); 558264790Sbapt } 559264790Sbapt} 560264790Sbapt 561264790Sbaptstatic void 562264790Sbaptexport_pam_environment(void) 563264790Sbapt{ 564264790Sbapt char **pp; 565264790Sbapt char *p; 566264790Sbapt 567264790Sbapt for (pp = environ_pam; *pp != NULL; pp++) { 568264790Sbapt if (ok_to_export(*pp)) { 569264790Sbapt p = strchr(*pp, '='); 570264790Sbapt *p = '\0'; 571264790Sbapt setenv(*pp, p + 1, 1); 572264790Sbapt } 573264790Sbapt free(*pp); 574264790Sbapt } 575264790Sbapt} 576264790Sbapt 577264790Sbapt/* 578264790Sbapt * Sanity checks on PAM environmental variables: 579264790Sbapt * - Make sure there is an '=' in the string. 580264790Sbapt * - Make sure the string doesn't run on too long. 581264790Sbapt * - Do not export certain variables. This list was taken from the 582264790Sbapt * Solaris pam_putenv(3) man page. 583264790Sbapt * Note that if the user is chrooted, PAM may have a better idea than we 584264790Sbapt * do of where her home directory is. 585264790Sbapt */ 586264790Sbaptstatic int 587264790Sbaptok_to_export(const char *s) 588264790Sbapt{ 589264790Sbapt static const char *noexport[] = { 590264790Sbapt "SHELL", /* "HOME", */ "LOGNAME", "MAIL", "CDPATH", 591264790Sbapt "IFS", "PATH", NULL 592264790Sbapt }; 593264790Sbapt const char **pp; 594264790Sbapt size_t n; 595264790Sbapt 596264790Sbapt if (strlen(s) > 1024 || strchr(s, '=') == NULL) 597264790Sbapt return 0; 598264790Sbapt if (strncmp(s, "LD_", 3) == 0) 599264790Sbapt return 0; 600264790Sbapt for (pp = noexport; *pp != NULL; pp++) { 601264790Sbapt n = strlen(*pp); 602264790Sbapt if (s[n] == '=' && strncmp(s, *pp, n) == 0) 603264790Sbapt return 0; 604264790Sbapt } 605264790Sbapt return 1; 606264790Sbapt} 607264790Sbapt 608264790Sbaptstatic void 609264790Sbaptusage(void) 610264790Sbapt{ 611264790Sbapt 612264790Sbapt fprintf(stderr, "usage: su [-] [-flms] [-c class] [login [args]]\n"); 613264790Sbapt exit(1); 614264790Sbapt /* NOTREACHED */ 615264790Sbapt} 616264790Sbapt 617264790Sbaptstatic int 618264790Sbaptchshell(const char *sh) 619264790Sbapt{ 620264790Sbapt int r; 621264790Sbapt char *cp; 622264790Sbapt 623264790Sbapt r = 0; 624264790Sbapt setusershell(); 625264790Sbapt while ((cp = getusershell()) != NULL && !r) 626264790Sbapt r = (strcmp(cp, sh) == 0); 627264790Sbapt endusershell(); 628264790Sbapt return r; 629264790Sbapt} 630264790Sbapt 631264790Sbaptstatic char * 632264790Sbaptontty(void) 633264790Sbapt{ 634264790Sbapt char *p; 635264790Sbapt static char buf[MAXPATHLEN + 4]; 636264790Sbapt 637264790Sbapt buf[0] = 0; 638264790Sbapt p = ttyname(STDERR_FILENO); 639264790Sbapt if (p) 640264790Sbapt snprintf(buf, sizeof(buf), " on %s", p); 641264790Sbapt return buf; 642264790Sbapt} 643264790Sbapt