su.c revision 9502
121308Sache/* 221308Sache * Copyright (c) 1988, 1993, 1994 321308Sache * The Regents of the University of California. All rights reserved. 4165670Sache * 521308Sache * Redistribution and use in source and binary forms, with or without 621308Sache * modification, are permitted provided that the following conditions 721308Sache * are met: 821308Sache * 1. Redistributions of source code must retain the above copyright 921308Sache * notice, this list of conditions and the following disclaimer. 1021308Sache * 2. Redistributions in binary form must reproduce the above copyright 1158310Sache * notice, this list of conditions and the following disclaimer in the 1221308Sache * documentation and/or other materials provided with the distribution. 1321308Sache * 3. All advertising materials mentioning features or use of this software 1421308Sache * must display the following acknowledgement: 1521308Sache * This product includes software developed by the University of 1621308Sache * California, Berkeley and its contributors. 1721308Sache * 4. Neither the name of the University nor the names of its contributors 1821308Sache * may be used to endorse or promote products derived from this software 1921308Sache * without specific prior written permission. 2021308Sache * 2121308Sache * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2258310Sache * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2321308Sache * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2421308Sache * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2521308Sache * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2621308Sache * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2721308Sache * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2821308Sache * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2921308Sache * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3021308Sache * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3121308Sache * SUCH DAMAGE. 3221308Sache */ 3321308Sache 3421308Sache#ifndef lint 3521308Sachestatic char copyright[] = 3621308Sache"@(#) Copyright (c) 1988, 1993, 1994\n\ 3721308Sache The Regents of the University of California. All rights reserved.\n"; 3821308Sache#endif /* not lint */ 3921308Sache 4021308Sache#ifndef lint 4121308Sachestatic char sccsid[] = "@(#)su.c 8.3 (Berkeley) 4/2/94"; 4221308Sache#endif /* not lint */ 4321308Sache 4421308Sache#include <sys/param.h> 4521308Sache#include <sys/time.h> 4621308Sache#include <sys/resource.h> 4721308Sache 4821308Sache#include <err.h> 4921308Sache#include <errno.h> 5058310Sache#include <grp.h> 51119610Sache#include <paths.h> 5258310Sache#include <pwd.h> 53165670Sache#include <stdio.h> 54165670Sache#include <stdlib.h> 5521308Sache#include <string.h> 5621308Sache#include <syslog.h> 5721308Sache#include <unistd.h> 5821308Sache 5921308Sache#ifdef SKEY 6021308Sache#include <skey.h> 6121308Sache#endif 6221308Sache 6321308Sache#ifdef KERBEROS 6421308Sache#include <kerberosIV/des.h> 6521308Sache#include <kerberosIV/krb.h> 6621308Sache#include <netdb.h> 6721308Sache 6821308Sache#define ARGSTR "-Kflm" 6921308Sache 7021308Sacheint use_kerberos = 1; 71165670Sache#else 72165670Sache#define ARGSTR "-flm" 73165670Sache#endif 74165670Sache 75165670Sachechar *ontty __P((void)); 76165670Sacheint chshell __P((char *)); 77165670Sache 78165670Sacheint 79165670Sachemain(argc, argv) 80165670Sache int argc; 81165670Sache char **argv; 82165670Sache{ 83165670Sache extern char **environ; 84165670Sache struct passwd *pwd; 85165670Sache#ifdef WHEELSU 86165670Sache char *targetpass; 87165670Sache int iswheelsu; 88165670Sache#endif /* WHEELSU */ 8921308Sache char *p, **g, *user, *shell, *username, *cleanenv[20], *nargv[4], **np; 9021308Sache struct group *gr; 9121308Sache uid_t ruid; 9221308Sache int asme, ch, asthem, fastlogin, prio; 9321308Sache enum { UNSET, YES, NO } iscsh = UNSET; 9421308Sache char shellbuf[MAXPATHLEN]; 9521308Sache 9621308Sache np = &nargv[3]; 97165670Sache *np-- = NULL; 98165670Sache#ifdef WHEELSU 99165670Sache iswheelsu = 10021308Sache#endif /* WHEELSU */ 10121308Sache asme = asthem = fastlogin = 0; 10221308Sache while ((ch = getopt(argc, argv, ARGSTR)) != EOF) 10321308Sache switch((char)ch) { 10421308Sache#ifdef KERBEROS 10521308Sache case 'K': 10675406Sache use_kerberos = 0; 10721308Sache break; 108165670Sache#endif 109165670Sache case 'f': 110165670Sache fastlogin = 1; 11121308Sache break; 11221308Sache case '-': 113165670Sache case 'l': 11421308Sache asme = 0; 11521308Sache asthem = 1; 11621308Sache break; 11721308Sache case 'm': 11821308Sache asme = 1; 11921308Sache asthem = 0; 12021308Sache break; 12121308Sache case '?': 122165670Sache default: 12321308Sache (void)fprintf(stderr, "usage: su [%s] [login]\n", 12421308Sache ARGSTR); 125165670Sache exit(1); 126165670Sache } 127165670Sache argv += optind; 128165670Sache 129165670Sache errno = 0; 130165670Sache prio = getpriority(PRIO_PROCESS, 0); 131165670Sache if (errno) 132165670Sache prio = 0; 133165670Sache (void)setpriority(PRIO_PROCESS, 0, -2); 134165670Sache openlog("su", LOG_CONS, 0); 135165670Sache 136165670Sache /* get current login name and shell */ 137165670Sache ruid = getuid(); 138165670Sache username = getlogin(); 139165670Sache if (username == NULL || (pwd = getpwnam(username)) == NULL || 140165670Sache pwd->pw_uid != ruid) 141165670Sache pwd = getpwuid(ruid); 142165670Sache if (pwd == NULL) 143165670Sache errx(1, "who are you?"); 144165670Sache username = strdup(pwd->pw_name); 145165670Sache if (username == NULL) 146165670Sache err(1, NULL); 147165670Sache if (asme) 148165670Sache if (pwd->pw_shell && *pwd->pw_shell) 149165670Sache shell = strcpy(shellbuf, pwd->pw_shell); 150165670Sache else { 151165670Sache shell = _PATH_BSHELL; 152165670Sache iscsh = NO; 153165670Sache } 154165670Sache 155165670Sache /* get target login information, default to root */ 156165670Sache user = *argv ? *argv : "root"; 157165670Sache if ((pwd = getpwnam(user)) == NULL) { 158165670Sache errx(1, "unknown login: %s", user); 159165670Sache } 160165670Sache 16121308Sache#ifdef WHEELSU 16221308Sache targetpass = strdup(pwd->pw_passwd); 16321308Sache#endif /* WHEELSU */ 16421308Sache 16521308Sache if (ruid) { 16621308Sache#ifdef KERBEROS 16775406Sache if (!use_kerberos || kerberos(username, user, pwd->pw_uid)) 16821308Sache#endif 16921308Sache { 17021308Sache /* only allow those in group zero to su to root. */ 17175406Sache if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0))) 17221308Sache for (g = gr->gr_mem;; ++g) { 17321308Sache if (!*g) 17421308Sache errx(1, 17521308Sache "you are not in the correct group to su %s.", 17621308Sache user); 17721308Sache if (strcmp(username, *g) == 0) { 17875406Sache#ifdef WHEELSU 17921308Sache iswheelsu = 1; 18021308Sache#endif /* WHEELSU */ 18121308Sache break; 18221308Sache } 18321308Sache } 18421308Sache /* if target requires a password, verify it */ 18521308Sache if (*pwd->pw_passwd) { 18621308Sache#ifdef SKEY 18721308Sache#ifdef WHEELSU 18821308Sache if (iswheelsu) { 18921308Sache pwd = getpwnam(username); 19021308Sache } 19121308Sache#endif /* WHEELSU */ 19221308Sache p = skey_getpass("Password:", pwd, 1); 19321308Sache if (!(!strcmp(pwd->pw_passwd, 19421308Sache skey_crypt(p, pwd->pw_passwd, pwd, 1)) 19521308Sache#ifdef WHEELSU 19621308Sache || (iswheelsu && !strcmp(targetpass, 19721308Sache crypt(p, 19821308Sache targetpass))) 19921308Sache#endif /* WHEELSU */ 20021308Sache )) { 20121308Sache#else 20221308Sache p = getpass("Password:"); 20321308Sache if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) { 20421308Sache#endif 20521308Sache fprintf(stderr, "Sorry\n"); 20621308Sache syslog(LOG_AUTH|LOG_WARNING, 20721308Sache "BAD SU %s to %s%s", username, 20821308Sache user, ontty()); 20921308Sache exit(1); 21021308Sache } 21121308Sache#ifdef WHEELSU 21221308Sache if (iswheelsu) { 21375406Sache pwd = getpwnam(user); 21421308Sache } 21521308Sache#endif /* WHEELSU */ 21621308Sache } 21721308Sache } 21875406Sache } 21921308Sache 22021308Sache if (asme) { 22121308Sache /* if asme and non-standard target shell, must be root */ 222165670Sache if (!chshell(pwd->pw_shell) && ruid) 223165670Sache errx(1, "permission denied (shell)."); 22421308Sache } else if (pwd->pw_shell && *pwd->pw_shell) { 22521308Sache shell = pwd->pw_shell; 22621308Sache iscsh = UNSET; 22721308Sache } else { 22821308Sache shell = _PATH_BSHELL; 22921308Sache iscsh = NO; 23021308Sache } 23121308Sache 23221308Sache /* if we're forking a csh, we want to slightly muck the args */ 23321308Sache if (iscsh == UNSET) { 23421308Sache if (p = strrchr(shell, '/')) 23521308Sache ++p; 23621308Sache else 23721308Sache p = shell; 23821308Sache if ((iscsh = strcmp(p, "csh") ? NO : YES) == NO) 23921308Sache iscsh = strcmp(p, "tcsh") ? NO : YES; 24021308Sache } 24121308Sache 24221308Sache /* set permissions */ 24321308Sache if (setgid(pwd->pw_gid) < 0) 24421308Sache err(1, "setgid"); 24521308Sache if (initgroups(user, pwd->pw_gid)) 24621308Sache errx(1, "initgroups failed"); 24721308Sache if (setuid(pwd->pw_uid) < 0) 24821308Sache err(1, "setuid"); 24921308Sache 25021308Sache if (!asme) { 25121308Sache if (asthem) { 25221308Sache p = getenv("TERM"); 25321308Sache cleanenv[0] = NULL; 25421308Sache environ = cleanenv; 25521308Sache (void)setenv("PATH", _PATH_DEFPATH, 1); 25621308Sache (void)setenv("TERM", p, 1); 25721308Sache if (chdir(pwd->pw_dir) < 0) 25821308Sache errx(1, "no directory"); 25921308Sache } 26021308Sache if (asthem || pwd->pw_uid) 26121308Sache (void)setenv("USER", pwd->pw_name, 1); 26221308Sache (void)setenv("HOME", pwd->pw_dir, 1); 26321308Sache (void)setenv("SHELL", shell, 1); 26421308Sache } 26521308Sache 26621308Sache if (iscsh == YES) { 26721308Sache if (fastlogin) 26821308Sache *np-- = "-f"; 26921308Sache if (asme) 27021308Sache *np-- = "-m"; 27121308Sache } 27221308Sache 27321308Sache /* csh strips the first character... */ 27421308Sache *np = asthem ? "-su" : iscsh == YES ? "_su" : "su"; 27521308Sache 27621308Sache if (ruid != 0) 27721308Sache syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s", 27821308Sache username, user, ontty()); 27921308Sache 28021308Sache (void)setpriority(PRIO_PROCESS, 0, prio); 28121308Sache 28221308Sache execv(shell, np); 28321308Sache err(1, "%s", shell); 28421308Sache} 28521308Sache 28621308Sacheint 28721308Sachechshell(sh) 28821308Sache char *sh; 28921308Sache{ 29021308Sache char *cp; 29121308Sache 29221308Sache while ((cp = getusershell()) != NULL) 29321308Sache if (strcmp(cp, sh) == 0) 29421308Sache return (1); 29575406Sache return (0); 29621308Sache} 29721308Sache 29821308Sachechar * 29921308Sacheontty() 300157184Sache{ 301157184Sache char *p; 302157184Sache static char buf[MAXPATHLEN + 4]; 303157184Sache 30421308Sache buf[0] = 0; 305157184Sache if (p = ttyname(STDERR_FILENO)) 30621308Sache snprintf(buf, sizeof(buf), " on %s", p); 30721308Sache return (buf); 30821308Sache} 30921308Sache 31021308Sache#ifdef KERBEROS 31121308Sachekerberos(username, user, uid) 31221308Sache char *username, *user; 31321308Sache int uid; 31421308Sache{ 31521308Sache extern char *krb_err_txt[]; 31621308Sache KTEXT_ST ticket; 31721308Sache AUTH_DAT authdata; 31821308Sache struct hostent *hp; 31921308Sache char *p; 32021308Sache int kerno; 32121308Sache u_long faddr; 32221308Sache char lrealm[REALM_SZ], krbtkfile[MAXPATHLEN]; 32375406Sache char hostname[MAXHOSTNAMELEN], savehost[MAXHOSTNAMELEN]; 32421308Sache char *krb_get_phost(); 32521308Sache 32621308Sache if (krb_get_lrealm(lrealm, 1) != KSUCCESS) 32721308Sache return (1); 32821308Sache if (koktologin(username, lrealm, user) && !uid) { 329 warnx("kerberos: not in %s's ACL.", user); 330 return (1); 331 } 332 (void)sprintf(krbtkfile, "%s_%s_%d", TKT_ROOT, user, getuid()); 333 334 (void)setenv("KRBTKFILE", krbtkfile, 1); 335 (void)krb_set_tkt_string(krbtkfile); 336 /* 337 * Set real as well as effective ID to 0 for the moment, 338 * to make the kerberos library do the right thing. 339 */ 340 if (setuid(0) < 0) { 341 warn("setuid"); 342 return (1); 343 } 344 345 /* 346 * Little trick here -- if we are su'ing to root, 347 * we need to get a ticket for "xxx.root", where xxx represents 348 * the name of the person su'ing. Otherwise (non-root case), 349 * we need to get a ticket for "yyy.", where yyy represents 350 * the name of the person being su'd to, and the instance is null 351 * 352 * We should have a way to set the ticket lifetime, 353 * with a system default for root. 354 */ 355 kerno = krb_get_pw_in_tkt((uid == 0 ? username : user), 356 (uid == 0 ? "root" : ""), lrealm, 357 "krbtgt", lrealm, DEFAULT_TKT_LIFE, 0); 358 359 if (kerno != KSUCCESS) { 360 if (kerno == KDC_PR_UNKNOWN) { 361 warnx("kerberos: principal unknown: %s.%s@%s", 362 (uid == 0 ? username : user), 363 (uid == 0 ? "root" : ""), lrealm); 364 return (1); 365 } 366 warnx("kerberos: unable to su: %s", krb_err_txt[kerno]); 367 syslog(LOG_NOTICE|LOG_AUTH, 368 "BAD Kerberos SU: %s to %s%s: %s", 369 username, user, ontty(), krb_err_txt[kerno]); 370 return (1); 371 } 372 373 if (chown(krbtkfile, uid, -1) < 0) { 374 warn("chown"); 375 (void)unlink(krbtkfile); 376 return (1); 377 } 378 379 (void)setpriority(PRIO_PROCESS, 0, -2); 380 381 if (gethostname(hostname, sizeof(hostname)) == -1) { 382 warn("gethostname"); 383 dest_tkt(); 384 return (1); 385 } 386 387 (void)strncpy(savehost, krb_get_phost(hostname), sizeof(savehost)); 388 savehost[sizeof(savehost) - 1] = '\0'; 389 390 kerno = krb_mk_req(&ticket, "rcmd", savehost, lrealm, 33); 391 392 if (kerno == KDC_PR_UNKNOWN) { 393 warnx("Warning: TGT not verified."); 394 syslog(LOG_NOTICE|LOG_AUTH, 395 "%s to %s%s, TGT not verified (%s); %s.%s not registered?", 396 username, user, ontty(), krb_err_txt[kerno], 397 "rcmd", savehost); 398 } else if (kerno != KSUCCESS) { 399 warnx("Unable to use TGT: %s", krb_err_txt[kerno]); 400 syslog(LOG_NOTICE|LOG_AUTH, "failed su: %s to %s%s: %s", 401 username, user, ontty(), krb_err_txt[kerno]); 402 dest_tkt(); 403 return (1); 404 } else { 405 if (!(hp = gethostbyname(hostname))) { 406 warnx("can't get addr of %s", hostname); 407 dest_tkt(); 408 return (1); 409 } 410 memmove((char *)&faddr, (char *)hp->h_addr, sizeof(faddr)); 411 412 if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr, 413 &authdata, "")) != KSUCCESS) { 414 warnx("kerberos: unable to verify rcmd ticket: %s\n", 415 krb_err_txt[kerno]); 416 syslog(LOG_NOTICE|LOG_AUTH, 417 "failed su: %s to %s%s: %s", username, 418 user, ontty(), krb_err_txt[kerno]); 419 dest_tkt(); 420 return (1); 421 } 422 } 423 return (0); 424} 425 426koktologin(name, realm, toname) 427 char *name, *realm, *toname; 428{ 429 AUTH_DAT *kdata; 430 AUTH_DAT kdata_st; 431 432 kdata = &kdata_st; 433 memset((char *)kdata, 0, sizeof(*kdata)); 434 (void)strcpy(kdata->pname, name); 435 (void)strcpy(kdata->pinst, 436 ((strcmp(toname, "root") == 0) ? "root" : "")); 437 (void)strcpy(kdata->prealm, realm); 438 return (kuserok(kdata, toname)); 439} 440#endif 441