su.c revision 254259
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 * 4. Neither the name of the University nor the names of its contributors 441590Srgrimes * may be used to endorse or promote products derived from this software 451590Srgrimes * without specific prior written permission. 461590Srgrimes * 471590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 481590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 491590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 501590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 511590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 521590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 531590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 541590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 551590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 561590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 571590Srgrimes * SUCH DAMAGE. 581590Srgrimes */ 591590Srgrimes 601590Srgrimes#ifndef lint 6114440Smarkmstatic const char copyright[] = 621590Srgrimes"@(#) Copyright (c) 1988, 1993, 1994\n\ 631590Srgrimes The Regents of the University of California. All rights reserved.\n"; 641590Srgrimes#endif /* not lint */ 651590Srgrimes 66127848Scharnier#if 0 671590Srgrimes#ifndef lint 681590Srgrimesstatic char sccsid[] = "@(#)su.c 8.3 (Berkeley) 4/2/94"; 69127848Scharnier#endif /* not lint */ 7028099Scharnier#endif 711590Srgrimes 72127848Scharnier#include <sys/cdefs.h> 73127848Scharnier__FBSDID("$FreeBSD: head/usr.bin/su/su.c 254259 2013-08-12 21:01:01Z trasz $"); 74127848Scharnier 751590Srgrimes#include <sys/param.h> 761590Srgrimes#include <sys/time.h> 771590Srgrimes#include <sys/resource.h> 7877220Smarkm#include <sys/wait.h> 791590Srgrimes 80161815Scsjp#ifdef USE_BSM_AUDIT 81161815Scsjp#include <bsm/libbsm.h> 82161815Scsjp#include <bsm/audit_uevents.h> 83161815Scsjp#endif 84161815Scsjp 851590Srgrimes#include <err.h> 861590Srgrimes#include <errno.h> 87200462Sdelphij#include <grp.h> 8877220Smarkm#include <login_cap.h> 891590Srgrimes#include <paths.h> 901590Srgrimes#include <pwd.h> 9177220Smarkm#include <signal.h> 921590Srgrimes#include <stdio.h> 931590Srgrimes#include <stdlib.h> 941590Srgrimes#include <string.h> 951590Srgrimes#include <syslog.h> 961590Srgrimes#include <unistd.h> 97161815Scsjp#include <stdarg.h> 9821646Sdavidn 9974874Smarkm#include <security/pam_appl.h> 10091745Sdes#include <security/openpam.h> 10174874Smarkm 102113262Sdes#define PAM_END() do { \ 103113262Sdes int local_ret; \ 104113262Sdes if (pamh != NULL) { \ 105113262Sdes local_ret = pam_setcred(pamh, PAM_DELETE_CRED); \ 106113262Sdes if (local_ret != PAM_SUCCESS) \ 107113262Sdes syslog(LOG_ERR, "pam_setcred: %s", \ 108113262Sdes pam_strerror(pamh, local_ret)); \ 109113262Sdes if (asthem) { \ 110113262Sdes local_ret = pam_close_session(pamh, 0); \ 111113262Sdes if (local_ret != PAM_SUCCESS) \ 112113262Sdes syslog(LOG_ERR, "pam_close_session: %s",\ 113113262Sdes pam_strerror(pamh, local_ret)); \ 114113262Sdes } \ 115113262Sdes local_ret = pam_end(pamh, local_ret); \ 116113262Sdes if (local_ret != PAM_SUCCESS) \ 117113262Sdes syslog(LOG_ERR, "pam_end: %s", \ 118113262Sdes pam_strerror(pamh, local_ret)); \ 119113262Sdes } \ 12077220Smarkm} while (0) 12174874Smarkm 12274874Smarkm 123113262Sdes#define PAM_SET_ITEM(what, item) do { \ 124113262Sdes int local_ret; \ 125113262Sdes local_ret = pam_set_item(pamh, what, item); \ 126113262Sdes if (local_ret != PAM_SUCCESS) { \ 127113262Sdes syslog(LOG_ERR, "pam_set_item(" #what "): %s", \ 128113262Sdes pam_strerror(pamh, local_ret)); \ 129113262Sdes errx(1, "pam_set_item(" #what "): %s", \ 130113262Sdes pam_strerror(pamh, local_ret)); \ 131130409Smarkm /* NOTREACHED */ \ 132113262Sdes } \ 13377220Smarkm} while (0) 1343702Spst 13577220Smarkmenum tristate { UNSET, YES, NO }; 1361590Srgrimes 13777220Smarkmstatic pam_handle_t *pamh = NULL; 13877220Smarkmstatic char **environ_pam; 13977220Smarkm 14077220Smarkmstatic char *ontty(void); 141130409Smarkmstatic int chshell(const char *); 142130409Smarkmstatic void usage(void) __dead2; 143130409Smarkmstatic void export_pam_environment(void); 14477220Smarkmstatic int ok_to_export(const char *); 14577220Smarkm 14677220Smarkmextern char **environ; 14777220Smarkm 1481590Srgrimesint 14977220Smarkmmain(int argc, char *argv[]) 1501590Srgrimes{ 151130409Smarkm static char *cleanenv; 152220055Sume struct passwd *pwd = NULL; 15391745Sdes struct pam_conv conv = { openpam_ttyconv, NULL }; 15477220Smarkm enum tristate iscsh; 15577220Smarkm login_cap_t *lc; 15683373Smarkm union { 15783373Smarkm const char **a; 15883373Smarkm char * const *b; 15997377Sdes } np; 16077220Smarkm uid_t ruid; 161153985Sbrian pid_t child_pid, child_pgrp, pid; 162130409Smarkm int asme, ch, asthem, fastlogin, prio, i, retcode, 163113262Sdes statusp, setmaclabel; 164130409Smarkm u_int setwhat; 165130409Smarkm char *username, *class, shellbuf[MAXPATHLEN]; 16683373Smarkm const char *p, *user, *shell, *mytty, **nargv; 167179547Sdwmalone const void *v; 168112695Sdavidxu struct sigaction sa, sa_int, sa_quit, sa_pipe; 169112695Sdavidxu int temp, fds[2]; 170161815Scsjp#ifdef USE_BSM_AUDIT 171161815Scsjp const char *aerr; 172161815Scsjp au_id_t auid; 173161815Scsjp#endif 17498837Sdillon 17577220Smarkm shell = class = cleanenv = NULL; 17677220Smarkm asme = asthem = fastlogin = statusp = 0; 17710586Sjoerg user = "root"; 17877220Smarkm iscsh = UNSET; 179105758Srwatson setmaclabel = 0; 18077220Smarkm 181105758Srwatson while ((ch = getopt(argc, argv, "-flmsc:")) != -1) 18277220Smarkm switch ((char)ch) { 1831590Srgrimes case 'f': 1841590Srgrimes fastlogin = 1; 1851590Srgrimes break; 1861590Srgrimes case '-': 1871590Srgrimes case 'l': 1881590Srgrimes asme = 0; 1891590Srgrimes asthem = 1; 1901590Srgrimes break; 1911590Srgrimes case 'm': 1921590Srgrimes asme = 1; 1931590Srgrimes asthem = 0; 1941590Srgrimes break; 195105758Srwatson case 's': 196105758Srwatson setmaclabel = 1; 197105758Srwatson break; 19830793Sguido case 'c': 19930793Sguido class = optarg; 20030793Sguido break; 2011590Srgrimes case '?': 2021590Srgrimes default: 20328099Scharnier usage(); 204130409Smarkm /* NOTREACHED */ 20528099Scharnier } 20639538Sroberto 20739538Sroberto if (optind < argc) 20810586Sjoerg user = argv[optind++]; 20910586Sjoerg 21028612Sjoerg if (user == NULL) 21128612Sjoerg usage(); 212130409Smarkm /* NOTREACHED */ 21328612Sjoerg 214140392Srwatson /* 215140392Srwatson * Try to provide more helpful debugging output if su(1) is running 216140392Srwatson * non-setuid, or was run from a file system not mounted setuid. 217140392Srwatson */ 218140392Srwatson if (geteuid() != 0) 219140392Srwatson errx(1, "not running setuid"); 220140392Srwatson 221161815Scsjp#ifdef USE_BSM_AUDIT 222161815Scsjp if (getauid(&auid) < 0 && errno != ENOSYS) { 223161815Scsjp syslog(LOG_AUTH | LOG_ERR, "getauid: %s", strerror(errno)); 224161815Scsjp errx(1, "Permission denied"); 225161815Scsjp } 226161815Scsjp#endif 227161815Scsjp if (strlen(user) > MAXLOGNAME - 1) { 228161815Scsjp#ifdef USE_BSM_AUDIT 229161815Scsjp if (audit_submit(AUE_su, auid, 230190700Scsjp EPERM, 1, "username too long: '%s'", user)) 231161815Scsjp errx(1, "Permission denied"); 232161815Scsjp#endif 233161815Scsjp errx(1, "username too long"); 234161815Scsjp } 235161815Scsjp 236130409Smarkm nargv = malloc(sizeof(char *) * (size_t)(argc + 4)); 23777220Smarkm if (nargv == NULL) 23877220Smarkm errx(1, "malloc failure"); 23977220Smarkm 24010586Sjoerg nargv[argc + 3] = NULL; 24110586Sjoerg for (i = argc; i >= optind; i--) 24277220Smarkm nargv[i + 3] = argv[i]; 24383373Smarkm np.a = &nargv[i + 3]; 24410586Sjoerg 2451590Srgrimes argv += optind; 2461590Srgrimes 2471590Srgrimes errno = 0; 2481590Srgrimes prio = getpriority(PRIO_PROCESS, 0); 2491590Srgrimes if (errno) 2501590Srgrimes prio = 0; 25177220Smarkm 25277220Smarkm setpriority(PRIO_PROCESS, 0, -2); 25374874Smarkm openlog("su", LOG_CONS, LOG_AUTH); 2541590Srgrimes 25577220Smarkm /* get current login name, real uid and shell */ 2561590Srgrimes ruid = getuid(); 2571590Srgrimes username = getlogin(); 258220055Sume if (username != NULL) 259220055Sume pwd = getpwnam(username); 260220055Sume if (pwd == NULL || pwd->pw_uid != ruid) 2611590Srgrimes pwd = getpwuid(ruid); 262161815Scsjp if (pwd == NULL) { 263161815Scsjp#ifdef USE_BSM_AUDIT 264190700Scsjp if (audit_submit(AUE_su, auid, EPERM, 1, 265161815Scsjp "unable to determine invoking subject: '%s'", username)) 266161815Scsjp errx(1, "Permission denied"); 267161815Scsjp#endif 2681590Srgrimes errx(1, "who are you?"); 269161815Scsjp } 27077220Smarkm 2711590Srgrimes username = strdup(pwd->pw_name); 2721590Srgrimes if (username == NULL) 27377220Smarkm err(1, "strdup failure"); 27477220Smarkm 27521646Sdavidn if (asme) { 27621646Sdavidn if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') { 27777220Smarkm /* must copy - pwd memory is recycled */ 27877220Smarkm shell = strncpy(shellbuf, pwd->pw_shell, 27977220Smarkm sizeof(shellbuf)); 28077220Smarkm shellbuf[sizeof(shellbuf) - 1] = '\0'; 28177220Smarkm } 28277220Smarkm else { 2831590Srgrimes shell = _PATH_BSHELL; 2841590Srgrimes iscsh = NO; 2851590Srgrimes } 28621646Sdavidn } 2871590Srgrimes 28877220Smarkm /* Do the whole PAM startup thing */ 28974874Smarkm retcode = pam_start("su", user, &conv, &pamh); 29074874Smarkm if (retcode != PAM_SUCCESS) { 29174874Smarkm syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, retcode)); 29274874Smarkm errx(1, "pam_start: %s", pam_strerror(pamh, retcode)); 29374874Smarkm } 29474874Smarkm 295110456Sdes PAM_SET_ITEM(PAM_RUSER, username); 29681529Smarkm 29774874Smarkm mytty = ttyname(STDERR_FILENO); 29874874Smarkm if (!mytty) 29974874Smarkm mytty = "tty"; 30077220Smarkm PAM_SET_ITEM(PAM_TTY, mytty); 30177220Smarkm 30277220Smarkm retcode = pam_authenticate(pamh, 0); 30374874Smarkm if (retcode != PAM_SUCCESS) { 304161815Scsjp#ifdef USE_BSM_AUDIT 305190700Scsjp if (audit_submit(AUE_su, auid, EPERM, 1, "bad su %s to %s on %s", 306161815Scsjp username, user, mytty)) 307161815Scsjp errx(1, "Permission denied"); 308161815Scsjp#endif 309105386Smarkm syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s on %s", 310105386Smarkm username, user, mytty); 31177220Smarkm errx(1, "Sorry"); 31274874Smarkm } 313161815Scsjp#ifdef USE_BSM_AUDIT 314161815Scsjp if (audit_submit(AUE_su, auid, 0, 0, "successful authentication")) 315161815Scsjp errx(1, "Permission denied"); 316161815Scsjp#endif 317179547Sdwmalone retcode = pam_get_item(pamh, PAM_USER, &v); 31877220Smarkm if (retcode == PAM_SUCCESS) 319179547Sdwmalone user = v; 32077220Smarkm else 32177220Smarkm syslog(LOG_ERR, "pam_get_item(PAM_USER): %s", 32277220Smarkm pam_strerror(pamh, retcode)); 323124166Sdes pwd = getpwnam(user); 324161815Scsjp if (pwd == NULL) { 325161815Scsjp#ifdef USE_BSM_AUDIT 326190700Scsjp if (audit_submit(AUE_su, auid, EPERM, 1, 327161815Scsjp "unknown subject: %s", user)) 328161815Scsjp errx(1, "Permission denied"); 329161815Scsjp#endif 330124166Sdes errx(1, "unknown login: %s", user); 331161815Scsjp } 33274874Smarkm 33377220Smarkm retcode = pam_acct_mgmt(pamh, 0); 33477220Smarkm if (retcode == PAM_NEW_AUTHTOK_REQD) { 33577220Smarkm retcode = pam_chauthtok(pamh, 33677220Smarkm PAM_CHANGE_EXPIRED_AUTHTOK); 33774874Smarkm if (retcode != PAM_SUCCESS) { 338161815Scsjp#ifdef USE_BSM_AUDIT 339161815Scsjp aerr = pam_strerror(pamh, retcode); 340161815Scsjp if (aerr == NULL) 341161815Scsjp aerr = "Unknown PAM error"; 342190700Scsjp if (audit_submit(AUE_su, auid, EPERM, 1, 343161815Scsjp "pam_chauthtok: %s", aerr)) 344161815Scsjp errx(1, "Permission denied"); 345161815Scsjp#endif 34677220Smarkm syslog(LOG_ERR, "pam_chauthtok: %s", 34777220Smarkm pam_strerror(pamh, retcode)); 34874874Smarkm errx(1, "Sorry"); 34974874Smarkm } 35074874Smarkm } 35177220Smarkm if (retcode != PAM_SUCCESS) { 352161815Scsjp#ifdef USE_BSM_AUDIT 353190700Scsjp if (audit_submit(AUE_su, auid, EPERM, 1, "pam_acct_mgmt: %s", 354161815Scsjp pam_strerror(pamh, retcode))) 355161815Scsjp errx(1, "Permission denied"); 356161815Scsjp#endif 35777220Smarkm syslog(LOG_ERR, "pam_acct_mgmt: %s", 35877220Smarkm pam_strerror(pamh, retcode)); 35977220Smarkm errx(1, "Sorry"); 36077220Smarkm } 36174874Smarkm 362124166Sdes /* get target login information */ 36377220Smarkm if (class == NULL) 36430793Sguido lc = login_getpwclass(pwd); 36577220Smarkm else { 366161815Scsjp if (ruid != 0) { 367161815Scsjp#ifdef USE_BSM_AUDIT 368190700Scsjp if (audit_submit(AUE_su, auid, EPERM, 1, 369161815Scsjp "only root may use -c")) 370161815Scsjp errx(1, "Permission denied"); 371161815Scsjp#endif 37230793Sguido errx(1, "only root may use -c"); 373161815Scsjp } 37430793Sguido lc = login_getclass(class); 37530793Sguido if (lc == NULL) 376254259Strasz err(1, "login_getclass"); 377254259Strasz if (lc->lc_class == NULL || strcmp(class, lc->lc_class) != 0) 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 } 472172749Sdavidxu tcsetpgrp(STDERR_FILENO, getpgrp()); 473113262Sdes if (pid == -1) 47477220Smarkm err(1, "waitpid"); 47581528Smarkm PAM_END(); 476130409Smarkm exit(WEXITSTATUS(statusp)); 47774874Smarkm case -1: 478130409Smarkm PAM_END(); 47977220Smarkm err(1, "fork"); 48074874Smarkm case 0: 481112695Sdavidxu close(fds[1]); 482112695Sdavidxu read(fds[0], &temp, 1); 483112695Sdavidxu close(fds[0]); 484112695Sdavidxu sigaction(SIGPIPE, &sa_pipe, NULL); 48598837Sdillon sigaction(SIGINT, &sa_int, NULL); 48698837Sdillon sigaction(SIGQUIT, &sa_quit, NULL); 487112695Sdavidxu 48877220Smarkm /* 48977220Smarkm * Set all user context except for: Environmental variables 49077220Smarkm * Umask Login records (wtmp, etc) Path 49177220Smarkm */ 49277220Smarkm setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK | 493105758Srwatson LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP | 494105758Srwatson LOGIN_SETMAC); 49577220Smarkm /* 496105758Srwatson * If -s is present, also set the MAC label. 497105758Srwatson */ 498105758Srwatson if (setmaclabel) 499105758Srwatson setwhat |= LOGIN_SETMAC; 500105758Srwatson /* 50177220Smarkm * Don't touch resource/priority settings if -m has been used 50277220Smarkm * or -l and -c hasn't, and we're not su'ing to root. 50377220Smarkm */ 50477220Smarkm if ((asme || (!asthem && class == NULL)) && pwd->pw_uid) 50577220Smarkm setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES); 50677220Smarkm if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0) 50777220Smarkm err(1, "setusercontext"); 50874874Smarkm 50977220Smarkm if (!asme) { 51077220Smarkm if (asthem) { 51177220Smarkm p = getenv("TERM"); 51277220Smarkm environ = &cleanenv; 513113262Sdes } 51469427Srwatson 515113262Sdes if (asthem || pwd->pw_uid) 516113262Sdes setenv("USER", pwd->pw_name, 1); 517113262Sdes setenv("HOME", pwd->pw_dir, 1); 518113262Sdes setenv("SHELL", shell, 1); 519113262Sdes 520113262Sdes if (asthem) { 52177220Smarkm /* 52277220Smarkm * Add any environmental variables that the 52377220Smarkm * PAM modules may have set. 52477220Smarkm */ 52577220Smarkm environ_pam = pam_getenvlist(pamh); 52677220Smarkm if (environ_pam) 52777220Smarkm export_pam_environment(); 5281590Srgrimes 52977220Smarkm /* set the su'd user's environment & umask */ 53077220Smarkm setusercontext(lc, pwd, pwd->pw_uid, 53177220Smarkm LOGIN_SETPATH | LOGIN_SETUMASK | 53277220Smarkm LOGIN_SETENV); 53377220Smarkm if (p) 53477220Smarkm setenv("TERM", p, 1); 535162761Sluoqi 536162761Sluoqi p = pam_getenv(pamh, "HOME"); 537162761Sluoqi if (chdir(p ? p : pwd->pw_dir) < 0) 538162761Sluoqi errx(1, "no directory"); 53977220Smarkm } 54077220Smarkm } 54177220Smarkm login_close(lc); 54274874Smarkm 54377220Smarkm if (iscsh == YES) { 54477220Smarkm if (fastlogin) 54583373Smarkm *np.a-- = "-f"; 54677220Smarkm if (asme) 54783373Smarkm *np.a-- = "-m"; 5481590Srgrimes } 54977220Smarkm /* csh strips the first character... */ 55083373Smarkm *np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su"; 55174874Smarkm 55277220Smarkm if (ruid != 0) 55377220Smarkm syslog(LOG_NOTICE, "%s to %s%s", username, user, 55477220Smarkm ontty()); 55574874Smarkm 55683373Smarkm execv(shell, np.b); 55777220Smarkm err(1, "%s", shell); 5581590Srgrimes } 5591590Srgrimes} 5601590Srgrimes 561130409Smarkmstatic void 56277220Smarkmexport_pam_environment(void) 56374874Smarkm{ 56474874Smarkm char **pp; 565171195Sscf char *p; 56674874Smarkm 56774874Smarkm for (pp = environ_pam; *pp != NULL; pp++) { 568171195Sscf if (ok_to_export(*pp)) { 569171195Sscf p = strchr(*pp, '='); 570171195Sscf *p = '\0'; 571171195Sscf setenv(*pp, p + 1, 1); 572171195Sscf } 573169177Sache free(*pp); 57474874Smarkm } 57574874Smarkm} 57674874Smarkm 57774874Smarkm/* 57874874Smarkm * Sanity checks on PAM environmental variables: 57974874Smarkm * - Make sure there is an '=' in the string. 58074874Smarkm * - Make sure the string doesn't run on too long. 58174874Smarkm * - Do not export certain variables. This list was taken from the 58274874Smarkm * Solaris pam_putenv(3) man page. 583113262Sdes * Note that if the user is chrooted, PAM may have a better idea than we 584113262Sdes * do of where her home directory is. 58574874Smarkm */ 58674874Smarkmstatic int 58777220Smarkmok_to_export(const char *s) 58874874Smarkm{ 58974874Smarkm static const char *noexport[] = { 590113262Sdes "SHELL", /* "HOME", */ "LOGNAME", "MAIL", "CDPATH", 59174874Smarkm "IFS", "PATH", NULL 59274874Smarkm }; 59374874Smarkm const char **pp; 59474874Smarkm size_t n; 59574874Smarkm 59674874Smarkm if (strlen(s) > 1024 || strchr(s, '=') == NULL) 59774874Smarkm return 0; 59874874Smarkm if (strncmp(s, "LD_", 3) == 0) 59974874Smarkm return 0; 60074874Smarkm for (pp = noexport; *pp != NULL; pp++) { 60174874Smarkm n = strlen(*pp); 60274874Smarkm if (s[n] == '=' && strncmp(s, *pp, n) == 0) 60374874Smarkm return 0; 60474874Smarkm } 60574874Smarkm return 1; 60674874Smarkm} 60774874Smarkm 60828099Scharnierstatic void 60977220Smarkmusage(void) 61028099Scharnier{ 61181702Sru 612105758Srwatson fprintf(stderr, "usage: su [-] [-flms] [-c class] [login [args]]\n"); 61381702Sru exit(1); 614130409Smarkm /* NOTREACHED */ 61528099Scharnier} 61628099Scharnier 61777220Smarkmstatic int 618130409Smarkmchshell(const char *sh) 6191590Srgrimes{ 62077220Smarkm int r; 6211590Srgrimes char *cp; 6221590Srgrimes 62377220Smarkm r = 0; 62421646Sdavidn setusershell(); 625121236Scognet while ((cp = getusershell()) != NULL && !r) 626121236Scognet r = (strcmp(cp, sh) == 0); 62721646Sdavidn endusershell(); 62821646Sdavidn return r; 6291590Srgrimes} 6301590Srgrimes 63177220Smarkmstatic char * 63277220Smarkmontty(void) 6331590Srgrimes{ 6341590Srgrimes char *p; 6351590Srgrimes static char buf[MAXPATHLEN + 4]; 6361590Srgrimes 6371590Srgrimes buf[0] = 0; 63814440Smarkm p = ttyname(STDERR_FILENO); 63914440Smarkm if (p) 6401590Srgrimes snprintf(buf, sizeof(buf), " on %s", p); 64177220Smarkm return buf; 6421590Srgrimes} 643