xref: /freebsd/sys/kern/kern_jail.c (revision 0b87f79976047c8f4332bbf7dc03146f6b0de79f)
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$
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 
29 MALLOC_DEFINE(M_PRISON, "prison", "Prison structures");
30 
31 SYSCTL_DECL(_security);
32 SYSCTL_NODE(_security, OID_AUTO, jail, CTLFLAG_RW, 0,
33     "Jail rules");
34 
35 mp_fixme("these variables need a lock")
36 
37 int	jail_set_hostname_allowed = 1;
38 SYSCTL_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 
42 int	jail_socket_unixiproute_only = 1;
43 SYSCTL_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 
47 int	jail_sysvipc_allowed = 0;
48 SYSCTL_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  */
55 int
56 jail(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 	MALLOC(pr, struct prison *, sizeof *pr , M_PRISON, M_WAITOK | M_ZERO);
76 	mtx_init(&pr->pr_mtx, "jail mutex", NULL, MTX_DEF);
77 	pr->pr_securelevel = securelevel;
78 	error = copyinstr(j.hostname, &pr->pr_host, sizeof pr->pr_host, 0);
79 	if (error)
80 		goto bail;
81 	ca.path = j.path;
82 	mtx_lock(&Giant);
83 	error = chroot(td, &ca);
84 	mtx_unlock(&Giant);
85 	if (error)
86 		goto bail;
87 	newcred = crget();
88 	pr->pr_ip = j.ip_number;
89 	PROC_LOCK(p);
90 	/* Implicitly fail if already in jail.  */
91 	error = suser_cred(p->p_ucred, 0);
92 	if (error)
93 		goto badcred;
94 	oldcred = p->p_ucred;
95 	crcopy(newcred, oldcred);
96 	p->p_ucred = newcred;
97 	p->p_ucred->cr_prison = pr;
98 	pr->pr_ref = 1;
99 	PROC_UNLOCK(p);
100 	crfree(oldcred);
101 	return (0);
102 badcred:
103 	PROC_UNLOCK(p);
104 	crfree(newcred);
105 bail:
106 	FREE(pr, M_PRISON);
107 	return (error);
108 }
109 
110 void
111 prison_free(struct prison *pr)
112 {
113 
114 	mtx_lock(&pr->pr_mtx);
115 	pr->pr_ref--;
116 	if (pr->pr_ref == 0) {
117 		mtx_unlock(&pr->pr_mtx);
118 		mtx_destroy(&pr->pr_mtx);
119 		if (pr->pr_linux != NULL)
120 			FREE(pr->pr_linux, M_PRISON);
121 		FREE(pr, M_PRISON);
122 		return;
123 	}
124 	mtx_unlock(&pr->pr_mtx);
125 }
126 
127 void
128 prison_hold(struct prison *pr)
129 {
130 
131 	mtx_lock(&pr->pr_mtx);
132 	pr->pr_ref++;
133 	mtx_unlock(&pr->pr_mtx);
134 }
135 
136 u_int32_t
137 prison_getip(struct ucred *cred)
138 {
139 
140 	return (cred->cr_prison->pr_ip);
141 }
142 
143 int
144 prison_ip(struct ucred *cred, int flag, u_int32_t *ip)
145 {
146 	u_int32_t tmp;
147 
148 	if (!jailed(cred))
149 		return (0);
150 	if (flag)
151 		tmp = *ip;
152 	else
153 		tmp = ntohl(*ip);
154 	if (tmp == INADDR_ANY) {
155 		if (flag)
156 			*ip = cred->cr_prison->pr_ip;
157 		else
158 			*ip = htonl(cred->cr_prison->pr_ip);
159 		return (0);
160 	}
161 	if (tmp == INADDR_LOOPBACK) {
162 		if (flag)
163 			*ip = cred->cr_prison->pr_ip;
164 		else
165 			*ip = htonl(cred->cr_prison->pr_ip);
166 		return (0);
167 	}
168 	if (cred->cr_prison->pr_ip != tmp)
169 		return (1);
170 	return (0);
171 }
172 
173 void
174 prison_remote_ip(struct ucred *cred, int flag, u_int32_t *ip)
175 {
176 	u_int32_t tmp;
177 
178 	if (!jailed(cred))
179 		return;
180 	if (flag)
181 		tmp = *ip;
182 	else
183 		tmp = ntohl(*ip);
184 	if (tmp == INADDR_LOOPBACK) {
185 		if (flag)
186 			*ip = cred->cr_prison->pr_ip;
187 		else
188 			*ip = htonl(cred->cr_prison->pr_ip);
189 		return;
190 	}
191 	return;
192 }
193 
194 int
195 prison_if(struct ucred *cred, struct sockaddr *sa)
196 {
197 	struct sockaddr_in *sai = (struct sockaddr_in*) sa;
198 	int ok;
199 
200 	if ((sai->sin_family != AF_INET) && jail_socket_unixiproute_only)
201 		ok = 1;
202 	else if (sai->sin_family != AF_INET)
203 		ok = 0;
204 	else if (cred->cr_prison->pr_ip != ntohl(sai->sin_addr.s_addr))
205 		ok = 1;
206 	else
207 		ok = 0;
208 	return (ok);
209 }
210 
211 /*
212  * Return 0 if jails permit p1 to frob p2, otherwise ESRCH.
213  */
214 int
215 prison_check(cred1, cred2)
216 	struct ucred *cred1, *cred2;
217 {
218 
219 	if (jailed(cred1)) {
220 		if (!jailed(cred2))
221 			return (ESRCH);
222 		if (cred2->cr_prison != cred1->cr_prison)
223 			return (ESRCH);
224 	}
225 
226 	return (0);
227 }
228 
229 /*
230  * Return 1 if the passed credential is in a jail, otherwise 0.
231  */
232 int
233 jailed(cred)
234 	struct ucred *cred;
235 {
236 
237 	return (cred->cr_prison != NULL);
238 }
239 
240 /*
241  * Return the correct hostname for the passed credential.
242  */
243 void
244 getcredhostname(cred, buf, size)
245 	struct ucred *cred;
246 	char *buf;
247 	size_t size;
248 {
249 
250 	if (jailed(cred)) {
251 		mtx_lock(&cred->cr_prison->pr_mtx);
252 		strncpy(buf, cred->cr_prison->pr_host, size);
253 		mtx_unlock(&cred->cr_prison->pr_mtx);
254 	}
255 	else
256 		strncpy(buf, hostname, size);
257 	buf[size - 1] = '\0';
258 }
259