--- //depot/vendor/freebsd/src/lib/libc/sys/cpuset_getaffinity.2 2008/09/29 16:39:06 +++ //depot/user/bz/jail/src/lib/libc/sys/cpuset_getaffinity.2 2008/10/03 14:38:56 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD: src/lib/libc/sys/cpuset_getaffinity.2,v 1.3 2008/09/29 15:44:48 jhb Exp $ .\" -.Dd March 29, 2008 +.Dd June 27, 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 --- //depot/vendor/freebsd/src/lib/libc/sys/jail.2 2008/08/03 22:35:29 +++ //depot/user/bz/jail/src/lib/libc/sys/jail.2 2008/11/23 15:08:43 @@ -8,7 +8,7 @@ .\" .\" $FreeBSD: src/lib/libc/sys/jail.2,v 1.29 2008/08/03 21:56:58 trhodes Exp $ .\" -.Dd August 3, 2008 +.Dd November 21, 2008 .Dt JAIL 2 .Os .Sh NAME @@ -32,15 +32,20 @@ .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 @@ 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 --- //depot/vendor/freebsd/src/lib/libkvm/kvm_proc.c 2008/03/12 10:41:25 +++ //depot/user/bz/jail/src/lib/libkvm/kvm_proc.c 2008/10/24 18:30:48 @@ -54,10 +54,11 @@ #include #include #include +#include +#include +#include #define _WANT_PRISON /* make jail.h give us 'struct prison' */ #include -#include -#include #include #include #include --- //depot/vendor/freebsd/src/share/man/man4/ddb.4 2008/10/18 06:36:43 +++ //depot/user/bz/jail/src/share/man/man4/ddb.4 2008/10/29 15:34:16 @@ -686,6 +686,15 @@ 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. .\" --- //depot/vendor/freebsd/src/sys/compat/freebsd32/freebsd32.h 2007/12/06 23:16:48 +++ //depot/user/bz/jail/src/sys/compat/freebsd32/freebsd32.h 2008/06/10 21:36:08 @@ -153,6 +153,24 @@ 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; --- //depot/vendor/freebsd/src/sys/compat/freebsd32/freebsd32_misc.c 2008/11/10 23:40:14 +++ //depot/user/bz/jail/src/sys/compat/freebsd32/freebsd32_misc.c 2008/11/20 14:26:11 @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -2036,6 +2037,66 @@ } 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; --- //depot/vendor/freebsd/src/sys/compat/freebsd32/freebsd32_proto.h 2008/11/09 10:55:16 +++ //depot/user/bz/jail/src/sys/compat/freebsd32/freebsd32_proto.h 2008/11/20 14:26:11 @@ -235,6 +235,9 @@ char modid_l_[PADL_(int)]; int modid; char modid_r_[PADR_(int)]; char stat_l_[PADL_(struct module_stat32 *)]; struct module_stat32 * stat; char stat_r_[PADR_(struct module_stat32 *)]; }; +struct freebsd32_jail_args { + char jail_l_[PADL_(struct jail32 *)]; struct jail32 * jail; char jail_r_[PADR_(struct jail32 *)]; +}; struct freebsd32_sigtimedwait_args { char set_l_[PADL_(const sigset_t *)]; const sigset_t * set; char set_r_[PADR_(const sigset_t *)]; char info_l_[PADL_(siginfo_t *)]; siginfo_t * info; char info_r_[PADR_(siginfo_t *)]; @@ -428,6 +431,7 @@ int freebsd32_preadv(struct thread *, struct freebsd32_preadv_args *); int freebsd32_pwritev(struct thread *, struct freebsd32_pwritev_args *); int freebsd32_modstat(struct thread *, struct freebsd32_modstat_args *); +int freebsd32_jail(struct thread *, struct freebsd32_jail_args *); int freebsd32_sigtimedwait(struct thread *, struct freebsd32_sigtimedwait_args *); int freebsd32_sigwaitinfo(struct thread *, struct freebsd32_sigwaitinfo_args *); int freebsd32_kevent(struct thread *, struct freebsd32_kevent_args *); @@ -641,6 +645,7 @@ #define FREEBSD32_SYS_AUE_freebsd32_preadv AUE_PREADV #define FREEBSD32_SYS_AUE_freebsd32_pwritev AUE_PWRITEV #define FREEBSD32_SYS_AUE_freebsd32_modstat AUE_NULL +#define FREEBSD32_SYS_AUE_freebsd32_jail AUE_JAIL #define FREEBSD32_SYS_AUE_freebsd32_sigtimedwait AUE_SIGWAIT #define FREEBSD32_SYS_AUE_freebsd32_sigwaitinfo AUE_NULL #define FREEBSD32_SYS_AUE_freebsd32_kevent AUE_NULL --- //depot/vendor/freebsd/src/sys/compat/freebsd32/freebsd32_syscall.h 2008/11/09 10:55:16 +++ //depot/user/bz/jail/src/sys/compat/freebsd32/freebsd32_syscall.h 2008/11/20 14:26:11 @@ -254,7 +254,7 @@ #define FREEBSD32_SYS_utrace 335 #define FREEBSD32_SYS_freebsd4_freebsd32_sendfile 336 #define FREEBSD32_SYS_kldsym 337 -#define FREEBSD32_SYS_jail 338 +#define FREEBSD32_SYS_freebsd32_jail 338 #define FREEBSD32_SYS_sigprocmask 340 #define FREEBSD32_SYS_sigsuspend 341 #define FREEBSD32_SYS_freebsd4_freebsd32_sigaction 342 --- //depot/vendor/freebsd/src/sys/compat/freebsd32/freebsd32_syscalls.c 2008/11/09 10:55:16 +++ //depot/user/bz/jail/src/sys/compat/freebsd32/freebsd32_syscalls.c 2008/11/20 14:26:11 @@ -345,7 +345,7 @@ "utrace", /* 335 = utrace */ "compat4.freebsd32_sendfile", /* 336 = old freebsd32_sendfile */ "kldsym", /* 337 = kldsym */ - "jail", /* 338 = jail */ + "freebsd32_jail", /* 338 = freebsd32_jail */ "#339", /* 339 = pioctl */ "sigprocmask", /* 340 = sigprocmask */ "sigsuspend", /* 341 = sigsuspend */ --- //depot/vendor/freebsd/src/sys/compat/freebsd32/freebsd32_sysent.c 2008/11/09 10:55:16 +++ //depot/user/bz/jail/src/sys/compat/freebsd32/freebsd32_sysent.c 2008/11/20 14:26:11 @@ -376,7 +376,7 @@ { AS(utrace_args), (sy_call_t *)utrace, AUE_NULL, NULL, 0, 0 }, /* 335 = utrace */ { compat4(AS(freebsd4_freebsd32_sendfile_args),freebsd32_sendfile), AUE_SENDFILE, NULL, 0, 0 }, /* 336 = old freebsd32_sendfile */ { AS(kldsym_args), (sy_call_t *)kldsym, AUE_NULL, NULL, 0, 0 }, /* 337 = kldsym */ - { AS(jail_args), (sy_call_t *)jail, AUE_JAIL, NULL, 0, 0 }, /* 338 = jail */ + { AS(freebsd32_jail_args), (sy_call_t *)freebsd32_jail, AUE_JAIL, NULL, 0, 0 }, /* 338 = freebsd32_jail */ { 0, (sy_call_t *)nosys, AUE_NULL, NULL, 0, 0 }, /* 339 = pioctl */ { AS(sigprocmask_args), (sy_call_t *)sigprocmask, AUE_SIGPROCMASK, NULL, 0, 0 }, /* 340 = sigprocmask */ { AS(sigsuspend_args), (sy_call_t *)sigsuspend, AUE_SIGSUSPEND, NULL, 0, 0 }, /* 341 = sigsuspend */ --- //depot/vendor/freebsd/src/sys/compat/freebsd32/syscalls.master 2008/11/09 10:50:13 +++ //depot/user/bz/jail/src/sys/compat/freebsd32/syscalls.master 2008/11/20 14:26:11 @@ -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); } --- //depot/vendor/freebsd/src/sys/kern/kern_cpuset.c 2008/07/07 21:40:13 +++ //depot/user/bz/jail/src/sys/kern/kern_cpuset.c 2008/10/24 18:38:46 @@ -53,6 +53,7 @@ #include #include #include +#include /* Must come after sys/proc.h */ #include @@ -208,7 +209,7 @@ * 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 @@ 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 @@ 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 @@ } /* + * 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 @@ */ 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 @@ PROC_UNLOCK(p); break; case CPU_WHICH_CPUSET: + case CPU_WHICH_JAIL: break; case CPU_WHICH_IRQ: return (EINVAL); @@ -829,6 +932,7 @@ thread_unlock(ttd); break; case CPU_WHICH_CPUSET: + case CPU_WHICH_JAIL: break; case CPU_WHICH_IRQ: error = EINVAL; @@ -856,6 +960,7 @@ } break; case CPU_WHICH_CPUSET: + case CPU_WHICH_JAIL: CPU_COPY(&set->cs_mask, mask); break; case CPU_WHICH_IRQ: @@ -936,6 +1041,7 @@ PROC_UNLOCK(p); break; case CPU_WHICH_CPUSET: + case CPU_WHICH_JAIL: break; case CPU_WHICH_IRQ: error = EINVAL; @@ -958,7 +1064,8 @@ 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); --- //depot/vendor/freebsd/src/sys/kern/kern_exit.c 2008/10/15 06:35:14 +++ //depot/user/bz/jail/src/sys/kern/kern_exit.c 2008/10/17 21:59:02 @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -453,6 +454,10 @@ 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 --- //depot/vendor/freebsd/src/sys/kern/kern_fork.c 2008/10/19 01:40:13 +++ //depot/user/bz/jail/src/sys/kern/kern_fork.c 2008/10/29 15:34:16 @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -452,6 +453,11 @@ __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); /* --- //depot/vendor/freebsd/src/sys/kern/kern_jail.c 2008/11/17 20:55:47 +++ //depot/user/bz/jail/src/sys/kern/kern_jail.c 2008/11/23 19:46:21 @@ -10,6 +10,9 @@ #include __FBSDID("$FreeBSD: src/sys/kern/kern_jail.c,v 1.84 2008/11/17 20:49:29 pjd Exp $"); +#include "opt_ddb.h" +#include "opt_inet.h" +#include "opt_inet6.h" #include "opt_mac.h" #include @@ -38,6 +41,12 @@ #include #include #include +#ifdef DDB +#include +#ifdef INET6 +#include +#endif /* INET6 */ +#endif /* DDB */ #include @@ -54,7 +63,7 @@ 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, @@ -81,6 +90,11 @@ &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; @@ -90,6 +104,12 @@ 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) @@ -101,6 +121,276 @@ 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; iip4s; 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; iip6s; 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; @@ -109,22 +399,72 @@ 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, @@ -137,16 +477,50 @@ 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)); + /* + * 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. */ - sx_xlock(&allprison_lock); tryprid = lastprid + 1; if (tryprid == JAIL_MAX) tryprid = 1; @@ -157,7 +531,7 @@ if (tryprid == JAIL_MAX) { sx_xunlock(&allprison_lock); error = EAGAIN; - goto e_dropvnref; + goto e_dropcpuset; } goto next; } @@ -180,6 +554,8 @@ 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); @@ -187,6 +563,12 @@ 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); } @@ -222,10 +604,27 @@ 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) @@ -245,12 +644,14 @@ 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); @@ -315,6 +716,8 @@ prisoncount--; sx_xunlock(&allprison_lock); + cpuset_rel(pr->pr_cpuset); + /* Free all OSD associated to this jail. */ osd_jail_exit(pr); @@ -323,8 +726,13 @@ 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); } @@ -347,79 +755,364 @@ mtx_unlock(&pr->pr_mtx); } -u_int32_t -prison_getip(struct ucred *cred) +void +prison_proc_hold(struct prison *pr) +{ + + 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_getip4(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)) + /* Do not change address passed in. */ + return (0); + + 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); - return (cred->cr_prison->pr_ip); + 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); + } + + /* + * 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 (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_ip(struct ucred *cred, int flag, u_int32_t *ip) +prison_remote_ip4(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)) return (0); - if (flag) - tmp = *ip; - else - tmp = ntohl(*ip); - if (tmp == INADDR_ANY) { - if (flag) - *ip = cred->cr_prison->pr_ip; + 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); +} + +/* + * 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) +{ + 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 - *ip = htonl(cred->cr_prison->pr_ip); + 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 (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); } - 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 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 (cred->cr_prison->pr_ip != tmp) + 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); } -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. + */ +static int +_prison_check_ip6(struct prison *pr, struct in6_addr *ia6) { - u_int32_t tmp; + 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); - if (!jailed(cred)) - return; - if (flag) - tmp = *ip; - else - tmp = ntohl(*ip); - if (tmp == INADDR_LOOPBACK) { - if (flag) - *ip = cred->cr_prison->pr_ip; + /* + * 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); } @@ -639,6 +1332,7 @@ * processes in the same jail. Likewise for signalling. */ case PRIV_SCHED_DIFFCRED: + case PRIV_SCHED_CPUSET: case PRIV_SIGNAL_DIFFCRED: case PRIV_SIGNAL_SUGID: @@ -748,6 +1442,8 @@ { struct xprison *xp, *sxp; struct prison *pr; + char *p; + size_t len; int count, error; if (jailed(req->td->td_ucred)) @@ -759,21 +1455,54 @@ 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); } @@ -793,3 +1522,60 @@ } 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 */ --- //depot/vendor/freebsd/src/sys/kern/uipc_socket.c 2008/11/22 12:40:49 +++ //depot/user/bz/jail/src/sys/kern/uipc_socket.c 2008/11/23 16:00:21 @@ -98,6 +98,7 @@ __FBSDID("$FreeBSD: src/sys/kern/uipc_socket.c,v 1.322 2008/11/22 12:36:15 kib Exp $"); #include "opt_inet.h" +#include "opt_inet6.h" #include "opt_mac.h" #include "opt_zero.h" #include "opt_compat.h" @@ -346,6 +347,9 @@ 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); } --- //depot/vendor/freebsd/src/sys/net/if.c 2008/11/26 22:35:45 +++ //depot/user/bz/jail/src/sys/net/if.c 2008/11/26 23:40:31 @@ -2211,7 +2211,7 @@ 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 --- //depot/vendor/freebsd/src/sys/net/rtsock.c 2008/10/25 18:01:02 +++ //depot/user/bz/jail/src/sys/net/rtsock.c 2008/11/20 16:23:26 @@ -31,11 +31,13 @@ */ #include "opt_sctp.h" #include "opt_mpath.h" +#include "opt_inet.h" +#include "opt_inet6.h" #include #include +#include #include -#include #include #include #include @@ -54,6 +56,9 @@ #include #include +#ifdef INET6 +#include +#endif #ifdef SCTP extern void sctp_addr_change(struct ifaddr *ifa, int cmd); @@ -309,6 +314,136 @@ .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 @@ 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 @@ 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 @@ 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 @@ 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] = --- //depot/vendor/freebsd/src/sys/netinet/in_pcb.c 2008/11/26 22:35:45 +++ //depot/user/bz/jail/src/sys/netinet/in_pcb.c 2008/11/26 23:40:31 @@ -294,7 +294,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ } 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 @@ 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 @@ 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 @@ } /* 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 @@ /* 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 @@ 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 @@ } /* 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 @@ 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 @@ } /* 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 @@ 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 @@ * 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( @@ -1108,6 +1113,7 @@ 0, pcbinfo->ipi_hashmask)]; LIST_FOREACH(inp, head, inp_hash) { #ifdef INET6 + /* XXX inp locking */ if ((inp->inp_vflag & INP_IPV4) == 0) continue; #endif @@ -1115,9 +1121,11 @@ 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); } } /* @@ -1147,7 +1155,11 @@ */ 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; /* @@ -1180,9 +1192,8 @@ if (wildcard < matchwild) { match = inp; matchwild = wildcard; - if (matchwild == 0) { + if (matchwild == 0) break; - } } } } @@ -1200,7 +1211,7 @@ 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); @@ -1208,60 +1219,108 @@ /* * 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; + } else { + if (local_exact != NULL) continue; - if (inp->inp_laddr.s_addr == laddr.s_addr) + } + + 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_CHECK_SOCKAF(inp->inp_socket, - AF_INET6)) - local_wild_mapped = inp; + /* XXX inp locking, NULL check */ + if (INP_CHECK_SOCKAF(inp->inp_socket, AF_INET6)) + 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); } --- //depot/vendor/freebsd/src/sys/netinet/raw_ip.c 2008/11/19 19:20:31 +++ //depot/user/bz/jail/src/sys/netinet/raw_ip.c 2008/11/20 16:23:26 @@ -273,10 +273,9 @@ 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 @@ 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 @@ 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 @@ } 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 @@ 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) || --- //depot/vendor/freebsd/src/sys/netinet/sctp_pcb.c 2008/11/12 14:20:14 +++ //depot/user/bz/jail/src/sys/netinet/sctp_pcb.c 2008/11/20 16:37:08 @@ -2553,7 +2553,6 @@ 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 @@ 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 @@ } 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 @@ 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 */ --- //depot/vendor/freebsd/src/sys/netinet/sctp_usrreq.c 2008/10/18 16:00:18 +++ //depot/user/bz/jail/src/sys/netinet/sctp_usrreq.c 2008/11/20 16:37:08 @@ -507,11 +507,10 @@ struct inpcb *ip_inp; int error; uint32_t vrf_id = SCTP_DEFAULT_VRFID; - #ifdef IPSEC uint32_t flags; +#endif -#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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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_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); --- //depot/vendor/freebsd/src/sys/netinet/tcp_usrreq.c 2008/11/26 20:55:13 +++ //depot/user/bz/jail/src/sys/netinet/tcp_usrreq.c 2008/11/26 23:40:31 @@ -454,8 +454,8 @@ 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); @@ -521,6 +521,10 @@ 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); @@ -529,6 +533,10 @@ 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); --- //depot/vendor/freebsd/src/sys/netinet/udp_usrreq.c 2008/11/26 22:35:45 +++ //depot/user/bz/jail/src/sys/netinet/udp_usrreq.c 2008/11/26 23:40:31 @@ -940,9 +940,10 @@ * 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 @@ 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); --- //depot/vendor/freebsd/src/sys/netinet6/in6_pcb.c 2008/11/26 20:55:13 +++ //depot/user/bz/jail/src/sys/netinet6/in6_pcb.c 2008/11/26 23:40:31 @@ -149,6 +149,10 @@ 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)) { /* @@ -223,6 +227,9 @@ 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) ? @@ -255,6 +262,9 @@ } 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) @@ -314,6 +324,8 @@ 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 @@ -688,15 +700,16 @@ 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); } } /* @@ -726,6 +739,10 @@ */ 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)) @@ -735,7 +752,7 @@ 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)) @@ -744,9 +761,8 @@ if (wildcard < matchwild) { match = inp; matchwild = wildcard; - if (matchwild == 0) { + if (matchwild == 0) break; - } } } } @@ -829,11 +845,11 @@ */ 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; @@ -847,10 +863,12 @@ /* * 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) && @@ -858,32 +876,79 @@ 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; + } else { + if (local_exact != NULL) continue; - if (IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, - laddr)) + } + + 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. --- //depot/vendor/freebsd/src/sys/netinet6/in6_src.c 2008/11/26 22:35:45 +++ //depot/user/bz/jail/src/sys/netinet6/in6_src.c 2008/11/26 23:40:31 @@ -80,6 +80,7 @@ #include #include #include +#include #include #include #include @@ -235,6 +236,11 @@ 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 @@ * 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 @@ 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 @@ 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; --- //depot/vendor/freebsd/src/sys/netinet6/raw_ip6.c 2008/11/26 22:35:45 +++ //depot/user/bz/jail/src/sys/netinet6/raw_ip6.c 2008/11/26 23:40:31 @@ -67,6 +67,7 @@ #include #include +#include #include #include #include @@ -162,6 +163,7 @@ 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 @@ 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 @@ 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 @@ 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) --- //depot/vendor/freebsd/src/sys/netinet6/udp6_usrreq.c 2008/11/26 20:55:13 +++ //depot/user/bz/jail/src/sys/netinet6/udp6_usrreq.c 2008/11/26 23:40:31 @@ -71,6 +71,7 @@ #include "opt_mac.h" #include +#include #include #include #include @@ -860,6 +861,12 @@ 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 @@ 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) { --- //depot/vendor/freebsd/src/sys/security/mac_bsdextended/mac_bsdextended.c 2008/10/30 10:15:15 +++ //depot/user/bz/jail/src/sys/security/mac_bsdextended/mac_bsdextended.c 2008/11/06 19:07:10 @@ -57,6 +57,7 @@ #include #include #include +#include #include #include #include --- //depot/vendor/freebsd/src/sys/sys/cpuset.h 2008/04/11 03:28:08 +++ //depot/user/bz/jail/src/sys/sys/cpuset.h 2008/07/05 19:01:45 @@ -132,6 +132,7 @@ #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 @@ #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 --- //depot/vendor/freebsd/src/sys/sys/jail.h 2008/11/17 20:55:47 +++ //depot/user/bz/jail/src/sys/sys/jail.h 2008/11/20 16:37:08 @@ -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 @@ #include +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 @@ 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 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 @@ 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. --- //depot/vendor/freebsd/src/usr.bin/cpuset/cpuset.1 2008/09/29 16:39:06 +++ //depot/user/bz/jail/src/usr.bin/cpuset/cpuset.1 2008/10/03 14:38:56 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD: src/usr.bin/cpuset/cpuset.1,v 1.4 2008/09/29 15:44:48 jhb Exp $ .\" -.Dd March 11, 2008 +.Dd June 27, 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 @@ .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 @@ 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 @@ 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 --- //depot/vendor/freebsd/src/usr.bin/cpuset/cpuset.c 2008/04/11 03:48:34 +++ //depot/user/bz/jail/src/usr.bin/cpuset/cpuset.c 2008/07/05 19:01:45 @@ -49,6 +49,7 @@ int cflag; int gflag; int iflag; +int jflag; int lflag; int pflag; int rflag; @@ -153,7 +154,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ * 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 @@ 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 @@ 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); } --- //depot/vendor/freebsd/src/usr.sbin/jail/Makefile 2004/11/17 10:36:58 +++ //depot/user/bz/jail/src/usr.sbin/jail/Makefile 2008/03/24 21:43:28 @@ -1,5 +1,7 @@ # $FreeBSD: src/usr.sbin/jail/Makefile,v 1.10 2004/11/17 10:01:48 delphij Exp $ +.include + PROG= jail MAN= jail.8 DPADD= ${LIBUTIL} @@ -7,4 +9,8 @@ WARNS?= 6 +.if ${MK_INET6_SUPPORT} != "no" +CFLAGS+= -DINET6 +.endif + .include --- //depot/vendor/freebsd/src/usr.sbin/jail/jail.8 2007/04/05 21:24:47 +++ //depot/user/bz/jail/src/usr.sbin/jail/jail.8 2008/11/23 15:27:56 @@ -33,7 +33,7 @@ .\" .\" $FreeBSD: src/usr.sbin/jail/jail.8,v 1.84 2007/04/05 21:17:52 pjd Exp $ .\" -.Dd April 5, 2007 +.Dd November 23, 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 @@ .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 @@ 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 @@ 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 @@ 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 @@ .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 --- //depot/vendor/freebsd/src/usr.sbin/jail/jail.c 2006/05/12 17:40:48 +++ //depot/user/bz/jail/src/usr.sbin/jail/jail.c 2008/06/06 17:04:04 @@ -12,10 +12,14 @@ #include #include +#include +#include #include +#include #include #include +#include #include #include @@ -25,12 +29,34 @@ #include #include #include +#include #include #include -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 @@ 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 @@ 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 @@ 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 @@ } 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 @@ { (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 + --- //depot/vendor/freebsd/src/usr.sbin/jexec/Makefile 2006/04/19 15:39:56 +++ //depot/user/bz/jail/src/usr.sbin/jexec/Makefile 2008/06/16 13:03:15 @@ -6,4 +6,6 @@ LDADD= -lutil WARNS?= 6 +CFLAGS+= -DSUPPORT_OLD_XPRISON + .include --- //depot/vendor/freebsd/src/usr.sbin/jexec/jexec.8 2008/05/29 17:39:15 +++ //depot/user/bz/jail/src/usr.sbin/jexec/jexec.8 2008/06/16 13:31:10 @@ -23,9 +23,9 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD: src/usr.sbin/jexec/jexec.8,v 1.7 2008/05/29 17:00:01 mr Exp $ +.\" $FreeBSD: src/usr.sbin/jexec/jexec.8,v 1.4 2006/09/29 17:57:02 ru Exp $ .\" -.Dd May 26, 2008 +.Dd June 16, 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 @@ .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 @@ .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 . --- //depot/vendor/freebsd/src/usr.sbin/jexec/jexec.c 2008/05/29 17:39:15 +++ //depot/user/bz/jail/src/usr.sbin/jexec/jexec.c 2008/06/23 14:06:55 @@ -23,27 +23,177 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/usr.sbin/jexec/jexec.c,v 1.7 2008/05/29 17:00:01 mr Exp $ + * $FreeBSD: src/usr.sbin/jexec/jexec.c,v 1.4 2006/09/29 17:04:03 ru Exp $ */ #include #include #include -#include - +#include #include #include -#include #include #include #include +#include #include #include -#include 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 +218,18 @@ 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 +238,6 @@ username = optarg; Uflag = 1; break; - case 'h': - hflag = 1; - break; default: usage(); } @@ -94,15 +246,22 @@ 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 +289,6 @@ 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); -} --- //depot/vendor/freebsd/src/usr.sbin/jls/Makefile 2003/04/09 03:36:45 +++ //depot/user/bz/jail/src/usr.sbin/jls/Makefile 2008/04/12 15:51:17 @@ -4,4 +4,6 @@ MAN= jls.8 WARNS?= 6 +CFLAGS+= -DSUPPORT_OLD_XPRISON + .include --- //depot/vendor/freebsd/src/usr.sbin/jls/jls.8 2003/04/09 03:36:45 +++ //depot/user/bz/jail/src/usr.sbin/jls/jls.8 2008/09/14 12:59:43 @@ -25,20 +25,44 @@ .\" .\" $FreeBSD: src/usr.sbin/jls/jls.8,v 1.1 2003/04/09 03:04:12 mike Exp $ .\" -.Dd April 8, 2003 +.Dd September 14, 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 , --- //depot/vendor/freebsd/src/usr.sbin/jls/jls.c 2005/08/19 11:37:05 +++ //depot/user/bz/jail/src/usr.sbin/jls/jls.c 2008/10/04 07:39:43 @@ -27,57 +27,216 @@ */ #include +#include #include #include +#include +#include #include #include #include #include #include #include +#include +#include + +#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"); + 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 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++; + 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); }