1 /* $OpenBSD: su.c,v 1.56 2003/11/09 20:13:58 otto 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.56 2003/11/09 20:13:58 otto 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
main(int argc,char ** argv)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 seteuid(pwd->pw_uid);
254 setegid(pwd->pw_gid);
255 if (chdir(pwd->pw_dir) < 0)
256 auth_err(as, 1, "%s", pwd->pw_dir);
257 seteuid(0);
258 setegid(0); /* XXX use a saved gid instead? */
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
verify_user(char * from,struct passwd * pwd,char * style,login_cap_t * lc,auth_session_t * as)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
chshell(const char * sh)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 *
ontty(void)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 *
getloginname(void)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
usage(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
auth_err(auth_session_t * as,int eval,const char * fmt,...)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
auth_errx(auth_session_t * as,int eval,const char * fmt,...)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