su.c revision 28612
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 35static 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 42static char sccsid[] = "@(#)su.c 8.3 (Berkeley) 4/2/94"; 43#endif 44static const char rcsid[] = 45 "$Id: su.c,v 1.21 1997/08/12 06:45:43 charnier 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 82static int kerberos(char *username, char *user, int uid, char *pword); 83static int koktologin(char *name, char *toname); 84 85int use_kerberos = 1; 86#else /* !KERBEROS */ 87#define ARGSTR "-flm" 88#endif /* KERBEROS */ 89 90char *ontty __P((void)); 91int chshell __P((char *)); 92static void usage __P((void)); 93 94int 95main(argc, argv) 96 int argc; 97 char **argv; 98{ 99 extern char **environ; 100 struct passwd *pwd; 101#ifdef WHEELSU 102 char *targetpass; 103 int iswheelsu; 104#endif /* WHEELSU */ 105 char *p, **g, *user, *shell=NULL, *username, *cleanenv[20], **nargv, **np; 106 struct group *gr; 107 uid_t ruid; 108 int asme, ch, asthem, fastlogin, prio, i; 109 enum { UNSET, YES, NO } iscsh = UNSET; 110#ifdef LOGIN_CAP 111 login_cap_t *lc; 112 int setwhat; 113#ifdef LOGIN_CAP_AUTH 114 char *style, *approvep, *auth_method = NULL; 115#endif 116#endif 117 char shellbuf[MAXPATHLEN]; 118 119#ifdef WHEELSU 120 iswheelsu = 121#endif /* WHEELSU */ 122 asme = asthem = fastlogin = 0; 123 user = "root"; 124 while(optind < argc) 125 if((ch = getopt(argc, argv, ARGSTR)) != -1) 126 switch((char)ch) { 127#ifdef KERBEROS 128 case 'K': 129 use_kerberos = 0; 130 break; 131#endif 132 case 'f': 133 fastlogin = 1; 134 break; 135 case '-': 136 case 'l': 137 asme = 0; 138 asthem = 1; 139 break; 140 case 'm': 141 asme = 1; 142 asthem = 0; 143 break; 144 case '?': 145 default: 146 usage(); 147 } 148 else 149 { 150 user = argv[optind++]; 151 break; 152 } 153 154 if (user == NULL) 155 usage(); 156 157 if ((nargv = malloc (sizeof (char *) * (argc + 4))) == NULL) { 158 errx(1, "malloc failure"); 159 } 160 161 nargv[argc + 3] = NULL; 162 for (i = argc; i >= optind; i--) 163 nargv[i + 3] = argv[i]; 164 np = &nargv[i + 3]; 165 166 argv += optind; 167 168 errno = 0; 169 prio = getpriority(PRIO_PROCESS, 0); 170 if (errno) 171 prio = 0; 172 (void)setpriority(PRIO_PROCESS, 0, -2); 173 openlog("su", LOG_CONS, 0); 174 175 /* get current login name and shell */ 176 ruid = getuid(); 177 username = getlogin(); 178 if (username == NULL || (pwd = getpwnam(username)) == NULL || 179 pwd->pw_uid != ruid) 180 pwd = getpwuid(ruid); 181 if (pwd == NULL) 182 errx(1, "who are you?"); 183 username = strdup(pwd->pw_name); 184 if (username == NULL) 185 err(1, NULL); 186 if (asme) { 187 if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') { 188 /* copy: pwd memory is recycled */ 189 shell = strncpy(shellbuf, pwd->pw_shell, sizeof shellbuf); 190 shellbuf[sizeof shellbuf - 1] = '\0'; 191 } else { 192 shell = _PATH_BSHELL; 193 iscsh = NO; 194 } 195 } 196 197#ifdef LOGIN_CAP_AUTH 198 if (auth_method = strchr(user, ':')) { 199 *auth_method = '\0'; 200 auth_method++; 201 if (*auth_method == '\0') 202 auth_method = NULL; 203 } 204#endif /* !LOGIN_CAP_AUTH */ 205 206 /* get target login information, default to root */ 207 if ((pwd = getpwnam(user)) == NULL) { 208 errx(1, "unknown login: %s", user); 209 } 210#ifdef LOGIN_CAP 211 lc = login_getpwclass(pwd); 212#endif 213 214#ifdef WHEELSU 215 targetpass = strdup(pwd->pw_passwd); 216#endif /* WHEELSU */ 217 218 if (ruid) { 219#ifdef KERBEROS 220 if (use_kerberos && koktologin(username, user) 221 && !pwd->pw_uid) { 222 warnx("kerberos: not in %s's ACL.", user); 223 use_kerberos = 0; 224 } 225#endif 226 { 227 /* only allow those in group zero to su to root. */ 228 if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0)) && 229 gr->gr_mem && *(gr->gr_mem)) 230 for (g = gr->gr_mem;; ++g) { 231 if (!*g) 232 errx(1, 233 "you are not in the correct group to su %s.", 234 user); 235 if (strcmp(username, *g) == 0) { 236#ifdef WHEELSU 237 iswheelsu = 1; 238#endif /* WHEELSU */ 239 break; 240 } 241 } 242 } 243 /* if target requires a password, verify it */ 244 if (*pwd->pw_passwd) { 245#ifdef LOGIN_CAP_AUTH 246 /* 247 * This hands off authorisation to an authorisation program, 248 * depending on the styles available for the "auth-su", 249 * authorisation styles. 250 */ 251 if ((style = login_getstyle(lc, auth_method, "su")) == NULL) 252 errx(1, "auth method available for su.\n"); 253 if (authenticate(user, lc ? lc->lc_class : "default", style, "su") != 0) { 254#ifdef WHEELSU 255 if (!iswheelsu || authenticate(username, lc ? lc->lc_class : "default", style, "su") != 0) { 256#endif /* WHEELSU */ 257 { 258 fprintf(stderr, "Sorry\n"); 259 syslog(LOG_AUTH|LOG_WARNING,"BAD SU %s to %s%s", username, user, ontty()); 260 exit(1); 261 } 262 } 263 264 /* 265 * If authentication succeeds, run any approval 266 * program, if applicable for this class. 267 */ 268 approvep = login_getcapstr(lc, "approve", NULL, NULL); 269 if (approvep==NULL || auth_script(approvep, approvep, username, lc->lc_class, 0) == 0) { 270 int r = auth_scan(AUTH_OKAY); 271 /* See what the authorise program says */ 272 if (!(r & AUTH_ROOTOKAY) && pwd->pw_uid == 0) { 273 fprintf(stderr, "Sorry\n"); 274 syslog(LOG_AUTH|LOG_WARNING,"UNAPPROVED ROOT SU %s%s", user, ontty()); 275 exit(1); 276 } 277 } 278#else /* !LOGIN_CAP_AUTH */ 279#ifdef SKEY 280#ifdef WHEELSU 281 if (iswheelsu) { 282 pwd = getpwnam(username); 283 } 284#endif /* WHEELSU */ 285 p = skey_getpass("Password:", pwd, 1); 286 if (!(!strcmp(pwd->pw_passwd, skey_crypt(p, pwd->pw_passwd, pwd, 1)) 287#ifdef WHEELSU 288 || (iswheelsu && !strcmp(targetpass, crypt(p,targetpass))) 289#endif /* WHEELSU */ 290 )) { 291#else 292 p = getpass("Password:"); 293 if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) { 294#endif 295#ifdef KERBEROS 296 if (!use_kerberos || (use_kerberos && kerberos(username, user, pwd->pw_uid, p))) 297#endif 298 { 299 fprintf(stderr, "Sorry\n"); 300 syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s%s", username, user, ontty()); 301 exit(1); 302 } 303 } 304#ifdef WHEELSU 305 if (iswheelsu) { 306 pwd = getpwnam(user); 307 } 308#endif /* WHEELSU */ 309#endif /* LOGIN_CAP_AUTH */ 310 } 311 if (pwd->pw_expire && time(NULL) >= pwd->pw_expire) { 312 fprintf(stderr, "Sorry - account expired\n"); 313 syslog(LOG_AUTH|LOG_WARNING, 314 "BAD SU %s to %s%s", username, 315 user, ontty()); 316 exit(1); 317 } 318 } 319 320 if (asme) { 321 /* if asme and non-standard target shell, must be root */ 322 if (!chshell(pwd->pw_shell) && ruid) 323 errx(1, "permission denied (shell)."); 324 } else if (pwd->pw_shell && *pwd->pw_shell) { 325 shell = pwd->pw_shell; 326 iscsh = UNSET; 327 } else { 328 shell = _PATH_BSHELL; 329 iscsh = NO; 330 } 331 332 /* if we're forking a csh, we want to slightly muck the args */ 333 if (iscsh == UNSET) { 334 p = strrchr(shell, '/'); 335 if (p) 336 ++p; 337 else 338 p = shell; 339 if ((iscsh = strcmp(p, "csh") ? NO : YES) == NO) 340 iscsh = strcmp(p, "tcsh") ? NO : YES; 341 } 342 343 (void)setpriority(PRIO_PROCESS, 0, prio); 344 345#ifdef LOGIN_CAP 346 /* Set everything now except the environment & umask */ 347 setwhat = LOGIN_SETUSER|LOGIN_SETGROUP|LOGIN_SETRESOURCES|LOGIN_SETPRIORITY; 348 /* 349 * Don't touch resource/priority settings if -m has been 350 * used or -l hasn't, and we're not su'ing to root. 351 */ 352 if ((asme || !asthem) && 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 cleanenv[0] = NULL; 370 environ = cleanenv; 371#ifdef LOGIN_CAP 372 /* set the su'd user's environment & umask */ 373 setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH|LOGIN_SETUMASK|LOGIN_SETENV); 374#else 375 (void)setenv("PATH", _PATH_DEFPATH, 1); 376#endif 377 if (p) 378 (void)setenv("TERM", p, 1); 379 if (chdir(pwd->pw_dir) < 0) 380 errx(1, "no directory"); 381 } 382 if (asthem || pwd->pw_uid) 383 (void)setenv("USER", pwd->pw_name, 1); 384 (void)setenv("HOME", pwd->pw_dir, 1); 385 (void)setenv("SHELL", shell, 1); 386 } 387 if (iscsh == YES) { 388 if (fastlogin) 389 *np-- = "-f"; 390 if (asme) 391 *np-- = "-m"; 392 } 393 394 /* csh strips the first character... */ 395 *np = asthem ? "-su" : iscsh == YES ? "_su" : "su"; 396 397 if (ruid != 0) 398 syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s", 399 username, user, ontty()); 400 401 login_close(lc); 402 403 execv(shell, np); 404 err(1, "%s", shell); 405} 406 407static void 408usage() 409{ 410 (void)fprintf(stderr, "usage: su [%s] [login [args]]\n", ARGSTR); 411 exit(1); 412} 413 414int 415chshell(sh) 416 char *sh; 417{ 418 int r = 0; 419 char *cp; 420 421 setusershell(); 422 while (!r && (cp = getusershell()) != NULL) 423 r = strcmp(cp, sh) == 0; 424 endusershell(); 425 return r; 426} 427 428char * 429ontty() 430{ 431 char *p; 432 static char buf[MAXPATHLEN + 4]; 433 434 buf[0] = 0; 435 p = ttyname(STDERR_FILENO); 436 if (p) 437 snprintf(buf, sizeof(buf), " on %s", p); 438 return (buf); 439} 440 441#ifdef KERBEROS 442int 443kerberos(username, user, uid, pword) 444 char *username, *user; 445 int uid; 446 char *pword; 447{ 448 extern char *krb_err_txt[]; 449 KTEXT_ST ticket; 450 AUTH_DAT authdata; 451 int kerno; 452 u_long faddr; 453 struct sockaddr_in local_addr; 454 char lrealm[REALM_SZ], krbtkfile[MAXPATHLEN]; 455 char hostname[MAXHOSTNAMELEN], savehost[MAXHOSTNAMELEN]; 456 char *krb_get_phost(); 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 ((kerno = krb_get_local_addr(&local_addr)) != KSUCCESS) { 535 warnx("Unable to get our local address: %s", 536 krb_err_txt[kerno]); 537 dest_tkt(); 538 return (1); 539 } 540 faddr = local_addr.sin_addr.s_addr; 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 555int 556koktologin(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