su.c revision 29923
1/* 2 * Copyright (c) 1988, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#ifndef lint 35static const char copyright[] = 36"@(#) Copyright (c) 1988, 1993, 1994\n\ 37 The Regents of the University of California. All rights reserved.\n"; 38#endif /* not lint */ 39 40#ifndef lint 41#if 0 42static char sccsid[] = "@(#)su.c 8.3 (Berkeley) 4/2/94"; 43#endif 44static const char rcsid[] = 45 "$Id$"; 46#endif /* not lint */ 47 48#include <sys/param.h> 49#include <sys/time.h> 50#include <sys/resource.h> 51 52#include <err.h> 53#include <errno.h> 54#include <grp.h> 55#include <paths.h> 56#include <pwd.h> 57#include <stdio.h> 58#include <stdlib.h> 59#include <string.h> 60#include <syslog.h> 61#include <unistd.h> 62 63#ifdef LOGIN_CAP 64#include <login_cap.h> 65#ifdef LOGIN_CAP_AUTH 66#undef SKEY 67#undef KERBEROS 68#endif 69#endif 70 71#ifdef SKEY 72#include <skey.h> 73#endif 74 75#ifdef KERBEROS 76#include <des.h> 77#include <krb.h> 78#include <netdb.h> 79 80#define ARGSTR "-Kflm" 81 82static int kerberos(char *username, char *user, int uid, char *pword); 83static int koktologin(char *name, char *toname); 84 85int use_kerberos = 1; 86#else /* !KERBEROS */ 87#define ARGSTR "-flm" 88#endif /* KERBEROS */ 89 90char *ontty __P((void)); 91int chshell __P((char *)); 92static void usage __P((void)); 93 94int 95main(argc, argv) 96 int argc; 97 char **argv; 98{ 99 extern char **environ; 100 struct passwd *pwd; 101#ifdef WHEELSU 102 char *targetpass; 103 int iswheelsu; 104#endif /* WHEELSU */ 105 char *p, **g, *user, *shell=NULL, *username, **cleanenv, **nargv, **np; 106 struct group *gr; 107 uid_t ruid; 108 int asme, ch, asthem, fastlogin, prio, i; 109 enum { UNSET, YES, NO } iscsh = UNSET; 110#ifdef LOGIN_CAP 111 login_cap_t *lc; 112 int setwhat; 113#ifdef LOGIN_CAP_AUTH 114 char *style, *approvep, *auth_method = NULL; 115#endif 116#endif 117#ifdef KERBEROS 118 char *k; 119#endif 120 char shellbuf[MAXPATHLEN]; 121 122#ifdef WHEELSU 123 iswheelsu = 124#endif /* WHEELSU */ 125 asme = asthem = fastlogin = 0; 126 user = "root"; 127 while(optind < argc) 128 if((ch = getopt(argc, argv, ARGSTR)) != -1) 129 switch((char)ch) { 130#ifdef KERBEROS 131 case 'K': 132 use_kerberos = 0; 133 break; 134#endif 135 case 'f': 136 fastlogin = 1; 137 break; 138 case '-': 139 case 'l': 140 asme = 0; 141 asthem = 1; 142 break; 143 case 'm': 144 asme = 1; 145 asthem = 0; 146 break; 147 case '?': 148 default: 149 usage(); 150 } 151 else 152 { 153 user = argv[optind++]; 154 break; 155 } 156 157 if (user == NULL) 158 usage(); 159 160 if ((nargv = malloc (sizeof (char *) * (argc + 4))) == NULL) { 161 errx(1, "malloc failure"); 162 } 163 164 nargv[argc + 3] = NULL; 165 for (i = argc; i >= optind; i--) 166 nargv[i + 3] = argv[i]; 167 np = &nargv[i + 3]; 168 169 argv += optind; 170 171 errno = 0; 172 prio = getpriority(PRIO_PROCESS, 0); 173 if (errno) 174 prio = 0; 175 (void)setpriority(PRIO_PROCESS, 0, -2); 176 openlog("su", LOG_CONS, 0); 177 178 /* get current login name and shell */ 179 ruid = getuid(); 180 username = getlogin(); 181 if (username == NULL || (pwd = getpwnam(username)) == NULL || 182 pwd->pw_uid != ruid) 183 pwd = getpwuid(ruid); 184 if (pwd == NULL) 185 errx(1, "who are you?"); 186 username = strdup(pwd->pw_name); 187 if (username == NULL) 188 err(1, NULL); 189 if (asme) { 190 if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') { 191 /* copy: pwd memory is recycled */ 192 shell = strncpy(shellbuf, pwd->pw_shell, sizeof shellbuf); 193 shellbuf[sizeof shellbuf - 1] = '\0'; 194 } else { 195 shell = _PATH_BSHELL; 196 iscsh = NO; 197 } 198 } 199 200#ifdef LOGIN_CAP_AUTH 201 if (auth_method = strchr(user, ':')) { 202 *auth_method = '\0'; 203 auth_method++; 204 if (*auth_method == '\0') 205 auth_method = NULL; 206 } 207#endif /* !LOGIN_CAP_AUTH */ 208 209 /* get target login information, default to root */ 210 if ((pwd = getpwnam(user)) == NULL) { 211 errx(1, "unknown login: %s", user); 212 } 213#ifdef LOGIN_CAP 214 lc = login_getpwclass(pwd); 215#endif 216 217#ifdef WHEELSU 218 targetpass = strdup(pwd->pw_passwd); 219#endif /* WHEELSU */ 220 221 if (ruid) { 222#ifdef KERBEROS 223 if (use_kerberos && koktologin(username, user) 224 && !pwd->pw_uid) { 225 warnx("kerberos: not in %s's ACL.", user); 226 use_kerberos = 0; 227 } 228#endif 229 { 230 /* only allow those in group zero to su to root. */ 231 if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0)) && 232 gr->gr_mem && *(gr->gr_mem)) 233 for (g = gr->gr_mem;; ++g) { 234 if (!*g) 235 errx(1, 236 "you are not in the correct group to su %s.", 237 user); 238 if (strcmp(username, *g) == 0) { 239#ifdef WHEELSU 240 iswheelsu = 1; 241#endif /* WHEELSU */ 242 break; 243 } 244 } 245 } 246 /* if target requires a password, verify it */ 247 if (*pwd->pw_passwd) { 248#ifdef LOGIN_CAP_AUTH 249 /* 250 * This hands off authorisation to an authorisation program, 251 * depending on the styles available for the "auth-su", 252 * authorisation styles. 253 */ 254 if ((style = login_getstyle(lc, auth_method, "su")) == NULL) 255 errx(1, "auth method available for su.\n"); 256 if (authenticate(user, lc ? lc->lc_class : "default", style, "su") != 0) { 257#ifdef WHEELSU 258 if (!iswheelsu || authenticate(username, lc ? lc->lc_class : "default", style, "su") != 0) { 259#endif /* WHEELSU */ 260 { 261 fprintf(stderr, "Sorry\n"); 262 syslog(LOG_AUTH|LOG_WARNING,"BAD SU %s to %s%s", username, user, ontty()); 263 exit(1); 264 } 265 } 266 267 /* 268 * If authentication succeeds, run any approval 269 * program, if applicable for this class. 270 */ 271 approvep = login_getcapstr(lc, "approve", NULL, NULL); 272 if (approvep==NULL || auth_script(approvep, approvep, username, lc->lc_class, 0) == 0) { 273 int r = auth_scan(AUTH_OKAY); 274 /* See what the authorise program says */ 275 if (!(r & AUTH_ROOTOKAY) && pwd->pw_uid == 0) { 276 fprintf(stderr, "Sorry\n"); 277 syslog(LOG_AUTH|LOG_WARNING,"UNAPPROVED ROOT SU %s%s", user, ontty()); 278 exit(1); 279 } 280 } 281#else /* !LOGIN_CAP_AUTH */ 282#ifdef SKEY 283#ifdef WHEELSU 284 if (iswheelsu) { 285 pwd = getpwnam(username); 286 } 287#endif /* WHEELSU */ 288 p = skey_getpass("Password:", pwd, 1); 289 if (!(!strcmp(pwd->pw_passwd, skey_crypt(p, pwd->pw_passwd, pwd, 1)) 290#ifdef WHEELSU 291 || (iswheelsu && !strcmp(targetpass, crypt(p,targetpass))) 292#endif /* WHEELSU */ 293 )) { 294#else 295 p = getpass("Password:"); 296 if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) { 297#endif 298#ifdef KERBEROS 299 if (!use_kerberos || (use_kerberos && kerberos(username, user, pwd->pw_uid, p))) 300#endif 301 { 302 fprintf(stderr, "Sorry\n"); 303 syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s%s", username, user, ontty()); 304 exit(1); 305 } 306 } 307#ifdef WHEELSU 308 if (iswheelsu) { 309 pwd = getpwnam(user); 310 } 311#endif /* WHEELSU */ 312#endif /* LOGIN_CAP_AUTH */ 313 } 314 if (pwd->pw_expire && time(NULL) >= pwd->pw_expire) { 315 fprintf(stderr, "Sorry - account expired\n"); 316 syslog(LOG_AUTH|LOG_WARNING, 317 "BAD SU %s to %s%s", username, 318 user, ontty()); 319 exit(1); 320 } 321 } 322 323 if (asme) { 324 /* if asme and non-standard target shell, must be root */ 325 if (!chshell(pwd->pw_shell) && ruid) 326 errx(1, "permission denied (shell)."); 327 } else if (pwd->pw_shell && *pwd->pw_shell) { 328 shell = pwd->pw_shell; 329 iscsh = UNSET; 330 } else { 331 shell = _PATH_BSHELL; 332 iscsh = NO; 333 } 334 335 /* if we're forking a csh, we want to slightly muck the args */ 336 if (iscsh == UNSET) { 337 p = strrchr(shell, '/'); 338 if (p) 339 ++p; 340 else 341 p = shell; 342 if ((iscsh = strcmp(p, "csh") ? NO : YES) == NO) 343 iscsh = strcmp(p, "tcsh") ? NO : YES; 344 } 345 346 (void)setpriority(PRIO_PROCESS, 0, prio); 347 348#ifdef LOGIN_CAP 349 /* Set everything now except the environment & umask */ 350 setwhat = LOGIN_SETUSER|LOGIN_SETGROUP|LOGIN_SETRESOURCES|LOGIN_SETPRIORITY; 351 /* 352 * Don't touch resource/priority settings if -m has been 353 * used or -l hasn't, and we're not su'ing to root. 354 */ 355 if ((asme || !asthem) && pwd->pw_uid) 356 setwhat &= ~(LOGIN_SETPRIORITY|LOGIN_SETRESOURCES); 357 if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0) 358 err(1, "setusercontext"); 359#else 360 /* set permissions */ 361 if (setgid(pwd->pw_gid) < 0) 362 err(1, "setgid"); 363 if (initgroups(user, pwd->pw_gid)) 364 errx(1, "initgroups failed"); 365 if (setuid(pwd->pw_uid) < 0) 366 err(1, "setuid"); 367#endif 368 369 if (!asme) { 370 if (asthem) { 371 p = getenv("TERM"); 372#ifdef KERBEROS 373 k = getenv("KRBTKFILE"); 374#endif 375 if ((cleanenv = calloc(20, sizeof(char*))) == NULL) 376 errx(1, "calloc"); 377 cleanenv[0] = NULL; 378 environ = cleanenv; 379#ifdef LOGIN_CAP 380 /* set the su'd user's environment & umask */ 381 setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH|LOGIN_SETUMASK|LOGIN_SETENV); 382#else 383 (void)setenv("PATH", _PATH_DEFPATH, 1); 384#endif 385 if (p) 386 (void)setenv("TERM", p, 1); 387#ifdef KERBEROS 388 if (k) 389 (void)setenv("KRBTKFILE", k, 1); 390#endif 391 if (chdir(pwd->pw_dir) < 0) 392 errx(1, "no directory"); 393 } 394 if (asthem || pwd->pw_uid) 395 (void)setenv("USER", pwd->pw_name, 1); 396 (void)setenv("HOME", pwd->pw_dir, 1); 397 (void)setenv("SHELL", shell, 1); 398 } 399 if (iscsh == YES) { 400 if (fastlogin) 401 *np-- = "-f"; 402 if (asme) 403 *np-- = "-m"; 404 } 405 406 /* csh strips the first character... */ 407 *np = asthem ? "-su" : iscsh == YES ? "_su" : "su"; 408 409 if (ruid != 0) 410 syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s", 411 username, user, ontty()); 412 413 login_close(lc); 414 415 execv(shell, np); 416 err(1, "%s", shell); 417} 418 419static void 420usage() 421{ 422 (void)fprintf(stderr, "usage: su [%s] [login [args]]\n", ARGSTR); 423 exit(1); 424} 425 426int 427chshell(sh) 428 char *sh; 429{ 430 int r = 0; 431 char *cp; 432 433 setusershell(); 434 while (!r && (cp = getusershell()) != NULL) 435 r = strcmp(cp, sh) == 0; 436 endusershell(); 437 return r; 438} 439 440char * 441ontty() 442{ 443 char *p; 444 static char buf[MAXPATHLEN + 4]; 445 446 buf[0] = 0; 447 p = ttyname(STDERR_FILENO); 448 if (p) 449 snprintf(buf, sizeof(buf), " on %s", p); 450 return (buf); 451} 452 453#ifdef KERBEROS 454int 455kerberos(username, user, uid, pword) 456 char *username, *user; 457 int uid; 458 char *pword; 459{ 460 KTEXT_ST ticket; 461 AUTH_DAT authdata; 462 int kerno; 463 u_long faddr; 464 char lrealm[REALM_SZ], krbtkfile[MAXPATHLEN]; 465 char hostname[MAXHOSTNAMELEN], savehost[MAXHOSTNAMELEN]; 466 char *krb_get_phost(); 467 struct hostent *hp; 468 469 if (krb_get_lrealm(lrealm, 1) != KSUCCESS) 470 return (1); 471 (void)sprintf(krbtkfile, "%s_%s_%lu", TKT_ROOT, user, 472 (unsigned long)getuid()); 473 474 (void)setenv("KRBTKFILE", krbtkfile, 1); 475 (void)krb_set_tkt_string(krbtkfile); 476 /* 477 * Set real as well as effective ID to 0 for the moment, 478 * to make the kerberos library do the right thing. 479 */ 480 if (setuid(0) < 0) { 481 warn("setuid"); 482 return (1); 483 } 484 485 /* 486 * Little trick here -- if we are su'ing to root, 487 * we need to get a ticket for "xxx.root", where xxx represents 488 * the name of the person su'ing. Otherwise (non-root case), 489 * we need to get a ticket for "yyy.", where yyy represents 490 * the name of the person being su'd to, and the instance is null 491 * 492 * We should have a way to set the ticket lifetime, 493 * with a system default for root. 494 */ 495 kerno = krb_get_pw_in_tkt((uid == 0 ? username : user), 496 (uid == 0 ? "root" : ""), lrealm, 497 "krbtgt", lrealm, DEFAULT_TKT_LIFE, pword); 498 499 if (kerno != KSUCCESS) { 500 if (kerno == KDC_PR_UNKNOWN) { 501 warnx("kerberos: principal unknown: %s.%s@%s", 502 (uid == 0 ? username : user), 503 (uid == 0 ? "root" : ""), lrealm); 504 return (1); 505 } 506 warnx("kerberos: unable to su: %s", krb_err_txt[kerno]); 507 syslog(LOG_NOTICE|LOG_AUTH, 508 "BAD Kerberos SU: %s to %s%s: %s", 509 username, user, ontty(), krb_err_txt[kerno]); 510 return (1); 511 } 512 513 if (chown(krbtkfile, uid, -1) < 0) { 514 warn("chown"); 515 (void)unlink(krbtkfile); 516 return (1); 517 } 518 519 (void)setpriority(PRIO_PROCESS, 0, -2); 520 521 if (gethostname(hostname, sizeof(hostname)) == -1) { 522 warn("gethostname"); 523 dest_tkt(); 524 return (1); 525 } 526 527 (void)strncpy(savehost, krb_get_phost(hostname), sizeof(savehost)); 528 savehost[sizeof(savehost) - 1] = '\0'; 529 530 kerno = krb_mk_req(&ticket, "rcmd", savehost, lrealm, 33); 531 532 if (kerno == KDC_PR_UNKNOWN) { 533 warnx("Warning: TGT not verified."); 534 syslog(LOG_NOTICE|LOG_AUTH, 535 "%s to %s%s, TGT not verified (%s); %s.%s not registered?", 536 username, user, ontty(), krb_err_txt[kerno], 537 "rcmd", savehost); 538 } else if (kerno != KSUCCESS) { 539 warnx("Unable to use TGT: %s", krb_err_txt[kerno]); 540 syslog(LOG_NOTICE|LOG_AUTH, "failed su: %s to %s%s: %s", 541 username, user, ontty(), krb_err_txt[kerno]); 542 dest_tkt(); 543 return (1); 544 } else { 545 if (!(hp = gethostbyname(hostname))) { 546 warnx("can't get addr of %s", hostname); 547 dest_tkt(); 548 return (1); 549 } 550 memmove((char *)&faddr, (char *)hp->h_addr, sizeof(faddr)); 551 552 if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr, 553 &authdata, "")) != KSUCCESS) { 554 warnx("kerberos: unable to verify rcmd ticket: %s\n", 555 krb_err_txt[kerno]); 556 syslog(LOG_NOTICE|LOG_AUTH, 557 "failed su: %s to %s%s: %s", username, 558 user, ontty(), krb_err_txt[kerno]); 559 dest_tkt(); 560 return (1); 561 } 562 } 563 return (0); 564} 565 566int 567koktologin(name, toname) 568 char *name, *toname; 569{ 570 AUTH_DAT *kdata; 571 AUTH_DAT kdata_st; 572 char realm[REALM_SZ]; 573 574 if (krb_get_lrealm(realm, 1) != KSUCCESS) 575 return (1); 576 kdata = &kdata_st; 577 memset((char *)kdata, 0, sizeof(*kdata)); 578 (void)strncpy(kdata->pname, name, sizeof kdata->pname - 1); 579 (void)strncpy(kdata->pinst, 580 ((strcmp(toname, "root") == 0) ? "root" : ""), sizeof kdata->pinst - 1); 581 (void)strncpy(kdata->prealm, realm, sizeof kdata->prealm - 1); 582 return (kuserok(kdata, toname)); 583} 584#endif 585