summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
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