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