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