1 /* $NetBSD: su.c,v 1.18 1997/07/02 05:42:13 lukem Exp $ */ 2 3 /* 4 * Copyright (c) 1988 The Regents of the University of California. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #ifndef lint 37 char copyright[] = 38 "@(#) Copyright (c) 1988 The Regents of the University of California.\n\ 39 All rights reserved.\n"; 40 #endif /* not lint */ 41 42 #ifndef lint 43 #if 0 44 static char sccsid[] = "@(#)su.c 8.3 (Berkeley) 4/2/94";*/ 45 #else 46 static char rcsid[] = "$NetBSD: su.c,v 1.18 1997/07/02 05:42:13 lukem Exp $"; 47 #endif 48 #endif /* not lint */ 49 50 #include <sys/param.h> 51 #include <sys/time.h> 52 #include <sys/resource.h> 53 #include <err.h> 54 #include <errno.h> 55 #include <grp.h> 56 #include <paths.h> 57 #include <pwd.h> 58 #include <stdio.h> 59 #include <skey.h> 60 #include <stdlib.h> 61 #include <string.h> 62 #include <syslog.h> 63 #include <tzfile.h> 64 #include <unistd.h> 65 66 #ifdef KERBEROS 67 #include <kerberosIV/des.h> 68 #include <kerberosIV/krb.h> 69 #include <netdb.h> 70 71 #define ARGSTR "-Kflm" 72 73 int use_kerberos = 1; 74 75 static int kerberos __P((char *, char *, int)); 76 static int koktologin __P((char *, char *, char *)); 77 78 #else 79 #define ARGSTR "-flm" 80 #endif 81 82 #ifndef SUGROUP 83 #define SUGROUP "wheel" 84 #endif 85 86 87 int main __P((int, char **)); 88 89 static int chshell __P((char *)); 90 static char *ontty __P((void)); 91 92 93 int 94 main(argc, argv) 95 int argc; 96 char **argv; 97 { 98 extern char *__progname; 99 extern char **environ; 100 struct passwd *pwd; 101 char *p; 102 struct group *gr; 103 struct timeval tp; 104 uid_t ruid; 105 int asme, ch, asthem, fastlogin, prio; 106 enum { UNSET, YES, NO } iscsh = UNSET; 107 char *user, *shell, *avshell, *username, *cleanenv[10], **np; 108 char shellbuf[MAXPATHLEN], avshellbuf[MAXPATHLEN]; 109 110 asme = asthem = fastlogin = 0; 111 shell = NULL; 112 while ((ch = getopt(argc, argv, ARGSTR)) != EOF) 113 switch((char)ch) { 114 #ifdef KERBEROS 115 case 'K': 116 use_kerberos = 0; 117 break; 118 #endif 119 case 'f': 120 fastlogin = 1; 121 break; 122 case '-': 123 case 'l': 124 asme = 0; 125 asthem = 1; 126 break; 127 case 'm': 128 asme = 1; 129 asthem = 0; 130 break; 131 case '?': 132 default: 133 (void)fprintf(stderr, 134 "Usage: %s [%s] [login [shell arguments]]\n", 135 __progname, ARGSTR); 136 exit(1); 137 } 138 argv += optind; 139 140 errno = 0; 141 prio = getpriority(PRIO_PROCESS, 0); 142 if (errno) 143 prio = 0; 144 (void)setpriority(PRIO_PROCESS, 0, -2); 145 openlog("su", LOG_CONS, 0); 146 147 /* get current login name and shell */ 148 ruid = getuid(); 149 username = getlogin(); 150 if (username == NULL || (pwd = getpwnam(username)) == NULL || 151 pwd->pw_uid != ruid) 152 pwd = getpwuid(ruid); 153 if (pwd == NULL) { 154 errx(1, "who are you?"); 155 } 156 username = strdup(pwd->pw_name); 157 if (asme) 158 if (pwd->pw_shell && *pwd->pw_shell) 159 shell = strncpy(shellbuf, pwd->pw_shell, 160 sizeof(shellbuf) + 1); 161 else { 162 shell = _PATH_BSHELL; 163 iscsh = NO; 164 } 165 166 /* get target login information, default to root */ 167 user = *argv ? *argv : "root"; 168 np = *argv ? argv : argv-1; 169 170 if ((pwd = getpwnam(user)) == NULL) { 171 errx(1, "unknown login %s", user); 172 } 173 174 if (ruid) { 175 #ifdef KERBEROS 176 if (!use_kerberos || kerberos(username, user, pwd->pw_uid)) 177 #endif 178 { 179 /* Only allow those in group SUGROUP to su to root, 180 but only if that group has any members. 181 If SUGROUP has no members, allow anyone to su root */ 182 if (pwd->pw_uid == 0 && 183 (gr = getgrnam(SUGROUP)) && *gr->gr_mem) { 184 char **g; 185 186 for (g = gr->gr_mem; ; g++) { 187 if (*g == NULL) 188 errx(1, 189 "you are not listed in the correct secondary group (%s) to su %s.", 190 SUGROUP, user); 191 if (strcmp(username, *g) == 0) 192 break; 193 } 194 } 195 /* if target requires a password, verify it */ 196 if (*pwd->pw_passwd) { 197 p = getpass("Password:"); 198 #ifdef SKEY 199 if (strcasecmp(p, "s/key") == 0) { 200 if (skey_haskey(user)) 201 errx(1, "Sorry, you have no s/key."); 202 else { 203 if (skey_authenticate(user)) { 204 goto badlogin; 205 } 206 } 207 208 } else 209 #endif 210 if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) { 211 badlogin: 212 fprintf(stderr, "Sorry\n"); 213 syslog(LOG_AUTH|LOG_WARNING, 214 "BAD SU %s to %s%s", username, 215 user, ontty()); 216 exit(1); 217 } 218 } 219 } 220 } 221 222 if (asme) { 223 /* if asme and non-standard target shell, must be root */ 224 if (!chshell(pwd->pw_shell) && ruid) 225 errx(1,"permission denied (shell)."); 226 } else if (pwd->pw_shell && *pwd->pw_shell) { 227 shell = pwd->pw_shell; 228 iscsh = UNSET; 229 } else { 230 shell = _PATH_BSHELL; 231 iscsh = NO; 232 } 233 234 if ((p = strrchr(shell, '/')) != NULL) 235 avshell = p+1; 236 else 237 avshell = shell; 238 239 /* if we're forking a csh, we want to slightly muck the args */ 240 if (iscsh == UNSET) 241 iscsh = strstr(avshell, "csh") ? YES : NO; 242 243 /* set permissions */ 244 if (setgid(pwd->pw_gid) < 0) 245 err(1, "setgid"); 246 if (initgroups(user, pwd->pw_gid)) 247 errx(1, "initgroups failed"); 248 if (setuid(pwd->pw_uid) < 0) 249 err(1, "setuid"); 250 251 if (!asme) { 252 if (asthem) { 253 p = getenv("TERM"); 254 cleanenv[0] = NULL; 255 environ = cleanenv; 256 (void)setenv("PATH", _PATH_DEFPATH, 1); 257 if (p) 258 (void)setenv("TERM", p, 1); 259 if (chdir(pwd->pw_dir) < 0) 260 errx(1, "no directory"); 261 } 262 if (asthem || pwd->pw_uid) 263 (void)setenv("USER", pwd->pw_name, 1); 264 (void)setenv("HOME", pwd->pw_dir, 1); 265 (void)setenv("SHELL", shell, 1); 266 } 267 268 if (iscsh == YES) { 269 if (fastlogin) 270 *np-- = "-f"; 271 if (asme) 272 *np-- = "-m"; 273 } 274 275 if (asthem) { 276 avshellbuf[0] = '-'; 277 (void)strncpy(avshellbuf+1, avshell, sizeof(avshellbuf) - 2); 278 avshell = avshellbuf; 279 } else if (iscsh == YES) { 280 /* csh strips the first character... */ 281 avshellbuf[0] = '_'; 282 (void)strncpy(avshellbuf+1, avshell, sizeof(avshellbuf) - 2); 283 avshell = avshellbuf; 284 } 285 *np = avshell; 286 287 if (pwd->pw_change || pwd->pw_expire) 288 (void)gettimeofday(&tp, (struct timezone *)NULL); 289 if (pwd->pw_change) 290 if (tp.tv_sec >= pwd->pw_change) { 291 (void)printf("%s -- %s's password has expired.\n", 292 (ruid ? "Sorry" : "Note"), user); 293 if (ruid != 0) 294 exit(1); 295 } else if (pwd->pw_change - tp.tv_sec < 296 _PASSWORD_WARNDAYS * SECSPERDAY) 297 (void)printf("Warning: %s's password expires on %s", 298 user, ctime(&pwd->pw_change)); 299 if (pwd->pw_expire) 300 if (tp.tv_sec >= pwd->pw_expire) { 301 (void)printf("%s -- %s's account has expired.\n", 302 (ruid ? "Sorry" : "Note"), user); 303 if (ruid != 0) 304 exit(1); 305 } else if (pwd->pw_expire - tp.tv_sec < 306 _PASSWORD_WARNDAYS * SECSPERDAY) 307 (void)printf("Warning: %s's account expires on %s", 308 user, ctime(&pwd->pw_expire)); 309 310 if (ruid != 0) 311 syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s", 312 username, user, ontty()); 313 314 (void)setpriority(PRIO_PROCESS, 0, prio); 315 316 execv(shell, np); 317 err(1, "%s", shell); 318 } 319 320 static int 321 chshell(sh) 322 char *sh; 323 { 324 char *cp; 325 char *getusershell(); 326 327 while ((cp = getusershell()) != NULL) 328 if (!strcmp(cp, sh)) 329 return (1); 330 return (0); 331 } 332 333 static char * 334 ontty() 335 { 336 char *p, *ttyname(); 337 static char buf[MAXPATHLEN + 4]; 338 339 buf[0] = 0; 340 if ((p = ttyname(STDERR_FILENO)) != NULL) 341 (void)snprintf(buf, sizeof buf, " on %s", p); 342 return (buf); 343 } 344 345 #ifdef KERBEROS 346 static int 347 kerberos(username, user, uid) 348 char *username, *user; 349 int uid; 350 { 351 KTEXT_ST ticket; 352 AUTH_DAT authdata; 353 struct hostent *hp; 354 register char *p; 355 int kerno; 356 u_long faddr; 357 char lrealm[REALM_SZ], krbtkfile[MAXPATHLEN]; 358 char hostname[MAXHOSTNAMELEN], savehost[MAXHOSTNAMELEN]; 359 char *ontty(), *krb_get_phost(); 360 361 if (krb_get_lrealm(lrealm, 1) != KSUCCESS) 362 return (1); 363 if (koktologin(username, lrealm, user) && !uid) { 364 warnx("kerberos: not in %s's ACL.", user); 365 return (1); 366 } 367 (void)(void)snprintf(krbtkfile, sizeof krbtkfile, "%s_%s_%d", TKT_ROOT, 368 user, getuid()); 369 370 (void)setenv("KRBTKFILE", krbtkfile, 1); 371 (void)krb_set_tkt_string(krbtkfile); 372 /* 373 * Set real as well as effective ID to 0 for the moment, 374 * to make the kerberos library do the right thing. 375 */ 376 if (setuid(0) < 0) { 377 warn("setuid"); 378 return (1); 379 } 380 381 /* 382 * Little trick here -- if we are su'ing to root, 383 * we need to get a ticket for "xxx.root", where xxx represents 384 * the name of the person su'ing. Otherwise (non-root case), 385 * we need to get a ticket for "yyy.", where yyy represents 386 * the name of the person being su'd to, and the instance is null 387 * 388 * We should have a way to set the ticket lifetime, 389 * with a system default for root. 390 */ 391 kerno = krb_get_pw_in_tkt((uid == 0 ? username : user), 392 (uid == 0 ? "root" : ""), lrealm, 393 "krbtgt", lrealm, DEFAULT_TKT_LIFE, 0); 394 395 if (kerno != KSUCCESS) { 396 if (kerno == KDC_PR_UNKNOWN) { 397 warnx("kerberos: principal unknown: %s.%s@%s", 398 (uid == 0 ? username : user), 399 (uid == 0 ? "root" : ""), lrealm); 400 return (1); 401 } 402 warnx("kerberos: unable to su: %s", krb_err_txt[kerno]); 403 syslog(LOG_NOTICE|LOG_AUTH, 404 "BAD Kerberos SU: %s to %s%s: %s", 405 username, user, ontty(), krb_err_txt[kerno]); 406 return (1); 407 } 408 409 if (chown(krbtkfile, uid, -1) < 0) { 410 warn("chown"); 411 (void)unlink(krbtkfile); 412 return (1); 413 } 414 415 (void)setpriority(PRIO_PROCESS, 0, -2); 416 417 if (gethostname(hostname, sizeof(hostname)) == -1) { 418 warn("gethostname"); 419 dest_tkt(); 420 return (1); 421 } 422 423 (void)strncpy(savehost, krb_get_phost(hostname), sizeof(savehost)); 424 savehost[sizeof(savehost) - 1] = '\0'; 425 426 kerno = krb_mk_req(&ticket, "rcmd", savehost, lrealm, 33); 427 428 if (kerno == KDC_PR_UNKNOWN) { 429 warnx("Warning: TGT not verified."); 430 syslog(LOG_NOTICE|LOG_AUTH, 431 "%s to %s%s, TGT not verified (%s); %s.%s not registered?", 432 username, user, ontty(), krb_err_txt[kerno], 433 "rcmd", savehost); 434 } else if (kerno != KSUCCESS) { 435 warnx("Unable to use TGT: %s", krb_err_txt[kerno]); 436 syslog(LOG_NOTICE|LOG_AUTH, "failed su: %s to %s%s: %s", 437 username, user, ontty(), krb_err_txt[kerno]); 438 dest_tkt(); 439 return (1); 440 } else { 441 if (!(hp = gethostbyname(hostname))) { 442 warnx("can't get addr of %s", hostname); 443 dest_tkt(); 444 return (1); 445 } 446 memmove((char *)&faddr, (char *)hp->h_addr, sizeof(faddr)); 447 448 if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr, 449 &authdata, "")) != KSUCCESS) { 450 warnx("kerberos: unable to verify rcmd ticket: %s\n", 451 krb_err_txt[kerno]); 452 syslog(LOG_NOTICE|LOG_AUTH, 453 "failed su: %s to %s%s: %s", username, 454 user, ontty(), krb_err_txt[kerno]); 455 dest_tkt(); 456 return (1); 457 } 458 } 459 return (0); 460 } 461 462 static int 463 koktologin(name, realm, toname) 464 char *name, *realm, *toname; 465 { 466 register AUTH_DAT *kdata; 467 AUTH_DAT kdata_st; 468 469 kdata = &kdata_st; 470 memset((char *)kdata, 0, sizeof(*kdata)); 471 (void)strncpy(kdata->pname, name, sizeof(kdata->pname) - 1); 472 (void)strncpy(kdata->pinst, 473 ((strcmp(toname, "root") == 0) ? "root" : ""), sizeof(kdata->pinst) - 1); 474 (void)strncpy(kdata->prealm, realm, sizeof(kdata->prealm) - 1); 475 return (kuserok(kdata, toname)); 476 } 477 #endif 478