kern_jail.c revision 91391
1/*
2 * ----------------------------------------------------------------------------
3 * "THE BEER-WARE LICENSE" (Revision 42):
4 * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
5 * can do whatever you want with this stuff. If we meet some day, and you think
6 * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7 * ----------------------------------------------------------------------------
8 *
9 * $FreeBSD: head/sys/kern/kern_jail.c 91391 2002-02-27 16:43:20Z robert $
10 *
11 */
12
13#include <sys/param.h>
14#include <sys/types.h>
15#include <sys/kernel.h>
16#include <sys/systm.h>
17#include <sys/errno.h>
18#include <sys/sysproto.h>
19#include <sys/malloc.h>
20#include <sys/proc.h>
21#include <sys/jail.h>
22#include <sys/lock.h>
23#include <sys/mutex.h>
24#include <sys/socket.h>
25#include <sys/sysctl.h>
26#include <net/if.h>
27#include <netinet/in.h>
28
29MALLOC_DEFINE(M_PRISON, "prison", "Prison structures");
30
31SYSCTL_DECL(_security);
32SYSCTL_NODE(_security, OID_AUTO, jail, CTLFLAG_RW, 0,
33    "Jail rules");
34
35mp_fixme("these variables need a lock")
36
37int	jail_set_hostname_allowed = 1;
38SYSCTL_INT(_security_jail, OID_AUTO, set_hostname_allowed, CTLFLAG_RW,
39    &jail_set_hostname_allowed, 0,
40    "Processes in jail can set their hostnames");
41
42int	jail_socket_unixiproute_only = 1;
43SYSCTL_INT(_security_jail, OID_AUTO, socket_unixiproute_only, CTLFLAG_RW,
44    &jail_socket_unixiproute_only, 0,
45    "Processes in jail are limited to creating UNIX/IPv4/route sockets only");
46
47int	jail_sysvipc_allowed = 0;
48SYSCTL_INT(_security_jail, OID_AUTO, sysvipc_allowed, CTLFLAG_RW,
49    &jail_sysvipc_allowed, 0,
50    "Processes in jail can use System V IPC primitives");
51
52/*
53 * MPSAFE
54 */
55int
56jail(td, uap)
57	struct thread *td;
58	struct jail_args /* {
59		syscallarg(struct jail *) jail;
60	} */ *uap;
61{
62	struct proc *p = td->td_proc;
63	int error;
64	struct prison *pr;
65	struct jail j;
66	struct chroot_args ca;
67	struct ucred *newcred = NULL, *oldcred;
68
69	error = copyin(uap->jail, &j, sizeof j);
70	if (error)
71		return (error);
72	if (j.version != 0)
73		return (EINVAL);
74
75	mtx_lock(&Giant);
76	MALLOC(pr, struct prison *, sizeof *pr , M_PRISON, M_WAITOK | M_ZERO);
77	mtx_init(&pr->pr_mtx, "jail mutex", MTX_DEF);
78	pr->pr_securelevel = securelevel;
79	error = copyinstr(j.hostname, &pr->pr_host, sizeof pr->pr_host, 0);
80	if (error)
81		goto bail;
82	ca.path = j.path;
83	error = chroot(td, &ca);
84	if (error)
85		goto bail;
86	newcred = crget();
87	pr->pr_ip = j.ip_number;
88	PROC_LOCK(p);
89	/* Implicitly fail if already in jail.  */
90	error = suser_xxx(p->p_ucred, NULL, 0);
91	if (error)
92		goto badcred;
93	oldcred = p->p_ucred;
94	crcopy(newcred, oldcred);
95	p->p_ucred = newcred;
96	p->p_ucred->cr_prison = pr;
97	pr->pr_ref = 1;
98	PROC_UNLOCK(p);
99	crfree(oldcred);
100	mtx_unlock(&Giant);
101	return (0);
102badcred:
103	PROC_UNLOCK(p);
104	crfree(newcred);
105bail:
106	FREE(pr, M_PRISON);
107	mtx_unlock(&Giant);
108	return (error);
109}
110
111void
112prison_free(struct prison *pr)
113{
114
115	mtx_lock(&pr->pr_mtx);
116	pr->pr_ref--;
117	if (pr->pr_ref == 0) {
118		mtx_unlock(&pr->pr_mtx);
119		mtx_destroy(&pr->pr_mtx);
120		if (pr->pr_linux != NULL)
121			FREE(pr->pr_linux, M_PRISON);
122		FREE(pr, M_PRISON);
123		return;
124	}
125	mtx_unlock(&pr->pr_mtx);
126}
127
128void
129prison_hold(struct prison *pr)
130{
131
132	mtx_lock(&pr->pr_mtx);
133	pr->pr_ref++;
134	mtx_unlock(&pr->pr_mtx);
135}
136
137u_int32_t
138prison_getip(struct ucred *cred)
139{
140
141	return (cred->cr_prison->pr_ip);
142}
143
144int
145prison_ip(struct ucred *cred, int flag, u_int32_t *ip)
146{
147	u_int32_t tmp;
148
149	if (!jailed(cred))
150		return (0);
151	if (flag)
152		tmp = *ip;
153	else
154		tmp = ntohl(*ip);
155	if (tmp == INADDR_ANY) {
156		if (flag)
157			*ip = cred->cr_prison->pr_ip;
158		else
159			*ip = htonl(cred->cr_prison->pr_ip);
160		return (0);
161	}
162	if (tmp == INADDR_LOOPBACK) {
163		if (flag)
164			*ip = cred->cr_prison->pr_ip;
165		else
166			*ip = htonl(cred->cr_prison->pr_ip);
167		return (0);
168	}
169	if (cred->cr_prison->pr_ip != tmp)
170		return (1);
171	return (0);
172}
173
174void
175prison_remote_ip(struct ucred *cred, int flag, u_int32_t *ip)
176{
177	u_int32_t tmp;
178
179	if (!jailed(cred))
180		return;
181	if (flag)
182		tmp = *ip;
183	else
184		tmp = ntohl(*ip);
185	if (tmp == INADDR_LOOPBACK) {
186		if (flag)
187			*ip = cred->cr_prison->pr_ip;
188		else
189			*ip = htonl(cred->cr_prison->pr_ip);
190		return;
191	}
192	return;
193}
194
195int
196prison_if(struct ucred *cred, struct sockaddr *sa)
197{
198	struct sockaddr_in *sai = (struct sockaddr_in*) sa;
199	int ok;
200
201	if ((sai->sin_family != AF_INET) && jail_socket_unixiproute_only)
202		ok = 1;
203	else if (sai->sin_family != AF_INET)
204		ok = 0;
205	else if (cred->cr_prison->pr_ip != ntohl(sai->sin_addr.s_addr))
206		ok = 1;
207	else
208		ok = 0;
209	return (ok);
210}
211
212/*
213 * Return 0 if jails permit p1 to frob p2, otherwise ESRCH.
214 */
215int
216prison_check(cred1, cred2)
217	struct ucred *cred1, *cred2;
218{
219
220	if (jailed(cred1)) {
221		if (!jailed(cred2))
222			return (ESRCH);
223		if (cred2->cr_prison != cred1->cr_prison)
224			return (ESRCH);
225	}
226
227	return (0);
228}
229
230/*
231 * Return 1 if the passed credential is in a jail, otherwise 0.
232 */
233int
234jailed(cred)
235	struct ucred *cred;
236{
237
238	return (cred->cr_prison != NULL);
239}
240
241/*
242 * Return the correct hostname for the passed credential.
243 */
244void
245getcredhostname(cred, buf, size)
246	struct ucred *cred;
247	char *buf;
248	size_t size;
249{
250
251	if (jailed(cred)) {
252		mtx_lock(&cred->cr_prison->pr_mtx);
253		strncpy(buf, cred->cr_prison->pr_host, size);
254		mtx_unlock(&cred->cr_prison->pr_mtx);
255	}
256	else
257		strncpy(buf, hostname, size);
258	buf[size - 1] = '\0';
259}
260