GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/su/su.c Lines: 0 222 0.0 %
Date: 2017-11-07 Branches: 0 216 0.0 %

Line Branch Exec Source
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 flock cpath wpath", 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 flock cpath wpath", 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 flock cpath wpath", 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
}