1 /* $OpenBSD: su.c,v 1.40 2001/06/25 21:29:31 hin 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 /*static char sccsid[] = "from: @(#)su.c 5.26 (Berkeley) 7/6/91";*/ 44 static char rcsid[] = "$OpenBSD: su.c,v 1.40 2001/06/25 21:29:31 hin Exp $"; 45 #endif /* not lint */ 46 47 #include <sys/param.h> 48 #include <sys/time.h> 49 #include <sys/resource.h> 50 51 #include <err.h> 52 #include <errno.h> 53 #include <grp.h> 54 #include <login_cap.h> 55 #include <paths.h> 56 #include <pwd.h> 57 #include <stdio.h> 58 #include <stdlib.h> 59 #include <string.h> 60 #include <syslog.h> 61 #include <unistd.h> 62 #ifdef __STDC__ 63 #include <stdarg.h> 64 #else 65 #include <varargs.h> 66 #endif 67 #include <bsd_auth.h> 68 69 char *ontty __P((void)); 70 int chshell __P((char *)); 71 void usage __P((void)); 72 void auth_err __P((auth_session_t *, int, const char *, ...)); 73 void auth_errx __P((auth_session_t *, int, const char *, ...)); 74 75 int 76 main(argc, argv) 77 int argc; 78 char **argv; 79 { 80 extern char **environ; 81 enum { UNSET, YES, NO } iscsh; 82 struct passwd *pwd; 83 struct group *gr; 84 uid_t ruid; 85 login_cap_t *lc; 86 auth_session_t *as; 87 int asme, asthem, authok, ch, fastlogin, prio; 88 char *class, *style, *p, **g; 89 char *user, *shell, *avshell, *username, **np, *fullname; 90 char shellbuf[MAXPATHLEN], avshellbuf[MAXPATHLEN]; 91 92 iscsh = UNSET; 93 class = shell = style = NULL; 94 asme = asthem = fastlogin = 0; 95 while ((ch = getopt(argc, argv, "-a:c:fKlm")) != -1) 96 switch(ch) { 97 case 'a': 98 if (style) 99 usage(); 100 style = optarg; 101 break; 102 case 'c': 103 if (class) 104 usage(); 105 class = optarg; 106 break; 107 case 'f': 108 fastlogin = 1; 109 break; 110 case 'K': 111 if (style) 112 usage(); 113 style = "passwd"; 114 break; 115 case '-': 116 case 'l': 117 asme = 0; 118 asthem = 1; 119 break; 120 case 'm': 121 asme = 1; 122 asthem = 0; 123 break; 124 case '?': 125 default: 126 usage(); 127 } 128 argv += optind; 129 130 errno = 0; 131 prio = getpriority(PRIO_PROCESS, 0); 132 if (errno) 133 prio = 0; 134 (void)setpriority(PRIO_PROCESS, 0, -2); 135 openlog("su", LOG_CONS, 0); 136 137 if ((as = auth_open()) == NULL) { 138 syslog(LOG_ERR, "auth_open: %m"); 139 err(1, "unable to initialize BSD authentication"); 140 } 141 auth_setoption(as, "login", "yes"); 142 143 /* get current login name and shell */ 144 ruid = getuid(); 145 username = getlogin(); 146 147 if(username != NULL) 148 auth_setoption(as, "invokinguser", username); 149 150 if (username == NULL || (pwd = getpwnam(username)) == NULL || 151 pwd->pw_uid != ruid) 152 pwd = getpwuid(ruid); 153 if (pwd == NULL) 154 auth_errx(as, 1, "who are you?"); 155 if ((username = strdup(pwd->pw_name)) == NULL) 156 auth_err(as, 1, "can't allocate memory"); 157 if (asme) { 158 if (pwd->pw_shell && *pwd->pw_shell) { 159 strlcpy(shellbuf, pwd->pw_shell, sizeof(shellbuf)); 160 shell = shellbuf; 161 } else { 162 shell = _PATH_BSHELL; 163 iscsh = NO; 164 } 165 } 166 167 /* get target login information, default to root */ 168 user = *argv ? *argv : "root"; 169 np = *argv ? argv : argv - 1; 170 171 if ((pwd = getpwnam(user)) == NULL) 172 auth_errx(as, 1, "unknown login %s", user); 173 if ((user = strdup(pwd->pw_name)) == NULL) 174 auth_err(as, 1, "can't allocate memory"); 175 176 /* If the user specified a login class and we are root, use it */ 177 if (ruid && class) 178 auth_errx(as, 1, "only the superuser may specify a login class"); 179 if (class) 180 pwd->pw_class = class; 181 if ((lc = login_getclass(pwd->pw_class)) == NULL) 182 auth_errx(as, 1, "no such login class: %s", 183 class ? class : LOGIN_DEFCLASS); 184 185 if (ruid) { 186 /* 187 * If we are trying to become root and the default style 188 * is being used, don't bother to look it up (we might be 189 * be su'ing up to fix /etc/login.conf) 190 */ 191 if ((pwd->pw_uid || !style || strcmp(style, LOGIN_DEFSTYLE)) && 192 (style = login_getstyle(lc, style, "auth-su")) == NULL) 193 auth_errx(as, 1, "invalid authentication type"); 194 fullname = user; 195 /* 196 * Let the authentication program know whether they are 197 * in group wheel or not (if trying to become super user) 198 */ 199 if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0)) 200 && gr->gr_mem && *(gr->gr_mem)) { 201 for (g = gr->gr_mem; *g; ++g) { 202 if (strcmp(username, *g) == 0) { 203 auth_setoption(as, "wheel", "yes"); 204 break; 205 } 206 } 207 if (!*g) 208 auth_setoption(as, "wheel", "no"); 209 } 210 211 auth_verify(as, style, fullname, lc->lc_class, NULL); 212 authok = auth_getstate(as); 213 if ((authok & AUTH_ALLOW) == 0) { 214 if ((p = auth_getvalue(as, "errormsg")) != NULL) 215 fprintf(stderr, "%s\n", p); 216 fprintf(stderr, "Sorry\n"); 217 syslog(LOG_AUTH|LOG_WARNING, 218 "BAD SU %s to %s%s", username, user, ontty()); 219 auth_close(as); 220 exit(1); 221 } 222 } 223 224 if (asme) { 225 /* if asme and non-standard target shell, must be root */ 226 if (!chshell(pwd->pw_shell) && ruid) 227 auth_errx(as, 1, "permission denied (shell)."); 228 } else if (pwd->pw_shell && *pwd->pw_shell) { 229 shell = pwd->pw_shell; 230 iscsh = UNSET; 231 } else { 232 shell = _PATH_BSHELL; 233 iscsh = NO; 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 seteuid(pwd->pw_uid); 256 setegid(pwd->pw_gid); 257 if (chdir(pwd->pw_dir) < 0) 258 auth_err(as, 1, "%s", pwd->pw_dir); 259 seteuid(0); 260 setegid(0); /* XXX use a saved gid instead? */ 261 } else if (pwd->pw_uid == 0) { 262 if (setusercontext(lc, 263 pwd, pwd->pw_uid, LOGIN_SETPATH|LOGIN_SETUMASK)) 264 auth_err(as, 1, "unable to set user context"); 265 } 266 if (asthem || pwd->pw_uid) { 267 if (setenv("LOGNAME", pwd->pw_name, 1) == -1 || 268 setenv("USER", pwd->pw_name, 1) == -1) 269 auth_err(as, 1, "unable to set environment"); 270 } 271 if (setenv("HOME", pwd->pw_dir, 1) == -1 || 272 setenv("SHELL", shell, 1) == -1) 273 auth_err(as, 1, "unable to set environment"); 274 } 275 276 if (iscsh == YES) { 277 if (fastlogin) 278 *np-- = "-f"; 279 if (asme) 280 *np-- = "-m"; 281 } 282 283 if (asthem) { 284 avshellbuf[0] = '-'; 285 strlcpy(avshellbuf+1, avshell, sizeof(avshellbuf) - 1); 286 avshell = avshellbuf; 287 } else if (iscsh == YES) { 288 /* csh strips the first character... */ 289 avshellbuf[0] = '_'; 290 strlcpy(avshellbuf+1, avshell, sizeof(avshellbuf) - 1); 291 avshell = avshellbuf; 292 } 293 294 *np = avshell; 295 296 if (ruid != 0) 297 syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s", 298 username, user, ontty()); 299 300 (void)setpriority(PRIO_PROCESS, 0, prio); 301 if (setusercontext(lc, pwd, pwd->pw_uid, 302 (asthem ? (LOGIN_SETPRIORITY | LOGIN_SETUMASK) : 0) | 303 LOGIN_SETRESOURCES | LOGIN_SETGROUP | LOGIN_SETUSER)) 304 auth_err(as, 1, "unable to set user context"); 305 if (pwd->pw_uid && auth_approval(as, lc, pwd->pw_name, "su") <= 0) 306 auth_err(as, 1, "approval failure"); 307 auth_close(as); 308 309 execv(shell, np); 310 err(1, "%s", shell); 311 } 312 313 int 314 chshell(sh) 315 char *sh; 316 { 317 char *cp; 318 319 while ((cp = getusershell()) != NULL) 320 if (strcmp(cp, sh) == 0) 321 return (1); 322 return (0); 323 } 324 325 char * 326 ontty() 327 { 328 static char buf[MAXPATHLEN + 4]; 329 char *p; 330 331 buf[0] = 0; 332 if ((p = ttyname(STDERR_FILENO))) 333 snprintf(buf, sizeof(buf), " on %s", p); 334 return (buf); 335 } 336 337 void 338 usage() 339 { 340 extern char *__progname; 341 342 fprintf(stderr, "usage: %s [-fKlm] [-a auth-type] %s ", __progname, 343 "[-c login-class] [login [shell arguments]]\n"); 344 exit(1); 345 } 346 347 void 348 #ifdef __STDC__ 349 auth_err(auth_session_t *as, int eval, const char *fmt, ...) 350 #else 351 auth_err(va_alist) 352 va_dcl 353 #endif 354 { 355 va_list ap; 356 #ifdef __STDC__ 357 va_start(ap, fmt); 358 #else 359 auth_session_t *as; 360 int eval; 361 const char *fmt; 362 363 va_start(ap); 364 as = va_arg(ap, auth_session_t *); 365 eval = va_arg(ap, int); 366 fmt = va_arg(ap, const char *); 367 #endif 368 verr(eval, fmt, ap); 369 auth_close(as); 370 va_end(ap); 371 } 372 373 void 374 #ifdef __STDC__ 375 auth_errx(auth_session_t *as, int eval, const char *fmt, ...) 376 #else 377 auth_errx(va_alist) 378 va_dcl 379 #endif 380 { 381 va_list ap; 382 #ifdef __STDC__ 383 va_start(ap, fmt); 384 #else 385 auth_session_t *as; 386 int eval; 387 const char *fmt; 388 389 va_start(ap); 390 as = va_arg(ap, auth_session_t *); 391 eval = va_arg(ap, int); 392 fmt = va_arg(ap, const char *); 393 #endif 394 verrx(eval, fmt, ap); 395 auth_close(as); 396 va_end(ap); 397 } 398