--- //depot/vendor/freebsd_7/src/lib/libc/sys/cpuset_getaffinity.2 2008/07/25 18:10:49 +++ //depot/user/bz/jail_7/src/lib/libc/sys/cpuset_getaffinity.2 2008/07/26 10:18:16 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD: src/lib/libc/sys/cpuset_getaffinity.2,v 1.2.2.1 2008/07/25 17:46:01 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_7/src/lib/libc/sys/jail.2 2007/11/15 02:59:41 +++ //depot/user/bz/jail_7/src/lib/libc/sys/jail.2 2008/06/16 17:25:27 @@ -8,7 +8,7 @@ .\" .\" $FreeBSD: src/lib/libc/sys/jail.2,v 1.28 2005/02/09 18:03:14 ru Exp $ .\" -.Dd April 8, 2003 +.Dd January 20, 2008 .Dt JAIL 2 .Os .Sh NAME @@ -32,15 +32,19 @@ .Bd -literal -offset indent struct jail { u_int32_t version; - char *path; - char *hostname; - u_int32_t ip_number; + char *path; + char *hostname; + 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 +58,21 @@ 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 ip4s +and +.Dq Li ip6s +give the numbers of IPv4 and IPv6 addresses that will be passed +in by their complementing pointers. +.Pp +The +.Dq Li ip4 +and +.Dq Li ip6 +pointers can be set to a list of IPv4 and IPv6 addresses +assigned to the prison. +Addresses are given as one contiguous memory allocation +per address family. +IPv4 addresses have to be in network byte order. .Pp The .Fn jail_attach --- //depot/vendor/freebsd_7/src/lib/libkvm/Makefile 2008/08/26 18:10:49 +++ //depot/user/bz/jail_7/src/lib/libkvm/Makefile 2008/09/17 20:21:25 @@ -8,6 +8,7 @@ .if ${MACHINE} == "sun4v" CFLAGS+=-DSUN4V .endif +CFLAGS+=-DSUPPORT_OLD_XPRISON SRCS= kvm.c kvm_${MACHINE_ARCH}.c kvm_cptime.c kvm_file.c kvm_getloadavg.c \ kvm_getswapinfo.c kvm_pcpu.c kvm_proc.c --- //depot/vendor/freebsd_7/src/lib/libkvm/kvm_proc.c 2007/11/15 02:59:41 +++ //depot/user/bz/jail_7/src/lib/libkvm/kvm_proc.c 2008/07/26 11:56:25 @@ -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 @@ -88,6 +89,74 @@ static int ticks; static int hz; + +static int +kvm_read_prison_id(kvm_t *kd, void *cr_prison) +{ + int pr_version; + + if (KREAD(kd, (u_long)cr_prison, &pr_version)) { + _kvm_err(kd, kd->program, "kvm_read failed getting prison " + "version from %x", cr_prison); + return (-1); + } + + if (pr_version > XPRISON_VERSION) { + _kvm_err(kd, kd->program, "Sci-Fi prison. Kernel/userland " + "out of sync at %x?", cr_prison); + return (-1); + } + switch (pr_version) { +#ifdef SUPPORT_OLD_XPRISON + case 1: + { + struct xprison_v1 xp; + + if (KREAD(kd, (u_long)cr_prison, &xp)) { + _kvm_err(kd, kd->program, "kvm_read failed getting " + "version %d prison data from %x", + pr_version, cr_prison); + return (-1); + } + return (xp.pr_id); + /* NOTREACHED */ + break; + } + case 2: + _kvm_err(kd, kd->program, "Jail version %d found at %x was " + "used by multi-IPv4 jail implementations that never " + "made it into the official kernel.", pr_version, cr_prison); + return (-1); + /* NOTREACHED */ + break; +#endif + case 3: + { + struct xprison xp; + + if (KREAD(kd, (u_long)cr_prison, &xp)) { + _kvm_err(kd, kd->program, "kvm_read failed getting " + "version %d prison data from %x", + pr_version, cr_prison); + return (-1); + } + return (xp.pr_id); + /* NOTREACHED */ + break; + } + default: + _kvm_err(kd, kd->program, "Prison version %d found at %x " + "unknown. Kernel/userland out of sync?", + pr_version, cr_prison); + return (-1); + /* NOTREACHED */ + break; + } + + /* NOTREACHED */ + return (-1); +} + /* * Read proc's from memory file into buffer bp, which has space to hold * at most maxcnt procs. @@ -145,13 +214,10 @@ NGROUPS * sizeof(gid_t)); kp->ki_uid = ucred.cr_uid; if (ucred.cr_prison != NULL) { - if (KREAD(kd, (u_long)ucred.cr_prison, &pr)) { - _kvm_err(kd, kd->program, - "can't read prison at %x", - ucred.cr_prison); + kp->ki_jid = + kvm_read_prison_id(kd, ucred.cr_prison); + if (kp->ki_jid == -1) return (-1); - } - kp->ki_jid = pr.pr_id; } } --- //depot/vendor/freebsd_7/src/share/man/man4/ddb.4 2008/08/31 20:10:42 +++ //depot/user/bz/jail_7/src/share/man/man4/ddb.4 2008/09/17 20:21:25 @@ -60,7 +60,7 @@ .\" .\" $FreeBSD: src/share/man/man4/ddb.4,v 1.42.2.7 2008/08/31 20:08:00 rwatson Exp $ .\" -.Dd December 26, 2007 +.Dd July 8, 2008 .Dt DDB 4 .Os .Sh NAME @@ -540,6 +540,15 @@ is given, displays details about the given GEOM object (class, geom, provider or consumer). .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 map Ns Oo Li / Ns Cm f Oc Ar addr Prints the VM map at .Ar addr . --- //depot/vendor/freebsd_7/src/sys/compat/freebsd32/freebsd32.h 2007/12/19 21:20:39 +++ //depot/user/bz/jail_7/src/sys/compat/freebsd32/freebsd32.h 2008/06/16 17:25:27 @@ -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_7/src/sys/compat/freebsd32/freebsd32_misc.c 2008/07/25 18:10:49 +++ //depot/user/bz/jail_7/src/sys/compat/freebsd32/freebsd32_misc.c 2008/07/26 09:21:26 @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -1963,6 +1964,72 @@ } 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); + /* NOTREACHED */ + break; + 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); + /* NOTREACHED */ + break; + } + + error = kern_jail(td, &j); + return (error); +} + +int freebsd32_sigaction(struct thread *td, struct freebsd32_sigaction_args *uap) { struct sigaction32 s32; --- //depot/vendor/freebsd_7/src/sys/compat/freebsd32/freebsd32_proto.h 2008/07/25 20:11:23 +++ //depot/user/bz/jail_7/src/sys/compat/freebsd32/freebsd32_proto.h 2008/07/26 09:21:26 @@ -224,6 +224,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 jail *)]; struct jail * jail; char jail_r_[PADR_(struct jail *)]; +}; 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 *)]; @@ -394,6 +397,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 *); @@ -601,6 +605,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_7/src/sys/compat/freebsd32/freebsd32_syscall.h 2008/07/25 20:11:23 +++ //depot/user/bz/jail_7/src/sys/compat/freebsd32/freebsd32_syscall.h 2008/07/26 09:21:26 @@ -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_7/src/sys/compat/freebsd32/freebsd32_syscalls.c 2008/07/25 20:11:23 +++ //depot/user/bz/jail_7/src/sys/compat/freebsd32/freebsd32_syscalls.c 2008/07/26 09:21:26 @@ -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_7/src/sys/compat/freebsd32/freebsd32_sysent.c 2008/07/25 20:11:23 +++ //depot/user/bz/jail_7/src/sys/compat/freebsd32/freebsd32_sysent.c 2008/07/26 09:21:26 @@ -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_7/src/sys/compat/freebsd32/syscalls.master 2008/08/28 16:12:40 +++ //depot/user/bz/jail_7/src/sys/compat/freebsd32/syscalls.master 2008/09/17 20:21:25 @@ -574,7 +574,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 jail *jail); } 339 AUE_NULL UNIMPL pioctl 340 AUE_SIGPROCMASK NOPROTO { int sigprocmask(int how, \ const sigset_t *set, sigset_t *oset); } --- //depot/vendor/freebsd_7/src/sys/kern/kern_cpuset.c 2008/07/25 18:10:49 +++ //depot/user/bz/jail_7/src/sys/kern/kern_cpuset.c 2008/07/26 11:56:25 @@ -53,6 +53,7 @@ #include #include #include +#include /* Must come after sys/proc.h */ #include @@ -221,6 +222,27 @@ if (set) cpuset_ref(set); mtx_unlock_spin(&cpuset_lock); + + if (set != NULL && jailed(curthread->td_ucred)) { + struct cpuset *rset, *jset; + struct prison *pr; + + rset = cpuset_refroot(set); + + pr = curthread->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); } @@ -420,6 +442,32 @@ 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); + } default: return (EINVAL); } @@ -668,6 +716,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. */ @@ -771,6 +872,7 @@ PROC_UNLOCK(p); break; case CPU_WHICH_CPUSET: + case CPU_WHICH_JAIL: break; } switch (uap->level) { @@ -831,6 +933,7 @@ thread_unlock(ttd); break; case CPU_WHICH_CPUSET: + case CPU_WHICH_JAIL: break; } if (uap->level == CPU_LEVEL_ROOT) @@ -857,6 +960,7 @@ PROC_SUNLOCK(p); break; case CPU_WHICH_CPUSET: + case CPU_WHICH_JAIL: CPU_COPY(&set->cs_mask, mask); break; } @@ -934,6 +1038,7 @@ PROC_UNLOCK(p); break; case CPU_WHICH_CPUSET: + case CPU_WHICH_JAIL: break; } if (uap->level == CPU_LEVEL_ROOT) @@ -953,7 +1058,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_7/src/sys/kern/kern_exit.c 2008/09/19 10:12:44 +++ //depot/user/bz/jail_7/src/sys/kern/kern_exit.c 2008/09/20 10:20:23 @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -461,6 +462,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_7/src/sys/kern/kern_fork.c 2008/08/27 05:11:37 +++ //depot/user/bz/jail_7/src/sys/kern/kern_fork.c 2008/09/17 20:21:25 @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -442,6 +443,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_7/src/sys/kern/kern_jail.c 2008/08/02 17:11:01 +++ //depot/user/bz/jail_7/src/sys/kern/kern_jail.c 2008/09/17 20:21:25 @@ -10,6 +10,8 @@ #include __FBSDID("$FreeBSD: src/sys/kern/kern_jail.c,v 1.70.2.4 2008/08/02 16:53:45 bz Exp $"); +#include "opt_ddb.h" +#include "opt_inet6.h" #include "opt_mac.h" #include @@ -35,6 +37,12 @@ #include #include #include +#ifdef DDB +#include +#ifdef INET6 +#include +#endif /* INET6 */ +#endif /* DDB */ #include @@ -51,7 +59,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, @@ -78,6 +86,16 @@ &jail_mount_allowed, 0, "Processes in jail can mount/unmount jail-friendly file systems"); +int jail_jailed_sockets_first = 1; +SYSCTL_INT(_security_jail, OID_AUTO, jailed_sockets_first, CTLFLAG_RW, + &jail_jailed_sockets_first, 0, + "Choose jailed sockets before non-jailed sockets"); + +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; @@ -114,6 +132,145 @@ SYSINIT(prison, SI_SUB_INTRINSIC, SI_ORDER_ANY, init_prison, NULL); +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); +} + +#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; + + i = 0; + do { + if (ia6a->s6_addr[i] > ia6b->s6_addr[i]) + rc = 1; + else if (ia6a->s6_addr[i] < ia6b->s6_addr[i]) + rc = -1; + else + rc = 0; + i++; + } while (i <= 15 && rc == 0); + + return (rc); +} +#endif + +static int +jail_copyin_ips(struct jail *j) +{ + struct in_addr *ip4; +#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. + */ + ip4 = NULL; +#ifdef INET6 + ip6 = NULL; +#endif + if (j->ip4s > 0) { + MALLOC(ip4, struct in_addr *, 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; + } +#ifdef INET6 + if (j->ip6s > 0) { + MALLOC(ip6, struct in6_addr *, 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 + FREE(ip4, M_PRISON); + return (error); +} + /* * struct jail_args { * struct jail *jail; @@ -122,23 +279,115 @@ 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. */ + bzero(&j, sizeof(struct jail)); + error = copyin(uap->jail, &j, sizeof(struct jail_v0)); + if (error) + return (error); + break; + case 1: + /* + * Version 1 was used by multi-IPv4 jail implementations + * that never made it into the official kernel. + */ + return (EINVAL); + /* NOTREACHED */ + break; + 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); + /* NOTREACHED */ + break; + } + + error = kern_jail(td, &j); + return (error); +} + +int +kern_jail(struct thread *td, struct jail *j) +{ struct nameidata nd; struct prison *pr, *tpr; struct prison_service *psrv; - struct jail j; struct jail_attach_args jaa; int vfslocked, error, tryprid; - error = copyin(uap->jail, &j, sizeof(j)); - if (error) - return (error); - if (j.version != 0) + KASSERT(j != NULL, ("%s: j is NULL", __func__)); + + /* + * Finish coversion over for older versions and + * setup of struct jail. + */ + switch (j->version) { + case 0: + { + /* FreeBSD single IPv4 jails. */ + struct in_addr *ip4; + + if (j->ip4s == INADDR_ANY || j->ip4s == INADDR_BROADCAST) + return (EINVAL); + + MALLOC(ip4, struct in_addr *, 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; + } + case 1: + /* + * Version 1 was used by multi-IPv4 jail implementations + * that never made it into the official kernel. + */ + return (EINVAL); + /* NOTREACHED */ + break; + case 2: /* JAIL_API_VERSION */ + /* FreeBSD multi-IPv4/IPv6,noIP jails. */ + if (j->ip4s > jail_max_af_ips) + return (EINVAL); +#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); + break; + default: + /* Sci-Fi jails are not supported, sorry. */ return (EINVAL); + /* NOTREACHED */ + break; + } + /* Allocate struct prison and fill it with life. */ MALLOC(pr, struct prison *, 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, @@ -151,10 +400,25 @@ VOP_UNLOCK(nd.ni_vp, 0, td); 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; if (prison_service_slots == 0) @@ -164,6 +428,21 @@ M_PRISON, M_ZERO | M_WAITOK); } + /* + * 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; + /* Determine next pr_id and add prison to allprison list. */ sx_xlock(&allprison_lock); tryprid = lastprid + 1; @@ -176,7 +455,7 @@ if (tryprid == JAIL_MAX) { sx_xunlock(&allprison_lock); error = EAGAIN; - goto e_dropvnref; + goto e_dropcpuset; } goto next; } @@ -207,6 +486,8 @@ psrv->ps_destroy(psrv, pr); } sx_sunlock(&allprison_lock); +e_dropcpuset: + cpuset_rel(pr->pr_cpuset); e_dropvnref: if (pr->pr_slots != NULL) FREE(pr->pr_slots, M_PRISON); @@ -216,6 +497,10 @@ e_killmtx: mtx_destroy(&pr->pr_mtx); FREE(pr, M_PRISON); +#ifdef INET6 + FREE(j->ip6, M_PRISON); +#endif + FREE(j->ip4, M_PRISON); return (error); } @@ -251,10 +536,24 @@ 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, td); if ((error = change_dir(pr->pr_root, td)) != 0) @@ -274,12 +573,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, td); VFS_UNLOCK_GIANT(vfslocked); +e_unref: mtx_lock(&pr->pr_mtx); pr->pr_ref--; mtx_unlock(&pr->pr_mtx); @@ -340,6 +641,9 @@ psrv->ps_destroy(psrv, pr); } sx_sunlock(&allprison_lock); + + cpuset_rel(pr->pr_cpuset); + if (pr->pr_slots != NULL) FREE(pr->pr_slots, M_PRISON); @@ -348,8 +652,11 @@ 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 + FREE(pr->pr_ip4, M_PRISON); FREE(pr, M_PRISON); } @@ -364,79 +671,336 @@ 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); +} + + +/* + * 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_ip4(struct ucred *cred, struct in_addr *ia) +{ + struct in_addr ia0; + + KASSERT(cred != NULL, ("%s: cred is NULL", __func__)); + KASSERT(ia != NULL, ("%s: ia is NULL", __func__)); + + if (!jailed(cred)) + return (0); + if (cred->cr_prison->pr_ip4 == NULL) + return (1); + + ia0.s_addr = ntohl(ia->s_addr); + if (ia0.s_addr == INADDR_LOOPBACK) { + ia->s_addr = cred->cr_prison->pr_ip4[0].s_addr; + return (0); + } + + /* 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 || jailed_ip4(cred, ia)) + return (0); + + return (1); +} + +/* + * Rewrite destination address in case we will connect to loopback address. + * + * Returns 0 on success, 1 on error. + * Address passed in in NBO and returned in NBO. + */ +int +prison_remote_ip4(struct ucred *cred, struct in_addr *ia) { - return (cred->cr_prison->pr_ip); + KASSERT(cred != NULL, ("%s: cred is NULL", __func__)); + KASSERT(ia != NULL, ("%s: ia is NULL", __func__)); + + if (!jailed(cred)) + return (0); + if (cred->cr_prison->pr_ip4 == NULL) + return (1); + + if (ntohl(ia->s_addr) == INADDR_LOOPBACK) { + ia->s_addr = cred->cr_prison->pr_ip4[0].s_addr; + return (0); + } + + /* Return success because nothing had to be changed. */ + return (0); } +/* + * 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. + */ int -prison_ip(struct ucred *cred, int flag, u_int32_t *ip) +jailed_ip4(struct ucred *cred, struct in_addr *ia) { - u_int32_t tmp; + int i, a, z, d; + + KASSERT(cred != NULL, ("%s: cred is NULL", __func__)); + KASSERT(ia != NULL, ("%s: ia is NULL", __func__)); if (!jailed(cred)) + return (1); + if (cred->cr_prison->pr_ip4 == NULL) return (0); - if (flag) - tmp = *ip; - else - tmp = ntohl(*ip); - if (tmp == INADDR_ANY) { - if (flag) - *ip = cred->cr_prison->pr_ip; + + /* Check the primary IP. */ + if (cred->cr_prison->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 = cred->cr_prison->pr_ip4s - 2; + while (a <= z) { + i = (a + z) / 2; + d = qcmp_v4(&cred->cr_prison->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); +} + +#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_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) || jailed_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. + */ +int +jailed_ip6(struct ucred *cred, struct in6_addr *ia6) { - u_int32_t tmp; + int i, a, z, d; + + KASSERT(cred != NULL, ("%s: cred is NULL", __func__)); + KASSERT(ia6 != NULL, ("%s: ia6 is NULL", __func__)); if (!jailed(cred)) - return; - if (flag) - tmp = *ip; - else - tmp = ntohl(*ip); - if (tmp == INADDR_LOOPBACK) { - if (flag) - *ip = cred->cr_prison->pr_ip; + return (1); + if (cred->cr_prison->pr_ip6 == NULL) + return (0); + + /* Check the primary IP. */ + if (IN6_ARE_ADDR_EQUAL(&cred->cr_prison->pr_ip6[0], ia6)) + return (1); + + /* All the other IPs are sorted so we can do a binary search. */ + a = 0; + z = cred->cr_prison->pr_ip6s - 2; + while (a <= z) { + i = (a + z) / 2; + d = qcmp_v6(&cred->cr_prison->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); } +#endif +/* + * Check if given address belongs to the jail referenced by cred + * (wrapper to jailed_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) { struct sockaddr_in *sai; +#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) + { + case AF_INET: + sai = (struct sockaddr_in *)sa; + if (jailed_ip4(cred, &sai->sin_addr)) + ok = 1; + break; +#ifdef INET6 + case AF_INET6: + sai6 = (struct sockaddr_in6 *)sa; + if (jailed_ip6(cred, (struct in6_addr *)&sai6->sin6_addr)) + ok = 1; + break; +#endif + default: + if (!jail_socket_unixiproute_only) + ok = 1; + } + return (ok); } @@ -652,6 +1216,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: @@ -947,6 +1512,8 @@ { struct xprison *xp, *sxp; struct prison *pr; + char *p; + size_t len; int count, error; if (jailed(req->td->td_ucred)) @@ -958,21 +1525,48 @@ return (0); } - sxp = xp = malloc(sizeof(*xp) * count, M_TEMP, M_WAITOK | M_ZERO); + len = sizeof(*xp) * count; + LIST_FOREACH(pr, &allprison, pr_list) { + len += pr->pr_ip4s * sizeof(struct in_addr); +#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++; + xp->pr_ip4s = pr->pr_ip4s; +#ifdef INET6 + xp->pr_ip6s = pr->pr_ip6s; +#endif + p = (char *)(xp + 1); + 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)); + } +#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); } @@ -992,3 +1586,55 @@ } 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; + struct in_addr ia; +#ifdef INET6 + char ip6buf[INET6_ADDRSTRLEN]; +#endif + const char *state; + int i; + + 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); + 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)); + } +#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_7/src/sys/kern/uipc_socket.c 2008/09/15 21:12:18 +++ //depot/user/bz/jail_7/src/sys/kern/uipc_socket.c 2008/09/17 20:21:25 @@ -98,6 +98,7 @@ __FBSDID("$FreeBSD: src/sys/kern/uipc_socket.c,v 1.302.2.6 2008/09/15 20:46:32 rwatson Exp $"); #include "opt_inet.h" +#include "opt_inet6.h" #include "opt_mac.h" #include "opt_zero.h" #include "opt_compat.h" @@ -347,6 +348,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_7/src/sys/net/if.c 2008/08/03 22:11:16 +++ //depot/user/bz/jail_7/src/sys/net/if.c 2008/09/17 20:21:25 @@ -2181,7 +2181,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_7/src/sys/net/rtsock.c 2008/08/31 16:12:11 +++ //depot/user/bz/jail_7/src/sys/net/rtsock.c 2008/09/17 20:21:25 @@ -30,10 +30,12 @@ * $FreeBSD: src/sys/net/rtsock.c,v 1.143.2.3 2008/08/31 15:55:59 rwatson Exp $ */ #include "opt_sctp.h" +#include "opt_inet6.h" + #include #include +#include #include -#include #include #include #include @@ -51,6 +53,9 @@ #include #include +#ifdef INET6 +#include +#endif #ifdef SCTP extern void sctp_addr_change(struct ifaddr *ifa, int cmd); @@ -306,6 +311,123 @@ .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) { + 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 (jailed_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 (jailed_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; + } +#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 (jailed_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 (jailed_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); + /* NOT REACHED */ + break; + } + + return (0); +} + /*ARGSUSED*/ static int route_output(struct mbuf *m, struct socket *so) @@ -317,7 +439,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)) && @@ -459,16 +581,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)); + 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] = - (struct sockaddr *)&jail; - } 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; @@ -1147,7 +1270,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; @@ -1195,7 +1318,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_7/src/sys/netinet/in_pcb.c 2008/09/01 08:10:58 +++ //depot/user/bz/jail_7/src/sys/netinet/in_pcb.c 2008/09/18 18:32:45 @@ -276,7 +276,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; /* @@ -305,9 +305,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_ip4(cred, &sin->sin_addr) != 0) + return (EINVAL); if (sin->sin_port != *lportp) { /* Don't allow the port to change. */ if (*lportp != 0) @@ -342,14 +341,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(so->so_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. @@ -366,10 +362,10 @@ t->inp_socket->so_cred->cr_uid)) return (EADDRINUSE); } - if (prison && prison_ip(cred, 0, &sin->sin_addr.s_addr)) + if (prison_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 @@ -401,9 +397,8 @@ u_short first, last; int count; - if (laddr.s_addr != INADDR_ANY) - if (prison_ip(cred, 0, &laddr.s_addr)) - return (EINVAL); + if (prison_ip4(cred, &laddr)) + return (EINVAL); if (inp->inp_flags & INP_HIGHPORT) { first = ipport_hifirstauto; /* sysctl */ @@ -487,7 +482,7 @@ wild, cred)); } } - if (prison_ip(cred, 0, &laddr.s_addr)) + if (prison_ip4(cred, &laddr)) return (EINVAL); *laddrp = laddr.s_addr; *lportp = lport; @@ -541,7 +536,211 @@ return (0); } + /* + * Do proper source address selection on an unbound socket in case + * of connect. Take jails into account as well. + */ +static int +in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr, + struct ucred *cred) +{ + struct in_ifaddr *ia; + struct ifaddr *ifa; + struct sockaddr *sa; + struct sockaddr_in *sin; + struct route sro; + int error; + + KASSERT(inp != NULL, ("%s: inp NULL", __func__)); + KASSERT(faddr != NULL, ("%s: faddr NULL", __func__)); + KASSERT(laddr != NULL, ("%s: laddr NULL", __func__)); + + error = 0; + ia = NULL; + bzero(&sro, sizeof(sro)); + + sin = (struct sockaddr_in *)&sro.ro_dst; + sin->sin_family = AF_INET; + sin->sin_len = sizeof(struct sockaddr_in); + sin->sin_addr.s_addr = faddr->s_addr; + + /* + * If route is known our src addr is taken from the i/f, + * else punt. + * + * Find out route to destination. + */ + if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0) + in_rtalloc_ign(&sro, RTF_CLONING, inp->inp_inc.inc_fibnum); + + /* + * If we found a route, use the address corresponding to + * the outgoing interface. + * + * Otherwise assume faddr is reachable on a directly connected + * network and try to find a corresponding interface to take + * the source address from. + */ + if (sro.ro_rt == NULL || sro.ro_rt->rt_ifp == NULL) { + struct ifnet *ifp; + + ia = ifatoia(ifa_ifwithdstaddr((struct sockaddr *)sin)); + if (ia == NULL) + ia = ifatoia(ifa_ifwithnet((struct sockaddr *)sin)); + if (ia == NULL) { + error = ENETUNREACH; + goto done; + } + + if (cred == NULL || !jailed(cred)) { + laddr->s_addr = ia->ia_addr.sin_addr.s_addr; + goto done; + } + + ifp = ia->ia_ifp; + ia = NULL; + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + + sa = ifa->ifa_addr; + if (sa->sa_family != AF_INET) + continue; + sin = (struct sockaddr_in *)sa; + if (jailed_ip4(cred, &sin->sin_addr) != 0) { + ia = (struct in_ifaddr *)ifa; + break; + } + } + if (ia != NULL) { + laddr->s_addr = ia->ia_addr.sin_addr.s_addr; + goto done; + } + + /* 3. As a last resort return the 'default' jail address. */ + if (prison_getip4(cred, laddr) != 0) + error = EADDRNOTAVAIL; + goto done; + } + + /* + * If the outgoing interface on the route found is not + * a loopback interface, use the address from that interface. + * In case of jails do those three steps: + * 1. check if the interface address belongs to the jail. If so use it. + * 2. check if we have any address on the outgoing interface + * belonging to this jail. If so use it. + * 3. as a last resort return the 'default' jail address. + */ + if ((sro.ro_rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) { + + /* If not jailed, use the default returned. */ + if (cred == NULL || !jailed(cred)) { + ia = (struct in_ifaddr *)sro.ro_rt->rt_ifa; + laddr->s_addr = ia->ia_addr.sin_addr.s_addr; + goto done; + } + + /* Jailed. */ + /* 1. Check if the iface address belongs to the jail. */ + sin = (struct sockaddr_in *)sro.ro_rt->rt_ifa->ifa_addr; + if (jailed_ip4(cred, &sin->sin_addr) != 0) { + ia = (struct in_ifaddr *)sro.ro_rt->rt_ifa; + laddr->s_addr = ia->ia_addr.sin_addr.s_addr; + goto done; + } + + /* + * 2. Check if we have any address on the outgoing interface + * belonging to this jail. + */ + TAILQ_FOREACH(ifa, &sro.ro_rt->rt_ifp->if_addrhead, ifa_link) { + + sa = ifa->ifa_addr; + if (sa->sa_family != AF_INET) + continue; + sin = (struct sockaddr_in *)sa; + if (jailed_ip4(cred, &sin->sin_addr) != 0) { + ia = (struct in_ifaddr *)ifa; + break; + } + } + if (ia != NULL) { + laddr->s_addr = ia->ia_addr.sin_addr.s_addr; + goto done; + } + + /* 3. As a last resort return the 'default' jail address. */ + if (prison_getip4(cred, laddr) != 0) + error = EADDRNOTAVAIL; + goto done; + } + + /* + * The outgoing interface is marked with 'loopback net', so a route + * to ourselves is here. + * Try to find the interface of the destination address and then + * take the address from there. That interface is not necessarily + * a loopback interface. + * In case of jails, check that it is an address of the jail + * and if we cannot find, fall back to the 'default' jail address. + */ + if ((sro.ro_rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) { + struct sockaddr_in sain; + + bzero(&sain, sizeof(struct sockaddr_in)); + sain.sin_family = AF_INET; + sain.sin_len = sizeof(struct sockaddr_in); + sain.sin_addr.s_addr = faddr->s_addr; + + ia = ifatoia(ifa_ifwithdstaddr(sintosa(&sain))); + if (ia == NULL) + ia = ifatoia(ifa_ifwithnet(sintosa(&sain))); + + if (cred == NULL || !jailed(cred)) { + if (ia == NULL) { + error = ENETUNREACH; + goto done; + } + laddr->s_addr = ia->ia_addr.sin_addr.s_addr; + goto done; + } + + /* Jailed. */ + if (ia != NULL) { + struct ifnet *ifp; + + ifp = ia->ia_ifp; + ia = NULL; + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + + sa = ifa->ifa_addr; + if (sa->sa_family != AF_INET) + continue; + sin = (struct sockaddr_in *)sa; + if (jailed_ip4(cred, &sin->sin_addr) != 0) { + ia = (struct in_ifaddr *)ifa; + break; + } + } + if (ia != NULL) { + laddr->s_addr = ia->ia_addr.sin_addr.s_addr; + goto done; + } + } + + /* 3. As a last resort return the 'default' jail address. */ + if (prison_getip4(cred, laddr) != 0) + error = EADDRNOTAVAIL; + goto done; + } + +done: + if (sro.ro_rt != NULL) + RTFREE(sro.ro_rt); + return (error); +} + +/* * Set up for a connect from a socket to the specified address. * On entry, *laddrp and *lportp should contain the current local * address and port for the PCB; these are updated to the values @@ -563,10 +762,8 @@ { struct sockaddr_in *sin = (struct sockaddr_in *)nam; struct in_ifaddr *ia; - struct sockaddr_in sa; - struct ucred *socred; struct inpcb *oinp; - struct in_addr laddr, faddr; + struct in_addr laddr, faddr, jailia; u_short lport, fport; int error; @@ -589,17 +786,7 @@ lport = *lportp; faddr = sin->sin_addr; fport = sin->sin_port; - socred = inp->inp_socket->so_cred; - if (laddr.s_addr == INADDR_ANY && jailed(socred)) { - bzero(&sa, sizeof(sa)); - sa.sin_addr.s_addr = htonl(prison_getip(socred)); - sa.sin_len = sizeof(sa); - sa.sin_family = AF_INET; - error = in_pcbbind_setup(inp, (struct sockaddr *)&sa, - &laddr.s_addr, &lport, cred); - if (error) - return (error); - } + if (!TAILQ_EMPTY(&in_ifaddrhead)) { /* * If the destination address is INADDR_ANY, @@ -608,44 +795,27 @@ * and the primary interface supports broadcast, * choose the broadcast address for that interface. */ - if (faddr.s_addr == INADDR_ANY) - faddr = IA_SIN(TAILQ_FIRST(&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(&in_ifaddrhead))->sin_addr; + } + } else if (faddr.s_addr == (u_long)INADDR_BROADCAST && (TAILQ_FIRST(&in_ifaddrhead)->ia_ifp->if_flags & IFF_BROADCAST)) faddr = satosin(&TAILQ_FIRST( &in_ifaddrhead)->ia_broadaddr)->sin_addr; } if (laddr.s_addr == INADDR_ANY) { - ia = NULL; - /* - * If route is known our src addr is taken from the i/f, - * else punt. - * - * Find out route to destination - */ - if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0) - ia = ip_rtaddr(faddr, inp->inp_inc.inc_fibnum); - /* - * If we found a route, use the address corresponding to - * the outgoing interface. - * - * Otherwise assume faddr is reachable on a directly connected - * network and try to find a corresponding interface to take - * the source address from. - */ - if (ia == NULL) { - bzero(&sa, sizeof(sa)); - sa.sin_addr = faddr; - sa.sin_len = sizeof(sa); - sa.sin_family = AF_INET; + + error = in_pcbladdr(inp, &faddr, &laddr, cred); + if (error) + return (error); - ia = ifatoia(ifa_ifwithdstaddr(sintosa(&sa))); - if (ia == NULL) - ia = ifatoia(ifa_ifwithnet(sintosa(&sa))); - if (ia == NULL) - return (ENETUNREACH); - } /* * If the destination address is multicast and an outgoing * interface has been set as a multicast option, use the @@ -664,9 +834,10 @@ break; if (ia == NULL) return (EADDRNOTAVAIL); + + laddr = ia->ia_addr.sin_addr; } } - laddr = ia->ia_addr.sin_addr; } oinp = in_pcblookup_hash(inp->inp_pcbinfo, faddr, fport, laddr, lport, @@ -921,18 +1092,26 @@ head = &pcbinfo->ipi_hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->ipi_hashmask)]; LIST_FOREACH(inp, head, inp_hash) { + NOP_INP_RLOCK(inp); #ifdef INET6 - if ((inp->inp_vflag & INP_IPV4) == 0) + if ((inp->inp_vflag & INP_IPV4) == 0) { + NOP_INP_RUNLOCK(inp); continue; + } #endif if (inp->inp_faddr.s_addr == INADDR_ANY && inp->inp_laddr.s_addr == laddr.s_addr && inp->inp_lport == lport) { - /* - * Found. - */ - return (inp); + /* Found? */ + /* Those values could be NULL, really. */ + if (inp->inp_socket == NULL || cred == NULL || + inp->inp_socket->so_cred->cr_prison == + cred->cr_prison) { + NOP_INP_RUNLOCK(inp); + return (inp); + } } + NOP_INP_RUNLOCK(inp); } /* * Not found. @@ -961,9 +1140,18 @@ */ LIST_FOREACH(inp, &phd->phd_pcblist, inp_portlist) { wildcard = 0; + NOP_INP_RLOCK(inp); + if (cred != NULL && inp->inp_socket != NULL && + inp->inp_socket->so_cred->cr_prison != + cred->cr_prison) { + NOP_INP_RUNLOCK(inp); + continue; + } #ifdef INET6 - if ((inp->inp_vflag & INP_IPV4) == 0) + if ((inp->inp_vflag & INP_IPV4) == 0) { + NOP_INP_RUNLOCK(inp); continue; + } /* * We never select the PCB that has * INP_IPV6 flag and is bound to :: if @@ -985,8 +1173,11 @@ if (inp->inp_laddr.s_addr != INADDR_ANY) { if (laddr.s_addr == INADDR_ANY) wildcard++; - else if (inp->inp_laddr.s_addr != laddr.s_addr) + else if (inp->inp_laddr.s_addr != + laddr.s_addr) { + NOP_INP_RUNLOCK(inp); continue; + } } else { if (laddr.s_addr != INADDR_ANY) wildcard++; @@ -995,9 +1186,11 @@ match = inp; matchwild = wildcard; if (matchwild == 0) { + NOP_INP_RUNLOCK(inp); break; } } + NOP_INP_RUNLOCK(inp); } } return (match); @@ -1014,7 +1207,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); @@ -1022,60 +1215,175 @@ /* * 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) { + NOP_INP_RLOCK(inp); #ifdef INET6 - if ((inp->inp_vflag & INP_IPV4) == 0) + if ((inp->inp_vflag & INP_IPV4) == 0) { + NOP_INP_RUNLOCK(inp); 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) { + if (inp->inp_socket == NULL || + !jailed(inp->inp_socket->so_cred)) { + if (!jail_jailed_sockets_first) { + NOP_INP_RUNLOCK(inp); + return (inp); + } else if (tmpinp == NULL) + tmpinp = inp; + } else { + if (jail_jailed_sockets_first) { + NOP_INP_RUNLOCK(inp); + return (inp); + } else if (tmpinp == NULL) + tmpinp = inp; + } + } + NOP_INP_RUNLOCK(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, *jail_exact = NULL; + struct ucred *cred; + int injail; + /* + * Order of socket selection: + * if (jail_jailed_sockets_first) { + * 1. jailed, non-wild. + * 2. jailed, wild. + * 3. non-jailed, non-wild. + * 4. non-jailed, wild. + * } else { + * 1. non-jailed, non-wild. + * 2. non-jailed, wild. + * 3. jailed, non-wild. + * 4. jailed, wild. + * } + */ + head = &pcbinfo->ipi_hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->ipi_hashmask)]; LIST_FOREACH(inp, head, inp_hash) { + NOP_INP_RLOCK(inp); #ifdef INET6 - if ((inp->inp_vflag & INP_IPV4) == 0) + if ((inp->inp_vflag & INP_IPV4) == 0) { + NOP_INP_RUNLOCK(inp); 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) { + NOP_INP_RUNLOCK(inp); + continue; + } + + if (ifp && ifp->if_type == IFT_FAITH && + (inp->inp_flags & INP_FAITH) == 0) { + NOP_INP_RUNLOCK(inp); + continue; + } + + if (inp->inp_socket == NULL) { + cred = NULL; + injail = 0; + } else { + cred = inp->inp_socket->so_cred; + injail = jailed(cred); + } + + if (injail) { + if (jail_exact != NULL) { + /* We should have returned earlier. */ + KASSERT(!jail_jailed_sockets_first, + ("jail_exact == NULL")); + NOP_INP_RUNLOCK(inp); + continue; + } + if (!jailed_ip4(cred, &laddr)) { + NOP_INP_RUNLOCK(inp); + continue; + } + } else { + if (local_exact != NULL) { + /* We should have returned earlier. */ + KASSERT(jail_jailed_sockets_first, + ("local_exact == NULL")); + NOP_INP_RUNLOCK(inp); continue; - if (inp->inp_laddr.s_addr == laddr.s_addr) - return (inp); - else if (inp->inp_laddr.s_addr == INADDR_ANY) { + } + } + + if (inp->inp_laddr.s_addr == laddr.s_addr) { + if (injail) { + if (jail_jailed_sockets_first) { + NOP_INP_RUNLOCK(inp); + return (inp); + } else + jail_exact = inp; + } else { + if (!jail_jailed_sockets_first) { + NOP_INP_RUNLOCK(inp); + return (inp); + } 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; + 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; - } } - } + NOP_INP_RUNLOCK(inp); + } /* LIST_FOREACH */ + + if (jail_jailed_sockets_first) { + KASSERT(jail_exact == NULL, ("jail_exact != NULL")); + 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_mapped != NULL) + return (local_wild_mapped); +#endif /* defined(INET6) */ + } else { + KASSERT(local_exact == NULL, ("local_exact != NULL")); + if (local_wild != NULL) + return (local_wild); #ifdef INET6 - if (local_wild == NULL) - return (local_wild_mapped); + if (local_wild_mapped != NULL) + return (local_wild_mapped); #endif - return (local_wild); - } + if (jail_exact != NULL) + return (jail_exact); + if (jail_wild != NULL) + return (jail_wild); + } + } /* if (wildcard == INPLOOKUP_WILDCARD) */ + return (NULL); } --- //depot/vendor/freebsd_7/src/sys/netinet/in_pcb.h 2008/09/01 05:10:52 +++ //depot/user/bz/jail_7/src/sys/netinet/in_pcb.h 2008/09/17 20:21:25 @@ -465,6 +465,15 @@ #define INP_CHECK_SOCKAF(so, af) (INP_SOCKAF(so) == af) +#define NOP_INP_RLOCK(inp) +#define NOP_INP_WLOCK(inp) +#define NOP_INP_RUNLOCK(inp) +#define NOP_INP_WUNLOCK(inp) +#define NOP_INP_LOCK_ASSERT(inp) +#define NOP_INP_RLOCK_ASSERT(inp) +#define NOP_INP_WLOCK_ASSERT(inp) +#define NOP_INP_UNLOCK_ASSERT(inp) + #ifdef _KERNEL extern int ipport_reservedhigh; extern int ipport_reservedlow; --- //depot/vendor/freebsd_7/src/sys/netinet/raw_ip.c 2008/08/25 22:11:52 +++ //depot/user/bz/jail_7/src/sys/netinet/raw_ip.c 2008/09/19 11:15:59 @@ -265,11 +265,12 @@ if (inp->inp_faddr.s_addr != ip->ip_src.s_addr) continue; INP_RLOCK(inp); - if (jailed(inp->inp_socket->so_cred) && - (htonl(prison_getip(inp->inp_socket->so_cred)) != - ip->ip_dst.s_addr)) { - INP_RUNLOCK(inp); - continue; + if (jailed(inp->inp_socket->so_cred)) { + if (!jailed_ip4(inp->inp_socket->so_cred, + &ip->ip_dst)) { + INP_RUNLOCK(inp); + continue; + } } if (last) { struct mbuf *n; @@ -296,11 +297,11 @@ inp->inp_faddr.s_addr != ip->ip_src.s_addr) continue; INP_RLOCK(inp); - if (jailed(inp->inp_socket->so_cred) && - (htonl(prison_getip(inp->inp_socket->so_cred)) != - ip->ip_dst.s_addr)) { - INP_RUNLOCK(inp); - continue; + if (jailed(inp->inp_socket->so_cred)) { + if (!jailed_ip4(inp->inp_socket->so_cred, &ip->ip_dst)) { + INP_RUNLOCK(inp); + continue; + } } if (last) { struct mbuf *n; @@ -360,11 +361,16 @@ ip->ip_off = 0; ip->ip_p = inp->inp_ip_p; ip->ip_len = m->m_pkthdr.len; - if (jailed(inp->inp_socket->so_cred)) - ip->ip_src.s_addr = - htonl(prison_getip(inp->inp_socket->so_cred)); - else + if (jailed(inp->inp_socket->so_cred)) { + if (prison_getip4(inp->inp_socket->so_cred, + &ip->ip_src) != 0) { + 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 { @@ -374,13 +380,10 @@ } INP_RLOCK(inp); ip = mtod(m, struct ip *); - if (jailed(inp->inp_socket->so_cred)) { - if (ip->ip_src.s_addr != - htonl(prison_getip(inp->inp_socket->so_cred))) { - INP_RUNLOCK(inp); - m_freem(m); - return (EPERM); - } + if (jailed_ip4(inp->inp_socket->so_cred, &ip->ip_src) != 1) { + INP_RUNLOCK(inp); + m_freem(m); + return (EPERM); } /* @@ -782,13 +785,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 (jailed_ip4(td->td_ucred, &addr->sin_addr) != 1) + return (EADDRNOTAVAIL); if (TAILQ_EMPTY(&ifnet) || (addr->sin_family != AF_INET && addr->sin_family != AF_IMPLINK) || --- //depot/vendor/freebsd_7/src/sys/netinet/sctp_pcb.c 2008/08/27 13:10:38 +++ //depot/user/bz/jail_7/src/sys/netinet/sctp_pcb.c 2008/09/17 20:21:25 @@ -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_ip4() call will transmute the ip address + * to the proper value. + */ + if (p && prison_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_ip6() call will transmute the ipv6 address + * to the proper value. */ + if (p && prison_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_7/src/sys/netinet/sctp_usrreq.c 2008/08/27 13:10:38 +++ //depot/user/bz/jail_7/src/sys/netinet/sctp_usrreq.c 2008/09/18 18:32:45 @@ -3949,12 +3949,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) { @@ -3964,9 +3961,10 @@ error = EINVAL; break; } - if (prison && prison_ip(td->td_ucred, 0, &(((struct sockaddr_in *)(addrs->addr))->sin_addr.s_addr))) { + if (td != NULL && prison_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; } } else if (addrs->addr->sa_family == AF_INET6) { sz = sizeof(struct sctp_getaddresses) - sizeof(struct sockaddr) + sizeof(struct sockaddr_in6); @@ -3975,7 +3973,12 @@ error = EINVAL; break; } - /* JAIL XXXX Add else here for V6 */ + if (td != NULL && prison_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; + } } sctp_bindx_add_address(so, inp, addrs->addr, addrs->sget_assoc_id, vrf_id, @@ -3987,12 +3990,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); @@ -4001,9 +4001,10 @@ error = EINVAL; break; } - if (prison && prison_ip(td->td_ucred, 0, &(((struct sockaddr_in *)(addrs->addr))->sin_addr.s_addr))) { + if (td != NULL && prison_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; } } else if (addrs->addr->sa_family == AF_INET6) { sz = sizeof(struct sctp_getaddresses) - sizeof(struct sockaddr) + sizeof(struct sockaddr_in6); @@ -4012,7 +4013,12 @@ error = EINVAL; break; } - /* JAIL XXXX Add else here for V6 */ + if (td != NULL && prison_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; + } } sctp_bindx_delete_address(so, inp, addrs->addr, addrs->sget_assoc_id, vrf_id, @@ -4104,13 +4110,29 @@ 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_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); + } } - 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); + 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); + } } SCTP_INP_INCR_REF(inp); SCTP_ASOC_CREATE_LOCK(inp); --- //depot/vendor/freebsd_7/src/sys/netinet/tcp_usrreq.c 2008/08/18 09:10:37 +++ //depot/user/bz/jail_7/src/sys/netinet/tcp_usrreq.c 2008/09/17 20:21:25 @@ -462,8 +462,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(&tcbinfo); @@ -528,6 +528,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); @@ -536,6 +540,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_7/src/sys/netinet/udp_usrreq.c 2008/09/16 19:13:38 +++ //depot/user/bz/jail_7/src/sys/netinet/udp_usrreq.c 2008/09/20 08:40:27 @@ -939,9 +939,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 @@ -1188,8 +1189,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_7/src/sys/netinet6/in6_pcb.c 2008/09/01 20:11:44 +++ //depot/user/bz/jail_7/src/sys/netinet6/in6_pcb.c 2008/09/17 20:21:25 @@ -145,6 +145,10 @@ if ((error = sa6_embedscope(sin6, ip6_use_defzone)) != 0) return(error); + if (prison_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)) { /* @@ -219,6 +223,9 @@ return (EADDRINUSE); } } + if (prison_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) ? @@ -251,12 +258,14 @@ } inp->in6p_laddr = sin6->sin6_addr; } + if (prison_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) return (e); - } - else { + } else { inp->inp_lport = lport; if (in_pcbinshash(inp) != 0) { inp->in6p_laddr = in6addr_any; @@ -310,6 +319,8 @@ if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) sin6->sin6_addr = in6addr_loopback; } + if (prison_remote_ip6(inp->inp_socket->so_cred, &sin6->sin6_addr) != 0) + return (EADDRNOTAVAIL); /* * XXX: in6_selectsrc might replace the bound local address @@ -325,7 +336,7 @@ return(error); } - if (*plocal_addr6 == 0) { + if (*plocal_addr6 == NULL) { if (error == 0) error = EADDRNOTAVAIL; return (error); @@ -690,16 +701,24 @@ head = &pcbinfo->ipi_hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->ipi_hashmask)]; LIST_FOREACH(inp, head, inp_hash) { - if ((inp->inp_vflag & INP_IPV6) == 0) + NOP_INP_RLOCK(inp); + if ((inp->inp_vflag & INP_IPV6) == 0) { + NOP_INP_RUNLOCK(inp); 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. */ + /* Those values could be NULL, really. */ + if (inp->inp_socket == NULL || cred == NULL || + inp->inp_socket->so_cred->cr_prison == + cred->cr_prison) { + NOP_INP_RUNLOCK(inp); + return (inp); + } } + NOP_INP_RUNLOCK(inp); } /* * Not found. @@ -728,8 +747,17 @@ */ LIST_FOREACH(inp, &phd->phd_pcblist, inp_portlist) { wildcard = 0; - if ((inp->inp_vflag & INP_IPV6) == 0) + NOP_INP_RLOCK(inp); + if (cred != NULL && inp->inp_socket != NULL && + inp->inp_socket->so_cred->cr_prison != + cred->cr_prison) { + NOP_INP_RUNLOCK(inp); + continue; + } + if ((inp->inp_vflag & INP_IPV6) == 0) { + NOP_INP_RUNLOCK(inp); continue; + } if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) wildcard++; if (!IN6_IS_ADDR_UNSPECIFIED( @@ -737,8 +765,10 @@ if (IN6_IS_ADDR_UNSPECIFIED(laddr)) wildcard++; else if (!IN6_ARE_ADDR_EQUAL( - &inp->in6p_laddr, laddr)) + &inp->in6p_laddr, laddr)) { + NOP_INP_RUNLOCK(inp); continue; + } } else { if (!IN6_IS_ADDR_UNSPECIFIED(laddr)) wildcard++; @@ -747,9 +777,11 @@ match = inp; matchwild = wildcard; if (matchwild == 0) { + NOP_INP_RUNLOCK(inp); break; } } + NOP_INP_RUNLOCK(inp); } } return (match); @@ -831,11 +863,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; @@ -849,47 +881,155 @@ /* * 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) { - if ((inp->inp_vflag & INP_IPV6) == 0) + NOP_INP_RLOCK(inp); + if ((inp->inp_vflag & INP_IPV6) == 0) { + NOP_INP_RUNLOCK(inp); continue; + } if (IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr, faddr) && IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, laddr) && inp->inp_fport == fport && inp->inp_lport == lport) { - /* - * Found. - */ - return (inp); + if (inp->inp_socket == NULL || + !jailed(inp->inp_socket->so_cred)) { + if (!jail_jailed_sockets_first) { + NOP_INP_RUNLOCK(inp); + return (inp); + } else if (tmpinp == NULL) + tmpinp = inp; + } else { + if (jail_jailed_sockets_first) { + NOP_INP_RUNLOCK(inp); + return (inp); + } else if (tmpinp == NULL) + tmpinp = inp; + } } + NOP_INP_RUNLOCK(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, *jail_exact = NULL; + struct ucred *cred; + int injail; + /* + * Order of socket selection: + * if (jail_jailed_sockets_first) { + * 1. jailed, non-wild. + * 2. jailed, wild. + * 3. non-jailed, non-wild. + * 4. non-jailed, wild. + * } else { + * 1. non-jailed, non-wild. + * 2. non-jailed, wild. + * 3. jailed, non-wild. + * 4. jailed, wild. + * } + */ head = &pcbinfo->ipi_hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->ipi_hashmask)]; LIST_FOREACH(inp, head, inp_hash) { - if ((inp->inp_vflag & INP_IPV6) == 0) + NOP_INP_RLOCK(inp); + if ((inp->inp_vflag & INP_IPV6) == 0) { + NOP_INP_RUNLOCK(inp); + continue; + } + + if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) || + inp->inp_lport != lport) { + NOP_INP_RUNLOCK(inp); + continue; + } + + if (faith && (inp->inp_flags & INP_FAITH) == 0){ + NOP_INP_RUNLOCK(inp); continue; - if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) && - inp->inp_lport == lport) { - if (faith && (inp->inp_flags & INP_FAITH) == 0) + } + + if (inp->inp_socket == NULL) { + cred = NULL; + injail = 0; + } else { + cred = inp->inp_socket->so_cred; + injail = jailed(cred); + } + + if (injail) { + if (jail_exact != NULL) { + /* We should have returned earlier. */ + KASSERT(!jail_jailed_sockets_first, + ("jail_exact == NULL")); + NOP_INP_RUNLOCK(inp); + continue; + } + if (!jailed_ip6(cred, laddr)) { + NOP_INP_RUNLOCK(inp); + continue; + } + } else { + if (local_exact != NULL) { + /* We should have returned earlier. */ + KASSERT(jail_jailed_sockets_first, + ("local_exact == NULL")); + NOP_INP_RUNLOCK(inp); continue; - if (IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, - laddr)) - return (inp); - else if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) + } + } + + if (IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, laddr)) { + if (injail) { + if (jail_jailed_sockets_first) { + NOP_INP_RUNLOCK(inp); + return (inp); + } else + jail_exact = inp; + } else { + if (!jail_jailed_sockets_first) { + NOP_INP_RUNLOCK(inp); + return (inp); + } else + local_exact = inp; + } + } else if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) { + if (injail) + jail_wild = inp; + else local_wild = inp; } + NOP_INP_RUNLOCK(inp); + } /* LIST_FOREACH */ + + if (jail_jailed_sockets_first) { + KASSERT(jail_exact == NULL, ("jail_exact != NULL")); + if (jail_wild != NULL) + return (jail_wild); + if (local_exact != NULL) + return (local_exact); + if (local_wild != NULL) + return (local_wild); + } else { + KASSERT(local_exact == NULL, ("local_exact != NULL")); + if (local_wild != NULL) + return (local_wild); + if (jail_exact != NULL) + return (jail_exact); + if (jail_wild != NULL) + return (jail_wild); } - return (local_wild); - } + } /* if (wildcard == INPLOOKUP_WILDCARD) */ - /* - * Not found. - */ return (NULL); } --- //depot/vendor/freebsd_7/src/sys/netinet6/in6_src.c 2008/09/01 20:11:44 +++ //depot/user/bz/jail_7/src/sys/netinet6/in6_src.c 2008/09/18 18:32:45 @@ -79,6 +79,7 @@ #include #include #include +#include #include #include @@ -224,6 +225,11 @@ if (*errorp != 0) return (NULL); } + if (cred != NULL && prison_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 || @@ -241,6 +247,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_ip6(cred, &inp->in6p_laddr, + ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0)) != 0) { + *errorp = EADDRNOTAVAIL; + return (NULL); + } return (&inp->in6p_laddr); } @@ -292,6 +303,11 @@ if (!ip6_use_deprecated && IFA6_IS_DEPRECATED(ia)) continue; + if (cred != NULL && + prison_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; @@ -765,6 +781,10 @@ INP_INFO_WLOCK_ASSERT(pcbinfo); INP_WLOCK_ASSERT(inp); + if (prison_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_7/src/sys/netinet6/raw_ip6.c 2008/09/01 20:11:44 +++ //depot/user/bz/jail_7/src/sys/netinet6/raw_ip6.c 2008/09/17 20:21:25 @@ -67,6 +67,7 @@ #include #include +#include #include #include #include @@ -166,6 +167,13 @@ !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src)) continue; INP_RLOCK(in6p); + if (jailed(in6p->inp_socket->so_cred)) { + if (!jailed_ip6(in6p->inp_socket->so_cred, + &ip6->ip6_dst)) { + INP_RUNLOCK(in6p); + continue; + } + } if (in6p->in6p_cksum != -1) { rip6stat.rip6s_isum++; if (in6_cksum(m, proto, *offp, @@ -391,6 +399,11 @@ error = EADDRNOTAVAIL; goto bad; } + if (jailed(in6p->inp_socket->so_cred)) + if (prison_getip6(in6p->inp_socket->so_cred, in6a) != 0) { + error = EPERM; + goto bad; + } ip6->ip6_src = *in6a; if (oifp && scope_ambiguous) { @@ -649,6 +662,8 @@ if (nam->sa_len != sizeof(*addr)) return (EINVAL); + if (!jailed_ip6(td->td_ucred, &addr->sin6_addr)) + return (EADDRNOTAVAIL); if (TAILQ_EMPTY(&ifnet) || addr->sin6_family != AF_INET6) return (EADDRNOTAVAIL); if ((error = sa6_embedscope(addr, ip6_use_defzone)) != 0) --- //depot/vendor/freebsd_7/src/sys/netinet6/udp6_usrreq.c 2008/09/15 21:12:18 +++ //depot/user/bz/jail_7/src/sys/netinet6/udp6_usrreq.c 2008/09/17 20:21:25 @@ -71,6 +71,7 @@ #include "opt_mac.h" #include +#include #include #include #include @@ -845,6 +846,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) { @@ -859,6 +866,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_7/src/sys/security/mac_bsdextended/mac_bsdextended.c 2008/08/31 19:12:29 +++ //depot/user/bz/jail_7/src/sys/security/mac_bsdextended/mac_bsdextended.c 2008/09/17 20:21:25 @@ -45,14 +45,15 @@ #include #include +#include #include -#include #include #include #include #include #include #include +#include #include #include #include --- //depot/vendor/freebsd_7/src/sys/sys/cpuset.h 2008/07/25 18:10:49 +++ //depot/user/bz/jail_7/src/sys/sys/cpuset.h 2008/07/26 10:18:16 @@ -131,6 +131,8 @@ #define CPU_WHICH_TID 1 /* Specifies a thread id. */ #define CPU_WHICH_PID 2 /* Specifies a process id. */ #define CPU_WHICH_CPUSET 3 /* Specifies a set id. */ +/* CPU_WHICH_IRQ */ +#define CPU_WHICH_JAIL 5 /* Specifies a jail id. */ /* * Reserved cpuset identifiers. @@ -167,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_7/src/sys/sys/jail.h 2008/07/27 19:16:48 +++ //depot/user/bz/jail_7/src/sys/sys/jail.h 2008/09/18 18:32:45 @@ -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 @@ -61,19 +114,28 @@ * (d) set only during destruction of jail, no mutex needed */ #if defined(_KERNEL) || defined(_WANT_PRISON) +struct cpuset; + struct prison { LIST_ENTRY(prison) pr_list; /* (a) all prisons */ int pr_id; /* (c) prison id */ int pr_ref; /* (p) refcount */ + int pr_state; /* (p) prison state */ + int pr_nprocs; /* (p) process count */ char pr_path[MAXPATHLEN]; /* (c) chroot path */ + struct cpuset *pr_cpuset; /* (p) cpuset */ struct vnode *pr_root; /* (c) vnode to rdir */ char pr_host[MAXHOSTNAMELEN]; /* (p) jail hostname */ - u_int32_t pr_ip; /* (c) ip addr host */ + char pr_name[MAXHOSTNAMELEN]; /* (c) admin jail name */ void *pr_linux; /* (p) linux abi */ int pr_securelevel; /* (p) securelevel */ struct task pr_task; /* (d) destroy task */ struct mtx pr_mtx; void **pr_slots; /* (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 */ @@ -89,6 +151,7 @@ extern int jail_getfsstat_jailrootonly; extern int jail_allow_raw_sockets; extern int jail_chflags_allowed; +extern int jail_jailed_sockets_first; LIST_HEAD(prisonlist, prison); extern struct prisonlist allprison; @@ -101,6 +164,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); @@ -109,12 +174,21 @@ struct statfs *sp); struct prison *prison_find(int prid); void prison_free(struct prison *pr); -u_int32_t prison_getip(struct ucred *cred); void prison_hold(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_ip4(struct ucred *cred, struct in_addr *ia); +int prison_remote_ip4(struct ucred *cred, struct in_addr *ia); +int jailed_ip4(struct ucred *cred, struct in_addr *ia); +#ifdef INET6 +int prison_getip6(struct ucred *, struct in6_addr *); +int prison_ip6(struct ucred *, struct in6_addr *, int); +int prison_remote_ip6(struct ucred *, struct in6_addr *); +int jailed_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_7/src/usr.bin/cpuset/cpuset.1 2008/07/25 18:10:49 +++ //depot/user/bz/jail_7/src/usr.bin/cpuset/cpuset.1 2008/07/26 10:18:16 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD: src/usr.bin/cpuset/cpuset.1,v 1.3.2.1 2008/07/25 17:46:01 jhb Exp $ .\" -.Dd March 2, 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_7/src/usr.bin/cpuset/cpuset.c 2008/07/25 18:10:49 +++ //depot/user/bz/jail_7/src/usr.bin/cpuset/cpuset.c 2008/07/26 10:18:16 @@ -46,6 +46,7 @@ int cflag; int gflag; int iflag; +int jflag; int lflag; int pflag; int rflag; @@ -149,7 +150,7 @@ printf("\n"); } -const char *whichnames[] = { NULL, "tid", "pid", "cpuset" }; +const char *whichnames[] = { NULL, "tid", "pid", "cpuset", "N/A", "jail" }; const char *levelnames[] = { NULL, " root", " cpuset", "" }; static void @@ -194,7 +195,7 @@ level = CPU_LEVEL_WHICH; which = CPU_WHICH_PID; id = pid = tid = setid = -1; - while ((ch = getopt(argc, argv, "cgil:p:rs:t:")) != -1) { + while ((ch = getopt(argc, argv, "cgij:l:p:rs:t:")) != -1) { switch (ch) { case 'c': if (rflag) @@ -208,6 +209,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); @@ -243,7 +249,7 @@ if (argc || lflag) usage(); /* Only one identity specifier. */ - if (sflag + pflag + tflag > 1) + if (jflag + sflag + pflag + tflag > 1) usage(); if (iflag) printsetid(); @@ -257,7 +263,7 @@ * The user wants to run a command with a set and possibly cpumask. */ if (argc) { - if (pflag | rflag | tflag) + if (pflag | rflag | tflag | jflag) usage(); if (sflag) { if (cpuset_setid(CPU_WHICH_PID, -1, setid)) @@ -283,7 +289,7 @@ if (!lflag && !sflag) usage(); /* You can only set a mask on a thread. */ - if (tflag && (sflag || pflag)) + if (tflag && (sflag | pflag | jflag)) usage(); if (pflag && sflag) { if (cpuset_setid(CPU_WHICH_PID, pid, setid)) @@ -313,8 +319,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]\n"); + " cpuset [-cr] [-l cpu-list] [-j jailid | -p pid | -t tid | -s setid]\n"); fprintf(stderr, - " cpuset [-cgir] [-p pid | -t tid | -s setid]\n"); + " cpuset [-cgir] [-j jailid | -p pid | -t tid | -s setid]\n"); exit(1); } --- //depot/vendor/freebsd_7/src/usr.sbin/jail/Makefile 2007/11/15 02:59:41 +++ //depot/user/bz/jail_7/src/usr.sbin/jail/Makefile 2008/06/16 17:25:27 @@ -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_7/src/usr.sbin/jail/jail.8 2007/11/15 02:59:41 +++ //depot/user/bz/jail_7/src/usr.sbin/jail/jail.8 2008/07/24 21:15:50 @@ -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 June 1, 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,11 @@ 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. +For IPv4 the first address given will be kept as the primary address as +long as we are lacking proper source address selection for unbound +sockets. .It Ar command Pathname of the program which is to be executed. .El @@ -179,7 +201,9 @@ 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 depending on the +.Va security.jail.jail_jailed_sockets_first +sysctl. This means changing .Xr inetd 8 to only listen on the @@ -555,6 +579,13 @@ 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. +.It Va security.jail.jail_jailed_sockets_first +If zero, sockets of the base system are checked first; if non-zero +sockets inside a jail will take precedence of sockets in the base +system. .El .Pp The read-only sysctl variable @@ -622,6 +653,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_7/src/usr.sbin/jail/jail.c 2007/11/15 02:59:41 +++ //depot/user/bz/jail_7/src/usr.sbin/jail/jail.c 2008/06/16 17:25:27 @@ -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_7/src/usr.sbin/jexec/Makefile 2007/11/15 02:59:41 +++ //depot/user/bz/jail_7/src/usr.sbin/jexec/Makefile 2008/06/16 17:25:27 @@ -6,4 +6,6 @@ LDADD= -lutil WARNS?= 6 +CFLAGS+= -DSUPPORT_OLD_XPRISON + .include --- //depot/vendor/freebsd_7/src/usr.sbin/jexec/jexec.8 2007/11/15 02:59:41 +++ //depot/user/bz/jail_7/src/usr.sbin/jexec/jexec.8 2008/06/16 17:25:27 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD: src/usr.sbin/jexec/jexec.8,v 1.4 2006/09/29 17:57:02 ru Exp $ .\" -.Dd April 19, 2006 +.Dd June 16, 2008 .Dt JEXEC 8 .Os .Sh NAME @@ -34,17 +34,36 @@ .Sh SYNOPSIS .Nm .Op Fl u Ar username | Fl U Ar username +.Op Fl n Ar jailname .Ar jid command ... .Sh DESCRIPTION The .Nm utility executes .Ar command -inside the jail identified by -.Ar jid . +inside the jail identified by either +.Ar jailname +or +.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 @@ -63,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_7/src/usr.sbin/jexec/jexec.c 2007/11/15 02:59:41 +++ //depot/user/bz/jail_7/src/usr.sbin/jexec/jexec.c 2008/07/24 20:56:17 @@ -29,16 +29,171 @@ #include #include +#include #include #include #include #include #include +#include #include #include static void usage(void); +#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); \ if (pwd == NULL) { \ @@ -63,12 +218,17 @@ struct passwd *pwd = NULL; gid_t groups[NGROUPS]; int ch, ngroups, uflag, Uflag; - char *username; + char *jailname, *username; + ch = uflag = Uflag = 0; - username = NULL; + jailname = username = NULL; + jid = -1; - while ((ch = getopt(argc, argv, "u:U:")) != -1) { + while ((ch = getopt(argc, argv, "i:n:u:U:")) != -1) { switch (ch) { + case 'n': + jailname = optarg; + break; case 'u': username = optarg; uflag = 1; @@ -85,11 +245,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; - 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) @@ -117,6 +288,6 @@ fprintf(stderr, "%s%s\n", "usage: jexec [-u username | -U username]", - " jid command ..."); + " [-n jailname] jid command ..."); exit(1); } --- //depot/vendor/freebsd_7/src/usr.sbin/jls/Makefile 2007/11/15 02:59:41 +++ //depot/user/bz/jail_7/src/usr.sbin/jls/Makefile 2008/06/16 17:25:27 @@ -4,4 +4,6 @@ MAN= jls.8 WARNS?= 6 +CFLAGS+= -DSUPPORT_OLD_XPRISON + .include --- //depot/vendor/freebsd_7/src/usr.sbin/jls/jls.8 2007/11/15 02:59:41 +++ //depot/user/bz/jail_7/src/usr.sbin/jls/jls.8 2008/09/18 18:32:45 @@ -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_7/src/usr.sbin/jls/jls.c 2007/11/15 02:59:41 +++ //depot/user/bz/jail_7/src/usr.sbin/jls/jls.c 2008/09/18 18:32:45 @@ -27,57 +27,217 @@ */ #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; + char ch; + unsigned flags; + size_t i, j, len; + void *p, *q; + int version; + + 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); }