xref: /freebsd/contrib/openbsm/bin/auditdistd/sandbox.c (revision aa77200569e397d6ff1fdb4d255d0fa254d0a128)
1*aa772005SRobert Watson /*-
2*aa772005SRobert Watson  * Copyright (c) 2012 The FreeBSD Foundation
3*aa772005SRobert Watson  * All rights reserved.
4*aa772005SRobert Watson  *
5*aa772005SRobert Watson  * This software was developed by Pawel Jakub Dawidek under sponsorship from
6*aa772005SRobert Watson  * the FreeBSD Foundation.
7*aa772005SRobert Watson  *
8*aa772005SRobert Watson  * Redistribution and use in source and binary forms, with or without
9*aa772005SRobert Watson  * modification, are permitted provided that the following conditions
10*aa772005SRobert Watson  * are met:
11*aa772005SRobert Watson  * 1. Redistributions of source code must retain the above copyright
12*aa772005SRobert Watson  *    notice, this list of conditions and the following disclaimer.
13*aa772005SRobert Watson  * 2. Redistributions in binary form must reproduce the above copyright
14*aa772005SRobert Watson  *    notice, this list of conditions and the following disclaimer in the
15*aa772005SRobert Watson  *    documentation and/or other materials provided with the distribution.
16*aa772005SRobert Watson  *
17*aa772005SRobert Watson  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18*aa772005SRobert Watson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19*aa772005SRobert Watson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20*aa772005SRobert Watson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21*aa772005SRobert Watson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22*aa772005SRobert Watson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23*aa772005SRobert Watson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24*aa772005SRobert Watson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25*aa772005SRobert Watson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26*aa772005SRobert Watson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27*aa772005SRobert Watson  * SUCH DAMAGE.
28*aa772005SRobert Watson  *
29*aa772005SRobert Watson  * $P4: //depot/projects/trustedbsd/openbsm/bin/auditdistd/sandbox.c#3 $
30*aa772005SRobert Watson  */
31*aa772005SRobert Watson 
32*aa772005SRobert Watson #include <config/config.h>
33*aa772005SRobert Watson 
34*aa772005SRobert Watson #include <sys/param.h>
35*aa772005SRobert Watson #ifdef HAVE_JAIL
36*aa772005SRobert Watson #include <sys/jail.h>
37*aa772005SRobert Watson #endif
38*aa772005SRobert Watson #ifdef HAVE_CAP_ENTER
39*aa772005SRobert Watson #include <sys/capability.h>
40*aa772005SRobert Watson #endif
41*aa772005SRobert Watson 
42*aa772005SRobert Watson #include <errno.h>
43*aa772005SRobert Watson #include <pwd.h>
44*aa772005SRobert Watson #include <stdarg.h>
45*aa772005SRobert Watson #include <stdbool.h>
46*aa772005SRobert Watson #include <stdio.h>
47*aa772005SRobert Watson #include <stdlib.h>
48*aa772005SRobert Watson #include <strings.h>
49*aa772005SRobert Watson #include <unistd.h>
50*aa772005SRobert Watson 
51*aa772005SRobert Watson #include "pjdlog.h"
52*aa772005SRobert Watson #include "sandbox.h"
53*aa772005SRobert Watson 
54*aa772005SRobert Watson static int
55*aa772005SRobert Watson groups_compare(const void *grp0, const void *grp1)
56*aa772005SRobert Watson {
57*aa772005SRobert Watson 	gid_t gr0 = *(const gid_t *)grp0;
58*aa772005SRobert Watson 	gid_t gr1 = *(const gid_t *)grp1;
59*aa772005SRobert Watson 
60*aa772005SRobert Watson 	return (gr0 <= gr1 ? (gr0 < gr1 ? -1 : 0) : 1);
61*aa772005SRobert Watson 
62*aa772005SRobert Watson }
63*aa772005SRobert Watson 
64*aa772005SRobert Watson int
65*aa772005SRobert Watson sandbox(const char *user, bool capsicum, const char *fmt, ...)
66*aa772005SRobert Watson {
67*aa772005SRobert Watson #ifdef HAVE_JAIL
68*aa772005SRobert Watson 	struct jail jailst;
69*aa772005SRobert Watson 	char *jailhost;
70*aa772005SRobert Watson 	va_list ap;
71*aa772005SRobert Watson #endif
72*aa772005SRobert Watson 	struct passwd *pw;
73*aa772005SRobert Watson 	uid_t ruid, euid;
74*aa772005SRobert Watson 	gid_t rgid, egid;
75*aa772005SRobert Watson #ifdef HAVE_GETRESUID
76*aa772005SRobert Watson 	uid_t suid;
77*aa772005SRobert Watson #endif
78*aa772005SRobert Watson #ifdef HAVE_GETRESGID
79*aa772005SRobert Watson 	gid_t sgid;
80*aa772005SRobert Watson #endif
81*aa772005SRobert Watson 	gid_t *groups, *ggroups;
82*aa772005SRobert Watson 	bool jailed;
83*aa772005SRobert Watson 	int ngroups, ret;
84*aa772005SRobert Watson 
85*aa772005SRobert Watson 	PJDLOG_ASSERT(user != NULL);
86*aa772005SRobert Watson 	PJDLOG_ASSERT(fmt != NULL);
87*aa772005SRobert Watson 
88*aa772005SRobert Watson 	ret = -1;
89*aa772005SRobert Watson 	groups = NULL;
90*aa772005SRobert Watson 	ggroups = NULL;
91*aa772005SRobert Watson 
92*aa772005SRobert Watson 	/*
93*aa772005SRobert Watson 	 * According to getpwnam(3) we have to clear errno before calling the
94*aa772005SRobert Watson 	 * function to be able to distinguish between an error and missing
95*aa772005SRobert Watson 	 * entry (with is not treated as error by getpwnam(3)).
96*aa772005SRobert Watson 	 */
97*aa772005SRobert Watson 	errno = 0;
98*aa772005SRobert Watson 	pw = getpwnam(user);
99*aa772005SRobert Watson 	if (pw == NULL) {
100*aa772005SRobert Watson 		if (errno != 0) {
101*aa772005SRobert Watson 			pjdlog_errno(LOG_ERR,
102*aa772005SRobert Watson 			    "Unable to find info about '%s' user", user);
103*aa772005SRobert Watson 			goto out;
104*aa772005SRobert Watson 		} else {
105*aa772005SRobert Watson 			pjdlog_error("'%s' user doesn't exist.", user);
106*aa772005SRobert Watson 			errno = ENOENT;
107*aa772005SRobert Watson 			goto out;
108*aa772005SRobert Watson 		}
109*aa772005SRobert Watson 	}
110*aa772005SRobert Watson 
111*aa772005SRobert Watson 	ngroups = sysconf(_SC_NGROUPS_MAX);
112*aa772005SRobert Watson 	if (ngroups == -1) {
113*aa772005SRobert Watson 		pjdlog_errno(LOG_WARNING,
114*aa772005SRobert Watson 		    "Unable to obtain maximum number of groups");
115*aa772005SRobert Watson 		ngroups = NGROUPS_MAX;
116*aa772005SRobert Watson 	}
117*aa772005SRobert Watson 	ngroups++;	/* For base gid. */
118*aa772005SRobert Watson 	groups = malloc(sizeof(groups[0]) * ngroups);
119*aa772005SRobert Watson 	if (groups == NULL) {
120*aa772005SRobert Watson 		pjdlog_error("Unable to allocate memory for %d groups.",
121*aa772005SRobert Watson 		    ngroups);
122*aa772005SRobert Watson 		goto out;
123*aa772005SRobert Watson 	}
124*aa772005SRobert Watson 	if (getgrouplist(user, pw->pw_gid, groups, &ngroups) == -1) {
125*aa772005SRobert Watson 		pjdlog_error("Unable to obtain groups of user %s.", user);
126*aa772005SRobert Watson 		goto out;
127*aa772005SRobert Watson 	}
128*aa772005SRobert Watson 
129*aa772005SRobert Watson #ifdef HAVE_JAIL
130*aa772005SRobert Watson 	va_start(ap, fmt);
131*aa772005SRobert Watson 	(void)vasprintf(&jailhost, fmt, ap);
132*aa772005SRobert Watson 	va_end(ap);
133*aa772005SRobert Watson 	if (jailhost == NULL) {
134*aa772005SRobert Watson 		pjdlog_error("Unable to allocate memory for jail host name.");
135*aa772005SRobert Watson 		goto out;
136*aa772005SRobert Watson 	}
137*aa772005SRobert Watson 	bzero(&jailst, sizeof(jailst));
138*aa772005SRobert Watson 	jailst.version = JAIL_API_VERSION;
139*aa772005SRobert Watson 	jailst.path = pw->pw_dir;
140*aa772005SRobert Watson 	jailst.hostname = jailhost;
141*aa772005SRobert Watson 	if (jail(&jailst) >= 0) {
142*aa772005SRobert Watson 		jailed = true;
143*aa772005SRobert Watson 	} else {
144*aa772005SRobert Watson 		jailed = false;
145*aa772005SRobert Watson 		pjdlog_errno(LOG_WARNING,
146*aa772005SRobert Watson 		    "Unable to jail to directory %s", pw->pw_dir);
147*aa772005SRobert Watson 	}
148*aa772005SRobert Watson 	free(jailhost);
149*aa772005SRobert Watson #else	/* !HAVE_JAIL */
150*aa772005SRobert Watson 	jailed = false;
151*aa772005SRobert Watson #endif	/* !HAVE_JAIL */
152*aa772005SRobert Watson 
153*aa772005SRobert Watson 	if (!jailed) {
154*aa772005SRobert Watson 		if (chroot(pw->pw_dir) == -1) {
155*aa772005SRobert Watson 			pjdlog_errno(LOG_ERR,
156*aa772005SRobert Watson 			    "Unable to change root directory to %s",
157*aa772005SRobert Watson 			    pw->pw_dir);
158*aa772005SRobert Watson 			goto out;
159*aa772005SRobert Watson 		}
160*aa772005SRobert Watson 	}
161*aa772005SRobert Watson 	PJDLOG_VERIFY(chdir("/") == 0);
162*aa772005SRobert Watson 
163*aa772005SRobert Watson 	if (setgroups(ngroups, groups) == -1) {
164*aa772005SRobert Watson 		pjdlog_errno(LOG_ERR, "Unable to set groups");
165*aa772005SRobert Watson 		goto out;
166*aa772005SRobert Watson 	}
167*aa772005SRobert Watson 	if (setgid(pw->pw_gid) == -1) {
168*aa772005SRobert Watson 		pjdlog_errno(LOG_ERR, "Unable to set gid to %u",
169*aa772005SRobert Watson 		    (unsigned int)pw->pw_gid);
170*aa772005SRobert Watson 		goto out;
171*aa772005SRobert Watson 	}
172*aa772005SRobert Watson 	if (setuid(pw->pw_uid) == -1) {
173*aa772005SRobert Watson 		pjdlog_errno(LOG_ERR, "Unable to set uid to %u",
174*aa772005SRobert Watson 		    (unsigned int)pw->pw_uid);
175*aa772005SRobert Watson 		goto out;
176*aa772005SRobert Watson 	}
177*aa772005SRobert Watson 
178*aa772005SRobert Watson #ifdef HAVE_CAP_ENTER
179*aa772005SRobert Watson 	if (capsicum) {
180*aa772005SRobert Watson 		capsicum = (cap_enter() == 0);
181*aa772005SRobert Watson 		if (!capsicum) {
182*aa772005SRobert Watson 			pjdlog_common(LOG_DEBUG, 1, errno,
183*aa772005SRobert Watson 			    "Unable to sandbox using capsicum");
184*aa772005SRobert Watson 		}
185*aa772005SRobert Watson 	}
186*aa772005SRobert Watson #else	/* !HAVE_CAP_ENTER */
187*aa772005SRobert Watson 	capsicum = false;
188*aa772005SRobert Watson #endif	/* !HAVE_CAP_ENTER */
189*aa772005SRobert Watson 
190*aa772005SRobert Watson 	/*
191*aa772005SRobert Watson 	 * Better be sure that everything succeeded.
192*aa772005SRobert Watson 	 */
193*aa772005SRobert Watson #ifdef HAVE_GETRESUID
194*aa772005SRobert Watson 	PJDLOG_VERIFY(getresuid(&ruid, &euid, &suid) == 0);
195*aa772005SRobert Watson 	PJDLOG_VERIFY(suid == pw->pw_uid);
196*aa772005SRobert Watson #else
197*aa772005SRobert Watson 	ruid = getuid();
198*aa772005SRobert Watson 	euid = geteuid();
199*aa772005SRobert Watson #endif
200*aa772005SRobert Watson 	PJDLOG_VERIFY(ruid == pw->pw_uid);
201*aa772005SRobert Watson 	PJDLOG_VERIFY(euid == pw->pw_uid);
202*aa772005SRobert Watson #ifdef HAVE_GETRESGID
203*aa772005SRobert Watson 	PJDLOG_VERIFY(getresgid(&rgid, &egid, &sgid) == 0);
204*aa772005SRobert Watson 	PJDLOG_VERIFY(sgid == pw->pw_gid);
205*aa772005SRobert Watson #else
206*aa772005SRobert Watson 	rgid = getgid();
207*aa772005SRobert Watson 	egid = getegid();
208*aa772005SRobert Watson #endif
209*aa772005SRobert Watson 	PJDLOG_VERIFY(rgid == pw->pw_gid);
210*aa772005SRobert Watson 	PJDLOG_VERIFY(egid == pw->pw_gid);
211*aa772005SRobert Watson 	PJDLOG_VERIFY(getgroups(0, NULL) == ngroups);
212*aa772005SRobert Watson 	ggroups = malloc(sizeof(ggroups[0]) * ngroups);
213*aa772005SRobert Watson 	if (ggroups == NULL) {
214*aa772005SRobert Watson 		pjdlog_error("Unable to allocate memory for %d groups.",
215*aa772005SRobert Watson 		    ngroups);
216*aa772005SRobert Watson 		goto out;
217*aa772005SRobert Watson 	}
218*aa772005SRobert Watson 	PJDLOG_VERIFY(getgroups(ngroups, ggroups) == ngroups);
219*aa772005SRobert Watson 	qsort(groups, (size_t)ngroups, sizeof(groups[0]), groups_compare);
220*aa772005SRobert Watson 	qsort(ggroups, (size_t)ngroups, sizeof(ggroups[0]), groups_compare);
221*aa772005SRobert Watson 	PJDLOG_VERIFY(bcmp(groups, ggroups, sizeof(groups[0]) * ngroups) == 0);
222*aa772005SRobert Watson 
223*aa772005SRobert Watson 	pjdlog_debug(1,
224*aa772005SRobert Watson 	    "Privileges successfully dropped using %s%s+setgid+setuid.",
225*aa772005SRobert Watson 	    capsicum ? "capsicum+" : "", jailed ? "jail" : "chroot");
226*aa772005SRobert Watson 
227*aa772005SRobert Watson 	ret = 0;
228*aa772005SRobert Watson out:
229*aa772005SRobert Watson 	if (groups != NULL)
230*aa772005SRobert Watson 		free(groups);
231*aa772005SRobert Watson 	if (ggroups != NULL)
232*aa772005SRobert Watson 		free(ggroups);
233*aa772005SRobert Watson 	return (ret);
234*aa772005SRobert Watson }
235