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