xref: /dragonfly/sys/kern/kern_jail.c (revision ad7a2457)
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  */
10 /*-
11  * Copyright (c) 2006 Victor Balada Diaz <victor@bsdes.net>
12  * All rights reserved.
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions and the following disclaimer.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 
37 /*
38  * $FreeBSD: src/sys/kern/kern_jail.c,v 1.6.2.3 2001/08/17 01:00:26 rwatson Exp $
39  * $DragonFly: src/sys/kern/kern_jail.c,v 1.19 2008/05/17 18:20:33 dillon Exp $
40  */
41 
42 #include "opt_inet6.h"
43 
44 #include <sys/param.h>
45 #include <sys/types.h>
46 #include <sys/kernel.h>
47 #include <sys/systm.h>
48 #include <sys/errno.h>
49 #include <sys/sysproto.h>
50 #include <sys/malloc.h>
51 #include <sys/nlookup.h>
52 #include <sys/namecache.h>
53 #include <sys/proc.h>
54 #include <sys/priv.h>
55 #include <sys/jail.h>
56 #include <sys/socket.h>
57 #include <sys/sysctl.h>
58 #include <sys/kern_syscall.h>
59 #include <net/if.h>
60 #include <netinet/in.h>
61 #include <netinet6/in6_var.h>
62 
63 static struct prison	*prison_find(int);
64 static void		prison_ipcache_init(struct prison *);
65 
66 MALLOC_DEFINE(M_PRISON, "prison", "Prison structures");
67 
68 SYSCTL_NODE(, OID_AUTO, jail, CTLFLAG_RW, 0,
69     "Jail rules");
70 
71 int	jail_set_hostname_allowed = 1;
72 SYSCTL_INT(_jail, OID_AUTO, set_hostname_allowed, CTLFLAG_RW,
73     &jail_set_hostname_allowed, 0,
74     "Processes in jail can set their hostnames");
75 
76 int	jail_socket_unixiproute_only = 1;
77 SYSCTL_INT(_jail, OID_AUTO, socket_unixiproute_only, CTLFLAG_RW,
78     &jail_socket_unixiproute_only, 0,
79     "Processes in jail are limited to creating UNIX/IPv[46]/route sockets only");
80 
81 int	jail_sysvipc_allowed = 0;
82 SYSCTL_INT(_jail, OID_AUTO, sysvipc_allowed, CTLFLAG_RW,
83     &jail_sysvipc_allowed, 0,
84     "Processes in jail can use System V IPC primitives");
85 
86 int    jail_chflags_allowed = 0;
87 SYSCTL_INT(_jail, OID_AUTO, chflags_allowed, CTLFLAG_RW,
88     &jail_chflags_allowed, 0,
89     "Process in jail can set chflags(1)");
90 
91 int    jail_allow_raw_sockets = 0;
92 SYSCTL_INT(_jail, OID_AUTO, allow_raw_sockets, CTLFLAG_RW,
93     &jail_allow_raw_sockets, 0,
94     "Process in jail can create raw sockets");
95 
96 int	lastprid = 0;
97 int	prisoncount = 0;
98 
99 LIST_HEAD(prisonlist, prison);
100 struct	prisonlist allprison = LIST_HEAD_INITIALIZER(&allprison);
101 
102 static int
103 kern_jail_attach(int jid)
104 {
105 	struct proc *p = curthread->td_proc;
106 	struct prison *pr;
107 	int error;
108 
109 	pr = prison_find(jid);
110 	if (pr == NULL)
111 		return(EINVAL);
112 
113 	error = kern_chroot(&pr->pr_root);
114 	if (error)
115 		return(error);
116 
117 	prison_hold(pr);
118 	cratom(&p->p_ucred);
119 	p->p_ucred->cr_prison = pr;
120 	p->p_flag |= P_JAILED;
121 
122 	return(0);
123 }
124 
125 /*
126  * jail()
127  *
128  * jail_args(syscallarg(struct jail *) jail)
129  */
130 int
131 sys_jail(struct jail_args *uap)
132 {
133 	struct prison *pr, *tpr;
134 	struct jail j;
135 	struct jail_v0 jv0;
136 	struct thread *td = curthread;
137 	int error, tryprid, i;
138 	uint32_t jversion;
139 	struct nlookupdata nd;
140 	/* Multiip */
141 	struct sockaddr_storage *uips; /* Userland ips */
142 	struct sockaddr_in ip4addr;
143 	struct jail_ip_storage *jip;
144 	/* Multiip */
145 
146 	error = priv_check(td, PRIV_ROOT);
147 	if (error) {
148 		uap->sysmsg_result = -1;
149 		return (error);
150 	}
151 	error = copyin(uap->jail, &jversion, sizeof(jversion));
152 	if (error) {
153 		uap->sysmsg_result = -1;
154 		return (error);
155 	}
156 	pr = kmalloc(sizeof(*pr), M_PRISON, M_WAITOK | M_ZERO);
157 	SLIST_INIT(&pr->pr_ips);
158 
159 	switch (jversion) {
160 	case 0:
161 		/* Single IPv4 jails. */
162 
163 		error = copyin(uap->jail, &jv0, sizeof(jv0));
164 		if (error)
165 			goto bail;
166 		jip = kmalloc(sizeof(*jip),  M_PRISON, M_WAITOK | M_ZERO);
167 		ip4addr.sin_family = AF_INET;
168 		ip4addr.sin_addr.s_addr = htonl(jv0.ip_number);
169 		memcpy(&jip->ip, &ip4addr, sizeof(ip4addr));
170 		SLIST_INSERT_HEAD(&pr->pr_ips, jip, entries);
171 		break;
172 	case 1:
173 		/*
174 		 * DragonFly multi noIP/IPv4/IPv6 jails
175 		 *
176 		 * NOTE: This version is unsupported by FreeBSD
177 		 * (which uses version 2 instead).
178 		 */
179 
180 		error = copyin(uap->jail, &j, sizeof(j));
181 		if (error)
182 			goto bail;
183 		uips = kmalloc((sizeof(*uips) * j.n_ips), M_PRISON,
184 				M_WAITOK | M_ZERO);
185 		error = copyin(j.ips, uips, (sizeof(*uips) * j.n_ips));
186 		if (error) {
187 			kfree(uips, M_PRISON);
188 			goto bail;
189 		}
190 		for (i = 0; i < j.n_ips; i++) {
191 			jip = kmalloc(sizeof(*jip),  M_PRISON,
192 				      M_WAITOK | M_ZERO);
193 			memcpy(&jip->ip, &uips[i], sizeof(*uips));
194 			SLIST_INSERT_HEAD(&pr->pr_ips, jip, entries);
195 		}
196 		kfree(uips, M_PRISON);
197 		break;
198 	default:
199 		error = EINVAL;
200 		goto bail;
201 	}
202 
203 	error = copyinstr(j.hostname, &pr->pr_host, sizeof(pr->pr_host), 0);
204 	if (error)
205 		goto bail;
206 	error = nlookup_init(&nd, j.path, UIO_USERSPACE, NLC_FOLLOW);
207 	if (error)
208 		goto nlookup_init_clean;
209 	error = nlookup(&nd);
210 	if (error)
211 		goto nlookup_init_clean;
212 	cache_copy(&nd.nl_nch, &pr->pr_root);
213 
214 	varsymset_init(&pr->pr_varsymset, NULL);
215 	prison_ipcache_init(pr);
216 
217 	tryprid = lastprid + 1;
218 	if (tryprid == JAIL_MAX)
219 		tryprid = 1;
220 next:
221 	LIST_FOREACH(tpr, &allprison, pr_list) {
222 		if (tpr->pr_id != tryprid)
223 			continue;
224 		tryprid++;
225 		if (tryprid == JAIL_MAX) {
226 			error = ERANGE;
227 			goto varsym_clean;
228 		}
229 		goto next;
230 	}
231 	pr->pr_id = lastprid = tryprid;
232 	LIST_INSERT_HEAD(&allprison, pr, pr_list);
233 	prisoncount++;
234 
235 	error = kern_jail_attach(pr->pr_id);
236 	if (error)
237 		goto jail_attach_clean;
238 
239 	nlookup_done(&nd);
240 	uap->sysmsg_result = pr->pr_id;
241 	return (0);
242 
243 jail_attach_clean:
244 	LIST_REMOVE(pr, pr_list);
245 varsym_clean:
246 	varsymset_clean(&pr->pr_varsymset);
247 nlookup_init_clean:
248 	nlookup_done(&nd);
249 bail:
250 	/* Delete all ips */
251 	while (!SLIST_EMPTY(&pr->pr_ips)) {
252 		jip = SLIST_FIRST(&pr->pr_ips);
253 		SLIST_REMOVE_HEAD(&pr->pr_ips, entries);
254 		kfree(jip, M_PRISON);
255 	}
256 	kfree(pr, M_PRISON);
257 	return(error);
258 }
259 
260 /*
261  * int jail_attach(int jid);
262  */
263 int
264 sys_jail_attach(struct jail_attach_args *uap)
265 {
266 	struct thread *td = curthread;
267 	int error;
268 
269 	error = priv_check(td, PRIV_ROOT);
270 	if (error)
271 		return(error);
272 
273 	return(kern_jail_attach(uap->jid));
274 }
275 
276 static void
277 prison_ipcache_init(struct prison *pr)
278 {
279 	struct jail_ip_storage *jis;
280 	struct sockaddr_in *ip4;
281 	struct sockaddr_in6 *ip6;
282 
283 	SLIST_FOREACH(jis, &pr->pr_ips, entries) {
284 		switch (jis->ip.ss_family) {
285 		case AF_INET:
286 			ip4 = (struct sockaddr_in *)&jis->ip;
287 			if ((ntohl(ip4->sin_addr.s_addr) >> IN_CLASSA_NSHIFT) ==
288 			    IN_LOOPBACKNET) {
289 				/* loopback address */
290 				if (pr->local_ip4 == NULL)
291 					pr->local_ip4 = ip4;
292 			} else {
293 				/* public address */
294 				if (pr->nonlocal_ip4 == NULL)
295 					pr->nonlocal_ip4 = ip4;
296 			}
297 			break;
298 
299 		case AF_INET6:
300 			ip6 = (struct sockaddr_in6 *)&jis->ip;
301 			if (IN6_IS_ADDR_LOOPBACK(&ip6->sin6_addr)) {
302 				/* loopback address */
303 				if (pr->local_ip6 == NULL)
304 					pr->local_ip6 = ip6;
305 			} else {
306 				/* public address */
307 				if (pr->nonlocal_ip6 == NULL)
308 					pr->nonlocal_ip6 = ip6;
309 			}
310 			break;
311 		}
312 	}
313 }
314 
315 /*
316  * Changes INADDR_LOOPBACK for a valid jail address.
317  * ip is in network byte order.
318  * Returns 1 if the ip is among jail valid ips.
319  * Returns 0 if is not among jail valid ips or
320  * if couldn't replace INADDR_LOOPBACK for a valid
321  * IP.
322  */
323 int
324 prison_replace_wildcards(struct thread *td, struct sockaddr *ip)
325 {
326 	struct sockaddr_in *ip4 = (struct sockaddr_in *)ip;
327 	struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *)ip;
328 	struct prison *pr;
329 
330 	if (td->td_proc == NULL)
331 		return (1);
332 	if ((pr = td->td_proc->p_ucred->cr_prison) == NULL)
333 		return (1);
334 
335 	if ((ip->sa_family == AF_INET &&
336 	    ip4->sin_addr.s_addr == htonl(INADDR_ANY)) ||
337 	    (ip->sa_family == AF_INET6 &&
338 	    IN6_IS_ADDR_UNSPECIFIED(&ip6->sin6_addr)))
339 		return (1);
340 	if ((ip->sa_family == AF_INET &&
341 	    ip4->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) ||
342 	    (ip->sa_family == AF_INET6 &&
343 	    IN6_IS_ADDR_LOOPBACK(&ip6->sin6_addr))) {
344 		if (!prison_get_local(pr, ip->sa_family, ip) &&
345 		    !prison_get_nonlocal(pr, ip->sa_family, ip))
346 			return(0);
347 		else
348 			return(1);
349 	}
350 	if (jailed_ip(pr, ip))
351 		return(1);
352 	return(0);
353 }
354 
355 int
356 prison_remote_ip(struct thread *td, struct sockaddr *ip)
357 {
358 	struct sockaddr_in *ip4 = (struct sockaddr_in *)ip;
359 	struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *)ip;
360 	struct prison *pr;
361 
362 	if (td == NULL || td->td_proc == NULL)
363 		return(1);
364 	if ((pr = td->td_proc->p_ucred->cr_prison) == NULL)
365 		return(1);
366 	if ((ip->sa_family == AF_INET &&
367 	    ip4->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) ||
368 	    (ip->sa_family == AF_INET6 &&
369 	    IN6_IS_ADDR_LOOPBACK(&ip6->sin6_addr))) {
370 		if (!prison_get_local(pr, ip->sa_family, ip) &&
371 		    !prison_get_nonlocal(pr, ip->sa_family, ip))
372 			return(0);
373 		else
374 			return(1);
375 	}
376 	return(1);
377 }
378 
379 /*
380  * Prison get non loopback ip:
381  * - af is the address family of the ip we want (AF_INET|AF_INET6).
382  * - If ip != NULL, put the first IP address that is not a loopback address
383  *   into *ip.
384  *
385  * ip is in network by order and we don't touch it unless we find a valid ip.
386  * No matter if ip == NULL or not, we return either a valid struct sockaddr *,
387  * or NULL.  This struct may not be modified.
388  */
389 struct sockaddr *
390 prison_get_nonlocal(struct prison *pr, sa_family_t af, struct sockaddr *ip)
391 {
392 	struct sockaddr_in *ip4 = (struct sockaddr_in *)ip;
393 	struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *)ip;
394 
395 	/* Check if it is cached */
396 	switch(af) {
397 	case AF_INET:
398 		if (ip4 != NULL && pr->nonlocal_ip4 != NULL)
399 			ip4->sin_addr.s_addr = pr->nonlocal_ip4->sin_addr.s_addr;
400 		return (struct sockaddr *)pr->nonlocal_ip4;
401 
402 	case AF_INET6:
403 		if (ip6 != NULL && pr->nonlocal_ip6 != NULL)
404 			ip6->sin6_addr = pr->nonlocal_ip6->sin6_addr;
405 		return (struct sockaddr *)pr->nonlocal_ip6;
406 	}
407 
408 	/* NOTREACHED */
409 	return NULL;
410 }
411 
412 /*
413  * Prison get loopback ip.
414  * - af is the address family of the ip we want (AF_INET|AF_INET6).
415  * - If ip != NULL, put the first IP address that is not a loopback address
416  *   into *ip.
417  *
418  * ip is in network by order and we don't touch it unless we find a valid ip.
419  * No matter if ip == NULL or not, we return either a valid struct sockaddr *,
420  * or NULL.  This struct may not be modified.
421  */
422 struct sockaddr *
423 prison_get_local(struct prison *pr, sa_family_t af, struct sockaddr *ip)
424 {
425 	struct sockaddr_in *ip4 = (struct sockaddr_in *)ip;
426 	struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *)ip;
427 
428 	/* Check if it is cached */
429 	switch(af) {
430 	case AF_INET:
431 		if (ip4 != NULL && pr->local_ip4 != NULL)
432 			ip4->sin_addr.s_addr = pr->local_ip4->sin_addr.s_addr;
433 		return (struct sockaddr *)pr->local_ip4;
434 
435 	case AF_INET6:
436 		if (ip6 != NULL && pr->local_ip6 != NULL)
437 			ip6->sin6_addr = pr->local_ip6->sin6_addr;
438 		return (struct sockaddr *)pr->local_ip6;
439 	}
440 
441 	/* NOTREACHED */
442 	return NULL;
443 }
444 
445 /* Check if the IP is among ours, if it is return 1, else 0 */
446 int
447 jailed_ip(struct prison *pr, struct sockaddr *ip)
448 {
449 	struct jail_ip_storage *jis;
450 	struct sockaddr_in *jip4, *ip4;
451 	struct sockaddr_in6 *jip6, *ip6;
452 
453 	if (pr == NULL)
454 		return(0);
455 	ip4 = (struct sockaddr_in *)ip;
456 	ip6 = (struct sockaddr_in6 *)ip;
457 	SLIST_FOREACH(jis, &pr->pr_ips, entries) {
458 		switch (ip->sa_family) {
459 		case AF_INET:
460 			jip4 = (struct sockaddr_in *) &jis->ip;
461 			if (jip4->sin_family == AF_INET &&
462 			    ip4->sin_addr.s_addr == jip4->sin_addr.s_addr)
463 				return(1);
464 			break;
465 		case AF_INET6:
466 			jip6 = (struct sockaddr_in6 *) &jis->ip;
467 			if (jip6->sin6_family == AF_INET6 &&
468 			    IN6_ARE_ADDR_EQUAL(&ip6->sin6_addr,
469 					       &jip6->sin6_addr))
470 				return(1);
471 			break;
472 		}
473 	}
474 	/* Ip not in list */
475 	return(0);
476 }
477 
478 int
479 prison_if(struct ucred *cred, struct sockaddr *sa)
480 {
481 	struct prison *pr;
482 	struct sockaddr_in *sai = (struct sockaddr_in*) sa;
483 
484 	pr = cred->cr_prison;
485 
486 	if (((sai->sin_family != AF_INET) && (sai->sin_family != AF_INET6))
487 	    && jail_socket_unixiproute_only)
488 		return(1);
489 	else if ((sai->sin_family != AF_INET) && (sai->sin_family != AF_INET6))
490 		return(0);
491 	else if (jailed_ip(pr, sa))
492 		return(0);
493 	return(1);
494 }
495 
496 /*
497  * Returns a prison instance, or NULL on failure.
498  */
499 static struct prison *
500 prison_find(int prid)
501 {
502 	struct prison *pr;
503 
504 	LIST_FOREACH(pr, &allprison, pr_list) {
505 		if (pr->pr_id == prid)
506 			break;
507 	}
508 	return(pr);
509 }
510 
511 static int
512 sysctl_jail_list(SYSCTL_HANDLER_ARGS)
513 {
514 	struct jail_ip_storage *jip;
515 #ifdef INET6
516 	struct sockaddr_in6 *jsin6;
517 #endif
518 	struct sockaddr_in *jsin;
519 	struct proc *p;
520 	struct prison *pr;
521 	unsigned int jlssize, jlsused;
522 	int count, error;
523 	char *jls; /* Jail list */
524 	char *oip; /* Output ip */
525 	char *fullpath, *freepath;
526 
527 	jlsused = 0;
528 	p = curthread->td_proc;
529 
530 	if (jailed(p->p_ucred))
531 		return (0);
532 retry:
533 	count = prisoncount;
534 
535 	if (count == 0)
536 		return(0);
537 
538 	jlssize = (count * 1024);
539 	jls = kmalloc(jlssize + 1, M_TEMP, M_WAITOK | M_ZERO);
540 	if (count < prisoncount) {
541 		kfree(jls, M_TEMP);
542 		goto retry;
543 	}
544 	count = prisoncount;
545 
546 	LIST_FOREACH(pr, &allprison, pr_list) {
547 		error = cache_fullpath(p, &pr->pr_root, &fullpath, &freepath);
548 		if (error)
549 			continue;
550 		if (jlsused && jlsused < jlssize)
551 			jls[jlsused++] = '\n';
552 		count = ksnprintf(jls + jlsused, (jlssize - jlsused),
553 				 "%d %s %s",
554 				 pr->pr_id, pr->pr_host, fullpath);
555 		kfree(freepath, M_TEMP);
556 		if (count < 0)
557 			goto end;
558 		jlsused += count;
559 
560 		/* Copy the IPS */
561 		SLIST_FOREACH(jip, &pr->pr_ips, entries) {
562 			jsin = (struct sockaddr_in *)&jip->ip;
563 
564 			switch(jsin->sin_family) {
565 			case AF_INET:
566 				oip = inet_ntoa(jsin->sin_addr);
567 				break;
568 #ifdef INET6
569 			case AF_INET6:
570 				jsin6 = (struct sockaddr_in6 *)&jip->ip;
571 				oip = ip6_sprintf(&jsin6->sin6_addr);
572 				break;
573 #endif
574 			default:
575 				oip = "?family?";
576 				break;
577 			}
578 
579 			if ((jlssize - jlsused) < (strlen(oip) + 1)) {
580 				error = ERANGE;
581 				goto end;
582 			}
583 			count = ksnprintf(jls + jlsused, (jlssize - jlsused),
584 					  " %s", oip);
585 			if (count < 0)
586 				goto end;
587 			jlsused += count;
588 		}
589 	}
590 
591 	/*
592 	 * The format is:
593 	 * pr_id <SPC> hostname1 <SPC> PATH1 <SPC> IP1 <SPC> IP2\npr_id...
594 	 */
595 	error = SYSCTL_OUT(req, jls, jlsused);
596 end:
597 	kfree(jls, M_TEMP);
598 	return(error);
599 }
600 
601 SYSCTL_OID(_jail, OID_AUTO, list, CTLTYPE_STRING | CTLFLAG_RD, NULL, 0,
602 	   sysctl_jail_list, "A", "List of active jails");
603 
604 void
605 prison_hold(struct prison *pr)
606 {
607 	pr->pr_ref++;
608 }
609 
610 void
611 prison_free(struct prison *pr)
612 {
613 	struct jail_ip_storage *jls;
614 	KKASSERT(pr->pr_ref >= 1);
615 
616 	if (--pr->pr_ref > 0)
617 		return;
618 
619 	/* Delete all ips */
620 	while (!SLIST_EMPTY(&pr->pr_ips)) {
621 		jls = SLIST_FIRST(&pr->pr_ips);
622 		SLIST_REMOVE_HEAD(&pr->pr_ips, entries);
623 		kfree(jls, M_PRISON);
624 	}
625 	LIST_REMOVE(pr, pr_list);
626 	prisoncount--;
627 
628 	if (pr->pr_linux != NULL)
629 		kfree(pr->pr_linux, M_PRISON);
630 	varsymset_clean(&pr->pr_varsymset);
631 	cache_drop(&pr->pr_root);
632 	kfree(pr, M_PRISON);
633 }
634