xref: /onnv-gate/usr/src/cmd/pfexecd/pfexecd.c (revision 12471:10160519e417)
112273SCasper.Dik@Sun.COM /*
212273SCasper.Dik@Sun.COM  * CDDL HEADER START
312273SCasper.Dik@Sun.COM  *
412273SCasper.Dik@Sun.COM  * The contents of this file are subject to the terms of the
512273SCasper.Dik@Sun.COM  * Common Development and Distribution License (the "License").
612273SCasper.Dik@Sun.COM  * You may not use this file except in compliance with the License.
712273SCasper.Dik@Sun.COM  *
812273SCasper.Dik@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
912273SCasper.Dik@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1012273SCasper.Dik@Sun.COM  * See the License for the specific language governing permissions
1112273SCasper.Dik@Sun.COM  * and limitations under the License.
1212273SCasper.Dik@Sun.COM  *
1312273SCasper.Dik@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1412273SCasper.Dik@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1512273SCasper.Dik@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1612273SCasper.Dik@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1712273SCasper.Dik@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1812273SCasper.Dik@Sun.COM  *
1912273SCasper.Dik@Sun.COM  * CDDL HEADER END
2012273SCasper.Dik@Sun.COM  *
2112273SCasper.Dik@Sun.COM  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
2212273SCasper.Dik@Sun.COM  *
2312273SCasper.Dik@Sun.COM  */
2412273SCasper.Dik@Sun.COM 
2512273SCasper.Dik@Sun.COM #define	_POSIX_PTHREAD_SEMANTICS 1
2612273SCasper.Dik@Sun.COM 
2712273SCasper.Dik@Sun.COM #include <sys/param.h>
2812273SCasper.Dik@Sun.COM #include <sys/klpd.h>
2912273SCasper.Dik@Sun.COM #include <sys/syscall.h>
3012273SCasper.Dik@Sun.COM #include <sys/systeminfo.h>
3112273SCasper.Dik@Sun.COM 
3212273SCasper.Dik@Sun.COM #include <alloca.h>
3312273SCasper.Dik@Sun.COM #include <ctype.h>
3412273SCasper.Dik@Sun.COM #include <deflt.h>
3512273SCasper.Dik@Sun.COM #include <door.h>
3612273SCasper.Dik@Sun.COM #include <errno.h>
3712273SCasper.Dik@Sun.COM #include <grp.h>
3812273SCasper.Dik@Sun.COM #include <priv.h>
3912273SCasper.Dik@Sun.COM #include <pwd.h>
4012273SCasper.Dik@Sun.COM #include <regex.h>
4112273SCasper.Dik@Sun.COM #include <secdb.h>
4212273SCasper.Dik@Sun.COM #include <signal.h>
4312273SCasper.Dik@Sun.COM #include <stdio.h>
4412273SCasper.Dik@Sun.COM #include <stdlib.h>
4512273SCasper.Dik@Sun.COM #include <string.h>
4612273SCasper.Dik@Sun.COM #include <syslog.h>
4712273SCasper.Dik@Sun.COM #include <unistd.h>
4812273SCasper.Dik@Sun.COM 
4912273SCasper.Dik@Sun.COM #include <auth_attr.h>
5012273SCasper.Dik@Sun.COM #include <exec_attr.h>
5112273SCasper.Dik@Sun.COM #include <prof_attr.h>
5212273SCasper.Dik@Sun.COM #include <user_attr.h>
5312273SCasper.Dik@Sun.COM 
5412273SCasper.Dik@Sun.COM static int doorfd = -1;
5512273SCasper.Dik@Sun.COM 
5612273SCasper.Dik@Sun.COM static size_t repsz, setsz;
5712273SCasper.Dik@Sun.COM 
5812273SCasper.Dik@Sun.COM static uid_t get_uid(const char *, boolean_t *, char *);
5912273SCasper.Dik@Sun.COM static gid_t get_gid(const char *, boolean_t *, char *);
6012273SCasper.Dik@Sun.COM static priv_set_t *get_privset(const char *, boolean_t *, char *);
6112273SCasper.Dik@Sun.COM static priv_set_t *get_granted_privs(uid_t);
6212273SCasper.Dik@Sun.COM 
6312273SCasper.Dik@Sun.COM /*
6412273SCasper.Dik@Sun.COM  * Remove the isaexec path of an executable if we can't find the
6512273SCasper.Dik@Sun.COM  * executable at the first attempt.
6612273SCasper.Dik@Sun.COM  */
6712273SCasper.Dik@Sun.COM 
6812273SCasper.Dik@Sun.COM static regex_t regc;
6912273SCasper.Dik@Sun.COM static boolean_t cansplice = B_TRUE;
7012273SCasper.Dik@Sun.COM 
7112273SCasper.Dik@Sun.COM static void
init_isa_regex(void)7212273SCasper.Dik@Sun.COM init_isa_regex(void)
7312273SCasper.Dik@Sun.COM {
7412273SCasper.Dik@Sun.COM 	char *isalist;
7512273SCasper.Dik@Sun.COM 	size_t isalen = 255;		/* wild guess */
7612273SCasper.Dik@Sun.COM 	size_t len;
7712273SCasper.Dik@Sun.COM 	long ret;
7812273SCasper.Dik@Sun.COM 	char *regexpr;
7912273SCasper.Dik@Sun.COM 	char *p;
8012273SCasper.Dik@Sun.COM 
8112273SCasper.Dik@Sun.COM 	/*
8212273SCasper.Dik@Sun.COM 	 * Extract the isalist(5) for userland from the kernel.
8312273SCasper.Dik@Sun.COM 	 */
8412273SCasper.Dik@Sun.COM 	isalist = malloc(isalen);
8512273SCasper.Dik@Sun.COM 	do {
8612273SCasper.Dik@Sun.COM 		ret = sysinfo(SI_ISALIST, isalist, isalen);
8712273SCasper.Dik@Sun.COM 		if (ret == -1l) {
8812273SCasper.Dik@Sun.COM 			free(isalist);
8912273SCasper.Dik@Sun.COM 			return;
9012273SCasper.Dik@Sun.COM 		}
9112273SCasper.Dik@Sun.COM 		if (ret > isalen) {
9212273SCasper.Dik@Sun.COM 			isalen = ret;
9312273SCasper.Dik@Sun.COM 			isalist = realloc(isalist, isalen);
9412273SCasper.Dik@Sun.COM 		} else
9512273SCasper.Dik@Sun.COM 			break;
9612273SCasper.Dik@Sun.COM 	} while (isalist != NULL);
9712273SCasper.Dik@Sun.COM 
9812273SCasper.Dik@Sun.COM 
9912273SCasper.Dik@Sun.COM 	if (isalist == NULL)
10012273SCasper.Dik@Sun.COM 		return;
10112273SCasper.Dik@Sun.COM 
10212273SCasper.Dik@Sun.COM 	/* allocate room for the regex + (/())/[^/]*$ + needed \\. */
10312273SCasper.Dik@Sun.COM #define	LEFT	"(/("
10412273SCasper.Dik@Sun.COM #define	RIGHT	"))/[^/]*$"
10512273SCasper.Dik@Sun.COM 
10612273SCasper.Dik@Sun.COM 	regexpr = alloca(ret * 2 + sizeof (LEFT RIGHT));
10712273SCasper.Dik@Sun.COM 	(void) strcpy(regexpr, LEFT);
10812273SCasper.Dik@Sun.COM 	len = strlen(regexpr);
10912273SCasper.Dik@Sun.COM 
11012273SCasper.Dik@Sun.COM 	for (p = isalist; *p; p++) {
11112273SCasper.Dik@Sun.COM 		switch (*p) {
11212273SCasper.Dik@Sun.COM 		case '+':
11312273SCasper.Dik@Sun.COM 		case '|':
11412273SCasper.Dik@Sun.COM 		case '*':
11512273SCasper.Dik@Sun.COM 		case '[':
11612273SCasper.Dik@Sun.COM 		case ']':
11712273SCasper.Dik@Sun.COM 		case '{':
11812273SCasper.Dik@Sun.COM 		case '}':
11912273SCasper.Dik@Sun.COM 		case '\\':
12012273SCasper.Dik@Sun.COM 			regexpr[len++] = '\\';
12112273SCasper.Dik@Sun.COM 		default:
12212273SCasper.Dik@Sun.COM 			regexpr[len++] = *p;
12312273SCasper.Dik@Sun.COM 			break;
12412273SCasper.Dik@Sun.COM 		case ' ':
12512273SCasper.Dik@Sun.COM 		case '\t':
12612273SCasper.Dik@Sun.COM 			regexpr[len++] = '|';
12712273SCasper.Dik@Sun.COM 			break;
12812273SCasper.Dik@Sun.COM 		}
12912273SCasper.Dik@Sun.COM 	}
13012273SCasper.Dik@Sun.COM 
13112273SCasper.Dik@Sun.COM 	free(isalist);
13212273SCasper.Dik@Sun.COM 	regexpr[len] = '\0';
13312273SCasper.Dik@Sun.COM 	(void) strcat(regexpr, RIGHT);
13412273SCasper.Dik@Sun.COM 
13512273SCasper.Dik@Sun.COM 	if (regcomp(&regc, regexpr, REG_EXTENDED) != 0)
13612273SCasper.Dik@Sun.COM 		return;
13712273SCasper.Dik@Sun.COM 
13812273SCasper.Dik@Sun.COM 	cansplice = B_TRUE;
13912273SCasper.Dik@Sun.COM }
14012273SCasper.Dik@Sun.COM 
14112273SCasper.Dik@Sun.COM #define	NMATCH	2
14212273SCasper.Dik@Sun.COM 
14312273SCasper.Dik@Sun.COM static boolean_t
removeisapath(char * path)14412273SCasper.Dik@Sun.COM removeisapath(char *path)
14512273SCasper.Dik@Sun.COM {
14612273SCasper.Dik@Sun.COM 	regmatch_t match[NMATCH];
14712273SCasper.Dik@Sun.COM 
14812273SCasper.Dik@Sun.COM 	if (!cansplice || regexec(&regc, path, NMATCH, match, 0) != 0)
14912273SCasper.Dik@Sun.COM 		return (B_FALSE);
15012273SCasper.Dik@Sun.COM 
15112273SCasper.Dik@Sun.COM 	/*
15212273SCasper.Dik@Sun.COM 	 * The first match includes the whole matched expression including the
15312273SCasper.Dik@Sun.COM 	 * end of the string.  The second match includes the "/" + "isa" and
15412273SCasper.Dik@Sun.COM 	 * that is the part we need to remove.
15512273SCasper.Dik@Sun.COM 	 */
15612273SCasper.Dik@Sun.COM 
15712273SCasper.Dik@Sun.COM 	if (match[1].rm_so == -1)
15812273SCasper.Dik@Sun.COM 		return (B_FALSE);
15912273SCasper.Dik@Sun.COM 
16012273SCasper.Dik@Sun.COM 	/* match[0].rm_eo == strlen(path) */
16112273SCasper.Dik@Sun.COM 	(void) memmove(path + match[1].rm_so, path + match[1].rm_eo,
16212273SCasper.Dik@Sun.COM 	    match[0].rm_eo - match[1].rm_eo + 1);
16312273SCasper.Dik@Sun.COM 
16412273SCasper.Dik@Sun.COM 	return (B_TRUE);
16512273SCasper.Dik@Sun.COM }
16612273SCasper.Dik@Sun.COM 
16712273SCasper.Dik@Sun.COM static int
register_pfexec(int fd)16812273SCasper.Dik@Sun.COM register_pfexec(int fd)
16912273SCasper.Dik@Sun.COM {
17012273SCasper.Dik@Sun.COM 	int ret = syscall(SYS_privsys, PRIVSYS_PFEXEC_REG, fd);
17112273SCasper.Dik@Sun.COM 
17212273SCasper.Dik@Sun.COM 	return (ret);
17312273SCasper.Dik@Sun.COM }
17412273SCasper.Dik@Sun.COM 
17512273SCasper.Dik@Sun.COM /* ARGSUSED */
17612273SCasper.Dik@Sun.COM static void
unregister_pfexec(int sig)17712273SCasper.Dik@Sun.COM unregister_pfexec(int sig)
17812273SCasper.Dik@Sun.COM {
17912273SCasper.Dik@Sun.COM 	if (doorfd != -1)
18012273SCasper.Dik@Sun.COM 		(void) syscall(SYS_privsys, PRIVSYS_PFEXEC_UNREG, doorfd);
18112273SCasper.Dik@Sun.COM 	_exit(0);
18212273SCasper.Dik@Sun.COM }
18312273SCasper.Dik@Sun.COM 
18412273SCasper.Dik@Sun.COM static int
alldigits(const char * s)18512273SCasper.Dik@Sun.COM alldigits(const char *s)
18612273SCasper.Dik@Sun.COM {
18712273SCasper.Dik@Sun.COM 	int c;
18812273SCasper.Dik@Sun.COM 
18912273SCasper.Dik@Sun.COM 	if (*s == '\0')
19012273SCasper.Dik@Sun.COM 		return (0);
19112273SCasper.Dik@Sun.COM 
19212273SCasper.Dik@Sun.COM 	while ((c = *s++) != '\0') {
19312273SCasper.Dik@Sun.COM 		if (!isdigit(c)) {
19412273SCasper.Dik@Sun.COM 			return (0);
19512273SCasper.Dik@Sun.COM 		}
19612273SCasper.Dik@Sun.COM 	}
19712273SCasper.Dik@Sun.COM 
19812273SCasper.Dik@Sun.COM 	return (1);
19912273SCasper.Dik@Sun.COM }
20012273SCasper.Dik@Sun.COM 
20112273SCasper.Dik@Sun.COM static uid_t
get_uid(const char * v,boolean_t * ok,char * path)20212273SCasper.Dik@Sun.COM get_uid(const char *v, boolean_t *ok, char *path)
20312273SCasper.Dik@Sun.COM {
20412273SCasper.Dik@Sun.COM 	struct passwd *pwd, pwdm;
20512273SCasper.Dik@Sun.COM 	char buf[1024];
20612273SCasper.Dik@Sun.COM 
20712273SCasper.Dik@Sun.COM 	if (getpwnam_r(v, &pwdm, buf, sizeof (buf), &pwd) == 0 && pwd != NULL)
20812273SCasper.Dik@Sun.COM 		return (pwd->pw_uid);
20912273SCasper.Dik@Sun.COM 
21012273SCasper.Dik@Sun.COM 	if (alldigits(v))
21112273SCasper.Dik@Sun.COM 		return (atoi(v));
21212273SCasper.Dik@Sun.COM 
21312273SCasper.Dik@Sun.COM 	*ok = B_FALSE;
21412273SCasper.Dik@Sun.COM 	syslog(LOG_ERR, "%s: %s: unknown username\n", path, v);
21512273SCasper.Dik@Sun.COM 	return ((uid_t)-1);
21612273SCasper.Dik@Sun.COM }
21712273SCasper.Dik@Sun.COM 
21812273SCasper.Dik@Sun.COM static uid_t
get_gid(const char * v,boolean_t * ok,char * path)21912273SCasper.Dik@Sun.COM get_gid(const char *v, boolean_t *ok, char *path)
22012273SCasper.Dik@Sun.COM {
22112273SCasper.Dik@Sun.COM 	struct group *grp, grpm;
22212273SCasper.Dik@Sun.COM 	char buf[1024];
22312273SCasper.Dik@Sun.COM 
22412273SCasper.Dik@Sun.COM 	if (getgrnam_r(v, &grpm, buf, sizeof (buf), &grp) == 0 && grp != NULL)
22512273SCasper.Dik@Sun.COM 		return (grp->gr_gid);
22612273SCasper.Dik@Sun.COM 
22712273SCasper.Dik@Sun.COM 	if (alldigits(v))
22812273SCasper.Dik@Sun.COM 		return (atoi(v));
22912273SCasper.Dik@Sun.COM 
23012273SCasper.Dik@Sun.COM 	*ok = B_FALSE;
23112273SCasper.Dik@Sun.COM 	syslog(LOG_ERR, "%s: %s: unknown groupname\n", path, v);
23212273SCasper.Dik@Sun.COM 	return ((gid_t)-1);
23312273SCasper.Dik@Sun.COM }
23412273SCasper.Dik@Sun.COM 
23512273SCasper.Dik@Sun.COM static priv_set_t *
get_privset(const char * s,boolean_t * ok,char * path)23612273SCasper.Dik@Sun.COM get_privset(const char *s, boolean_t *ok, char *path)
23712273SCasper.Dik@Sun.COM {
23812273SCasper.Dik@Sun.COM 	priv_set_t *res;
23912273SCasper.Dik@Sun.COM 
24012273SCasper.Dik@Sun.COM 	if ((res = priv_str_to_set(s, ",", NULL)) == NULL) {
24112273SCasper.Dik@Sun.COM 		syslog(LOG_ERR, "%s: %s: bad privilege set\n", path, s);
24212273SCasper.Dik@Sun.COM 		if (ok != NULL)
24312273SCasper.Dik@Sun.COM 			*ok = B_FALSE;
24412273SCasper.Dik@Sun.COM 	}
24512273SCasper.Dik@Sun.COM 	return (res);
24612273SCasper.Dik@Sun.COM }
24712273SCasper.Dik@Sun.COM 
24812273SCasper.Dik@Sun.COM /*ARGSUSED*/
24912273SCasper.Dik@Sun.COM static int
ggp_callback(const char * prof,kva_t * attr,void * ctxt,void * vres)25012273SCasper.Dik@Sun.COM ggp_callback(const char *prof, kva_t *attr, void *ctxt, void *vres)
25112273SCasper.Dik@Sun.COM {
25212273SCasper.Dik@Sun.COM 	priv_set_t *res = vres;
25312273SCasper.Dik@Sun.COM 	char *privs;
25412273SCasper.Dik@Sun.COM 
25512273SCasper.Dik@Sun.COM 	if (attr == NULL)
25612273SCasper.Dik@Sun.COM 		return (0);
25712273SCasper.Dik@Sun.COM 
25812273SCasper.Dik@Sun.COM 	/* get privs from this profile */
25912273SCasper.Dik@Sun.COM 	privs = kva_match(attr, PROFATTR_PRIVS_KW);
26012273SCasper.Dik@Sun.COM 	if (privs != NULL) {
26112273SCasper.Dik@Sun.COM 		priv_set_t *tmp = priv_str_to_set(privs, ",", NULL);
26212273SCasper.Dik@Sun.COM 		if (tmp != NULL) {
26312273SCasper.Dik@Sun.COM 			priv_union(tmp, res);
26412273SCasper.Dik@Sun.COM 			priv_freeset(tmp);
26512273SCasper.Dik@Sun.COM 		}
26612273SCasper.Dik@Sun.COM 	}
26712273SCasper.Dik@Sun.COM 
26812273SCasper.Dik@Sun.COM 	return (0);
26912273SCasper.Dik@Sun.COM }
27012273SCasper.Dik@Sun.COM 
27112273SCasper.Dik@Sun.COM /*
27212273SCasper.Dik@Sun.COM  * This routine exists on failure and returns NULL if no granted privileges
27312273SCasper.Dik@Sun.COM  * are set.
27412273SCasper.Dik@Sun.COM  */
27512273SCasper.Dik@Sun.COM static priv_set_t *
get_granted_privs(uid_t uid)27612273SCasper.Dik@Sun.COM get_granted_privs(uid_t uid)
27712273SCasper.Dik@Sun.COM {
27812273SCasper.Dik@Sun.COM 	priv_set_t *res;
27912273SCasper.Dik@Sun.COM 	struct passwd *pwd, pwdm;
28012273SCasper.Dik@Sun.COM 	char buf[1024];
28112273SCasper.Dik@Sun.COM 
28212273SCasper.Dik@Sun.COM 	if (getpwuid_r(uid, &pwdm, buf, sizeof (buf), &pwd) != 0 || pwd == NULL)
28312273SCasper.Dik@Sun.COM 		return (NULL);
28412273SCasper.Dik@Sun.COM 
28512273SCasper.Dik@Sun.COM 	res = priv_allocset();
28612273SCasper.Dik@Sun.COM 	if (res == NULL)
28712273SCasper.Dik@Sun.COM 		return (NULL);
28812273SCasper.Dik@Sun.COM 
28912273SCasper.Dik@Sun.COM 	priv_emptyset(res);
29012273SCasper.Dik@Sun.COM 
29112273SCasper.Dik@Sun.COM 	(void) _enum_profs(pwd->pw_name, ggp_callback, NULL, res);
29212273SCasper.Dik@Sun.COM 
29312273SCasper.Dik@Sun.COM 	return (res);
29412273SCasper.Dik@Sun.COM }
29512273SCasper.Dik@Sun.COM 
29612273SCasper.Dik@Sun.COM static void
callback_forced_privs(pfexec_arg_t * pap)29712273SCasper.Dik@Sun.COM callback_forced_privs(pfexec_arg_t *pap)
29812273SCasper.Dik@Sun.COM {
29912273SCasper.Dik@Sun.COM 	execattr_t *exec;
30012273SCasper.Dik@Sun.COM 	char *value;
30112273SCasper.Dik@Sun.COM 	priv_set_t *fset;
30212273SCasper.Dik@Sun.COM 	void *res = alloca(setsz);
30312273SCasper.Dik@Sun.COM 
30412273SCasper.Dik@Sun.COM 	/* Empty set signifies no forced privileges. */
30512273SCasper.Dik@Sun.COM 	priv_emptyset(res);
30612273SCasper.Dik@Sun.COM 
30712273SCasper.Dik@Sun.COM 	exec = getexecprof("Forced Privilege", KV_COMMAND, pap->pfa_path,
30812273SCasper.Dik@Sun.COM 	    GET_ONE);
30912273SCasper.Dik@Sun.COM 
31012273SCasper.Dik@Sun.COM 	if (exec == NULL && removeisapath(pap->pfa_path)) {
31112273SCasper.Dik@Sun.COM 		exec = getexecprof("Forced Privilege", KV_COMMAND,
31212273SCasper.Dik@Sun.COM 		    pap->pfa_path, GET_ONE);
31312273SCasper.Dik@Sun.COM 	}
31412273SCasper.Dik@Sun.COM 
31512273SCasper.Dik@Sun.COM 	if (exec == NULL) {
31612273SCasper.Dik@Sun.COM 		(void) door_return(res, setsz, NULL, 0);
31712273SCasper.Dik@Sun.COM 		return;
31812273SCasper.Dik@Sun.COM 	}
31912273SCasper.Dik@Sun.COM 
32012273SCasper.Dik@Sun.COM 	if ((value = kva_match(exec->attr, EXECATTR_IPRIV_KW)) == NULL ||
32112273SCasper.Dik@Sun.COM 	    (fset = get_privset(value, NULL, pap->pfa_path)) == NULL) {
32212273SCasper.Dik@Sun.COM 		free_execattr(exec);
32312273SCasper.Dik@Sun.COM 		(void) door_return(res, setsz, NULL, 0);
32412273SCasper.Dik@Sun.COM 		return;
32512273SCasper.Dik@Sun.COM 	}
32612273SCasper.Dik@Sun.COM 
32712273SCasper.Dik@Sun.COM 	priv_copyset(fset, res);
32812273SCasper.Dik@Sun.COM 	priv_freeset(fset);
32912273SCasper.Dik@Sun.COM 
33012273SCasper.Dik@Sun.COM 	free_execattr(exec);
33112273SCasper.Dik@Sun.COM 	(void) door_return(res, setsz, NULL, 0);
33212273SCasper.Dik@Sun.COM }
33312273SCasper.Dik@Sun.COM 
33412273SCasper.Dik@Sun.COM static void
callback_user_privs(pfexec_arg_t * pap)33512273SCasper.Dik@Sun.COM callback_user_privs(pfexec_arg_t *pap)
33612273SCasper.Dik@Sun.COM {
33712273SCasper.Dik@Sun.COM 	priv_set_t *gset, *wset;
33812273SCasper.Dik@Sun.COM 	uint32_t res;
33912273SCasper.Dik@Sun.COM 
34012273SCasper.Dik@Sun.COM 	wset = (priv_set_t *)&pap->pfa_buf;
34112273SCasper.Dik@Sun.COM 	gset = get_granted_privs(pap->pfa_uid);
34212273SCasper.Dik@Sun.COM 
34312273SCasper.Dik@Sun.COM 	res = priv_issubset(wset, gset);
34412273SCasper.Dik@Sun.COM 	priv_freeset(gset);
34512273SCasper.Dik@Sun.COM 
34612273SCasper.Dik@Sun.COM 	(void) door_return((char *)&res, sizeof (res), NULL, 0);
34712273SCasper.Dik@Sun.COM }
34812273SCasper.Dik@Sun.COM 
34912273SCasper.Dik@Sun.COM static void
callback_pfexec(pfexec_arg_t * pap)35012273SCasper.Dik@Sun.COM callback_pfexec(pfexec_arg_t *pap)
35112273SCasper.Dik@Sun.COM {
35212273SCasper.Dik@Sun.COM 	pfexec_reply_t *res = alloca(repsz);
35312273SCasper.Dik@Sun.COM 	uid_t uid, euid, uuid;
35412273SCasper.Dik@Sun.COM 	gid_t gid, egid;
35512273SCasper.Dik@Sun.COM 	struct passwd pw, *pwd;
35612273SCasper.Dik@Sun.COM 	char buf[1024];
357*12471SCasper.Dik@Sun.COM 	execattr_t *exec = NULL;
35812273SCasper.Dik@Sun.COM 	char *value;
35912273SCasper.Dik@Sun.COM 	priv_set_t *lset, *iset;
36012273SCasper.Dik@Sun.COM 	size_t mysz = repsz - 2 * setsz;
36112273SCasper.Dik@Sun.COM 	char *path = pap->pfa_path;
36212273SCasper.Dik@Sun.COM 
36312273SCasper.Dik@Sun.COM 	uuid = pap->pfa_uid;
36412273SCasper.Dik@Sun.COM 
36512273SCasper.Dik@Sun.COM 	if (getpwuid_r(uuid, &pw, buf, sizeof (buf), &pwd) != 0 || pwd == NULL)
36612273SCasper.Dik@Sun.COM 		goto stdexec;
36712273SCasper.Dik@Sun.COM 
36812273SCasper.Dik@Sun.COM 	exec = getexecuser(pwd->pw_name, KV_COMMAND, path, GET_ONE);
36912273SCasper.Dik@Sun.COM 
370*12471SCasper.Dik@Sun.COM 	if ((exec == NULL || exec->attr == NULL) && removeisapath(path)) {
371*12471SCasper.Dik@Sun.COM 		free_execattr(exec);
37212273SCasper.Dik@Sun.COM 		exec = getexecuser(pwd->pw_name, KV_COMMAND, path, GET_ONE);
373*12471SCasper.Dik@Sun.COM 	}
37412273SCasper.Dik@Sun.COM 
37512273SCasper.Dik@Sun.COM 	if (exec == NULL) {
37612273SCasper.Dik@Sun.COM 		res->pfr_allowed = B_FALSE;
37712273SCasper.Dik@Sun.COM 		goto ret;
37812273SCasper.Dik@Sun.COM 	}
37912273SCasper.Dik@Sun.COM 
38012273SCasper.Dik@Sun.COM 	if (exec->attr == NULL)
38112273SCasper.Dik@Sun.COM 		goto stdexec;
38212273SCasper.Dik@Sun.COM 
38312273SCasper.Dik@Sun.COM 	/* Found in execattr, so clearly we can use it */
38412273SCasper.Dik@Sun.COM 	res->pfr_allowed = B_TRUE;
38512273SCasper.Dik@Sun.COM 
38612273SCasper.Dik@Sun.COM 	uid = euid = (uid_t)-1;
38712273SCasper.Dik@Sun.COM 	gid = egid = (gid_t)-1;
38812273SCasper.Dik@Sun.COM 	lset = iset = NULL;
38912273SCasper.Dik@Sun.COM 
39012273SCasper.Dik@Sun.COM 	/*
39112273SCasper.Dik@Sun.COM 	 * If there's an error in parsing uid, gid, privs, then return
39212273SCasper.Dik@Sun.COM 	 * failure.
39312273SCasper.Dik@Sun.COM 	 */
39412273SCasper.Dik@Sun.COM 	if ((value = kva_match(exec->attr, EXECATTR_UID_KW)) != NULL)
39512273SCasper.Dik@Sun.COM 		euid = uid = get_uid(value, &res->pfr_allowed, path);
39612273SCasper.Dik@Sun.COM 
39712273SCasper.Dik@Sun.COM 	if ((value = kva_match(exec->attr, EXECATTR_GID_KW)) != NULL)
39812273SCasper.Dik@Sun.COM 		egid = gid = get_gid(value, &res->pfr_allowed, path);
39912273SCasper.Dik@Sun.COM 
40012273SCasper.Dik@Sun.COM 	if ((value = kva_match(exec->attr, EXECATTR_EUID_KW)) != NULL)
40112273SCasper.Dik@Sun.COM 		euid = get_uid(value, &res->pfr_allowed, path);
40212273SCasper.Dik@Sun.COM 
40312273SCasper.Dik@Sun.COM 	if ((value = kva_match(exec->attr, EXECATTR_EGID_KW)) != NULL)
40412273SCasper.Dik@Sun.COM 		egid = get_gid(value, &res->pfr_allowed, path);
40512273SCasper.Dik@Sun.COM 
40612273SCasper.Dik@Sun.COM 	if ((value = kva_match(exec->attr, EXECATTR_LPRIV_KW)) != NULL)
40712273SCasper.Dik@Sun.COM 		lset = get_privset(value, &res->pfr_allowed, path);
40812273SCasper.Dik@Sun.COM 
40912273SCasper.Dik@Sun.COM 	if ((value = kva_match(exec->attr, EXECATTR_IPRIV_KW)) != NULL)
41012273SCasper.Dik@Sun.COM 		iset = get_privset(value, &res->pfr_allowed, path);
41112273SCasper.Dik@Sun.COM 
41212273SCasper.Dik@Sun.COM 	/*
41312273SCasper.Dik@Sun.COM 	 * Remove LD_* variables in the kernel when the runtime linker might
41412273SCasper.Dik@Sun.COM 	 * use them later on because the uids are equal.
41512273SCasper.Dik@Sun.COM 	 */
41612273SCasper.Dik@Sun.COM 	res->pfr_scrubenv = (uid != (uid_t)-1 && euid == uid) ||
41712273SCasper.Dik@Sun.COM 	    (gid != (gid_t)-1 && egid == gid) || iset != NULL;
41812273SCasper.Dik@Sun.COM 
41912273SCasper.Dik@Sun.COM 	res->pfr_euid = euid;
42012273SCasper.Dik@Sun.COM 	res->pfr_ruid = uid;
42112273SCasper.Dik@Sun.COM 	res->pfr_egid = egid;
42212273SCasper.Dik@Sun.COM 	res->pfr_rgid = gid;
42312273SCasper.Dik@Sun.COM 
42412273SCasper.Dik@Sun.COM 	/* Now add the privilege sets */
42512273SCasper.Dik@Sun.COM 	res->pfr_ioff = res->pfr_loff = 0;
42612273SCasper.Dik@Sun.COM 	if (iset != NULL) {
42712273SCasper.Dik@Sun.COM 		res->pfr_ioff = mysz;
42812273SCasper.Dik@Sun.COM 		priv_copyset(iset, PFEXEC_REPLY_IPRIV(res));
42912273SCasper.Dik@Sun.COM 		mysz += setsz;
43012273SCasper.Dik@Sun.COM 		priv_freeset(iset);
43112273SCasper.Dik@Sun.COM 	}
43212273SCasper.Dik@Sun.COM 	if (lset != NULL) {
43312273SCasper.Dik@Sun.COM 		res->pfr_loff = mysz;
43412273SCasper.Dik@Sun.COM 		priv_copyset(lset, PFEXEC_REPLY_LPRIV(res));
43512273SCasper.Dik@Sun.COM 		mysz += setsz;
43612273SCasper.Dik@Sun.COM 		priv_freeset(lset);
43712273SCasper.Dik@Sun.COM 	}
43812273SCasper.Dik@Sun.COM 
43912273SCasper.Dik@Sun.COM 	res->pfr_setcred = uid != (uid_t)-1 || euid != (uid_t)-1 ||
44012273SCasper.Dik@Sun.COM 	    egid != (gid_t)-1 || gid != (gid_t)-1 || iset != NULL ||
44112273SCasper.Dik@Sun.COM 	    lset != NULL;
44212273SCasper.Dik@Sun.COM 
44312273SCasper.Dik@Sun.COM 	/* If the real uid changes, we stop running under a profile shell */
44412273SCasper.Dik@Sun.COM 	res->pfr_clearflag = uid != (uid_t)-1 && uid != uuid;
44512273SCasper.Dik@Sun.COM 	free_execattr(exec);
44612273SCasper.Dik@Sun.COM ret:
44712273SCasper.Dik@Sun.COM 	(void) door_return((char *)res, mysz, NULL, 0);
44812273SCasper.Dik@Sun.COM 	return;
44912273SCasper.Dik@Sun.COM 
45012273SCasper.Dik@Sun.COM stdexec:
451*12471SCasper.Dik@Sun.COM 	free_execattr(exec);
452*12471SCasper.Dik@Sun.COM 
45312273SCasper.Dik@Sun.COM 	res->pfr_scrubenv = B_FALSE;
45412273SCasper.Dik@Sun.COM 	res->pfr_setcred = B_FALSE;
45512273SCasper.Dik@Sun.COM 	res->pfr_allowed = B_TRUE;
45612273SCasper.Dik@Sun.COM 
45712273SCasper.Dik@Sun.COM 	(void) door_return((char *)res, mysz, NULL, 0);
45812273SCasper.Dik@Sun.COM }
45912273SCasper.Dik@Sun.COM 
46012273SCasper.Dik@Sun.COM /* ARGSUSED */
46112273SCasper.Dik@Sun.COM static void
callback(void * cookie,char * argp,size_t asz,door_desc_t * dp,uint_t ndesc)46212273SCasper.Dik@Sun.COM callback(void *cookie, char *argp, size_t asz, door_desc_t *dp, uint_t ndesc)
46312273SCasper.Dik@Sun.COM {
46412273SCasper.Dik@Sun.COM 	/* LINTED ALIGNMENT */
46512273SCasper.Dik@Sun.COM 	pfexec_arg_t *pap = (pfexec_arg_t *)argp;
46612273SCasper.Dik@Sun.COM 
46712273SCasper.Dik@Sun.COM 	if (asz < sizeof (pfexec_arg_t) || pap->pfa_vers != PFEXEC_ARG_VERS) {
46812273SCasper.Dik@Sun.COM 		(void) door_return(NULL, 0, NULL, 0);
46912273SCasper.Dik@Sun.COM 		return;
47012273SCasper.Dik@Sun.COM 	}
47112273SCasper.Dik@Sun.COM 
47212273SCasper.Dik@Sun.COM 	switch (pap->pfa_call) {
47312273SCasper.Dik@Sun.COM 	case PFEXEC_EXEC_ATTRS:
47412273SCasper.Dik@Sun.COM 		callback_pfexec(pap);
47512273SCasper.Dik@Sun.COM 		break;
47612273SCasper.Dik@Sun.COM 	case PFEXEC_FORCED_PRIVS:
47712273SCasper.Dik@Sun.COM 		callback_forced_privs(pap);
47812273SCasper.Dik@Sun.COM 		break;
47912273SCasper.Dik@Sun.COM 	case PFEXEC_USER_PRIVS:
48012273SCasper.Dik@Sun.COM 		callback_user_privs(pap);
48112273SCasper.Dik@Sun.COM 		break;
48212273SCasper.Dik@Sun.COM 	default:
48312273SCasper.Dik@Sun.COM 		syslog(LOG_ERR, "Bad Call: %d\n", pap->pfa_call);
48412273SCasper.Dik@Sun.COM 		break;
48512273SCasper.Dik@Sun.COM 	}
48612273SCasper.Dik@Sun.COM 
48712273SCasper.Dik@Sun.COM 	/*
48812273SCasper.Dik@Sun.COM 	 * If the door_return(ptr, size, NULL, 0) fails, make sure we
48912273SCasper.Dik@Sun.COM 	 * don't lose server threads.
49012273SCasper.Dik@Sun.COM 	 */
49112273SCasper.Dik@Sun.COM 	(void) door_return(NULL, 0, NULL, 0);
49212273SCasper.Dik@Sun.COM }
49312273SCasper.Dik@Sun.COM 
49412273SCasper.Dik@Sun.COM int
main(void)49512273SCasper.Dik@Sun.COM main(void)
49612273SCasper.Dik@Sun.COM {
49712273SCasper.Dik@Sun.COM 	const priv_impl_info_t *info;
49812273SCasper.Dik@Sun.COM 
49912273SCasper.Dik@Sun.COM 	(void) signal(SIGINT, unregister_pfexec);
50012273SCasper.Dik@Sun.COM 	(void) signal(SIGQUIT, unregister_pfexec);
50112273SCasper.Dik@Sun.COM 	(void) signal(SIGTERM, unregister_pfexec);
50212273SCasper.Dik@Sun.COM 	(void) signal(SIGHUP, unregister_pfexec);
50312273SCasper.Dik@Sun.COM 
50412273SCasper.Dik@Sun.COM 	info = getprivimplinfo();
50512273SCasper.Dik@Sun.COM 	if (info == NULL)
50612273SCasper.Dik@Sun.COM 		exit(1);
50712273SCasper.Dik@Sun.COM 
50812273SCasper.Dik@Sun.COM 	if (fork() > 0)
50912273SCasper.Dik@Sun.COM 		_exit(0);
51012273SCasper.Dik@Sun.COM 
51112273SCasper.Dik@Sun.COM 	openlog("pfexecd", LOG_PID, LOG_DAEMON);
51212273SCasper.Dik@Sun.COM 	setsz = info->priv_setsize * sizeof (priv_chunk_t);
51312273SCasper.Dik@Sun.COM 	repsz = 2 * setsz + sizeof (pfexec_reply_t);
51412273SCasper.Dik@Sun.COM 
51512273SCasper.Dik@Sun.COM 	init_isa_regex();
51612273SCasper.Dik@Sun.COM 
51712273SCasper.Dik@Sun.COM 	doorfd = door_create(callback, NULL, DOOR_REFUSE_DESC);
51812273SCasper.Dik@Sun.COM 
51912273SCasper.Dik@Sun.COM 	if (doorfd == -1 || register_pfexec(doorfd) != 0) {
52012273SCasper.Dik@Sun.COM 		perror("doorfd");
52112273SCasper.Dik@Sun.COM 		exit(1);
52212273SCasper.Dik@Sun.COM 	}
52312273SCasper.Dik@Sun.COM 
52412273SCasper.Dik@Sun.COM 	/* LINTED CONSTCOND */
52512273SCasper.Dik@Sun.COM 	while (1)
52612273SCasper.Dik@Sun.COM 		(void) sigpause(SIGINT);
52712273SCasper.Dik@Sun.COM 
52812273SCasper.Dik@Sun.COM 	return (0);
52912273SCasper.Dik@Sun.COM }
530