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