su.c revision 169177
11590Srgrimes/* 2140392Srwatson * Copyright (c) 2002, 2005 Networks Associates Technologies, Inc. 397377Sdes * All rights reserved. 41590Srgrimes * 597377Sdes * Portions of this software were developed for the FreeBSD Project by 697377Sdes * ThinkSec AS and NAI Labs, the Security Research Division of Network 797377Sdes * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 897377Sdes * ("CBOSS"), as part of the DARPA CHATS research program. 997377Sdes * 101590Srgrimes * Redistribution and use in source and binary forms, with or without 111590Srgrimes * modification, are permitted provided that the following conditions 121590Srgrimes * are met: 131590Srgrimes * 1. Redistributions of source code must retain the above copyright 141590Srgrimes * notice, this list of conditions and the following disclaimer. 151590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 161590Srgrimes * notice, this list of conditions and the following disclaimer in the 171590Srgrimes * documentation and/or other materials provided with the distribution. 18140392Srwatson * 19140392Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20140392Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21140392Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22140392Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23140392Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24140392Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25140392Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26140392Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27140392Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28140392Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29140392Srwatson * SUCH DAMAGE. 30140392Srwatson */ 31140392Srwatson/*- 32140392Srwatson * Copyright (c) 1988, 1993, 1994 33140392Srwatson * The Regents of the University of California. All rights reserved. 34140392Srwatson * 35140392Srwatson * Redistribution and use in source and binary forms, with or without 36140392Srwatson * modification, are permitted provided that the following conditions 37140392Srwatson * are met: 38140392Srwatson * 1. Redistributions of source code must retain the above copyright 39140392Srwatson * notice, this list of conditions and the following disclaimer. 40140392Srwatson * 2. Redistributions in binary form must reproduce the above copyright 41140392Srwatson * notice, this list of conditions and the following disclaimer in the 42140392Srwatson * documentation and/or other materials provided with the distribution. 431590Srgrimes * 3. All advertising materials mentioning features or use of this software 441590Srgrimes * must display the following acknowledgement: 451590Srgrimes * This product includes software developed by the University of 461590Srgrimes * California, Berkeley and its contributors. 471590Srgrimes * 4. Neither the name of the University nor the names of its contributors 481590Srgrimes * may be used to endorse or promote products derived from this software 491590Srgrimes * without specific prior written permission. 501590Srgrimes * 511590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 521590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 531590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 541590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 551590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 561590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 571590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 581590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 591590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 601590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 611590Srgrimes * SUCH DAMAGE. 621590Srgrimes */ 631590Srgrimes 641590Srgrimes#ifndef lint 6514440Smarkmstatic const char copyright[] = 661590Srgrimes"@(#) Copyright (c) 1988, 1993, 1994\n\ 671590Srgrimes The Regents of the University of California. All rights reserved.\n"; 681590Srgrimes#endif /* not lint */ 691590Srgrimes 70127848Scharnier#if 0 711590Srgrimes#ifndef lint 721590Srgrimesstatic char sccsid[] = "@(#)su.c 8.3 (Berkeley) 4/2/94"; 73127848Scharnier#endif /* not lint */ 7428099Scharnier#endif 751590Srgrimes 76127848Scharnier#include <sys/cdefs.h> 77127848Scharnier__FBSDID("$FreeBSD: head/usr.bin/su/su.c 169177 2007-05-01 16:02:44Z ache $"); 78127848Scharnier 791590Srgrimes#include <sys/param.h> 801590Srgrimes#include <sys/time.h> 811590Srgrimes#include <sys/resource.h> 8277220Smarkm#include <sys/wait.h> 831590Srgrimes 84161815Scsjp#ifdef USE_BSM_AUDIT 85161815Scsjp#include <bsm/libbsm.h> 86161815Scsjp#include <bsm/audit_uevents.h> 87161815Scsjp#endif 88161815Scsjp 891590Srgrimes#include <err.h> 901590Srgrimes#include <errno.h> 911590Srgrimes#include <grp.h> 9277220Smarkm#include <login_cap.h> 931590Srgrimes#include <paths.h> 941590Srgrimes#include <pwd.h> 9577220Smarkm#include <signal.h> 961590Srgrimes#include <stdio.h> 971590Srgrimes#include <stdlib.h> 981590Srgrimes#include <string.h> 991590Srgrimes#include <syslog.h> 1001590Srgrimes#include <unistd.h> 101161815Scsjp#include <stdarg.h> 10221646Sdavidn 10374874Smarkm#include <security/pam_appl.h> 10491745Sdes#include <security/openpam.h> 10574874Smarkm 106113262Sdes#define PAM_END() do { \ 107113262Sdes int local_ret; \ 108113262Sdes if (pamh != NULL) { \ 109113262Sdes local_ret = pam_setcred(pamh, PAM_DELETE_CRED); \ 110113262Sdes if (local_ret != PAM_SUCCESS) \ 111113262Sdes syslog(LOG_ERR, "pam_setcred: %s", \ 112113262Sdes pam_strerror(pamh, local_ret)); \ 113113262Sdes if (asthem) { \ 114113262Sdes local_ret = pam_close_session(pamh, 0); \ 115113262Sdes if (local_ret != PAM_SUCCESS) \ 116113262Sdes syslog(LOG_ERR, "pam_close_session: %s",\ 117113262Sdes pam_strerror(pamh, local_ret)); \ 118113262Sdes } \ 119113262Sdes local_ret = pam_end(pamh, local_ret); \ 120113262Sdes if (local_ret != PAM_SUCCESS) \ 121113262Sdes syslog(LOG_ERR, "pam_end: %s", \ 122113262Sdes pam_strerror(pamh, local_ret)); \ 123113262Sdes } \ 12477220Smarkm} while (0) 12574874Smarkm 12674874Smarkm 127113262Sdes#define PAM_SET_ITEM(what, item) do { \ 128113262Sdes int local_ret; \ 129113262Sdes local_ret = pam_set_item(pamh, what, item); \ 130113262Sdes if (local_ret != PAM_SUCCESS) { \ 131113262Sdes syslog(LOG_ERR, "pam_set_item(" #what "): %s", \ 132113262Sdes pam_strerror(pamh, local_ret)); \ 133113262Sdes errx(1, "pam_set_item(" #what "): %s", \ 134113262Sdes pam_strerror(pamh, local_ret)); \ 135130409Smarkm /* NOTREACHED */ \ 136113262Sdes } \ 13777220Smarkm} while (0) 1383702Spst 13977220Smarkmenum tristate { UNSET, YES, NO }; 1401590Srgrimes 14177220Smarkmstatic pam_handle_t *pamh = NULL; 14277220Smarkmstatic char **environ_pam; 14377220Smarkm 14477220Smarkmstatic char *ontty(void); 145130409Smarkmstatic int chshell(const char *); 146130409Smarkmstatic void usage(void) __dead2; 147130409Smarkmstatic void export_pam_environment(void); 14877220Smarkmstatic int ok_to_export(const char *); 14977220Smarkm 15077220Smarkmextern char **environ; 15177220Smarkm 1521590Srgrimesint 15377220Smarkmmain(int argc, char *argv[]) 1541590Srgrimes{ 155130409Smarkm static char *cleanenv; 15677220Smarkm struct passwd *pwd; 15791745Sdes struct pam_conv conv = { openpam_ttyconv, NULL }; 15877220Smarkm enum tristate iscsh; 15977220Smarkm login_cap_t *lc; 16083373Smarkm union { 16183373Smarkm const char **a; 16283373Smarkm char * const *b; 16397377Sdes } np; 16477220Smarkm uid_t ruid; 165153985Sbrian pid_t child_pid, child_pgrp, pid; 166130409Smarkm int asme, ch, asthem, fastlogin, prio, i, retcode, 167113262Sdes statusp, setmaclabel; 168130409Smarkm u_int setwhat; 169130409Smarkm char *username, *class, shellbuf[MAXPATHLEN]; 17083373Smarkm const char *p, *user, *shell, *mytty, **nargv; 171112695Sdavidxu struct sigaction sa, sa_int, sa_quit, sa_pipe; 172112695Sdavidxu int temp, fds[2]; 173161815Scsjp#ifdef USE_BSM_AUDIT 174161815Scsjp const char *aerr; 175161815Scsjp au_id_t auid; 176161815Scsjp#endif 17798837Sdillon 17877220Smarkm shell = class = cleanenv = NULL; 17977220Smarkm asme = asthem = fastlogin = statusp = 0; 18010586Sjoerg user = "root"; 18177220Smarkm iscsh = UNSET; 182105758Srwatson setmaclabel = 0; 18377220Smarkm 184105758Srwatson while ((ch = getopt(argc, argv, "-flmsc:")) != -1) 18577220Smarkm switch ((char)ch) { 1861590Srgrimes case 'f': 1871590Srgrimes fastlogin = 1; 1881590Srgrimes break; 1891590Srgrimes case '-': 1901590Srgrimes case 'l': 1911590Srgrimes asme = 0; 1921590Srgrimes asthem = 1; 1931590Srgrimes break; 1941590Srgrimes case 'm': 1951590Srgrimes asme = 1; 1961590Srgrimes asthem = 0; 1971590Srgrimes break; 198105758Srwatson case 's': 199105758Srwatson setmaclabel = 1; 200105758Srwatson break; 20130793Sguido case 'c': 20230793Sguido class = optarg; 20330793Sguido break; 2041590Srgrimes case '?': 2051590Srgrimes default: 20628099Scharnier usage(); 207130409Smarkm /* NOTREACHED */ 20828099Scharnier } 20939538Sroberto 21039538Sroberto if (optind < argc) 21110586Sjoerg user = argv[optind++]; 21210586Sjoerg 21328612Sjoerg if (user == NULL) 21428612Sjoerg usage(); 215130409Smarkm /* NOTREACHED */ 21628612Sjoerg 217140392Srwatson /* 218140392Srwatson * Try to provide more helpful debugging output if su(1) is running 219140392Srwatson * non-setuid, or was run from a file system not mounted setuid. 220140392Srwatson */ 221140392Srwatson if (geteuid() != 0) 222140392Srwatson errx(1, "not running setuid"); 223140392Srwatson 224161815Scsjp#ifdef USE_BSM_AUDIT 225161815Scsjp if (getauid(&auid) < 0 && errno != ENOSYS) { 226161815Scsjp syslog(LOG_AUTH | LOG_ERR, "getauid: %s", strerror(errno)); 227161815Scsjp errx(1, "Permission denied"); 228161815Scsjp } 229161815Scsjp#endif 230161815Scsjp if (strlen(user) > MAXLOGNAME - 1) { 231161815Scsjp#ifdef USE_BSM_AUDIT 232161815Scsjp if (audit_submit(AUE_su, auid, 233161815Scsjp 1, EPERM, "username too long: '%s'", user)) 234161815Scsjp errx(1, "Permission denied"); 235161815Scsjp#endif 236161815Scsjp errx(1, "username too long"); 237161815Scsjp } 238161815Scsjp 239130409Smarkm nargv = malloc(sizeof(char *) * (size_t)(argc + 4)); 24077220Smarkm if (nargv == NULL) 24177220Smarkm errx(1, "malloc failure"); 24277220Smarkm 24310586Sjoerg nargv[argc + 3] = NULL; 24410586Sjoerg for (i = argc; i >= optind; i--) 24577220Smarkm nargv[i + 3] = argv[i]; 24683373Smarkm np.a = &nargv[i + 3]; 24710586Sjoerg 2481590Srgrimes argv += optind; 2491590Srgrimes 2501590Srgrimes errno = 0; 2511590Srgrimes prio = getpriority(PRIO_PROCESS, 0); 2521590Srgrimes if (errno) 2531590Srgrimes prio = 0; 25477220Smarkm 25577220Smarkm setpriority(PRIO_PROCESS, 0, -2); 25674874Smarkm openlog("su", LOG_CONS, LOG_AUTH); 2571590Srgrimes 25877220Smarkm /* get current login name, real uid and shell */ 2591590Srgrimes ruid = getuid(); 2601590Srgrimes username = getlogin(); 26177220Smarkm pwd = getpwnam(username); 26277220Smarkm if (username == NULL || pwd == NULL || pwd->pw_uid != ruid) 2631590Srgrimes pwd = getpwuid(ruid); 264161815Scsjp if (pwd == NULL) { 265161815Scsjp#ifdef USE_BSM_AUDIT 266161815Scsjp if (audit_submit(AUE_su, auid, 1, EPERM, 267161815Scsjp "unable to determine invoking subject: '%s'", username)) 268161815Scsjp errx(1, "Permission denied"); 269161815Scsjp#endif 2701590Srgrimes errx(1, "who are you?"); 271161815Scsjp } 27277220Smarkm 2731590Srgrimes username = strdup(pwd->pw_name); 2741590Srgrimes if (username == NULL) 27577220Smarkm err(1, "strdup failure"); 27677220Smarkm 27721646Sdavidn if (asme) { 27821646Sdavidn if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') { 27977220Smarkm /* must copy - pwd memory is recycled */ 28077220Smarkm shell = strncpy(shellbuf, pwd->pw_shell, 28177220Smarkm sizeof(shellbuf)); 28277220Smarkm shellbuf[sizeof(shellbuf) - 1] = '\0'; 28377220Smarkm } 28477220Smarkm else { 2851590Srgrimes shell = _PATH_BSHELL; 2861590Srgrimes iscsh = NO; 2871590Srgrimes } 28821646Sdavidn } 2891590Srgrimes 29077220Smarkm /* Do the whole PAM startup thing */ 29174874Smarkm retcode = pam_start("su", user, &conv, &pamh); 29274874Smarkm if (retcode != PAM_SUCCESS) { 29374874Smarkm syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, retcode)); 29474874Smarkm errx(1, "pam_start: %s", pam_strerror(pamh, retcode)); 29574874Smarkm } 29674874Smarkm 297110456Sdes PAM_SET_ITEM(PAM_RUSER, username); 29881529Smarkm 29974874Smarkm mytty = ttyname(STDERR_FILENO); 30074874Smarkm if (!mytty) 30174874Smarkm mytty = "tty"; 30277220Smarkm PAM_SET_ITEM(PAM_TTY, mytty); 30377220Smarkm 30477220Smarkm retcode = pam_authenticate(pamh, 0); 30574874Smarkm if (retcode != PAM_SUCCESS) { 306161815Scsjp#ifdef USE_BSM_AUDIT 307161815Scsjp if (audit_submit(AUE_su, auid, 1, EPERM, "bad su %s to %s on %s", 308161815Scsjp username, user, mytty)) 309161815Scsjp errx(1, "Permission denied"); 310161815Scsjp#endif 311105386Smarkm syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s on %s", 312105386Smarkm username, user, mytty); 31377220Smarkm errx(1, "Sorry"); 31474874Smarkm } 315161815Scsjp#ifdef USE_BSM_AUDIT 316161815Scsjp if (audit_submit(AUE_su, auid, 0, 0, "successful authentication")) 317161815Scsjp errx(1, "Permission denied"); 318161815Scsjp#endif 31977220Smarkm retcode = pam_get_item(pamh, PAM_USER, (const void **)&p); 32077220Smarkm if (retcode == PAM_SUCCESS) 32177220Smarkm user = p; 32277220Smarkm else 32377220Smarkm syslog(LOG_ERR, "pam_get_item(PAM_USER): %s", 32477220Smarkm pam_strerror(pamh, retcode)); 325124166Sdes pwd = getpwnam(user); 326161815Scsjp if (pwd == NULL) { 327161815Scsjp#ifdef USE_BSM_AUDIT 328161815Scsjp if (audit_submit(AUE_su, auid, 1, EPERM, 329161815Scsjp "unknown subject: %s", user)) 330161815Scsjp errx(1, "Permission denied"); 331161815Scsjp#endif 332124166Sdes errx(1, "unknown login: %s", user); 333161815Scsjp } 33474874Smarkm 33577220Smarkm retcode = pam_acct_mgmt(pamh, 0); 33677220Smarkm if (retcode == PAM_NEW_AUTHTOK_REQD) { 33777220Smarkm retcode = pam_chauthtok(pamh, 33877220Smarkm PAM_CHANGE_EXPIRED_AUTHTOK); 33974874Smarkm if (retcode != PAM_SUCCESS) { 340161815Scsjp#ifdef USE_BSM_AUDIT 341161815Scsjp aerr = pam_strerror(pamh, retcode); 342161815Scsjp if (aerr == NULL) 343161815Scsjp aerr = "Unknown PAM error"; 344161815Scsjp if (audit_submit(AUE_su, auid, 1, EPERM, 345161815Scsjp "pam_chauthtok: %s", aerr)) 346161815Scsjp errx(1, "Permission denied"); 347161815Scsjp#endif 34877220Smarkm syslog(LOG_ERR, "pam_chauthtok: %s", 34977220Smarkm pam_strerror(pamh, retcode)); 35074874Smarkm errx(1, "Sorry"); 35174874Smarkm } 35274874Smarkm } 35377220Smarkm if (retcode != PAM_SUCCESS) { 354161815Scsjp#ifdef USE_BSM_AUDIT 355161815Scsjp if (audit_submit(AUE_su, auid, 1, EPERM, "pam_acct_mgmt: %s", 356161815Scsjp pam_strerror(pamh, retcode))) 357161815Scsjp errx(1, "Permission denied"); 358161815Scsjp#endif 35977220Smarkm syslog(LOG_ERR, "pam_acct_mgmt: %s", 36077220Smarkm pam_strerror(pamh, retcode)); 36177220Smarkm errx(1, "Sorry"); 36277220Smarkm } 36374874Smarkm 364124166Sdes /* get target login information */ 36577220Smarkm if (class == NULL) 36630793Sguido lc = login_getpwclass(pwd); 36777220Smarkm else { 368161815Scsjp if (ruid != 0) { 369161815Scsjp#ifdef USE_BSM_AUDIT 370161815Scsjp if (audit_submit(AUE_su, auid, 1, EPERM, 371161815Scsjp "only root may use -c")) 372161815Scsjp errx(1, "Permission denied"); 373161815Scsjp#endif 37430793Sguido errx(1, "only root may use -c"); 375161815Scsjp } 37630793Sguido lc = login_getclass(class); 37730793Sguido if (lc == NULL) 37830793Sguido errx(1, "unknown class: %s", class); 37930793Sguido } 3801590Srgrimes 38177220Smarkm /* if asme and non-standard target shell, must be root */ 3821590Srgrimes if (asme) { 38377220Smarkm if (ruid != 0 && !chshell(pwd->pw_shell)) 384127848Scharnier errx(1, "permission denied (shell)"); 38577220Smarkm } 38677220Smarkm else if (pwd->pw_shell && *pwd->pw_shell) { 3871590Srgrimes shell = pwd->pw_shell; 3881590Srgrimes iscsh = UNSET; 38977220Smarkm } 39077220Smarkm else { 3911590Srgrimes shell = _PATH_BSHELL; 3921590Srgrimes iscsh = NO; 3931590Srgrimes } 3941590Srgrimes 3951590Srgrimes /* if we're forking a csh, we want to slightly muck the args */ 3961590Srgrimes if (iscsh == UNSET) { 39714440Smarkm p = strrchr(shell, '/'); 39814440Smarkm if (p) 3991590Srgrimes ++p; 4001590Srgrimes else 4011590Srgrimes p = shell; 40277220Smarkm iscsh = strcmp(p, "csh") ? (strcmp(p, "tcsh") ? NO : YES) : YES; 4031590Srgrimes } 40477220Smarkm setpriority(PRIO_PROCESS, 0, prio); 4051590Srgrimes 40621646Sdavidn /* 40777220Smarkm * PAM modules might add supplementary groups in pam_setcred(), so 40877220Smarkm * initialize them first. 40974874Smarkm */ 41074874Smarkm if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0) 41174874Smarkm err(1, "setusercontext"); 41274874Smarkm 41374874Smarkm retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED); 414113262Sdes if (retcode != PAM_SUCCESS) { 415113262Sdes syslog(LOG_ERR, "pam_setcred: %s", 41677220Smarkm pam_strerror(pamh, retcode)); 417113262Sdes errx(1, "failed to establish credentials."); 418113262Sdes } 419113262Sdes if (asthem) { 420113262Sdes retcode = pam_open_session(pamh, 0); 421113262Sdes if (retcode != PAM_SUCCESS) { 422113262Sdes syslog(LOG_ERR, "pam_open_session: %s", 423113262Sdes pam_strerror(pamh, retcode)); 424113262Sdes errx(1, "failed to open session."); 425113262Sdes } 426113262Sdes } 42774874Smarkm 42874874Smarkm /* 42974874Smarkm * We must fork() before setuid() because we need to call 43074874Smarkm * pam_setcred(pamh, PAM_DELETE_CRED) as root. 43174874Smarkm */ 43298837Sdillon sa.sa_flags = SA_RESTART; 433105362Stjr sa.sa_handler = SIG_IGN; 43498837Sdillon sigemptyset(&sa.sa_mask); 43598837Sdillon sigaction(SIGINT, &sa, &sa_int); 43698837Sdillon sigaction(SIGQUIT, &sa, &sa_quit); 437112695Sdavidxu sigaction(SIGPIPE, &sa, &sa_pipe); 438112085Sdavidxu sa.sa_handler = SIG_DFL; 439112085Sdavidxu sigaction(SIGTSTP, &sa, NULL); 44074874Smarkm statusp = 1; 441112695Sdavidxu if (pipe(fds) == -1) { 442130409Smarkm PAM_END(); 443112695Sdavidxu err(1, "pipe"); 444112695Sdavidxu } 44577220Smarkm child_pid = fork(); 44677220Smarkm switch (child_pid) { 44774874Smarkm default: 448122013Sdavidxu sa.sa_handler = SIG_IGN; 449122013Sdavidxu sigaction(SIGTTOU, &sa, NULL); 450112695Sdavidxu close(fds[0]); 451153985Sbrian setpgid(child_pid, child_pid); 452153985Sbrian if (tcgetpgrp(STDERR_FILENO) == getpgrp()) 453153985Sbrian tcsetpgrp(STDERR_FILENO, child_pid); 454112695Sdavidxu close(fds[1]); 455112695Sdavidxu sigaction(SIGPIPE, &sa_pipe, NULL); 456113262Sdes while ((pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) { 45777220Smarkm if (WIFSTOPPED(statusp)) { 458153985Sbrian child_pgrp = getpgid(child_pid); 459153985Sbrian if (tcgetpgrp(STDERR_FILENO) == child_pgrp) 460153985Sbrian tcsetpgrp(STDERR_FILENO, getpgrp()); 461153966Sbrian kill(getpid(), SIGSTOP); 462153985Sbrian if (tcgetpgrp(STDERR_FILENO) == getpgrp()) { 463153985Sbrian child_pgrp = getpgid(child_pid); 464153985Sbrian tcsetpgrp(STDERR_FILENO, child_pgrp); 465153985Sbrian } 466153966Sbrian kill(child_pid, SIGCONT); 46777220Smarkm statusp = 1; 46877220Smarkm continue; 46977220Smarkm } 47077220Smarkm break; 47174874Smarkm } 472153985Sbrian child_pgrp = getpgid(child_pid); 473153985Sbrian if (tcgetpgrp(STDERR_FILENO) == child_pgrp) 474153985Sbrian tcsetpgrp(STDERR_FILENO, getpgrp()); 475113262Sdes if (pid == -1) 47677220Smarkm err(1, "waitpid"); 47781528Smarkm PAM_END(); 478130409Smarkm exit(WEXITSTATUS(statusp)); 47974874Smarkm case -1: 480130409Smarkm PAM_END(); 48177220Smarkm err(1, "fork"); 48274874Smarkm case 0: 483112695Sdavidxu close(fds[1]); 484112695Sdavidxu read(fds[0], &temp, 1); 485112695Sdavidxu close(fds[0]); 486112695Sdavidxu sigaction(SIGPIPE, &sa_pipe, NULL); 48798837Sdillon sigaction(SIGINT, &sa_int, NULL); 48898837Sdillon sigaction(SIGQUIT, &sa_quit, NULL); 489112695Sdavidxu 49077220Smarkm /* 49177220Smarkm * Set all user context except for: Environmental variables 49277220Smarkm * Umask Login records (wtmp, etc) Path 49377220Smarkm */ 49477220Smarkm setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK | 495105758Srwatson LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP | 496105758Srwatson LOGIN_SETMAC); 49777220Smarkm /* 498105758Srwatson * If -s is present, also set the MAC label. 499105758Srwatson */ 500105758Srwatson if (setmaclabel) 501105758Srwatson setwhat |= LOGIN_SETMAC; 502105758Srwatson /* 50377220Smarkm * Don't touch resource/priority settings if -m has been used 50477220Smarkm * or -l and -c hasn't, and we're not su'ing to root. 50577220Smarkm */ 50677220Smarkm if ((asme || (!asthem && class == NULL)) && pwd->pw_uid) 50777220Smarkm setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES); 50877220Smarkm if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0) 50977220Smarkm err(1, "setusercontext"); 51074874Smarkm 51177220Smarkm if (!asme) { 51277220Smarkm if (asthem) { 51377220Smarkm p = getenv("TERM"); 51477220Smarkm environ = &cleanenv; 515113262Sdes } 51669427Srwatson 517113262Sdes if (asthem || pwd->pw_uid) 518113262Sdes setenv("USER", pwd->pw_name, 1); 519113262Sdes setenv("HOME", pwd->pw_dir, 1); 520113262Sdes setenv("SHELL", shell, 1); 521113262Sdes 522113262Sdes if (asthem) { 52377220Smarkm /* 52477220Smarkm * Add any environmental variables that the 52577220Smarkm * PAM modules may have set. 52677220Smarkm */ 52777220Smarkm environ_pam = pam_getenvlist(pamh); 52877220Smarkm if (environ_pam) 52977220Smarkm export_pam_environment(); 5301590Srgrimes 53177220Smarkm /* set the su'd user's environment & umask */ 53277220Smarkm setusercontext(lc, pwd, pwd->pw_uid, 53377220Smarkm LOGIN_SETPATH | LOGIN_SETUMASK | 53477220Smarkm LOGIN_SETENV); 53577220Smarkm if (p) 53677220Smarkm setenv("TERM", p, 1); 537162761Sluoqi 538162761Sluoqi p = pam_getenv(pamh, "HOME"); 539162761Sluoqi if (chdir(p ? p : pwd->pw_dir) < 0) 540162761Sluoqi errx(1, "no directory"); 54177220Smarkm } 54277220Smarkm } 54377220Smarkm login_close(lc); 54474874Smarkm 54577220Smarkm if (iscsh == YES) { 54677220Smarkm if (fastlogin) 54783373Smarkm *np.a-- = "-f"; 54877220Smarkm if (asme) 54983373Smarkm *np.a-- = "-m"; 5501590Srgrimes } 55177220Smarkm /* csh strips the first character... */ 55283373Smarkm *np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su"; 55374874Smarkm 55477220Smarkm if (ruid != 0) 55577220Smarkm syslog(LOG_NOTICE, "%s to %s%s", username, user, 55677220Smarkm ontty()); 55774874Smarkm 55883373Smarkm execv(shell, np.b); 55977220Smarkm err(1, "%s", shell); 5601590Srgrimes } 5611590Srgrimes} 5621590Srgrimes 563130409Smarkmstatic void 56477220Smarkmexport_pam_environment(void) 56574874Smarkm{ 56674874Smarkm char **pp; 56774874Smarkm 56874874Smarkm for (pp = environ_pam; *pp != NULL; pp++) { 56974874Smarkm if (ok_to_export(*pp)) 570169177Sache putenv(*pp); 571169177Sache free(*pp); 57274874Smarkm } 57374874Smarkm} 57474874Smarkm 57574874Smarkm/* 57674874Smarkm * Sanity checks on PAM environmental variables: 57774874Smarkm * - Make sure there is an '=' in the string. 57874874Smarkm * - Make sure the string doesn't run on too long. 57974874Smarkm * - Do not export certain variables. This list was taken from the 58074874Smarkm * Solaris pam_putenv(3) man page. 581113262Sdes * Note that if the user is chrooted, PAM may have a better idea than we 582113262Sdes * do of where her home directory is. 58374874Smarkm */ 58474874Smarkmstatic int 58577220Smarkmok_to_export(const char *s) 58674874Smarkm{ 58774874Smarkm static const char *noexport[] = { 588113262Sdes "SHELL", /* "HOME", */ "LOGNAME", "MAIL", "CDPATH", 58974874Smarkm "IFS", "PATH", NULL 59074874Smarkm }; 59174874Smarkm const char **pp; 59274874Smarkm size_t n; 59374874Smarkm 59474874Smarkm if (strlen(s) > 1024 || strchr(s, '=') == NULL) 59574874Smarkm return 0; 59674874Smarkm if (strncmp(s, "LD_", 3) == 0) 59774874Smarkm return 0; 59874874Smarkm for (pp = noexport; *pp != NULL; pp++) { 59974874Smarkm n = strlen(*pp); 60074874Smarkm if (s[n] == '=' && strncmp(s, *pp, n) == 0) 60174874Smarkm return 0; 60274874Smarkm } 60374874Smarkm return 1; 60474874Smarkm} 60574874Smarkm 60628099Scharnierstatic void 60777220Smarkmusage(void) 60828099Scharnier{ 60981702Sru 610105758Srwatson fprintf(stderr, "usage: su [-] [-flms] [-c class] [login [args]]\n"); 61181702Sru exit(1); 612130409Smarkm /* NOTREACHED */ 61328099Scharnier} 61428099Scharnier 61577220Smarkmstatic int 616130409Smarkmchshell(const char *sh) 6171590Srgrimes{ 61877220Smarkm int r; 6191590Srgrimes char *cp; 6201590Srgrimes 62177220Smarkm r = 0; 62221646Sdavidn setusershell(); 623121236Scognet while ((cp = getusershell()) != NULL && !r) 624121236Scognet r = (strcmp(cp, sh) == 0); 62521646Sdavidn endusershell(); 62621646Sdavidn return r; 6271590Srgrimes} 6281590Srgrimes 62977220Smarkmstatic char * 63077220Smarkmontty(void) 6311590Srgrimes{ 6321590Srgrimes char *p; 6331590Srgrimes static char buf[MAXPATHLEN + 4]; 6341590Srgrimes 6351590Srgrimes buf[0] = 0; 63614440Smarkm p = ttyname(STDERR_FILENO); 63714440Smarkm if (p) 6381590Srgrimes snprintf(buf, sizeof(buf), " on %s", p); 63977220Smarkm return buf; 6401590Srgrimes} 641