1 /* $OpenBSD: su.c,v 1.65 2011/01/11 10:07:56 robert 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. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/param.h> 33 #include <sys/time.h> 34 #include <sys/resource.h> 35 36 #include <err.h> 37 #include <errno.h> 38 #include <grp.h> 39 #include <login_cap.h> 40 #include <paths.h> 41 #include <pwd.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <syslog.h> 46 #include <unistd.h> 47 #include <utmp.h> 48 #include <stdarg.h> 49 #include <bsd_auth.h> 50 51 char *getloginname(void); 52 char *ontty(void); 53 int chshell(const char *); 54 int verify_user(char *, struct passwd *, char *, login_cap_t *, 55 auth_session_t *); 56 void usage(void); 57 void auth_err(auth_session_t *, int, const char *, ...); 58 void auth_errx(auth_session_t *, int, const char *, ...); 59 60 int 61 main(int argc, char **argv) 62 { 63 int asme = 0, asthem = 0, ch, fastlogin = 0, emlogin = 0, prio; 64 int altshell = 0, homeless = 0; 65 char *user, *shell = NULL, *avshell, *username, **np; 66 char *class = NULL, *style = NULL, *p; 67 enum { UNSET, YES, NO } iscsh = UNSET; 68 char avshellbuf[MAXPATHLEN]; 69 extern char **environ; 70 auth_session_t *as; 71 struct passwd *pwd; 72 login_cap_t *lc; 73 uid_t ruid; 74 u_int flags; 75 76 while ((ch = getopt(argc, argv, "a:c:fKLlms:-")) != -1) 77 switch (ch) { 78 case 'a': 79 if (style) 80 usage(); 81 style = optarg; 82 break; 83 case 'c': 84 if (class) 85 usage(); 86 class = optarg; 87 break; 88 case 'f': 89 fastlogin = 1; 90 break; 91 case 'K': 92 if (style) 93 usage(); 94 style = "passwd"; 95 break; 96 case 'L': 97 emlogin = 1; 98 break; 99 case 'l': 100 case '-': 101 asme = 0; 102 asthem = 1; 103 break; 104 case 'm': 105 asme = 1; 106 asthem = 0; 107 break; 108 case 's': 109 altshell = 1; 110 shell = optarg; 111 break; 112 default: 113 usage(); 114 } 115 argv += optind; 116 117 errno = 0; 118 prio = getpriority(PRIO_PROCESS, 0); 119 if (errno) 120 prio = 0; 121 (void)setpriority(PRIO_PROCESS, 0, -2); 122 openlog("su", LOG_CONS, 0); 123 124 if ((as = auth_open()) == NULL) { 125 syslog(LOG_ERR, "auth_open: %m"); 126 err(1, "unable to initialize BSD authentication"); 127 } 128 auth_setoption(as, "login", "yes"); 129 130 /* get current login name and shell */ 131 ruid = getuid(); 132 username = getlogin(); 133 134 if (ruid && class) 135 auth_errx(as, 1, "only the superuser may specify a login class"); 136 137 if (ruid && altshell) 138 auth_errx(as, 1, "only the superuser may specify a login shell"); 139 140 if (username != NULL) 141 auth_setoption(as, "invokinguser", username); 142 143 if (username == NULL || (pwd = getpwnam(username)) == NULL || 144 pwd->pw_uid != ruid) 145 pwd = getpwuid(ruid); 146 if (pwd == NULL) 147 auth_errx(as, 1, "who are you?"); 148 if ((username = strdup(pwd->pw_name)) == NULL) 149 auth_errx(as, 1, "can't allocate memory"); 150 if (asme && !altshell) { 151 if (pwd->pw_shell && *pwd->pw_shell) { 152 if ((shell = strdup(pwd->pw_shell)) == NULL) 153 auth_errx(as, 1, "can't allocate memory"); 154 } else { 155 shell = _PATH_BSHELL; 156 iscsh = NO; 157 } 158 } 159 160 for (;;) { 161 /* get target user, default to root unless in -L mode */ 162 if (*argv) { 163 user = *argv; 164 } else if (emlogin) { 165 if ((user = getloginname()) == NULL) { 166 auth_close(as); 167 exit(1); 168 } 169 } else { 170 user = "root"; 171 } 172 /* style may be specified as part of the username */ 173 if ((p = strchr(user, ':')) != NULL) { 174 *p++ = '\0'; 175 style = p; /* XXX overrides -a flag */ 176 } 177 178 /* 179 * Clean and setup our current authentication session. 180 * Note that options *are* not cleared. 181 */ 182 auth_clean(as); 183 if (auth_setitem(as, AUTHV_INTERACTIVE, "True") != 0 || 184 auth_setitem(as, AUTHV_NAME, user) != 0) 185 auth_errx(as, 1, "can't allocate memory"); 186 if ((user = auth_getitem(as, AUTHV_NAME)) == NULL) 187 auth_errx(as, 1, "internal error"); 188 if (auth_setpwd(as, NULL) || (pwd = auth_getpwd(as)) == NULL) { 189 if (emlogin) 190 pwd = NULL; 191 else 192 auth_errx(as, 1, "unknown login %s", user); 193 } 194 195 /* If the user specified a login class, use it */ 196 if (!class && pwd && pwd->pw_class && pwd->pw_class[0] != '\0') 197 class = strdup(pwd->pw_class); 198 if ((lc = login_getclass(class)) == NULL) 199 auth_errx(as, 1, "no such login class: %s", 200 class ? class : LOGIN_DEFCLASS); 201 202 if ((ruid == 0 && !emlogin) || 203 verify_user(username, pwd, style, lc, as) == 0) 204 break; 205 syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s%s", 206 username, user, ontty()); 207 if (!emlogin) { 208 fprintf(stderr, "Sorry\n"); 209 auth_close(as); 210 exit(1); 211 } 212 fprintf(stderr, "Login incorrect\n"); 213 } 214 215 if (!altshell) { 216 if (asme) { 217 /* if asme and non-std target shell, must be root */ 218 if (ruid && !chshell(shell)) 219 auth_errx(as, 1, "permission denied (shell)."); 220 } else if (pwd->pw_shell && *pwd->pw_shell) { 221 if ((shell = strdup(pwd->pw_shell)) == NULL) 222 auth_errx(as, 1, "can't allocate memory"); 223 iscsh = UNSET; 224 } else { 225 shell = _PATH_BSHELL; 226 iscsh = NO; 227 } 228 } 229 230 if ((p = strrchr(shell, '/'))) 231 avshell = p+1; 232 else 233 avshell = shell; 234 235 /* if we're forking a csh, we want to slightly muck the args */ 236 if (iscsh == UNSET) 237 iscsh = strcmp(avshell, "csh") ? NO : YES; 238 239 if (!asme) { 240 if (asthem) { 241 p = getenv("TERM"); 242 if ((environ = calloc(1, sizeof (char *))) == NULL) 243 auth_errx(as, 1, "calloc"); 244 if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH)) 245 auth_err(as, 1, "unable to set user context"); 246 if (p && setenv("TERM", p, 1) == -1) 247 auth_err(as, 1, "unable to set environment"); 248 249 setegid(pwd->pw_gid); 250 seteuid(pwd->pw_uid); 251 252 homeless = chdir(pwd->pw_dir); 253 if (homeless) { 254 if (login_getcapbool(lc, "requirehome", 0)) { 255 auth_err(as, 1, "%s", pwd->pw_dir); 256 } else { 257 (void)printf("No home directory %s!\n", pwd->pw_dir); 258 (void)printf("Logging in with home = \"/\".\n"); 259 if (chdir("/") < 0) 260 auth_err(as, 1, "/"); 261 } 262 } 263 setegid(0); /* XXX use a saved gid instead? */ 264 seteuid(0); 265 } else if (pwd->pw_uid == 0) { 266 if (setusercontext(lc, 267 pwd, pwd->pw_uid, LOGIN_SETPATH|LOGIN_SETUMASK)) 268 auth_err(as, 1, "unable to set user context"); 269 } 270 if (asthem || pwd->pw_uid) { 271 if (setenv("LOGNAME", pwd->pw_name, 1) == -1 || 272 setenv("USER", pwd->pw_name, 1) == -1) 273 auth_err(as, 1, "unable to set environment"); 274 } 275 if (setenv("HOME", homeless ? "/" : pwd->pw_dir, 1) == -1 || 276 setenv("SHELL", shell, 1) == -1) 277 auth_err(as, 1, "unable to set environment"); 278 } else if (altshell) { 279 if (setenv("SHELL", shell, 1) == -1) 280 auth_err(as, 1, "unable to set environment"); 281 } 282 283 np = *argv ? argv : argv - 1; 284 if (iscsh == YES) { 285 if (fastlogin) 286 *np-- = "-f"; 287 if (asme) 288 *np-- = "-m"; 289 } 290 291 if (asthem) { 292 avshellbuf[0] = '-'; 293 strlcpy(avshellbuf+1, avshell, sizeof(avshellbuf) - 1); 294 avshell = avshellbuf; 295 } else if (iscsh == YES) { 296 /* csh strips the first character... */ 297 avshellbuf[0] = '_'; 298 strlcpy(avshellbuf+1, avshell, sizeof(avshellbuf) - 1); 299 avshell = avshellbuf; 300 } 301 302 *np = avshell; 303 304 if (ruid != 0) 305 syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s", 306 username, user, ontty()); 307 308 (void)setpriority(PRIO_PROCESS, 0, prio); 309 if (emlogin) { 310 flags = LOGIN_SETALL & ~LOGIN_SETPATH; 311 /* 312 * Only call setlogin() if this process is a session leader. 313 * In practice, this means the login name will be set only if 314 * we are exec'd by a shell. This is important because 315 * otherwise the parent shell's login name would change too. 316 */ 317 if (getsid(0) != getpid()) 318 flags &= ~LOGIN_SETLOGIN; 319 } else { 320 flags = LOGIN_SETRESOURCES|LOGIN_SETGROUP|LOGIN_SETUSER; 321 if (asthem) 322 flags |= LOGIN_SETENV|LOGIN_SETPRIORITY|LOGIN_SETUMASK; 323 } 324 if (setusercontext(lc, pwd, pwd->pw_uid, flags) != 0) 325 auth_err(as, 1, "unable to set user context"); 326 if (pwd->pw_uid && auth_approval(as, lc, pwd->pw_name, "su") <= 0) 327 auth_err(as, 1, "approval failure"); 328 auth_close(as); 329 330 execv(shell, np); 331 err(1, "%s", shell); 332 } 333 334 int 335 verify_user(char *from, struct passwd *pwd, char *style, 336 login_cap_t *lc, auth_session_t *as) 337 { 338 struct group *gr; 339 char **g, *cp; 340 int authok; 341 342 /* 343 * If we are trying to become root and the default style 344 * is being used, don't bother to look it up (we might be 345 * be su'ing up to fix /etc/login.conf) 346 */ 347 if ((pwd == NULL || pwd->pw_uid != 0 || style == NULL || 348 strcmp(style, LOGIN_DEFSTYLE) != 0) && 349 (style = login_getstyle(lc, style, "auth-su")) == NULL) 350 auth_errx(as, 1, "invalid authentication type"); 351 352 /* 353 * Let the authentication program know whether they are 354 * in group wheel or not (if trying to become super user) 355 */ 356 if (pwd != NULL && pwd->pw_uid == 0 && (gr = getgrgid(0)) != NULL && 357 gr->gr_mem != NULL && *(gr->gr_mem) != NULL) { 358 for (g = gr->gr_mem; *g; ++g) { 359 if (strcmp(from, *g) == 0) { 360 auth_setoption(as, "wheel", "yes"); 361 break; 362 } 363 } 364 if (!*g) 365 auth_setoption(as, "wheel", "no"); 366 } 367 368 auth_verify(as, style, NULL, lc->lc_class, (char *)NULL); 369 authok = auth_getstate(as); 370 if ((authok & AUTH_ALLOW) == 0) { 371 if ((cp = auth_getvalue(as, "errormsg")) != NULL) 372 fprintf(stderr, "%s\n", cp); 373 return(1); 374 } 375 return(0); 376 } 377 378 int 379 chshell(const char *sh) 380 { 381 char *cp; 382 int found = 0; 383 384 setusershell(); 385 while ((cp = getusershell()) != NULL) { 386 if (strcmp(cp, sh) == 0) { 387 found = 1; 388 break; 389 } 390 } 391 endusershell(); 392 return (found); 393 } 394 395 char * 396 ontty(void) 397 { 398 static char buf[MAXPATHLEN + 4]; 399 char *p; 400 401 buf[0] = 0; 402 if ((p = ttyname(STDERR_FILENO))) 403 snprintf(buf, sizeof(buf), " on %s", p); 404 return (buf); 405 } 406 407 /* 408 * Allow for a '.' and 16 characters for any instance as well as 409 * space for a ':' and 16 characters defining the authentication type. 410 */ 411 #define NBUFSIZ (UT_NAMESIZE + 1 + 16 + 1 + 16) 412 413 char * 414 getloginname(void) 415 { 416 static char nbuf[NBUFSIZ], *p; 417 int ch; 418 419 for (;;) { 420 (void)printf("login: "); 421 for (p = nbuf; (ch = getchar()) != '\n'; ) { 422 if (ch == EOF) 423 return (NULL); 424 if (p < nbuf + (NBUFSIZ - 1)) 425 *p++ = ch; 426 } 427 if (p > nbuf) { 428 if (nbuf[0] == '-') { 429 (void)fprintf(stderr, 430 "login names may not start with '-'.\n"); 431 } else { 432 *p = '\0'; 433 break; 434 } 435 } 436 } 437 return (nbuf); 438 } 439 440 void 441 usage(void) 442 { 443 extern char *__progname; 444 445 fprintf(stderr, "usage: %s [-fKLlm] [-a auth-type] [-c login-class] " 446 "[-s login-shell]\n" 447 "%-*s[login [shell arguments]]\n", __progname, 448 (int)strlen(__progname) + 8, ""); 449 exit(1); 450 } 451 452 void 453 auth_err(auth_session_t *as, int eval, const char *fmt, ...) 454 { 455 va_list ap; 456 457 va_start(ap, fmt); 458 vwarn(fmt, ap); 459 va_end(ap); 460 auth_close(as); 461 exit(eval); 462 } 463 464 void 465 auth_errx(auth_session_t *as, int eval, const char *fmt, ...) 466 { 467 va_list ap; 468 469 va_start(ap, fmt); 470 vwarnx(fmt, ap); 471 va_end(ap); 472 auth_close(as); 473 exit(eval); 474 } 475