summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/libc/sys/cpuset_getaffinity.24
-rw-r--r--lib/libc/sys/jail.235
-rw-r--r--lib/libkvm/kvm_proc.c5
-rw-r--r--share/man/man4/ddb.411
-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
-rw-r--r--usr.bin/cpuset/cpuset.114
-rw-r--r--usr.bin/cpuset/cpuset.c22
-rw-r--r--usr.sbin/jail/Makefile6
-rw-r--r--usr.sbin/jail/jail.846
-rw-r--r--usr.sbin/jail/jail.c252
-rw-r--r--usr.sbin/jexec/Makefile2
-rw-r--r--usr.sbin/jexec/jexec.843
-rw-r--r--usr.sbin/jexec/jexec.c245
-rw-r--r--usr.sbin/jls/Makefile2
-rw-r--r--usr.sbin/jls/jls.834
-rw-r--r--usr.sbin/jls/jls.c202
39 files changed, 2421 insertions, 383 deletions
diff --git a/lib/libc/sys/cpuset_getaffinity.2 b/lib/libc/sys/cpuset_getaffinity.2
index 87250ec..b065ac3 100644
--- a/lib/libc/sys/cpuset_getaffinity.2
+++ b/lib/libc/sys/cpuset_getaffinity.2
@@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd March 29, 2008
+.Dd November 29, 2008
.Dt CPUSET 2
.Os
.Sh NAME
@@ -46,7 +46,7 @@
and
.Fn cpuset_setaffinity
allow the manipulation of sets of CPUs available to processes, threads,
-interrupts and other resources.
+interrupts, jails and other resources.
These functions may manipulate sets of CPUs that contain many processes
or per-object anonymous masks that effect only a single object.
.Pp
diff --git a/lib/libc/sys/jail.2 b/lib/libc/sys/jail.2
index 5c6b0b0..f7b3912 100644
--- a/lib/libc/sys/jail.2
+++ b/lib/libc/sys/jail.2
@@ -8,7 +8,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd August 3, 2008
+.Dd November 29, 2008
.Dt JAIL 2
.Os
.Sh NAME
@@ -32,15 +32,20 @@ The argument is a pointer to a structure describing the prison:
.Bd -literal -offset indent
struct jail {
u_int32_t version;
- char *path;
- char *hostname;
- u_int32_t ip_number;
+ char *path;
+ char *hostname;
+ char *jailname;
+ unsigned int ip4s;
+ unsigned int ip6s;
+ struct in_addr *ip4;
+ struct in6_addr *ip6;
};
.Ed
.Pp
.Dq Li version
defines the version of the API in use.
-It should be set to zero at this time.
+.Dv JAIL_API_VERSION
+is defined for the current version.
.Pp
The
.Dq Li path
@@ -54,8 +59,24 @@ This can be changed
from the inside of the prison.
.Pp
The
-.Dq Li ip_number
-can be set to the IP number assigned to the prison.
+.Dq Li jailname
+pointer is an optional name that can be assigned to the jail
+for example for managment purposes.
+.Pp
+The
+.Dq Li ip4s
+and
+.Dq Li ip6s
+give the numbers of IPv4 and IPv6 addresses that will be passed
+via their respective pointers.
+.Pp
+The
+.Dq Li ip4
+and
+.Dq Li ip6
+pointers can be set to an arrays of IPv4 and IPv6 addresses to be assigned to
+the prison, or NULL if none.
+IPv4 addresses must be in network byte order.
.Pp
The
.Fn jail_attach
diff --git a/lib/libkvm/kvm_proc.c b/lib/libkvm/kvm_proc.c
index a2265f9..7402b93 100644
--- a/lib/libkvm/kvm_proc.c
+++ b/lib/libkvm/kvm_proc.c
@@ -54,10 +54,11 @@ __FBSDID("$FreeBSD$");
#include <sys/_lock.h>
#include <sys/_mutex.h>
#include <sys/_task.h>
-#define _WANT_PRISON /* make jail.h give us 'struct prison' */
-#include <sys/jail.h>
+#include <sys/cpuset.h>
#include <sys/user.h>
#include <sys/proc.h>
+#define _WANT_PRISON /* make jail.h give us 'struct prison' */
+#include <sys/jail.h>
#include <sys/exec.h>
#include <sys/stat.h>
#include <sys/sysent.h>
diff --git a/share/man/man4/ddb.4 b/share/man/man4/ddb.4
index 07f05b6..f037ba2 100644
--- a/share/man/man4/ddb.4
+++ b/share/man/man4/ddb.4
@@ -60,7 +60,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd October 18, 2008
+.Dd November 29, 2008
.Dt DDB 4
.Os
.Sh NAME
@@ -686,6 +686,15 @@ Dump the interrupt statistics.
Show interrupt lines and their respective kernel threads.
.\"
.Pp
+.It Ic show Cm jails
+Show the list of
+.Xr jail 8
+instances.
+In addition to what
+.Xr jls 8
+shows, also list kernel internal details.
+.\"
+.Pp
.It Ic show Cm lapic
Show information from the local APIC registers for this CPU.
.\"
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>
diff --git a/usr.bin/cpuset/cpuset.1 b/usr.bin/cpuset/cpuset.1
index ba06e48..e8f5593 100644
--- a/usr.bin/cpuset/cpuset.1
+++ b/usr.bin/cpuset/cpuset.1
@@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd March 11, 2008
+.Dd November 29, 2008
.Dt CPUSET 1
.Os
.Sh NAME
@@ -43,10 +43,10 @@
.Nm
.Op Fl cr
.Op Fl l Ar cpu-list
-.Op Fl p Ar pid | Fl r Ar tid | Fl s Ar setid
+.Op Fl j Ar jailid | Fl p Ar pid | Fl r Ar tid | Fl s Ar setid | Fl x Ar irq
.Nm
.Op Fl cgir
-.Op Fl p Ar pid | Fl r Ar tid | Fl s Ar setid
+.Op Fl j Ar jailid | Fl p Ar pid | Fl r Ar tid | Fl s Ar setid | Fl x Ar irq
.Sh DESCRIPTION
The
.Nm
@@ -56,8 +56,8 @@ about processor binding, sets, and available processors in the system.
.Pp
.Nm
requires a target to modify or query.
-The target may be specified as a command, process id, thread id, or a
-cpuset id.
+The target may be specified as a command, process id, thread id, a
+cpuset id, an irq or a jail id.
Using
.Fl g
the target's set id or mask may be queried.
@@ -111,6 +111,8 @@ the id of the target.
When used with the
.Fl g
option print the id rather than the valid mask of the target.
+.It Fl j Ar jailid
+Specifies a jail id as the target of the operation.
.It Fl l Ar cpu-list
Specifies a list of CPUs to apply to a target.
Specification may include
@@ -124,6 +126,8 @@ The requested operation should reference the root set available via the
target specifier.
.It Fl t Ar tid
Specifies a thread id as the target of the operation.
+.It Fl x Ar irq
+Specifies an irq as the target of the operation.
.El
.Sh EXIT STATUS
.Ex -std
diff --git a/usr.bin/cpuset/cpuset.c b/usr.bin/cpuset/cpuset.c
index 5858a3d..35b13af 100644
--- a/usr.bin/cpuset/cpuset.c
+++ b/usr.bin/cpuset/cpuset.c
@@ -49,6 +49,7 @@ __FBSDID("$FreeBSD$");
int cflag;
int gflag;
int iflag;
+int jflag;
int lflag;
int pflag;
int rflag;
@@ -153,7 +154,7 @@ printset(cpuset_t *mask)
printf("\n");
}
-const char *whichnames[] = { NULL, "tid", "pid", "cpuset", "irq" };
+const char *whichnames[] = { NULL, "tid", "pid", "cpuset", "irq", "jail" };
const char *levelnames[] = { NULL, " root", " cpuset", "" };
static void
@@ -198,7 +199,7 @@ main(int argc, char *argv[])
level = CPU_LEVEL_WHICH;
which = CPU_WHICH_PID;
id = pid = tid = setid = -1;
- while ((ch = getopt(argc, argv, "cgil:p:rs:t:x:")) != -1) {
+ while ((ch = getopt(argc, argv, "cgij:l:p:rs:t:x:")) != -1) {
switch (ch) {
case 'c':
if (rflag)
@@ -212,6 +213,11 @@ main(int argc, char *argv[])
case 'i':
iflag = 1;
break;
+ case 'j':
+ jflag = 1;
+ which = CPU_WHICH_JAIL;
+ id = atoi(optarg);
+ break;
case 'l':
lflag = 1;
parselist(optarg, &mask);
@@ -252,7 +258,7 @@ main(int argc, char *argv[])
if (argc || lflag)
usage();
/* Only one identity specifier. */
- if (xflag + sflag + pflag + tflag > 1)
+ if (jflag + xflag + sflag + pflag + tflag > 1)
usage();
if (iflag)
printsetid();
@@ -266,7 +272,7 @@ main(int argc, char *argv[])
* The user wants to run a command with a set and possibly cpumask.
*/
if (argc) {
- if (pflag | rflag | tflag | xflag)
+ if (pflag | rflag | tflag | xflag | jflag)
usage();
if (sflag) {
if (cpuset_setid(CPU_WHICH_PID, -1, setid))
@@ -292,10 +298,10 @@ main(int argc, char *argv[])
if (!lflag && !sflag)
usage();
/* You can only set a mask on a thread. */
- if (tflag && (sflag | pflag | xflag))
+ if (tflag && (sflag | pflag | xflag | jflag))
usage();
/* You can only set a mask on an irq. */
- if (xflag && (pflag | sflag | tflag))
+ if (xflag && (jflag | pflag | sflag | tflag))
usage();
if (pflag && sflag) {
if (cpuset_setid(CPU_WHICH_PID, pid, setid))
@@ -325,8 +331,8 @@ usage(void)
fprintf(stderr,
" cpuset [-l cpu-list] [-s setid] -p pid\n");
fprintf(stderr,
- " cpuset [-cr] [-l cpu-list] [-p pid | -t tid | -s setid | -x irq]\n");
+ " cpuset [-cr] [-l cpu-list] [-j jailid | -p pid | -t tid | -s setid | -x irq]\n");
fprintf(stderr,
- " cpuset [-cgir] [-p pid | -t tid | -s setid | -x irq]\n");
+ " cpuset [-cgir] [-j jailid | -p pid | -t tid | -s setid | -x irq]\n");
exit(1);
}
diff --git a/usr.sbin/jail/Makefile b/usr.sbin/jail/Makefile
index 812cbc9..e92ced0 100644
--- a/usr.sbin/jail/Makefile
+++ b/usr.sbin/jail/Makefile
@@ -1,5 +1,7 @@
# $FreeBSD$
+.include <bsd.own.mk>
+
PROG= jail
MAN= jail.8
DPADD= ${LIBUTIL}
@@ -7,4 +9,8 @@ LDADD= -lutil
WARNS?= 6
+.if ${MK_INET6_SUPPORT} != "no"
+CFLAGS+= -DINET6
+.endif
+
.include <bsd.prog.mk>
diff --git a/usr.sbin/jail/jail.8 b/usr.sbin/jail/jail.8
index 147c210..95af6ec 100644
--- a/usr.sbin/jail/jail.8
+++ b/usr.sbin/jail/jail.8
@@ -33,7 +33,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd April 5, 2007
+.Dd November 29, 2008
.Dt JAIL 8
.Os
.Sh NAME
@@ -41,11 +41,12 @@
.Nd "imprison process and its descendants"
.Sh SYNOPSIS
.Nm
-.Op Fl i
+.Op Fl hi
+.Op Fl n Ar jailname
.Op Fl J Ar jid_file
.Op Fl s Ar securelevel
.Op Fl l u Ar username | Fl U Ar username
-.Ar path hostname ip-number command ...
+.Ar path hostname [ip[,..]] command ...
.Sh DESCRIPTION
The
.Nm
@@ -53,8 +54,26 @@ utility imprisons a process and all future descendants.
.Pp
The options are as follows:
.Bl -tag -width ".Fl u Ar username"
+.It Fl h
+Resolve
+.Va hostname
+and add all IP addresses returned by the resolver
+to the list of
+.Va ip-addresses
+for this prison.
+This may affect default address selection for outgoing IPv4 connections
+of prisons.
+The address first returned by the resolver for the IPv4 address family
+will be used as default.
+For IPv6 source address selection is done by a well defined algorithm.
.It Fl i
Output the jail identifier of the newly created jail.
+.It Fl n Ar jailname
+Assign and administrative name to the jail that can be used for management
+or auditing purposes.
+The system will
+.Sy not enforce
+the name to be unique.
.It Fl J Ar jid_file
Write a
.Ar jid_file
@@ -92,8 +111,14 @@ should run.
Directory which is to be the root of the prison.
.It Ar hostname
Hostname of the prison.
-.It Ar ip-number
-IP number assigned to the prison.
+.It Ar ip-addresses
+None, one or more IPv4 and IPv6 addresses assigned to the prison.
+The first address of each address family that was assigned to the jail will
+be used as the source address in case source address selection on unbound
+sockets cannot find a better match.
+It is only possible to start multiple jails with the same IP address,
+if none of the jails has more than this single overlapping IP address
+assigned to itself for the address family in question.
.It Ar command
Pathname of the program which is to be executed.
.El
@@ -179,7 +204,7 @@ is to disable IP services on the host system that listen on all local
IP addresses for a service.
If a network service is present in the host environment that binds all
available IP addresses rather than specific IP addresses, it may service
-requests sent to jail IP addresses.
+requests sent to jail IP addresses if the jail did not bind the port.
This means changing
.Xr inetd 8
to only listen on the
@@ -555,6 +580,9 @@ command can be used to find file system types available for mount from within
a jail.
This functionality is disabled by default, but can be enabled by setting this
MIB entry to 1.
+.It Va security.jail.jail_max_af_ips
+This MIB entry determines how may address per address family a prison
+may have. The default is 255.
.El
.Pp
The read-only sysctl variable
@@ -622,6 +650,12 @@ who contributed it to
.An Robert Watson
wrote the extended documentation, found a few bugs, added
a few new features, and cleaned up the userland jail environment.
+.Pp
+.An Bjoern A. Zeeb
+added multi-IP jail support for IPv4 and IPv6 based on a patch
+originally done by
+.An Pawel Jakub Dawidek
+for IPv4.
.Sh BUGS
Jail currently lacks the ability to allow access to
specific jail information via
diff --git a/usr.sbin/jail/jail.c b/usr.sbin/jail/jail.c
index e21fcc8..be337b7 100644
--- a/usr.sbin/jail/jail.c
+++ b/usr.sbin/jail/jail.c
@@ -12,10 +12,14 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/jail.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
#include <sys/sysctl.h>
+#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <netdb.h>
#include <err.h>
#include <errno.h>
@@ -25,12 +29,34 @@ __FBSDID("$FreeBSD$");
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
+#include <strings.h>
#include <string.h>
#include <unistd.h>
-static void usage(void);
+static void usage(void);
+static int add_addresses(struct addrinfo *);
+static struct in_addr *copy_addr4(void);
+#ifdef INET6
+static struct in6_addr *copy_addr6(void);
+#endif
+
extern char **environ;
+struct addr4entry {
+ STAILQ_ENTRY(addr4entry) addr4entries;
+ struct in_addr ip4;
+ int count;
+};
+struct addr6entry {
+ STAILQ_ENTRY(addr6entry) addr6entries;
+#ifdef INET6
+ struct in6_addr ip6;
+#endif
+ int count;
+};
+STAILQ_HEAD(addr4head, addr4entry) addr4 = STAILQ_HEAD_INITIALIZER(addr4);
+STAILQ_HEAD(addr6head, addr6entry) addr6 = STAILQ_HEAD_INITIALIZER(addr6);
+
#define GET_USER_INFO do { \
pwd = getpwnam(username); \
if (pwd == NULL) { \
@@ -53,22 +79,26 @@ main(int argc, char **argv)
login_cap_t *lcap = NULL;
struct jail j;
struct passwd *pwd = NULL;
- struct in_addr in;
gid_t groups[NGROUPS];
- int ch, i, iflag, Jflag, lflag, ngroups, securelevel, uflag, Uflag;
- char path[PATH_MAX], *ep, *username, *JidFile;
+ int ch, error, i, ngroups, securelevel;
+ int hflag, iflag, Jflag, lflag, uflag, Uflag;
+ char path[PATH_MAX], *jailname, *ep, *username, *JidFile, *ip;
static char *cleanenv;
const char *shell, *p = NULL;
long ltmp;
FILE *fp;
+ struct addrinfo hints, *res0;
- iflag = Jflag = lflag = uflag = Uflag = 0;
+ hflag = iflag = Jflag = lflag = uflag = Uflag = 0;
securelevel = -1;
- username = JidFile = cleanenv = NULL;
+ jailname = username = JidFile = cleanenv = NULL;
fp = NULL;
- while ((ch = getopt(argc, argv, "ils:u:U:J:")) != -1) {
+ while ((ch = getopt(argc, argv, "hiln:s:u:U:J:")) != -1) {
switch (ch) {
+ case 'h':
+ hflag = 1;
+ break;
case 'i':
iflag = 1;
break;
@@ -76,6 +106,9 @@ main(int argc, char **argv)
JidFile = optarg;
Jflag = 1;
break;
+ case 'n':
+ jailname = optarg;
+ break;
case 's':
ltmp = strtol(optarg, &ep, 0);
if (*ep || ep == optarg || ltmp > INT_MAX || !ltmp)
@@ -111,13 +144,62 @@ main(int argc, char **argv)
err(1, "realpath: %s", argv[0]);
if (chdir(path) != 0)
err(1, "chdir: %s", path);
+ /* Initialize struct jail. */
memset(&j, 0, sizeof(j));
- j.version = 0;
+ j.version = JAIL_API_VERSION;
j.path = path;
j.hostname = argv[1];
- if (inet_aton(argv[2], &in) == 0)
- errx(1, "Could not make sense of ip-number: %s", argv[2]);
- j.ip_number = ntohl(in.s_addr);
+ if (jailname != NULL)
+ j.jailname = jailname;
+
+ /* Handle IP addresses. If requested resolve hostname too. */
+ bzero(&hints, sizeof(struct addrinfo));
+ hints.ai_protocol = IPPROTO_TCP;
+ hints.ai_socktype = SOCK_STREAM;
+ if (JAIL_API_VERSION < 2)
+ hints.ai_family = PF_INET;
+ else
+ hints.ai_family = PF_UNSPEC;
+ /* Handle hostname. */
+ if (hflag != 0) {
+ error = getaddrinfo(j.hostname, NULL, &hints, &res0);
+ if (error != 0)
+ errx(1, "failed to handle hostname: %s",
+ gai_strerror(error));
+ error = add_addresses(res0);
+ freeaddrinfo(res0);
+ if (error != 0)
+ errx(1, "failed to add addresses.");
+ }
+ /* Handle IP addresses. */
+ hints.ai_flags = AI_NUMERICHOST;
+ ip = strtok(argv[2], ",");
+ while (ip != NULL) {
+ error = getaddrinfo(ip, NULL, &hints, &res0);
+ if (error != 0)
+ errx(1, "failed to handle ip: %s", gai_strerror(error));
+ error = add_addresses(res0);
+ freeaddrinfo(res0);
+ if (error != 0)
+ errx(1, "failed to add addresses.");
+ ip = strtok(NULL, ",");
+ }
+ /* Count IP addresses and add them to struct jail. */
+ if (!STAILQ_EMPTY(&addr4)) {
+ j.ip4s = STAILQ_FIRST(&addr4)->count;
+ j.ip4 = copy_addr4();
+ if (j.ip4s > 0 && j.ip4 == NULL)
+ errx(1, "copy_addr4()");
+ }
+#ifdef INET6
+ if (!STAILQ_EMPTY(&addr6)) {
+ j.ip6s = STAILQ_FIRST(&addr6)->count;
+ j.ip6 = copy_addr6();
+ if (j.ip6s > 0 && j.ip6 == NULL)
+ errx(1, "copy_addr6()");
+ }
+#endif
+
if (Jflag) {
fp = fopen(JidFile, "w");
if (fp == NULL)
@@ -125,7 +207,7 @@ main(int argc, char **argv)
}
i = jail(&j);
if (i == -1)
- err(1, "jail");
+ err(1, "syscall failed with");
if (iflag) {
printf("%d\n", i);
fflush(stdout);
@@ -183,8 +265,148 @@ usage(void)
{
(void)fprintf(stderr, "%s%s%s\n",
- "usage: jail [-i] [-J jid_file] [-s securelevel] [-l -u ",
- "username | -U username]",
- " path hostname ip-number command ...");
+ "usage: jail [-hi] [-n jailname] [-J jid_file] ",
+ "[-s securelevel] [-l -u username | -U username] ",
+ "path hostname [ip[,..]] command ...");
exit(1);
}
+
+static int
+add_addresses(struct addrinfo *res0)
+{
+ int error;
+ struct addrinfo *res;
+ struct addr4entry *a4p;
+ struct sockaddr_in *sai;
+#ifdef INET6
+ struct addr6entry *a6p;
+ struct sockaddr_in6 *sai6;
+#endif
+ int count;
+
+ error = 0;
+ for (res = res0; res && error == 0; res = res->ai_next) {
+ switch (res->ai_family) {
+ case AF_INET:
+ sai = (struct sockaddr_in *)(void *)res->ai_addr;
+ STAILQ_FOREACH(a4p, &addr4, addr4entries) {
+ if (bcmp(&sai->sin_addr, &a4p->ip4,
+ sizeof(struct in_addr)) == 0) {
+ err(1, "Ignoring duplicate IPv4 address.");
+ break;
+ }
+ }
+ a4p = (struct addr4entry *) malloc(
+ sizeof(struct addr4entry));
+ if (a4p == NULL) {
+ error = 1;
+ break;
+ }
+ bzero(a4p, sizeof(struct addr4entry));
+ bcopy(&sai->sin_addr, &a4p->ip4,
+ sizeof(struct in_addr));
+ if (!STAILQ_EMPTY(&addr4))
+ count = STAILQ_FIRST(&addr4)->count;
+ else
+ count = 0;
+ STAILQ_INSERT_TAIL(&addr4, a4p, addr4entries);
+ STAILQ_FIRST(&addr4)->count = count + 1;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ sai6 = (struct sockaddr_in6 *)(void *)res->ai_addr;
+ STAILQ_FOREACH(a6p, &addr6, addr6entries) {
+ if (bcmp(&sai6->sin6_addr, &a6p->ip6,
+ sizeof(struct in6_addr)) == 0) {
+ err(1, "Ignoring duplicate IPv6 address.");
+ break;
+ }
+ }
+ a6p = (struct addr6entry *) malloc(
+ sizeof(struct addr6entry));
+ if (a6p == NULL) {
+ error = 1;
+ break;
+ }
+ bzero(a6p, sizeof(struct addr6entry));
+ bcopy(&sai6->sin6_addr, &a6p->ip6,
+ sizeof(struct in6_addr));
+ if (!STAILQ_EMPTY(&addr6))
+ count = STAILQ_FIRST(&addr6)->count;
+ else
+ count = 0;
+ STAILQ_INSERT_TAIL(&addr6, a6p, addr6entries);
+ STAILQ_FIRST(&addr6)->count = count + 1;
+ break;
+#endif
+ default:
+ err(1, "Address family %d not supported. Ignoring.\n",
+ res->ai_family);
+ break;
+ }
+ }
+
+ return (error);
+}
+
+static struct in_addr *
+copy_addr4(void)
+{
+ size_t len;
+ struct in_addr *ip4s, *p, ia;
+ struct addr4entry *a4p;
+
+ if (STAILQ_EMPTY(&addr4))
+ return NULL;
+
+ len = STAILQ_FIRST(&addr4)->count * sizeof(struct in_addr);
+
+ ip4s = p = (struct in_addr *)malloc(len);
+ if (ip4s == NULL)
+ return (NULL);
+
+ bzero(p, len);
+
+ while (!STAILQ_EMPTY(&addr4)) {
+ a4p = STAILQ_FIRST(&addr4);
+ STAILQ_REMOVE_HEAD(&addr4, addr4entries);
+ ia.s_addr = a4p->ip4.s_addr;
+ bcopy(&ia, p, sizeof(struct in_addr));
+ p++;
+ free(a4p);
+ }
+
+ return (ip4s);
+}
+
+#ifdef INET6
+static struct in6_addr *
+copy_addr6(void)
+{
+ size_t len;
+ struct in6_addr *ip6s, *p;
+ struct addr6entry *a6p;
+
+ if (STAILQ_EMPTY(&addr6))
+ return NULL;
+
+ len = STAILQ_FIRST(&addr6)->count * sizeof(struct in6_addr);
+
+ ip6s = p = (struct in6_addr *)malloc(len);
+ if (ip6s == NULL)
+ return (NULL);
+
+ bzero(p, len);
+
+ while (!STAILQ_EMPTY(&addr6)) {
+ a6p = STAILQ_FIRST(&addr6);
+ STAILQ_REMOVE_HEAD(&addr6, addr6entries);
+ bcopy(&a6p->ip6, p, sizeof(struct in6_addr));
+ p++;
+ free(a6p);
+ }
+
+ return (ip6s);
+}
+#endif
+
diff --git a/usr.sbin/jexec/Makefile b/usr.sbin/jexec/Makefile
index 2bf817c..049ccd4 100644
--- a/usr.sbin/jexec/Makefile
+++ b/usr.sbin/jexec/Makefile
@@ -6,4 +6,6 @@ DPADD= ${LIBUTIL}
LDADD= -lutil
WARNS?= 6
+CFLAGS+= -DSUPPORT_OLD_XPRISON
+
.include <bsd.prog.mk>
diff --git a/usr.sbin/jexec/jexec.8 b/usr.sbin/jexec/jexec.8
index 40c4979..bdda23d 100644
--- a/usr.sbin/jexec/jexec.8
+++ b/usr.sbin/jexec/jexec.8
@@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd May 26, 2008
+.Dd November 29, 2008
.Dt JEXEC 8
.Os
.Sh NAME
@@ -34,22 +34,36 @@
.Sh SYNOPSIS
.Nm
.Op Fl u Ar username | Fl U Ar username
-.Op Fl h Ar hostname | Fl h Ar ip-number | Ar jid
-.Ar command ...
+.Op Fl n Ar jailname
+.Ar jid command ...
.Sh DESCRIPTION
The
.Nm
utility executes
.Ar command
-inside the jail identified by
-.Ar hostname
+inside the jail identified by either
+.Ar jailname
or
-.Ar ip-number
-or
-.Ar jid .
+.Ar jid
+or both.
+.Pp
+If the jail cannot be identified uniquely by the given parameters,
+an error message is printed.
+.Nm
+will also check the state of the jail (once supported) to be
+.Dv ALIVE
+and ignore jails in other states.
+The mandatory argument
+.Ar jid
+is the unique jail identifier as given by
+.Xr jls 8 .
+In case you only want to match on other criteria, give an empty string.
.Pp
The following options are available:
.Bl -tag -width indent
+.It Fl n Ar jailname
+The name of the jail, if given upon creation of the jail.
+This is not the hostname of the jail.
.It Fl u Ar username
The user name from host environment as whom the
.Ar command
@@ -59,9 +73,6 @@ The user name from jailed environment as whom the
.Ar command
should run.
.El
-.Sh "CAUTIONS"
-Only jid is guaranteed to uniquely identify a jail.
-Hostname or ip-number only work here if matched to one unique jail.
.Sh SEE ALSO
.Xr jail_attach 2 ,
.Xr jail 8 ,
@@ -71,3 +82,13 @@ The
.Nm
utility was added in
.Fx 5.1 .
+.Sh BUGS
+If the jail is not identified by
+.Ar jid
+there is a possible race in between the lookup of the jail
+and executing the command inside the jail.
+Giving a
+.Ar jid
+has a similar race as another process can stop the jail and
+start another one after the user looked up the
+.Ar jid .
diff --git a/usr.sbin/jexec/jexec.c b/usr.sbin/jexec/jexec.c
index 137eec3..69bc8f0 100644
--- a/usr.sbin/jexec/jexec.c
+++ b/usr.sbin/jexec/jexec.c
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 2003 Mike Barcroft <mike@FreeBSD.org>
+ * Copyright (c) 2008 Bjoern A. Zeeb <bz@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -30,20 +31,171 @@
#include <sys/jail.h>
#include <sys/sysctl.h>
-#include <arpa/inet.h>
+#include <netinet/in.h>
#include <err.h>
#include <errno.h>
-#include <limits.h>
#include <login_cap.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <pwd.h>
#include <unistd.h>
-#include <string.h>
static void usage(void);
-static int addr2jid(const char *addr);
+
+#ifdef SUPPORT_OLD_XPRISON
+static
+char *lookup_xprison_v1(void *p, char *end, int *id)
+{
+ struct xprison_v1 *xp;
+
+ if (id == NULL)
+ errx(1, "Internal error. Invalid ID pointer.");
+
+ if ((char *)p + sizeof(struct xprison_v1) > end)
+ errx(1, "Invalid length for jail");
+
+ xp = (struct xprison_v1 *)p;
+
+ *id = xp->pr_id;
+ return ((char *)(xp + 1));
+}
+#endif
+
+static
+char *lookup_xprison_v3(void *p, char *end, int *id, char *jailname)
+{
+ struct xprison *xp;
+ char *q;
+ int ok;
+
+ if (id == NULL)
+ errx(1, "Internal error. Invalid ID pointer.");
+
+ if ((char *)p + sizeof(struct xprison) > end)
+ errx(1, "Invalid length for jail");
+
+ xp = (struct xprison *)p;
+ ok = 1;
+
+ /* Jail state and name. */
+ if (xp->pr_state < 0 || xp->pr_state >
+ (int)((sizeof(prison_states) / sizeof(struct prison_state))))
+ errx(1, "Invalid jail state.");
+ else if (xp->pr_state != PRISON_STATE_ALIVE)
+ ok = 0;
+ if (jailname != NULL) {
+ if (xp->pr_name == NULL)
+ ok = 0;
+ else if (strcmp(jailname, xp->pr_name) != 0)
+ ok = 0;
+ }
+
+ q = (char *)(xp + 1);
+ /* IPv4 addresses. */
+ q += (xp->pr_ip4s * sizeof(struct in_addr));
+ if ((char *)q > end)
+ errx(1, "Invalid length for jail");
+ /* IPv6 addresses. */
+ q += (xp->pr_ip6s * sizeof(struct in6_addr));
+ if ((char *)q > end)
+ errx(1, "Invalid length for jail");
+
+ if (ok)
+ *id = xp->pr_id;
+ return (q);
+}
+
+static int
+lookup_jail(int jid, char *jailname)
+{
+ size_t i, j, len;
+ void *p, *q;
+ int version, id, xid, count;
+
+ if (sysctlbyname("security.jail.list", NULL, &len, NULL, 0) == -1)
+ err(1, "sysctlbyname(): security.jail.list");
+
+ j = len;
+ for (i = 0; i < 4; i++) {
+ if (len <= 0)
+ exit(0);
+ p = q = malloc(len);
+ if (p == NULL)
+ err(1, "malloc()");
+
+ if (sysctlbyname("security.jail.list", q, &len, NULL, 0) == -1) {
+ if (errno == ENOMEM) {
+ free(p);
+ p = NULL;
+ len += j;
+ continue;
+ }
+ err(1, "sysctlbyname(): security.jail.list");
+ }
+ break;
+ }
+ if (p == NULL)
+ err(1, "sysctlbyname(): security.jail.list");
+ if (len < sizeof(int))
+ errx(1, "This is no prison. Kernel and userland out of sync?");
+ version = *(int *)p;
+ if (version > XPRISON_VERSION)
+ errx(1, "Sci-Fi prison. Kernel/userland out of sync?");
+
+ count = 0;
+ xid = -1;
+ for (; q != NULL && (char *)q + sizeof(int) < (char *)p + len;) {
+ version = *(int *)q;
+ if (version > XPRISON_VERSION)
+ errx(1, "Sci-Fi prison. Kernel/userland out of sync?");
+ id = -1;
+ switch (version) {
+#ifdef SUPPORT_OLD_XPRISON
+ case 1:
+ if (jailname != NULL)
+ errx(1, "Version 1 prisons did not "
+ "support jail names.");
+ q = lookup_xprison_v1(q, (char *)p + len, &id);
+ break;
+ case 2:
+ errx(1, "Version 2 was used by multi-IPv4 jail "
+ "implementations that never made it into the "
+ "official kernel.");
+ /* NOTREACHED */
+ break;
+#endif
+ case 3:
+ q = lookup_xprison_v3(q, (char *)p + len, &id, jailname);
+ break;
+ default:
+ errx(1, "Prison unknown. Kernel/userland out of sync?");
+ /* NOTREACHED */
+ break;
+ }
+ /* Possible match. */
+ if (id > 0) {
+ /* Do we have a jail ID to match as well? */
+ if (jid > 0) {
+ if (jid == id) {
+ xid = id;
+ count++;
+ }
+ } else {
+ xid = id;
+ count++;
+ }
+ }
+ }
+
+ free(p);
+
+ if (count != 1)
+ errx(1, "Could not uniquely identify the jail.");
+
+ return (xid);
+}
#define GET_USER_INFO do { \
pwd = getpwnam(username); \
@@ -68,13 +220,18 @@ main(int argc, char *argv[])
login_cap_t *lcap = NULL;
struct passwd *pwd = NULL;
gid_t groups[NGROUPS];
- int ch, ngroups, uflag, Uflag, hflag;
- char *username;
- ch = uflag = Uflag = hflag = 0;
- username = NULL;
+ int ch, ngroups, uflag, Uflag;
+ char *jailname, *username;
+
+ ch = uflag = Uflag = 0;
+ jailname = username = NULL;
+ jid = -1;
- while ((ch = getopt(argc, argv, "u:U:h")) != -1) {
+ while ((ch = getopt(argc, argv, "i:n:u:U:")) != -1) {
switch (ch) {
+ case 'n':
+ jailname = optarg;
+ break;
case 'u':
username = optarg;
uflag = 1;
@@ -83,9 +240,6 @@ main(int argc, char *argv[])
username = optarg;
Uflag = 1;
break;
- case 'h':
- hflag = 1;
- break;
default:
usage();
}
@@ -94,15 +248,22 @@ main(int argc, char *argv[])
argv += optind;
if (argc < 2)
usage();
+ if (strlen(argv[0]) > 0) {
+ jid = (int)strtol(argv[0], NULL, 10);
+ if (errno)
+ err(1, "Unable to parse jail ID.");
+ }
+ if (jid <= 0 && jailname == NULL) {
+ fprintf(stderr, "Neither jail ID nor jail name given.\n");
+ usage();
+ }
if (uflag && Uflag)
usage();
if (uflag)
GET_USER_INFO;
- if (hflag) {
- if ((jid = addr2jid(argv[0])) == 0)
- errx(1, "jail_attach(): Cannot convert %s to jid", argv[0]);
- } else
- jid = (int)strtol(argv[0], NULL, 10);
+ jid = lookup_jail(jid, jailname);
+ if (jid <= 0)
+ errx(1, "Cannot identify jail.");
if (jail_attach(jid) == -1)
err(1, "jail_attach(): %d", jid);
if (chdir("/") == -1)
@@ -130,54 +291,6 @@ usage(void)
fprintf(stderr, "%s%s\n",
"usage: jexec [-u username | -U username]",
- " [-h hostname | -h ip-number | jid] command ...");
+ " [-n jailname] jid command ...");
exit(1);
}
-
-static int
-addr2jid(const char *addr)
-{
- struct xprison *sxp, *xp;
- struct in_addr in;
- size_t i, len;
- int jid, cnt;
- jid = cnt = 0;
-
- if (sysctlbyname("security.jail.list", NULL, &len, NULL, 0) == -1)
- err(1, "sysctlbyname(): security.jail.list");
- for (i = 0; i < 4; i++) {
- if (len <= 0)
- err(1, "sysctlbyname(): len <=0");
- sxp = xp = malloc(len);
- if (sxp == NULL)
- err(1, "malloc()");
- if (sysctlbyname("security.jail.list", xp, &len, NULL, 0) == -1) {
- if (errno == ENOMEM) {
- free(sxp);
- sxp = NULL;
- continue;
- }
- err(1, "sysctlbyname(): security.jail.list");
- }
- break;
- }
- if (sxp == NULL)
- err(1, "sysctlbyname(): security.jail.list");
- if (len < sizeof(*xp) || len % sizeof(*xp) ||
- xp->pr_version != XPRISON_VERSION)
- errx(1, "Kernel and userland out of sync");
- for (i = 0; i < len / sizeof(*xp); i++) {
- in.s_addr = ntohl(xp->pr_ip);
- if ((strcmp(inet_ntoa(in), addr) == 0) ||
- (strcmp(xp->pr_host, addr) == 0)) {
- jid = xp->pr_id;
- cnt++;
- }
- xp++;
- }
- free(sxp);
- if (cnt == 1)
- return (jid);
- else
- return(0);
-}
diff --git a/usr.sbin/jls/Makefile b/usr.sbin/jls/Makefile
index 3968946..01294bd 100644
--- a/usr.sbin/jls/Makefile
+++ b/usr.sbin/jls/Makefile
@@ -4,4 +4,6 @@ PROG= jls
MAN= jls.8
WARNS?= 6
+CFLAGS+= -DSUPPORT_OLD_XPRISON
+
.include <bsd.prog.mk>
diff --git a/usr.sbin/jls/jls.8 b/usr.sbin/jls/jls.8
index 4dc68c7..aff9848 100644
--- a/usr.sbin/jls/jls.8
+++ b/usr.sbin/jls/jls.8
@@ -25,20 +25,44 @@
.\"
.\" $FreeBSD$
.\"
-.Dd April 8, 2003
+.Dd November 29, 2008
.Dt JLS 8
.Os
.Sh NAME
.Nm jls
-.Nd "list active jails"
+.Nd "list jails"
.Sh SYNOPSIS
.Nm
+.Op Fl av
.Sh DESCRIPTION
The
.Nm
-utility lists all active jails.
-Each jail is represented by one row which contains the following columns:
-jail identifier (JID), IP address, hostname, and path.
+utility lists all jails.
+By default only active jails are listed.
+.Pp
+The options are as follows:
+.Bl -tag -width ".Fl a"
+.It Fl a
+Show jails in all states, not only active ones.
+.It Fl v
+Show more verbose information.
+This also lists cpusets, jail state, multi-IP, etc. instead of the
+classic single-IP jail output.
+.El
+.Pp
+Each jail is represented by rows which, depending on
+.Fl v ,
+contain the following columns:
+.Bl -item -offset indent -compact
+.It
+jail identifier (JID), hostname and path
+.It
+jail state and name
+.It
+jail cpuset
+.It
+followed by one IP adddress per line.
+.El
.Sh SEE ALSO
.Xr jail 2 ,
.Xr jail 8 ,
diff --git a/usr.sbin/jls/jls.c b/usr.sbin/jls/jls.c
index 23a3946..4488c31 100644
--- a/usr.sbin/jls/jls.c
+++ b/usr.sbin/jls/jls.c
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 2003 Mike Barcroft <mike@FreeBSD.org>
+ * Copyright (c) 2008 Bjoern A. Zeeb <bz@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -27,57 +28,216 @@
*/
#include <sys/param.h>
+#include <sys/types.h>
#include <sys/jail.h>
#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
#include <arpa/inet.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#define FLAG_A 0x00001
+#define FLAG_V 0x00002
+
+#ifdef SUPPORT_OLD_XPRISON
+static
+char *print_xprison_v1(void *p, char *end)
+{
+ struct xprison_v1 *xp;
+ struct in_addr in;
+
+ if ((char *)p + sizeof(struct xprison_v1) > end)
+ errx(1, "Invalid length for jail");
+
+ xp = (struct xprison_v1 *)p;
+ printf("%6d %-29.29s %.74s\n",
+ xp->pr_id, xp->pr_host, xp->pr_path);
+
+ /* We are not printing an empty line here for state and name. */
+ /* We are not printing an empty line here for cpusetid. */
+
+ /* IPv4 address. */
+ in.s_addr = htonl(xp->pr_ip);
+ printf("%6s %-15.15s\n", "", inet_ntoa(in));
+
+ return ((char *)(xp + 1));
+}
+#endif
+
+static
+char *print_xprison_v3(void *p, char *end, unsigned flags)
+{
+ struct xprison *xp;
+ struct in_addr *iap, in;
+ struct in6_addr *ia6p;
+ char buf[INET6_ADDRSTRLEN];
+ const char *state;
+ char *q;
+ uint32_t i;
+
+ if ((char *)p + sizeof(struct xprison) > end)
+ errx(1, "Invalid length for jail");
+ xp = (struct xprison *)p;
+
+ if (xp->pr_state < 0 || xp->pr_state > (int)
+ ((sizeof(prison_states) / sizeof(struct prison_state))))
+ state = "(bogus)";
+ else
+ state = prison_states[xp->pr_state].state_name;
+
+ /* See if we should print non-ACTIVE jails. No? */
+ if ((flags & FLAG_A) == 0 && strcmp(state, "ALIVE")) {
+ q = (char *)(xp + 1);
+ q += (xp->pr_ip4s * sizeof(struct in_addr));
+ if (q > end)
+ errx(1, "Invalid length for jail");
+ q += (xp->pr_ip6s * sizeof(struct in6_addr));
+ if (q > end)
+ errx(1, "Invalid length for jail");
+ return (q);
+ }
+
+ printf("%6d %-29.29s %.74s\n",
+ xp->pr_id, xp->pr_host, xp->pr_path);
+
+ /* Jail state and name. */
+ if (flags & FLAG_V)
+ printf("%6s %-29.29s %.74s\n",
+ "", (xp->pr_name != NULL) ? xp->pr_name : "", state);
+
+ /* cpusetid. */
+ if (flags & FLAG_V)
+ printf("%6s %-6d\n",
+ "", xp->pr_cpusetid);
+
+ q = (char *)(xp + 1);
+ /* IPv4 addresses. */
+ iap = (struct in_addr *)(void *)q;
+ q += (xp->pr_ip4s * sizeof(struct in_addr));
+ if (q > end)
+ errx(1, "Invalid length for jail");
+ for (i = 0; i < xp->pr_ip4s; i++) {
+ if (i == 0 || flags & FLAG_V) {
+ in.s_addr = iap[i].s_addr;
+ printf("%6s %-15.15s\n", "", inet_ntoa(in));
+ }
+ }
+ /* IPv6 addresses. */
+ ia6p = (struct in6_addr *)(void *)q;
+ q += (xp->pr_ip6s * sizeof(struct in6_addr));
+ if (q > end)
+ errx(1, "Invalid length for jail");
+ for (i = 0; i < xp->pr_ip6s; i++) {
+ if (flags & FLAG_V) {
+ inet_ntop(AF_INET6, &ia6p[i], buf, sizeof(buf));
+ printf("%6s %s\n", "", buf);
+ }
+ }
+
+ return (q);
+}
+
+static void
+usage(void)
+{
+
+ (void)fprintf(stderr, "usage: jls [-av]\n");
+ exit(1);
+}
int
-main(void)
+main(int argc, char *argv[])
{
- struct xprison *sxp, *xp;
- struct in_addr in;
- size_t i, len;
+ int ch, version;
+ unsigned flags;
+ size_t i, j, len;
+ void *p, *q;
+
+ flags = 0;
+ while ((ch = getopt(argc, argv, "av")) != -1) {
+ switch (ch) {
+ case 'a':
+ flags |= FLAG_A;
+ break;
+ case 'v':
+ flags |= FLAG_V;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
if (sysctlbyname("security.jail.list", NULL, &len, NULL, 0) == -1)
err(1, "sysctlbyname(): security.jail.list");
+ j = len;
for (i = 0; i < 4; i++) {
if (len <= 0)
exit(0);
- sxp = xp = malloc(len);
- if (sxp == NULL)
+ p = q = malloc(len);
+ if (p == NULL)
err(1, "malloc()");
- if (sysctlbyname("security.jail.list", xp, &len, NULL, 0) == -1) {
+ if (sysctlbyname("security.jail.list", q, &len, NULL, 0) == -1) {
if (errno == ENOMEM) {
- free(sxp);
- sxp = NULL;
+ free(p);
+ p = NULL;
+ len += j;
continue;
}
err(1, "sysctlbyname(): security.jail.list");
}
break;
}
- if (sxp == NULL)
+ if (p == NULL)
err(1, "sysctlbyname(): security.jail.list");
- if (len < sizeof(*xp) || len % sizeof(*xp) ||
- xp->pr_version != XPRISON_VERSION)
- errx(1, "Kernel and userland out of sync");
-
- printf(" JID IP Address Hostname Path\n");
- for (i = 0; i < len / sizeof(*xp); i++) {
- in.s_addr = ntohl(xp->pr_ip);
- printf("%6d %-15.15s %-29.29s %.74s\n",
- xp->pr_id, inet_ntoa(in), xp->pr_host, xp->pr_path);
- xp++;
+ if (len < sizeof(int))
+ errx(1, "This is no prison. Kernel and userland out of sync?");
+ version = *(int *)p;
+ if (version > XPRISON_VERSION)
+ errx(1, "Sci-Fi prison. Kernel/userland out of sync?");
+
+ printf(" JID Hostname Path\n");
+ if (flags & FLAG_V) {
+ printf(" Name State\n");
+ printf(" CPUSetID\n");
+ }
+ printf(" IP Address(es)\n");
+ for (; q != NULL && (char *)q + sizeof(int) < (char *)p + len;) {
+ version = *(int *)q;
+ if (version > XPRISON_VERSION)
+ errx(1, "Sci-Fi prison. Kernel/userland out of sync?");
+ switch (version) {
+#ifdef SUPPORT_OLD_XPRISON
+ case 1:
+ q = print_xprison_v1(q, (char *)p + len);
+ break;
+ case 2:
+ errx(1, "Version 2 was used by multi-IPv4 jail "
+ "implementations that never made it into the "
+ "official kernel.");
+ /* NOTREACHED */
+ break;
+#endif
+ case 3:
+ q = print_xprison_v3(q, (char *)p + len, flags);
+ break;
+ default:
+ errx(1, "Prison unknown. Kernel/userland out of sync?");
+ /* NOTREACHED */
+ break;
+ }
}
- free(sxp);
+
+ free(p);
exit(0);
}
OpenPOWER on IntegriCloud