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.15 1997/01/13 06:39:19 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 for (g = gr->gr_mem;; ++g) { 228 if (!*g) 229 errx(1, 230 "you are not in the correct group to su %s.", 231 user); 232 if (strcmp(username, *g) == 0) { 233 #ifdef WHEELSU 234 iswheelsu = 1; 235 #endif /* WHEELSU */ 236 break; 237 } 238 } 239 } 240 /* if target requires a password, verify it */ 241 if (*pwd->pw_passwd) { 242 #ifdef LOGIN_CAP_AUTH 243 /* 244 * This hands off authorisation to an authorisation program, 245 * depending on the styles available for the "auth-su", 246 * authorisation styles. 247 */ 248 if ((style = login_getstyle(lc, auth_method, "su")) == NULL) 249 errx(1, "auth method available for su.\n"); 250 if (authenticate(user, lc ? lc->lc_class : "default", style, "su") != 0) { 251 #ifdef WHEELSU 252 if (!iswheelsu || authenticate(username, lc ? lc->lc_class : "default", style, "su") != 0) { 253 #endif /* WHEELSU */ 254 { 255 fprintf(stderr, "Sorry\n"); 256 syslog(LOG_AUTH|LOG_WARNING,"BAD SU %s to %s%s", username, user, ontty()); 257 exit(1); 258 } 259 } 260 261 /* 262 * If authentication succeeds, run any approval 263 * program, if applicable for this class. 264 */ 265 approvep = login_getcapstr(lc, "approve", NULL, NULL); 266 if (approvep==NULL || auth_script(approvep, approvep, username, lc->lc_class, 0) == 0) { 267 int r = auth_scan(AUTH_OKAY); 268 /* See what the authorise program says */ 269 if (!(r & AUTH_ROOTOKAY) && pwd->pw_uid == 0) { 270 fprintf(stderr, "Sorry\n"); 271 syslog(LOG_AUTH|LOG_WARNING,"UNAPPROVED ROOT SU %s%s", user, ontty()); 272 exit(1); 273 } 274 } 275 #else /* !LOGIN_CAP_AUTH */ 276 #ifdef SKEY 277 #ifdef WHEELSU 278 if (iswheelsu) { 279 pwd = getpwnam(username); 280 } 281 #endif /* WHEELSU */ 282 p = skey_getpass("Password:", pwd, 1); 283 if (!(!strcmp(pwd->pw_passwd, skey_crypt(p, pwd->pw_passwd, pwd, 1)) 284 #ifdef WHEELSU 285 || (iswheelsu && !strcmp(targetpass, crypt(p,targetpass))) 286 #endif /* WHEELSU */ 287 )) { 288 #else 289 p = getpass("Password:"); 290 if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) { 291 #endif 292 #ifdef KERBEROS 293 if (!use_kerberos || (use_kerberos && kerberos(username, user, pwd->pw_uid, p))) 294 #endif 295 { 296 fprintf(stderr, "Sorry\n"); 297 syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s%s", username, user, ontty()); 298 exit(1); 299 } 300 } 301 #ifdef WHEELSU 302 if (iswheelsu) { 303 pwd = getpwnam(user); 304 } 305 #endif /* WHEELSU */ 306 #endif /* LOGIN_CAP_AUTH */ 307 } 308 if (pwd->pw_expire && time(NULL) >= pwd->pw_expire) { 309 fprintf(stderr, "Sorry - account expired\n"); 310 syslog(LOG_AUTH|LOG_WARNING, 311 "BAD SU %s to %s%s", username, 312 user, ontty()); 313 exit(1); 314 } 315 } 316 317 if (asme) { 318 /* if asme and non-standard target shell, must be root */ 319 if (!chshell(pwd->pw_shell) && ruid) 320 errx(1, "permission denied (shell)."); 321 } else if (pwd->pw_shell && *pwd->pw_shell) { 322 shell = pwd->pw_shell; 323 iscsh = UNSET; 324 } else { 325 shell = _PATH_BSHELL; 326 iscsh = NO; 327 } 328 329 /* if we're forking a csh, we want to slightly muck the args */ 330 if (iscsh == UNSET) { 331 p = strrchr(shell, '/'); 332 if (p) 333 ++p; 334 else 335 p = shell; 336 if ((iscsh = strcmp(p, "csh") ? NO : YES) == NO) 337 iscsh = strcmp(p, "tcsh") ? NO : YES; 338 } 339 340 (void)setpriority(PRIO_PROCESS, 0, prio); 341 342 #ifdef LOGIN_CAP 343 /* Set everything now except the environment & umask */ 344 setwhat = LOGIN_SETUSER|LOGIN_SETGROUP|LOGIN_SETRESOURCES|LOGIN_SETPRIORITY; 345 /* 346 * Don't touch resource/priority settings if -m has been 347 * used or -l hasn't, and we're not su'ing to root. 348 */ 349 if ((asme || !asthem) && pwd->pw_uid) 350 setwhat &= ~(LOGIN_SETPRIORITY|LOGIN_SETRESOURCES); 351 if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0) 352 err(1, "setusercontext"); 353 #else 354 /* set permissions */ 355 if (setgid(pwd->pw_gid) < 0) 356 err(1, "setgid"); 357 if (initgroups(user, pwd->pw_gid)) 358 errx(1, "initgroups failed"); 359 if (setuid(pwd->pw_uid) < 0) 360 err(1, "setuid"); 361 #endif 362 363 if (!asme) { 364 if (asthem) { 365 p = getenv("TERM"); 366 cleanenv[0] = NULL; 367 environ = cleanenv; 368 #ifdef LOGIN_CAP 369 /* set the su'd user's environment & umask */ 370 setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH|LOGIN_SETUMASK|LOGIN_SETENV); 371 #else 372 (void)setenv("PATH", _PATH_DEFPATH, 1); 373 #endif 374 if (p) 375 (void)setenv("TERM", p, 1); 376 if (chdir(pwd->pw_dir) < 0) 377 errx(1, "no directory"); 378 } 379 if (asthem || pwd->pw_uid) 380 (void)setenv("USER", pwd->pw_name, 1); 381 (void)setenv("HOME", pwd->pw_dir, 1); 382 (void)setenv("SHELL", shell, 1); 383 } 384 if (iscsh == YES) { 385 if (fastlogin) 386 *np-- = "-f"; 387 if (asme) 388 *np-- = "-m"; 389 } 390 391 /* csh strips the first character... */ 392 *np = asthem ? "-su" : iscsh == YES ? "_su" : "su"; 393 394 if (ruid != 0) 395 syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s", 396 username, user, ontty()); 397 398 login_close(lc); 399 400 execv(shell, np); 401 err(1, "%s", shell); 402 } 403 404 int 405 chshell(sh) 406 char *sh; 407 { 408 int r = 0; 409 char *cp; 410 411 setusershell(); 412 while (!r && (cp = getusershell()) != NULL) 413 r = strcmp(cp, sh) == 0; 414 endusershell(); 415 return r; 416 } 417 418 char * 419 ontty() 420 { 421 char *p; 422 static char buf[MAXPATHLEN + 4]; 423 424 buf[0] = 0; 425 p = ttyname(STDERR_FILENO); 426 if (p) 427 snprintf(buf, sizeof(buf), " on %s", p); 428 return (buf); 429 } 430 431 #ifdef KERBEROS 432 int 433 kerberos(username, user, uid, pword) 434 char *username, *user; 435 int uid; 436 char *pword; 437 { 438 extern char *krb_err_txt[]; 439 KTEXT_ST ticket; 440 AUTH_DAT authdata; 441 int kerno; 442 u_long faddr; 443 struct sockaddr_in local_addr; 444 char lrealm[REALM_SZ], krbtkfile[MAXPATHLEN]; 445 char hostname[MAXHOSTNAMELEN], savehost[MAXHOSTNAMELEN]; 446 char *krb_get_phost(); 447 448 if (krb_get_lrealm(lrealm, 1) != KSUCCESS) 449 return (1); 450 (void)sprintf(krbtkfile, "%s_%s_%lu", TKT_ROOT, user, 451 (unsigned long)getuid()); 452 453 (void)setenv("KRBTKFILE", krbtkfile, 1); 454 (void)krb_set_tkt_string(krbtkfile); 455 /* 456 * Set real as well as effective ID to 0 for the moment, 457 * to make the kerberos library do the right thing. 458 */ 459 if (setuid(0) < 0) { 460 warn("setuid"); 461 return (1); 462 } 463 464 /* 465 * Little trick here -- if we are su'ing to root, 466 * we need to get a ticket for "xxx.root", where xxx represents 467 * the name of the person su'ing. Otherwise (non-root case), 468 * we need to get a ticket for "yyy.", where yyy represents 469 * the name of the person being su'd to, and the instance is null 470 * 471 * We should have a way to set the ticket lifetime, 472 * with a system default for root. 473 */ 474 kerno = krb_get_pw_in_tkt((uid == 0 ? username : user), 475 (uid == 0 ? "root" : ""), lrealm, 476 "krbtgt", lrealm, DEFAULT_TKT_LIFE, pword); 477 478 if (kerno != KSUCCESS) { 479 if (kerno == KDC_PR_UNKNOWN) { 480 warnx("kerberos: principal unknown: %s.%s@%s", 481 (uid == 0 ? username : user), 482 (uid == 0 ? "root" : ""), lrealm); 483 return (1); 484 } 485 warnx("kerberos: unable to su: %s", krb_err_txt[kerno]); 486 syslog(LOG_NOTICE|LOG_AUTH, 487 "BAD Kerberos SU: %s to %s%s: %s", 488 username, user, ontty(), krb_err_txt[kerno]); 489 return (1); 490 } 491 492 if (chown(krbtkfile, uid, -1) < 0) { 493 warn("chown"); 494 (void)unlink(krbtkfile); 495 return (1); 496 } 497 498 (void)setpriority(PRIO_PROCESS, 0, -2); 499 500 if (gethostname(hostname, sizeof(hostname)) == -1) { 501 warn("gethostname"); 502 dest_tkt(); 503 return (1); 504 } 505 506 (void)strncpy(savehost, krb_get_phost(hostname), sizeof(savehost)); 507 savehost[sizeof(savehost) - 1] = '\0'; 508 509 kerno = krb_mk_req(&ticket, "rcmd", savehost, lrealm, 33); 510 511 if (kerno == KDC_PR_UNKNOWN) { 512 warnx("Warning: TGT not verified."); 513 syslog(LOG_NOTICE|LOG_AUTH, 514 "%s to %s%s, TGT not verified (%s); %s.%s not registered?", 515 username, user, ontty(), krb_err_txt[kerno], 516 "rcmd", savehost); 517 } else if (kerno != KSUCCESS) { 518 warnx("Unable to use TGT: %s", krb_err_txt[kerno]); 519 syslog(LOG_NOTICE|LOG_AUTH, "failed su: %s to %s%s: %s", 520 username, user, ontty(), krb_err_txt[kerno]); 521 dest_tkt(); 522 return (1); 523 } else { 524 if ((kerno = krb_get_local_addr(&local_addr)) != KSUCCESS) { 525 warnx("Unable to get our local address: %s", 526 krb_err_txt[kerno]); 527 dest_tkt(); 528 return (1); 529 } 530 faddr = local_addr.sin_addr.s_addr; 531 if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr, 532 &authdata, "")) != KSUCCESS) { 533 warnx("kerberos: unable to verify rcmd ticket: %s\n", 534 krb_err_txt[kerno]); 535 syslog(LOG_NOTICE|LOG_AUTH, 536 "failed su: %s to %s%s: %s", username, 537 user, ontty(), krb_err_txt[kerno]); 538 dest_tkt(); 539 return (1); 540 } 541 } 542 return (0); 543 } 544 545 int 546 koktologin(name, toname) 547 char *name, *toname; 548 { 549 AUTH_DAT *kdata; 550 AUTH_DAT kdata_st; 551 char realm[REALM_SZ]; 552 553 if (krb_get_lrealm(realm, 1) != KSUCCESS) 554 return (1); 555 kdata = &kdata_st; 556 memset((char *)kdata, 0, sizeof(*kdata)); 557 (void)strncpy(kdata->pname, name, sizeof kdata->pname - 1); 558 (void)strncpy(kdata->pinst, 559 ((strcmp(toname, "root") == 0) ? "root" : ""), sizeof kdata->pinst - 1); 560 (void)strncpy(kdata->prealm, realm, sizeof kdata->prealm - 1); 561 return (kuserok(kdata, toname)); 562 } 563 #endif 564