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$"); 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) 37630793Sguido errx(1, "unknown class: %s", class); 37730793Sguido } 3781590Srgrimes 37977220Smarkm /* if asme and non-standard target shell, must be root */ 3801590Srgrimes if (asme) { 38177220Smarkm if (ruid != 0 && !chshell(pwd->pw_shell)) 382127848Scharnier errx(1, "permission denied (shell)"); 38377220Smarkm } 38477220Smarkm else if (pwd->pw_shell && *pwd->pw_shell) { 3851590Srgrimes shell = pwd->pw_shell; 3861590Srgrimes iscsh = UNSET; 38777220Smarkm } 38877220Smarkm else { 3891590Srgrimes shell = _PATH_BSHELL; 3901590Srgrimes iscsh = NO; 3911590Srgrimes } 3921590Srgrimes 3931590Srgrimes /* if we're forking a csh, we want to slightly muck the args */ 3941590Srgrimes if (iscsh == UNSET) { 39514440Smarkm p = strrchr(shell, '/'); 39614440Smarkm if (p) 3971590Srgrimes ++p; 3981590Srgrimes else 3991590Srgrimes p = shell; 40077220Smarkm iscsh = strcmp(p, "csh") ? (strcmp(p, "tcsh") ? NO : YES) : YES; 4011590Srgrimes } 40277220Smarkm setpriority(PRIO_PROCESS, 0, prio); 4031590Srgrimes 40421646Sdavidn /* 40577220Smarkm * PAM modules might add supplementary groups in pam_setcred(), so 40677220Smarkm * initialize them first. 40774874Smarkm */ 40874874Smarkm if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0) 40974874Smarkm err(1, "setusercontext"); 41074874Smarkm 41174874Smarkm retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED); 412113262Sdes if (retcode != PAM_SUCCESS) { 413113262Sdes syslog(LOG_ERR, "pam_setcred: %s", 41477220Smarkm pam_strerror(pamh, retcode)); 415113262Sdes errx(1, "failed to establish credentials."); 416113262Sdes } 417113262Sdes if (asthem) { 418113262Sdes retcode = pam_open_session(pamh, 0); 419113262Sdes if (retcode != PAM_SUCCESS) { 420113262Sdes syslog(LOG_ERR, "pam_open_session: %s", 421113262Sdes pam_strerror(pamh, retcode)); 422113262Sdes errx(1, "failed to open session."); 423113262Sdes } 424113262Sdes } 42574874Smarkm 42674874Smarkm /* 42774874Smarkm * We must fork() before setuid() because we need to call 42874874Smarkm * pam_setcred(pamh, PAM_DELETE_CRED) as root. 42974874Smarkm */ 43098837Sdillon sa.sa_flags = SA_RESTART; 431105362Stjr sa.sa_handler = SIG_IGN; 43298837Sdillon sigemptyset(&sa.sa_mask); 43398837Sdillon sigaction(SIGINT, &sa, &sa_int); 43498837Sdillon sigaction(SIGQUIT, &sa, &sa_quit); 435112695Sdavidxu sigaction(SIGPIPE, &sa, &sa_pipe); 436112085Sdavidxu sa.sa_handler = SIG_DFL; 437112085Sdavidxu sigaction(SIGTSTP, &sa, NULL); 43874874Smarkm statusp = 1; 439112695Sdavidxu if (pipe(fds) == -1) { 440130409Smarkm PAM_END(); 441112695Sdavidxu err(1, "pipe"); 442112695Sdavidxu } 44377220Smarkm child_pid = fork(); 44477220Smarkm switch (child_pid) { 44574874Smarkm default: 446122013Sdavidxu sa.sa_handler = SIG_IGN; 447122013Sdavidxu sigaction(SIGTTOU, &sa, NULL); 448112695Sdavidxu close(fds[0]); 449153985Sbrian setpgid(child_pid, child_pid); 450153985Sbrian if (tcgetpgrp(STDERR_FILENO) == getpgrp()) 451153985Sbrian tcsetpgrp(STDERR_FILENO, child_pid); 452112695Sdavidxu close(fds[1]); 453112695Sdavidxu sigaction(SIGPIPE, &sa_pipe, NULL); 454113262Sdes while ((pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) { 45577220Smarkm if (WIFSTOPPED(statusp)) { 456153985Sbrian child_pgrp = getpgid(child_pid); 457153985Sbrian if (tcgetpgrp(STDERR_FILENO) == child_pgrp) 458153985Sbrian tcsetpgrp(STDERR_FILENO, getpgrp()); 459153966Sbrian kill(getpid(), SIGSTOP); 460153985Sbrian if (tcgetpgrp(STDERR_FILENO) == getpgrp()) { 461153985Sbrian child_pgrp = getpgid(child_pid); 462153985Sbrian tcsetpgrp(STDERR_FILENO, child_pgrp); 463153985Sbrian } 464153966Sbrian kill(child_pid, SIGCONT); 46577220Smarkm statusp = 1; 46677220Smarkm continue; 46777220Smarkm } 46877220Smarkm break; 46974874Smarkm } 470172749Sdavidxu tcsetpgrp(STDERR_FILENO, getpgrp()); 471113262Sdes if (pid == -1) 47277220Smarkm err(1, "waitpid"); 47381528Smarkm PAM_END(); 474130409Smarkm exit(WEXITSTATUS(statusp)); 47574874Smarkm case -1: 476130409Smarkm PAM_END(); 47777220Smarkm err(1, "fork"); 47874874Smarkm case 0: 479112695Sdavidxu close(fds[1]); 480112695Sdavidxu read(fds[0], &temp, 1); 481112695Sdavidxu close(fds[0]); 482112695Sdavidxu sigaction(SIGPIPE, &sa_pipe, NULL); 48398837Sdillon sigaction(SIGINT, &sa_int, NULL); 48498837Sdillon sigaction(SIGQUIT, &sa_quit, NULL); 485112695Sdavidxu 48677220Smarkm /* 48777220Smarkm * Set all user context except for: Environmental variables 48877220Smarkm * Umask Login records (wtmp, etc) Path 48977220Smarkm */ 49077220Smarkm setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK | 491105758Srwatson LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP | 492105758Srwatson LOGIN_SETMAC); 49377220Smarkm /* 494105758Srwatson * If -s is present, also set the MAC label. 495105758Srwatson */ 496105758Srwatson if (setmaclabel) 497105758Srwatson setwhat |= LOGIN_SETMAC; 498105758Srwatson /* 49977220Smarkm * Don't touch resource/priority settings if -m has been used 50077220Smarkm * or -l and -c hasn't, and we're not su'ing to root. 50177220Smarkm */ 50277220Smarkm if ((asme || (!asthem && class == NULL)) && pwd->pw_uid) 50377220Smarkm setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES); 50477220Smarkm if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0) 50577220Smarkm err(1, "setusercontext"); 50674874Smarkm 50777220Smarkm if (!asme) { 50877220Smarkm if (asthem) { 50977220Smarkm p = getenv("TERM"); 51077220Smarkm environ = &cleanenv; 511113262Sdes } 51269427Srwatson 513113262Sdes if (asthem || pwd->pw_uid) 514113262Sdes setenv("USER", pwd->pw_name, 1); 515113262Sdes setenv("HOME", pwd->pw_dir, 1); 516113262Sdes setenv("SHELL", shell, 1); 517113262Sdes 518113262Sdes if (asthem) { 51977220Smarkm /* 52077220Smarkm * Add any environmental variables that the 52177220Smarkm * PAM modules may have set. 52277220Smarkm */ 52377220Smarkm environ_pam = pam_getenvlist(pamh); 52477220Smarkm if (environ_pam) 52577220Smarkm export_pam_environment(); 5261590Srgrimes 52777220Smarkm /* set the su'd user's environment & umask */ 52877220Smarkm setusercontext(lc, pwd, pwd->pw_uid, 52977220Smarkm LOGIN_SETPATH | LOGIN_SETUMASK | 53077220Smarkm LOGIN_SETENV); 53177220Smarkm if (p) 53277220Smarkm setenv("TERM", p, 1); 533162761Sluoqi 534162761Sluoqi p = pam_getenv(pamh, "HOME"); 535162761Sluoqi if (chdir(p ? p : pwd->pw_dir) < 0) 536162761Sluoqi errx(1, "no directory"); 53777220Smarkm } 53877220Smarkm } 53977220Smarkm login_close(lc); 54074874Smarkm 54177220Smarkm if (iscsh == YES) { 54277220Smarkm if (fastlogin) 54383373Smarkm *np.a-- = "-f"; 54477220Smarkm if (asme) 54583373Smarkm *np.a-- = "-m"; 5461590Srgrimes } 54777220Smarkm /* csh strips the first character... */ 54883373Smarkm *np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su"; 54974874Smarkm 55077220Smarkm if (ruid != 0) 55177220Smarkm syslog(LOG_NOTICE, "%s to %s%s", username, user, 55277220Smarkm ontty()); 55374874Smarkm 55483373Smarkm execv(shell, np.b); 55577220Smarkm err(1, "%s", shell); 5561590Srgrimes } 5571590Srgrimes} 5581590Srgrimes 559130409Smarkmstatic void 56077220Smarkmexport_pam_environment(void) 56174874Smarkm{ 56274874Smarkm char **pp; 563171195Sscf char *p; 56474874Smarkm 56574874Smarkm for (pp = environ_pam; *pp != NULL; pp++) { 566171195Sscf if (ok_to_export(*pp)) { 567171195Sscf p = strchr(*pp, '='); 568171195Sscf *p = '\0'; 569171195Sscf setenv(*pp, p + 1, 1); 570171195Sscf } 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