kern_jail.c revision 113630
146197Sphk/*
246197Sphk * ----------------------------------------------------------------------------
346197Sphk * "THE BEER-WARE LICENSE" (Revision 42):
446197Sphk * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
546197Sphk * can do whatever you want with this stuff. If we meet some day, and you think
646197Sphk * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
746197Sphk * ----------------------------------------------------------------------------
846197Sphk *
950477Speter * $FreeBSD: head/sys/kern/kern_jail.c 113630 2003-04-17 22:26:53Z jhb $
1046197Sphk *
1146197Sphk */
1246155Sphk
1346155Sphk#include <sys/param.h>
1446155Sphk#include <sys/types.h>
1546155Sphk#include <sys/kernel.h>
1646155Sphk#include <sys/systm.h>
1746155Sphk#include <sys/errno.h>
1846155Sphk#include <sys/sysproto.h>
1946155Sphk#include <sys/malloc.h>
2046155Sphk#include <sys/proc.h>
2146155Sphk#include <sys/jail.h>
2287275Srwatson#include <sys/lock.h>
2387275Srwatson#include <sys/mutex.h>
24113275Smike#include <sys/namei.h>
25113275Smike#include <sys/queue.h>
2646155Sphk#include <sys/socket.h>
27113275Smike#include <sys/syscallsubr.h>
2857163Srwatson#include <sys/sysctl.h>
29113275Smike#include <sys/vnode.h>
3046155Sphk#include <net/if.h>
3146155Sphk#include <netinet/in.h>
3246155Sphk
3346155SphkMALLOC_DEFINE(M_PRISON, "prison", "Prison structures");
3446155Sphk
3589414SarrSYSCTL_DECL(_security);
3689414SarrSYSCTL_NODE(_security, OID_AUTO, jail, CTLFLAG_RW, 0,
3757163Srwatson    "Jail rules");
3857163Srwatson
3984828Sjhbmp_fixme("these variables need a lock")
4084828Sjhb
4157163Srwatsonint	jail_set_hostname_allowed = 1;
4289414SarrSYSCTL_INT(_security_jail, OID_AUTO, set_hostname_allowed, CTLFLAG_RW,
4357163Srwatson    &jail_set_hostname_allowed, 0,
4457163Srwatson    "Processes in jail can set their hostnames");
4557163Srwatson
4661235Srwatsonint	jail_socket_unixiproute_only = 1;
4789414SarrSYSCTL_INT(_security_jail, OID_AUTO, socket_unixiproute_only, CTLFLAG_RW,
4861235Srwatson    &jail_socket_unixiproute_only, 0,
4961235Srwatson    "Processes in jail are limited to creating UNIX/IPv4/route sockets only");
5061235Srwatson
5168024Srwatsonint	jail_sysvipc_allowed = 0;
5289414SarrSYSCTL_INT(_security_jail, OID_AUTO, sysvipc_allowed, CTLFLAG_RW,
5368024Srwatson    &jail_sysvipc_allowed, 0,
5468024Srwatson    "Processes in jail can use System V IPC primitives");
5568024Srwatson
56113275Smike/* allprison, lastprid, and prisoncount are protected by allprison_mtx. */
57113275Smikestruct	prisonlist allprison;
58113275Smikestruct	mtx allprison_mtx;
59113275Smikeint	lastprid = 0;
60113275Smikeint	prisoncount = 0;
61113275Smike
62113275Smikestatic void		 init_prison(void *);
63113275Smikestatic struct prison	*prison_find(int);
64113275Smikestatic int		 sysctl_jail_list(SYSCTL_HANDLER_ARGS);
65113275Smike
66113275Smikestatic void
67113275Smikeinit_prison(void *data __unused)
68113275Smike{
69113275Smike
70113275Smike	mtx_init(&allprison_mtx, "allprison", NULL, MTX_DEF);
71113275Smike	LIST_INIT(&allprison);
72113275Smike}
73113275Smike
74113275SmikeSYSINIT(prison, SI_SUB_INTRINSIC, SI_ORDER_ANY, init_prison, NULL);
75113275Smike
7682710Sdillon/*
7782710Sdillon * MPSAFE
7882710Sdillon */
7946155Sphkint
8083366Sjulianjail(td, uap)
8183366Sjulian	struct thread *td;
8272786Srwatson	struct jail_args /* {
83107850Salfred		struct jail *jail;
8472786Srwatson	} */ *uap;
8546155Sphk{
86113275Smike	struct nameidata nd;
87113275Smike	struct prison *pr, *tpr;
8846155Sphk	struct jail j;
89113275Smike	struct jail_attach_args jaa;
90113275Smike	int error, tryprid;
9146155Sphk
9246155Sphk	error = copyin(uap->jail, &j, sizeof j);
9346155Sphk	if (error)
9484828Sjhb		return (error);
9584828Sjhb	if (j.version != 0)
9684828Sjhb		return (EINVAL);
9784828Sjhb
98111119Simp	MALLOC(pr, struct prison *, sizeof *pr , M_PRISON, M_WAITOK | M_ZERO);
9993818Sjhb	mtx_init(&pr->pr_mtx, "jail mutex", NULL, MTX_DEF);
100113275Smike	pr->pr_ref = 1;
101113275Smike	error = copyinstr(j.path, &pr->pr_path, sizeof pr->pr_path, 0);
102113275Smike	if (error)
103113275Smike		goto e_killmtx;
104113275Smike	mtx_lock(&Giant);
105113275Smike	NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, pr->pr_path, td);
106113275Smike	error = namei(&nd);
107113275Smike	if (error) {
108113275Smike		mtx_unlock(&Giant);
109113275Smike		goto e_killmtx;
110113275Smike	}
111113275Smike	pr->pr_root = nd.ni_vp;
112113275Smike	VOP_UNLOCK(nd.ni_vp, 0, td);
113113275Smike	NDFREE(&nd, NDF_ONLY_PNBUF);
114113275Smike	mtx_unlock(&Giant);
11546155Sphk	error = copyinstr(j.hostname, &pr->pr_host, sizeof pr->pr_host, 0);
11684828Sjhb	if (error)
117113275Smike		goto e_dropvnref;
118113275Smike	pr->pr_ip = j.ip_number;
119113275Smike	pr->pr_linux = NULL;
120113275Smike	pr->pr_securelevel = securelevel;
121113275Smike
122113275Smike	/* Determine next pr_id and add prison to allprison list. */
123113275Smike	mtx_lock(&allprison_mtx);
124113275Smike	tryprid = lastprid + 1;
125113275Smike	if (tryprid == JAIL_MAX)
126113275Smike		tryprid = 1;
127113275Smikenext:
128113275Smike	LIST_FOREACH(tpr, &allprison, pr_list) {
129113275Smike		if (tpr->pr_id == tryprid) {
130113275Smike			tryprid++;
131113275Smike			if (tryprid == JAIL_MAX) {
132113275Smike				mtx_unlock(&allprison_mtx);
133113275Smike				error = EAGAIN;
134113275Smike				goto e_dropvnref;
135113275Smike			}
136113275Smike			goto next;
137113275Smike		}
138113275Smike	}
139113275Smike	pr->pr_id = jaa.jid = lastprid = tryprid;
140113275Smike	LIST_INSERT_HEAD(&allprison, pr, pr_list);
141113275Smike	prisoncount++;
142113275Smike	mtx_unlock(&allprison_mtx);
143113275Smike
144113275Smike	error = jail_attach(td, &jaa);
145113275Smike	if (error)
146113275Smike		goto e_dropprref;
147113275Smike	mtx_lock(&pr->pr_mtx);
148113275Smike	pr->pr_ref--;
149113275Smike	mtx_unlock(&pr->pr_mtx);
150113275Smike	td->td_retval[0] = jaa.jid;
151113275Smike	return (0);
152113275Smikee_dropprref:
153113275Smike	mtx_lock(&allprison_mtx);
154113275Smike	LIST_REMOVE(pr, pr_list);
155113275Smike	prisoncount--;
156113275Smike	mtx_unlock(&allprison_mtx);
157113275Smikee_dropvnref:
15899227Siedowse	mtx_lock(&Giant);
159113275Smike	vrele(pr->pr_root);
16099227Siedowse	mtx_unlock(&Giant);
161113275Smikee_killmtx:
162113275Smike	mtx_destroy(&pr->pr_mtx);
163113275Smike	FREE(pr, M_PRISON);
164113275Smike	return (error);
165113275Smike}
166113275Smike
167113275Smike/*
168113275Smike * MPSAFE
169113275Smike */
170113275Smikeint
171113275Smikejail_attach(td, uap)
172113275Smike	struct thread *td;
173113275Smike	struct jail_attach_args /* {
174113275Smike		int jid;
175113275Smike	} */ *uap;
176113275Smike{
177113275Smike	struct proc *p;
178113275Smike	struct ucred *newcred, *oldcred;
179113275Smike	struct prison *pr;
180113275Smike	int error;
181113275Smike
182113275Smike	p = td->td_proc;
183113275Smike
184113275Smike	mtx_lock(&allprison_mtx);
185113275Smike	pr = prison_find(uap->jid);
186113275Smike	if (pr == NULL) {
187113275Smike		mtx_unlock(&allprison_mtx);
188113275Smike		return (EINVAL);
189113275Smike	}
190113275Smike	pr->pr_ref++;
191113275Smike	mtx_unlock(&pr->pr_mtx);
192113275Smike	mtx_unlock(&allprison_mtx);
193113275Smike
194113275Smike	error = suser_cred(td->td_ucred, PRISON_ROOT);
19546155Sphk	if (error)
196113275Smike		goto e_dropref;
197113275Smike	mtx_lock(&Giant);
198113275Smike	vn_lock(pr->pr_root, LK_EXCLUSIVE | LK_RETRY, td);
199113275Smike	if ((error = change_dir(pr->pr_root, td)) != 0)
200113275Smike		goto e_unlock;
201113275Smike#ifdef MAC
202113275Smike	if ((error = mac_check_vnode_chroot(td->td_ucred, pr->pr_root)))
203113275Smike		goto e_unlock;
204113275Smike#endif
205113275Smike	VOP_UNLOCK(pr->pr_root, 0, td);
206113275Smike	change_root(pr->pr_root, td);
207113275Smike	mtx_unlock(&Giant);
208113275Smike
20984828Sjhb	newcred = crget();
21084828Sjhb	PROC_LOCK(p);
21184828Sjhb	/* Implicitly fail if already in jail.  */
21293593Sjhb	error = suser_cred(p->p_ucred, 0);
213113275Smike	if (error) {
214113275Smike		PROC_UNLOCK(p);
215113275Smike		crfree(newcred);
216113275Smike		goto e_dropref;
217113275Smike	}
21884828Sjhb	oldcred = p->p_ucred;
219113275Smike	setsugid(p);
22084828Sjhb	crcopy(newcred, oldcred);
221113630Sjhb	newcred->cr_prison = pr;
22284828Sjhb	p->p_ucred = newcred;
22384828Sjhb	PROC_UNLOCK(p);
22484828Sjhb	crfree(oldcred);
22546155Sphk	return (0);
226113275Smikee_unlock:
227113275Smike	VOP_UNLOCK(pr->pr_root, 0, td);
228113275Smike	mtx_unlock(&Giant);
229113275Smikee_dropref:
230113275Smike	mtx_lock(&pr->pr_mtx);
231113275Smike	pr->pr_ref--;
232113275Smike	mtx_unlock(&pr->pr_mtx);
23346155Sphk	return (error);
23446155Sphk}
23546155Sphk
236113275Smike/*
237113275Smike * Returns a locked prison instance, or NULL on failure.
238113275Smike */
239113275Smikestatic struct prison *
240113275Smikeprison_find(int prid)
241113275Smike{
242113275Smike	struct prison *pr;
243113275Smike
244113275Smike	mtx_assert(&allprison_mtx, MA_OWNED);
245113275Smike	LIST_FOREACH(pr, &allprison, pr_list) {
246113275Smike		if (pr->pr_id == prid) {
247113275Smike			mtx_lock(&pr->pr_mtx);
248113275Smike			return (pr);
249113275Smike		}
250113275Smike	}
251113275Smike	return (NULL);
252113275Smike}
253113275Smike
25472786Srwatsonvoid
25572786Srwatsonprison_free(struct prison *pr)
25672786Srwatson{
25772786Srwatson
258113275Smike	mtx_assert(&Giant, MA_OWNED);
259113275Smike	mtx_lock(&allprison_mtx);
26087275Srwatson	mtx_lock(&pr->pr_mtx);
26172786Srwatson	pr->pr_ref--;
26272786Srwatson	if (pr->pr_ref == 0) {
263113275Smike		LIST_REMOVE(pr, pr_list);
26487275Srwatson		mtx_unlock(&pr->pr_mtx);
265113275Smike		prisoncount--;
266113275Smike		mtx_unlock(&allprison_mtx);
267113275Smike		vrele(pr->pr_root);
26887275Srwatson		mtx_destroy(&pr->pr_mtx);
26972786Srwatson		if (pr->pr_linux != NULL)
27072786Srwatson			FREE(pr->pr_linux, M_PRISON);
27172786Srwatson		FREE(pr, M_PRISON);
27287275Srwatson		return;
27372786Srwatson	}
27487275Srwatson	mtx_unlock(&pr->pr_mtx);
275113275Smike	mtx_unlock(&allprison_mtx);
27672786Srwatson}
27772786Srwatson
27872786Srwatsonvoid
27972786Srwatsonprison_hold(struct prison *pr)
28072786Srwatson{
28172786Srwatson
28287275Srwatson	mtx_lock(&pr->pr_mtx);
28372786Srwatson	pr->pr_ref++;
28487275Srwatson	mtx_unlock(&pr->pr_mtx);
28572786Srwatson}
28672786Srwatson
28787275Srwatsonu_int32_t
28887275Srwatsonprison_getip(struct ucred *cred)
28987275Srwatson{
29087275Srwatson
29187275Srwatson	return (cred->cr_prison->pr_ip);
29287275Srwatson}
29387275Srwatson
29446155Sphkint
29572786Srwatsonprison_ip(struct ucred *cred, int flag, u_int32_t *ip)
29646155Sphk{
29746155Sphk	u_int32_t tmp;
29846155Sphk
29972786Srwatson	if (!jailed(cred))
30046155Sphk		return (0);
30146155Sphk	if (flag)
30246155Sphk		tmp = *ip;
30346155Sphk	else
30446155Sphk		tmp = ntohl(*ip);
30546155Sphk	if (tmp == INADDR_ANY) {
30646155Sphk		if (flag)
30772786Srwatson			*ip = cred->cr_prison->pr_ip;
30846155Sphk		else
30972786Srwatson			*ip = htonl(cred->cr_prison->pr_ip);
31046155Sphk		return (0);
31146155Sphk	}
31281114Srwatson	if (tmp == INADDR_LOOPBACK) {
31381114Srwatson		if (flag)
31481114Srwatson			*ip = cred->cr_prison->pr_ip;
31581114Srwatson		else
31681114Srwatson			*ip = htonl(cred->cr_prison->pr_ip);
31781114Srwatson		return (0);
31881114Srwatson	}
31972786Srwatson	if (cred->cr_prison->pr_ip != tmp)
32046155Sphk		return (1);
32146155Sphk	return (0);
32246155Sphk}
32346155Sphk
32446155Sphkvoid
32572786Srwatsonprison_remote_ip(struct ucred *cred, int flag, u_int32_t *ip)
32646155Sphk{
32746155Sphk	u_int32_t tmp;
32846155Sphk
32972786Srwatson	if (!jailed(cred))
33046155Sphk		return;
33146155Sphk	if (flag)
33246155Sphk		tmp = *ip;
33346155Sphk	else
33446155Sphk		tmp = ntohl(*ip);
33581114Srwatson	if (tmp == INADDR_LOOPBACK) {
33646155Sphk		if (flag)
33772786Srwatson			*ip = cred->cr_prison->pr_ip;
33846155Sphk		else
33972786Srwatson			*ip = htonl(cred->cr_prison->pr_ip);
34046155Sphk		return;
34146155Sphk	}
34246155Sphk	return;
34346155Sphk}
34446155Sphk
34546155Sphkint
34672786Srwatsonprison_if(struct ucred *cred, struct sockaddr *sa)
34746155Sphk{
34846155Sphk	struct sockaddr_in *sai = (struct sockaddr_in*) sa;
34946155Sphk	int ok;
35046155Sphk
35161235Srwatson	if ((sai->sin_family != AF_INET) && jail_socket_unixiproute_only)
35261235Srwatson		ok = 1;
35361235Srwatson	else if (sai->sin_family != AF_INET)
35446155Sphk		ok = 0;
35572786Srwatson	else if (cred->cr_prison->pr_ip != ntohl(sai->sin_addr.s_addr))
35646155Sphk		ok = 1;
35746155Sphk	else
35846155Sphk		ok = 0;
35946155Sphk	return (ok);
36046155Sphk}
36172786Srwatson
36272786Srwatson/*
36372786Srwatson * Return 0 if jails permit p1 to frob p2, otherwise ESRCH.
36472786Srwatson */
36572786Srwatsonint
36672786Srwatsonprison_check(cred1, cred2)
36772786Srwatson	struct ucred *cred1, *cred2;
36872786Srwatson{
36972786Srwatson
37072786Srwatson	if (jailed(cred1)) {
37172786Srwatson		if (!jailed(cred2))
37272786Srwatson			return (ESRCH);
37372786Srwatson		if (cred2->cr_prison != cred1->cr_prison)
37472786Srwatson			return (ESRCH);
37572786Srwatson	}
37672786Srwatson
37772786Srwatson	return (0);
37872786Srwatson}
37972786Srwatson
38072786Srwatson/*
38172786Srwatson * Return 1 if the passed credential is in a jail, otherwise 0.
38272786Srwatson */
38372786Srwatsonint
38472786Srwatsonjailed(cred)
38572786Srwatson	struct ucred *cred;
38672786Srwatson{
38772786Srwatson
38872786Srwatson	return (cred->cr_prison != NULL);
38972786Srwatson}
39091384Srobert
39191384Srobert/*
39291384Srobert * Return the correct hostname for the passed credential.
39391384Srobert */
39491391Srobertvoid
39591391Srobertgetcredhostname(cred, buf, size)
39691384Srobert	struct ucred *cred;
39791391Srobert	char *buf;
39891391Srobert	size_t size;
39991384Srobert{
40091384Srobert
40191391Srobert	if (jailed(cred)) {
40291391Srobert		mtx_lock(&cred->cr_prison->pr_mtx);
403105354Srobert		strlcpy(buf, cred->cr_prison->pr_host, size);
40491391Srobert		mtx_unlock(&cred->cr_prison->pr_mtx);
40591391Srobert	}
40691391Srobert	else
407105354Srobert		strlcpy(buf, hostname, size);
40891384Srobert}
409113275Smike
410113275Smikestatic int
411113275Smikesysctl_jail_list(SYSCTL_HANDLER_ARGS)
412113275Smike{
413113275Smike	struct xprison *xp, *sxp;
414113275Smike	struct prison *pr;
415113275Smike	int count, error;
416113275Smike
417113275Smike	mtx_assert(&Giant, MA_OWNED);
418113275Smikeretry:
419113275Smike	mtx_lock(&allprison_mtx);
420113275Smike	count = prisoncount;
421113275Smike	mtx_unlock(&allprison_mtx);
422113275Smike
423113275Smike	if (count == 0)
424113275Smike		return (0);
425113275Smike
426113275Smike	sxp = xp = malloc(sizeof(*xp) * count, M_TEMP, M_WAITOK | M_ZERO);
427113275Smike	mtx_lock(&allprison_mtx);
428113275Smike	if (count != prisoncount) {
429113275Smike		mtx_unlock(&allprison_mtx);
430113275Smike		free(sxp, M_TEMP);
431113275Smike		goto retry;
432113275Smike	}
433113275Smike
434113275Smike	LIST_FOREACH(pr, &allprison, pr_list) {
435113275Smike		mtx_lock(&pr->pr_mtx);
436113275Smike		xp->pr_version = XPRISON_VERSION;
437113275Smike		xp->pr_id = pr->pr_id;
438113275Smike		strlcpy(xp->pr_path, pr->pr_path, sizeof(xp->pr_path));
439113275Smike		strlcpy(xp->pr_host, pr->pr_host, sizeof(xp->pr_host));
440113275Smike		xp->pr_ip = pr->pr_ip;
441113275Smike		mtx_unlock(&pr->pr_mtx);
442113275Smike		xp++;
443113275Smike	}
444113275Smike	mtx_unlock(&allprison_mtx);
445113275Smike
446113275Smike	error = SYSCTL_OUT(req, sxp, sizeof(*sxp) * count);
447113275Smike	free(sxp, M_TEMP);
448113275Smike	if (error)
449113275Smike		return (error);
450113275Smike	return (0);
451113275Smike}
452113275Smike
453113275SmikeSYSCTL_OID(_security_jail, OID_AUTO, list, CTLTYPE_STRUCT | CTLFLAG_RD,
454113275Smike    NULL, 0, sysctl_jail_list, "S", "List of active jails");
455