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