summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorbz <bz@FreeBSD.org>2008-11-29 14:32:14 +0000
committerbz <bz@FreeBSD.org>2008-11-29 14:32:14 +0000
commitd2730d5b27273f2e5a9b9f2703b896b5194496ee (patch)
tree2b8f4835032d12a0e61cc40dad151e279cf7a49f /sys
parent3b10ad8d71152b89fda1bef42dc519fc460aba1b (diff)
downloadFreeBSD-src-d2730d5b27273f2e5a9b9f2703b896b5194496ee.zip
FreeBSD-src-d2730d5b27273f2e5a9b9f2703b896b5194496ee.tar.gz
MFp4:
Bring in updated jail support from bz_jail branch. This enhances the current jail implementation to permit multiple addresses per jail. In addtion to IPv4, IPv6 is supported as well. Due to updated checks it is even possible to have jails without an IP address at all, which basically gives one a chroot with restricted process view, no networking,.. SCTP support was updated and supports IPv6 in jails as well. Cpuset support permits jails to be bound to specific processor sets after creation. Jails can have an unrestricted (no duplicate protection, etc.) name in addition to the hostname. The jail name cannot be changed from within a jail and is considered to be used for management purposes or as audit-token in the future. DDB 'show jails' command was added to aid debugging. Proper compat support permits 32bit jail binaries to be used on 64bit systems to manage jails. Also backward compatibility was preserved where possible: for jail v1 syscalls, as well as with user space management utilities. Both jail as well as prison version were updated for the new features. A gap was intentionally left as the intermediate versions had been used by various patches floating around the last years. Bump __FreeBSD_version for the afore mentioned and in kernel changes. Special thanks to: - Pawel Jakub Dawidek (pjd) for his multi-IPv4 patches and Olivier Houchard (cognet) for initial single-IPv6 patches. - Jeff Roberson (jeff) and Randall Stewart (rrs) for their help, ideas and review on cpuset and SCTP support. - Robert Watson (rwatson) for lots and lots of help, discussions, suggestions and review of most of the patch at various stages. - John Baldwin (jhb) for his help. - Simon L. Nielsen (simon) as early adopter testing changes on cluster machines as well as all the testers and people who provided feedback the last months on freebsd-jail and other channels. - My employer, CK Software GmbH, for the support so I could work on this. Reviewed by: (see above) MFC after: 3 months (this is just so that I get the mail) X-MFC Before: 7.2-RELEASE if possible
Diffstat (limited to 'sys')
-rw-r--r--sys/compat/freebsd32/freebsd32.h18
-rw-r--r--sys/compat/freebsd32/freebsd32_misc.c61
-rw-r--r--sys/compat/freebsd32/syscalls.master2
-rw-r--r--sys/kern/kern_cpuset.c115
-rw-r--r--sys/kern/kern_exit.c5
-rw-r--r--sys/kern/kern_fork.c6
-rw-r--r--sys/kern/kern_jail.c908
-rw-r--r--sys/kern/uipc_socket.c4
-rw-r--r--sys/net/if.c2
-rw-r--r--sys/net/rtsock.c160
-rw-r--r--sys/netinet/in_pcb.c162
-rw-r--r--sys/netinet/raw_ip.c46
-rw-r--r--sys/netinet/sctp_pcb.c35
-rw-r--r--sys/netinet/sctp_usrreq.c77
-rw-r--r--sys/netinet/tcp_usrreq.c12
-rw-r--r--sys/netinet/udp_usrreq.c14
-rw-r--r--sys/netinet6/in6_pcb.c111
-rw-r--r--sys/netinet6/in6_src.c21
-rw-r--r--sys/netinet6/raw_ip6.c13
-rw-r--r--sys/netinet6/udp6_usrreq.c14
-rw-r--r--sys/security/mac_bsdextended/mac_bsdextended.c1
-rw-r--r--sys/sys/cpuset.h5
-rw-r--r--sys/sys/jail.h87
-rw-r--r--sys/sys/param.h2
24 files changed, 1647 insertions, 234 deletions
diff --git a/sys/compat/freebsd32/freebsd32.h b/sys/compat/freebsd32/freebsd32.h
index 0c77bab..08d6510 100644
--- a/sys/compat/freebsd32/freebsd32.h
+++ b/sys/compat/freebsd32/freebsd32.h
@@ -153,6 +153,24 @@ struct stat32 {
unsigned int :(8 / 2) * (16 - (int)sizeof(struct timespec32));
};
+struct jail32_v0 {
+ u_int32_t version;
+ uint32_t path;
+ uint32_t hostname;
+ u_int32_t ip_number;
+};
+
+struct jail32 {
+ uint32_t version;
+ uint32_t path;
+ uint32_t hostname;
+ uint32_t jailname;
+ uint32_t ip4s;
+ uint32_t ip6s;
+ uint32_t ip4;
+ uint32_t ip6;
+};
+
struct sigaction32 {
u_int32_t sa_u;
int sa_flags;
diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c
index 17ca159..266d063 100644
--- a/sys/compat/freebsd32/freebsd32_misc.c
+++ b/sys/compat/freebsd32/freebsd32_misc.c
@@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$");
#include <sys/fcntl.h>
#include <sys/filedesc.h>
#include <sys/imgact.h>
+#include <sys/jail.h>
#include <sys/kernel.h>
#include <sys/limits.h>
#include <sys/lock.h>
@@ -2036,6 +2037,66 @@ done2:
}
int
+freebsd32_jail(struct thread *td, struct freebsd32_jail_args *uap)
+{
+ uint32_t version;
+ int error;
+ struct jail j;
+
+ error = copyin(uap->jail, &version, sizeof(uint32_t));
+ if (error)
+ return (error);
+ switch (version) {
+ case 0:
+ {
+ /* FreeBSD single IPv4 jails. */
+ struct jail32_v0 j32_v0;
+
+ bzero(&j, sizeof(struct jail));
+ error = copyin(uap->jail, &j32_v0, sizeof(struct jail32_v0));
+ if (error)
+ return (error);
+ CP(j32_v0, j, version);
+ PTRIN_CP(j32_v0, j, path);
+ PTRIN_CP(j32_v0, j, hostname);
+ j.ip4s = j32_v0.ip_number;
+ break;
+ }
+
+ case 1:
+ /*
+ * Version 1 was used by multi-IPv4 jail implementations
+ * that never made it into the official kernel.
+ */
+ return (EINVAL);
+
+ case 2: /* JAIL_API_VERSION */
+ {
+ /* FreeBSD multi-IPv4/IPv6,noIP jails. */
+ struct jail32 j32;
+
+ error = copyin(uap->jail, &j32, sizeof(struct jail32));
+ if (error)
+ return (error);
+ CP(j32, j, version);
+ PTRIN_CP(j32, j, path);
+ PTRIN_CP(j32, j, hostname);
+ PTRIN_CP(j32, j, jailname);
+ CP(j32, j, ip4s);
+ CP(j32, j, ip6s);
+ PTRIN_CP(j32, j, ip4);
+ PTRIN_CP(j32, j, ip6);
+ break;
+ }
+
+ default:
+ /* Sci-Fi jails are not supported, sorry. */
+ return (EINVAL);
+ }
+ return (kern_jail(td, &j));
+}
+
+int
freebsd32_sigaction(struct thread *td, struct freebsd32_sigaction_args *uap)
{
struct sigaction32 s32;
diff --git a/sys/compat/freebsd32/syscalls.master b/sys/compat/freebsd32/syscalls.master
index dc06557..97aefd2 100644
--- a/sys/compat/freebsd32/syscalls.master
+++ b/sys/compat/freebsd32/syscalls.master
@@ -572,7 +572,7 @@
off_t *sbytes, int flags); }
337 AUE_NULL NOPROTO { int kldsym(int fileid, int cmd, \
void *data); }
-338 AUE_JAIL NOPROTO { int jail(struct jail *jail); }
+338 AUE_JAIL STD { int freebsd32_jail(struct jail32 *jail); }
339 AUE_NULL UNIMPL pioctl
340 AUE_SIGPROCMASK NOPROTO { int sigprocmask(int how, \
const sigset_t *set, sigset_t *oset); }
diff --git a/sys/kern/kern_cpuset.c b/sys/kern/kern_cpuset.c
index 2a4c248..07e5b24 100644
--- a/sys/kern/kern_cpuset.c
+++ b/sys/kern/kern_cpuset.c
@@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$");
#include <sys/limits.h>
#include <sys/bus.h>
#include <sys/interrupt.h>
+#include <sys/jail.h> /* Must come after sys/proc.h */
#include <vm/uma.h>
@@ -208,7 +209,7 @@ cpuset_rel_complete(struct cpuset *set)
* Find a set based on an id. Returns it with a ref.
*/
static struct cpuset *
-cpuset_lookup(cpusetid_t setid)
+cpuset_lookup(cpusetid_t setid, struct thread *td)
{
struct cpuset *set;
@@ -221,6 +222,28 @@ cpuset_lookup(cpusetid_t setid)
if (set)
cpuset_ref(set);
mtx_unlock_spin(&cpuset_lock);
+
+ KASSERT(td != NULL, ("[%s:%d] td is NULL", __func__, __LINE__));
+ if (set != NULL && jailed(td->td_ucred)) {
+ struct cpuset *rset, *jset;
+ struct prison *pr;
+
+ rset = cpuset_refroot(set);
+
+ pr = td->td_ucred->cr_prison;
+ mtx_lock(&pr->pr_mtx);
+ cpuset_ref(pr->pr_cpuset);
+ jset = pr->pr_cpuset;
+ mtx_unlock(&pr->pr_mtx);
+
+ if (jset->cs_id != rset->cs_id) {
+ cpuset_rel(set);
+ set = NULL;
+ }
+ cpuset_rel(jset);
+ cpuset_rel(rset);
+ }
+
return (set);
}
@@ -412,12 +435,38 @@ cpuset_which(cpuwhich_t which, id_t id, struct proc **pp, struct thread **tdp,
set = cpuset_refbase(curthread->td_cpuset);
thread_unlock(curthread);
} else
- set = cpuset_lookup(id);
+ set = cpuset_lookup(id, curthread);
if (set) {
*setp = set;
return (0);
}
return (ESRCH);
+ case CPU_WHICH_JAIL:
+ {
+ /* Find `set' for prison with given id. */
+ struct prison *pr;
+
+ sx_slock(&allprison_lock);
+ pr = prison_find(id);
+ sx_sunlock(&allprison_lock);
+ if (pr == NULL)
+ return (ESRCH);
+ if (jailed(curthread->td_ucred)) {
+ if (curthread->td_ucred->cr_prison == pr) {
+ cpuset_ref(pr->pr_cpuset);
+ set = pr->pr_cpuset;
+ }
+ } else {
+ cpuset_ref(pr->pr_cpuset);
+ set = pr->pr_cpuset;
+ }
+ mtx_unlock(&pr->pr_mtx);
+ if (set) {
+ *setp = set;
+ return (0);
+ }
+ return (ESRCH);
+ }
case CPU_WHICH_IRQ:
return (0);
default:
@@ -664,6 +713,59 @@ cpuset_thread0(void)
}
/*
+ * Create a cpuset, which would be cpuset_create() but
+ * mark the new 'set' as root.
+ *
+ * We are not going to reparent the td to it. Use cpuset_reparentproc() for that.
+ *
+ * In case of no error, returns the set in *setp locked with a reference.
+ */
+int
+cpuset_create_root(struct thread *td, struct cpuset **setp)
+{
+ struct cpuset *root;
+ struct cpuset *set;
+ int error;
+
+ KASSERT(td != NULL, ("[%s:%d] invalid td", __func__, __LINE__));
+ KASSERT(setp != NULL, ("[%s:%d] invalid setp", __func__, __LINE__));
+
+ thread_lock(td);
+ root = cpuset_refroot(td->td_cpuset);
+ thread_unlock(td);
+
+ error = cpuset_create(setp, td->td_cpuset, &root->cs_mask);
+ cpuset_rel(root);
+ if (error)
+ return (error);
+
+ KASSERT(*setp != NULL, ("[%s:%d] cpuset_create returned invalid data",
+ __func__, __LINE__));
+
+ /* Mark the set as root. */
+ set = *setp;
+ set->cs_flags |= CPU_SET_ROOT;
+
+ return (0);
+}
+
+int
+cpuset_setproc_update_set(struct proc *p, struct cpuset *set)
+{
+ int error;
+
+ KASSERT(p != NULL, ("[%s:%d] invalid proc", __func__, __LINE__));
+ KASSERT(set != NULL, ("[%s:%d] invalid set", __func__, __LINE__));
+
+ cpuset_ref(set);
+ error = cpuset_setproc(p->p_pid, set, NULL);
+ if (error)
+ return (error);
+ cpuset_rel(set);
+ return (0);
+}
+
+/*
* This is called once the final set of system cpus is known. Modifies
* the root set and all children and mark the root readonly.
*/
@@ -728,7 +830,7 @@ cpuset_setid(struct thread *td, struct cpuset_setid_args *uap)
*/
if (uap->which != CPU_WHICH_PID)
return (EINVAL);
- set = cpuset_lookup(uap->setid);
+ set = cpuset_lookup(uap->setid, td);
if (set == NULL)
return (ESRCH);
error = cpuset_setproc(uap->id, set, NULL);
@@ -767,6 +869,7 @@ cpuset_getid(struct thread *td, struct cpuset_getid_args *uap)
PROC_UNLOCK(p);
break;
case CPU_WHICH_CPUSET:
+ case CPU_WHICH_JAIL:
break;
case CPU_WHICH_IRQ:
return (EINVAL);
@@ -829,6 +932,7 @@ cpuset_getaffinity(struct thread *td, struct cpuset_getaffinity_args *uap)
thread_unlock(ttd);
break;
case CPU_WHICH_CPUSET:
+ case CPU_WHICH_JAIL:
break;
case CPU_WHICH_IRQ:
error = EINVAL;
@@ -856,6 +960,7 @@ cpuset_getaffinity(struct thread *td, struct cpuset_getaffinity_args *uap)
}
break;
case CPU_WHICH_CPUSET:
+ case CPU_WHICH_JAIL:
CPU_COPY(&set->cs_mask, mask);
break;
case CPU_WHICH_IRQ:
@@ -936,6 +1041,7 @@ cpuset_setaffinity(struct thread *td, struct cpuset_setaffinity_args *uap)
PROC_UNLOCK(p);
break;
case CPU_WHICH_CPUSET:
+ case CPU_WHICH_JAIL:
break;
case CPU_WHICH_IRQ:
error = EINVAL;
@@ -958,7 +1064,8 @@ cpuset_setaffinity(struct thread *td, struct cpuset_setaffinity_args *uap)
error = cpuset_setproc(uap->id, NULL, mask);
break;
case CPU_WHICH_CPUSET:
- error = cpuset_which(CPU_WHICH_CPUSET, uap->id, &p,
+ case CPU_WHICH_JAIL:
+ error = cpuset_which(uap->which, uap->id, &p,
&ttd, &set);
if (error == 0) {
error = cpuset_modify(set, mask);
diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c
index 6ebe032..3b45233 100644
--- a/sys/kern/kern_exit.c
+++ b/sys/kern/kern_exit.c
@@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$");
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/pioctl.h>
+#include <sys/jail.h>
#include <sys/tty.h>
#include <sys/wait.h>
#include <sys/vmmeter.h>
@@ -453,6 +454,10 @@ exit1(struct thread *td, int rv)
p->p_xstat = rv;
p->p_xthread = td;
+ /* In case we are jailed tell the prison that we are gone. */
+ if (jailed(p->p_ucred))
+ prison_proc_free(p->p_ucred->cr_prison);
+
#ifdef KDTRACE_HOOKS
/*
* Tell the DTrace fasttrap provider about the exit if it
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c
index 673913b..96dd9c8 100644
--- a/sys/kern/kern_fork.c
+++ b/sys/kern/kern_fork.c
@@ -54,6 +54,7 @@ __FBSDID("$FreeBSD$");
#include <sys/mutex.h>
#include <sys/priv.h>
#include <sys/proc.h>
+#include <sys/jail.h>
#include <sys/pioctl.h>
#include <sys/resourcevar.h>
#include <sys/sched.h>
@@ -452,6 +453,11 @@ again:
__rangeof(struct proc, p_startzero, p_endzero));
p2->p_ucred = crhold(td->td_ucred);
+
+ /* In case we are jailed tell the prison that we exist. */
+ if (jailed(p2->p_ucred))
+ prison_proc_hold(p2->p_ucred->cr_prison);
+
PROC_UNLOCK(p2);
/*
diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c
index aae9178..b453d58 100644
--- a/sys/kern/kern_jail.c
+++ b/sys/kern/kern_jail.c
@@ -1,5 +1,7 @@
/*-
- * Copyright (c) 1999 Poul-Henning Kamp. All rights reserved.
+ * Copyright (c) 1999 Poul-Henning Kamp.
+ * Copyright (c) 2008 Bjoern A. Zeeb.
+ * All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -26,6 +28,9 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include "opt_ddb.h"
+#include "opt_inet.h"
+#include "opt_inet6.h"
#include "opt_mac.h"
#include <sys/param.h>
@@ -54,6 +59,12 @@ __FBSDID("$FreeBSD$");
#include <sys/osd.h>
#include <net/if.h>
#include <netinet/in.h>
+#ifdef DDB
+#include <ddb/ddb.h>
+#ifdef INET6
+#include <netinet6/in6_var.h>
+#endif /* INET6 */
+#endif /* DDB */
#include <security/mac/mac_framework.h>
@@ -70,7 +81,7 @@ SYSCTL_INT(_security_jail, OID_AUTO, set_hostname_allowed, CTLFLAG_RW,
int jail_socket_unixiproute_only = 1;
SYSCTL_INT(_security_jail, OID_AUTO, socket_unixiproute_only, CTLFLAG_RW,
&jail_socket_unixiproute_only, 0,
- "Processes in jail are limited to creating UNIX/IPv4/route sockets only");
+ "Processes in jail are limited to creating UNIX/IP/route sockets only");
int jail_sysvipc_allowed = 0;
SYSCTL_INT(_security_jail, OID_AUTO, sysvipc_allowed, CTLFLAG_RW,
@@ -97,6 +108,11 @@ SYSCTL_INT(_security_jail, OID_AUTO, mount_allowed, CTLFLAG_RW,
&jail_mount_allowed, 0,
"Processes in jail can mount/unmount jail-friendly file systems");
+int jail_max_af_ips = 255;
+SYSCTL_INT(_security_jail, OID_AUTO, jail_max_af_ips, CTLFLAG_RW,
+ &jail_max_af_ips, 0,
+ "Number of IP addresses a jail may have at most per address family");
+
/* allprison, lastprid, and prisoncount are protected by allprison_lock. */
struct prisonlist allprison;
struct sx allprison_lock;
@@ -106,6 +122,12 @@ int prisoncount = 0;
static void init_prison(void *);
static void prison_complete(void *context, int pending);
static int sysctl_jail_list(SYSCTL_HANDLER_ARGS);
+#ifdef INET
+static int _prison_check_ip4(struct prison *, struct in_addr *);
+#endif
+#ifdef INET6
+static int _prison_check_ip6(struct prison *, struct in6_addr *);
+#endif
static void
init_prison(void *data __unused)
@@ -117,6 +139,276 @@ init_prison(void *data __unused)
SYSINIT(prison, SI_SUB_INTRINSIC, SI_ORDER_ANY, init_prison, NULL);
+#ifdef INET
+static int
+qcmp_v4(const void *ip1, const void *ip2)
+{
+ in_addr_t iaa, iab;
+
+ /*
+ * We need to compare in HBO here to get the list sorted as expected
+ * by the result of the code. Sorting NBO addresses gives you
+ * interesting results. If you do not understand, do not try.
+ */
+ iaa = ntohl(((const struct in_addr *)ip1)->s_addr);
+ iab = ntohl(((const struct in_addr *)ip2)->s_addr);
+
+ /*
+ * Do not simply return the difference of the two numbers, the int is
+ * not wide enough.
+ */
+ if (iaa > iab)
+ return (1);
+ else if (iaa < iab)
+ return (-1);
+ else
+ return (0);
+}
+#endif
+
+#ifdef INET6
+static int
+qcmp_v6(const void *ip1, const void *ip2)
+{
+ const struct in6_addr *ia6a, *ia6b;
+ int i, rc;
+
+ ia6a = (const struct in6_addr *)ip1;
+ ia6b = (const struct in6_addr *)ip2;
+
+ rc = 0;
+ for (i=0; rc == 0 && i < sizeof(struct in6_addr); i++) {
+ if (ia6a->s6_addr[i] > ia6b->s6_addr[i])
+ rc = 1;
+ else if (ia6a->s6_addr[i] < ia6b->s6_addr[i])
+ rc = -1;
+ }
+ return (rc);
+}
+#endif
+
+#if defined(INET) || defined(INET6)
+static int
+prison_check_conflicting_ips(struct prison *p)
+{
+ struct prison *pr;
+ int i;
+
+ sx_assert(&allprison_lock, SX_LOCKED);
+
+ if (p->pr_ip4s == 0 && p->pr_ip6s == 0)
+ return (0);
+
+ LIST_FOREACH(pr, &allprison, pr_list) {
+ /*
+ * Skip 'dying' prisons to avoid problems when
+ * restarting multi-IP jails.
+ */
+ if (pr->pr_state == PRISON_STATE_DYING)
+ continue;
+
+ /*
+ * We permit conflicting IPs if there is no
+ * more than 1 IP on eeach jail.
+ * In case there is one duplicate on a jail with
+ * more than one IP stop checking and return error.
+ */
+#ifdef INET
+ if ((p->pr_ip4s >= 1 && pr->pr_ip4s > 1) ||
+ (p->pr_ip4s > 1 && pr->pr_ip4s >= 1)) {
+ for (i = 0; i < p->pr_ip4s; i++) {
+ if (_prison_check_ip4(pr, &p->pr_ip4[i]))
+ return (EINVAL);
+ }
+ }
+#endif
+#ifdef INET6
+ if ((p->pr_ip6s >= 1 && pr->pr_ip6s > 1) ||
+ (p->pr_ip6s > 1 && pr->pr_ip6s >= 1)) {
+ for (i = 0; i < p->pr_ip6s; i++) {
+ if (_prison_check_ip6(pr, &p->pr_ip6[i]))
+ return (EINVAL);
+ }
+ }
+#endif
+ }
+
+ return (0);
+}
+
+static int
+jail_copyin_ips(struct jail *j)
+{
+#ifdef INET
+ struct in_addr *ip4;
+#endif
+#ifdef INET6
+ struct in6_addr *ip6;
+#endif
+ int error, i;
+
+ /*
+ * Copy in addresses, check for duplicate addresses and do some
+ * simple 0 and broadcast checks. If users give other bogus addresses
+ * it is their problem.
+ *
+ * IP addresses are all sorted but ip[0] to preserve the primary IP
+ * address as given from userland. This special IP is used for
+ * unbound outgoing connections as well for "loopback" traffic.
+ */
+#ifdef INET
+ ip4 = NULL;
+#endif
+#ifdef INET6
+ ip6 = NULL;
+#endif
+#ifdef INET
+ if (j->ip4s > 0) {
+ ip4 = (struct in_addr *)malloc(j->ip4s * sizeof(struct in_addr),
+ M_PRISON, M_WAITOK | M_ZERO);
+ error = copyin(j->ip4, ip4, j->ip4s * sizeof(struct in_addr));
+ if (error)
+ goto e_free_ip;
+ /* Sort all but the first IPv4 address. */
+ if (j->ip4s > 1)
+ qsort((ip4 + 1), j->ip4s - 1,
+ sizeof(struct in_addr), qcmp_v4);
+
+ /*
+ * We do not have to care about byte order for these checks
+ * so we will do them in NBO.
+ */
+ for (i=0; i<j->ip4s; i++) {
+ if (ip4[i].s_addr == htonl(INADDR_ANY) ||
+ ip4[i].s_addr == htonl(INADDR_BROADCAST)) {
+ error = EINVAL;
+ goto e_free_ip;
+ }
+ if ((i+1) < j->ip4s &&
+ (ip4[0].s_addr == ip4[i+1].s_addr ||
+ ip4[i].s_addr == ip4[i+1].s_addr)) {
+ error = EINVAL;
+ goto e_free_ip;
+ }
+ }
+
+ j->ip4 = ip4;
+ }
+#endif
+#ifdef INET6
+ if (j->ip6s > 0) {
+ ip6 = (struct in6_addr *)malloc(j->ip6s * sizeof(struct in6_addr),
+ M_PRISON, M_WAITOK | M_ZERO);
+ error = copyin(j->ip6, ip6, j->ip6s * sizeof(struct in6_addr));
+ if (error)
+ goto e_free_ip;
+ /* Sort all but the first IPv6 address. */
+ if (j->ip6s > 1)
+ qsort((ip6 + 1), j->ip6s - 1,
+ sizeof(struct in6_addr), qcmp_v6);
+ for (i=0; i<j->ip6s; i++) {
+ if (IN6_IS_ADDR_UNSPECIFIED(&ip6[i])) {
+ error = EINVAL;
+ goto e_free_ip;
+ }
+ if ((i+1) < j->ip6s &&
+ (IN6_ARE_ADDR_EQUAL(&ip6[0], &ip6[i+1]) ||
+ IN6_ARE_ADDR_EQUAL(&ip6[i], &ip6[i+1]))) {
+ error = EINVAL;
+ goto e_free_ip;
+ }
+ }
+
+ j->ip6 = ip6;
+ }
+#endif
+ return (0);
+
+e_free_ip:
+#ifdef INET6
+ free(ip6, M_PRISON);
+#endif
+#ifdef INET
+ free(ip4, M_PRISON);
+#endif
+ return (error);
+}
+#endif /* INET || INET6 */
+
+static int
+jail_handle_ips(struct jail *j)
+{
+#if defined(INET) || defined(INET6)
+ int error;
+#endif
+
+ /*
+ * Finish conversion for older versions, copyin and setup IPs.
+ */
+ switch (j->version) {
+ case 0:
+ {
+#ifdef INET
+ /* FreeBSD single IPv4 jails. */
+ struct in_addr *ip4;
+
+ if (j->ip4s == INADDR_ANY || j->ip4s == INADDR_BROADCAST)
+ return (EINVAL);
+ ip4 = (struct in_addr *)malloc(sizeof(struct in_addr),
+ M_PRISON, M_WAITOK | M_ZERO);
+
+ /*
+ * Jail version 0 still used HBO for the IPv4 address.
+ */
+ ip4->s_addr = htonl(j->ip4s);
+ j->ip4s = 1;
+ j->ip4 = ip4;
+ break;
+#else
+ return (EINVAL);
+#endif
+ }
+
+ case 1:
+ /*
+ * Version 1 was used by multi-IPv4 jail implementations
+ * that never made it into the official kernel.
+ * We should never hit this here; jail() should catch it.
+ */
+ return (EINVAL);
+
+ case 2: /* JAIL_API_VERSION */
+ /* FreeBSD multi-IPv4/IPv6,noIP jails. */
+#if defined(INET) || defined(INET6)
+#ifdef INET
+ if (j->ip4s > jail_max_af_ips)
+ return (EINVAL);
+#else
+ if (j->ip4s != 0)
+ return (EINVAL);
+#endif
+#ifdef INET6
+ if (j->ip6s > jail_max_af_ips)
+ return (EINVAL);
+#else
+ if (j->ip6s != 0)
+ return (EINVAL);
+#endif
+ error = jail_copyin_ips(j);
+ if (error)
+ return (error);
+#endif
+ break;
+
+ default:
+ /* Sci-Fi jails are not supported, sorry. */
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+
/*
* struct jail_args {
* struct jail *jail;
@@ -125,22 +417,72 @@ SYSINIT(prison, SI_SUB_INTRINSIC, SI_ORDER_ANY, init_prison, NULL);
int
jail(struct thread *td, struct jail_args *uap)
{
+ uint32_t version;
+ int error;
+ struct jail j;
+
+ error = copyin(uap->jail, &version, sizeof(uint32_t));
+ if (error)
+ return (error);
+
+ switch (version) {
+ case 0:
+ /* FreeBSD single IPv4 jails. */
+ {
+ struct jail_v0 j0;
+
+ bzero(&j, sizeof(struct jail));
+ error = copyin(uap->jail, &j0, sizeof(struct jail_v0));
+ if (error)
+ return (error);
+ j.version = j0.version;
+ j.path = j0.path;
+ j.hostname = j0.hostname;
+ j.ip4s = j0.ip_number;
+ break;
+ }
+
+ case 1:
+ /*
+ * Version 1 was used by multi-IPv4 jail implementations
+ * that never made it into the official kernel.
+ */
+ return (EINVAL);
+
+ case 2: /* JAIL_API_VERSION */
+ /* FreeBSD multi-IPv4/IPv6,noIP jails. */
+ error = copyin(uap->jail, &j, sizeof(struct jail));
+ if (error)
+ return (error);
+ break;
+
+ default:
+ /* Sci-Fi jails are not supported, sorry. */
+ return (EINVAL);
+ }
+ return (kern_jail(td, &j));
+}
+
+int
+kern_jail(struct thread *td, struct jail *j)
+{
struct nameidata nd;
struct prison *pr, *tpr;
- struct jail j;
struct jail_attach_args jaa;
int vfslocked, error, tryprid;
- error = copyin(uap->jail, &j, sizeof(j));
+ KASSERT(j != NULL, ("%s: j is NULL", __func__));
+
+ /* Handle addresses - convert old structs, copyin, check IPs. */
+ error = jail_handle_ips(j);
if (error)
return (error);
- if (j.version != 0)
- return (EINVAL);
+ /* Allocate struct prison and fill it with life. */
pr = malloc(sizeof(*pr), M_PRISON, M_WAITOK | M_ZERO);
mtx_init(&pr->pr_mtx, "jail mutex", NULL, MTX_DEF);
pr->pr_ref = 1;
- error = copyinstr(j.path, &pr->pr_path, sizeof(pr->pr_path), 0);
+ error = copyinstr(j->path, &pr->pr_path, sizeof(pr->pr_path), NULL);
if (error)
goto e_killmtx;
NDINIT(&nd, LOOKUP, MPSAFE | FOLLOW | LOCKLEAF, UIO_SYSSPACE,
@@ -153,16 +495,50 @@ jail(struct thread *td, struct jail_args *uap)
VOP_UNLOCK(nd.ni_vp, 0);
NDFREE(&nd, NDF_ONLY_PNBUF);
VFS_UNLOCK_GIANT(vfslocked);
- error = copyinstr(j.hostname, &pr->pr_host, sizeof(pr->pr_host), 0);
+ error = copyinstr(j->hostname, &pr->pr_host, sizeof(pr->pr_host), NULL);
if (error)
goto e_dropvnref;
- pr->pr_ip = j.ip_number;
+ if (j->jailname != NULL) {
+ error = copyinstr(j->jailname, &pr->pr_name,
+ sizeof(pr->pr_name), NULL);
+ if (error)
+ goto e_dropvnref;
+ }
+ if (j->ip4s > 0) {
+ pr->pr_ip4 = j->ip4;
+ pr->pr_ip4s = j->ip4s;
+ }
+#ifdef INET6
+ if (j->ip6s > 0) {
+ pr->pr_ip6 = j->ip6;
+ pr->pr_ip6s = j->ip6s;
+ }
+#endif
pr->pr_linux = NULL;
pr->pr_securelevel = securelevel;
bzero(&pr->pr_osd, sizeof(pr->pr_osd));
- /* Determine next pr_id and add prison to allprison list. */
+ /*
+ * Pre-set prison state to ALIVE upon cration. This is needed so we
+ * can later attach the process to it, etc (avoiding another extra
+ * state for ther process of creation, complicating things).
+ */
+ pr->pr_state = PRISON_STATE_ALIVE;
+
+ /* Allocate a dedicated cpuset for each jail. */
+ error = cpuset_create_root(td, &pr->pr_cpuset);
+ if (error)
+ goto e_dropvnref;
+
sx_xlock(&allprison_lock);
+ /* Make sure we cannot run into problems with ambiguous bind()ings. */
+ error = prison_check_conflicting_ips(pr);
+ if (error) {
+ sx_xunlock(&allprison_lock);
+ goto e_dropcpuset;
+ }
+
+ /* Determine next pr_id and add prison to allprison list. */
tryprid = lastprid + 1;
if (tryprid == JAIL_MAX)
tryprid = 1;
@@ -173,7 +549,7 @@ next:
if (tryprid == JAIL_MAX) {
sx_xunlock(&allprison_lock);
error = EAGAIN;
- goto e_dropvnref;
+ goto e_dropcpuset;
}
goto next;
}
@@ -196,6 +572,8 @@ e_dropprref:
LIST_REMOVE(pr, pr_list);
prisoncount--;
sx_xunlock(&allprison_lock);
+e_dropcpuset:
+ cpuset_rel(pr->pr_cpuset);
e_dropvnref:
vfslocked = VFS_LOCK_GIANT(pr->pr_root->v_mount);
vrele(pr->pr_root);
@@ -203,6 +581,12 @@ e_dropvnref:
e_killmtx:
mtx_destroy(&pr->pr_mtx);
free(pr, M_PRISON);
+#ifdef INET6
+ free(j->ip6, M_PRISON);
+#endif
+#ifdef INET
+ free(j->ip4, M_PRISON);
+#endif
return (error);
}
@@ -238,10 +622,27 @@ jail_attach(struct thread *td, struct jail_attach_args *uap)
sx_sunlock(&allprison_lock);
return (EINVAL);
}
+
+ /*
+ * Do not allow a process to attach to a prison that is not
+ * considered to be "ALIVE".
+ */
+ if (pr->pr_state != PRISON_STATE_ALIVE) {
+ mtx_unlock(&pr->pr_mtx);
+ sx_sunlock(&allprison_lock);
+ return (EINVAL);
+ }
pr->pr_ref++;
mtx_unlock(&pr->pr_mtx);
sx_sunlock(&allprison_lock);
+ /*
+ * Reparent the newly attached process to this jail.
+ */
+ error = cpuset_setproc_update_set(p, pr->pr_cpuset);
+ if (error)
+ goto e_unref;
+
vfslocked = VFS_LOCK_GIANT(pr->pr_root->v_mount);
vn_lock(pr->pr_root, LK_EXCLUSIVE | LK_RETRY);
if ((error = change_dir(pr->pr_root, td)) != 0)
@@ -261,12 +662,14 @@ jail_attach(struct thread *td, struct jail_attach_args *uap)
crcopy(newcred, oldcred);
newcred->cr_prison = pr;
p->p_ucred = newcred;
+ prison_proc_hold(pr);
PROC_UNLOCK(p);
crfree(oldcred);
return (0);
e_unlock:
VOP_UNLOCK(pr->pr_root, 0);
VFS_UNLOCK_GIANT(vfslocked);
+e_unref:
mtx_lock(&pr->pr_mtx);
pr->pr_ref--;
mtx_unlock(&pr->pr_mtx);
@@ -331,6 +734,8 @@ prison_complete(void *context, int pending)
prisoncount--;
sx_xunlock(&allprison_lock);
+ cpuset_rel(pr->pr_cpuset);
+
/* Free all OSD associated to this jail. */
osd_jail_exit(pr);
@@ -339,8 +744,13 @@ prison_complete(void *context, int pending)
VFS_UNLOCK_GIANT(vfslocked);
mtx_destroy(&pr->pr_mtx);
- if (pr->pr_linux != NULL)
- free(pr->pr_linux, M_PRISON);
+ free(pr->pr_linux, M_PRISON);
+#ifdef INET6
+ free(pr->pr_ip6, M_PRISON);
+#endif
+#ifdef INET
+ free(pr->pr_ip4, M_PRISON);
+#endif
free(pr, M_PRISON);
}
@@ -363,79 +773,364 @@ prison_hold(struct prison *pr)
mtx_unlock(&pr->pr_mtx);
}
-u_int32_t
-prison_getip(struct ucred *cred)
+void
+prison_proc_hold(struct prison *pr)
{
- return (cred->cr_prison->pr_ip);
+ mtx_lock(&pr->pr_mtx);
+ KASSERT(pr->pr_state == PRISON_STATE_ALIVE,
+ ("Cannot add a process to a non-alive prison (id=%d).", pr->pr_id));
+ pr->pr_nprocs++;
+ mtx_unlock(&pr->pr_mtx);
}
+void
+prison_proc_free(struct prison *pr)
+{
+
+ mtx_lock(&pr->pr_mtx);
+ KASSERT(pr->pr_state == PRISON_STATE_ALIVE && pr->pr_nprocs > 0,
+ ("Trying to kill a process in a dead prison (id=%d).", pr->pr_id));
+ pr->pr_nprocs--;
+ if (pr->pr_nprocs == 0)
+ pr->pr_state = PRISON_STATE_DYING;
+ mtx_unlock(&pr->pr_mtx);
+}
+
+
+#ifdef INET
+/*
+ * Pass back primary IPv4 address of this jail.
+ *
+ * If not jailed return success but do not alter the address. Caller has to
+ * make sure to intialize it correctly (INADDR_ANY).
+ *
+ * Returns 0 on success, 1 on error. Address returned in NBO.
+ */
int
-prison_ip(struct ucred *cred, int flag, u_int32_t *ip)
+prison_getip4(struct ucred *cred, struct in_addr *ia)
{
- u_int32_t tmp;
+
+ KASSERT(cred != NULL, ("%s: cred is NULL", __func__));
+ KASSERT(ia != NULL, ("%s: ia is NULL", __func__));
if (!jailed(cred))
+ /* Do not change address passed in. */
return (0);
- if (flag)
- tmp = *ip;
- else
- tmp = ntohl(*ip);
- if (tmp == INADDR_ANY) {
- if (flag)
- *ip = cred->cr_prison->pr_ip;
- else
- *ip = htonl(cred->cr_prison->pr_ip);
+
+ if (cred->cr_prison->pr_ip4 == NULL)
+ return (1);
+
+ ia->s_addr = cred->cr_prison->pr_ip4[0].s_addr;
+ return (0);
+}
+
+/*
+ * Make sure our (source) address is set to something meaningful to this
+ * jail.
+ *
+ * Returns 0 on success, 1 on error. Address passed in in NBO and returned
+ * in NBO.
+ */
+int
+prison_local_ip4(struct ucred *cred, struct in_addr *ia)
+{
+ struct in_addr ia0;
+
+ KASSERT(cred != NULL, ("%s: cred is NULL", __func__));
+ KASSERT(ia != NULL, ("%s: ia is NULL", __func__));
+
+ if (!jailed(cred))
+ return (0);
+ if (cred->cr_prison->pr_ip4 == NULL)
+ return (1);
+
+ ia0.s_addr = ntohl(ia->s_addr);
+ if (ia0.s_addr == INADDR_LOOPBACK) {
+ ia->s_addr = cred->cr_prison->pr_ip4[0].s_addr;
return (0);
}
- if (tmp == INADDR_LOOPBACK) {
- if (flag)
- *ip = cred->cr_prison->pr_ip;
- else
- *ip = htonl(cred->cr_prison->pr_ip);
+
+ /*
+ * In case there is only 1 IPv4 address, bind directly.
+ */
+ if (ia0.s_addr == INADDR_ANY && cred->cr_prison->pr_ip4s == 1) {
+ ia->s_addr = cred->cr_prison->pr_ip4[0].s_addr;
return (0);
}
- if (cred->cr_prison->pr_ip != tmp)
+
+ if (ia0.s_addr == INADDR_ANY || prison_check_ip4(cred, ia))
+ return (0);
+
+ return (1);
+}
+
+/*
+ * Rewrite destination address in case we will connect to loopback address.
+ *
+ * Returns 0 on success, 1 on error. Address passed in in NBO and returned
+ * in NBO.
+ */
+int
+prison_remote_ip4(struct ucred *cred, struct in_addr *ia)
+{
+
+ KASSERT(cred != NULL, ("%s: cred is NULL", __func__));
+ KASSERT(ia != NULL, ("%s: ia is NULL", __func__));
+
+ if (!jailed(cred))
+ return (0);
+ if (cred->cr_prison->pr_ip4 == NULL)
return (1);
+ if (ntohl(ia->s_addr) == INADDR_LOOPBACK) {
+ ia->s_addr = cred->cr_prison->pr_ip4[0].s_addr;
+ return (0);
+ }
+
+ /*
+ * Return success because nothing had to be changed.
+ */
return (0);
}
-void
-prison_remote_ip(struct ucred *cred, int flag, u_int32_t *ip)
+/*
+ * Check if given address belongs to the jail referenced by cred.
+ *
+ * Returns 1 if address belongs to jail, 0 if not. Address passed in in NBO.
+ */
+static int
+_prison_check_ip4(struct prison *pr, struct in_addr *ia)
{
- u_int32_t tmp;
+ int i, a, z, d;
+
+ if (pr->pr_ip4 == NULL)
+ return (0);
+
+ /*
+ * Check the primary IP.
+ */
+ if (pr->pr_ip4[0].s_addr == ia->s_addr)
+ return (1);
+
+ /*
+ * All the other IPs are sorted so we can do a binary search.
+ */
+ a = 0;
+ z = pr->pr_ip4s - 2;
+ while (a <= z) {
+ i = (a + z) / 2;
+ d = qcmp_v4(&pr->pr_ip4[i+1], ia);
+ if (d > 0)
+ z = i - 1;
+ else if (d < 0)
+ a = i + 1;
+ else
+ return (1);
+ }
+ return (0);
+}
+
+int
+prison_check_ip4(struct ucred *cred, struct in_addr *ia)
+{
+
+ KASSERT(cred != NULL, ("%s: cred is NULL", __func__));
+ KASSERT(ia != NULL, ("%s: ia is NULL", __func__));
if (!jailed(cred))
- return;
- if (flag)
- tmp = *ip;
- else
- tmp = ntohl(*ip);
- if (tmp == INADDR_LOOPBACK) {
- if (flag)
- *ip = cred->cr_prison->pr_ip;
+ return (1);
+
+ return (_prison_check_ip4(cred->cr_prison, ia));
+}
+#endif
+
+#ifdef INET6
+/*
+ * Pass back primary IPv6 address for this jail.
+ *
+ * If not jailed return success but do not alter the address. Caller has to
+ * make sure to intialize it correctly (IN6ADDR_ANY_INIT).
+ *
+ * Returns 0 on success, 1 on error.
+ */
+int
+prison_getip6(struct ucred *cred, struct in6_addr *ia6)
+{
+
+ KASSERT(cred != NULL, ("%s: cred is NULL", __func__));
+ KASSERT(ia6 != NULL, ("%s: ia6 is NULL", __func__));
+
+ if (!jailed(cred))
+ return (0);
+ if (cred->cr_prison->pr_ip6 == NULL)
+ return (1);
+ bcopy(&cred->cr_prison->pr_ip6[0], ia6, sizeof(struct in6_addr));
+ return (0);
+}
+
+/*
+ * Make sure our (source) address is set to something meaningful to this jail.
+ *
+ * v6only should be set based on (inp->inp_flags & IN6P_IPV6_V6ONLY != 0)
+ * when needed while binding.
+ *
+ * Returns 0 on success, 1 on error.
+ */
+int
+prison_local_ip6(struct ucred *cred, struct in6_addr *ia6, int v6only)
+{
+
+ KASSERT(cred != NULL, ("%s: cred is NULL", __func__));
+ KASSERT(ia6 != NULL, ("%s: ia6 is NULL", __func__));
+
+ if (!jailed(cred))
+ return (0);
+ if (cred->cr_prison->pr_ip6 == NULL)
+ return (1);
+ if (IN6_IS_ADDR_LOOPBACK(ia6)) {
+ bcopy(&cred->cr_prison->pr_ip6[0], ia6,
+ sizeof(struct in6_addr));
+ return (0);
+ }
+
+ /*
+ * In case there is only 1 IPv6 address, and v6only is true, then
+ * bind directly.
+ */
+ if (v6only != 0 && IN6_IS_ADDR_UNSPECIFIED(ia6) &&
+ cred->cr_prison->pr_ip6s == 1) {
+ bcopy(&cred->cr_prison->pr_ip6[0], ia6,
+ sizeof(struct in6_addr));
+ return (0);
+ }
+ if (IN6_IS_ADDR_UNSPECIFIED(ia6) || prison_check_ip6(cred, ia6))
+ return (0);
+ return (1);
+}
+
+/*
+ * Rewrite destination address in case we will connect to loopback address.
+ *
+ * Returns 0 on success, 1 on error.
+ */
+int
+prison_remote_ip6(struct ucred *cred, struct in6_addr *ia6)
+{
+
+ KASSERT(cred != NULL, ("%s: cred is NULL", __func__));
+ KASSERT(ia6 != NULL, ("%s: ia6 is NULL", __func__));
+
+ if (!jailed(cred))
+ return (0);
+ if (cred->cr_prison->pr_ip6 == NULL)
+ return (1);
+ if (IN6_IS_ADDR_LOOPBACK(ia6)) {
+ bcopy(&cred->cr_prison->pr_ip6[0], ia6,
+ sizeof(struct in6_addr));
+ return (0);
+ }
+
+ /*
+ * Return success because nothing had to be changed.
+ */
+ return (0);
+}
+
+/*
+ * Check if given address belongs to the jail referenced by cred.
+ *
+ * Returns 1 if address belongs to jail, 0 if not.
+ */
+static int
+_prison_check_ip6(struct prison *pr, struct in6_addr *ia6)
+{
+ int i, a, z, d;
+
+ if (pr->pr_ip6 == NULL)
+ return (0);
+
+ /*
+ * Check the primary IP.
+ */
+ if (IN6_ARE_ADDR_EQUAL(&pr->pr_ip6[0], ia6))
+ return (1);
+
+ /*
+ * All the other IPs are sorted so we can do a binary search.
+ */
+ a = 0;
+ z = pr->pr_ip6s - 2;
+ while (a <= z) {
+ i = (a + z) / 2;
+ d = qcmp_v6(&pr->pr_ip6[i+1], ia6);
+ if (d > 0)
+ z = i - 1;
+ else if (d < 0)
+ a = i + 1;
else
- *ip = htonl(cred->cr_prison->pr_ip);
- return;
+ return (1);
}
- return;
+ return (0);
}
int
+prison_check_ip6(struct ucred *cred, struct in6_addr *ia6)
+{
+
+ KASSERT(cred != NULL, ("%s: cred is NULL", __func__));
+ KASSERT(ia6 != NULL, ("%s: ia6 is NULL", __func__));
+
+ if (!jailed(cred))
+ return (1);
+
+ return (_prison_check_ip6(cred->cr_prison, ia6));
+}
+#endif
+
+/*
+ * Check if given address belongs to the jail referenced by cred (wrapper to
+ * prison_check_ip[46]).
+ *
+ * Returns 1 if address belongs to jail, 0 if not. IPv4 Address passed in in
+ * NBO.
+ */
+int
prison_if(struct ucred *cred, struct sockaddr *sa)
{
+#ifdef INET
struct sockaddr_in *sai;
+#endif
+#ifdef INET6
+ struct sockaddr_in6 *sai6;
+#endif
int ok;
- sai = (struct sockaddr_in *)sa;
- if ((sai->sin_family != AF_INET) && jail_socket_unixiproute_only)
- ok = 1;
- else if (sai->sin_family != AF_INET)
- ok = 0;
- else if (cred->cr_prison->pr_ip != ntohl(sai->sin_addr.s_addr))
- ok = 1;
- else
- ok = 0;
+ KASSERT(cred != NULL, ("%s: cred is NULL", __func__));
+ KASSERT(sa != NULL, ("%s: sa is NULL", __func__));
+
+ ok = 0;
+ switch(sa->sa_family)
+ {
+#ifdef INET
+ case AF_INET:
+ sai = (struct sockaddr_in *)sa;
+ if (prison_check_ip4(cred, &sai->sin_addr))
+ ok = 1;
+ break;
+
+#endif
+#ifdef INET6
+ case AF_INET6:
+ sai6 = (struct sockaddr_in6 *)sa;
+ if (prison_check_ip6(cred, (struct in6_addr *)&sai6->sin6_addr))
+ ok = 1;
+ break;
+
+#endif
+ default:
+ if (!jail_socket_unixiproute_only)
+ ok = 1;
+ }
return (ok);
}
@@ -655,6 +1350,7 @@ prison_priv_check(struct ucred *cred, int priv)
* processes in the same jail. Likewise for signalling.
*/
case PRIV_SCHED_DIFFCRED:
+ case PRIV_SCHED_CPUSET:
case PRIV_SIGNAL_DIFFCRED:
case PRIV_SIGNAL_SUGID:
@@ -764,6 +1460,8 @@ sysctl_jail_list(SYSCTL_HANDLER_ARGS)
{
struct xprison *xp, *sxp;
struct prison *pr;
+ char *p;
+ size_t len;
int count, error;
if (jailed(req->td->td_ucred))
@@ -775,21 +1473,54 @@ sysctl_jail_list(SYSCTL_HANDLER_ARGS)
return (0);
}
- sxp = xp = malloc(sizeof(*xp) * count, M_TEMP, M_WAITOK | M_ZERO);
+ len = sizeof(*xp) * count;
+ LIST_FOREACH(pr, &allprison, pr_list) {
+#ifdef INET
+ len += pr->pr_ip4s * sizeof(struct in_addr);
+#endif
+#ifdef INET6
+ len += pr->pr_ip6s * sizeof(struct in6_addr);
+#endif
+ }
+
+ sxp = xp = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
LIST_FOREACH(pr, &allprison, pr_list) {
xp->pr_version = XPRISON_VERSION;
xp->pr_id = pr->pr_id;
- xp->pr_ip = pr->pr_ip;
+ xp->pr_state = pr->pr_state;
+ xp->pr_cpusetid = pr->pr_cpuset->cs_id;
strlcpy(xp->pr_path, pr->pr_path, sizeof(xp->pr_path));
mtx_lock(&pr->pr_mtx);
strlcpy(xp->pr_host, pr->pr_host, sizeof(xp->pr_host));
+ strlcpy(xp->pr_name, pr->pr_name, sizeof(xp->pr_name));
mtx_unlock(&pr->pr_mtx);
- xp++;
+#ifdef INET
+ xp->pr_ip4s = pr->pr_ip4s;
+#endif
+#ifdef INET6
+ xp->pr_ip6s = pr->pr_ip6s;
+#endif
+ p = (char *)(xp + 1);
+#ifdef INET
+ if (pr->pr_ip4s > 0) {
+ bcopy(pr->pr_ip4, (struct in_addr *)p,
+ pr->pr_ip4s * sizeof(struct in_addr));
+ p += (pr->pr_ip4s * sizeof(struct in_addr));
+ }
+#endif
+#ifdef INET6
+ if (pr->pr_ip6s > 0) {
+ bcopy(pr->pr_ip6, (struct in6_addr *)p,
+ pr->pr_ip6s * sizeof(struct in6_addr));
+ p += (pr->pr_ip6s * sizeof(struct in6_addr));
+ }
+#endif
+ xp = (struct xprison *)p;
}
sx_sunlock(&allprison_lock);
- error = SYSCTL_OUT(req, sxp, sizeof(*sxp) * count);
+ error = SYSCTL_OUT(req, sxp, len);
free(sxp, M_TEMP);
return (error);
}
@@ -809,3 +1540,60 @@ sysctl_jail_jailed(SYSCTL_HANDLER_ARGS)
}
SYSCTL_PROC(_security_jail, OID_AUTO, jailed, CTLTYPE_INT | CTLFLAG_RD,
NULL, 0, sysctl_jail_jailed, "I", "Process in jail?");
+
+#ifdef DDB
+DB_SHOW_COMMAND(jails, db_show_jails)
+{
+ struct prison *pr;
+#ifdef INET
+ struct in_addr ia;
+#endif
+#ifdef INET6
+ char ip6buf[INET6_ADDRSTRLEN];
+#endif
+ const char *state;
+#if defined(INET) || defined(INET6)
+ int i;
+#endif
+
+ db_printf(
+ " JID pr_ref pr_nprocs pr_ip4s pr_ip6s\n");
+ db_printf(
+ " Hostname Path\n");
+ db_printf(
+ " Name State\n");
+ db_printf(
+ " Cpusetid\n");
+ db_printf(
+ " IP Address(es)\n");
+ LIST_FOREACH(pr, &allprison, pr_list) {
+ db_printf("%6d %6d %9d %7d %7d\n",
+ pr->pr_id, pr->pr_ref, pr->pr_nprocs,
+ pr->pr_ip4s, pr->pr_ip6s);
+ db_printf("%6s %-29.29s %.74s\n",
+ "", pr->pr_host, pr->pr_path);
+ if (pr->pr_state < 0 || pr->pr_state > (int)((sizeof(
+ prison_states) / sizeof(struct prison_state))))
+ state = "(bogus)";
+ else
+ state = prison_states[pr->pr_state].state_name;
+ db_printf("%6s %-29.29s %.74s\n",
+ "", (pr->pr_name != NULL) ? pr->pr_name : "", state);
+ db_printf("%6s %-6d\n",
+ "", pr->pr_cpuset->cs_id);
+#ifdef INET
+ for (i=0; i < pr->pr_ip4s; i++) {
+ ia.s_addr = pr->pr_ip4[i].s_addr;
+ db_printf("%6s %s\n", "", inet_ntoa(ia));
+ }
+#endif
+#ifdef INET6
+ for (i=0; i < pr->pr_ip6s; i++)
+ db_printf("%6s %s\n",
+ "", ip6_sprintf(ip6buf, &pr->pr_ip6[i]));
+#endif /* INET6 */
+ if (db_pager_quit)
+ break;
+ }
+}
+#endif /* DDB */
diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c
index 53c800e..2aa63e8 100644
--- a/sys/kern/uipc_socket.c
+++ b/sys/kern/uipc_socket.c
@@ -98,6 +98,7 @@
__FBSDID("$FreeBSD$");
#include "opt_inet.h"
+#include "opt_inet6.h"
#include "opt_mac.h"
#include "opt_zero.h"
#include "opt_compat.h"
@@ -346,6 +347,9 @@ socreate(int dom, struct socket **aso, int type, int proto,
if (jailed(cred) && jail_socket_unixiproute_only &&
prp->pr_domain->dom_family != PF_LOCAL &&
prp->pr_domain->dom_family != PF_INET &&
+#ifdef INET6
+ prp->pr_domain->dom_family != PF_INET6 &&
+#endif
prp->pr_domain->dom_family != PF_ROUTE) {
return (EPROTONOSUPPORT);
}
diff --git a/sys/net/if.c b/sys/net/if.c
index 203a75f..9cc6f83 100644
--- a/sys/net/if.c
+++ b/sys/net/if.c
@@ -2211,7 +2211,7 @@ again:
struct sockaddr *sa = ifa->ifa_addr;
if (jailed(curthread->td_ucred) &&
- prison_if(curthread->td_ucred, sa))
+ !prison_if(curthread->td_ucred, sa))
continue;
addrs++;
#ifdef COMPAT_43
diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c
index ae6fa6a..15c6928 100644
--- a/sys/net/rtsock.c
+++ b/sys/net/rtsock.c
@@ -31,11 +31,13 @@
*/
#include "opt_sctp.h"
#include "opt_mpath.h"
+#include "opt_inet.h"
+#include "opt_inet6.h"
#include <sys/param.h>
#include <sys/domain.h>
-#include <sys/kernel.h>
#include <sys/jail.h>
+#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/priv.h>
@@ -54,6 +56,9 @@
#include <net/route.h>
#include <netinet/in.h>
+#ifdef INET6
+#include <netinet6/scope6_var.h>
+#endif
#ifdef SCTP
extern void sctp_addr_change(struct ifaddr *ifa, int cmd);
@@ -309,6 +314,136 @@ static struct pr_usrreqs route_usrreqs = {
.pru_close = rts_close,
};
+#ifndef _SOCKADDR_UNION_DEFINED
+#define _SOCKADDR_UNION_DEFINED
+/*
+ * The union of all possible address formats we handle.
+ */
+union sockaddr_union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+};
+#endif /* _SOCKADDR_UNION_DEFINED */
+
+static int
+rtm_get_jailed(struct rt_addrinfo *info, struct ifnet *ifp,
+ struct rtentry *rt, union sockaddr_union *saun, struct ucred *cred)
+{
+
+ switch (info->rti_info[RTAX_DST]->sa_family) {
+#ifdef INET
+ case AF_INET:
+ {
+ struct in_addr ia;
+
+ /*
+ * 1. Check if the returned address is part of the jail.
+ */
+ ia = ((struct sockaddr_in *)rt->rt_ifa->ifa_addr)->sin_addr;
+ if (prison_check_ip4(cred, &ia) != 0) {
+ info->rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr;
+
+ } else {
+ struct ifaddr *ifa;
+ int found;
+
+ found = 0;
+
+ /*
+ * 2. Try to find an address on the given outgoing
+ * interface that belongs to the jail.
+ */
+ TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
+ struct sockaddr *sa;
+ sa = ifa->ifa_addr;
+ if (sa->sa_family != AF_INET)
+ continue;
+ ia = ((struct sockaddr_in *)sa)->sin_addr;
+ if (prison_check_ip4(cred, &ia) != 0) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ /*
+ * 3. As a last resort return the 'default'
+ * jail address.
+ */
+ if (prison_getip4(cred, &ia) != 0)
+ return (ESRCH);
+ }
+ bzero(&saun->sin, sizeof(struct sockaddr_in));
+ saun->sin.sin_len = sizeof(struct sockaddr_in);
+ saun->sin.sin_family = AF_INET;
+ saun->sin.sin_addr.s_addr = ia.s_addr;
+ info->rti_info[RTAX_IFA] =
+ (struct sockaddr *)&saun->sin;
+ }
+ break;
+ }
+#endif
+#ifdef INET6
+ case AF_INET6:
+ {
+ struct in6_addr ia6;
+
+ /*
+ * 1. Check if the returned address is part of the jail.
+ */
+ bcopy(&((struct sockaddr_in6 *)rt->rt_ifa->ifa_addr)->sin6_addr,
+ &ia6, sizeof(struct in6_addr));
+ if (prison_check_ip6(cred, &ia6) != 0) {
+ info->rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr;
+ } else {
+ struct ifaddr *ifa;
+ int found;
+
+ found = 0;
+
+ /*
+ * 2. Try to find an address on the given outgoing
+ * interface that belongs to the jail.
+ */
+ TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
+ struct sockaddr *sa;
+ sa = ifa->ifa_addr;
+ if (sa->sa_family != AF_INET6)
+ continue;
+ bcopy(&((struct sockaddr_in6 *)sa)->sin6_addr,
+ &ia6, sizeof(struct in6_addr));
+ if (prison_check_ip6(cred, &ia6) != 0) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ /*
+ * 3. As a last resort return the 'default'
+ * jail address.
+ */
+ if (prison_getip6(cred, &ia6) != 0)
+ return (ESRCH);
+ }
+ bzero(&saun->sin6, sizeof(struct sockaddr_in6));
+ saun->sin6.sin6_len = sizeof(struct sockaddr_in6);
+ saun->sin6.sin6_family = AF_INET6;
+ bcopy(&ia6, &saun->sin6.sin6_addr,
+ sizeof(struct in6_addr));
+ if (sa6_recoverscope(&saun->sin6) != 0)
+ return (ESRCH);
+ info->rti_info[RTAX_IFA] =
+ (struct sockaddr *)&saun->sin6;
+ }
+ break;
+ }
+#endif
+ default:
+ return (ESRCH);
+ }
+ return (0);
+}
+
/*ARGSUSED*/
static int
route_output(struct mbuf *m, struct socket *so)
@@ -321,7 +456,7 @@ route_output(struct mbuf *m, struct socket *so)
struct rt_addrinfo info;
int len, error = 0;
struct ifnet *ifp = NULL;
- struct sockaddr_in jail;
+ union sockaddr_union saun;
#define senderr(e) { error = e; goto flush;}
if (m == NULL || ((m->m_len < sizeof(long)) &&
@@ -481,16 +616,17 @@ route_output(struct mbuf *m, struct socket *so)
info.rti_info[RTAX_IFP] =
ifp->if_addr->ifa_addr;
if (jailed(so->so_cred)) {
- bzero(&jail, sizeof(jail));
- jail.sin_family = PF_INET;
- jail.sin_len = sizeof(jail);
- jail.sin_addr.s_addr =
- htonl(prison_getip(so->so_cred));
- info.rti_info[RTAX_IFA] =
- (struct sockaddr *)&jail;
- } else
+ error = rtm_get_jailed(
+ &info, ifp, rt, &saun,
+ so->so_cred);
+ if (error != 0) {
+ RT_UNLOCK(rt);
+ senderr(ESRCH);
+ }
+ } else {
info.rti_info[RTAX_IFA] =
rt->rt_ifa->ifa_addr;
+ }
if (ifp->if_flags & IFF_POINTOPOINT)
info.rti_info[RTAX_BRD] =
rt->rt_ifa->ifa_dstaddr;
@@ -1171,7 +1307,7 @@ sysctl_iflist(int af, struct walkarg *w)
if (af && af != ifa->ifa_addr->sa_family)
continue;
if (jailed(curthread->td_ucred) &&
- prison_if(curthread->td_ucred, ifa->ifa_addr))
+ !prison_if(curthread->td_ucred, ifa->ifa_addr))
continue;
info.rti_info[RTAX_IFA] = ifa->ifa_addr;
info.rti_info[RTAX_NETMASK] = ifa->ifa_netmask;
@@ -1220,7 +1356,7 @@ sysctl_ifmalist(int af, struct walkarg *w)
if (af && af != ifma->ifma_addr->sa_family)
continue;
if (jailed(curproc->p_ucred) &&
- prison_if(curproc->p_ucred, ifma->ifma_addr))
+ !prison_if(curproc->p_ucred, ifma->ifma_addr))
continue;
info.rti_info[RTAX_IFA] = ifma->ifma_addr;
info.rti_info[RTAX_GATEWAY] =
diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c
index 9ca1edc..bb2eae0 100644
--- a/sys/netinet/in_pcb.c
+++ b/sys/netinet/in_pcb.c
@@ -294,7 +294,7 @@ in_pcbbind_setup(struct inpcb *inp, struct sockaddr *nam, in_addr_t *laddrp,
struct in_addr laddr;
u_short lport = 0;
int wild = 0, reuseport = (so->so_options & SO_REUSEPORT);
- int error, prison = 0;
+ int error;
int dorandom;
/*
@@ -323,9 +323,8 @@ in_pcbbind_setup(struct inpcb *inp, struct sockaddr *nam, in_addr_t *laddrp,
if (sin->sin_family != AF_INET)
return (EAFNOSUPPORT);
#endif
- if (sin->sin_addr.s_addr != INADDR_ANY)
- if (prison_ip(cred, 0, &sin->sin_addr.s_addr))
- return(EINVAL);
+ if (prison_local_ip4(cred, &sin->sin_addr))
+ return (EINVAL);
if (sin->sin_port != *lportp) {
/* Don't allow the port to change. */
if (*lportp != 0)
@@ -360,14 +359,11 @@ in_pcbbind_setup(struct inpcb *inp, struct sockaddr *nam, in_addr_t *laddrp,
priv_check_cred(cred, PRIV_NETINET_RESERVEDPORT,
0))
return (EACCES);
- if (jailed(cred))
- prison = 1;
if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) &&
priv_check_cred(inp->inp_cred,
PRIV_NETINET_REUSEPORT, 0) != 0) {
t = in_pcblookup_local(pcbinfo, sin->sin_addr,
- lport, prison ? 0 : INPLOOKUP_WILDCARD,
- cred);
+ lport, INPLOOKUP_WILDCARD, cred);
/*
* XXX
* This entire block sorely needs a rewrite.
@@ -384,10 +380,10 @@ in_pcbbind_setup(struct inpcb *inp, struct sockaddr *nam, in_addr_t *laddrp,
t->inp_cred->cr_uid))
return (EADDRINUSE);
}
- if (prison && prison_ip(cred, 0, &sin->sin_addr.s_addr))
+ if (prison_local_ip4(cred, &sin->sin_addr))
return (EADDRNOTAVAIL);
t = in_pcblookup_local(pcbinfo, sin->sin_addr,
- lport, prison ? 0 : wild, cred);
+ lport, wild, cred);
if (t && (t->inp_vflag & INP_TIMEWAIT)) {
/*
* XXXRW: If an incpb has had its timewait
@@ -419,9 +415,8 @@ in_pcbbind_setup(struct inpcb *inp, struct sockaddr *nam, in_addr_t *laddrp,
u_short first, last, aux;
int count;
- if (laddr.s_addr != INADDR_ANY)
- if (prison_ip(cred, 0, &laddr.s_addr))
- return (EINVAL);
+ if (prison_local_ip4(cred, &laddr))
+ return (EINVAL);
if (inp->inp_flags & INP_HIGHPORT) {
first = V_ipport_hifirstauto; /* sysctl */
@@ -487,7 +482,7 @@ in_pcbbind_setup(struct inpcb *inp, struct sockaddr *nam, in_addr_t *laddrp,
} while (in_pcblookup_local(pcbinfo, laddr,
lport, wild, cred));
}
- if (prison_ip(cred, 0, &laddr.s_addr))
+ if (prison_local_ip4(cred, &laddr))
return (EINVAL);
*laddrp = laddr.s_addr;
*lportp = lport;
@@ -556,7 +551,7 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr,
struct route sro;
int error;
- KASSERT(laddr != NULL, ("%s: null laddr", __func__));
+ KASSERT(laddr != NULL, ("%s: laddr NULL", __func__));
error = 0;
ia = NULL;
@@ -608,7 +603,7 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr,
if (sa->sa_family != AF_INET)
continue;
sin = (struct sockaddr_in *)sa;
- if (htonl(prison_getip(cred)) == sin->sin_addr.s_addr) {
+ if (prison_check_ip4(cred, &sin->sin_addr)) {
ia = (struct in_ifaddr *)ifa;
break;
}
@@ -619,7 +614,8 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr,
}
/* 3. As a last resort return the 'default' jail address. */
- laddr->s_addr = htonl(prison_getip(cred));
+ if (prison_getip4(cred, laddr) != 0)
+ error = EADDRNOTAVAIL;
goto done;
}
@@ -644,7 +640,7 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr,
/* Jailed. */
/* 1. Check if the iface address belongs to the jail. */
sin = (struct sockaddr_in *)sro.ro_rt->rt_ifa->ifa_addr;
- if (htonl(prison_getip(cred)) == sin->sin_addr.s_addr) {
+ if (prison_check_ip4(cred, &sin->sin_addr)) {
ia = (struct in_ifaddr *)sro.ro_rt->rt_ifa;
laddr->s_addr = ia->ia_addr.sin_addr.s_addr;
goto done;
@@ -660,7 +656,7 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr,
if (sa->sa_family != AF_INET)
continue;
sin = (struct sockaddr_in *)sa;
- if (htonl(prison_getip(cred)) == sin->sin_addr.s_addr) {
+ if (prison_check_ip4(cred, &sin->sin_addr)) {
ia = (struct in_ifaddr *)ifa;
break;
}
@@ -671,7 +667,8 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr,
}
/* 3. As a last resort return the 'default' jail address. */
- laddr->s_addr = htonl(prison_getip(cred));
+ if (prison_getip4(cred, laddr) != 0)
+ error = EADDRNOTAVAIL;
goto done;
}
@@ -717,8 +714,7 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr,
if (sa->sa_family != AF_INET)
continue;
sin = (struct sockaddr_in *)sa;
- if (htonl(prison_getip(cred)) ==
- sin->sin_addr.s_addr) {
+ if (prison_check_ip4(cred, &sin->sin_addr)) {
ia = (struct in_ifaddr *)ifa;
break;
}
@@ -730,7 +726,8 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr,
}
/* 3. As a last resort return the 'default' jail address. */
- laddr->s_addr = htonl(prison_getip(cred));
+ if (prison_getip4(cred, laddr) != 0)
+ error = EADDRNOTAVAIL;
goto done;
}
@@ -764,7 +761,7 @@ in_pcbconnect_setup(struct inpcb *inp, struct sockaddr *nam,
struct sockaddr_in *sin = (struct sockaddr_in *)nam;
struct in_ifaddr *ia;
struct inpcb *oinp;
- struct in_addr laddr, faddr;
+ struct in_addr laddr, faddr, jailia;
u_short lport, fport;
int error;
@@ -796,9 +793,17 @@ in_pcbconnect_setup(struct inpcb *inp, struct sockaddr *nam,
* and the primary interface supports broadcast,
* choose the broadcast address for that interface.
*/
- if (faddr.s_addr == INADDR_ANY)
- faddr = IA_SIN(TAILQ_FIRST(&V_in_ifaddrhead))->sin_addr;
- else if (faddr.s_addr == (u_long)INADDR_BROADCAST &&
+ if (faddr.s_addr == INADDR_ANY) {
+ if (cred != NULL && jailed(cred)) {
+ if (prison_getip4(cred, &jailia) != 0)
+ return (EADDRNOTAVAIL);
+ faddr.s_addr = jailia.s_addr;
+ } else {
+ faddr =
+ IA_SIN(TAILQ_FIRST(&V_in_ifaddrhead))->
+ sin_addr;
+ }
+ } else if (faddr.s_addr == (u_long)INADDR_BROADCAST &&
(TAILQ_FIRST(&V_in_ifaddrhead)->ia_ifp->if_flags &
IFF_BROADCAST))
faddr = satosin(&TAILQ_FIRST(
@@ -1115,6 +1120,7 @@ in_pcblookup_local(struct inpcbinfo *pcbinfo, struct in_addr laddr,
0, pcbinfo->ipi_hashmask)];
LIST_FOREACH(inp, head, inp_hash) {
#ifdef INET6
+ /* XXX inp locking */
if ((inp->inp_vflag & INP_IPV4) == 0)
continue;
#endif
@@ -1122,9 +1128,11 @@ in_pcblookup_local(struct inpcbinfo *pcbinfo, struct in_addr laddr,
inp->inp_laddr.s_addr == laddr.s_addr &&
inp->inp_lport == lport) {
/*
- * Found.
+ * Found?
*/
- return (inp);
+ if (cred == NULL ||
+ inp->inp_cred->cr_prison == cred->cr_prison)
+ return (inp);
}
}
/*
@@ -1154,7 +1162,11 @@ in_pcblookup_local(struct inpcbinfo *pcbinfo, struct in_addr laddr,
*/
LIST_FOREACH(inp, &phd->phd_pcblist, inp_portlist) {
wildcard = 0;
+ if (cred != NULL &&
+ inp->inp_cred->cr_prison != cred->cr_prison)
+ continue;
#ifdef INET6
+ /* XXX inp locking */
if ((inp->inp_vflag & INP_IPV4) == 0)
continue;
/*
@@ -1187,9 +1199,8 @@ in_pcblookup_local(struct inpcbinfo *pcbinfo, struct in_addr laddr,
if (wildcard < matchwild) {
match = inp;
matchwild = wildcard;
- if (matchwild == 0) {
+ if (matchwild == 0)
break;
- }
}
}
}
@@ -1207,7 +1218,7 @@ in_pcblookup_hash(struct inpcbinfo *pcbinfo, struct in_addr faddr,
struct ifnet *ifp)
{
struct inpcbhead *head;
- struct inpcb *inp;
+ struct inpcb *inp, *tmpinp;
u_short fport = fport_arg, lport = lport_arg;
INP_INFO_LOCK_ASSERT(pcbinfo);
@@ -1215,59 +1226,108 @@ in_pcblookup_hash(struct inpcbinfo *pcbinfo, struct in_addr faddr,
/*
* First look for an exact match.
*/
+ tmpinp = NULL;
head = &pcbinfo->ipi_hashbase[INP_PCBHASH(faddr.s_addr, lport, fport,
pcbinfo->ipi_hashmask)];
LIST_FOREACH(inp, head, inp_hash) {
#ifdef INET6
+ /* XXX inp locking */
if ((inp->inp_vflag & INP_IPV4) == 0)
continue;
#endif
if (inp->inp_faddr.s_addr == faddr.s_addr &&
inp->inp_laddr.s_addr == laddr.s_addr &&
inp->inp_fport == fport &&
- inp->inp_lport == lport)
- return (inp);
+ inp->inp_lport == lport) {
+ /*
+ * XXX We should be able to directly return
+ * the inp here, without any checks.
+ * Well unless both bound with SO_REUSEPORT?
+ */
+ if (jailed(inp->inp_cred))
+ return (inp);
+ if (tmpinp == NULL)
+ tmpinp = inp;
+ }
}
+ if (tmpinp != NULL)
+ return (tmpinp);
/*
* Then look for a wildcard match, if requested.
*/
- if (wildcard) {
- struct inpcb *local_wild = NULL;
+ if (wildcard == INPLOOKUP_WILDCARD) {
+ struct inpcb *local_wild = NULL, *local_exact = NULL;
#ifdef INET6
struct inpcb *local_wild_mapped = NULL;
#endif
+ struct inpcb *jail_wild = NULL;
+ int injail;
+
+ /*
+ * Order of socket selection - we always prefer jails.
+ * 1. jailed, non-wild.
+ * 2. jailed, wild.
+ * 3. non-jailed, non-wild.
+ * 4. non-jailed, wild.
+ */
head = &pcbinfo->ipi_hashbase[INP_PCBHASH(INADDR_ANY, lport,
0, pcbinfo->ipi_hashmask)];
LIST_FOREACH(inp, head, inp_hash) {
#ifdef INET6
+ /* XXX inp locking */
if ((inp->inp_vflag & INP_IPV4) == 0)
continue;
#endif
- if (inp->inp_faddr.s_addr == INADDR_ANY &&
- inp->inp_lport == lport) {
- if (ifp && ifp->if_type == IFT_FAITH &&
- (inp->inp_flags & INP_FAITH) == 0)
+ if (inp->inp_faddr.s_addr != INADDR_ANY ||
+ inp->inp_lport != lport)
+ continue;
+
+ /* XXX inp locking */
+ if (ifp && ifp->if_type == IFT_FAITH &&
+ (inp->inp_flags & INP_FAITH) == 0)
+ continue;
+
+ injail = jailed(inp->inp_cred);
+ if (injail) {
+ if (!prison_check_ip4(inp->inp_cred, &laddr))
continue;
- if (inp->inp_laddr.s_addr == laddr.s_addr)
+ } else {
+ if (local_exact != NULL)
+ continue;
+ }
+
+ if (inp->inp_laddr.s_addr == laddr.s_addr) {
+ if (injail)
return (inp);
- else if (inp->inp_laddr.s_addr == INADDR_ANY) {
+ else
+ local_exact = inp;
+ } else if (inp->inp_laddr.s_addr == INADDR_ANY) {
#ifdef INET6
- if (inp->inp_vflag & INP_IPV6PROTO)
- local_wild_mapped = inp;
+ /* XXX inp locking, NULL check */
+ if (inp->inp_vflag & INP_IPV6PROTO)
+ local_wild_mapped = inp;
+ else
+#endif /* INET6 */
+ if (injail)
+ jail_wild = inp;
else
-#endif
local_wild = inp;
- }
}
- }
+ } /* LIST_FOREACH */
+ if (jail_wild != NULL)
+ return (jail_wild);
+ if (local_exact != NULL)
+ return (local_exact);
+ if (local_wild != NULL)
+ return (local_wild);
#ifdef INET6
- if (local_wild == NULL)
+ if (local_wild_mapped != NULL)
return (local_wild_mapped);
-#endif
- return (local_wild);
- }
+#endif /* defined(INET6) */
+ } /* if (wildcard == INPLOOKUP_WILDCARD) */
+
return (NULL);
}
diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c
index a84db95..71eaaa0 100644
--- a/sys/netinet/raw_ip.c
+++ b/sys/netinet/raw_ip.c
@@ -273,10 +273,9 @@ rip_input(struct mbuf *m, int off)
continue;
if (inp->inp_faddr.s_addr != ip->ip_src.s_addr)
continue;
- if (jailed(inp->inp_cred) &&
- (htonl(prison_getip(inp->inp_cred)) !=
- ip->ip_dst.s_addr)) {
- continue;
+ if (jailed(inp->inp_cred)) {
+ if (!prison_check_ip4(inp->inp_cred, &ip->ip_dst))
+ continue;
}
if (last) {
struct mbuf *n;
@@ -304,10 +303,9 @@ rip_input(struct mbuf *m, int off)
if (inp->inp_faddr.s_addr &&
inp->inp_faddr.s_addr != ip->ip_src.s_addr)
continue;
- if (jailed(inp->inp_cred) &&
- (htonl(prison_getip(inp->inp_cred)) !=
- ip->ip_dst.s_addr)) {
- continue;
+ if (jailed(inp->inp_cred)) {
+ if (!prison_check_ip4(inp->inp_cred, &ip->ip_dst))
+ continue;
}
if (last) {
struct mbuf *n;
@@ -369,11 +367,15 @@ rip_output(struct mbuf *m, struct socket *so, u_long dst)
ip->ip_off = 0;
ip->ip_p = inp->inp_ip_p;
ip->ip_len = m->m_pkthdr.len;
- if (jailed(inp->inp_cred))
- ip->ip_src.s_addr =
- htonl(prison_getip(inp->inp_cred));
- else
+ if (jailed(inp->inp_cred)) {
+ if (prison_getip4(inp->inp_cred, &ip->ip_src)) {
+ INP_RUNLOCK(inp);
+ m_freem(m);
+ return (EPERM);
+ }
+ } else {
ip->ip_src = inp->inp_laddr;
+ }
ip->ip_dst.s_addr = dst;
ip->ip_ttl = inp->inp_ip_ttl;
} else {
@@ -383,13 +385,10 @@ rip_output(struct mbuf *m, struct socket *so, u_long dst)
}
INP_RLOCK(inp);
ip = mtod(m, struct ip *);
- if (jailed(inp->inp_cred)) {
- if (ip->ip_src.s_addr !=
- htonl(prison_getip(inp->inp_cred))) {
- INP_RUNLOCK(inp);
- m_freem(m);
- return (EPERM);
- }
+ if (!prison_check_ip4(inp->inp_cred, &ip->ip_src)) {
+ INP_RUNLOCK(inp);
+ m_freem(m);
+ return (EPERM);
}
/*
@@ -805,13 +804,8 @@ rip_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
if (nam->sa_len != sizeof(*addr))
return (EINVAL);
- if (jailed(td->td_ucred)) {
- if (addr->sin_addr.s_addr == INADDR_ANY)
- addr->sin_addr.s_addr =
- htonl(prison_getip(td->td_ucred));
- if (htonl(prison_getip(td->td_ucred)) != addr->sin_addr.s_addr)
- return (EADDRNOTAVAIL);
- }
+ if (!prison_check_ip4(td->td_ucred, &addr->sin_addr))
+ return (EADDRNOTAVAIL);
if (TAILQ_EMPTY(&V_ifnet) ||
(addr->sin_family != AF_INET && addr->sin_family != AF_IMPLINK) ||
diff --git a/sys/netinet/sctp_pcb.c b/sys/netinet/sctp_pcb.c
index 3fa9eb5..4ce0f12 100644
--- a/sys/netinet/sctp_pcb.c
+++ b/sys/netinet/sctp_pcb.c
@@ -2553,7 +2553,6 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
struct inpcb *ip_inp;
int port_reuse_active = 0;
int bindall;
- int prison = 0;
uint16_t lport;
int error;
uint32_t vrf_id;
@@ -2580,9 +2579,6 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
if (p == NULL)
panic("null proc/thread");
#endif
- if (p && jailed(p->td_ucred)) {
- prison = 1;
- }
if (addr != NULL) {
switch (addr->sa_family) {
case AF_INET:
@@ -2600,18 +2596,13 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
}
sin = (struct sockaddr_in *)addr;
lport = sin->sin_port;
- if (prison) {
- /*
- * For INADDR_ANY and LOOPBACK the
- * prison_ip() call will transmute
- * the ip address to the proper
- * value (i.e. the IP address owned
- * by the jail).
- */
- if (prison_ip(p->td_ucred, 0, &sin->sin_addr.s_addr)) {
- SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EINVAL);
- return (EINVAL);
- }
+ /*
+ * For LOOPBACK the prison_local_ip4() call will transmute the ip address
+ * to the proper value.
+ */
+ if (p && prison_local_ip4(p->td_ucred, &sin->sin_addr) != 0) {
+ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EINVAL);
+ return (EINVAL);
}
if (sin->sin_addr.s_addr != INADDR_ANY) {
bindall = 0;
@@ -2634,12 +2625,16 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
return (EINVAL);
}
lport = sin6->sin6_port;
+
/*
- * Jail checks for IPv6 should go HERE! i.e.
- * add the prison_ip() equivilant in this
- * postion to transmute the addresses to the
- * proper one jailed.
+ * For LOOPBACK the prison_local_ip6() call will transmute the ipv6 address
+ * to the proper value.
*/
+ if (p && prison_local_ip6(p->td_ucred, &sin6->sin6_addr,
+ (SCTP_IPV6_V6ONLY(inp) != 0)) != 0) {
+ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EINVAL);
+ return (EINVAL);
+ }
if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
bindall = 0;
/* KAME hack: embed scopeid */
diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c
index 882aee7..058415e 100644
--- a/sys/netinet/sctp_usrreq.c
+++ b/sys/netinet/sctp_usrreq.c
@@ -507,11 +507,10 @@ sctp_attach(struct socket *so, int proto, struct thread *p)
struct inpcb *ip_inp;
int error;
uint32_t vrf_id = SCTP_DEFAULT_VRFID;
-
#ifdef IPSEC
uint32_t flags;
-
#endif
+
inp = (struct sctp_inpcb *)so->so_pcb;
if (inp != 0) {
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
@@ -3951,12 +3950,8 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize,
struct sctp_getaddresses *addrs;
size_t sz;
struct thread *td;
- int prison = 0;
td = (struct thread *)p;
- if (jailed(td->td_ucred)) {
- prison = 1;
- }
SCTP_CHECK_AND_CAST(addrs, optval, struct sctp_getaddresses,
optsize);
if (addrs->addr->sa_family == AF_INET) {
@@ -3966,10 +3961,12 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize,
error = EINVAL;
break;
}
- if (prison && prison_ip(td->td_ucred, 0, &(((struct sockaddr_in *)(addrs->addr))->sin_addr.s_addr))) {
+ if (td != NULL && prison_local_ip4(td->td_ucred, &(((struct sockaddr_in *)(addrs->addr))->sin_addr))) {
SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_USRREQ, EADDRNOTAVAIL);
error = EADDRNOTAVAIL;
+ break;
}
+#ifdef INET6
} else if (addrs->addr->sa_family == AF_INET6) {
sz = sizeof(struct sctp_getaddresses) - sizeof(struct sockaddr) + sizeof(struct sockaddr_in6);
if (optsize < sz) {
@@ -3977,7 +3974,16 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize,
error = EINVAL;
break;
}
- /* JAIL XXXX Add else here for V6 */
+ if (td != NULL && prison_local_ip6(td->td_ucred, &(((struct sockaddr_in6 *)(addrs->addr))->sin6_addr),
+ (SCTP_IPV6_V6ONLY(inp) != 0)) != 0) {
+ SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_USRREQ, EADDRNOTAVAIL);
+ error = EADDRNOTAVAIL;
+ break;
+ }
+#endif
+ } else {
+ error = EAFNOSUPPORT;
+ break;
}
sctp_bindx_add_address(so, inp, addrs->addr,
addrs->sget_assoc_id, vrf_id,
@@ -3989,12 +3995,9 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize,
struct sctp_getaddresses *addrs;
size_t sz;
struct thread *td;
- int prison = 0;
td = (struct thread *)p;
- if (jailed(td->td_ucred)) {
- prison = 1;
- }
+
SCTP_CHECK_AND_CAST(addrs, optval, struct sctp_getaddresses, optsize);
if (addrs->addr->sa_family == AF_INET) {
sz = sizeof(struct sctp_getaddresses) - sizeof(struct sockaddr) + sizeof(struct sockaddr_in);
@@ -4003,10 +4006,12 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize,
error = EINVAL;
break;
}
- if (prison && prison_ip(td->td_ucred, 0, &(((struct sockaddr_in *)(addrs->addr))->sin_addr.s_addr))) {
+ if (td != NULL && prison_local_ip4(td->td_ucred, &(((struct sockaddr_in *)(addrs->addr))->sin_addr))) {
SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_USRREQ, EADDRNOTAVAIL);
error = EADDRNOTAVAIL;
+ break;
}
+#ifdef INET6
} else if (addrs->addr->sa_family == AF_INET6) {
sz = sizeof(struct sctp_getaddresses) - sizeof(struct sockaddr) + sizeof(struct sockaddr_in6);
if (optsize < sz) {
@@ -4014,7 +4019,16 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize,
error = EINVAL;
break;
}
- /* JAIL XXXX Add else here for V6 */
+ if (td != NULL && prison_local_ip6(td->td_ucred, &(((struct sockaddr_in6 *)(addrs->addr))->sin6_addr),
+ (SCTP_IPV6_V6ONLY(inp) != 0)) != 0) {
+ SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_USRREQ, EADDRNOTAVAIL);
+ error = EADDRNOTAVAIL;
+ break;
+ }
+#endif
+ } else {
+ error = EAFNOSUPPORT;
+ break;
}
sctp_bindx_delete_address(so, inp, addrs->addr,
addrs->sget_assoc_id, vrf_id,
@@ -4106,13 +4120,34 @@ sctp_connect(struct socket *so, struct sockaddr *addr, struct thread *p)
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
return EINVAL;
}
- if ((addr->sa_family == AF_INET6) && (addr->sa_len != sizeof(struct sockaddr_in6))) {
- SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
- return (EINVAL);
- }
- if ((addr->sa_family == AF_INET) && (addr->sa_len != sizeof(struct sockaddr_in))) {
- SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
- return (EINVAL);
+#ifdef INET6
+ if (addr->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6p;
+ if (addr->sa_len != sizeof(struct sockaddr_in6)) {
+ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
+ return (EINVAL);
+ }
+ sin6p = (struct sockaddr_in6 *)addr;
+ if (p != NULL && prison_remote_ip6(p->td_ucred, &sin6p->sin6_addr) != 0) {
+ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
+ return (EINVAL);
+ }
+ } else
+#endif
+ if (addr->sa_family == AF_INET) {
+ struct sockaddr_in *sinp;
+ if (addr->sa_len != sizeof(struct sockaddr_in)) {
+ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
+ return (EINVAL);
+ }
+ sinp = (struct sockaddr_in *)addr;
+ if (p != NULL && prison_remote_ip4(p->td_ucred, &sinp->sin_addr) != 0) {
+ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
+ return (EINVAL);
+ }
+ } else {
+ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EAFNOSUPPORT);
+ return (EAFNOSUPPORT);
}
SCTP_INP_INCR_REF(inp);
SCTP_ASOC_CREATE_LOCK(inp);
diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c
index 2ee8aa4..6428b64 100644
--- a/sys/netinet/tcp_usrreq.c
+++ b/sys/netinet/tcp_usrreq.c
@@ -440,8 +440,8 @@ tcp_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
if (sinp->sin_family == AF_INET
&& IN_MULTICAST(ntohl(sinp->sin_addr.s_addr)))
return (EAFNOSUPPORT);
- if (jailed(td->td_ucred))
- prison_remote_ip(td->td_ucred, 0, &sinp->sin_addr.s_addr);
+ if (prison_remote_ip4(td->td_ucred, &sinp->sin_addr) != 0)
+ return (EINVAL);
TCPDEBUG0;
INP_INFO_WLOCK(&V_tcbinfo);
@@ -507,6 +507,10 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
in6_sin6_2_sin(&sin, sin6p);
inp->inp_vflag |= INP_IPV4;
inp->inp_vflag &= ~INP_IPV6;
+ if (prison_remote_ip4(td->td_ucred, &sin.sin_addr) != 0) {
+ error = EINVAL;
+ goto out;
+ }
if ((error = tcp_connect(tp, (struct sockaddr *)&sin, td)) != 0)
goto out;
error = tcp_output_connect(so, nam);
@@ -515,6 +519,10 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
inp->inp_vflag &= ~INP_IPV4;
inp->inp_vflag |= INP_IPV6;
inp->inp_inc.inc_isipv6 = 1;
+ if (prison_remote_ip6(td->td_ucred, &sin6p->sin6_addr) != 0) {
+ error = EINVAL;
+ goto out;
+ }
if ((error = tcp6_connect(tp, nam, td)) != 0)
goto out;
error = tcp_output_connect(so, nam);
diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c
index de341af..2e52309 100644
--- a/sys/netinet/udp_usrreq.c
+++ b/sys/netinet/udp_usrreq.c
@@ -940,9 +940,10 @@ udp_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr,
* Jail may rewrite the destination address, so let it do
* that before we use it.
*/
- if (jailed(td->td_ucred))
- prison_remote_ip(td->td_ucred, 0,
- &sin->sin_addr.s_addr);
+ if (prison_remote_ip4(td->td_ucred, &sin->sin_addr) != 0) {
+ error = EINVAL;
+ goto release;
+ }
/*
* If a local address or port hasn't yet been selected, or if
@@ -1194,8 +1195,11 @@ udp_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
return (EISCONN);
}
sin = (struct sockaddr_in *)nam;
- if (jailed(td->td_ucred))
- prison_remote_ip(td->td_ucred, 0, &sin->sin_addr.s_addr);
+ if (prison_remote_ip4(td->td_ucred, &sin->sin_addr) != 0) {
+ INP_WUNLOCK(inp);
+ INP_INFO_WUNLOCK(&udbinfo);
+ return (EAFNOSUPPORT);
+ }
error = in_pcbconnect(inp, nam, td->td_ucred);
if (error == 0)
soisconnected(so);
diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c
index c854891..6cf40ce 100644
--- a/sys/netinet6/in6_pcb.c
+++ b/sys/netinet6/in6_pcb.c
@@ -143,6 +143,10 @@ in6_pcbbind(register struct inpcb *inp, struct sockaddr *nam,
if ((error = sa6_embedscope(sin6, V_ip6_use_defzone)) != 0)
return(error);
+ if (prison_local_ip6(cred, &sin6->sin6_addr,
+ ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0)) != 0)
+ return (EINVAL);
+
lport = sin6->sin6_port;
if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
/*
@@ -217,6 +221,9 @@ in6_pcbbind(register struct inpcb *inp, struct sockaddr *nam,
return (EADDRINUSE);
}
}
+ if (prison_local_ip6(cred, &sin6->sin6_addr,
+ ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0)) != 0)
+ return (EADDRNOTAVAIL);
t = in6_pcblookup_local(pcbinfo, &sin6->sin6_addr,
lport, wild, cred);
if (t && (reuseport & ((t->inp_vflag & INP_TIMEWAIT) ?
@@ -249,6 +256,9 @@ in6_pcbbind(register struct inpcb *inp, struct sockaddr *nam,
}
inp->in6p_laddr = sin6->sin6_addr;
}
+ if (prison_local_ip6(cred, &inp->in6p_laddr,
+ ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0)) != 0)
+ return (EINVAL);
if (lport == 0) {
int e;
if ((e = in6_pcbsetport(&inp->in6p_laddr, inp, cred)) != 0)
@@ -308,6 +318,8 @@ in6_pcbladdr(register struct inpcb *inp, struct sockaddr *nam,
if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))
sin6->sin6_addr = in6addr_loopback;
}
+ if (prison_remote_ip6(inp->inp_cred, &sin6->sin6_addr) != 0)
+ return (EADDRNOTAVAIL);
/*
* XXX: in6_selectsrc might replace the bound local address
@@ -649,15 +661,16 @@ in6_pcblookup_local(struct inpcbinfo *pcbinfo, struct in6_addr *laddr,
head = &pcbinfo->ipi_hashbase[INP_PCBHASH(INADDR_ANY, lport,
0, pcbinfo->ipi_hashmask)];
LIST_FOREACH(inp, head, inp_hash) {
+ /* XXX inp locking */
if ((inp->inp_vflag & INP_IPV6) == 0)
continue;
if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) &&
IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, laddr) &&
inp->inp_lport == lport) {
- /*
- * Found.
- */
- return (inp);
+ /* Found. */
+ if (cred == NULL ||
+ inp->inp_cred->cr_prison == cred->cr_prison)
+ return (inp);
}
}
/*
@@ -687,6 +700,10 @@ in6_pcblookup_local(struct inpcbinfo *pcbinfo, struct in6_addr *laddr,
*/
LIST_FOREACH(inp, &phd->phd_pcblist, inp_portlist) {
wildcard = 0;
+ if (cred != NULL &&
+ inp->inp_cred->cr_prison != cred->cr_prison)
+ continue;
+ /* XXX inp locking */
if ((inp->inp_vflag & INP_IPV6) == 0)
continue;
if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr))
@@ -696,7 +713,7 @@ in6_pcblookup_local(struct inpcbinfo *pcbinfo, struct in6_addr *laddr,
if (IN6_IS_ADDR_UNSPECIFIED(laddr))
wildcard++;
else if (!IN6_ARE_ADDR_EQUAL(
- &inp->in6p_laddr, laddr))
+ &inp->in6p_laddr, laddr))
continue;
} else {
if (!IN6_IS_ADDR_UNSPECIFIED(laddr))
@@ -705,9 +722,8 @@ in6_pcblookup_local(struct inpcbinfo *pcbinfo, struct in6_addr *laddr,
if (wildcard < matchwild) {
match = inp;
matchwild = wildcard;
- if (matchwild == 0) {
+ if (matchwild == 0)
break;
- }
}
}
}
@@ -790,11 +806,11 @@ in6_rtchange(struct inpcb *inp, int errno)
*/
struct inpcb *
in6_pcblookup_hash(struct inpcbinfo *pcbinfo, struct in6_addr *faddr,
- u_int fport_arg, struct in6_addr *laddr, u_int lport_arg,
- int wildcard, struct ifnet *ifp)
+ u_int fport_arg, struct in6_addr *laddr, u_int lport_arg, int wildcard,
+ struct ifnet *ifp)
{
struct inpcbhead *head;
- register struct inpcb *inp;
+ struct inpcb *inp, *tmpinp;
u_short fport = fport_arg, lport = lport_arg;
int faith;
@@ -808,10 +824,12 @@ in6_pcblookup_hash(struct inpcbinfo *pcbinfo, struct in6_addr *faddr,
/*
* First look for an exact match.
*/
+ tmpinp = NULL;
head = &pcbinfo->ipi_hashbase[
INP_PCBHASH(faddr->s6_addr32[3] /* XXX */, lport, fport,
pcbinfo->ipi_hashmask)];
LIST_FOREACH(inp, head, inp_hash) {
+ /* XXX inp locking */
if ((inp->inp_vflag & INP_IPV6) == 0)
continue;
if (IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr, faddr) &&
@@ -819,32 +837,79 @@ in6_pcblookup_hash(struct inpcbinfo *pcbinfo, struct in6_addr *faddr,
inp->inp_fport == fport &&
inp->inp_lport == lport) {
/*
- * Found.
+ * XXX We should be able to directly return
+ * the inp here, without any checks.
+ * Well unless both bound with SO_REUSEPORT?
*/
- return (inp);
+ if (jailed(inp->inp_cred))
+ return (inp);
+ if (tmpinp == NULL)
+ tmpinp = inp;
}
}
- if (wildcard) {
- struct inpcb *local_wild = NULL;
+ if (tmpinp != NULL)
+ return (tmpinp);
+ /*
+ * Then look for a wildcard match, if requested.
+ */
+ if (wildcard == INPLOOKUP_WILDCARD) {
+ struct inpcb *local_wild = NULL, *local_exact = NULL;
+ struct inpcb *jail_wild = NULL;
+ int injail;
+
+ /*
+ * Order of socket selection - we always prefer jails.
+ * 1. jailed, non-wild.
+ * 2. jailed, wild.
+ * 3. non-jailed, non-wild.
+ * 4. non-jailed, wild.
+ */
head = &pcbinfo->ipi_hashbase[INP_PCBHASH(INADDR_ANY, lport,
0, pcbinfo->ipi_hashmask)];
LIST_FOREACH(inp, head, inp_hash) {
+ /* XXX inp locking */
if ((inp->inp_vflag & INP_IPV6) == 0)
continue;
- if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) &&
- inp->inp_lport == lport) {
- if (faith && (inp->inp_flags & INP_FAITH) == 0)
+
+ if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) ||
+ inp->inp_lport != lport) {
+ continue;
+ }
+
+ /* XXX inp locking */
+ if (faith && (inp->inp_flags & INP_FAITH) == 0)
+ continue;
+
+ injail = jailed(inp->inp_cred);
+ if (injail) {
+ if (!prison_check_ip6(inp->inp_cred, laddr))
continue;
- if (IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr,
- laddr))
+ } else {
+ if (local_exact != NULL)
+ continue;
+ }
+
+ if (IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, laddr)) {
+ if (injail)
return (inp);
- else if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr))
+ else
+ local_exact = inp;
+ } else if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) {
+ if (injail)
+ jail_wild = inp;
+ else
local_wild = inp;
}
- }
- return (local_wild);
- }
+ } /* LIST_FOREACH */
+
+ if (jail_wild != NULL)
+ return (jail_wild);
+ if (local_exact != NULL)
+ return (local_exact);
+ if (local_wild != NULL)
+ return (local_wild);
+ } /* if (wildcard == INPLOOKUP_WILDCARD) */
/*
* Not found.
diff --git a/sys/netinet6/in6_src.c b/sys/netinet6/in6_src.c
index 8fa27c6..481ad12 100644
--- a/sys/netinet6/in6_src.c
+++ b/sys/netinet6/in6_src.c
@@ -80,6 +80,7 @@ __FBSDID("$FreeBSD$");
#include <sys/sysctl.h>
#include <sys/errno.h>
#include <sys/time.h>
+#include <sys/jail.h>
#include <sys/kernel.h>
#include <sys/sx.h>
#include <sys/vimage.h>
@@ -235,6 +236,11 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
if (*errorp != 0)
return (NULL);
}
+ if (cred != NULL && prison_local_ip6(cred, &srcsock.sin6_addr,
+ (inp != NULL && (inp->inp_flags & IN6P_IPV6_V6ONLY) != 0)) != 0) {
+ *errorp = EADDRNOTAVAIL;
+ return (NULL);
+ }
ia6 = (struct in6_ifaddr *)ifa_ifwithaddr((struct sockaddr *)(&srcsock));
if (ia6 == NULL ||
@@ -252,6 +258,11 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
* Otherwise, if the socket has already bound the source, just use it.
*/
if (inp != NULL && !IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) {
+ if (cred != NULL && prison_local_ip6(cred, &inp->in6p_laddr,
+ ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0)) != 0) {
+ *errorp = EADDRNOTAVAIL;
+ return (NULL);
+ }
return (&inp->in6p_laddr);
}
@@ -303,6 +314,12 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
if (!V_ip6_use_deprecated && IFA6_IS_DEPRECATED(ia))
continue;
+ if (cred != NULL &&
+ prison_local_ip6(cred, &ia->ia_addr.sin6_addr,
+ (inp != NULL &&
+ (inp->inp_flags & IN6P_IPV6_V6ONLY) != 0)) != 0)
+ continue;
+
/* Rule 1: Prefer same address */
if (IN6_ARE_ADDR_EQUAL(&dst, &ia->ia_addr.sin6_addr)) {
ia_best = ia;
@@ -784,6 +801,10 @@ in6_pcbsetport(struct in6_addr *laddr, struct inpcb *inp, struct ucred *cred)
INP_INFO_WLOCK_ASSERT(pcbinfo);
INP_WLOCK_ASSERT(inp);
+ if (prison_local_ip6(cred, laddr,
+ ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0)) != 0)
+ return(EINVAL);
+
/* XXX: this is redundant when called from in6_pcbbind */
if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0)
wild = INPLOOKUP_WILDCARD;
diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c
index 940bd1e..4537f7d 100644
--- a/sys/netinet6/raw_ip6.c
+++ b/sys/netinet6/raw_ip6.c
@@ -67,6 +67,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/errno.h>
+#include <sys/jail.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
@@ -162,6 +163,7 @@ rip6_input(struct mbuf **mp, int *offp, int proto)
INP_INFO_RLOCK(&V_ripcbinfo);
LIST_FOREACH(in6p, &V_ripcb, inp_list) {
+ /* XXX inp locking */
if ((in6p->in6p_vflag & INP_IPV6) == 0)
continue;
if (in6p->in6p_ip6_nxt &&
@@ -173,6 +175,10 @@ rip6_input(struct mbuf **mp, int *offp, int proto)
if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) &&
!IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src))
continue;
+ if (jailed(in6p->inp_cred)) {
+ if (!prison_check_ip6(in6p->inp_cred, &ip6->ip6_dst))
+ continue;
+ }
INP_RLOCK(in6p);
if (in6p->in6p_cksum != -1) {
V_rip6stat.rip6s_isum++;
@@ -401,6 +407,11 @@ rip6_output(m, va_alist)
error = EADDRNOTAVAIL;
goto bad;
}
+ if (jailed(in6p->inp_cred))
+ if (prison_getip6(in6p->inp_cred, in6a) != 0) {
+ error = EPERM;
+ goto bad;
+ }
ip6->ip6_src = *in6a;
if (oifp && scope_ambiguous) {
@@ -663,6 +674,8 @@ rip6_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
if (nam->sa_len != sizeof(*addr))
return (EINVAL);
+ if (!prison_check_ip6(td->td_ucred, &addr->sin6_addr))
+ return (EADDRNOTAVAIL);
if (TAILQ_EMPTY(&V_ifnet) || addr->sin6_family != AF_INET6)
return (EADDRNOTAVAIL);
if ((error = sa6_embedscope(addr, V_ip6_use_defzone)) != 0)
diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c
index 19a5199..6d233dd 100644
--- a/sys/netinet6/udp6_usrreq.c
+++ b/sys/netinet6/udp6_usrreq.c
@@ -71,6 +71,7 @@ __FBSDID("$FreeBSD$");
#include "opt_mac.h"
#include <sys/param.h>
+#include <sys/jail.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/mbuf.h>
@@ -860,6 +861,12 @@ udp6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
goto out;
}
in6_sin6_2_sin(&sin, sin6_p);
+ if (td && jailed(td->td_ucred))
+ if (prison_remote_ip4(td->td_ucred,
+ &sin.sin_addr) != 0) {
+ error = EAFNOSUPPORT;
+ goto out;
+ }
error = in_pcbconnect(inp, (struct sockaddr *)&sin,
td->td_ucred);
if (error == 0) {
@@ -874,6 +881,13 @@ udp6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
error = EISCONN;
goto out;
}
+ if (td && jailed(td->td_ucred)) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam;
+ if (prison_remote_ip6(td->td_ucred, &sin6->sin6_addr) != 0) {
+ error = EAFNOSUPPORT;
+ goto out;
+ }
+ }
error = in6_pcbconnect(inp, nam, td->td_ucred);
if (error == 0) {
if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) {
diff --git a/sys/security/mac_bsdextended/mac_bsdextended.c b/sys/security/mac_bsdextended/mac_bsdextended.c
index 0f7d5f8..b30b214 100644
--- a/sys/security/mac_bsdextended/mac_bsdextended.c
+++ b/sys/security/mac_bsdextended/mac_bsdextended.c
@@ -57,6 +57,7 @@
#include <sys/mount.h>
#include <sys/mutex.h>
#include <sys/priv.h>
+#include <sys/proc.h>
#include <sys/systm.h>
#include <sys/vnode.h>
#include <sys/sysctl.h>
diff --git a/sys/sys/cpuset.h b/sys/sys/cpuset.h
index c4bda75..326160d 100644
--- a/sys/sys/cpuset.h
+++ b/sys/sys/cpuset.h
@@ -132,6 +132,7 @@ typedef struct _cpuset {
#define CPU_WHICH_PID 2 /* Specifies a process id. */
#define CPU_WHICH_CPUSET 3 /* Specifies a set id. */
#define CPU_WHICH_IRQ 4 /* Specifies an irq #. */
+#define CPU_WHICH_JAIL 5 /* Specifies a jail id. */
/*
* Reserved cpuset identifiers.
@@ -168,11 +169,15 @@ struct cpuset {
#define CPU_SET_RDONLY 0x0002 /* No modification allowed. */
extern cpuset_t *cpuset_root;
+struct proc;
+struct thread;
struct cpuset *cpuset_thread0(void);
struct cpuset *cpuset_ref(struct cpuset *);
void cpuset_rel(struct cpuset *);
int cpuset_setthread(lwpid_t id, cpuset_t *);
+int cpuset_create_root(struct thread *, struct cpuset **);
+int cpuset_setproc_update_set(struct proc *, struct cpuset *);
#else
__BEGIN_DECLS
diff --git a/sys/sys/jail.h b/sys/sys/jail.h
index 08bc840..5fab37a 100644
--- a/sys/sys/jail.h
+++ b/sys/sys/jail.h
@@ -13,21 +13,74 @@
#ifndef _SYS_JAIL_H_
#define _SYS_JAIL_H_
-struct jail {
+#ifdef _KERNEL
+struct jail_v0 {
u_int32_t version;
char *path;
char *hostname;
u_int32_t ip_number;
};
+#endif
+
+struct jail {
+ uint32_t version;
+ char *path;
+ char *hostname;
+ char *jailname;
+ uint32_t ip4s;
+ uint32_t ip6s;
+ struct in_addr *ip4;
+ struct in6_addr *ip6;
+};
+#define JAIL_API_VERSION 2
+
+/*
+ * For all xprison structs, always keep the pr_version an int and
+ * the first variable so userspace can easily distinguish them.
+ */
+#ifndef _KERNEL
+struct xprison_v1 {
+ int pr_version;
+ int pr_id;
+ char pr_path[MAXPATHLEN];
+ char pr_host[MAXHOSTNAMELEN];
+ u_int32_t pr_ip;
+};
+#endif
struct xprison {
int pr_version;
int pr_id;
+ int pr_state;
+ cpusetid_t pr_cpusetid;
char pr_path[MAXPATHLEN];
char pr_host[MAXHOSTNAMELEN];
- u_int32_t pr_ip;
+ char pr_name[MAXHOSTNAMELEN];
+ uint32_t pr_ip4s;
+ uint32_t pr_ip6s;
+#if 0
+ /*
+ * sizeof(xprison) will be malloced + size needed for all
+ * IPv4 and IPv6 addesses. Offsets are based numbers of addresses.
+ */
+ struct in_addr pr_ip4[];
+ struct in6_addr pr_ip6[];
+#endif
+};
+#define XPRISON_VERSION 3
+
+static const struct prison_state {
+ int pr_state;
+ const char * state_name;
+} prison_states[] = {
+#define PRISON_STATE_INVALID 0
+ { PRISON_STATE_INVALID, "INVALID" },
+#define PRISON_STATE_ALIVE 1
+ { PRISON_STATE_ALIVE, "ALIVE" },
+#define PRISON_STATE_DYING 2
+ { PRISON_STATE_DYING, "DYING" },
};
-#define XPRISON_VERSION 1
+
#ifndef _KERNEL
@@ -52,6 +105,8 @@ MALLOC_DECLARE(M_PRISON);
#include <sys/osd.h>
+struct cpuset;
+
/*
* This structure describes a prison. It is pointed to by all struct
* ucreds's of the inmates. pr_ref keeps track of them and is used to
@@ -68,15 +123,22 @@ struct prison {
LIST_ENTRY(prison) pr_list; /* (a) all prisons */
int pr_id; /* (c) prison id */
int pr_ref; /* (p) refcount */
+ int pr_state; /* (p) prison state */
+ int pr_nprocs; /* (p) process count */
char pr_path[MAXPATHLEN]; /* (c) chroot path */
+ struct cpuset *pr_cpuset; /* (p) cpuset */
struct vnode *pr_root; /* (c) vnode to rdir */
char pr_host[MAXHOSTNAMELEN]; /* (p) jail hostname */
- u_int32_t pr_ip; /* (c) ip addr host */
+ char pr_name[MAXHOSTNAMELEN]; /* (c) admin jail name */
void *pr_linux; /* (p) linux abi */
int pr_securelevel; /* (p) securelevel */
struct task pr_task; /* (d) destroy task */
struct mtx pr_mtx;
struct osd pr_osd; /* (p) additional data */
+ int pr_ip4s; /* (c) number of v4 IPs */
+ struct in_addr *pr_ip4; /* (c) v4 IPs of jail */
+ int pr_ip6s; /* (c) number of v6 IPs */
+ struct in6_addr *pr_ip6; /* (c) v6 IPs of jail */
};
#endif /* _KERNEL || _WANT_PRISON */
@@ -104,6 +166,8 @@ struct ucred;
struct mount;
struct sockaddr;
struct statfs;
+struct thread;
+int kern_jail(struct thread *, struct jail *);
int jailed(struct ucred *cred);
void getcredhostname(struct ucred *cred, char *, size_t);
int prison_check(struct ucred *cred1, struct ucred *cred2);
@@ -113,13 +177,22 @@ void prison_enforce_statfs(struct ucred *cred, struct mount *mp,
struct prison *prison_find(int prid);
void prison_free(struct prison *pr);
void prison_free_locked(struct prison *pr);
-u_int32_t prison_getip(struct ucred *cred);
void prison_hold(struct prison *pr);
void prison_hold_locked(struct prison *pr);
+void prison_proc_hold(struct prison *);
+void prison_proc_free(struct prison *);
+int prison_getip4(struct ucred *cred, struct in_addr *ia);
+int prison_local_ip4(struct ucred *cred, struct in_addr *ia);
+int prison_remote_ip4(struct ucred *cred, struct in_addr *ia);
+int prison_check_ip4(struct ucred *cred, struct in_addr *ia);
+#ifdef INET6
+int prison_getip6(struct ucred *, struct in6_addr *);
+int prison_local_ip6(struct ucred *, struct in6_addr *, int);
+int prison_remote_ip6(struct ucred *, struct in6_addr *);
+int prison_check_ip6(struct ucred *, struct in6_addr *);
+#endif
int prison_if(struct ucred *cred, struct sockaddr *sa);
-int prison_ip(struct ucred *cred, int flag, u_int32_t *ip);
int prison_priv_check(struct ucred *cred, int priv);
-void prison_remote_ip(struct ucred *cred, int flags, u_int32_t *ip);
/*
* Kernel jail services.
diff --git a/sys/sys/param.h b/sys/sys/param.h
index e8dd9b3..fdc16e8 100644
--- a/sys/sys/param.h
+++ b/sys/sys/param.h
@@ -57,7 +57,7 @@
* is created, otherwise 1.
*/
#undef __FreeBSD_version
-#define __FreeBSD_version 800055 /* Master, propagated to newvers */
+#define __FreeBSD_version 800056 /* Master, propagated to newvers */
#ifndef LOCORE
#include <sys/types.h>
OpenPOWER on IntegriCloud