su.c revision 3702
11590Srgrimes/* 21590Srgrimes * Copyright (c) 1988, 1993, 1994 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * Redistribution and use in source and binary forms, with or without 61590Srgrimes * modification, are permitted provided that the following conditions 71590Srgrimes * are met: 81590Srgrimes * 1. Redistributions of source code must retain the above copyright 91590Srgrimes * notice, this list of conditions and the following disclaimer. 101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111590Srgrimes * notice, this list of conditions and the following disclaimer in the 121590Srgrimes * documentation and/or other materials provided with the distribution. 131590Srgrimes * 3. All advertising materials mentioning features or use of this software 141590Srgrimes * must display the following acknowledgement: 151590Srgrimes * This product includes software developed by the University of 161590Srgrimes * California, Berkeley and its contributors. 171590Srgrimes * 4. Neither the name of the University nor the names of its contributors 181590Srgrimes * may be used to endorse or promote products derived from this software 191590Srgrimes * without specific prior written permission. 201590Srgrimes * 211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311590Srgrimes * SUCH DAMAGE. 321590Srgrimes */ 331590Srgrimes 341590Srgrimes#ifndef lint 351590Srgrimesstatic char copyright[] = 361590Srgrimes"@(#) Copyright (c) 1988, 1993, 1994\n\ 371590Srgrimes The Regents of the University of California. All rights reserved.\n"; 381590Srgrimes#endif /* not lint */ 391590Srgrimes 401590Srgrimes#ifndef lint 411590Srgrimesstatic char sccsid[] = "@(#)su.c 8.3 (Berkeley) 4/2/94"; 421590Srgrimes#endif /* not lint */ 431590Srgrimes 441590Srgrimes#include <sys/param.h> 451590Srgrimes#include <sys/time.h> 461590Srgrimes#include <sys/resource.h> 471590Srgrimes 481590Srgrimes#include <err.h> 491590Srgrimes#include <errno.h> 501590Srgrimes#include <grp.h> 511590Srgrimes#include <paths.h> 521590Srgrimes#include <pwd.h> 531590Srgrimes#include <stdio.h> 541590Srgrimes#include <stdlib.h> 551590Srgrimes#include <string.h> 561590Srgrimes#include <syslog.h> 571590Srgrimes#include <unistd.h> 581590Srgrimes 593702Spst#ifdef SKEY 603702Spst#include <skey.h> 613702Spst#endif 623702Spst 631590Srgrimes#ifdef KERBEROS 641590Srgrimes#include <kerberosIV/des.h> 651590Srgrimes#include <kerberosIV/krb.h> 661590Srgrimes#include <netdb.h> 671590Srgrimes 681590Srgrimes#define ARGSTR "-Kflm" 691590Srgrimes 701590Srgrimesint use_kerberos = 1; 711590Srgrimes#else 721590Srgrimes#define ARGSTR "-flm" 731590Srgrimes#endif 741590Srgrimes 751590Srgrimeschar *ontty __P((void)); 761590Srgrimesint chshell __P((char *)); 771590Srgrimes 781590Srgrimesint 791590Srgrimesmain(argc, argv) 801590Srgrimes int argc; 811590Srgrimes char **argv; 821590Srgrimes{ 831590Srgrimes extern char **environ; 841590Srgrimes struct passwd *pwd; 851590Srgrimes char *p, **g, *user, *shell, *username, *cleanenv[2], *nargv[4], **np; 861590Srgrimes struct group *gr; 871590Srgrimes uid_t ruid; 881590Srgrimes int asme, ch, asthem, fastlogin, prio; 891590Srgrimes enum { UNSET, YES, NO } iscsh = UNSET; 901590Srgrimes char shellbuf[MAXPATHLEN]; 911590Srgrimes 921590Srgrimes np = &nargv[3]; 931590Srgrimes *np-- = NULL; 941590Srgrimes asme = asthem = fastlogin = 0; 951590Srgrimes while ((ch = getopt(argc, argv, ARGSTR)) != EOF) 961590Srgrimes switch((char)ch) { 971590Srgrimes#ifdef KERBEROS 981590Srgrimes case 'K': 991590Srgrimes use_kerberos = 0; 1001590Srgrimes break; 1011590Srgrimes#endif 1021590Srgrimes case 'f': 1031590Srgrimes fastlogin = 1; 1041590Srgrimes break; 1051590Srgrimes case '-': 1061590Srgrimes case 'l': 1071590Srgrimes asme = 0; 1081590Srgrimes asthem = 1; 1091590Srgrimes break; 1101590Srgrimes case 'm': 1111590Srgrimes asme = 1; 1121590Srgrimes asthem = 0; 1131590Srgrimes break; 1141590Srgrimes case '?': 1151590Srgrimes default: 1161590Srgrimes (void)fprintf(stderr, "usage: su [%s] [login]\n", 1171590Srgrimes ARGSTR); 1181590Srgrimes exit(1); 1191590Srgrimes } 1201590Srgrimes argv += optind; 1211590Srgrimes 1221590Srgrimes errno = 0; 1231590Srgrimes prio = getpriority(PRIO_PROCESS, 0); 1241590Srgrimes if (errno) 1251590Srgrimes prio = 0; 1261590Srgrimes (void)setpriority(PRIO_PROCESS, 0, -2); 1271590Srgrimes openlog("su", LOG_CONS, 0); 1281590Srgrimes 1291590Srgrimes /* get current login name and shell */ 1301590Srgrimes ruid = getuid(); 1311590Srgrimes username = getlogin(); 1321590Srgrimes if (username == NULL || (pwd = getpwnam(username)) == NULL || 1331590Srgrimes pwd->pw_uid != ruid) 1341590Srgrimes pwd = getpwuid(ruid); 1351590Srgrimes if (pwd == NULL) 1361590Srgrimes errx(1, "who are you?"); 1371590Srgrimes username = strdup(pwd->pw_name); 1381590Srgrimes if (username == NULL) 1391590Srgrimes err(1, NULL); 1401590Srgrimes if (asme) 1411590Srgrimes if (pwd->pw_shell && *pwd->pw_shell) 1421590Srgrimes shell = strcpy(shellbuf, pwd->pw_shell); 1431590Srgrimes else { 1441590Srgrimes shell = _PATH_BSHELL; 1451590Srgrimes iscsh = NO; 1461590Srgrimes } 1471590Srgrimes 1481590Srgrimes /* get target login information, default to root */ 1491590Srgrimes user = *argv ? *argv : "root"; 1501590Srgrimes if ((pwd = getpwnam(user)) == NULL) { 1511590Srgrimes fprintf(stderr, "su: unknown login %s\n", user); 1521590Srgrimes exit(1); 1531590Srgrimes } 1541590Srgrimes 1551590Srgrimes if (ruid) { 1561590Srgrimes#ifdef KERBEROS 1571590Srgrimes if (!use_kerberos || kerberos(username, user, pwd->pw_uid)) 1581590Srgrimes#endif 1591590Srgrimes { 1601590Srgrimes /* only allow those in group zero to su to root. */ 1611590Srgrimes if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0))) 1621590Srgrimes for (g = gr->gr_mem;; ++g) { 1631590Srgrimes if (!*g) 1641590Srgrimes errx(1, 1651590Srgrimes "you are not in the correct group to su %s.", 1661590Srgrimes user); 1671590Srgrimes if (strcmp(username, *g) == 0) 1681590Srgrimes break; 1691590Srgrimes } 1701590Srgrimes /* if target requires a password, verify it */ 1711590Srgrimes if (*pwd->pw_passwd) { 1723208Spst#ifdef SKEY 1733208Spst p = skey_getpass("Password:", pwd, 1); 1743208Spst if (strcmp(pwd->pw_passwd, 1753208Spst skey_crypt(p, pwd->pw_passwd, pwd, 1))) { 1763208Spst#else 1771590Srgrimes p = getpass("Password:"); 1781590Srgrimes if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) { 1793208Spst#endif 1801590Srgrimes fprintf(stderr, "Sorry\n"); 1811590Srgrimes syslog(LOG_AUTH|LOG_WARNING, 1821590Srgrimes "BAD SU %s to %s%s", username, 1831590Srgrimes user, ontty()); 1841590Srgrimes exit(1); 1851590Srgrimes } 1861590Srgrimes } 1871590Srgrimes } 1881590Srgrimes } 1891590Srgrimes 1901590Srgrimes if (asme) { 1911590Srgrimes /* if asme and non-standard target shell, must be root */ 1921590Srgrimes if (!chshell(pwd->pw_shell) && ruid) 1931590Srgrimes errx(1, "permission denied (shell)."); 1941590Srgrimes } else if (pwd->pw_shell && *pwd->pw_shell) { 1951590Srgrimes shell = pwd->pw_shell; 1961590Srgrimes iscsh = UNSET; 1971590Srgrimes } else { 1981590Srgrimes shell = _PATH_BSHELL; 1991590Srgrimes iscsh = NO; 2001590Srgrimes } 2011590Srgrimes 2021590Srgrimes /* if we're forking a csh, we want to slightly muck the args */ 2031590Srgrimes if (iscsh == UNSET) { 2041590Srgrimes if (p = strrchr(shell, '/')) 2051590Srgrimes ++p; 2061590Srgrimes else 2071590Srgrimes p = shell; 2081590Srgrimes iscsh = strcmp(p, "csh") ? NO : YES; 2091590Srgrimes } 2101590Srgrimes 2111590Srgrimes /* set permissions */ 2121590Srgrimes if (setgid(pwd->pw_gid) < 0) 2131590Srgrimes err(1, "setgid"); 2141590Srgrimes if (initgroups(user, pwd->pw_gid)) 2151590Srgrimes errx(1, "initgroups failed"); 2161590Srgrimes if (setuid(pwd->pw_uid) < 0) 2171590Srgrimes err(1, "setuid"); 2181590Srgrimes 2191590Srgrimes if (!asme) { 2201590Srgrimes if (asthem) { 2211590Srgrimes p = getenv("TERM"); 2221590Srgrimes cleanenv[0] = _PATH_DEFPATH; 2231590Srgrimes cleanenv[1] = NULL; 2241590Srgrimes environ = cleanenv; 2251590Srgrimes (void)setenv("TERM", p, 1); 2261590Srgrimes if (chdir(pwd->pw_dir) < 0) 2271590Srgrimes errx(1, "no directory"); 2281590Srgrimes } 2291590Srgrimes if (asthem || pwd->pw_uid) 2301590Srgrimes (void)setenv("USER", pwd->pw_name, 1); 2311590Srgrimes (void)setenv("HOME", pwd->pw_dir, 1); 2321590Srgrimes (void)setenv("SHELL", shell, 1); 2331590Srgrimes } 2341590Srgrimes 2351590Srgrimes if (iscsh == YES) { 2361590Srgrimes if (fastlogin) 2371590Srgrimes *np-- = "-f"; 2381590Srgrimes if (asme) 2391590Srgrimes *np-- = "-m"; 2401590Srgrimes } 2411590Srgrimes 2421590Srgrimes /* csh strips the first character... */ 2431590Srgrimes *np = asthem ? "-su" : iscsh == YES ? "_su" : "su"; 2441590Srgrimes 2451590Srgrimes if (ruid != 0) 2461590Srgrimes syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s", 2471590Srgrimes username, user, ontty()); 2481590Srgrimes 2491590Srgrimes (void)setpriority(PRIO_PROCESS, 0, prio); 2501590Srgrimes 2511590Srgrimes execv(shell, np); 2521590Srgrimes err(1, "%s", shell); 2531590Srgrimes} 2541590Srgrimes 2551590Srgrimesint 2561590Srgrimeschshell(sh) 2571590Srgrimes char *sh; 2581590Srgrimes{ 2591590Srgrimes char *cp; 2601590Srgrimes 2611590Srgrimes while ((cp = getusershell()) != NULL) 2621590Srgrimes if (strcmp(cp, sh) == 0) 2631590Srgrimes return (1); 2641590Srgrimes return (0); 2651590Srgrimes} 2661590Srgrimes 2671590Srgrimeschar * 2681590Srgrimesontty() 2691590Srgrimes{ 2701590Srgrimes char *p; 2711590Srgrimes static char buf[MAXPATHLEN + 4]; 2721590Srgrimes 2731590Srgrimes buf[0] = 0; 2741590Srgrimes if (p = ttyname(STDERR_FILENO)) 2751590Srgrimes snprintf(buf, sizeof(buf), " on %s", p); 2761590Srgrimes return (buf); 2771590Srgrimes} 2781590Srgrimes 2791590Srgrimes#ifdef KERBEROS 2801590Srgrimeskerberos(username, user, uid) 2811590Srgrimes char *username, *user; 2821590Srgrimes int uid; 2831590Srgrimes{ 2841590Srgrimes extern char *krb_err_txt[]; 2851590Srgrimes KTEXT_ST ticket; 2861590Srgrimes AUTH_DAT authdata; 2871590Srgrimes struct hostent *hp; 2881590Srgrimes char *p; 2891590Srgrimes int kerno; 2901590Srgrimes u_long faddr; 2911590Srgrimes char lrealm[REALM_SZ], krbtkfile[MAXPATHLEN]; 2921590Srgrimes char hostname[MAXHOSTNAMELEN], savehost[MAXHOSTNAMELEN]; 2931590Srgrimes char *krb_get_phost(); 2941590Srgrimes 2951590Srgrimes if (krb_get_lrealm(lrealm, 1) != KSUCCESS) 2961590Srgrimes return (1); 2971590Srgrimes if (koktologin(username, lrealm, user) && !uid) { 2981590Srgrimes warnx("kerberos: not in %s's ACL.", user); 2991590Srgrimes return (1); 3001590Srgrimes } 3011590Srgrimes (void)sprintf(krbtkfile, "%s_%s_%d", TKT_ROOT, user, getuid()); 3021590Srgrimes 3031590Srgrimes (void)setenv("KRBTKFILE", krbtkfile, 1); 3041590Srgrimes (void)krb_set_tkt_string(krbtkfile); 3051590Srgrimes /* 3061590Srgrimes * Set real as well as effective ID to 0 for the moment, 3071590Srgrimes * to make the kerberos library do the right thing. 3081590Srgrimes */ 3091590Srgrimes if (setuid(0) < 0) { 3101590Srgrimes warn("setuid"); 3111590Srgrimes return (1); 3121590Srgrimes } 3131590Srgrimes 3141590Srgrimes /* 3151590Srgrimes * Little trick here -- if we are su'ing to root, 3161590Srgrimes * we need to get a ticket for "xxx.root", where xxx represents 3171590Srgrimes * the name of the person su'ing. Otherwise (non-root case), 3181590Srgrimes * we need to get a ticket for "yyy.", where yyy represents 3191590Srgrimes * the name of the person being su'd to, and the instance is null 3201590Srgrimes * 3211590Srgrimes * We should have a way to set the ticket lifetime, 3221590Srgrimes * with a system default for root. 3231590Srgrimes */ 3241590Srgrimes kerno = krb_get_pw_in_tkt((uid == 0 ? username : user), 3251590Srgrimes (uid == 0 ? "root" : ""), lrealm, 3261590Srgrimes "krbtgt", lrealm, DEFAULT_TKT_LIFE, 0); 3271590Srgrimes 3281590Srgrimes if (kerno != KSUCCESS) { 3291590Srgrimes if (kerno == KDC_PR_UNKNOWN) { 3301590Srgrimes warnx("kerberos: principal unknown: %s.%s@%s", 3311590Srgrimes (uid == 0 ? username : user), 3321590Srgrimes (uid == 0 ? "root" : ""), lrealm); 3331590Srgrimes return (1); 3341590Srgrimes } 3351590Srgrimes warnx("kerberos: unable to su: %s", krb_err_txt[kerno]); 3361590Srgrimes syslog(LOG_NOTICE|LOG_AUTH, 3371590Srgrimes "BAD Kerberos SU: %s to %s%s: %s", 3381590Srgrimes username, user, ontty(), krb_err_txt[kerno]); 3391590Srgrimes return (1); 3401590Srgrimes } 3411590Srgrimes 3421590Srgrimes if (chown(krbtkfile, uid, -1) < 0) { 3431590Srgrimes warn("chown"); 3441590Srgrimes (void)unlink(krbtkfile); 3451590Srgrimes return (1); 3461590Srgrimes } 3471590Srgrimes 3481590Srgrimes (void)setpriority(PRIO_PROCESS, 0, -2); 3491590Srgrimes 3501590Srgrimes if (gethostname(hostname, sizeof(hostname)) == -1) { 3511590Srgrimes warn("gethostname"); 3521590Srgrimes dest_tkt(); 3531590Srgrimes return (1); 3541590Srgrimes } 3551590Srgrimes 3561590Srgrimes (void)strncpy(savehost, krb_get_phost(hostname), sizeof(savehost)); 3571590Srgrimes savehost[sizeof(savehost) - 1] = '\0'; 3581590Srgrimes 3591590Srgrimes kerno = krb_mk_req(&ticket, "rcmd", savehost, lrealm, 33); 3601590Srgrimes 3611590Srgrimes if (kerno == KDC_PR_UNKNOWN) { 3621590Srgrimes warnx("Warning: TGT not verified."); 3631590Srgrimes syslog(LOG_NOTICE|LOG_AUTH, 3641590Srgrimes "%s to %s%s, TGT not verified (%s); %s.%s not registered?", 3651590Srgrimes username, user, ontty(), krb_err_txt[kerno], 3661590Srgrimes "rcmd", savehost); 3671590Srgrimes } else if (kerno != KSUCCESS) { 3681590Srgrimes warnx("Unable to use TGT: %s", krb_err_txt[kerno]); 3691590Srgrimes syslog(LOG_NOTICE|LOG_AUTH, "failed su: %s to %s%s: %s", 3701590Srgrimes username, user, ontty(), krb_err_txt[kerno]); 3711590Srgrimes dest_tkt(); 3721590Srgrimes return (1); 3731590Srgrimes } else { 3741590Srgrimes if (!(hp = gethostbyname(hostname))) { 3751590Srgrimes warnx("can't get addr of %s", hostname); 3761590Srgrimes dest_tkt(); 3771590Srgrimes return (1); 3781590Srgrimes } 3791590Srgrimes memmove((char *)&faddr, (char *)hp->h_addr, sizeof(faddr)); 3801590Srgrimes 3811590Srgrimes if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr, 3821590Srgrimes &authdata, "")) != KSUCCESS) { 3831590Srgrimes warnx("kerberos: unable to verify rcmd ticket: %s\n", 3841590Srgrimes krb_err_txt[kerno]); 3851590Srgrimes syslog(LOG_NOTICE|LOG_AUTH, 3861590Srgrimes "failed su: %s to %s%s: %s", username, 3871590Srgrimes user, ontty(), krb_err_txt[kerno]); 3881590Srgrimes dest_tkt(); 3891590Srgrimes return (1); 3901590Srgrimes } 3911590Srgrimes } 3921590Srgrimes return (0); 3931590Srgrimes} 3941590Srgrimes 3951590Srgrimeskoktologin(name, realm, toname) 3961590Srgrimes char *name, *realm, *toname; 3971590Srgrimes{ 3981590Srgrimes AUTH_DAT *kdata; 3991590Srgrimes AUTH_DAT kdata_st; 4001590Srgrimes 4011590Srgrimes kdata = &kdata_st; 4021590Srgrimes memset((char *)kdata, 0, sizeof(*kdata)); 4031590Srgrimes (void)strcpy(kdata->pname, name); 4041590Srgrimes (void)strcpy(kdata->pinst, 4051590Srgrimes ((strcmp(toname, "root") == 0) ? "root" : "")); 4061590Srgrimes (void)strcpy(kdata->prealm, realm); 4071590Srgrimes return (kuserok(kdata, toname)); 4081590Srgrimes} 4091590Srgrimes#endif 410