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