xref: /openbsd-src/usr.bin/su/su.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
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