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 35 static 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 42 static char sccsid[] = "@(#)su.c 8.3 (Berkeley) 4/2/94"; 43 #endif 44 static const char rcsid[] = 45 "$Id: su.c,v 1.26 1998/05/25 03:34:52 steve Exp $"; 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 #ifdef LOGIN_CAP 81 #define ARGSTR "-Kflmc:" 82 #else 83 #define ARGSTR "-Kflm" 84 #endif 85 86 static int kerberos(char *username, char *user, int uid, char *pword); 87 static int koktologin(char *name, char *toname); 88 89 int use_kerberos = 1; 90 #else /* !KERBEROS */ 91 #ifdef LOGIN_CAP 92 #define ARGSTR "-flmc:" 93 #else 94 #define ARGSTR "-flm" 95 #endif 96 #endif /* KERBEROS */ 97 98 char *ontty __P((void)); 99 int chshell __P((char *)); 100 static void usage __P((void)); 101 102 int 103 main(argc, argv) 104 int argc; 105 char **argv; 106 { 107 extern char **environ; 108 struct passwd *pwd; 109 #ifdef WHEELSU 110 char *targetpass; 111 int iswheelsu; 112 #endif /* WHEELSU */ 113 char *p, **g, *user, *shell=NULL, *username, **cleanenv, **nargv, **np; 114 struct group *gr; 115 uid_t ruid; 116 gid_t gid; 117 int asme, ch, asthem, fastlogin, prio, i; 118 enum { UNSET, YES, NO } iscsh = UNSET; 119 #ifdef LOGIN_CAP 120 login_cap_t *lc; 121 char *class=NULL; 122 int setwhat; 123 #ifdef LOGIN_CAP_AUTH 124 char *style, *approvep, *auth_method = NULL; 125 #endif 126 #endif 127 #ifdef KERBEROS 128 char *k; 129 #endif 130 char shellbuf[MAXPATHLEN]; 131 132 #ifdef WHEELSU 133 iswheelsu = 134 #endif /* WHEELSU */ 135 asme = asthem = fastlogin = 0; 136 user = "root"; 137 while(optind < argc) 138 if((ch = getopt(argc, argv, ARGSTR)) != -1) 139 switch((char)ch) { 140 #ifdef KERBEROS 141 case 'K': 142 use_kerberos = 0; 143 break; 144 #endif 145 case 'f': 146 fastlogin = 1; 147 break; 148 case '-': 149 case 'l': 150 asme = 0; 151 asthem = 1; 152 break; 153 case 'm': 154 asme = 1; 155 asthem = 0; 156 break; 157 #ifdef LOGIN_CAP 158 case 'c': 159 class = optarg; 160 break; 161 #endif 162 case '?': 163 default: 164 usage(); 165 } 166 else 167 { 168 user = argv[optind++]; 169 break; 170 } 171 172 if (strlen(user) > MAXLOGNAME - 1) { 173 (void)fprintf(stderr, "su: username too long.\n"); 174 exit(1); 175 } 176 177 if (user == NULL) 178 usage(); 179 180 if ((nargv = malloc (sizeof (char *) * (argc + 4))) == NULL) { 181 errx(1, "malloc failure"); 182 } 183 184 nargv[argc + 3] = NULL; 185 for (i = argc; i >= optind; i--) 186 nargv[i + 3] = argv[i]; 187 np = &nargv[i + 3]; 188 189 argv += optind; 190 191 errno = 0; 192 prio = getpriority(PRIO_PROCESS, 0); 193 if (errno) 194 prio = 0; 195 (void)setpriority(PRIO_PROCESS, 0, -2); 196 openlog("su", LOG_CONS, 0); 197 198 /* get current login name and shell */ 199 ruid = getuid(); 200 username = getlogin(); 201 if (username == NULL || (pwd = getpwnam(username)) == NULL || 202 pwd->pw_uid != ruid) 203 pwd = getpwuid(ruid); 204 if (pwd == NULL) 205 errx(1, "who are you?"); 206 username = strdup(pwd->pw_name); 207 gid = pwd->pw_gid; 208 if (username == NULL) 209 err(1, NULL); 210 if (asme) { 211 if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') { 212 /* copy: pwd memory is recycled */ 213 shell = strncpy(shellbuf, pwd->pw_shell, sizeof shellbuf); 214 shellbuf[sizeof shellbuf - 1] = '\0'; 215 } else { 216 shell = _PATH_BSHELL; 217 iscsh = NO; 218 } 219 } 220 221 #ifdef LOGIN_CAP_AUTH 222 if (auth_method = strchr(user, ':')) { 223 *auth_method = '\0'; 224 auth_method++; 225 if (*auth_method == '\0') 226 auth_method = NULL; 227 } 228 #endif /* !LOGIN_CAP_AUTH */ 229 230 /* get target login information, default to root */ 231 if ((pwd = getpwnam(user)) == NULL) { 232 errx(1, "unknown login: %s", user); 233 } 234 #ifdef LOGIN_CAP 235 if (class==NULL) { 236 lc = login_getpwclass(pwd); 237 } else { 238 if (ruid) 239 errx(1, "only root may use -c"); 240 lc = login_getclass(class); 241 if (lc == NULL) 242 errx(1, "unknown class: %s", class); 243 } 244 #endif 245 246 #ifdef WHEELSU 247 targetpass = strdup(pwd->pw_passwd); 248 #endif /* WHEELSU */ 249 250 if (ruid) { 251 #ifdef KERBEROS 252 if (use_kerberos && koktologin(username, user) 253 && !pwd->pw_uid) { 254 warnx("kerberos: not in %s's ACL.", user); 255 use_kerberos = 0; 256 } 257 #endif 258 { 259 /* 260 * Only allow those with pw_gid==0 or those listed in 261 * group zero to su to root. If group zero entry is 262 * missing or empty, then allow anyone to su to root. 263 * iswheelsu will only be set if the user is EXPLICITLY 264 * listed in group zero. 265 */ 266 if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0)) && 267 gr->gr_mem && *(gr->gr_mem)) 268 for (g = gr->gr_mem;; ++g) { 269 if (!*g) 270 if (gid == 0) 271 break; 272 else 273 errx(1, "you are not in the correct group to su %s.", user); 274 if (strcmp(username, *g) == 0) { 275 #ifdef WHEELSU 276 iswheelsu = 1; 277 #endif /* WHEELSU */ 278 break; 279 } 280 } 281 } 282 /* if target requires a password, verify it */ 283 if (*pwd->pw_passwd) { 284 #ifdef LOGIN_CAP_AUTH 285 /* 286 * This hands off authorisation to an authorisation program, 287 * depending on the styles available for the "auth-su", 288 * authorisation styles. 289 */ 290 if ((style = login_getstyle(lc, auth_method, "su")) == NULL) 291 errx(1, "auth method available for su.\n"); 292 if (authenticate(user, lc ? lc->lc_class : "default", style, "su") != 0) { 293 #ifdef WHEELSU 294 if (!iswheelsu || authenticate(username, lc ? lc->lc_class : "default", style, "su") != 0) { 295 #endif /* WHEELSU */ 296 { 297 fprintf(stderr, "Sorry\n"); 298 syslog(LOG_AUTH|LOG_WARNING,"BAD SU %s to %s%s", username, user, ontty()); 299 exit(1); 300 } 301 } 302 303 /* 304 * If authentication succeeds, run any approval 305 * program, if applicable for this class. 306 */ 307 approvep = login_getcapstr(lc, "approve", NULL, NULL); 308 if (approvep==NULL || auth_script(approvep, approvep, username, lc->lc_class, 0) == 0) { 309 int r = auth_scan(AUTH_OKAY); 310 /* See what the authorise program says */ 311 if (!(r & AUTH_ROOTOKAY) && pwd->pw_uid == 0) { 312 fprintf(stderr, "Sorry\n"); 313 syslog(LOG_AUTH|LOG_WARNING,"UNAPPROVED ROOT SU %s%s", user, ontty()); 314 exit(1); 315 } 316 } 317 #else /* !LOGIN_CAP_AUTH */ 318 #ifdef SKEY 319 #ifdef WHEELSU 320 if (iswheelsu) { 321 pwd = getpwnam(username); 322 } 323 #endif /* WHEELSU */ 324 p = skey_getpass("Password:", pwd, 1); 325 if (!(!strcmp(pwd->pw_passwd, skey_crypt(p, pwd->pw_passwd, pwd, 1)) 326 #ifdef WHEELSU 327 || (iswheelsu && !strcmp(targetpass, crypt(p,targetpass))) 328 #endif /* WHEELSU */ 329 )) { 330 #else 331 p = getpass("Password:"); 332 if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) { 333 #endif 334 #ifdef KERBEROS 335 if (!use_kerberos || (use_kerberos && kerberos(username, user, pwd->pw_uid, p))) 336 #endif 337 { 338 fprintf(stderr, "Sorry\n"); 339 syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s%s", username, user, ontty()); 340 exit(1); 341 } 342 } 343 #ifdef WHEELSU 344 if (iswheelsu) { 345 pwd = getpwnam(user); 346 } 347 #endif /* WHEELSU */ 348 #endif /* LOGIN_CAP_AUTH */ 349 } 350 if (pwd->pw_expire && time(NULL) >= pwd->pw_expire) { 351 fprintf(stderr, "Sorry - account expired\n"); 352 syslog(LOG_AUTH|LOG_WARNING, 353 "BAD SU %s to %s%s", username, 354 user, ontty()); 355 exit(1); 356 } 357 } 358 359 if (asme) { 360 /* if asme and non-standard target shell, must be root */ 361 if (!chshell(pwd->pw_shell) && ruid) 362 errx(1, "permission denied (shell)."); 363 } else if (pwd->pw_shell && *pwd->pw_shell) { 364 shell = pwd->pw_shell; 365 iscsh = UNSET; 366 } else { 367 shell = _PATH_BSHELL; 368 iscsh = NO; 369 } 370 371 /* if we're forking a csh, we want to slightly muck the args */ 372 if (iscsh == UNSET) { 373 p = strrchr(shell, '/'); 374 if (p) 375 ++p; 376 else 377 p = shell; 378 if ((iscsh = strcmp(p, "csh") ? NO : YES) == NO) 379 iscsh = strcmp(p, "tcsh") ? NO : YES; 380 } 381 382 (void)setpriority(PRIO_PROCESS, 0, prio); 383 384 #ifdef LOGIN_CAP 385 /* Set everything now except the environment & umask */ 386 setwhat = LOGIN_SETUSER|LOGIN_SETGROUP|LOGIN_SETRESOURCES|LOGIN_SETPRIORITY; 387 /* 388 * Don't touch resource/priority settings if -m has been 389 * used or -l and -c hasn't, and we're not su'ing to root. 390 */ 391 if ((asme || (!asthem && class == NULL)) && pwd->pw_uid) 392 setwhat &= ~(LOGIN_SETPRIORITY|LOGIN_SETRESOURCES); 393 if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0) 394 err(1, "setusercontext"); 395 #else 396 /* set permissions */ 397 if (setgid(pwd->pw_gid) < 0) 398 err(1, "setgid"); 399 if (initgroups(user, pwd->pw_gid)) 400 errx(1, "initgroups failed"); 401 if (setuid(pwd->pw_uid) < 0) 402 err(1, "setuid"); 403 #endif 404 405 if (!asme) { 406 if (asthem) { 407 p = getenv("TERM"); 408 #ifdef KERBEROS 409 k = getenv("KRBTKFILE"); 410 #endif 411 if ((cleanenv = calloc(20, sizeof(char*))) == NULL) 412 errx(1, "calloc"); 413 cleanenv[0] = NULL; 414 environ = cleanenv; 415 #ifdef LOGIN_CAP 416 /* set the su'd user's environment & umask */ 417 setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH|LOGIN_SETUMASK|LOGIN_SETENV); 418 #else 419 (void)setenv("PATH", _PATH_DEFPATH, 1); 420 #endif 421 if (p) 422 (void)setenv("TERM", p, 1); 423 #ifdef KERBEROS 424 if (k) 425 (void)setenv("KRBTKFILE", k, 1); 426 #endif 427 if (chdir(pwd->pw_dir) < 0) 428 errx(1, "no directory"); 429 } 430 if (asthem || pwd->pw_uid) 431 (void)setenv("USER", pwd->pw_name, 1); 432 (void)setenv("HOME", pwd->pw_dir, 1); 433 (void)setenv("SHELL", shell, 1); 434 } 435 if (iscsh == YES) { 436 if (fastlogin) 437 *np-- = "-f"; 438 if (asme) 439 *np-- = "-m"; 440 } 441 442 /* csh strips the first character... */ 443 *np = asthem ? "-su" : iscsh == YES ? "_su" : "su"; 444 445 if (ruid != 0) 446 syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s", 447 username, user, ontty()); 448 449 login_close(lc); 450 451 execv(shell, np); 452 err(1, "%s", shell); 453 } 454 455 static void 456 usage() 457 { 458 (void)fprintf(stderr, "usage: su [%s] [login [args]]\n", ARGSTR); 459 exit(1); 460 } 461 462 int 463 chshell(sh) 464 char *sh; 465 { 466 int r = 0; 467 char *cp; 468 469 setusershell(); 470 while (!r && (cp = getusershell()) != NULL) 471 r = strcmp(cp, sh) == 0; 472 endusershell(); 473 return r; 474 } 475 476 char * 477 ontty() 478 { 479 char *p; 480 static char buf[MAXPATHLEN + 4]; 481 482 buf[0] = 0; 483 p = ttyname(STDERR_FILENO); 484 if (p) 485 snprintf(buf, sizeof(buf), " on %s", p); 486 return (buf); 487 } 488 489 #ifdef KERBEROS 490 int 491 kerberos(username, user, uid, pword) 492 char *username, *user; 493 int uid; 494 char *pword; 495 { 496 KTEXT_ST ticket; 497 AUTH_DAT authdata; 498 int kerno; 499 u_long faddr; 500 char lrealm[REALM_SZ], krbtkfile[MAXPATHLEN]; 501 char hostname[MAXHOSTNAMELEN], savehost[MAXHOSTNAMELEN]; 502 char *krb_get_phost(); 503 struct hostent *hp; 504 505 if (krb_get_lrealm(lrealm, 1) != KSUCCESS) 506 return (1); 507 (void)sprintf(krbtkfile, "%s_%s_%lu", TKT_ROOT, user, 508 (unsigned long)getuid()); 509 510 (void)setenv("KRBTKFILE", krbtkfile, 1); 511 (void)krb_set_tkt_string(krbtkfile); 512 /* 513 * Set real as well as effective ID to 0 for the moment, 514 * to make the kerberos library do the right thing. 515 */ 516 if (setuid(0) < 0) { 517 warn("setuid"); 518 return (1); 519 } 520 521 /* 522 * Little trick here -- if we are su'ing to root, 523 * we need to get a ticket for "xxx.root", where xxx represents 524 * the name of the person su'ing. Otherwise (non-root case), 525 * we need to get a ticket for "yyy.", where yyy represents 526 * the name of the person being su'd to, and the instance is null 527 * 528 * We should have a way to set the ticket lifetime, 529 * with a system default for root. 530 */ 531 kerno = krb_get_pw_in_tkt((uid == 0 ? username : user), 532 (uid == 0 ? "root" : ""), lrealm, 533 "krbtgt", lrealm, DEFAULT_TKT_LIFE, pword); 534 535 if (kerno != KSUCCESS) { 536 if (kerno == KDC_PR_UNKNOWN) { 537 warnx("kerberos: principal unknown: %s.%s@%s", 538 (uid == 0 ? username : user), 539 (uid == 0 ? "root" : ""), lrealm); 540 return (1); 541 } 542 warnx("kerberos: unable to su: %s", krb_err_txt[kerno]); 543 syslog(LOG_NOTICE|LOG_AUTH, 544 "BAD Kerberos SU: %s to %s%s: %s", 545 username, user, ontty(), krb_err_txt[kerno]); 546 return (1); 547 } 548 549 if (chown(krbtkfile, uid, -1) < 0) { 550 warn("chown"); 551 (void)unlink(krbtkfile); 552 return (1); 553 } 554 555 (void)setpriority(PRIO_PROCESS, 0, -2); 556 557 if (gethostname(hostname, sizeof(hostname)) == -1) { 558 warn("gethostname"); 559 dest_tkt(); 560 return (1); 561 } 562 563 (void)strncpy(savehost, krb_get_phost(hostname), sizeof(savehost)); 564 savehost[sizeof(savehost) - 1] = '\0'; 565 566 kerno = krb_mk_req(&ticket, "rcmd", savehost, lrealm, 33); 567 568 if (kerno == KDC_PR_UNKNOWN) { 569 warnx("Warning: TGT not verified."); 570 syslog(LOG_NOTICE|LOG_AUTH, 571 "%s to %s%s, TGT not verified (%s); %s.%s not registered?", 572 username, user, ontty(), krb_err_txt[kerno], 573 "rcmd", savehost); 574 } else if (kerno != KSUCCESS) { 575 warnx("Unable to use TGT: %s", krb_err_txt[kerno]); 576 syslog(LOG_NOTICE|LOG_AUTH, "failed su: %s to %s%s: %s", 577 username, user, ontty(), krb_err_txt[kerno]); 578 dest_tkt(); 579 return (1); 580 } else { 581 if (!(hp = gethostbyname(hostname))) { 582 warnx("can't get addr of %s", hostname); 583 dest_tkt(); 584 return (1); 585 } 586 memmove((char *)&faddr, (char *)hp->h_addr, sizeof(faddr)); 587 588 if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr, 589 &authdata, "")) != KSUCCESS) { 590 warnx("kerberos: unable to verify rcmd ticket: %s\n", 591 krb_err_txt[kerno]); 592 syslog(LOG_NOTICE|LOG_AUTH, 593 "failed su: %s to %s%s: %s", username, 594 user, ontty(), krb_err_txt[kerno]); 595 dest_tkt(); 596 return (1); 597 } 598 } 599 return (0); 600 } 601 602 int 603 koktologin(name, toname) 604 char *name, *toname; 605 { 606 AUTH_DAT *kdata; 607 AUTH_DAT kdata_st; 608 char realm[REALM_SZ]; 609 610 if (krb_get_lrealm(realm, 1) != KSUCCESS) 611 return (1); 612 kdata = &kdata_st; 613 memset((char *)kdata, 0, sizeof(*kdata)); 614 (void)strncpy(kdata->pname, name, sizeof kdata->pname - 1); 615 (void)strncpy(kdata->pinst, 616 ((strcmp(toname, "root") == 0) ? "root" : ""), sizeof kdata->pinst - 1); 617 (void)strncpy(kdata->prealm, realm, sizeof kdata->prealm - 1); 618 return (kuserok(kdata, toname)); 619 } 620 #endif 621