su.c revision 83373
115885Sjulian/* 215885Sjulian * Copyright (c) 1988, 1993, 1994 315885Sjulian * The Regents of the University of California. All rights reserved. 415885Sjulian * 515885Sjulian * Redistribution and use in source and binary forms, with or without 615885Sjulian * modification, are permitted provided that the following conditions 715885Sjulian * are met: 815885Sjulian * 1. Redistributions of source code must retain the above copyright 915885Sjulian * notice, this list of conditions and the following disclaimer. 1015885Sjulian * 2. Redistributions in binary form must reproduce the above copyright 1115885Sjulian * notice, this list of conditions and the following disclaimer in the 1215885Sjulian * documentation and/or other materials provided with the distribution. 1315885Sjulian * 3. All advertising materials mentioning features or use of this software 1415885Sjulian * must display the following acknowledgement: 1515885Sjulian * This product includes software developed by the University of 1615885Sjulian * California, Berkeley and its contributors. 1715885Sjulian * 4. Neither the name of the University nor the names of its contributors 1815885Sjulian * may be used to endorse or promote products derived from this software 1915885Sjulian * without specific prior written permission. 2015885Sjulian * 2115885Sjulian * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2218207Sbde * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2318207Sbde * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2418207Sbde * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2518207Sbde * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2615885Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2715885Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2817967Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2917967Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3017254Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3117254Sjulian * SUCH DAMAGE. 3217254Sjulian */ 3317254Sjulian 3417254Sjulian#ifndef lint 3517254Sjulianstatic const char copyright[] = 3615885Sjulian"@(#) Copyright (c) 1988, 1993, 1994\n\ 3715885Sjulian The Regents of the University of California. All rights reserved.\n"; 3815885Sjulian#endif /* not lint */ 3915885Sjulian 4015885Sjulian#ifndef lint 4115885Sjulian#if 0 4215885Sjulianstatic char sccsid[] = "@(#)su.c 8.3 (Berkeley) 4/2/94"; 4315885Sjulian#endif 4415885Sjulianstatic const char rcsid[] = 4515885Sjulian "$FreeBSD: head/usr.bin/su/su.c 83373 2001-09-12 19:15:02Z markm $"; 4615885Sjulian#endif /* not lint */ 4715885Sjulian 4815885Sjulian#include <sys/param.h> 4915885Sjulian#include <sys/time.h> 5015885Sjulian#include <sys/resource.h> 5115885Sjulian#include <sys/wait.h> 5215885Sjulian 5315885Sjulian#include <err.h> 5418240Sjulian#include <errno.h> 5515885Sjulian#include <grp.h> 5617921Sjulian#include <libutil.h> 5717921Sjulian#include <login_cap.h> 5817921Sjulian#include <paths.h> 5915885Sjulian#include <pwd.h> 6015885Sjulian#include <signal.h> 6115885Sjulian#include <stdio.h> 6215885Sjulian#include <stdlib.h> 6315885Sjulian#include <string.h> 6415885Sjulian#include <syslog.h> 6517921Sjulian#include <unistd.h> 6617921Sjulian 6717921Sjulian#include <security/pam_appl.h> 6817921Sjulian#include <security/pam_misc.h> 6917921Sjulian 7017921Sjulian#define PAM_END() do { \ 7115885Sjulian int local_ret; \ 7215885Sjulian if (pamh != NULL && creds_set) { \ 7315885Sjulian local_ret = pam_setcred(pamh, PAM_DELETE_CRED); \ 7417921Sjulian if (local_ret != PAM_SUCCESS) \ 7517921Sjulian syslog(LOG_ERR, "pam_setcred: %s", \ 7617921Sjulian pam_strerror(pamh, local_ret)); \ 7717921Sjulian local_ret = pam_end(pamh, local_ret); \ 7817921Sjulian if (local_ret != PAM_SUCCESS) \ 7917921Sjulian syslog(LOG_ERR, "pam_end: %s", \ 8017921Sjulian pam_strerror(pamh, local_ret)); \ 8115885Sjulian } \ 8215885Sjulian} while (0) 8315885Sjulian 8415885Sjulian 8515885Sjulian#define PAM_SET_ITEM(what, item) do { \ 8615885Sjulian int local_ret; \ 8715885Sjulian local_ret = pam_set_item(pamh, what, item); \ 8815885Sjulian if (local_ret != PAM_SUCCESS) { \ 8917921Sjulian syslog(LOG_ERR, "pam_set_item(" #what "): %s", \ 9017921Sjulian pam_strerror(pamh, local_ret)); \ 9117921Sjulian errx(1, "pam_set_item(" #what "): %s", \ 9217921Sjulian pam_strerror(pamh, local_ret)); \ 9315885Sjulian } \ 9415885Sjulian} while (0) 9515885Sjulian 9615885Sjulianenum tristate { UNSET, YES, NO }; 9715885Sjulian 9815885Sjulianstatic pam_handle_t *pamh = NULL; 9917921Sjulianstatic int creds_set = 0; 10017921Sjulianstatic char **environ_pam; 10117921Sjulian 10215885Sjulianstatic char *ontty(void); 10315885Sjulianstatic int chshell(char *); 10415885Sjulianstatic void usage(void); 10515885Sjulianstatic int export_pam_environment(void); 10615885Sjulianstatic int ok_to_export(const char *); 10715885Sjulian 10815885Sjulianextern char **environ; 10917921Sjulian 11017921Sjulianint 11117921Sjulianmain(int argc, char *argv[]) 11217921Sjulian{ 11317921Sjulian struct passwd *pwd; 11415885Sjulian struct pam_conv conv = {misc_conv, NULL}; 11515885Sjulian enum tristate iscsh; 11615885Sjulian login_cap_t *lc; 11715885Sjulian union { 11815885Sjulian const char **a; 11915885Sjulian char * const *b; 12015885Sjulian } np; 12117921Sjulian uid_t ruid; 12217921Sjulian gid_t gid; 12317921Sjulian int asme, ch, asthem, fastlogin, prio, i, setwhat, retcode, 12417921Sjulian statusp, child_pid, child_pgrp, ret_pid; 12517921Sjulian char *username, *cleanenv, *class, shellbuf[MAXPATHLEN], 12615885Sjulian myhost[MAXHOSTNAMELEN + 1]; 12715885Sjulian const char *p, *user, *shell, *mytty, **nargv; 12815885Sjulian 12915885Sjulian shell = class = cleanenv = NULL; 13015885Sjulian asme = asthem = fastlogin = statusp = 0; 13115885Sjulian user = "root"; 13215885Sjulian iscsh = UNSET; 13315885Sjulian 13415885Sjulian while ((ch = getopt(argc, argv, "-flmc:")) != -1) 13515885Sjulian switch ((char)ch) { 13617921Sjulian case 'f': 13717921Sjulian fastlogin = 1; 13817921Sjulian break; 13917921Sjulian case '-': 14017921Sjulian case 'l': 14115885Sjulian asme = 0; 14218240Sjulian asthem = 1; 14318244Sjulian break; 14415885Sjulian case 'm': 14515885Sjulian asme = 1; 14615885Sjulian asthem = 0; 14715885Sjulian break; 14815885Sjulian case 'c': 14917921Sjulian class = optarg; 15017921Sjulian break; 15115885Sjulian case '?': 15215885Sjulian default: 15318240Sjulian usage(); 15415885Sjulian } 15515885Sjulian 15615885Sjulian if (optind < argc) 15715885Sjulian user = argv[optind++]; 15815885Sjulian 15918240Sjulian if (user == NULL) 16015885Sjulian usage(); 16115885Sjulian 16218240Sjulian if (strlen(user) > MAXLOGNAME - 1) 16315885Sjulian errx(1, "username too long"); 16418240Sjulian 16518240Sjulian nargv = malloc(sizeof(char *) * (argc + 4)); 16618240Sjulian if (nargv == NULL) 16718240Sjulian errx(1, "malloc failure"); 16818240Sjulian 16918240Sjulian nargv[argc + 3] = NULL; 17018240Sjulian for (i = argc; i >= optind; i--) 17115885Sjulian nargv[i + 3] = argv[i]; 17217921Sjulian np.a = &nargv[i + 3]; 17317921Sjulian 17417921Sjulian argv += optind; 17517921Sjulian 17620407Swollman errno = 0; 17720407Swollman prio = getpriority(PRIO_PROCESS, 0); 17820407Swollman if (errno) 17918240Sjulian prio = 0; 18018240Sjulian 18118240Sjulian setpriority(PRIO_PROCESS, 0, -2); 18220407Swollman openlog("su", LOG_CONS, LOG_AUTH); 18315885Sjulian 18417921Sjulian /* get current login name, real uid and shell */ 18517921Sjulian ruid = getuid(); 18617921Sjulian username = getlogin(); 18717921Sjulian pwd = getpwnam(username); 18820407Swollman if (username == NULL || pwd == NULL || pwd->pw_uid != ruid) 18920407Swollman pwd = getpwuid(ruid); 19020407Swollman if (pwd == NULL) 19115885Sjulian errx(1, "who are you?"); 19215885Sjulian gid = pwd->pw_gid; 19315885Sjulian 19415885Sjulian username = strdup(pwd->pw_name); 19515885Sjulian if (username == NULL) 19615885Sjulian err(1, "strdup failure"); 19715885Sjulian 19815885Sjulian if (asme) { 19915885Sjulian if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') { 20017921Sjulian /* must copy - pwd memory is recycled */ 20117921Sjulian shell = strncpy(shellbuf, pwd->pw_shell, 20217921Sjulian sizeof(shellbuf)); 20317921Sjulian shellbuf[sizeof(shellbuf) - 1] = '\0'; 20415885Sjulian } 20515885Sjulian else { 20617921Sjulian shell = _PATH_BSHELL; 20717921Sjulian iscsh = NO; 20817921Sjulian } 20915885Sjulian } 21015885Sjulian 21115885Sjulian /* Do the whole PAM startup thing */ 21215885Sjulian retcode = pam_start("su", user, &conv, &pamh); 21315885Sjulian if (retcode != PAM_SUCCESS) { 21415885Sjulian syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, retcode)); 21515885Sjulian errx(1, "pam_start: %s", pam_strerror(pamh, retcode)); 21615885Sjulian } 21717921Sjulian 21817921Sjulian PAM_SET_ITEM(PAM_RUSER, getlogin()); 21917921Sjulian 22017921Sjulian gethostname(myhost, sizeof(myhost)); 22115885Sjulian PAM_SET_ITEM(PAM_RHOST, myhost); 22215885Sjulian 22315885Sjulian mytty = ttyname(STDERR_FILENO); 22415885Sjulian if (!mytty) 22515885Sjulian mytty = "tty"; 22615885Sjulian PAM_SET_ITEM(PAM_TTY, mytty); 22717921Sjulian 22817921Sjulian retcode = pam_authenticate(pamh, 0); 22917921Sjulian if (retcode != PAM_SUCCESS) { 23017921Sjulian syslog(LOG_ERR, "pam_authenticate: %s", 23115885Sjulian pam_strerror(pamh, retcode)); 23215885Sjulian errx(1, "Sorry"); 23315885Sjulian } 23415885Sjulian retcode = pam_get_item(pamh, PAM_USER, (const void **)&p); 23515885Sjulian if (retcode == PAM_SUCCESS) 23615885Sjulian user = p; 23715885Sjulian else 23815885Sjulian syslog(LOG_ERR, "pam_get_item(PAM_USER): %s", 23915885Sjulian pam_strerror(pamh, retcode)); 24015885Sjulian 24115885Sjulian retcode = pam_acct_mgmt(pamh, 0); 24215885Sjulian if (retcode == PAM_NEW_AUTHTOK_REQD) { 24317921Sjulian retcode = pam_chauthtok(pamh, 24417921Sjulian PAM_CHANGE_EXPIRED_AUTHTOK); 24517921Sjulian if (retcode != PAM_SUCCESS) { 24617921Sjulian syslog(LOG_ERR, "pam_chauthtok: %s", 24715885Sjulian pam_strerror(pamh, retcode)); 24815885Sjulian errx(1, "Sorry"); 24917921Sjulian } 25017921Sjulian } 25117921Sjulian if (retcode != PAM_SUCCESS) { 25217921Sjulian syslog(LOG_ERR, "pam_acct_mgmt: %s", 25317254Sjulian pam_strerror(pamh, retcode)); 25417254Sjulian errx(1, "Sorry"); 25517921Sjulian } 25617921Sjulian 25717921Sjulian /* get target login information, default to root */ 25817921Sjulian pwd = getpwnam(user); 25917254Sjulian if (pwd == NULL) 26017254Sjulian errx(1, "unknown login: %s", user); 26117254Sjulian if (class == NULL) 26217254Sjulian lc = login_getpwclass(pwd); 26315885Sjulian else { 26415885Sjulian if (ruid != 0) 26515885Sjulian errx(1, "only root may use -c"); 26615885Sjulian lc = login_getclass(class); 26715885Sjulian if (lc == NULL) 26815885Sjulian errx(1, "unknown class: %s", class); 26915885Sjulian } 27015885Sjulian 27115885Sjulian /* if asme and non-standard target shell, must be root */ 27215885Sjulian if (asme) { 27315885Sjulian if (ruid != 0 && !chshell(pwd->pw_shell)) 27415885Sjulian errx(1, "permission denied (shell)."); 27517921Sjulian } 27617921Sjulian else if (pwd->pw_shell && *pwd->pw_shell) { 27717921Sjulian shell = pwd->pw_shell; 27815885Sjulian iscsh = UNSET; 27917921Sjulian } 28017921Sjulian else { 28117921Sjulian shell = _PATH_BSHELL; 28217921Sjulian iscsh = NO; 28318240Sjulian } 28420407Swollman 28517921Sjulian /* if we're forking a csh, we want to slightly muck the args */ 28618240Sjulian if (iscsh == UNSET) { 28718240Sjulian p = strrchr(shell, '/'); 28818240Sjulian if (p) 28918240Sjulian ++p; 29018240Sjulian else 29115885Sjulian p = shell; 29217921Sjulian iscsh = strcmp(p, "csh") ? (strcmp(p, "tcsh") ? NO : YES) : YES; 29317921Sjulian } 29417921Sjulian setpriority(PRIO_PROCESS, 0, prio); 29517921Sjulian 29615885Sjulian /* 29715885Sjulian * PAM modules might add supplementary groups in pam_setcred(), so 29815885Sjulian * initialize them first. 29915885Sjulian */ 30015885Sjulian if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0) 30115885Sjulian err(1, "setusercontext"); 30215885Sjulian 30317921Sjulian retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED); 30417921Sjulian if (retcode != PAM_SUCCESS) 30517921Sjulian syslog(LOG_ERR, "pam_setcred(pamh, PAM_ESTABLISH_CRED): %s", 30617921Sjulian pam_strerror(pamh, retcode)); 30715885Sjulian else 30815885Sjulian creds_set = 1; 30915885Sjulian 31015885Sjulian /* 31115885Sjulian * We must fork() before setuid() because we need to call 31215885Sjulian * pam_setcred(pamh, PAM_DELETE_CRED) as root. 31317921Sjulian */ 31417921Sjulian 31518240Sjulian statusp = 1; 31618240Sjulian child_pid = fork(); 31718240Sjulian switch (child_pid) { 31818240Sjulian default: 31918240Sjulian while ((ret_pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) { 32017921Sjulian if (WIFSTOPPED(statusp)) { 32118240Sjulian child_pgrp = tcgetpgrp(1); 32215885Sjulian kill(getpid(), SIGSTOP); 32315885Sjulian tcsetpgrp(1, child_pgrp); 32415885Sjulian kill(child_pid, SIGCONT); 32515885Sjulian statusp = 1; 32615885Sjulian continue; 32715885Sjulian } 32815885Sjulian break; 32915885Sjulian } 33015885Sjulian if (ret_pid == -1) 33117254Sjulian err(1, "waitpid"); 33217921Sjulian PAM_END(); 33317921Sjulian exit(statusp); 33417921Sjulian case -1: 33517921Sjulian err(1, "fork"); 33617921Sjulian PAM_END(); 33717921Sjulian exit(1); 33815885Sjulian case 0: 33915885Sjulian /* 34015885Sjulian * Set all user context except for: Environmental variables 34115885Sjulian * Umask Login records (wtmp, etc) Path 34215885Sjulian */ 34315885Sjulian setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK | 34415885Sjulian LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP); 34515885Sjulian /* 34617967Sjulian * Don't touch resource/priority settings if -m has been used 34718005Sjulian * or -l and -c hasn't, and we're not su'ing to root. 34818005Sjulian */ 34918005Sjulian if ((asme || (!asthem && class == NULL)) && pwd->pw_uid) 35018005Sjulian setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES); 35118005Sjulian if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0) 35217967Sjulian err(1, "setusercontext"); 35317967Sjulian 35417967Sjulian if (!asme) { 35517967Sjulian if (asthem) { 35617967Sjulian p = getenv("TERM"); 35717967Sjulian environ = &cleanenv; 35817967Sjulian 35917967Sjulian /* 36015885Sjulian * Add any environmental variables that the 36115885Sjulian * PAM modules may have set. 36215885Sjulian */ 36315885Sjulian environ_pam = pam_getenvlist(pamh); 36415885Sjulian if (environ_pam) 36515885Sjulian export_pam_environment(); 36615885Sjulian 36717921Sjulian /* set the su'd user's environment & umask */ 36817921Sjulian setusercontext(lc, pwd, pwd->pw_uid, 36917921Sjulian LOGIN_SETPATH | LOGIN_SETUMASK | 37017921Sjulian LOGIN_SETENV); 37115885Sjulian if (p) 37215885Sjulian setenv("TERM", p, 1); 37315885Sjulian if (chdir(pwd->pw_dir) < 0) 37415885Sjulian errx(1, "no directory"); 37515885Sjulian } 37615885Sjulian if (asthem || pwd->pw_uid) 37715885Sjulian setenv("USER", pwd->pw_name, 1); 37815885Sjulian setenv("HOME", pwd->pw_dir, 1); 37917254Sjulian setenv("SHELL", shell, 1); 38017967Sjulian } 38115885Sjulian login_close(lc); 38215885Sjulian 38317921Sjulian if (iscsh == YES) { 38417921Sjulian if (fastlogin) 38517921Sjulian *np.a-- = "-f"; 38615885Sjulian if (asme) 38717921Sjulian *np.a-- = "-m"; 38817921Sjulian } 38917921Sjulian /* csh strips the first character... */ 39017921Sjulian *np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su"; 39117921Sjulian 39217921Sjulian if (ruid != 0) 39317921Sjulian syslog(LOG_NOTICE, "%s to %s%s", username, user, 39417921Sjulian ontty()); 39515885Sjulian 39615885Sjulian execv(shell, np.b); 39717254Sjulian err(1, "%s", shell); 39815885Sjulian } 39915885Sjulian} 40015885Sjulian 40115885Sjulianstatic int 40217254Sjulianexport_pam_environment(void) 40317964Sjulian{ 40417254Sjulian char **pp; 40517254Sjulian 40617254Sjulian for (pp = environ_pam; *pp != NULL; pp++) { 40717254Sjulian if (ok_to_export(*pp)) 40817254Sjulian putenv(*pp); 40917964Sjulian free(*pp); 41017254Sjulian } 41115885Sjulian return PAM_SUCCESS; 41215885Sjulian} 41315885Sjulian 41415885Sjulian/* 41515885Sjulian * Sanity checks on PAM environmental variables: 41615885Sjulian * - Make sure there is an '=' in the string. 41715885Sjulian * - Make sure the string doesn't run on too long. 41815885Sjulian * - Do not export certain variables. This list was taken from the 41915885Sjulian * Solaris pam_putenv(3) man page. 42015885Sjulian */ 42115885Sjulianstatic int 42217254Sjulianok_to_export(const char *s) 42317254Sjulian{ 42417254Sjulian static const char *noexport[] = { 42517921Sjulian "SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH", 42617921Sjulian "IFS", "PATH", NULL 42717921Sjulian }; 42817921Sjulian const char **pp; 42917254Sjulian size_t n; 43015885Sjulian 43117921Sjulian if (strlen(s) > 1024 || strchr(s, '=') == NULL) 43217921Sjulian return 0; 43317921Sjulian if (strncmp(s, "LD_", 3) == 0) 43417921Sjulian return 0; 43517921Sjulian for (pp = noexport; *pp != NULL; pp++) { 43617921Sjulian n = strlen(*pp); 43717921Sjulian if (s[n] == '=' && strncmp(s, *pp, n) == 0) 43817921Sjulian return 0; 43917921Sjulian } 44017921Sjulian return 1; 44115885Sjulian} 44215885Sjulian 44315885Sjulianstatic void 44415885Sjulianusage(void) 44517921Sjulian{ 44617921Sjulian 44717921Sjulian fprintf(stderr, "usage: su [-] [-flm] [-c class] [login [args]]\n"); 44817921Sjulian exit(1); 44917921Sjulian} 45015885Sjulian 45115885Sjulianstatic int 45215885Sjulianchshell(char *sh) 45315885Sjulian{ 45415885Sjulian int r; 45515885Sjulian char *cp; 45617921Sjulian 45717921Sjulian r = 0; 45817921Sjulian setusershell(); 45917921Sjulian do { 46017921Sjulian cp = getusershell(); 46115885Sjulian r = strcmp(cp, sh); 46215885Sjulian } while (!r && cp != NULL); 46315885Sjulian endusershell(); 46415885Sjulian return r; 46515885Sjulian} 46618005Sjulian 46715885Sjulianstatic char * 46815885Sjulianontty(void) 46917921Sjulian{ 47017921Sjulian char *p; 47117921Sjulian static char buf[MAXPATHLEN + 4]; 47215885Sjulian 47315885Sjulian buf[0] = 0; 47415885Sjulian p = ttyname(STDERR_FILENO); 47517921Sjulian if (p) 47617921Sjulian snprintf(buf, sizeof(buf), " on %s", p); 47717921Sjulian return buf; 47817921Sjulian} 47915885Sjulian