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