xref: /illumos-gate/usr/src/cmd/pfexecd/pfexecd.c (revision 2dedcc4f)
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.
22b01b59e3SRobert 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++] = '\\';
121*2dedcc4fSToomas Soome 			/* FALLTHROUGH */
122134a1f4eSCasper H.S. Dik 		default:
123134a1f4eSCasper H.S. Dik 			regexpr[len++] = *p;
124134a1f4eSCasper H.S. Dik 			break;
125134a1f4eSCasper H.S. Dik 		case ' ':
126134a1f4eSCasper H.S. Dik 		case '\t':
127134a1f4eSCasper H.S. Dik 			regexpr[len++] = '|';
128134a1f4eSCasper H.S. Dik 			break;
129134a1f4eSCasper H.S. Dik 		}
130134a1f4eSCasper H.S. Dik 	}
131134a1f4eSCasper H.S. Dik 
132134a1f4eSCasper H.S. Dik 	free(isalist);
133134a1f4eSCasper H.S. Dik 	regexpr[len] = '\0';
134134a1f4eSCasper H.S. Dik 	(void) strcat(regexpr, RIGHT);
135134a1f4eSCasper H.S. Dik 
136134a1f4eSCasper H.S. Dik 	if (regcomp(&regc, regexpr, REG_EXTENDED) != 0)
137134a1f4eSCasper H.S. Dik 		return;
138134a1f4eSCasper H.S. Dik 
139134a1f4eSCasper H.S. Dik 	cansplice = B_TRUE;
140134a1f4eSCasper H.S. Dik }
141134a1f4eSCasper H.S. Dik 
142134a1f4eSCasper H.S. Dik #define	NMATCH	2
143134a1f4eSCasper H.S. Dik 
144134a1f4eSCasper H.S. Dik static boolean_t
145134a1f4eSCasper H.S. Dik removeisapath(char *path)
146134a1f4eSCasper H.S. Dik {
147134a1f4eSCasper H.S. Dik 	regmatch_t match[NMATCH];
148134a1f4eSCasper H.S. Dik 
149134a1f4eSCasper H.S. Dik 	if (!cansplice || regexec(&regc, path, NMATCH, match, 0) != 0)
150134a1f4eSCasper H.S. Dik 		return (B_FALSE);
151134a1f4eSCasper H.S. Dik 
152134a1f4eSCasper H.S. Dik 	/*
153134a1f4eSCasper H.S. Dik 	 * The first match includes the whole matched expression including the
154134a1f4eSCasper H.S. Dik 	 * end of the string.  The second match includes the "/" + "isa" and
155134a1f4eSCasper H.S. Dik 	 * that is the part we need to remove.
156134a1f4eSCasper H.S. Dik 	 */
157134a1f4eSCasper H.S. Dik 
158134a1f4eSCasper H.S. Dik 	if (match[1].rm_so == -1)
159134a1f4eSCasper H.S. Dik 		return (B_FALSE);
160134a1f4eSCasper H.S. Dik 
161134a1f4eSCasper H.S. Dik 	/* match[0].rm_eo == strlen(path) */
162134a1f4eSCasper H.S. Dik 	(void) memmove(path + match[1].rm_so, path + match[1].rm_eo,
163134a1f4eSCasper H.S. Dik 	    match[0].rm_eo - match[1].rm_eo + 1);
164134a1f4eSCasper H.S. Dik 
165134a1f4eSCasper H.S. Dik 	return (B_TRUE);
166134a1f4eSCasper H.S. Dik }
167134a1f4eSCasper H.S. Dik 
168134a1f4eSCasper H.S. Dik static int
169134a1f4eSCasper H.S. Dik register_pfexec(int fd)
170134a1f4eSCasper H.S. Dik {
171134a1f4eSCasper H.S. Dik 	int ret = syscall(SYS_privsys, PRIVSYS_PFEXEC_REG, fd);
172134a1f4eSCasper H.S. Dik 
173134a1f4eSCasper H.S. Dik 	return (ret);
174134a1f4eSCasper H.S. Dik }
175134a1f4eSCasper H.S. Dik 
176134a1f4eSCasper H.S. Dik /* ARGSUSED */
177134a1f4eSCasper H.S. Dik static void
178134a1f4eSCasper H.S. Dik unregister_pfexec(int sig)
179134a1f4eSCasper H.S. Dik {
180134a1f4eSCasper H.S. Dik 	if (doorfd != -1)
181134a1f4eSCasper H.S. Dik 		(void) syscall(SYS_privsys, PRIVSYS_PFEXEC_UNREG, doorfd);
182134a1f4eSCasper H.S. Dik 	_exit(0);
183134a1f4eSCasper H.S. Dik }
184134a1f4eSCasper H.S. Dik 
185134a1f4eSCasper H.S. Dik static int
186134a1f4eSCasper H.S. Dik alldigits(const char *s)
187134a1f4eSCasper H.S. Dik {
188134a1f4eSCasper H.S. Dik 	int c;
189134a1f4eSCasper H.S. Dik 
190134a1f4eSCasper H.S. Dik 	if (*s == '\0')
191134a1f4eSCasper H.S. Dik 		return (0);
192134a1f4eSCasper H.S. Dik 
193134a1f4eSCasper H.S. Dik 	while ((c = *s++) != '\0') {
194134a1f4eSCasper H.S. Dik 		if (!isdigit(c)) {
195134a1f4eSCasper H.S. Dik 			return (0);
196134a1f4eSCasper H.S. Dik 		}
197134a1f4eSCasper H.S. Dik 	}
198134a1f4eSCasper H.S. Dik 
199134a1f4eSCasper H.S. Dik 	return (1);
200134a1f4eSCasper H.S. Dik }
201134a1f4eSCasper H.S. Dik 
202134a1f4eSCasper H.S. Dik static uid_t
203134a1f4eSCasper H.S. Dik get_uid(const char *v, boolean_t *ok, char *path)
204134a1f4eSCasper H.S. Dik {
205134a1f4eSCasper H.S. Dik 	struct passwd *pwd, pwdm;
206134a1f4eSCasper H.S. Dik 	char buf[1024];
207134a1f4eSCasper H.S. Dik 
208134a1f4eSCasper H.S. Dik 	if (getpwnam_r(v, &pwdm, buf, sizeof (buf), &pwd) == 0 && pwd != NULL)
209134a1f4eSCasper H.S. Dik 		return (pwd->pw_uid);
210134a1f4eSCasper H.S. Dik 
211134a1f4eSCasper H.S. Dik 	if (alldigits(v))
212134a1f4eSCasper H.S. Dik 		return (atoi(v));
213134a1f4eSCasper H.S. Dik 
214134a1f4eSCasper H.S. Dik 	*ok = B_FALSE;
215134a1f4eSCasper H.S. Dik 	syslog(LOG_ERR, "%s: %s: unknown username\n", path, v);
216134a1f4eSCasper H.S. Dik 	return ((uid_t)-1);
217134a1f4eSCasper H.S. Dik }
218134a1f4eSCasper H.S. Dik 
219134a1f4eSCasper H.S. Dik static uid_t
220134a1f4eSCasper H.S. Dik get_gid(const char *v, boolean_t *ok, char *path)
221134a1f4eSCasper H.S. Dik {
222134a1f4eSCasper H.S. Dik 	struct group *grp, grpm;
223134a1f4eSCasper H.S. Dik 	char buf[1024];
224134a1f4eSCasper H.S. Dik 
225134a1f4eSCasper H.S. Dik 	if (getgrnam_r(v, &grpm, buf, sizeof (buf), &grp) == 0 && grp != NULL)
226134a1f4eSCasper H.S. Dik 		return (grp->gr_gid);
227134a1f4eSCasper H.S. Dik 
228134a1f4eSCasper H.S. Dik 	if (alldigits(v))
229134a1f4eSCasper H.S. Dik 		return (atoi(v));
230134a1f4eSCasper H.S. Dik 
231134a1f4eSCasper H.S. Dik 	*ok = B_FALSE;
232134a1f4eSCasper H.S. Dik 	syslog(LOG_ERR, "%s: %s: unknown groupname\n", path, v);
233134a1f4eSCasper H.S. Dik 	return ((gid_t)-1);
234134a1f4eSCasper H.S. Dik }
235134a1f4eSCasper H.S. Dik 
236134a1f4eSCasper H.S. Dik static priv_set_t *
237134a1f4eSCasper H.S. Dik get_privset(const char *s, boolean_t *ok, char *path)
238134a1f4eSCasper H.S. Dik {
239134a1f4eSCasper H.S. Dik 	priv_set_t *res;
240134a1f4eSCasper H.S. Dik 
241134a1f4eSCasper H.S. Dik 	if ((res = priv_str_to_set(s, ",", NULL)) == NULL) {
242134a1f4eSCasper H.S. Dik 		syslog(LOG_ERR, "%s: %s: bad privilege set\n", path, s);
243134a1f4eSCasper H.S. Dik 		if (ok != NULL)
244134a1f4eSCasper H.S. Dik 			*ok = B_FALSE;
245134a1f4eSCasper H.S. Dik 	}
246134a1f4eSCasper H.S. Dik 	return (res);
247134a1f4eSCasper H.S. Dik }
248134a1f4eSCasper H.S. Dik 
249134a1f4eSCasper H.S. Dik /*ARGSUSED*/
250134a1f4eSCasper H.S. Dik static int
251134a1f4eSCasper H.S. Dik ggp_callback(const char *prof, kva_t *attr, void *ctxt, void *vres)
252134a1f4eSCasper H.S. Dik {
253134a1f4eSCasper H.S. Dik 	priv_set_t *res = vres;
254134a1f4eSCasper H.S. Dik 	char *privs;
255134a1f4eSCasper H.S. Dik 
256134a1f4eSCasper H.S. Dik 	if (attr == NULL)
257134a1f4eSCasper H.S. Dik 		return (0);
258134a1f4eSCasper H.S. Dik 
259134a1f4eSCasper H.S. Dik 	/* get privs from this profile */
260134a1f4eSCasper H.S. Dik 	privs = kva_match(attr, PROFATTR_PRIVS_KW);
261134a1f4eSCasper H.S. Dik 	if (privs != NULL) {
262134a1f4eSCasper H.S. Dik 		priv_set_t *tmp = priv_str_to_set(privs, ",", NULL);
263134a1f4eSCasper H.S. Dik 		if (tmp != NULL) {
264134a1f4eSCasper H.S. Dik 			priv_union(tmp, res);
265134a1f4eSCasper H.S. Dik 			priv_freeset(tmp);
266134a1f4eSCasper H.S. Dik 		}
267134a1f4eSCasper H.S. Dik 	}
268134a1f4eSCasper H.S. Dik 
269134a1f4eSCasper H.S. Dik 	return (0);
270134a1f4eSCasper H.S. Dik }
271134a1f4eSCasper H.S. Dik 
272134a1f4eSCasper H.S. Dik /*
273134a1f4eSCasper H.S. Dik  * This routine exists on failure and returns NULL if no granted privileges
274134a1f4eSCasper H.S. Dik  * are set.
275134a1f4eSCasper H.S. Dik  */
276134a1f4eSCasper H.S. Dik static priv_set_t *
277134a1f4eSCasper H.S. Dik get_granted_privs(uid_t uid)
278134a1f4eSCasper H.S. Dik {
279134a1f4eSCasper H.S. Dik 	priv_set_t *res;
280134a1f4eSCasper H.S. Dik 	struct passwd *pwd, pwdm;
281134a1f4eSCasper H.S. Dik 	char buf[1024];
282134a1f4eSCasper H.S. Dik 
283134a1f4eSCasper H.S. Dik 	if (getpwuid_r(uid, &pwdm, buf, sizeof (buf), &pwd) != 0 || pwd == NULL)
284134a1f4eSCasper H.S. Dik 		return (NULL);
285134a1f4eSCasper H.S. Dik 
286134a1f4eSCasper H.S. Dik 	res = priv_allocset();
287134a1f4eSCasper H.S. Dik 	if (res == NULL)
288134a1f4eSCasper H.S. Dik 		return (NULL);
289134a1f4eSCasper H.S. Dik 
290134a1f4eSCasper H.S. Dik 	priv_emptyset(res);
291134a1f4eSCasper H.S. Dik 
292134a1f4eSCasper H.S. Dik 	(void) _enum_profs(pwd->pw_name, ggp_callback, NULL, res);
293134a1f4eSCasper H.S. Dik 
294134a1f4eSCasper H.S. Dik 	return (res);
295134a1f4eSCasper H.S. Dik }
296134a1f4eSCasper H.S. Dik 
297134a1f4eSCasper H.S. Dik static void
298134a1f4eSCasper H.S. Dik callback_forced_privs(pfexec_arg_t *pap)
299134a1f4eSCasper H.S. Dik {
300134a1f4eSCasper H.S. Dik 	execattr_t *exec;
301134a1f4eSCasper H.S. Dik 	char *value;
302134a1f4eSCasper H.S. Dik 	priv_set_t *fset;
303134a1f4eSCasper H.S. Dik 	void *res = alloca(setsz);
304134a1f4eSCasper H.S. Dik 
305134a1f4eSCasper H.S. Dik 	/* Empty set signifies no forced privileges. */
306134a1f4eSCasper H.S. Dik 	priv_emptyset(res);
307134a1f4eSCasper H.S. Dik 
308134a1f4eSCasper H.S. Dik 	exec = getexecprof("Forced Privilege", KV_COMMAND, pap->pfa_path,
309134a1f4eSCasper H.S. Dik 	    GET_ONE);
310134a1f4eSCasper H.S. Dik 
311134a1f4eSCasper H.S. Dik 	if (exec == NULL && removeisapath(pap->pfa_path)) {
312134a1f4eSCasper H.S. Dik 		exec = getexecprof("Forced Privilege", KV_COMMAND,
313134a1f4eSCasper H.S. Dik 		    pap->pfa_path, GET_ONE);
314134a1f4eSCasper H.S. Dik 	}
315134a1f4eSCasper H.S. Dik 
316134a1f4eSCasper H.S. Dik 	if (exec == NULL) {
317134a1f4eSCasper H.S. Dik 		(void) door_return(res, setsz, NULL, 0);
318134a1f4eSCasper H.S. Dik 		return;
319134a1f4eSCasper H.S. Dik 	}
320134a1f4eSCasper H.S. Dik 
321134a1f4eSCasper H.S. Dik 	if ((value = kva_match(exec->attr, EXECATTR_IPRIV_KW)) == NULL ||
322134a1f4eSCasper H.S. Dik 	    (fset = get_privset(value, NULL, pap->pfa_path)) == NULL) {
323134a1f4eSCasper H.S. Dik 		free_execattr(exec);
324134a1f4eSCasper H.S. Dik 		(void) door_return(res, setsz, NULL, 0);
325134a1f4eSCasper H.S. Dik 		return;
326134a1f4eSCasper H.S. Dik 	}
327134a1f4eSCasper H.S. Dik 
328134a1f4eSCasper H.S. Dik 	priv_copyset(fset, res);
329134a1f4eSCasper H.S. Dik 	priv_freeset(fset);
330134a1f4eSCasper H.S. Dik 
331134a1f4eSCasper H.S. Dik 	free_execattr(exec);
332134a1f4eSCasper H.S. Dik 	(void) door_return(res, setsz, NULL, 0);
333134a1f4eSCasper H.S. Dik }
334134a1f4eSCasper H.S. Dik 
335134a1f4eSCasper H.S. Dik static void
336134a1f4eSCasper H.S. Dik callback_user_privs(pfexec_arg_t *pap)
337134a1f4eSCasper H.S. Dik {
338134a1f4eSCasper H.S. Dik 	priv_set_t *gset, *wset;
339134a1f4eSCasper H.S. Dik 	uint32_t res;
340134a1f4eSCasper H.S. Dik 
341134a1f4eSCasper H.S. Dik 	wset = (priv_set_t *)&pap->pfa_buf;
342134a1f4eSCasper H.S. Dik 	gset = get_granted_privs(pap->pfa_uid);
343134a1f4eSCasper H.S. Dik 
344134a1f4eSCasper H.S. Dik 	res = priv_issubset(wset, gset);
345134a1f4eSCasper H.S. Dik 	priv_freeset(gset);
346134a1f4eSCasper H.S. Dik 
347134a1f4eSCasper H.S. Dik 	(void) door_return((char *)&res, sizeof (res), NULL, 0);
348134a1f4eSCasper H.S. Dik }
349134a1f4eSCasper H.S. Dik 
350134a1f4eSCasper H.S. Dik static void
351134a1f4eSCasper H.S. Dik callback_pfexec(pfexec_arg_t *pap)
352134a1f4eSCasper H.S. Dik {
353134a1f4eSCasper H.S. Dik 	pfexec_reply_t *res = alloca(repsz);
354134a1f4eSCasper H.S. Dik 	uid_t uid, euid, uuid;
355134a1f4eSCasper H.S. Dik 	gid_t gid, egid;
356134a1f4eSCasper H.S. Dik 	struct passwd pw, *pwd;
357134a1f4eSCasper H.S. Dik 	char buf[1024];
358bf859931SCasper H.S. Dik 	execattr_t *exec = NULL;
359134a1f4eSCasper H.S. Dik 	char *value;
360134a1f4eSCasper H.S. Dik 	priv_set_t *lset, *iset;
361134a1f4eSCasper H.S. Dik 	size_t mysz = repsz - 2 * setsz;
362134a1f4eSCasper H.S. Dik 	char *path = pap->pfa_path;
363134a1f4eSCasper H.S. Dik 
364b01b59e3SRobert Mustacchi 	/*
365b01b59e3SRobert Mustacchi 	 * Initialize the pfexec_reply_t to a sane state.
366b01b59e3SRobert Mustacchi 	 */
367b01b59e3SRobert Mustacchi 	res->pfr_vers = pap->pfa_vers;
368b01b59e3SRobert Mustacchi 	res->pfr_len = 0;
369b01b59e3SRobert Mustacchi 	res->pfr_ruid = PFEXEC_NOTSET;
370b01b59e3SRobert Mustacchi 	res->pfr_euid = PFEXEC_NOTSET;
371b01b59e3SRobert Mustacchi 	res->pfr_rgid = PFEXEC_NOTSET;
372b01b59e3SRobert Mustacchi 	res->pfr_egid = PFEXEC_NOTSET;
373b01b59e3SRobert Mustacchi 	res->pfr_setcred = B_FALSE;
374b01b59e3SRobert Mustacchi 	res->pfr_scrubenv = B_TRUE;
375b01b59e3SRobert Mustacchi 	res->pfr_allowed = B_FALSE;
376b01b59e3SRobert Mustacchi 	res->pfr_ioff = 0;
377b01b59e3SRobert Mustacchi 	res->pfr_loff = 0;
378b01b59e3SRobert Mustacchi 
379134a1f4eSCasper H.S. Dik 	uuid = pap->pfa_uid;
380134a1f4eSCasper H.S. Dik 
381134a1f4eSCasper H.S. Dik 	if (getpwuid_r(uuid, &pw, buf, sizeof (buf), &pwd) != 0 || pwd == NULL)
382134a1f4eSCasper H.S. Dik 		goto stdexec;
383134a1f4eSCasper H.S. Dik 
384134a1f4eSCasper H.S. Dik 	exec = getexecuser(pwd->pw_name, KV_COMMAND, path, GET_ONE);
385134a1f4eSCasper H.S. Dik 
386bf859931SCasper H.S. Dik 	if ((exec == NULL || exec->attr == NULL) && removeisapath(path)) {
387bf859931SCasper H.S. Dik 		free_execattr(exec);
388134a1f4eSCasper H.S. Dik 		exec = getexecuser(pwd->pw_name, KV_COMMAND, path, GET_ONE);
389bf859931SCasper H.S. Dik 	}
390134a1f4eSCasper H.S. Dik 
391134a1f4eSCasper H.S. Dik 	if (exec == NULL) {
392134a1f4eSCasper H.S. Dik 		res->pfr_allowed = B_FALSE;
393134a1f4eSCasper H.S. Dik 		goto ret;
394134a1f4eSCasper H.S. Dik 	}
395134a1f4eSCasper H.S. Dik 
396134a1f4eSCasper H.S. Dik 	if (exec->attr == NULL)
397134a1f4eSCasper H.S. Dik 		goto stdexec;
398134a1f4eSCasper H.S. Dik 
399134a1f4eSCasper H.S. Dik 	/* Found in execattr, so clearly we can use it */
400134a1f4eSCasper H.S. Dik 	res->pfr_allowed = B_TRUE;
401134a1f4eSCasper H.S. Dik 
402134a1f4eSCasper H.S. Dik 	uid = euid = (uid_t)-1;
403134a1f4eSCasper H.S. Dik 	gid = egid = (gid_t)-1;
404134a1f4eSCasper H.S. Dik 	lset = iset = NULL;
405134a1f4eSCasper H.S. Dik 
406134a1f4eSCasper H.S. Dik 	/*
407134a1f4eSCasper H.S. Dik 	 * If there's an error in parsing uid, gid, privs, then return
408134a1f4eSCasper H.S. Dik 	 * failure.
409134a1f4eSCasper H.S. Dik 	 */
410134a1f4eSCasper H.S. Dik 	if ((value = kva_match(exec->attr, EXECATTR_UID_KW)) != NULL)
411134a1f4eSCasper H.S. Dik 		euid = uid = get_uid(value, &res->pfr_allowed, path);
412134a1f4eSCasper H.S. Dik 
413134a1f4eSCasper H.S. Dik 	if ((value = kva_match(exec->attr, EXECATTR_GID_KW)) != NULL)
414134a1f4eSCasper H.S. Dik 		egid = gid = get_gid(value, &res->pfr_allowed, path);
415134a1f4eSCasper H.S. Dik 
416134a1f4eSCasper H.S. Dik 	if ((value = kva_match(exec->attr, EXECATTR_EUID_KW)) != NULL)
417134a1f4eSCasper H.S. Dik 		euid = get_uid(value, &res->pfr_allowed, path);
418134a1f4eSCasper H.S. Dik 
419134a1f4eSCasper H.S. Dik 	if ((value = kva_match(exec->attr, EXECATTR_EGID_KW)) != NULL)
420134a1f4eSCasper H.S. Dik 		egid = get_gid(value, &res->pfr_allowed, path);
421134a1f4eSCasper H.S. Dik 
422134a1f4eSCasper H.S. Dik 	if ((value = kva_match(exec->attr, EXECATTR_LPRIV_KW)) != NULL)
423134a1f4eSCasper H.S. Dik 		lset = get_privset(value, &res->pfr_allowed, path);
424134a1f4eSCasper H.S. Dik 
425134a1f4eSCasper H.S. Dik 	if ((value = kva_match(exec->attr, EXECATTR_IPRIV_KW)) != NULL)
426134a1f4eSCasper H.S. Dik 		iset = get_privset(value, &res->pfr_allowed, path);
427134a1f4eSCasper H.S. Dik 
428134a1f4eSCasper H.S. Dik 	/*
429134a1f4eSCasper H.S. Dik 	 * Remove LD_* variables in the kernel when the runtime linker might
430134a1f4eSCasper H.S. Dik 	 * use them later on because the uids are equal.
431134a1f4eSCasper H.S. Dik 	 */
432134a1f4eSCasper H.S. Dik 	res->pfr_scrubenv = (uid != (uid_t)-1 && euid == uid) ||
433134a1f4eSCasper H.S. Dik 	    (gid != (gid_t)-1 && egid == gid) || iset != NULL;
434134a1f4eSCasper H.S. Dik 
435134a1f4eSCasper H.S. Dik 	res->pfr_euid = euid;
436134a1f4eSCasper H.S. Dik 	res->pfr_ruid = uid;
437134a1f4eSCasper H.S. Dik 	res->pfr_egid = egid;
438134a1f4eSCasper H.S. Dik 	res->pfr_rgid = gid;
439134a1f4eSCasper H.S. Dik 
440134a1f4eSCasper H.S. Dik 	/* Now add the privilege sets */
441134a1f4eSCasper H.S. Dik 	res->pfr_ioff = res->pfr_loff = 0;
442134a1f4eSCasper H.S. Dik 	if (iset != NULL) {
443134a1f4eSCasper H.S. Dik 		res->pfr_ioff = mysz;
444134a1f4eSCasper H.S. Dik 		priv_copyset(iset, PFEXEC_REPLY_IPRIV(res));
445134a1f4eSCasper H.S. Dik 		mysz += setsz;
446134a1f4eSCasper H.S. Dik 		priv_freeset(iset);
447134a1f4eSCasper H.S. Dik 	}
448134a1f4eSCasper H.S. Dik 	if (lset != NULL) {
449134a1f4eSCasper H.S. Dik 		res->pfr_loff = mysz;
450134a1f4eSCasper H.S. Dik 		priv_copyset(lset, PFEXEC_REPLY_LPRIV(res));
451134a1f4eSCasper H.S. Dik 		mysz += setsz;
452134a1f4eSCasper H.S. Dik 		priv_freeset(lset);
453134a1f4eSCasper H.S. Dik 	}
454134a1f4eSCasper H.S. Dik 
455134a1f4eSCasper H.S. Dik 	res->pfr_setcred = uid != (uid_t)-1 || euid != (uid_t)-1 ||
456134a1f4eSCasper H.S. Dik 	    egid != (gid_t)-1 || gid != (gid_t)-1 || iset != NULL ||
457134a1f4eSCasper H.S. Dik 	    lset != NULL;
458134a1f4eSCasper H.S. Dik 
459134a1f4eSCasper H.S. Dik 	/* If the real uid changes, we stop running under a profile shell */
460134a1f4eSCasper H.S. Dik 	res->pfr_clearflag = uid != (uid_t)-1 && uid != uuid;
461134a1f4eSCasper H.S. Dik 	free_execattr(exec);
462134a1f4eSCasper H.S. Dik ret:
463134a1f4eSCasper H.S. Dik 	(void) door_return((char *)res, mysz, NULL, 0);
464134a1f4eSCasper H.S. Dik 	return;
465134a1f4eSCasper H.S. Dik 
466134a1f4eSCasper H.S. Dik stdexec:
467bf859931SCasper H.S. Dik 	free_execattr(exec);
468bf859931SCasper H.S. Dik 
469134a1f4eSCasper H.S. Dik 	res->pfr_scrubenv = B_FALSE;
470134a1f4eSCasper H.S. Dik 	res->pfr_setcred = B_FALSE;
471134a1f4eSCasper H.S. Dik 	res->pfr_allowed = B_TRUE;
472134a1f4eSCasper H.S. Dik 
473134a1f4eSCasper H.S. Dik 	(void) door_return((char *)res, mysz, NULL, 0);
474134a1f4eSCasper H.S. Dik }
475134a1f4eSCasper H.S. Dik 
476134a1f4eSCasper H.S. Dik /* ARGSUSED */
477134a1f4eSCasper H.S. Dik static void
478134a1f4eSCasper H.S. Dik callback(void *cookie, char *argp, size_t asz, door_desc_t *dp, uint_t ndesc)
479134a1f4eSCasper H.S. Dik {
480134a1f4eSCasper H.S. Dik 	/* LINTED ALIGNMENT */
481134a1f4eSCasper H.S. Dik 	pfexec_arg_t *pap = (pfexec_arg_t *)argp;
482134a1f4eSCasper H.S. Dik 
483134a1f4eSCasper H.S. Dik 	if (asz < sizeof (pfexec_arg_t) || pap->pfa_vers != PFEXEC_ARG_VERS) {
484134a1f4eSCasper H.S. Dik 		(void) door_return(NULL, 0, NULL, 0);
485134a1f4eSCasper H.S. Dik 		return;
486134a1f4eSCasper H.S. Dik 	}
487134a1f4eSCasper H.S. Dik 
488134a1f4eSCasper H.S. Dik 	switch (pap->pfa_call) {
489134a1f4eSCasper H.S. Dik 	case PFEXEC_EXEC_ATTRS:
490134a1f4eSCasper H.S. Dik 		callback_pfexec(pap);
491134a1f4eSCasper H.S. Dik 		break;
492134a1f4eSCasper H.S. Dik 	case PFEXEC_FORCED_PRIVS:
493134a1f4eSCasper H.S. Dik 		callback_forced_privs(pap);
494134a1f4eSCasper H.S. Dik 		break;
495134a1f4eSCasper H.S. Dik 	case PFEXEC_USER_PRIVS:
496134a1f4eSCasper H.S. Dik 		callback_user_privs(pap);
497134a1f4eSCasper H.S. Dik 		break;
498134a1f4eSCasper H.S. Dik 	default:
499134a1f4eSCasper H.S. Dik 		syslog(LOG_ERR, "Bad Call: %d\n", pap->pfa_call);
500134a1f4eSCasper H.S. Dik 		break;
501134a1f4eSCasper H.S. Dik 	}
502134a1f4eSCasper H.S. Dik 
503134a1f4eSCasper H.S. Dik 	/*
504134a1f4eSCasper H.S. Dik 	 * If the door_return(ptr, size, NULL, 0) fails, make sure we
505134a1f4eSCasper H.S. Dik 	 * don't lose server threads.
506134a1f4eSCasper H.S. Dik 	 */
507134a1f4eSCasper H.S. Dik 	(void) door_return(NULL, 0, NULL, 0);
508134a1f4eSCasper H.S. Dik }
509134a1f4eSCasper H.S. Dik 
510134a1f4eSCasper H.S. Dik int
511134a1f4eSCasper H.S. Dik main(void)
512134a1f4eSCasper H.S. Dik {
513134a1f4eSCasper H.S. Dik 	const priv_impl_info_t *info;
514134a1f4eSCasper H.S. Dik 
515134a1f4eSCasper H.S. Dik 	(void) signal(SIGINT, unregister_pfexec);
516134a1f4eSCasper H.S. Dik 	(void) signal(SIGQUIT, unregister_pfexec);
517134a1f4eSCasper H.S. Dik 	(void) signal(SIGTERM, unregister_pfexec);
518134a1f4eSCasper H.S. Dik 	(void) signal(SIGHUP, unregister_pfexec);
519134a1f4eSCasper H.S. Dik 
520134a1f4eSCasper H.S. Dik 	info = getprivimplinfo();
521134a1f4eSCasper H.S. Dik 	if (info == NULL)
522134a1f4eSCasper H.S. Dik 		exit(1);
523134a1f4eSCasper H.S. Dik 
524134a1f4eSCasper H.S. Dik 	if (fork() > 0)
525134a1f4eSCasper H.S. Dik 		_exit(0);
526134a1f4eSCasper H.S. Dik 
527134a1f4eSCasper H.S. Dik 	openlog("pfexecd", LOG_PID, LOG_DAEMON);
528134a1f4eSCasper H.S. Dik 	setsz = info->priv_setsize * sizeof (priv_chunk_t);
529134a1f4eSCasper H.S. Dik 	repsz = 2 * setsz + sizeof (pfexec_reply_t);
530134a1f4eSCasper H.S. Dik 
531134a1f4eSCasper H.S. Dik 	init_isa_regex();
532134a1f4eSCasper H.S. Dik 
533134a1f4eSCasper H.S. Dik 	doorfd = door_create(callback, NULL, DOOR_REFUSE_DESC);
534134a1f4eSCasper H.S. Dik 
535134a1f4eSCasper H.S. Dik 	if (doorfd == -1 || register_pfexec(doorfd) != 0) {
536134a1f4eSCasper H.S. Dik 		perror("doorfd");
537134a1f4eSCasper H.S. Dik 		exit(1);
538134a1f4eSCasper H.S. Dik 	}
539134a1f4eSCasper H.S. Dik 
540134a1f4eSCasper H.S. Dik 	/* LINTED CONSTCOND */
541134a1f4eSCasper H.S. Dik 	while (1)
542134a1f4eSCasper H.S. Dik 		(void) sigpause(SIGINT);
543134a1f4eSCasper H.S. Dik 
544134a1f4eSCasper H.S. Dik 	return (0);
545134a1f4eSCasper H.S. Dik }
546