xref: /openbsd-src/usr.bin/doas/doas.c (revision 38599afa1d1d1f14a897b01350e8ce94486e1788)
1*38599afaStedu /* $OpenBSD: doas.c,v 1.99 2024/02/15 18:57:58 tedu Exp $ */
27bfbda14Stedu /*
37bfbda14Stedu  * Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
47bfbda14Stedu  *
57bfbda14Stedu  * Permission to use, copy, modify, and distribute this software for any
67bfbda14Stedu  * purpose with or without fee is hereby granted, provided that the above
77bfbda14Stedu  * copyright notice and this permission notice appear in all copies.
87bfbda14Stedu  *
97bfbda14Stedu  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
107bfbda14Stedu  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
117bfbda14Stedu  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
127bfbda14Stedu  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
137bfbda14Stedu  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
147bfbda14Stedu  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
157bfbda14Stedu  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
167bfbda14Stedu  */
1755aa874dSnicm 
187bfbda14Stedu #include <sys/types.h>
1955aa874dSnicm #include <sys/stat.h>
200a39d05fStedu #include <sys/ioctl.h>
217bfbda14Stedu 
227bfbda14Stedu #include <limits.h>
237bfbda14Stedu #include <login_cap.h>
247bfbda14Stedu #include <bsd_auth.h>
2580d966b4Stedu #include <readpassphrase.h>
267bfbda14Stedu #include <string.h>
277bfbda14Stedu #include <stdio.h>
287bfbda14Stedu #include <stdlib.h>
297bfbda14Stedu #include <err.h>
307bfbda14Stedu #include <unistd.h>
317bfbda14Stedu #include <pwd.h>
327bfbda14Stedu #include <grp.h>
337bfbda14Stedu #include <syslog.h>
341aced516Stedu #include <errno.h>
350a39d05fStedu #include <fcntl.h>
367bfbda14Stedu 
377bfbda14Stedu #include "doas.h"
387bfbda14Stedu 
397bfbda14Stedu static void __dead
usage(void)407bfbda14Stedu usage(void)
417bfbda14Stedu {
420a39d05fStedu 	fprintf(stderr, "usage: doas [-Lns] [-a style] [-C config] [-u user]"
43881f6c5fSkn 	    " command [arg ...]\n");
447bfbda14Stedu 	exit(1);
457bfbda14Stedu }
467bfbda14Stedu 
477bfbda14Stedu static int
parseuid(const char * s,uid_t * uid)487bfbda14Stedu parseuid(const char *s, uid_t *uid)
497bfbda14Stedu {
507bfbda14Stedu 	struct passwd *pw;
517bfbda14Stedu 	const char *errstr;
527bfbda14Stedu 
537bfbda14Stedu 	if ((pw = getpwnam(s)) != NULL) {
547bfbda14Stedu 		*uid = pw->pw_uid;
55754f2b84Stedu 		if (*uid == UID_MAX)
56754f2b84Stedu 			return -1;
577bfbda14Stedu 		return 0;
587bfbda14Stedu 	}
59754f2b84Stedu 	*uid = strtonum(s, 0, UID_MAX - 1, &errstr);
607bfbda14Stedu 	if (errstr)
617bfbda14Stedu 		return -1;
627bfbda14Stedu 	return 0;
637bfbda14Stedu }
647bfbda14Stedu 
657bfbda14Stedu static int
uidcheck(const char * s,uid_t desired)667bfbda14Stedu uidcheck(const char *s, uid_t desired)
677bfbda14Stedu {
687bfbda14Stedu 	uid_t uid;
697bfbda14Stedu 
707bfbda14Stedu 	if (parseuid(s, &uid) != 0)
717bfbda14Stedu 		return -1;
727bfbda14Stedu 	if (uid != desired)
737bfbda14Stedu 		return -1;
747bfbda14Stedu 	return 0;
757bfbda14Stedu }
767bfbda14Stedu 
77a5e960b5Stedu static int
parsegid(const char * s,gid_t * gid)78a5e960b5Stedu parsegid(const char *s, gid_t *gid)
797bfbda14Stedu {
807bfbda14Stedu 	struct group *gr;
817bfbda14Stedu 	const char *errstr;
827bfbda14Stedu 
83a5e960b5Stedu 	if ((gr = getgrnam(s)) != NULL) {
84a5e960b5Stedu 		*gid = gr->gr_gid;
85754f2b84Stedu 		if (*gid == GID_MAX)
86754f2b84Stedu 			return -1;
87a5e960b5Stedu 		return 0;
88a5e960b5Stedu 	}
89754f2b84Stedu 	*gid = strtonum(s, 0, GID_MAX - 1, &errstr);
907bfbda14Stedu 	if (errstr)
917bfbda14Stedu 		return -1;
92a5e960b5Stedu 	return 0;
937bfbda14Stedu }
947bfbda14Stedu 
957bfbda14Stedu static int
match(uid_t uid,gid_t * groups,int ngroups,uid_t target,const char * cmd,const char ** cmdargs,struct rule * r)967bfbda14Stedu match(uid_t uid, gid_t *groups, int ngroups, uid_t target, const char *cmd,
97cb7cef4cSzhuk     const char **cmdargs, struct rule *r)
987bfbda14Stedu {
997bfbda14Stedu 	int i;
1007bfbda14Stedu 
1017bfbda14Stedu 	if (r->ident[0] == ':') {
102a5e960b5Stedu 		gid_t rgid;
103a5e960b5Stedu 		if (parsegid(r->ident + 1, &rgid) == -1)
1047bfbda14Stedu 			return 0;
1057bfbda14Stedu 		for (i = 0; i < ngroups; i++) {
1067bfbda14Stedu 			if (rgid == groups[i])
1077bfbda14Stedu 				break;
1087bfbda14Stedu 		}
1097bfbda14Stedu 		if (i == ngroups)
1107bfbda14Stedu 			return 0;
1117bfbda14Stedu 	} else {
1127bfbda14Stedu 		if (uidcheck(r->ident, uid) != 0)
1137bfbda14Stedu 			return 0;
1147bfbda14Stedu 	}
1157bfbda14Stedu 	if (r->target && uidcheck(r->target, target) != 0)
1167bfbda14Stedu 		return 0;
117cb7cef4cSzhuk 	if (r->cmd) {
118cb7cef4cSzhuk 		if (strcmp(r->cmd, cmd))
1197bfbda14Stedu 			return 0;
120cb7cef4cSzhuk 		if (r->cmdargs) {
121cb7cef4cSzhuk 			/* if arguments were given, they should match explicitly */
122cb7cef4cSzhuk 			for (i = 0; r->cmdargs[i]; i++) {
123cb7cef4cSzhuk 				if (!cmdargs[i])
124cb7cef4cSzhuk 					return 0;
125cb7cef4cSzhuk 				if (strcmp(r->cmdargs[i], cmdargs[i]))
126cb7cef4cSzhuk 					return 0;
127cb7cef4cSzhuk 			}
128cb7cef4cSzhuk 			if (cmdargs[i])
129cb7cef4cSzhuk 				return 0;
130cb7cef4cSzhuk 		}
131cb7cef4cSzhuk 	}
1327bfbda14Stedu 	return 1;
1337bfbda14Stedu }
1347bfbda14Stedu 
1357bfbda14Stedu static int
permit(uid_t uid,gid_t * groups,int ngroups,const struct rule ** lastr,uid_t target,const char * cmd,const char ** cmdargs)13694372f51Stedu permit(uid_t uid, gid_t *groups, int ngroups, const struct rule **lastr,
137cb7cef4cSzhuk     uid_t target, const char *cmd, const char **cmdargs)
1387bfbda14Stedu {
139618b6875Smillert 	size_t i;
1407bfbda14Stedu 
1417bfbda14Stedu 	*lastr = NULL;
1427bfbda14Stedu 	for (i = 0; i < nrules; i++) {
14322ac959bSderaadt 		if (match(uid, groups, ngroups, target, cmd,
14422ac959bSderaadt 		    cmdargs, rules[i]))
1457bfbda14Stedu 			*lastr = rules[i];
1467bfbda14Stedu 	}
1477bfbda14Stedu 	if (!*lastr)
148*38599afaStedu 		return -1;
149*38599afaStedu 	if ((*lastr)->action == PERMIT)
1507bfbda14Stedu 		return 0;
151*38599afaStedu 	return -1;
1527bfbda14Stedu }
1537bfbda14Stedu 
1547bfbda14Stedu static void
parseconfig(const char * filename,int checkperms)155588a95a3Szhuk parseconfig(const char *filename, int checkperms)
1567bfbda14Stedu {
1577bfbda14Stedu 	extern FILE *yyfp;
1584a73e7e2Stedu 	extern int yyparse(void);
15955aa874dSnicm 	struct stat sb;
1607bfbda14Stedu 
1617bfbda14Stedu 	yyfp = fopen(filename, "r");
162a4ee1cabSespie 	if (!yyfp)
163a4ee1cabSespie 		err(1, checkperms ? "doas is not enabled, %s" :
164a4ee1cabSespie 		    "could not open config file %s", filename);
16555aa874dSnicm 
166588a95a3Szhuk 	if (checkperms) {
16755aa874dSnicm 		if (fstat(fileno(yyfp), &sb) != 0)
16855aa874dSnicm 			err(1, "fstat(\"%s\")", filename);
16955aa874dSnicm 		if ((sb.st_mode & (S_IWGRP|S_IWOTH)) != 0)
17055aa874dSnicm 			errx(1, "%s is writable by group or other", filename);
17155aa874dSnicm 		if (sb.st_uid != 0)
17255aa874dSnicm 			errx(1, "%s is not owned by root", filename);
173588a95a3Szhuk 	}
17455aa874dSnicm 
175ae699b87Stb 	yyparse();
1767bfbda14Stedu 	fclose(yyfp);
177cbc92769Stobias 	if (parse_error)
1787c3b0d20Szhuk 		exit(1);
1797bfbda14Stedu }
1807bfbda14Stedu 
1817bfbda14Stedu static void __dead
checkconfig(const char * confpath,int argc,char ** argv,uid_t uid,gid_t * groups,int ngroups,uid_t target)182588a95a3Szhuk checkconfig(const char *confpath, int argc, char **argv,
183cac2eb3dStedu     uid_t uid, gid_t *groups, int ngroups, uid_t target)
184cac2eb3dStedu {
18594372f51Stedu 	const struct rule *rule;
186*38599afaStedu 	int rv;
187588a95a3Szhuk 
188588a95a3Szhuk 	setresuid(uid, uid, uid);
1890fe80ecaSkn 	if (pledge("stdio rpath getpw", NULL) == -1)
1900fe80ecaSkn 		err(1, "pledge");
191588a95a3Szhuk 	parseconfig(confpath, 0);
192588a95a3Szhuk 	if (!argc)
193588a95a3Szhuk 		exit(0);
194*38599afaStedu 	rv = permit(uid, groups, ngroups, &rule, target, argv[0],
195*38599afaStedu  	    (const char **)argv + 1);
196*38599afaStedu 	if (rv == 0) {
197588a95a3Szhuk 		printf("permit%s\n", (rule->options & NOPASS) ? " nopass" : "");
198cac2eb3dStedu 		exit(0);
199588a95a3Szhuk 	} else {
200588a95a3Szhuk 		printf("deny\n");
201cac2eb3dStedu 		exit(1);
202588a95a3Szhuk 	}
203588a95a3Szhuk }
204588a95a3Szhuk 
205960755fcSjcs static int
authuser_checkpass(char * myname,char * login_style)2068a9f00d3Smillert authuser_checkpass(char *myname, char *login_style)
207ec7e1211Stedu {
208ec7e1211Stedu 	char *challenge = NULL, *response, rbuf[1024], cbuf[128];
209ec7e1211Stedu 	auth_session_t *as;
210ec7e1211Stedu 
211ec7e1211Stedu 	if (!(as = auth_userchallenge(myname, login_style, "auth-doas",
212960755fcSjcs 	    &challenge))) {
213960755fcSjcs 		warnx("Authentication failed");
214960755fcSjcs 		return AUTH_FAILED;
215960755fcSjcs 	}
216ec7e1211Stedu 	if (!challenge) {
217ec7e1211Stedu 		char host[HOST_NAME_MAX + 1];
218ce8e0faaSderaadt 
219ec7e1211Stedu 		if (gethostname(host, sizeof(host)))
220ec7e1211Stedu 			snprintf(host, sizeof(host), "?");
221ec7e1211Stedu 		snprintf(cbuf, sizeof(cbuf),
222ec7e1211Stedu 		    "\rdoas (%.32s@%.32s) password: ", myname, host);
223ec7e1211Stedu 		challenge = cbuf;
224ec7e1211Stedu 	}
225ec7e1211Stedu 	response = readpassphrase(challenge, rbuf, sizeof(rbuf),
226ec7e1211Stedu 	    RPP_REQUIRE_TTY);
227ec7e1211Stedu 	if (response == NULL && errno == ENOTTY) {
228ec7e1211Stedu 		syslog(LOG_AUTHPRIV | LOG_NOTICE,
229ec7e1211Stedu 		    "tty required for %s", myname);
230ec7e1211Stedu 		errx(1, "a tty is required");
231ec7e1211Stedu 	}
232ec7e1211Stedu 	if (!auth_userresponse(as, response, 0)) {
233df2c0958Stedu 		explicit_bzero(rbuf, sizeof(rbuf));
234ec7e1211Stedu 		syslog(LOG_AUTHPRIV | LOG_NOTICE,
235ec7e1211Stedu 		    "failed auth for %s", myname);
236960755fcSjcs 		warnx("Authentication failed");
237960755fcSjcs 		return AUTH_FAILED;
238ec7e1211Stedu 	}
239ec7e1211Stedu 	explicit_bzero(rbuf, sizeof(rbuf));
2408a9f00d3Smillert 	return AUTH_OK;
2418a9f00d3Smillert }
2428a9f00d3Smillert 
2438a9f00d3Smillert static void
authuser(char * myname,char * login_style,int persist)2448a9f00d3Smillert authuser(char *myname, char *login_style, int persist)
2458a9f00d3Smillert {
2468a9f00d3Smillert 	int i, fd = -1;
2478a9f00d3Smillert 
2488a9f00d3Smillert 	if (persist)
2498a9f00d3Smillert 		fd = open("/dev/tty", O_RDWR);
2508a9f00d3Smillert 	if (fd != -1) {
2518a9f00d3Smillert 		if (ioctl(fd, TIOCCHKVERAUTH) == 0)
2528a9f00d3Smillert 			goto good;
2538a9f00d3Smillert 	}
2548a9f00d3Smillert 	for (i = 0; i < AUTH_RETRIES; i++) {
2558a9f00d3Smillert 		if (authuser_checkpass(myname, login_style) == AUTH_OK)
2568a9f00d3Smillert 			goto good;
2578a9f00d3Smillert 	}
2588a9f00d3Smillert 	exit(1);
2590a39d05fStedu good:
2600a39d05fStedu 	if (fd != -1) {
261b2e46a46Stedu 		int secs = 5 * 60;
2620a39d05fStedu 		ioctl(fd, TIOCSETVERAUTH, &secs);
2630a39d05fStedu 		close(fd);
2640a39d05fStedu 	}
265ec7e1211Stedu }
266ec7e1211Stedu 
2677bfbda14Stedu int
unveilcommands(const char * ipath,const char * cmd)2680d5eb34fSderaadt unveilcommands(const char *ipath, const char *cmd)
2690d5eb34fSderaadt {
2700d5eb34fSderaadt 	char *path = NULL, *p;
2710d5eb34fSderaadt 	int unveils = 0;
2720d5eb34fSderaadt 
2730d5eb34fSderaadt 	if (strchr(cmd, '/') != NULL) {
2740d5eb34fSderaadt 		if (unveil(cmd, "x") != -1)
2750d5eb34fSderaadt 			unveils++;
2760d5eb34fSderaadt 		goto done;
2770d5eb34fSderaadt 	}
2780d5eb34fSderaadt 
2790d5eb34fSderaadt 	if (!ipath) {
2800d5eb34fSderaadt 		errno = ENOENT;
2810d5eb34fSderaadt 		goto done;
2820d5eb34fSderaadt 	}
2830d5eb34fSderaadt 	path = strdup(ipath);
2840d5eb34fSderaadt 	if (!path) {
2850d5eb34fSderaadt 		errno = ENOENT;
2860d5eb34fSderaadt 		goto done;
2870d5eb34fSderaadt 	}
2880d5eb34fSderaadt 	for (p = path; p && *p; ) {
2890d5eb34fSderaadt 		char buf[PATH_MAX];
2900d5eb34fSderaadt 		char *cp = strsep(&p, ":");
2910d5eb34fSderaadt 
2920d5eb34fSderaadt 		if (cp) {
2930d5eb34fSderaadt 			int r = snprintf(buf, sizeof buf, "%s/%s", cp, cmd);
294515e489cSderaadt 			if (r >= 0 && r < sizeof buf) {
2950d5eb34fSderaadt 				if (unveil(buf, "x") != -1)
2960d5eb34fSderaadt 					unveils++;
2970d5eb34fSderaadt 			}
2980d5eb34fSderaadt 		}
2990d5eb34fSderaadt 	}
3000d5eb34fSderaadt done:
3010d5eb34fSderaadt 	free(path);
3020d5eb34fSderaadt 	return (unveils);
3030d5eb34fSderaadt }
3040d5eb34fSderaadt 
3050d5eb34fSderaadt int
main(int argc,char ** argv)30687a59335Smartijn main(int argc, char **argv)
3077bfbda14Stedu {
308dd06eb07Sderaadt 	const char *safepath = "/bin:/sbin:/usr/bin:/usr/sbin:"
309dd06eb07Sderaadt 	    "/usr/local/bin:/usr/local/sbin";
310588a95a3Szhuk 	const char *confpath = NULL;
3118e3a9410Snicm 	char *shargv[] = { NULL, NULL };
3128e3a9410Snicm 	char *sh;
313b09050b2Stedu 	const char *p;
31403cee50eStedu 	const char *cmd;
31503cee50eStedu 	char cmdline[LINE_MAX];
316f0b8e6a9Stedu 	char mypwbuf[_PW_BUF_LEN], targpwbuf[_PW_BUF_LEN];
317f0b8e6a9Stedu 	struct passwd mypwstore, targpwstore;
318f0b8e6a9Stedu 	struct passwd *mypw, *targpw;
31994372f51Stedu 	const struct rule *rule;
32003cee50eStedu 	uid_t uid;
32103cee50eStedu 	uid_t target = 0;
32203cee50eStedu 	gid_t groups[NGROUPS_MAX + 1];
32303cee50eStedu 	int ngroups;
324f0b8e6a9Stedu 	int i, ch, rv;
32503cee50eStedu 	int sflag = 0;
32617356784Sespie 	int nflag = 0;
327add8871aSdoug 	char cwdpath[PATH_MAX];
328add8871aSdoug 	const char *cwd;
329a9ed2e03Ssthen 	char *login_style = NULL;
33087a59335Smartijn 	char **envp;
3317bfbda14Stedu 
3325de5e581Stedu 	setprogname("doas");
3335de5e581Stedu 
334af293d1aStedu 	closefrom(STDERR_FILENO + 1);
335af293d1aStedu 
33617356784Sespie 	uid = getuid();
337930540c5Stedu 
3380a39d05fStedu 	while ((ch = getopt(argc, argv, "a:C:Lnsu:")) != -1) {
3397bfbda14Stedu 		switch (ch) {
340a9ed2e03Ssthen 		case 'a':
341a9ed2e03Ssthen 			login_style = optarg;
342a9ed2e03Ssthen 			break;
343d5db240dStedu 		case 'C':
344588a95a3Szhuk 			confpath = optarg;
345588a95a3Szhuk 			break;
3460a39d05fStedu 		case 'L':
3470a39d05fStedu 			i = open("/dev/tty", O_RDWR);
3480a39d05fStedu 			if (i != -1)
3490a39d05fStedu 				ioctl(i, TIOCCLRVERAUTH);
350910a0be1Stedu 			exit(i == -1);
3517bfbda14Stedu 		case 'u':
3527bfbda14Stedu 			if (parseuid(optarg, &target) != 0)
3537bfbda14Stedu 				errx(1, "unknown user");
3547bfbda14Stedu 			break;
35517356784Sespie 		case 'n':
35617356784Sespie 			nflag = 1;
35717356784Sespie 			break;
3588e3a9410Snicm 		case 's':
3598e3a9410Snicm 			sflag = 1;
3608e3a9410Snicm 			break;
3617bfbda14Stedu 		default:
3627bfbda14Stedu 			usage();
3637bfbda14Stedu 			break;
3647bfbda14Stedu 		}
3657bfbda14Stedu 	}
3667bfbda14Stedu 	argv += optind;
3677bfbda14Stedu 	argc -= optind;
3687bfbda14Stedu 
369588a95a3Szhuk 	if (confpath) {
370588a95a3Szhuk 		if (sflag)
371588a95a3Szhuk 			usage();
372588a95a3Szhuk 	} else if ((!sflag && !argc) || (sflag && argc))
3737bfbda14Stedu 		usage();
3747bfbda14Stedu 
375f0b8e6a9Stedu 	rv = getpwuid_r(uid, &mypwstore, mypwbuf, sizeof(mypwbuf), &mypw);
376b59c1698Stedu 	if (rv != 0)
377f0b8e6a9Stedu 		err(1, "getpwuid_r failed");
378b59c1698Stedu 	if (mypw == NULL)
379b59c1698Stedu 		errx(1, "no passwd entry for self");
3807bfbda14Stedu 	ngroups = getgroups(NGROUPS_MAX, groups);
3817bfbda14Stedu 	if (ngroups == -1)
3827bfbda14Stedu 		err(1, "can't get groups");
3837bfbda14Stedu 	groups[ngroups++] = getgid();
3847bfbda14Stedu 
3858e3a9410Snicm 	if (sflag) {
3868e3a9410Snicm 		sh = getenv("SHELL");
387ff37429cSzhuk 		if (sh == NULL || *sh == '\0') {
388b59c1698Stedu 			shargv[0] = mypw->pw_shell;
389ff37429cSzhuk 		} else
3908e3a9410Snicm 			shargv[0] = sh;
3918e3a9410Snicm 		argv = shargv;
3928e3a9410Snicm 		argc = 1;
3938e3a9410Snicm 	}
3948e3a9410Snicm 
395cac2eb3dStedu 	if (confpath) {
3960fe80ecaSkn 		if (pledge("stdio rpath getpw id", NULL) == -1)
3970fe80ecaSkn 			err(1, "pledge");
398cac2eb3dStedu 		checkconfig(confpath, argc, argv, uid, groups, ngroups,
399cac2eb3dStedu 		    target);
400cac2eb3dStedu 		exit(1);	/* fail safe */
401cac2eb3dStedu 	}
402cac2eb3dStedu 
403bc35909dStedu 	if (geteuid())
404bc35909dStedu 		errx(1, "not installed setuid");
405bc35909dStedu 
406588a95a3Szhuk 	parseconfig("/etc/doas.conf", 1);
407588a95a3Szhuk 
4082626c77eSzhuk 	/* cmdline is used only for logging, no need to abort on truncate */
409952479b6Szhuk 	(void)strlcpy(cmdline, argv[0], sizeof(cmdline));
4108e3a9410Snicm 	for (i = 1; i < argc; i++) {
4118e3a9410Snicm 		if (strlcat(cmdline, " ", sizeof(cmdline)) >= sizeof(cmdline))
4122626c77eSzhuk 			break;
4138e3a9410Snicm 		if (strlcat(cmdline, argv[i], sizeof(cmdline)) >= sizeof(cmdline))
4142626c77eSzhuk 			break;
4158e3a9410Snicm 	}
4168e3a9410Snicm 
4172626c77eSzhuk 	cmd = argv[0];
418*38599afaStedu 	rv = permit(uid, groups, ngroups, &rule, target, cmd,
419*38599afaStedu 	    (const char **)argv + 1);
420*38599afaStedu 	if (rv != 0) {
421dd06eb07Sderaadt 		syslog(LOG_AUTHPRIV | LOG_NOTICE,
42291fb18fcSkn 		    "command not permitted for %s: %s", mypw->pw_name, cmdline);
4233f31dad3Stedu 		errc(1, EPERM, NULL);
4247bfbda14Stedu 	}
4257bfbda14Stedu 
4267bfbda14Stedu 	if (!(rule->options & NOPASS)) {
42717356784Sespie 		if (nflag)
4284d7faafbSmartijn 			errx(1, "Authentication required");
42980d966b4Stedu 
4308a9f00d3Smillert 		authuser(mypw->pw_name, login_style, rule->options & PERSIST);
4317bfbda14Stedu 	}
4327aef62b2Sderaadt 
433b09050b2Stedu 	if ((p = getenv("PATH")) != NULL)
434b09050b2Stedu 		formerpath = strdup(p);
435b09050b2Stedu 	if (formerpath == NULL)
436b09050b2Stedu 		formerpath = "";
437b09050b2Stedu 
438bc5a8259Sbeck 	if (unveil(_PATH_LOGIN_CONF, "r") == -1)
439bc5a8259Sbeck 		err(1, "unveil %s", _PATH_LOGIN_CONF);
440bc5a8259Sbeck 	if (unveil(_PATH_LOGIN_CONF ".db", "r") == -1)
441bc5a8259Sbeck 		err(1, "unveil %s.db", _PATH_LOGIN_CONF);
4426699d7aaSrobert 	if (unveil(_PATH_LOGIN_CONF_D, "r") == -1)
4436699d7aaSrobert 		err(1, "unveil %s", _PATH_LOGIN_CONF_D);
4440d5eb34fSderaadt 	if (rule->cmd) {
4450d5eb34fSderaadt 		if (setenv("PATH", safepath, 1) == -1)
4460d5eb34fSderaadt 			err(1, "failed to set PATH '%s'", safepath);
4470d5eb34fSderaadt 	}
4480d5eb34fSderaadt 	if (unveilcommands(getenv("PATH"), cmd) == 0)
4490d5eb34fSderaadt 		goto fail;
4500d5eb34fSderaadt 
4517aef62b2Sderaadt 	if (pledge("stdio rpath getpw exec id", NULL) == -1)
4527aef62b2Sderaadt 		err(1, "pledge");
4537aef62b2Sderaadt 
454f0b8e6a9Stedu 	rv = getpwuid_r(target, &targpwstore, targpwbuf, sizeof(targpwbuf), &targpw);
455b59c1698Stedu 	if (rv != 0)
456b59c1698Stedu 		err(1, "getpwuid_r failed");
457b59c1698Stedu 	if (targpw == NULL)
4587bfbda14Stedu 		errx(1, "no passwd entry for target");
4597aef62b2Sderaadt 
460814cff54Stb 	if (setusercontext(NULL, targpw, target, LOGIN_SETGROUP |
461814cff54Stb 	    LOGIN_SETPATH |
462814cff54Stb 	    LOGIN_SETPRIORITY | LOGIN_SETRESOURCES | LOGIN_SETUMASK |
463814cff54Stb 	    LOGIN_SETUSER | LOGIN_SETENV | LOGIN_SETRTABLE) != 0)
4647bfbda14Stedu 		errx(1, "failed to set user context for target");
4657bfbda14Stedu 
4667aef62b2Sderaadt 	if (pledge("stdio rpath exec", NULL) == -1)
4677aef62b2Sderaadt 		err(1, "pledge");
4687aef62b2Sderaadt 
469add8871aSdoug 	if (getcwd(cwdpath, sizeof(cwdpath)) == NULL)
470add8871aSdoug 		cwd = "(failed)";
471add8871aSdoug 	else
472add8871aSdoug 		cwd = cwdpath;
473add8871aSdoug 
4747aef62b2Sderaadt 	if (pledge("stdio exec", NULL) == -1)
4757aef62b2Sderaadt 		err(1, "pledge");
4767aef62b2Sderaadt 
477d4bf2b56Skn 	if (!(rule->options & NOLOG)) {
478d4bf2b56Skn 		syslog(LOG_AUTHPRIV | LOG_INFO,
479d4bf2b56Skn 		    "%s ran command %s as %s from %s",
480b59c1698Stedu 		    mypw->pw_name, cmdline, targpw->pw_name, cwd);
481d4bf2b56Skn 	}
482add8871aSdoug 
483e327fa4eStedu 	envp = prepenv(rule, mypw, targpw);
48492e88ce8Stedu 
4854e86a82bStedu 	/* setusercontext set path for the next process, so reset it for us */
486e327fa4eStedu 	if (rule->cmd) {
487e327fa4eStedu 		if (setenv("PATH", safepath, 1) == -1)
488e327fa4eStedu 			err(1, "failed to set PATH '%s'", safepath);
4894e86a82bStedu 	} else {
4904e86a82bStedu 		if (setenv("PATH", formerpath, 1) == -1)
4914e86a82bStedu 			err(1, "failed to set PATH '%s'", formerpath);
492e327fa4eStedu 	}
4937bfbda14Stedu 	execvpe(cmd, argv, envp);
4940d5eb34fSderaadt fail:
4951aced516Stedu 	if (errno == ENOENT)
4961aced516Stedu 		errx(1, "%s: command not found", cmd);
4977bfbda14Stedu 	err(1, "%s", cmd);
4987bfbda14Stedu }
499