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.13 1996/03/11 22:14:52 markm 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 SKEY 64 #include <skey.h> 65 #endif 66 67 #ifdef KERBEROS 68 #include <des.h> 69 #include <kerberosIV/krb.h> 70 #include <netdb.h> 71 72 #define ARGSTR "-Kflm" 73 74 static int kerberos(char *username, char *user, int uid, char *pword); 75 static int koktologin(char *name, char *toname); 76 77 int use_kerberos = 1; 78 #else 79 #define ARGSTR "-flm" 80 #endif 81 82 char *ontty __P((void)); 83 int chshell __P((char *)); 84 85 int 86 main(argc, argv) 87 int argc; 88 char **argv; 89 { 90 extern char **environ; 91 struct passwd *pwd; 92 #ifdef WHEELSU 93 char *targetpass; 94 int iswheelsu; 95 #endif /* WHEELSU */ 96 char *p, **g, *user, *shell, *username, *cleanenv[20], **nargv, **np; 97 struct group *gr; 98 uid_t ruid; 99 int asme, ch, asthem, fastlogin, prio, i; 100 enum { UNSET, YES, NO } iscsh = UNSET; 101 char shellbuf[MAXPATHLEN]; 102 103 #ifdef WHEELSU 104 iswheelsu = 105 #endif /* WHEELSU */ 106 asme = asthem = fastlogin = 0; 107 user = "root"; 108 while(optind < argc) 109 if((ch = getopt(argc, argv, ARGSTR)) != EOF) 110 switch((char)ch) { 111 #ifdef KERBEROS 112 case 'K': 113 use_kerberos = 0; 114 break; 115 #endif 116 case 'f': 117 fastlogin = 1; 118 break; 119 case '-': 120 case 'l': 121 asme = 0; 122 asthem = 1; 123 break; 124 case 'm': 125 asme = 1; 126 asthem = 0; 127 break; 128 case '?': 129 default: 130 (void)fprintf(stderr, "usage: su [%s] [login]\n", 131 ARGSTR); 132 exit(1); 133 } 134 else 135 { 136 user = argv[optind++]; 137 break; 138 } 139 140 if((nargv = malloc (sizeof (char *) * (argc + 4))) == NULL) { 141 errx(1, "malloc failure"); 142 } 143 144 nargv[argc + 3] = NULL; 145 for (i = argc; i >= optind; i--) 146 nargv[i + 3] = argv[i]; 147 np = &nargv[i + 3]; 148 149 argv += optind; 150 151 errno = 0; 152 prio = getpriority(PRIO_PROCESS, 0); 153 if (errno) 154 prio = 0; 155 (void)setpriority(PRIO_PROCESS, 0, -2); 156 openlog("su", LOG_CONS, 0); 157 158 /* get current login name and shell */ 159 ruid = getuid(); 160 username = getlogin(); 161 if (username == NULL || (pwd = getpwnam(username)) == NULL || 162 pwd->pw_uid != ruid) 163 pwd = getpwuid(ruid); 164 if (pwd == NULL) 165 errx(1, "who are you?"); 166 username = strdup(pwd->pw_name); 167 if (username == NULL) 168 err(1, NULL); 169 if (asme) 170 if (pwd->pw_shell && *pwd->pw_shell) 171 shell = strcpy(shellbuf, pwd->pw_shell); 172 else { 173 shell = _PATH_BSHELL; 174 iscsh = NO; 175 } 176 177 /* get target login information, default to root */ 178 if ((pwd = getpwnam(user)) == NULL) { 179 errx(1, "unknown login: %s", user); 180 } 181 182 #ifdef WHEELSU 183 targetpass = strdup(pwd->pw_passwd); 184 #endif /* WHEELSU */ 185 186 if (ruid) { 187 #ifdef KERBEROS 188 if (use_kerberos && koktologin(username, user) 189 && !pwd->pw_uid) { 190 warnx("kerberos: not in %s's ACL.", user); 191 use_kerberos = 0; 192 } 193 #endif 194 { 195 /* only allow those in group zero to su to root. */ 196 if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0))) 197 for (g = gr->gr_mem;; ++g) { 198 if (!*g) 199 errx(1, 200 "you are not in the correct group to su %s.", 201 user); 202 if (strcmp(username, *g) == 0) { 203 #ifdef WHEELSU 204 iswheelsu = 1; 205 #endif /* WHEELSU */ 206 break; 207 } 208 } 209 } 210 /* if target requires a password, verify it */ 211 if (*pwd->pw_passwd) { 212 #ifdef SKEY 213 #ifdef WHEELSU 214 if (iswheelsu) { 215 pwd = getpwnam(username); 216 } 217 #endif /* WHEELSU */ 218 p = skey_getpass("Password:", pwd, 1); 219 if (!(!strcmp(pwd->pw_passwd, 220 skey_crypt(p, pwd->pw_passwd, pwd, 1)) 221 #ifdef WHEELSU 222 || (iswheelsu && !strcmp(targetpass, 223 crypt(p, 224 targetpass))) 225 #endif /* WHEELSU */ 226 )) { 227 #else 228 p = getpass("Password:"); 229 if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) { 230 #endif 231 #ifdef KERBEROS 232 if (!use_kerberos || (use_kerberos && 233 kerberos(username, user, pwd->pw_uid, p)) 234 ) 235 #endif 236 { 237 fprintf(stderr, "Sorry\n"); 238 syslog(LOG_AUTH|LOG_WARNING, 239 "BAD SU %s to %s%s", username, 240 user, ontty()); 241 exit(1); 242 } 243 } 244 #ifdef WHEELSU 245 if (iswheelsu) { 246 pwd = getpwnam(user); 247 } 248 #endif /* WHEELSU */ 249 } 250 if (pwd->pw_expire && time(NULL) >= pwd->pw_expire) { 251 fprintf(stderr, "Sorry - account expired\n"); 252 syslog(LOG_AUTH|LOG_WARNING, 253 "BAD SU %s to %s%s", username, 254 user, ontty()); 255 exit(1); 256 } 257 } 258 259 if (asme) { 260 /* if asme and non-standard target shell, must be root */ 261 if (!chshell(pwd->pw_shell) && ruid) 262 errx(1, "permission denied (shell)."); 263 } else if (pwd->pw_shell && *pwd->pw_shell) { 264 shell = pwd->pw_shell; 265 iscsh = UNSET; 266 } else { 267 shell = _PATH_BSHELL; 268 iscsh = NO; 269 } 270 271 /* if we're forking a csh, we want to slightly muck the args */ 272 if (iscsh == UNSET) { 273 p = strrchr(shell, '/'); 274 if (p) 275 ++p; 276 else 277 p = shell; 278 if ((iscsh = strcmp(p, "csh") ? NO : YES) == NO) 279 iscsh = strcmp(p, "tcsh") ? NO : YES; 280 } 281 282 /* set permissions */ 283 if (setgid(pwd->pw_gid) < 0) 284 err(1, "setgid"); 285 if (initgroups(user, pwd->pw_gid)) 286 errx(1, "initgroups failed"); 287 if (setuid(pwd->pw_uid) < 0) 288 err(1, "setuid"); 289 290 if (!asme) { 291 if (asthem) { 292 p = getenv("TERM"); 293 cleanenv[0] = NULL; 294 environ = cleanenv; 295 (void)setenv("PATH", _PATH_DEFPATH, 1); 296 if (p) 297 (void)setenv("TERM", p, 1); 298 if (chdir(pwd->pw_dir) < 0) 299 errx(1, "no directory"); 300 } 301 if (asthem || pwd->pw_uid) 302 (void)setenv("USER", pwd->pw_name, 1); 303 (void)setenv("HOME", pwd->pw_dir, 1); 304 (void)setenv("SHELL", shell, 1); 305 } 306 307 if (iscsh == YES) { 308 if (fastlogin) 309 *np-- = "-f"; 310 if (asme) 311 *np-- = "-m"; 312 } 313 314 /* csh strips the first character... */ 315 *np = asthem ? "-su" : iscsh == YES ? "_su" : "su"; 316 317 if (ruid != 0) 318 syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s", 319 username, user, ontty()); 320 321 (void)setpriority(PRIO_PROCESS, 0, prio); 322 323 execv(shell, np); 324 err(1, "%s", shell); 325 } 326 327 int 328 chshell(sh) 329 char *sh; 330 { 331 char *cp; 332 333 while ((cp = getusershell()) != NULL) 334 if (strcmp(cp, sh) == 0) 335 return (1); 336 return (0); 337 } 338 339 char * 340 ontty() 341 { 342 char *p; 343 static char buf[MAXPATHLEN + 4]; 344 345 buf[0] = 0; 346 p = ttyname(STDERR_FILENO); 347 if (p) 348 snprintf(buf, sizeof(buf), " on %s", p); 349 return (buf); 350 } 351 352 #ifdef KERBEROS 353 int 354 kerberos(username, user, uid, pword) 355 char *username, *user; 356 int uid; 357 char *pword; 358 { 359 extern char *krb_err_txt[]; 360 KTEXT_ST ticket; 361 AUTH_DAT authdata; 362 int kerno; 363 u_long faddr; 364 struct sockaddr_in local_addr; 365 char lrealm[REALM_SZ], krbtkfile[MAXPATHLEN]; 366 char hostname[MAXHOSTNAMELEN], savehost[MAXHOSTNAMELEN]; 367 char *krb_get_phost(); 368 369 if (krb_get_lrealm(lrealm, 1) != KSUCCESS) 370 return (1); 371 (void)sprintf(krbtkfile, "%s_%s_%lu", TKT_ROOT, user, 372 (unsigned long)getuid()); 373 374 (void)setenv("KRBTKFILE", krbtkfile, 1); 375 (void)krb_set_tkt_string(krbtkfile); 376 /* 377 * Set real as well as effective ID to 0 for the moment, 378 * to make the kerberos library do the right thing. 379 */ 380 if (setuid(0) < 0) { 381 warn("setuid"); 382 return (1); 383 } 384 385 /* 386 * Little trick here -- if we are su'ing to root, 387 * we need to get a ticket for "xxx.root", where xxx represents 388 * the name of the person su'ing. Otherwise (non-root case), 389 * we need to get a ticket for "yyy.", where yyy represents 390 * the name of the person being su'd to, and the instance is null 391 * 392 * We should have a way to set the ticket lifetime, 393 * with a system default for root. 394 */ 395 kerno = krb_get_pw_in_tkt((uid == 0 ? username : user), 396 (uid == 0 ? "root" : ""), lrealm, 397 "krbtgt", lrealm, DEFAULT_TKT_LIFE, pword); 398 399 if (kerno != KSUCCESS) { 400 if (kerno == KDC_PR_UNKNOWN) { 401 warnx("kerberos: principal unknown: %s.%s@%s", 402 (uid == 0 ? username : user), 403 (uid == 0 ? "root" : ""), lrealm); 404 return (1); 405 } 406 warnx("kerberos: unable to su: %s", krb_err_txt[kerno]); 407 syslog(LOG_NOTICE|LOG_AUTH, 408 "BAD Kerberos SU: %s to %s%s: %s", 409 username, user, ontty(), krb_err_txt[kerno]); 410 return (1); 411 } 412 413 if (chown(krbtkfile, uid, -1) < 0) { 414 warn("chown"); 415 (void)unlink(krbtkfile); 416 return (1); 417 } 418 419 (void)setpriority(PRIO_PROCESS, 0, -2); 420 421 if (gethostname(hostname, sizeof(hostname)) == -1) { 422 warn("gethostname"); 423 dest_tkt(); 424 return (1); 425 } 426 427 (void)strncpy(savehost, krb_get_phost(hostname), sizeof(savehost)); 428 savehost[sizeof(savehost) - 1] = '\0'; 429 430 kerno = krb_mk_req(&ticket, "rcmd", savehost, lrealm, 33); 431 432 if (kerno == KDC_PR_UNKNOWN) { 433 warnx("Warning: TGT not verified."); 434 syslog(LOG_NOTICE|LOG_AUTH, 435 "%s to %s%s, TGT not verified (%s); %s.%s not registered?", 436 username, user, ontty(), krb_err_txt[kerno], 437 "rcmd", savehost); 438 } else if (kerno != KSUCCESS) { 439 warnx("Unable to use TGT: %s", krb_err_txt[kerno]); 440 syslog(LOG_NOTICE|LOG_AUTH, "failed su: %s to %s%s: %s", 441 username, user, ontty(), krb_err_txt[kerno]); 442 dest_tkt(); 443 return (1); 444 } else { 445 if ((kerno = krb_get_local_addr(&local_addr)) != KSUCCESS) { 446 warnx("Unable to get our local address: %s", 447 krb_err_txt[kerno]); 448 dest_tkt(); 449 return (1); 450 } 451 faddr = local_addr.sin_addr.s_addr; 452 if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr, 453 &authdata, "")) != KSUCCESS) { 454 warnx("kerberos: unable to verify rcmd ticket: %s\n", 455 krb_err_txt[kerno]); 456 syslog(LOG_NOTICE|LOG_AUTH, 457 "failed su: %s to %s%s: %s", username, 458 user, ontty(), krb_err_txt[kerno]); 459 dest_tkt(); 460 return (1); 461 } 462 } 463 return (0); 464 } 465 466 int 467 koktologin(name, toname) 468 char *name, *toname; 469 { 470 AUTH_DAT *kdata; 471 AUTH_DAT kdata_st; 472 char realm[REALM_SZ]; 473 474 if (krb_get_lrealm(realm, 1) != KSUCCESS) 475 return (1); 476 kdata = &kdata_st; 477 memset((char *)kdata, 0, sizeof(*kdata)); 478 (void)strcpy(kdata->pname, name); 479 (void)strcpy(kdata->pinst, 480 ((strcmp(toname, "root") == 0) ? "root" : "")); 481 (void)strcpy(kdata->prealm, realm); 482 return (kuserok(kdata, toname)); 483 } 484 #endif 485