su.c revision 163665
197403Sobrien/* 297403Sobrien * Copyright (c) 2002, 2005 Networks Associates Technologies, Inc. 397403Sobrien * All rights reserved. 497403Sobrien * 597403Sobrien * Portions of this software were developed for the FreeBSD Project by 697403Sobrien * ThinkSec AS and NAI Labs, the Security Research Division of Network 797403Sobrien * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 897403Sobrien * ("CBOSS"), as part of the DARPA CHATS research program. 997403Sobrien * 1097403Sobrien * Redistribution and use in source and binary forms, with or without 1197403Sobrien * modification, are permitted provided that the following conditions 1297403Sobrien * are met: 1397403Sobrien * 1. Redistributions of source code must retain the above copyright 1497403Sobrien * notice, this list of conditions and the following disclaimer. 1597403Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1697403Sobrien * notice, this list of conditions and the following disclaimer in the 1797403Sobrien * documentation and/or other materials provided with the distribution. 1897403Sobrien * 1997403Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2097403Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2197403Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2297403Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2397403Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2497403Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2597403Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2697403Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2797403Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2897403Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2997403Sobrien * SUCH DAMAGE. 3097403Sobrien */ 3197403Sobrien/*- 3297403Sobrien * Copyright (c) 1988, 1993, 1994 3397403Sobrien * The Regents of the University of California. All rights reserved. 3497403Sobrien * 3597403Sobrien * Redistribution and use in source and binary forms, with or without 3697403Sobrien * modification, are permitted provided that the following conditions 3797403Sobrien * are met: 3897403Sobrien * 1. Redistributions of source code must retain the above copyright 3997403Sobrien * notice, this list of conditions and the following disclaimer. 4097403Sobrien * 2. Redistributions in binary form must reproduce the above copyright 4197403Sobrien * notice, this list of conditions and the following disclaimer in the 4297403Sobrien * documentation and/or other materials provided with the distribution. 4397403Sobrien * 3. All advertising materials mentioning features or use of this software 4497403Sobrien * must display the following acknowledgement: 4597403Sobrien * This product includes software developed by the University of 4697403Sobrien * California, Berkeley and its contributors. 4797403Sobrien * 4. Neither the name of the University nor the names of its contributors 4897403Sobrien * may be used to endorse or promote products derived from this software 4997403Sobrien * without specific prior written permission. 5097403Sobrien * 5197403Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 5297403Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 5397403Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 5497403Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 5597403Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 5697403Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 5797403Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 5897403Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 5997403Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 6097403Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 6197403Sobrien * SUCH DAMAGE. 6297403Sobrien */ 6397403Sobrien 6497403Sobrien#ifndef lint 6597403Sobrienstatic const char copyright[] = 6697403Sobrien"@(#) Copyright (c) 1988, 1993, 1994\n\ 6797403Sobrien The Regents of the University of California. All rights reserved.\n"; 6897403Sobrien#endif /* not lint */ 6997403Sobrien 7097403Sobrien#if 0 7197403Sobrien#ifndef lint 7297403Sobrienstatic char sccsid[] = "@(#)su.c 8.3 (Berkeley) 4/2/94"; 7397403Sobrien#endif /* not lint */ 7497403Sobrien#endif 7597403Sobrien 7697403Sobrien#include <sys/cdefs.h> 7797403Sobrien__FBSDID("$FreeBSD: head/usr.bin/su/su.c 163665 2006-10-24 17:41:28Z sobomax $"); 7897403Sobrien 7997403Sobrien#include <sys/param.h> 8097403Sobrien#include <sys/time.h> 8197403Sobrien#include <sys/resource.h> 8297403Sobrien#include <sys/wait.h> 8397403Sobrien 8497403Sobrien#ifdef USE_BSM_AUDIT 8597403Sobrien#include <bsm/libbsm.h> 8697403Sobrien#include <bsm/audit_uevents.h> 8797403Sobrien#endif 8897403Sobrien 8997403Sobrien#include <err.h> 9097403Sobrien#include <errno.h> 9197403Sobrien#include <grp.h> 9297403Sobrien#include <login_cap.h> 9397403Sobrien#include <paths.h> 9497403Sobrien#include <pwd.h> 9597403Sobrien#include <signal.h> 9697403Sobrien#include <stdio.h> 9797403Sobrien#include <stdlib.h> 9897403Sobrien#include <string.h> 9997403Sobrien#include <syslog.h> 10097403Sobrien#include <unistd.h> 10197403Sobrien#include <stdarg.h> 10297403Sobrien 10397403Sobrien#include <security/pam_appl.h> 10497403Sobrien#include <security/openpam.h> 10597403Sobrien 10697403Sobrien#define PAM_END() do { \ 10797403Sobrien int local_ret; \ 10897403Sobrien if (pamh != NULL) { \ 10997403Sobrien local_ret = pam_setcred(pamh, PAM_DELETE_CRED); \ 11097403Sobrien if (local_ret != PAM_SUCCESS) \ 11197403Sobrien syslog(LOG_ERR, "pam_setcred: %s", \ 11297403Sobrien pam_strerror(pamh, local_ret)); \ 11397403Sobrien if (asthem) { \ 11497403Sobrien local_ret = pam_close_session(pamh, 0); \ 11597403Sobrien if (local_ret != PAM_SUCCESS) \ 11697403Sobrien syslog(LOG_ERR, "pam_close_session: %s",\ 11797403Sobrien pam_strerror(pamh, local_ret)); \ 11897403Sobrien } \ 11997403Sobrien local_ret = pam_end(pamh, local_ret); \ 12097403Sobrien if (local_ret != PAM_SUCCESS) \ 12197403Sobrien syslog(LOG_ERR, "pam_end: %s", \ 12297403Sobrien pam_strerror(pamh, local_ret)); \ 12397403Sobrien } \ 12497403Sobrien} while (0) 12597403Sobrien 12697403Sobrien 12797403Sobrien#define PAM_SET_ITEM(what, item) do { \ 12897403Sobrien int local_ret; \ 12997403Sobrien local_ret = pam_set_item(pamh, what, item); \ 13097403Sobrien if (local_ret != PAM_SUCCESS) { \ 13197403Sobrien syslog(LOG_ERR, "pam_set_item(" #what "): %s", \ 13297403Sobrien pam_strerror(pamh, local_ret)); \ 13397403Sobrien errx(1, "pam_set_item(" #what "): %s", \ 13497403Sobrien pam_strerror(pamh, local_ret)); \ 13597403Sobrien /* NOTREACHED */ \ 13697403Sobrien } \ 13797403Sobrien} while (0) 13897403Sobrien 13997403Sobrienenum tristate { UNSET, YES, NO }; 14097403Sobrien 14197403Sobrienstatic pam_handle_t *pamh = NULL; 14297403Sobrienstatic char **environ_pam; 14397403Sobrien 14497403Sobrienstatic char *ontty(void); 14597403Sobrienstatic int chshell(const char *); 14697403Sobrienstatic void usage(void) __dead2; 14797403Sobrienstatic void export_pam_environment(void); 14897403Sobrienstatic int ok_to_export(const char *); 14997403Sobrien 15097403Sobrienextern char **environ; 15197403Sobrien 15297403Sobrienint 15397403Sobrienmain(int argc, char *argv[]) 15497403Sobrien{ 15597403Sobrien static char *cleanenv; 15697403Sobrien struct passwd *pwd; 15797403Sobrien struct pam_conv conv = { openpam_ttyconv, NULL }; 15897403Sobrien enum tristate iscsh; 15997403Sobrien login_cap_t *lc; 16097403Sobrien union { 16197403Sobrien const char **a; 16297403Sobrien char * const *b; 16397403Sobrien } np; 16497403Sobrien uid_t ruid; 16597403Sobrien pid_t child_pid, child_pgrp, pid; 16697403Sobrien int asme, ch, asthem, fastlogin, prio, i, retcode, 16797403Sobrien statusp, setmaclabel; 16897403Sobrien u_int setwhat; 16997403Sobrien char *username, *class, shellbuf[MAXPATHLEN]; 17097403Sobrien const char *p, *user, *shell, *mytty, **nargv; 17197403Sobrien struct sigaction sa, sa_int, sa_quit, sa_pipe; 17297403Sobrien int temp, fds[2]; 17397403Sobrien#ifdef USE_BSM_AUDIT 17497403Sobrien const char *aerr; 17597403Sobrien au_id_t auid; 17697403Sobrien#endif 17797403Sobrien 17897403Sobrien shell = class = cleanenv = NULL; 17997403Sobrien asme = asthem = fastlogin = statusp = 0; 18097403Sobrien user = "root"; 18197403Sobrien iscsh = UNSET; 18297403Sobrien setmaclabel = 0; 18397403Sobrien 18497403Sobrien while ((ch = getopt(argc, argv, "-flmsc:")) != -1) 18597403Sobrien switch ((char)ch) { 18697403Sobrien case 'f': 18797403Sobrien fastlogin = 1; 18897403Sobrien break; 18997403Sobrien case '-': 19097403Sobrien case 'l': 19197403Sobrien asme = 0; 19297403Sobrien asthem = 1; 19397403Sobrien break; 19497403Sobrien case 'm': 19597403Sobrien asme = 1; 19697403Sobrien asthem = 0; 19797403Sobrien break; 19897403Sobrien case 's': 19997403Sobrien setmaclabel = 1; 20097403Sobrien break; 20197403Sobrien case 'c': 20297403Sobrien class = optarg; 20397403Sobrien break; 20497403Sobrien case '?': 20597403Sobrien default: 20697403Sobrien usage(); 20797403Sobrien /* NOTREACHED */ 20897403Sobrien } 20997403Sobrien 21097403Sobrien if (optind < argc) 21197403Sobrien user = argv[optind++]; 21297403Sobrien 21397403Sobrien if (user == NULL) 21497403Sobrien usage(); 21597403Sobrien /* NOTREACHED */ 21697403Sobrien 21797403Sobrien /* 21897403Sobrien * Try to provide more helpful debugging output if su(1) is running 21997403Sobrien * non-setuid, or was run from a file system not mounted setuid. 22097403Sobrien */ 22197403Sobrien if (geteuid() != 0) 22297403Sobrien errx(1, "not running setuid"); 22397403Sobrien 22497403Sobrien#ifdef USE_BSM_AUDIT 22597403Sobrien if (getauid(&auid) < 0 && errno != ENOSYS) { 22697403Sobrien syslog(LOG_AUTH | LOG_ERR, "getauid: %s", strerror(errno)); 22797403Sobrien errx(1, "Permission denied"); 22897403Sobrien } 22997403Sobrien#endif 23097403Sobrien if (strlen(user) > MAXLOGNAME - 1) { 23197403Sobrien#ifdef USE_BSM_AUDIT 23297403Sobrien if (audit_submit(AUE_su, auid, 23397403Sobrien 1, EPERM, "username too long: '%s'", user)) 23497403Sobrien errx(1, "Permission denied"); 23597403Sobrien#endif 23697403Sobrien errx(1, "username too long"); 23797403Sobrien } 23897403Sobrien 23997403Sobrien nargv = malloc(sizeof(char *) * (size_t)(argc + 4)); 24097403Sobrien if (nargv == NULL) 24197403Sobrien errx(1, "malloc failure"); 24297403Sobrien 24397403Sobrien nargv[argc + 3] = NULL; 24497403Sobrien for (i = argc; i >= optind; i--) 24597403Sobrien nargv[i + 3] = argv[i]; 24697403Sobrien np.a = &nargv[i + 3]; 24797403Sobrien 24897403Sobrien argv += optind; 24997403Sobrien 25097403Sobrien errno = 0; 25197403Sobrien prio = getpriority(PRIO_PROCESS, 0); 25297403Sobrien if (errno) 25397403Sobrien prio = 0; 25497403Sobrien 25597403Sobrien setpriority(PRIO_PROCESS, 0, -2); 25697403Sobrien openlog("su", LOG_CONS, LOG_AUTH); 25797403Sobrien 25897403Sobrien /* get current login name, real uid and shell */ 25997403Sobrien ruid = getuid(); 26097403Sobrien username = getlogin(); 26197403Sobrien pwd = getpwnam(username); 26297403Sobrien if (username == NULL || pwd == NULL || pwd->pw_uid != ruid) 26397403Sobrien pwd = getpwuid(ruid); 26497403Sobrien if (pwd == NULL) { 26597403Sobrien#ifdef USE_BSM_AUDIT 26697403Sobrien if (audit_submit(AUE_su, auid, 1, EPERM, 26797403Sobrien "unable to determine invoking subject: '%s'", username)) 26897403Sobrien errx(1, "Permission denied"); 26997403Sobrien#endif 27097403Sobrien errx(1, "who are you?"); 27197403Sobrien } 27297403Sobrien 27397403Sobrien username = strdup(pwd->pw_name); 27497403Sobrien if (username == NULL) 27597403Sobrien err(1, "strdup failure"); 27697403Sobrien 27797403Sobrien if (asme) { 27897403Sobrien if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') { 27997403Sobrien /* must copy - pwd memory is recycled */ 28097403Sobrien shell = strncpy(shellbuf, pwd->pw_shell, 28197403Sobrien sizeof(shellbuf)); 28297403Sobrien shellbuf[sizeof(shellbuf) - 1] = '\0'; 28397403Sobrien } 28497403Sobrien else { 28597403Sobrien shell = _PATH_BSHELL; 28697403Sobrien iscsh = NO; 28797403Sobrien } 28897403Sobrien } 28997403Sobrien 29097403Sobrien /* Do the whole PAM startup thing */ 29197403Sobrien retcode = pam_start("su", user, &conv, &pamh); 29297403Sobrien if (retcode != PAM_SUCCESS) { 29397403Sobrien syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, retcode)); 29497403Sobrien errx(1, "pam_start: %s", pam_strerror(pamh, retcode)); 29597403Sobrien } 29697403Sobrien 29797403Sobrien PAM_SET_ITEM(PAM_RUSER, username); 29897403Sobrien 29997403Sobrien mytty = ttyname(STDERR_FILENO); 30097403Sobrien if (!mytty) 30197403Sobrien mytty = "tty"; 30297403Sobrien PAM_SET_ITEM(PAM_TTY, mytty); 30397403Sobrien 30497403Sobrien retcode = pam_authenticate(pamh, 0); 30597403Sobrien if (retcode != PAM_SUCCESS) { 30697403Sobrien#ifdef USE_BSM_AUDIT 30797403Sobrien if (audit_submit(AUE_su, auid, 1, EPERM, "bad su %s to %s on %s", 30897403Sobrien username, user, mytty)) 30997403Sobrien errx(1, "Permission denied"); 31097403Sobrien#endif 31197403Sobrien syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s on %s", 31297403Sobrien username, user, mytty); 31397403Sobrien errx(1, "Sorry"); 31497403Sobrien } 31597403Sobrien#ifdef USE_BSM_AUDIT 31697403Sobrien if (audit_submit(AUE_su, auid, 0, 0, "successful authentication")) 31797403Sobrien errx(1, "Permission denied"); 31897403Sobrien#endif 31997403Sobrien retcode = pam_get_item(pamh, PAM_USER, (const void **)&p); 32097403Sobrien if (retcode == PAM_SUCCESS) 32197403Sobrien user = p; 32297403Sobrien else 32397403Sobrien syslog(LOG_ERR, "pam_get_item(PAM_USER): %s", 32497403Sobrien pam_strerror(pamh, retcode)); 32597403Sobrien pwd = getpwnam(user); 32697403Sobrien if (pwd == NULL) { 32797403Sobrien#ifdef USE_BSM_AUDIT 32897403Sobrien if (audit_submit(AUE_su, auid, 1, EPERM, 32997403Sobrien "unknown subject: %s", user)) 33097403Sobrien errx(1, "Permission denied"); 33197403Sobrien#endif 33297403Sobrien errx(1, "unknown login: %s", user); 33397403Sobrien } 33497403Sobrien 33597403Sobrien retcode = pam_acct_mgmt(pamh, 0); 33697403Sobrien if (retcode == PAM_NEW_AUTHTOK_REQD) { 33797403Sobrien retcode = pam_chauthtok(pamh, 33897403Sobrien PAM_CHANGE_EXPIRED_AUTHTOK); 33997403Sobrien if (retcode != PAM_SUCCESS) { 34097403Sobrien#ifdef USE_BSM_AUDIT 34197403Sobrien aerr = pam_strerror(pamh, retcode); 34297403Sobrien if (aerr == NULL) 34397403Sobrien aerr = "Unknown PAM error"; 34497403Sobrien if (audit_submit(AUE_su, auid, 1, EPERM, 34597403Sobrien "pam_chauthtok: %s", aerr)) 34697403Sobrien errx(1, "Permission denied"); 34797403Sobrien#endif 34897403Sobrien syslog(LOG_ERR, "pam_chauthtok: %s", 34997403Sobrien pam_strerror(pamh, retcode)); 35097403Sobrien errx(1, "Sorry"); 35197403Sobrien } 35297403Sobrien } 35397403Sobrien if (retcode != PAM_SUCCESS) { 35497403Sobrien#ifdef USE_BSM_AUDIT 35597403Sobrien if (audit_submit(AUE_su, auid, 1, EPERM, "pam_acct_mgmt: %s", 35697403Sobrien pam_strerror(pamh, retcode))) 35797403Sobrien errx(1, "Permission denied"); 35897403Sobrien#endif 35997403Sobrien syslog(LOG_ERR, "pam_acct_mgmt: %s", 36097403Sobrien pam_strerror(pamh, retcode)); 36197403Sobrien errx(1, "Sorry"); 36297403Sobrien } 36397403Sobrien 36497403Sobrien /* get target login information */ 36597403Sobrien if (class == NULL) 36697403Sobrien lc = login_getpwclass(pwd); 36797403Sobrien else { 36897403Sobrien if (ruid != 0) { 36997403Sobrien#ifdef USE_BSM_AUDIT 37097403Sobrien if (audit_submit(AUE_su, auid, 1, EPERM, 37197403Sobrien "only root may use -c")) 37297403Sobrien errx(1, "Permission denied"); 37397403Sobrien#endif 37497403Sobrien errx(1, "only root may use -c"); 37597403Sobrien } 37697403Sobrien lc = login_getclass(class); 37797403Sobrien if (lc == NULL) 37897403Sobrien errx(1, "unknown class: %s", class); 37997403Sobrien } 38097403Sobrien 38197403Sobrien /* if asme and non-standard target shell, must be root */ 38297403Sobrien if (asme) { 38397403Sobrien if (ruid != 0 && !chshell(pwd->pw_shell)) 38497403Sobrien errx(1, "permission denied (shell)"); 38597403Sobrien } 38697403Sobrien else if (pwd->pw_shell && *pwd->pw_shell) { 38797403Sobrien shell = pwd->pw_shell; 38897403Sobrien iscsh = UNSET; 38997403Sobrien } 39097403Sobrien else { 39197403Sobrien shell = _PATH_BSHELL; 39297403Sobrien iscsh = NO; 39397403Sobrien } 39497403Sobrien 39597403Sobrien /* if we're forking a csh, we want to slightly muck the args */ 39697403Sobrien if (iscsh == UNSET) { 39797403Sobrien p = strrchr(shell, '/'); 39897403Sobrien if (p) 39997403Sobrien ++p; 40097403Sobrien else 40197403Sobrien p = shell; 40297403Sobrien iscsh = strcmp(p, "csh") ? (strcmp(p, "tcsh") ? NO : YES) : YES; 40397403Sobrien } 40497403Sobrien setpriority(PRIO_PROCESS, 0, prio); 40597403Sobrien 40697403Sobrien /* 40797403Sobrien * PAM modules might add supplementary groups in pam_setcred(), so 40897403Sobrien * initialize them first. 40997403Sobrien */ 41097403Sobrien if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0) 41197403Sobrien err(1, "setusercontext"); 41297403Sobrien 41397403Sobrien retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED); 41497403Sobrien if (retcode != PAM_SUCCESS) { 41597403Sobrien syslog(LOG_ERR, "pam_setcred: %s", 41697403Sobrien pam_strerror(pamh, retcode)); 41797403Sobrien errx(1, "failed to establish credentials."); 41897403Sobrien } 41997403Sobrien if (asthem) { 42097403Sobrien retcode = pam_open_session(pamh, 0); 42197403Sobrien if (retcode != PAM_SUCCESS) { 42297403Sobrien syslog(LOG_ERR, "pam_open_session: %s", 42397403Sobrien pam_strerror(pamh, retcode)); 42497403Sobrien errx(1, "failed to open session."); 42597403Sobrien } 42697403Sobrien } 42797403Sobrien 42897403Sobrien /* 42997403Sobrien * We must fork() before setuid() because we need to call 43097403Sobrien * pam_setcred(pamh, PAM_DELETE_CRED) as root. 43197403Sobrien */ 43297403Sobrien sa.sa_flags = SA_RESTART; 43397403Sobrien sa.sa_handler = SIG_IGN; 43497403Sobrien sigemptyset(&sa.sa_mask); 43597403Sobrien sigaction(SIGINT, &sa, &sa_int); 43697403Sobrien sigaction(SIGQUIT, &sa, &sa_quit); 43797403Sobrien sigaction(SIGPIPE, &sa, &sa_pipe); 43897403Sobrien sa.sa_handler = SIG_DFL; 43997403Sobrien sigaction(SIGTSTP, &sa, NULL); 44097403Sobrien statusp = 1; 44197403Sobrien if (pipe(fds) == -1) { 44297403Sobrien PAM_END(); 44397403Sobrien err(1, "pipe"); 44497403Sobrien } 44597403Sobrien child_pid = fork(); 44697403Sobrien switch (child_pid) { 44797403Sobrien default: 44897403Sobrien sa.sa_handler = SIG_IGN; 44997403Sobrien sigaction(SIGTTOU, &sa, NULL); 45097403Sobrien close(fds[0]); 45197403Sobrien setpgid(child_pid, child_pid); 45297403Sobrien if (tcgetpgrp(STDERR_FILENO) == getpgrp()) 45397403Sobrien tcsetpgrp(STDERR_FILENO, child_pid); 45497403Sobrien close(fds[1]); 45597403Sobrien sigaction(SIGPIPE, &sa_pipe, NULL); 45697403Sobrien while ((pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) { 45797403Sobrien if (WIFSTOPPED(statusp)) { 45897403Sobrien child_pgrp = getpgid(child_pid); 45997403Sobrien if (tcgetpgrp(STDERR_FILENO) == child_pgrp) 46097403Sobrien tcsetpgrp(STDERR_FILENO, getpgrp()); 46197403Sobrien kill(getpid(), SIGSTOP); 46297403Sobrien if (tcgetpgrp(STDERR_FILENO) == getpgrp()) { 46397403Sobrien child_pgrp = getpgid(child_pid); 46497403Sobrien tcsetpgrp(STDERR_FILENO, child_pgrp); 46597403Sobrien } 46697403Sobrien kill(child_pid, SIGCONT); 46797403Sobrien statusp = 1; 46897403Sobrien continue; 46997403Sobrien } 47097403Sobrien break; 47197403Sobrien } 47297403Sobrien child_pgrp = getpgid(child_pid); 47397403Sobrien if (tcgetpgrp(STDERR_FILENO) == child_pgrp) 47497403Sobrien tcsetpgrp(STDERR_FILENO, getpgrp()); 47597403Sobrien if (pid == -1) 47697403Sobrien err(1, "waitpid"); 47797403Sobrien PAM_END(); 47897403Sobrien exit(WEXITSTATUS(statusp)); 47997403Sobrien case -1: 48097403Sobrien PAM_END(); 48197403Sobrien err(1, "fork"); 48297403Sobrien case 0: 48397403Sobrien close(fds[1]); 48497403Sobrien read(fds[0], &temp, 1); 48597403Sobrien close(fds[0]); 48697403Sobrien sigaction(SIGPIPE, &sa_pipe, NULL); 48797403Sobrien sigaction(SIGINT, &sa_int, NULL); 48897403Sobrien sigaction(SIGQUIT, &sa_quit, NULL); 48997403Sobrien 49097403Sobrien /* 49197403Sobrien * Set all user context except for: Environmental variables 49297403Sobrien * Umask Login records (wtmp, etc) Path 49397403Sobrien */ 49497403Sobrien setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK | 49597403Sobrien LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP | 49697403Sobrien LOGIN_SETMAC); 49797403Sobrien /* 49897403Sobrien * If -s is present, also set the MAC label. 49997403Sobrien */ 50097403Sobrien if (setmaclabel) 50197403Sobrien setwhat |= LOGIN_SETMAC; 50297403Sobrien /* 50397403Sobrien * Don't touch resource/priority settings if -m has been used 50497403Sobrien * or -l and -c hasn't, and we're not su'ing to root. 50597403Sobrien */ 50697403Sobrien if ((asme || (!asthem && class == NULL)) && pwd->pw_uid) 50797403Sobrien setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES); 50897403Sobrien if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0) 50997403Sobrien err(1, "setusercontext"); 51097403Sobrien 51197403Sobrien if (!asme) { 51297403Sobrien if (asthem) { 51397403Sobrien p = getenv("TERM"); 51497403Sobrien environ = &cleanenv; 51597403Sobrien } 51697403Sobrien 51797403Sobrien if (asthem || pwd->pw_uid) 51897403Sobrien setenv("USER", pwd->pw_name, 1); 51997403Sobrien setenv("HOME", pwd->pw_dir, 1); 52097403Sobrien setenv("SHELL", shell, 1); 52197403Sobrien 52297403Sobrien if (asthem) { 52397403Sobrien /* 52497403Sobrien * Add any environmental variables that the 52597403Sobrien * PAM modules may have set. 52697403Sobrien */ 52797403Sobrien environ_pam = pam_getenvlist(pamh); 52897403Sobrien if (environ_pam) 52997403Sobrien export_pam_environment(); 53097403Sobrien 53197403Sobrien /* set the su'd user's environment & umask */ 53297403Sobrien setusercontext(lc, pwd, pwd->pw_uid, 53397403Sobrien LOGIN_SETPATH | LOGIN_SETUMASK | 53497403Sobrien LOGIN_SETENV); 53597403Sobrien if (p) 53697403Sobrien setenv("TERM", p, 1); 53797403Sobrien 53897403Sobrien p = pam_getenv(pamh, "HOME"); 53997403Sobrien if (chdir(p ? p : pwd->pw_dir) < 0) 54097403Sobrien errx(1, "no directory"); 54197403Sobrien } 54297403Sobrien } 54397403Sobrien login_close(lc); 54497403Sobrien 54597403Sobrien if (iscsh == YES) { 54697403Sobrien if (fastlogin) 54797403Sobrien *np.a-- = "-f"; 54897403Sobrien if (asme) 54997403Sobrien *np.a-- = "-m"; 55097403Sobrien } 55197403Sobrien /* csh strips the first character... */ 55297403Sobrien *np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su"; 55397403Sobrien 55497403Sobrien if (ruid != 0) 55597403Sobrien syslog(LOG_NOTICE, "%s to %s%s", username, user, 55697403Sobrien ontty()); 55797403Sobrien 55897403Sobrien execv(shell, np.b); 55997403Sobrien err(1, "%s", shell); 56097403Sobrien } 56197403Sobrien} 56297403Sobrien 56397403Sobrienstatic void 56497403Sobrienexport_pam_environment(void) 56597403Sobrien{ 56697403Sobrien char **pp; 56797403Sobrien 56897403Sobrien for (pp = environ_pam; *pp != NULL; pp++) { 56997403Sobrien if (ok_to_export(*pp)) 57097403Sobrien putenv(*pp); 57197403Sobrien free(*pp); 57297403Sobrien } 57397403Sobrien} 57497403Sobrien 57597403Sobrien/* 57697403Sobrien * Sanity checks on PAM environmental variables: 57797403Sobrien * - Make sure there is an '=' in the string. 57897403Sobrien * - Make sure the string doesn't run on too long. 57997403Sobrien * - Do not export certain variables. This list was taken from the 58097403Sobrien * Solaris pam_putenv(3) man page. 58197403Sobrien * Note that if the user is chrooted, PAM may have a better idea than we 58297403Sobrien * do of where her home directory is. 58397403Sobrien */ 58497403Sobrienstatic int 58597403Sobrienok_to_export(const char *s) 58697403Sobrien{ 58797403Sobrien static const char *noexport[] = { 58897403Sobrien "SHELL", /* "HOME", */ "LOGNAME", "MAIL", "CDPATH", 58997403Sobrien "IFS", "PATH", NULL 59097403Sobrien }; 59197403Sobrien const char **pp; 59297403Sobrien size_t n; 59397403Sobrien 59497403Sobrien if (strlen(s) > 1024 || strchr(s, '=') == NULL) 59597403Sobrien return 0; 59697403Sobrien if (strncmp(s, "LD_", 3) == 0) 59797403Sobrien return 0; 59897403Sobrien for (pp = noexport; *pp != NULL; pp++) { 59997403Sobrien n = strlen(*pp); 60097403Sobrien if (s[n] == '=' && strncmp(s, *pp, n) == 0) 60197403Sobrien return 0; 60297403Sobrien } 60397403Sobrien return 1; 60497403Sobrien} 60597403Sobrien 60697403Sobrienstatic void 60797403Sobrienusage(void) 60897403Sobrien{ 60997403Sobrien 61097403Sobrien fprintf(stderr, "usage: su [-] [-flms] [-c class] [login [args]]\n"); 61197403Sobrien exit(1); 61297403Sobrien /* NOTREACHED */ 61397403Sobrien} 61497403Sobrien 61597403Sobrienstatic int 61697403Sobrienchshell(const char *sh) 61797403Sobrien{ 61897403Sobrien int r; 61997403Sobrien char *cp; 62097403Sobrien 62197403Sobrien r = 0; 62297403Sobrien setusershell(); 62397403Sobrien while ((cp = getusershell()) != NULL && !r) 62497403Sobrien r = (strcmp(cp, sh) == 0); 62597403Sobrien endusershell(); 62697403Sobrien return r; 62797403Sobrien} 62897403Sobrien 62997403Sobrienstatic char * 63097403Sobrienontty(void) 63197403Sobrien{ 63297403Sobrien char *p; 63397403Sobrien static char buf[MAXPATHLEN + 4]; 63497403Sobrien 63597403Sobrien buf[0] = 0; 63697403Sobrien p = ttyname(STDERR_FILENO); 63797403Sobrien if (p) 63897403Sobrien snprintf(buf, sizeof(buf), " on %s", p); 63997403Sobrien return buf; 64097403Sobrien} 64197403Sobrien