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 /* Set everything now except the environment & umask */ 339 setwhat = LOGIN_SETUSER|LOGIN_SETGROUP|LOGIN_SETRESOURCES|LOGIN_SETPRIORITY; 340 /* 341 * Don't touch resource/priority settings if -m has been 342 * used or -l and -c hasn't, and we're not su'ing to root. 343 */ 344 if ((asme || (!asthem && class == NULL)) && pwd->pw_uid) 345 setwhat &= ~(LOGIN_SETPRIORITY|LOGIN_SETRESOURCES); 346 if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0) 347 err(1, "setusercontext"); 348 #else 349 /* set permissions */ 350 if (setgid(pwd->pw_gid) < 0) 351 err(1, "setgid"); 352 if (initgroups(user, pwd->pw_gid)) 353 errx(1, "initgroups failed"); 354 if (setuid(pwd->pw_uid) < 0) 355 err(1, "setuid"); 356 #endif 357 358 if (!asme) { 359 if (asthem) { 360 p = getenv("TERM"); 361 #ifdef KERBEROS 362 k = getenv("KRBTKFILE"); 363 #endif 364 if ((cleanenv = calloc(20, sizeof(char*))) == NULL) 365 errx(1, "calloc"); 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 #ifdef KERBEROS 377 if (k) 378 (void)setenv("KRBTKFILE", k, 1); 379 #endif 380 if (chdir(pwd->pw_dir) < 0) 381 errx(1, "no directory"); 382 } 383 if (asthem || pwd->pw_uid) 384 (void)setenv("USER", pwd->pw_name, 1); 385 (void)setenv("HOME", pwd->pw_dir, 1); 386 (void)setenv("SHELL", shell, 1); 387 } 388 if (iscsh == YES) { 389 if (fastlogin) 390 *np-- = "-f"; 391 if (asme) 392 *np-- = "-m"; 393 } 394 395 /* csh strips the first character... */ 396 *np = asthem ? "-su" : iscsh == YES ? "_su" : "su"; 397 398 if (ruid != 0) 399 syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s", 400 username, user, ontty()); 401 402 login_close(lc); 403 404 execv(shell, np); 405 err(1, "%s", shell); 406 } 407 408 static void 409 usage() 410 { 411 (void)fprintf(stderr, "usage: su [%s] [login [args]]\n", ARGSTR); 412 exit(1); 413 } 414 415 int 416 chshell(sh) 417 char *sh; 418 { 419 int r = 0; 420 char *cp; 421 422 setusershell(); 423 while (!r && (cp = getusershell()) != NULL) 424 r = strcmp(cp, sh) == 0; 425 endusershell(); 426 return r; 427 } 428 429 char * 430 ontty() 431 { 432 char *p; 433 static char buf[MAXPATHLEN + 4]; 434 435 buf[0] = 0; 436 p = ttyname(STDERR_FILENO); 437 if (p) 438 snprintf(buf, sizeof(buf), " on %s", p); 439 return (buf); 440 } 441 442 #ifdef KERBEROS 443 int 444 kerberos(username, user, uid, pword) 445 char *username, *user; 446 int uid; 447 char *pword; 448 { 449 KTEXT_ST ticket; 450 AUTH_DAT authdata; 451 int kerno; 452 u_long faddr; 453 char lrealm[REALM_SZ], krbtkfile[MAXPATHLEN]; 454 char hostname[MAXHOSTNAMELEN], savehost[MAXHOSTNAMELEN]; 455 char *krb_get_phost(); 456 struct hostent *hp; 457 458 if (krb_get_lrealm(lrealm, 1) != KSUCCESS) 459 return (1); 460 (void)sprintf(krbtkfile, "%s_%s_%lu", TKT_ROOT, user, 461 (unsigned long)getuid()); 462 463 (void)setenv("KRBTKFILE", krbtkfile, 1); 464 (void)krb_set_tkt_string(krbtkfile); 465 /* 466 * Set real as well as effective ID to 0 for the moment, 467 * to make the kerberos library do the right thing. 468 */ 469 if (setuid(0) < 0) { 470 warn("setuid"); 471 return (1); 472 } 473 474 /* 475 * Little trick here -- if we are su'ing to root, 476 * we need to get a ticket for "xxx.root", where xxx represents 477 * the name of the person su'ing. Otherwise (non-root case), 478 * we need to get a ticket for "yyy.", where yyy represents 479 * the name of the person being su'd to, and the instance is null 480 * 481 * We should have a way to set the ticket lifetime, 482 * with a system default for root. 483 */ 484 kerno = krb_get_pw_in_tkt((uid == 0 ? username : user), 485 (uid == 0 ? "root" : ""), lrealm, 486 "krbtgt", lrealm, DEFAULT_TKT_LIFE, pword); 487 488 if (kerno != KSUCCESS) { 489 if (kerno == KDC_PR_UNKNOWN) { 490 warnx("kerberos: principal unknown: %s.%s@%s", 491 (uid == 0 ? username : user), 492 (uid == 0 ? "root" : ""), lrealm); 493 return (1); 494 } 495 warnx("kerberos: unable to su: %s", krb_err_txt[kerno]); 496 syslog(LOG_NOTICE|LOG_AUTH, 497 "BAD Kerberos SU: %s to %s%s: %s", 498 username, user, ontty(), krb_err_txt[kerno]); 499 return (1); 500 } 501 502 if (chown(krbtkfile, uid, -1) < 0) { 503 warn("chown"); 504 (void)unlink(krbtkfile); 505 return (1); 506 } 507 508 (void)setpriority(PRIO_PROCESS, 0, -2); 509 510 if (gethostname(hostname, sizeof(hostname)) == -1) { 511 warn("gethostname"); 512 dest_tkt(); 513 return (1); 514 } 515 516 (void)strncpy(savehost, krb_get_phost(hostname), sizeof(savehost)); 517 savehost[sizeof(savehost) - 1] = '\0'; 518 519 kerno = krb_mk_req(&ticket, "rcmd", savehost, lrealm, 33); 520 521 if (kerno == KDC_PR_UNKNOWN) { 522 warnx("Warning: TGT not verified."); 523 syslog(LOG_NOTICE|LOG_AUTH, 524 "%s to %s%s, TGT not verified (%s); %s.%s not registered?", 525 username, user, ontty(), krb_err_txt[kerno], 526 "rcmd", savehost); 527 } else if (kerno != KSUCCESS) { 528 warnx("Unable to use TGT: %s", krb_err_txt[kerno]); 529 syslog(LOG_NOTICE|LOG_AUTH, "failed su: %s to %s%s: %s", 530 username, user, ontty(), krb_err_txt[kerno]); 531 dest_tkt(); 532 return (1); 533 } else { 534 if (!(hp = gethostbyname(hostname))) { 535 warnx("can't get addr of %s", hostname); 536 dest_tkt(); 537 return (1); 538 } 539 memmove((char *)&faddr, (char *)hp->h_addr, sizeof(faddr)); 540 541 if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr, 542 &authdata, "")) != KSUCCESS) { 543 warnx("kerberos: unable to verify rcmd ticket: %s\n", 544 krb_err_txt[kerno]); 545 syslog(LOG_NOTICE|LOG_AUTH, 546 "failed su: %s to %s%s: %s", username, 547 user, ontty(), krb_err_txt[kerno]); 548 dest_tkt(); 549 return (1); 550 } 551 } 552 return (0); 553 } 554 555 int 556 koktologin(name, toname) 557 char *name, *toname; 558 { 559 AUTH_DAT *kdata; 560 AUTH_DAT kdata_st; 561 char realm[REALM_SZ]; 562 563 if (krb_get_lrealm(realm, 1) != KSUCCESS) 564 return (1); 565 kdata = &kdata_st; 566 memset((char *)kdata, 0, sizeof(*kdata)); 567 (void)strncpy(kdata->pname, name, sizeof kdata->pname - 1); 568 (void)strncpy(kdata->pinst, 569 ((strcmp(toname, "root") == 0) ? "root" : ""), sizeof kdata->pinst - 1); 570 (void)strncpy(kdata->prealm, realm, sizeof kdata->prealm - 1); 571 return (kuserok(kdata, toname)); 572 } 573 #endif 574