xref: /titanic_52/usr/src/cmd/pfexecd/pfexecd.c (revision b01b59e369b297df1b370a7ef94d2b9230d04452)
1134a1f4eSCasper H.S. Dik /*
2134a1f4eSCasper H.S. Dik  * CDDL HEADER START
3134a1f4eSCasper H.S. Dik  *
4134a1f4eSCasper H.S. Dik  * The contents of this file are subject to the terms of the
5134a1f4eSCasper H.S. Dik  * Common Development and Distribution License (the "License").
6134a1f4eSCasper H.S. Dik  * You may not use this file except in compliance with the License.
7134a1f4eSCasper H.S. Dik  *
8134a1f4eSCasper H.S. Dik  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9134a1f4eSCasper H.S. Dik  * or http://www.opensolaris.org/os/licensing.
10134a1f4eSCasper H.S. Dik  * See the License for the specific language governing permissions
11134a1f4eSCasper H.S. Dik  * and limitations under the License.
12134a1f4eSCasper H.S. Dik  *
13134a1f4eSCasper H.S. Dik  * When distributing Covered Code, include this CDDL HEADER in each
14134a1f4eSCasper H.S. Dik  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15134a1f4eSCasper H.S. Dik  * If applicable, add the following below this CDDL HEADER, with the
16134a1f4eSCasper H.S. Dik  * fields enclosed by brackets "[]" replaced with your own identifying
17134a1f4eSCasper H.S. Dik  * information: Portions Copyright [yyyy] [name of copyright owner]
18134a1f4eSCasper H.S. Dik  *
19134a1f4eSCasper H.S. Dik  * CDDL HEADER END
20134a1f4eSCasper H.S. Dik  *
21134a1f4eSCasper H.S. Dik  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
22*b01b59e3SRobert Mustacchi  * Copyright 2015, Joyent, Inc.
23134a1f4eSCasper H.S. Dik  */
24134a1f4eSCasper H.S. Dik 
25134a1f4eSCasper H.S. Dik #define	_POSIX_PTHREAD_SEMANTICS 1
26134a1f4eSCasper H.S. Dik 
27134a1f4eSCasper H.S. Dik #include <sys/param.h>
28134a1f4eSCasper H.S. Dik #include <sys/klpd.h>
29134a1f4eSCasper H.S. Dik #include <sys/syscall.h>
30134a1f4eSCasper H.S. Dik #include <sys/systeminfo.h>
31134a1f4eSCasper H.S. Dik 
32134a1f4eSCasper H.S. Dik #include <alloca.h>
33134a1f4eSCasper H.S. Dik #include <ctype.h>
34134a1f4eSCasper H.S. Dik #include <deflt.h>
35134a1f4eSCasper H.S. Dik #include <door.h>
36134a1f4eSCasper H.S. Dik #include <errno.h>
37134a1f4eSCasper H.S. Dik #include <grp.h>
38134a1f4eSCasper H.S. Dik #include <priv.h>
39134a1f4eSCasper H.S. Dik #include <pwd.h>
40134a1f4eSCasper H.S. Dik #include <regex.h>
41134a1f4eSCasper H.S. Dik #include <secdb.h>
42134a1f4eSCasper H.S. Dik #include <signal.h>
43134a1f4eSCasper H.S. Dik #include <stdio.h>
44134a1f4eSCasper H.S. Dik #include <stdlib.h>
45134a1f4eSCasper H.S. Dik #include <string.h>
46134a1f4eSCasper H.S. Dik #include <syslog.h>
47134a1f4eSCasper H.S. Dik #include <unistd.h>
48134a1f4eSCasper H.S. Dik 
49134a1f4eSCasper H.S. Dik #include <auth_attr.h>
50134a1f4eSCasper H.S. Dik #include <exec_attr.h>
51134a1f4eSCasper H.S. Dik #include <prof_attr.h>
52134a1f4eSCasper H.S. Dik #include <user_attr.h>
53134a1f4eSCasper H.S. Dik 
54134a1f4eSCasper H.S. Dik static int doorfd = -1;
55134a1f4eSCasper H.S. Dik 
56134a1f4eSCasper H.S. Dik static size_t repsz, setsz;
57134a1f4eSCasper H.S. Dik 
58134a1f4eSCasper H.S. Dik static uid_t get_uid(const char *, boolean_t *, char *);
59134a1f4eSCasper H.S. Dik static gid_t get_gid(const char *, boolean_t *, char *);
60134a1f4eSCasper H.S. Dik static priv_set_t *get_privset(const char *, boolean_t *, char *);
61134a1f4eSCasper H.S. Dik static priv_set_t *get_granted_privs(uid_t);
62134a1f4eSCasper H.S. Dik 
63134a1f4eSCasper H.S. Dik /*
64134a1f4eSCasper H.S. Dik  * Remove the isaexec path of an executable if we can't find the
65134a1f4eSCasper H.S. Dik  * executable at the first attempt.
66134a1f4eSCasper H.S. Dik  */
67134a1f4eSCasper H.S. Dik 
68134a1f4eSCasper H.S. Dik static regex_t regc;
69134a1f4eSCasper H.S. Dik static boolean_t cansplice = B_TRUE;
70134a1f4eSCasper H.S. Dik 
71134a1f4eSCasper H.S. Dik static void
72134a1f4eSCasper H.S. Dik init_isa_regex(void)
73134a1f4eSCasper H.S. Dik {
74134a1f4eSCasper H.S. Dik 	char *isalist;
75134a1f4eSCasper H.S. Dik 	size_t isalen = 255;		/* wild guess */
76134a1f4eSCasper H.S. Dik 	size_t len;
77134a1f4eSCasper H.S. Dik 	long ret;
78134a1f4eSCasper H.S. Dik 	char *regexpr;
79134a1f4eSCasper H.S. Dik 	char *p;
80134a1f4eSCasper H.S. Dik 
81134a1f4eSCasper H.S. Dik 	/*
82134a1f4eSCasper H.S. Dik 	 * Extract the isalist(5) for userland from the kernel.
83134a1f4eSCasper H.S. Dik 	 */
84134a1f4eSCasper H.S. Dik 	isalist = malloc(isalen);
85134a1f4eSCasper H.S. Dik 	do {
86134a1f4eSCasper H.S. Dik 		ret = sysinfo(SI_ISALIST, isalist, isalen);
87134a1f4eSCasper H.S. Dik 		if (ret == -1l) {
88134a1f4eSCasper H.S. Dik 			free(isalist);
89134a1f4eSCasper H.S. Dik 			return;
90134a1f4eSCasper H.S. Dik 		}
91134a1f4eSCasper H.S. Dik 		if (ret > isalen) {
92134a1f4eSCasper H.S. Dik 			isalen = ret;
93134a1f4eSCasper H.S. Dik 			isalist = realloc(isalist, isalen);
94134a1f4eSCasper H.S. Dik 		} else
95134a1f4eSCasper H.S. Dik 			break;
96134a1f4eSCasper H.S. Dik 	} while (isalist != NULL);
97134a1f4eSCasper H.S. Dik 
98134a1f4eSCasper H.S. Dik 
99134a1f4eSCasper H.S. Dik 	if (isalist == NULL)
100134a1f4eSCasper H.S. Dik 		return;
101134a1f4eSCasper H.S. Dik 
102134a1f4eSCasper H.S. Dik 	/* allocate room for the regex + (/())/[^/]*$ + needed \\. */
103134a1f4eSCasper H.S. Dik #define	LEFT	"(/("
104134a1f4eSCasper H.S. Dik #define	RIGHT	"))/[^/]*$"
105134a1f4eSCasper H.S. Dik 
106134a1f4eSCasper H.S. Dik 	regexpr = alloca(ret * 2 + sizeof (LEFT RIGHT));
107134a1f4eSCasper H.S. Dik 	(void) strcpy(regexpr, LEFT);
108134a1f4eSCasper H.S. Dik 	len = strlen(regexpr);
109134a1f4eSCasper H.S. Dik 
110134a1f4eSCasper H.S. Dik 	for (p = isalist; *p; p++) {
111134a1f4eSCasper H.S. Dik 		switch (*p) {
112134a1f4eSCasper H.S. Dik 		case '+':
113134a1f4eSCasper H.S. Dik 		case '|':
114134a1f4eSCasper H.S. Dik 		case '*':
115134a1f4eSCasper H.S. Dik 		case '[':
116134a1f4eSCasper H.S. Dik 		case ']':
117134a1f4eSCasper H.S. Dik 		case '{':
118134a1f4eSCasper H.S. Dik 		case '}':
119134a1f4eSCasper H.S. Dik 		case '\\':
120134a1f4eSCasper H.S. Dik 			regexpr[len++] = '\\';
121134a1f4eSCasper H.S. Dik 		default:
122134a1f4eSCasper H.S. Dik 			regexpr[len++] = *p;
123134a1f4eSCasper H.S. Dik 			break;
124134a1f4eSCasper H.S. Dik 		case ' ':
125134a1f4eSCasper H.S. Dik 		case '\t':
126134a1f4eSCasper H.S. Dik 			regexpr[len++] = '|';
127134a1f4eSCasper H.S. Dik 			break;
128134a1f4eSCasper H.S. Dik 		}
129134a1f4eSCasper H.S. Dik 	}
130134a1f4eSCasper H.S. Dik 
131134a1f4eSCasper H.S. Dik 	free(isalist);
132134a1f4eSCasper H.S. Dik 	regexpr[len] = '\0';
133134a1f4eSCasper H.S. Dik 	(void) strcat(regexpr, RIGHT);
134134a1f4eSCasper H.S. Dik 
135134a1f4eSCasper H.S. Dik 	if (regcomp(&regc, regexpr, REG_EXTENDED) != 0)
136134a1f4eSCasper H.S. Dik 		return;
137134a1f4eSCasper H.S. Dik 
138134a1f4eSCasper H.S. Dik 	cansplice = B_TRUE;
139134a1f4eSCasper H.S. Dik }
140134a1f4eSCasper H.S. Dik 
141134a1f4eSCasper H.S. Dik #define	NMATCH	2
142134a1f4eSCasper H.S. Dik 
143134a1f4eSCasper H.S. Dik static boolean_t
144134a1f4eSCasper H.S. Dik removeisapath(char *path)
145134a1f4eSCasper H.S. Dik {
146134a1f4eSCasper H.S. Dik 	regmatch_t match[NMATCH];
147134a1f4eSCasper H.S. Dik 
148134a1f4eSCasper H.S. Dik 	if (!cansplice || regexec(&regc, path, NMATCH, match, 0) != 0)
149134a1f4eSCasper H.S. Dik 		return (B_FALSE);
150134a1f4eSCasper H.S. Dik 
151134a1f4eSCasper H.S. Dik 	/*
152134a1f4eSCasper H.S. Dik 	 * The first match includes the whole matched expression including the
153134a1f4eSCasper H.S. Dik 	 * end of the string.  The second match includes the "/" + "isa" and
154134a1f4eSCasper H.S. Dik 	 * that is the part we need to remove.
155134a1f4eSCasper H.S. Dik 	 */
156134a1f4eSCasper H.S. Dik 
157134a1f4eSCasper H.S. Dik 	if (match[1].rm_so == -1)
158134a1f4eSCasper H.S. Dik 		return (B_FALSE);
159134a1f4eSCasper H.S. Dik 
160134a1f4eSCasper H.S. Dik 	/* match[0].rm_eo == strlen(path) */
161134a1f4eSCasper H.S. Dik 	(void) memmove(path + match[1].rm_so, path + match[1].rm_eo,
162134a1f4eSCasper H.S. Dik 	    match[0].rm_eo - match[1].rm_eo + 1);
163134a1f4eSCasper H.S. Dik 
164134a1f4eSCasper H.S. Dik 	return (B_TRUE);
165134a1f4eSCasper H.S. Dik }
166134a1f4eSCasper H.S. Dik 
167134a1f4eSCasper H.S. Dik static int
168134a1f4eSCasper H.S. Dik register_pfexec(int fd)
169134a1f4eSCasper H.S. Dik {
170134a1f4eSCasper H.S. Dik 	int ret = syscall(SYS_privsys, PRIVSYS_PFEXEC_REG, fd);
171134a1f4eSCasper H.S. Dik 
172134a1f4eSCasper H.S. Dik 	return (ret);
173134a1f4eSCasper H.S. Dik }
174134a1f4eSCasper H.S. Dik 
175134a1f4eSCasper H.S. Dik /* ARGSUSED */
176134a1f4eSCasper H.S. Dik static void
177134a1f4eSCasper H.S. Dik unregister_pfexec(int sig)
178134a1f4eSCasper H.S. Dik {
179134a1f4eSCasper H.S. Dik 	if (doorfd != -1)
180134a1f4eSCasper H.S. Dik 		(void) syscall(SYS_privsys, PRIVSYS_PFEXEC_UNREG, doorfd);
181134a1f4eSCasper H.S. Dik 	_exit(0);
182134a1f4eSCasper H.S. Dik }
183134a1f4eSCasper H.S. Dik 
184134a1f4eSCasper H.S. Dik static int
185134a1f4eSCasper H.S. Dik alldigits(const char *s)
186134a1f4eSCasper H.S. Dik {
187134a1f4eSCasper H.S. Dik 	int c;
188134a1f4eSCasper H.S. Dik 
189134a1f4eSCasper H.S. Dik 	if (*s == '\0')
190134a1f4eSCasper H.S. Dik 		return (0);
191134a1f4eSCasper H.S. Dik 
192134a1f4eSCasper H.S. Dik 	while ((c = *s++) != '\0') {
193134a1f4eSCasper H.S. Dik 		if (!isdigit(c)) {
194134a1f4eSCasper H.S. Dik 			return (0);
195134a1f4eSCasper H.S. Dik 		}
196134a1f4eSCasper H.S. Dik 	}
197134a1f4eSCasper H.S. Dik 
198134a1f4eSCasper H.S. Dik 	return (1);
199134a1f4eSCasper H.S. Dik }
200134a1f4eSCasper H.S. Dik 
201134a1f4eSCasper H.S. Dik static uid_t
202134a1f4eSCasper H.S. Dik get_uid(const char *v, boolean_t *ok, char *path)
203134a1f4eSCasper H.S. Dik {
204134a1f4eSCasper H.S. Dik 	struct passwd *pwd, pwdm;
205134a1f4eSCasper H.S. Dik 	char buf[1024];
206134a1f4eSCasper H.S. Dik 
207134a1f4eSCasper H.S. Dik 	if (getpwnam_r(v, &pwdm, buf, sizeof (buf), &pwd) == 0 && pwd != NULL)
208134a1f4eSCasper H.S. Dik 		return (pwd->pw_uid);
209134a1f4eSCasper H.S. Dik 
210134a1f4eSCasper H.S. Dik 	if (alldigits(v))
211134a1f4eSCasper H.S. Dik 		return (atoi(v));
212134a1f4eSCasper H.S. Dik 
213134a1f4eSCasper H.S. Dik 	*ok = B_FALSE;
214134a1f4eSCasper H.S. Dik 	syslog(LOG_ERR, "%s: %s: unknown username\n", path, v);
215134a1f4eSCasper H.S. Dik 	return ((uid_t)-1);
216134a1f4eSCasper H.S. Dik }
217134a1f4eSCasper H.S. Dik 
218134a1f4eSCasper H.S. Dik static uid_t
219134a1f4eSCasper H.S. Dik get_gid(const char *v, boolean_t *ok, char *path)
220134a1f4eSCasper H.S. Dik {
221134a1f4eSCasper H.S. Dik 	struct group *grp, grpm;
222134a1f4eSCasper H.S. Dik 	char buf[1024];
223134a1f4eSCasper H.S. Dik 
224134a1f4eSCasper H.S. Dik 	if (getgrnam_r(v, &grpm, buf, sizeof (buf), &grp) == 0 && grp != NULL)
225134a1f4eSCasper H.S. Dik 		return (grp->gr_gid);
226134a1f4eSCasper H.S. Dik 
227134a1f4eSCasper H.S. Dik 	if (alldigits(v))
228134a1f4eSCasper H.S. Dik 		return (atoi(v));
229134a1f4eSCasper H.S. Dik 
230134a1f4eSCasper H.S. Dik 	*ok = B_FALSE;
231134a1f4eSCasper H.S. Dik 	syslog(LOG_ERR, "%s: %s: unknown groupname\n", path, v);
232134a1f4eSCasper H.S. Dik 	return ((gid_t)-1);
233134a1f4eSCasper H.S. Dik }
234134a1f4eSCasper H.S. Dik 
235134a1f4eSCasper H.S. Dik static priv_set_t *
236134a1f4eSCasper H.S. Dik get_privset(const char *s, boolean_t *ok, char *path)
237134a1f4eSCasper H.S. Dik {
238134a1f4eSCasper H.S. Dik 	priv_set_t *res;
239134a1f4eSCasper H.S. Dik 
240134a1f4eSCasper H.S. Dik 	if ((res = priv_str_to_set(s, ",", NULL)) == NULL) {
241134a1f4eSCasper H.S. Dik 		syslog(LOG_ERR, "%s: %s: bad privilege set\n", path, s);
242134a1f4eSCasper H.S. Dik 		if (ok != NULL)
243134a1f4eSCasper H.S. Dik 			*ok = B_FALSE;
244134a1f4eSCasper H.S. Dik 	}
245134a1f4eSCasper H.S. Dik 	return (res);
246134a1f4eSCasper H.S. Dik }
247134a1f4eSCasper H.S. Dik 
248134a1f4eSCasper H.S. Dik /*ARGSUSED*/
249134a1f4eSCasper H.S. Dik static int
250134a1f4eSCasper H.S. Dik ggp_callback(const char *prof, kva_t *attr, void *ctxt, void *vres)
251134a1f4eSCasper H.S. Dik {
252134a1f4eSCasper H.S. Dik 	priv_set_t *res = vres;
253134a1f4eSCasper H.S. Dik 	char *privs;
254134a1f4eSCasper H.S. Dik 
255134a1f4eSCasper H.S. Dik 	if (attr == NULL)
256134a1f4eSCasper H.S. Dik 		return (0);
257134a1f4eSCasper H.S. Dik 
258134a1f4eSCasper H.S. Dik 	/* get privs from this profile */
259134a1f4eSCasper H.S. Dik 	privs = kva_match(attr, PROFATTR_PRIVS_KW);
260134a1f4eSCasper H.S. Dik 	if (privs != NULL) {
261134a1f4eSCasper H.S. Dik 		priv_set_t *tmp = priv_str_to_set(privs, ",", NULL);
262134a1f4eSCasper H.S. Dik 		if (tmp != NULL) {
263134a1f4eSCasper H.S. Dik 			priv_union(tmp, res);
264134a1f4eSCasper H.S. Dik 			priv_freeset(tmp);
265134a1f4eSCasper H.S. Dik 		}
266134a1f4eSCasper H.S. Dik 	}
267134a1f4eSCasper H.S. Dik 
268134a1f4eSCasper H.S. Dik 	return (0);
269134a1f4eSCasper H.S. Dik }
270134a1f4eSCasper H.S. Dik 
271134a1f4eSCasper H.S. Dik /*
272134a1f4eSCasper H.S. Dik  * This routine exists on failure and returns NULL if no granted privileges
273134a1f4eSCasper H.S. Dik  * are set.
274134a1f4eSCasper H.S. Dik  */
275134a1f4eSCasper H.S. Dik static priv_set_t *
276134a1f4eSCasper H.S. Dik get_granted_privs(uid_t uid)
277134a1f4eSCasper H.S. Dik {
278134a1f4eSCasper H.S. Dik 	priv_set_t *res;
279134a1f4eSCasper H.S. Dik 	struct passwd *pwd, pwdm;
280134a1f4eSCasper H.S. Dik 	char buf[1024];
281134a1f4eSCasper H.S. Dik 
282134a1f4eSCasper H.S. Dik 	if (getpwuid_r(uid, &pwdm, buf, sizeof (buf), &pwd) != 0 || pwd == NULL)
283134a1f4eSCasper H.S. Dik 		return (NULL);
284134a1f4eSCasper H.S. Dik 
285134a1f4eSCasper H.S. Dik 	res = priv_allocset();
286134a1f4eSCasper H.S. Dik 	if (res == NULL)
287134a1f4eSCasper H.S. Dik 		return (NULL);
288134a1f4eSCasper H.S. Dik 
289134a1f4eSCasper H.S. Dik 	priv_emptyset(res);
290134a1f4eSCasper H.S. Dik 
291134a1f4eSCasper H.S. Dik 	(void) _enum_profs(pwd->pw_name, ggp_callback, NULL, res);
292134a1f4eSCasper H.S. Dik 
293134a1f4eSCasper H.S. Dik 	return (res);
294134a1f4eSCasper H.S. Dik }
295134a1f4eSCasper H.S. Dik 
296134a1f4eSCasper H.S. Dik static void
297134a1f4eSCasper H.S. Dik callback_forced_privs(pfexec_arg_t *pap)
298134a1f4eSCasper H.S. Dik {
299134a1f4eSCasper H.S. Dik 	execattr_t *exec;
300134a1f4eSCasper H.S. Dik 	char *value;
301134a1f4eSCasper H.S. Dik 	priv_set_t *fset;
302134a1f4eSCasper H.S. Dik 	void *res = alloca(setsz);
303134a1f4eSCasper H.S. Dik 
304134a1f4eSCasper H.S. Dik 	/* Empty set signifies no forced privileges. */
305134a1f4eSCasper H.S. Dik 	priv_emptyset(res);
306134a1f4eSCasper H.S. Dik 
307134a1f4eSCasper H.S. Dik 	exec = getexecprof("Forced Privilege", KV_COMMAND, pap->pfa_path,
308134a1f4eSCasper H.S. Dik 	    GET_ONE);
309134a1f4eSCasper H.S. Dik 
310134a1f4eSCasper H.S. Dik 	if (exec == NULL && removeisapath(pap->pfa_path)) {
311134a1f4eSCasper H.S. Dik 		exec = getexecprof("Forced Privilege", KV_COMMAND,
312134a1f4eSCasper H.S. Dik 		    pap->pfa_path, GET_ONE);
313134a1f4eSCasper H.S. Dik 	}
314134a1f4eSCasper H.S. Dik 
315134a1f4eSCasper H.S. Dik 	if (exec == NULL) {
316134a1f4eSCasper H.S. Dik 		(void) door_return(res, setsz, NULL, 0);
317134a1f4eSCasper H.S. Dik 		return;
318134a1f4eSCasper H.S. Dik 	}
319134a1f4eSCasper H.S. Dik 
320134a1f4eSCasper H.S. Dik 	if ((value = kva_match(exec->attr, EXECATTR_IPRIV_KW)) == NULL ||
321134a1f4eSCasper H.S. Dik 	    (fset = get_privset(value, NULL, pap->pfa_path)) == NULL) {
322134a1f4eSCasper H.S. Dik 		free_execattr(exec);
323134a1f4eSCasper H.S. Dik 		(void) door_return(res, setsz, NULL, 0);
324134a1f4eSCasper H.S. Dik 		return;
325134a1f4eSCasper H.S. Dik 	}
326134a1f4eSCasper H.S. Dik 
327134a1f4eSCasper H.S. Dik 	priv_copyset(fset, res);
328134a1f4eSCasper H.S. Dik 	priv_freeset(fset);
329134a1f4eSCasper H.S. Dik 
330134a1f4eSCasper H.S. Dik 	free_execattr(exec);
331134a1f4eSCasper H.S. Dik 	(void) door_return(res, setsz, NULL, 0);
332134a1f4eSCasper H.S. Dik }
333134a1f4eSCasper H.S. Dik 
334134a1f4eSCasper H.S. Dik static void
335134a1f4eSCasper H.S. Dik callback_user_privs(pfexec_arg_t *pap)
336134a1f4eSCasper H.S. Dik {
337134a1f4eSCasper H.S. Dik 	priv_set_t *gset, *wset;
338134a1f4eSCasper H.S. Dik 	uint32_t res;
339134a1f4eSCasper H.S. Dik 
340134a1f4eSCasper H.S. Dik 	wset = (priv_set_t *)&pap->pfa_buf;
341134a1f4eSCasper H.S. Dik 	gset = get_granted_privs(pap->pfa_uid);
342134a1f4eSCasper H.S. Dik 
343134a1f4eSCasper H.S. Dik 	res = priv_issubset(wset, gset);
344134a1f4eSCasper H.S. Dik 	priv_freeset(gset);
345134a1f4eSCasper H.S. Dik 
346134a1f4eSCasper H.S. Dik 	(void) door_return((char *)&res, sizeof (res), NULL, 0);
347134a1f4eSCasper H.S. Dik }
348134a1f4eSCasper H.S. Dik 
349134a1f4eSCasper H.S. Dik static void
350134a1f4eSCasper H.S. Dik callback_pfexec(pfexec_arg_t *pap)
351134a1f4eSCasper H.S. Dik {
352134a1f4eSCasper H.S. Dik 	pfexec_reply_t *res = alloca(repsz);
353134a1f4eSCasper H.S. Dik 	uid_t uid, euid, uuid;
354134a1f4eSCasper H.S. Dik 	gid_t gid, egid;
355134a1f4eSCasper H.S. Dik 	struct passwd pw, *pwd;
356134a1f4eSCasper H.S. Dik 	char buf[1024];
357bf859931SCasper H.S. Dik 	execattr_t *exec = NULL;
358134a1f4eSCasper H.S. Dik 	char *value;
359134a1f4eSCasper H.S. Dik 	priv_set_t *lset, *iset;
360134a1f4eSCasper H.S. Dik 	size_t mysz = repsz - 2 * setsz;
361134a1f4eSCasper H.S. Dik 	char *path = pap->pfa_path;
362134a1f4eSCasper H.S. Dik 
363*b01b59e3SRobert Mustacchi 	/*
364*b01b59e3SRobert Mustacchi 	 * Initialize the pfexec_reply_t to a sane state.
365*b01b59e3SRobert Mustacchi 	 */
366*b01b59e3SRobert Mustacchi 	res->pfr_vers = pap->pfa_vers;
367*b01b59e3SRobert Mustacchi 	res->pfr_len = 0;
368*b01b59e3SRobert Mustacchi 	res->pfr_ruid = PFEXEC_NOTSET;
369*b01b59e3SRobert Mustacchi 	res->pfr_euid = PFEXEC_NOTSET;
370*b01b59e3SRobert Mustacchi 	res->pfr_rgid = PFEXEC_NOTSET;
371*b01b59e3SRobert Mustacchi 	res->pfr_egid = PFEXEC_NOTSET;
372*b01b59e3SRobert Mustacchi 	res->pfr_setcred = B_FALSE;
373*b01b59e3SRobert Mustacchi 	res->pfr_scrubenv = B_TRUE;
374*b01b59e3SRobert Mustacchi 	res->pfr_allowed = B_FALSE;
375*b01b59e3SRobert Mustacchi 	res->pfr_ioff = 0;
376*b01b59e3SRobert Mustacchi 	res->pfr_loff = 0;
377*b01b59e3SRobert Mustacchi 
378134a1f4eSCasper H.S. Dik 	uuid = pap->pfa_uid;
379134a1f4eSCasper H.S. Dik 
380134a1f4eSCasper H.S. Dik 	if (getpwuid_r(uuid, &pw, buf, sizeof (buf), &pwd) != 0 || pwd == NULL)
381134a1f4eSCasper H.S. Dik 		goto stdexec;
382134a1f4eSCasper H.S. Dik 
383134a1f4eSCasper H.S. Dik 	exec = getexecuser(pwd->pw_name, KV_COMMAND, path, GET_ONE);
384134a1f4eSCasper H.S. Dik 
385bf859931SCasper H.S. Dik 	if ((exec == NULL || exec->attr == NULL) && removeisapath(path)) {
386bf859931SCasper H.S. Dik 		free_execattr(exec);
387134a1f4eSCasper H.S. Dik 		exec = getexecuser(pwd->pw_name, KV_COMMAND, path, GET_ONE);
388bf859931SCasper H.S. Dik 	}
389134a1f4eSCasper H.S. Dik 
390134a1f4eSCasper H.S. Dik 	if (exec == NULL) {
391134a1f4eSCasper H.S. Dik 		res->pfr_allowed = B_FALSE;
392134a1f4eSCasper H.S. Dik 		goto ret;
393134a1f4eSCasper H.S. Dik 	}
394134a1f4eSCasper H.S. Dik 
395134a1f4eSCasper H.S. Dik 	if (exec->attr == NULL)
396134a1f4eSCasper H.S. Dik 		goto stdexec;
397134a1f4eSCasper H.S. Dik 
398134a1f4eSCasper H.S. Dik 	/* Found in execattr, so clearly we can use it */
399134a1f4eSCasper H.S. Dik 	res->pfr_allowed = B_TRUE;
400134a1f4eSCasper H.S. Dik 
401134a1f4eSCasper H.S. Dik 	uid = euid = (uid_t)-1;
402134a1f4eSCasper H.S. Dik 	gid = egid = (gid_t)-1;
403134a1f4eSCasper H.S. Dik 	lset = iset = NULL;
404134a1f4eSCasper H.S. Dik 
405134a1f4eSCasper H.S. Dik 	/*
406134a1f4eSCasper H.S. Dik 	 * If there's an error in parsing uid, gid, privs, then return
407134a1f4eSCasper H.S. Dik 	 * failure.
408134a1f4eSCasper H.S. Dik 	 */
409134a1f4eSCasper H.S. Dik 	if ((value = kva_match(exec->attr, EXECATTR_UID_KW)) != NULL)
410134a1f4eSCasper H.S. Dik 		euid = uid = get_uid(value, &res->pfr_allowed, path);
411134a1f4eSCasper H.S. Dik 
412134a1f4eSCasper H.S. Dik 	if ((value = kva_match(exec->attr, EXECATTR_GID_KW)) != NULL)
413134a1f4eSCasper H.S. Dik 		egid = gid = get_gid(value, &res->pfr_allowed, path);
414134a1f4eSCasper H.S. Dik 
415134a1f4eSCasper H.S. Dik 	if ((value = kva_match(exec->attr, EXECATTR_EUID_KW)) != NULL)
416134a1f4eSCasper H.S. Dik 		euid = get_uid(value, &res->pfr_allowed, path);
417134a1f4eSCasper H.S. Dik 
418134a1f4eSCasper H.S. Dik 	if ((value = kva_match(exec->attr, EXECATTR_EGID_KW)) != NULL)
419134a1f4eSCasper H.S. Dik 		egid = get_gid(value, &res->pfr_allowed, path);
420134a1f4eSCasper H.S. Dik 
421134a1f4eSCasper H.S. Dik 	if ((value = kva_match(exec->attr, EXECATTR_LPRIV_KW)) != NULL)
422134a1f4eSCasper H.S. Dik 		lset = get_privset(value, &res->pfr_allowed, path);
423134a1f4eSCasper H.S. Dik 
424134a1f4eSCasper H.S. Dik 	if ((value = kva_match(exec->attr, EXECATTR_IPRIV_KW)) != NULL)
425134a1f4eSCasper H.S. Dik 		iset = get_privset(value, &res->pfr_allowed, path);
426134a1f4eSCasper H.S. Dik 
427134a1f4eSCasper H.S. Dik 	/*
428134a1f4eSCasper H.S. Dik 	 * Remove LD_* variables in the kernel when the runtime linker might
429134a1f4eSCasper H.S. Dik 	 * use them later on because the uids are equal.
430134a1f4eSCasper H.S. Dik 	 */
431134a1f4eSCasper H.S. Dik 	res->pfr_scrubenv = (uid != (uid_t)-1 && euid == uid) ||
432134a1f4eSCasper H.S. Dik 	    (gid != (gid_t)-1 && egid == gid) || iset != NULL;
433134a1f4eSCasper H.S. Dik 
434134a1f4eSCasper H.S. Dik 	res->pfr_euid = euid;
435134a1f4eSCasper H.S. Dik 	res->pfr_ruid = uid;
436134a1f4eSCasper H.S. Dik 	res->pfr_egid = egid;
437134a1f4eSCasper H.S. Dik 	res->pfr_rgid = gid;
438134a1f4eSCasper H.S. Dik 
439134a1f4eSCasper H.S. Dik 	/* Now add the privilege sets */
440134a1f4eSCasper H.S. Dik 	res->pfr_ioff = res->pfr_loff = 0;
441134a1f4eSCasper H.S. Dik 	if (iset != NULL) {
442134a1f4eSCasper H.S. Dik 		res->pfr_ioff = mysz;
443134a1f4eSCasper H.S. Dik 		priv_copyset(iset, PFEXEC_REPLY_IPRIV(res));
444134a1f4eSCasper H.S. Dik 		mysz += setsz;
445134a1f4eSCasper H.S. Dik 		priv_freeset(iset);
446134a1f4eSCasper H.S. Dik 	}
447134a1f4eSCasper H.S. Dik 	if (lset != NULL) {
448134a1f4eSCasper H.S. Dik 		res->pfr_loff = mysz;
449134a1f4eSCasper H.S. Dik 		priv_copyset(lset, PFEXEC_REPLY_LPRIV(res));
450134a1f4eSCasper H.S. Dik 		mysz += setsz;
451134a1f4eSCasper H.S. Dik 		priv_freeset(lset);
452134a1f4eSCasper H.S. Dik 	}
453134a1f4eSCasper H.S. Dik 
454134a1f4eSCasper H.S. Dik 	res->pfr_setcred = uid != (uid_t)-1 || euid != (uid_t)-1 ||
455134a1f4eSCasper H.S. Dik 	    egid != (gid_t)-1 || gid != (gid_t)-1 || iset != NULL ||
456134a1f4eSCasper H.S. Dik 	    lset != NULL;
457134a1f4eSCasper H.S. Dik 
458134a1f4eSCasper H.S. Dik 	/* If the real uid changes, we stop running under a profile shell */
459134a1f4eSCasper H.S. Dik 	res->pfr_clearflag = uid != (uid_t)-1 && uid != uuid;
460134a1f4eSCasper H.S. Dik 	free_execattr(exec);
461134a1f4eSCasper H.S. Dik ret:
462134a1f4eSCasper H.S. Dik 	(void) door_return((char *)res, mysz, NULL, 0);
463134a1f4eSCasper H.S. Dik 	return;
464134a1f4eSCasper H.S. Dik 
465134a1f4eSCasper H.S. Dik stdexec:
466bf859931SCasper H.S. Dik 	free_execattr(exec);
467bf859931SCasper H.S. Dik 
468134a1f4eSCasper H.S. Dik 	res->pfr_scrubenv = B_FALSE;
469134a1f4eSCasper H.S. Dik 	res->pfr_setcred = B_FALSE;
470134a1f4eSCasper H.S. Dik 	res->pfr_allowed = B_TRUE;
471134a1f4eSCasper H.S. Dik 
472134a1f4eSCasper H.S. Dik 	(void) door_return((char *)res, mysz, NULL, 0);
473134a1f4eSCasper H.S. Dik }
474134a1f4eSCasper H.S. Dik 
475134a1f4eSCasper H.S. Dik /* ARGSUSED */
476134a1f4eSCasper H.S. Dik static void
477134a1f4eSCasper H.S. Dik callback(void *cookie, char *argp, size_t asz, door_desc_t *dp, uint_t ndesc)
478134a1f4eSCasper H.S. Dik {
479134a1f4eSCasper H.S. Dik 	/* LINTED ALIGNMENT */
480134a1f4eSCasper H.S. Dik 	pfexec_arg_t *pap = (pfexec_arg_t *)argp;
481134a1f4eSCasper H.S. Dik 
482134a1f4eSCasper H.S. Dik 	if (asz < sizeof (pfexec_arg_t) || pap->pfa_vers != PFEXEC_ARG_VERS) {
483134a1f4eSCasper H.S. Dik 		(void) door_return(NULL, 0, NULL, 0);
484134a1f4eSCasper H.S. Dik 		return;
485134a1f4eSCasper H.S. Dik 	}
486134a1f4eSCasper H.S. Dik 
487134a1f4eSCasper H.S. Dik 	switch (pap->pfa_call) {
488134a1f4eSCasper H.S. Dik 	case PFEXEC_EXEC_ATTRS:
489134a1f4eSCasper H.S. Dik 		callback_pfexec(pap);
490134a1f4eSCasper H.S. Dik 		break;
491134a1f4eSCasper H.S. Dik 	case PFEXEC_FORCED_PRIVS:
492134a1f4eSCasper H.S. Dik 		callback_forced_privs(pap);
493134a1f4eSCasper H.S. Dik 		break;
494134a1f4eSCasper H.S. Dik 	case PFEXEC_USER_PRIVS:
495134a1f4eSCasper H.S. Dik 		callback_user_privs(pap);
496134a1f4eSCasper H.S. Dik 		break;
497134a1f4eSCasper H.S. Dik 	default:
498134a1f4eSCasper H.S. Dik 		syslog(LOG_ERR, "Bad Call: %d\n", pap->pfa_call);
499134a1f4eSCasper H.S. Dik 		break;
500134a1f4eSCasper H.S. Dik 	}
501134a1f4eSCasper H.S. Dik 
502134a1f4eSCasper H.S. Dik 	/*
503134a1f4eSCasper H.S. Dik 	 * If the door_return(ptr, size, NULL, 0) fails, make sure we
504134a1f4eSCasper H.S. Dik 	 * don't lose server threads.
505134a1f4eSCasper H.S. Dik 	 */
506134a1f4eSCasper H.S. Dik 	(void) door_return(NULL, 0, NULL, 0);
507134a1f4eSCasper H.S. Dik }
508134a1f4eSCasper H.S. Dik 
509134a1f4eSCasper H.S. Dik int
510134a1f4eSCasper H.S. Dik main(void)
511134a1f4eSCasper H.S. Dik {
512134a1f4eSCasper H.S. Dik 	const priv_impl_info_t *info;
513134a1f4eSCasper H.S. Dik 
514134a1f4eSCasper H.S. Dik 	(void) signal(SIGINT, unregister_pfexec);
515134a1f4eSCasper H.S. Dik 	(void) signal(SIGQUIT, unregister_pfexec);
516134a1f4eSCasper H.S. Dik 	(void) signal(SIGTERM, unregister_pfexec);
517134a1f4eSCasper H.S. Dik 	(void) signal(SIGHUP, unregister_pfexec);
518134a1f4eSCasper H.S. Dik 
519134a1f4eSCasper H.S. Dik 	info = getprivimplinfo();
520134a1f4eSCasper H.S. Dik 	if (info == NULL)
521134a1f4eSCasper H.S. Dik 		exit(1);
522134a1f4eSCasper H.S. Dik 
523134a1f4eSCasper H.S. Dik 	if (fork() > 0)
524134a1f4eSCasper H.S. Dik 		_exit(0);
525134a1f4eSCasper H.S. Dik 
526134a1f4eSCasper H.S. Dik 	openlog("pfexecd", LOG_PID, LOG_DAEMON);
527134a1f4eSCasper H.S. Dik 	setsz = info->priv_setsize * sizeof (priv_chunk_t);
528134a1f4eSCasper H.S. Dik 	repsz = 2 * setsz + sizeof (pfexec_reply_t);
529134a1f4eSCasper H.S. Dik 
530134a1f4eSCasper H.S. Dik 	init_isa_regex();
531134a1f4eSCasper H.S. Dik 
532134a1f4eSCasper H.S. Dik 	doorfd = door_create(callback, NULL, DOOR_REFUSE_DESC);
533134a1f4eSCasper H.S. Dik 
534134a1f4eSCasper H.S. Dik 	if (doorfd == -1 || register_pfexec(doorfd) != 0) {
535134a1f4eSCasper H.S. Dik 		perror("doorfd");
536134a1f4eSCasper H.S. Dik 		exit(1);
537134a1f4eSCasper H.S. Dik 	}
538134a1f4eSCasper H.S. Dik 
539134a1f4eSCasper H.S. Dik 	/* LINTED CONSTCOND */
540134a1f4eSCasper H.S. Dik 	while (1)
541134a1f4eSCasper H.S. Dik 		(void) sigpause(SIGINT);
542134a1f4eSCasper H.S. Dik 
543134a1f4eSCasper H.S. Dik 	return (0);
544134a1f4eSCasper H.S. Dik }
545