Index: lib/libc/sys/jail.2 =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/lib/libc/sys/jail.2,v retrieving revision 1.28 diff -u -p -r1.28 jail.2 --- lib/libc/sys/jail.2 9 Feb 2005 18:03:14 -0000 1.28 +++ lib/libc/sys/jail.2 18 Jun 2008 22:30:42 -0000 @@ -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 @@ The argument is a pointer to a structure .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 @@ This can be changed from the inside of the prison. .Pp The -.Dq Li ip_number -can be set to the IP number assigned to the prison. +.Dq Li 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 Index: sys/compat/freebsd32/freebsd32.h =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/compat/freebsd32/freebsd32.h,v retrieving revision 1.6.2.1 diff -u -p -r1.6.2.1 freebsd32.h --- sys/compat/freebsd32/freebsd32.h 19 Dec 2007 20:37:53 -0000 1.6.2.1 +++ sys/compat/freebsd32/freebsd32.h 18 Jun 2008 22:30:54 -0000 @@ -153,6 +153,24 @@ struct stat32 { unsigned int :(8 / 2) * (16 - (int)sizeof(struct timespec32)); }; +struct jail32_v0 { + u_int32_t version; + uint32_t path; + uint32_t hostname; + u_int32_t ip_number; +}; + +struct jail32 { + uint32_t version; + uint32_t path; + uint32_t hostname; + uint32_t jailname; + uint32_t ip4s; + uint32_t ip6s; + uint32_t ip4; + uint32_t ip6; +}; + struct sigaction32 { u_int32_t sa_u; int sa_flags; Index: sys/compat/freebsd32/freebsd32_misc.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/compat/freebsd32/freebsd32_misc.c,v retrieving revision 1.67.2.3 diff -u -p -r1.67.2.3 freebsd32_misc.c --- sys/compat/freebsd32/freebsd32_misc.c 20 Dec 2007 19:43:55 -0000 1.67.2.3 +++ sys/compat/freebsd32/freebsd32_misc.c 18 Jun 2008 22:30:54 -0000 @@ -38,6 +38,7 @@ __FBSDID("$FreeBSD: src/sys/compat/freeb #include #include #include +#include #include #include #include @@ -1963,6 +1964,72 @@ done2: } int +freebsd32_jail(struct thread *td, struct freebsd32_jail_args *uap) +{ + uint32_t version; + int error; + struct jail j; + + error = copyin(uap->jail, &version, sizeof(uint32_t)); + if (error) + return (error); + + switch (version) { + case 0: + { + /* FreeBSD single IPv4 jails. */ + struct jail32_v0 j32_v0; + + bzero(&j, sizeof(struct jail)); + error = copyin(uap->jail, &j32_v0, sizeof(struct jail32_v0)); + if (error) + return (error); + + CP(j32_v0, j, version); + PTRIN_CP(j32_v0, j, path); + PTRIN_CP(j32_v0, j, hostname); + j.ip4s = j32_v0.ip_number; + break; + } + case 1: + /* + * Version 1 was used by multi-IPv4 jail implementations + * that never made it into the official kernel. + */ + return (EINVAL); + /* 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; Index: sys/compat/freebsd32/freebsd32_proto.h =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/compat/freebsd32/freebsd32_proto.h,v retrieving revision 1.78.2.1 diff -u -p -r1.78.2.1 freebsd32_proto.h --- sys/compat/freebsd32/freebsd32_proto.h 19 Dec 2007 20:48:53 -0000 1.78.2.1 +++ sys/compat/freebsd32/freebsd32_proto.h 18 Jun 2008 22:30:54 -0000 @@ -223,6 +223,9 @@ struct freebsd32_modstat_args { 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 *)]; @@ -364,6 +367,7 @@ int freebsd32_lutimes(struct thread *, s 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 *); @@ -567,6 +571,7 @@ int freebsd6_freebsd32_ftruncate(struct #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 Index: sys/compat/freebsd32/freebsd32_syscall.h =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/compat/freebsd32/freebsd32_syscall.h,v retrieving revision 1.76.2.1 diff -u -p -r1.76.2.1 freebsd32_syscall.h --- sys/compat/freebsd32/freebsd32_syscall.h 19 Dec 2007 20:48:53 -0000 1.76.2.1 +++ sys/compat/freebsd32/freebsd32_syscall.h 18 Jun 2008 22:30:54 -0000 @@ -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 Index: sys/compat/freebsd32/freebsd32_syscalls.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/compat/freebsd32/freebsd32_syscalls.c,v retrieving revision 1.67.2.1 diff -u -p -r1.67.2.1 freebsd32_syscalls.c --- sys/compat/freebsd32/freebsd32_syscalls.c 19 Dec 2007 20:48:53 -0000 1.67.2.1 +++ sys/compat/freebsd32/freebsd32_syscalls.c 18 Jun 2008 22:30:54 -0000 @@ -345,7 +345,7 @@ const char *freebsd32_syscallnames[] = { "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 */ Index: sys/compat/freebsd32/freebsd32_sysent.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/compat/freebsd32/freebsd32_sysent.c,v retrieving revision 1.77.2.1 diff -u -p -r1.77.2.1 freebsd32_sysent.c --- sys/compat/freebsd32/freebsd32_sysent.c 19 Dec 2007 20:48:53 -0000 1.77.2.1 +++ sys/compat/freebsd32/freebsd32_sysent.c 18 Jun 2008 22:30:54 -0000 @@ -377,7 +377,7 @@ struct sysent freebsd32_sysent[] = { { 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 */ Index: sys/compat/freebsd32/syscalls.master =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/compat/freebsd32/syscalls.master,v retrieving revision 1.91.2.2 diff -u -p -r1.91.2.2 syscalls.master --- sys/compat/freebsd32/syscalls.master 19 Dec 2007 20:43:41 -0000 1.91.2.2 +++ sys/compat/freebsd32/syscalls.master 18 Jun 2008 22:30:54 -0000 @@ -566,7 +566,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); } Index: sys/compat/linux/linux_misc.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/compat/linux/linux_misc.c,v retrieving revision 1.214 diff -u -p -r1.214 linux_misc.c --- sys/compat/linux/linux_misc.c 28 Aug 2007 12:26:35 -0000 1.214 +++ sys/compat/linux/linux_misc.c 18 Jun 2008 22:30:55 -0000 @@ -39,7 +39,6 @@ __FBSDID("$FreeBSD: src/sys/compat/linux #if defined(__i386__) #include #endif -#include #include #include #include @@ -50,6 +49,7 @@ __FBSDID("$FreeBSD: src/sys/compat/linux #include #include #include +#include /* Must come after sys/proc.h */ #include #include #include Index: sys/ddb/db_ps.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/ddb/db_ps.c,v retrieving revision 1.66 diff -u -p -r1.66 db_ps.c --- sys/ddb/db_ps.c 17 Sep 2007 05:27:19 -0000 1.66 +++ sys/ddb/db_ps.c 18 Jun 2008 22:30:56 -0000 @@ -32,10 +32,10 @@ __FBSDID("$FreeBSD: src/sys/ddb/db_ps.c, #include #include -#include #include #include #include +#include /* Must come after sys/proc.h */ #include #include #include Index: sys/kern/kern_jail.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/kern/kern_jail.c,v retrieving revision 1.70 diff -u -p -r1.70 kern_jail.c --- sys/kern/kern_jail.c 13 Apr 2007 23:54:22 -0000 1.70 +++ sys/kern/kern_jail.c 18 Jun 2008 22:31:00 -0000 @@ -10,6 +10,8 @@ #include __FBSDID("$FreeBSD: src/sys/kern/kern_jail.c,v 1.70 2007/04/13 23:54:22 pjd Exp $"); +#include "opt_ddb.h" +#include "opt_inet6.h" #include "opt_mac.h" #include @@ -35,6 +37,12 @@ __FBSDID("$FreeBSD: src/sys/kern/kern_ja #include #include #include +#ifdef DDB +#include +#ifdef INET6 +#include +#endif /* INET6 */ +#endif /* DDB */ #include @@ -51,7 +59,7 @@ SYSCTL_INT(_security_jail, OID_AUTO, set 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 @@ SYSCTL_INT(_security_jail, OID_AUTO, mou &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,129 @@ init_prison(void *data __unused) SYSINIT(prison, SI_SUB_INTRINSIC, SI_ORDER_ANY, init_prison, NULL); +static int +qcmp_v4(const void *ip1, const void *ip2) +{ + const struct in_addr *iaa, *iab; + + iaa = (const struct in_addr *)ip1; + iab = (const struct in_addr *)ip2; + + return (iaa->s_addr - iab->s_addr); +} + +#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; +#define IN6_ADDR_QUAD_COMPARE(a, b, i) \ + *(const u_int32_t *)(const void *)(&(a)->s6_addr[(i)]) - \ + *(const u_int32_t *)(const void *)(&(b)->s6_addr[(i)]) + + i = 0; + do { + rc = IN6_ADDR_QUAD_COMPARE(ia6a, ia6b, i); + i += 4; + } while (i <= 12 && rc == 0); + return (rc); +#undef IN6_ARE_ADDR_QUAD_COMPARE +} +#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 +263,116 @@ SYSINIT(prison, SI_SUB_INTRINSIC, SI_ORD 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); + pr->pr_state = PRISON_STATE_ALIVE; + 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 +385,25 @@ jail(struct thread *td, struct jail_args 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) @@ -214,6 +463,10 @@ e_dropvnref: 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); } @@ -344,8 +597,11 @@ prison_complete(void *context, int pendi 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); } @@ -360,79 +616,289 @@ prison_hold(struct prison *pr) mtx_unlock(&pr->pr_mtx); } -u_int32_t -prison_getip(struct ucred *cred) + +/* + * 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); + } + + if (ia0.s_addr == INADDR_ANY || jailed_ip4(cred, ia)) + return (0); - return (cred->cr_prison->pr_ip); + return (1); } +/* + * Rewrite destination address in case we will connect to loopback address. + * + * Returns 0 on success, 1 on error. + * Address passed in in NBO and returned in NBO. + */ int -prison_ip(struct ucred *cred, int flag, u_int32_t *ip) +prison_remote_ip4(struct ucred *cred, struct in_addr *ia) { - u_int32_t tmp; + + KASSERT(cred != NULL, ("%s: cred is NULL", __func__)); + KASSERT(ia != NULL, ("%s: ia is NULL", __func__)); if (!jailed(cred)) return (0); - if (flag) - tmp = *ip; - else - tmp = ntohl(*ip); - if (tmp == INADDR_ANY) { - if (flag) - *ip = cred->cr_prison->pr_ip; - else - *ip = htonl(cred->cr_prison->pr_ip); + if (cred->cr_prison->pr_ip4 == NULL) + return (1); + + if (ntohl(ia->s_addr) == INADDR_LOOPBACK) { + ia->s_addr = cred->cr_prison->pr_ip4[0].s_addr; return (0); } - if (tmp == INADDR_LOOPBACK) { - if (flag) - *ip = cred->cr_prison->pr_ip; + + /* 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 +jailed_ip4(struct ucred *cred, struct in_addr *ia) +{ + int i, a, z; + + 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); + + /* 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; + if (cred->cr_prison->pr_ip4[i+1].s_addr > ia->s_addr) + z = i - 1; + else if (cred->cr_prison->pr_ip4[i+1].s_addr < ia->s_addr) + 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. + * + * Returns 0 on success, 1 on error. + */ +int +prison_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); } - 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; + struct sockaddr_in6 *sai6; 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); } @@ -937,6 +1403,8 @@ sysctl_jail_list(SYSCTL_HANDLER_ARGS) { struct xprison *xp, *sxp; struct prison *pr; + char *p; + size_t len; int count, error; if (jailed(req->td->td_ucred)) @@ -948,21 +1416,47 @@ sysctl_jail_list(SYSCTL_HANDLER_ARGS) return (0); } - sxp = xp = malloc(sizeof(*xp) * count, M_TEMP, M_WAITOK | M_ZERO); + len = sizeof(*xp) * count; + LIST_FOREACH(pr, &allprison, pr_list) { + 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; 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); } @@ -982,3 +1476,51 @@ sysctl_jail_jailed(SYSCTL_HANDLER_ARGS) } SYSCTL_PROC(_security_jail, OID_AUTO, jailed, CTLTYPE_INT | CTLFLAG_RD, NULL, 0, sysctl_jail_jailed, "I", "Process in jail?"); + +#ifdef DDB +DB_SHOW_COMMAND(jails, db_show_jails) +{ + struct prison *pr; + struct in_addr ia; +#ifdef INET6 + char ip6buf[INET6_ADDRSTRLEN]; +#endif + const char *state; + int i; + + db_printf( + " JID pr_ref pr_ip4s pr_ip6s\n"); + db_printf( + " Hostname Path\n"); + db_printf( + " Name State\n"); + db_printf( + " IP Address(es)\n"); + LIST_FOREACH(pr, &allprison, pr_list) { + db_printf("%6d %6d %7d %7d\n", + pr->pr_id, pr->pr_ref, + 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); + 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 */ + Index: sys/kern/kern_priv.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/kern/kern_priv.c,v retrieving revision 1.4 diff -u -p -r1.4 kern_priv.c --- sys/kern/kern_priv.c 2 Jul 2007 14:03:29 -0000 1.4 +++ sys/kern/kern_priv.c 18 Jun 2008 22:31:00 -0000 @@ -32,10 +32,10 @@ #include "opt_mac.h" #include -#include #include #include #include +#include /* Must come after sys/proc.h */ #include #include Index: sys/kern/uipc_socket.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/kern/uipc_socket.c,v retrieving revision 1.302.4.1 diff -u -p -r1.302.4.1 uipc_socket.c --- sys/kern/uipc_socket.c 2 Feb 2008 12:44:13 -0000 1.302.4.1 +++ sys/kern/uipc_socket.c 18 Jun 2008 22:31:04 -0000 @@ -98,6 +98,7 @@ __FBSDID("$FreeBSD: src/sys/kern/uipc_socket.c,v 1.302.4.1 2008/02/02 12:44:13 rwatson Exp $"); #include "opt_inet.h" +#include "opt_inet6.h" #include "opt_mac.h" #include "opt_zero.h" #include "opt_compat.h" @@ -346,6 +347,9 @@ socreate(int dom, struct socket **aso, i 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); } Index: sys/net/if.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/net/if.c,v retrieving revision 1.273 diff -u -p -r1.273 if.c --- sys/net/if.c 27 Jul 2007 11:59:57 -0000 1.273 +++ sys/net/if.c 18 Jun 2008 22:31:08 -0000 @@ -2117,7 +2117,7 @@ again: struct sockaddr *sa = ifa->ifa_addr; if (jailed(curthread->td_ucred) && - prison_if(curthread->td_ucred, sa)) + !prison_if(curthread->td_ucred, sa)) continue; addrs++; #ifdef COMPAT_43 Index: sys/net/rtsock.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/net/rtsock.c,v retrieving revision 1.143 diff -u -p -r1.143 rtsock.c --- sys/net/rtsock.c 8 Sep 2007 19:28:45 -0000 1.143 +++ sys/net/rtsock.c 18 Jun 2008 22:31:09 -0000 @@ -33,11 +33,11 @@ #include #include #include -#include #include #include #include #include +#include /* Must come after sys/proc.h */ #include #include #include @@ -458,11 +458,17 @@ route_output(struct mbuf *m, struct sock info.rti_info[RTAX_IFP] = ifp->if_addr->ifa_addr; if (jailed(so->so_cred)) { + struct in_addr ia; + if (prison_getip4(so->so_cred, + &ia) != 0) { + RT_UNLOCK(rt); + senderr(ESRCH); + } 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)); + ia.s_addr; info.rti_info[RTAX_IFA] = (struct sockaddr *)&jail; } else @@ -1145,7 +1151,7 @@ sysctl_iflist(int af, struct walkarg *w) if (af && af != ifa->ifa_addr->sa_family) continue; if (jailed(curthread->td_ucred) && - prison_if(curthread->td_ucred, ifa->ifa_addr)) + !prison_if(curthread->td_ucred, ifa->ifa_addr)) continue; info.rti_info[RTAX_IFA] = ifa->ifa_addr; info.rti_info[RTAX_NETMASK] = ifa->ifa_netmask; @@ -1193,7 +1199,7 @@ sysctl_ifmalist(int af, struct walkarg * 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] = Index: sys/netinet/in_pcb.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/netinet/in_pcb.c,v retrieving revision 1.196.4.1 diff -u -p -r1.196.4.1 in_pcb.c --- sys/netinet/in_pcb.c 22 Dec 2007 20:54:46 -0000 1.196.4.1 +++ sys/netinet/in_pcb.c 18 Jun 2008 22:31:09 -0000 @@ -275,7 +275,7 @@ in_pcbbind_setup(struct inpcb *inp, stru 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; INP_INFO_WLOCK_ASSERT(pcbinfo); @@ -300,9 +300,8 @@ in_pcbbind_setup(struct inpcb *inp, stru 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) @@ -337,14 +336,11 @@ in_pcbbind_setup(struct inpcb *inp, stru 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(inp->inp_pcbinfo, - sin->sin_addr, lport, - prison ? 0 : INPLOOKUP_WILDCARD); + t = in_pcblookup_local(pcbinfo, sin->sin_addr, + lport, INPLOOKUP_WILDCARD, cred); /* * XXX * This entire block sorely needs a rewrite. @@ -361,10 +357,10 @@ in_pcbbind_setup(struct inpcb *inp, stru 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); + lport, wild, cred); if (t && (t->inp_vflag & INP_TIMEWAIT)) { /* * XXXRW: If an incpb has had its timewait @@ -396,9 +392,8 @@ in_pcbbind_setup(struct inpcb *inp, stru 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 */ @@ -461,7 +456,7 @@ in_pcbbind_setup(struct inpcb *inp, stru *lastport = first; lport = htons(*lastport); } while (in_pcblookup_local(pcbinfo, laddr, lport, - wild)); + wild, cred)); } else { /* * counting up @@ -479,10 +474,10 @@ in_pcbbind_setup(struct inpcb *inp, stru *lastport = first; lport = htons(*lastport); } while (in_pcblookup_local(pcbinfo, laddr, lport, - wild)); + wild, cred)); } } - if (prison_ip(cred, 0, &laddr.s_addr)) + if (prison_ip4(cred, &laddr)) return (EINVAL); *laddrp = laddr.s_addr; *lportp = lport; @@ -561,7 +556,7 @@ in_pcbconnect_setup(struct inpcb *inp, s 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; @@ -582,8 +577,11 @@ in_pcbconnect_setup(struct inpcb *inp, s fport = sin->sin_port; socred = inp->inp_socket->so_cred; if (laddr.s_addr == INADDR_ANY && jailed(socred)) { + if (prison_getip4(socred, &jailia) != 0) + return (EADDRNOTAVAIL); bzero(&sa, sizeof(sa)); - sa.sin_addr.s_addr = htonl(prison_getip(socred)); + sa.sin_addr.s_addr = jailia.s_addr; + sa.sin_port = lport; sa.sin_len = sizeof(sa); sa.sin_family = AF_INET; error = in_pcbbind_setup(inp, (struct sockaddr *)&sa, @@ -599,15 +597,30 @@ in_pcbconnect_setup(struct inpcb *inp, s * 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) { + if (cred != NULL && jailed(cred)) { + if (prison_getip4(cred, &jailia) != 0) + return (EADDRNOTAVAIL); + laddr.s_addr = jailia.s_addr; + } + } + if (laddr.s_addr == INADDR_ANY) { + ia = (struct in_ifaddr *)0; /* * If route is known our src addr is taken from the i/f, @@ -893,7 +906,7 @@ in_pcbpurgeif0(struct inpcbinfo *pcbinfo #define INP_LOOKUP_MAPPED_PCB_COST 3 struct inpcb * in_pcblookup_local(struct inpcbinfo *pcbinfo, struct in_addr laddr, - u_int lport_arg, int wild_okay) + u_short lport, int wild_okay, struct ucred *cred) { struct inpcb *inp; #ifdef INET6 @@ -902,7 +915,6 @@ in_pcblookup_local(struct inpcbinfo *pcb int matchwild = 3; #endif int wildcard; - u_short lport = lport_arg; INP_INFO_WLOCK_ASSERT(pcbinfo); @@ -915,18 +927,26 @@ in_pcblookup_local(struct inpcbinfo *pcb 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. @@ -955,9 +975,18 @@ in_pcblookup_local(struct inpcbinfo *pcb */ 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 @@ -979,8 +1008,11 @@ in_pcblookup_local(struct inpcbinfo *pcb 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++; @@ -989,9 +1021,11 @@ in_pcblookup_local(struct inpcbinfo *pcb match = inp; matchwild = wildcard; if (matchwild == 0) { + NOP_INP_RUNLOCK(inp); break; } } + NOP_INP_RUNLOCK(inp); } } return (match); @@ -1008,7 +1042,7 @@ in_pcblookup_hash(struct inpcbinfo *pcbi struct ifnet *ifp) { struct inpcbhead *head; - struct inpcb *inp; + struct inpcb *inp, *tmpinp; u_short fport = fport_arg, lport = lport_arg; INP_INFO_RLOCK_ASSERT(pcbinfo); @@ -1016,60 +1050,175 @@ in_pcblookup_hash(struct inpcbinfo *pcbi /* * 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 (inp->inp_laddr.s_addr == laddr.s_addr) - return (inp); - else if (inp->inp_laddr.s_addr == INADDR_ANY) { + } + 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) { + 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); } Index: sys/netinet/in_pcb.h =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/netinet/in_pcb.h,v retrieving revision 1.100.2.1 diff -u -p -r1.100.2.1 in_pcb.h --- sys/netinet/in_pcb.h 7 Dec 2007 05:46:08 -0000 1.100.2.1 +++ sys/netinet/in_pcb.h 18 Jun 2008 22:31:09 -0000 @@ -354,6 +354,15 @@ struct inpcbinfo { #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; @@ -381,7 +390,7 @@ void in_pcbfree(struct inpcb *); int in_pcbinshash(struct inpcb *); struct inpcb * in_pcblookup_local(struct inpcbinfo *, - struct in_addr, u_int, int); + struct in_addr, u_short, int, struct ucred *); struct inpcb * in_pcblookup_hash(struct inpcbinfo *, struct in_addr, u_int, struct in_addr, u_int, int, struct ifnet *); Index: sys/netinet/raw_ip.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/netinet/raw_ip.c,v retrieving revision 1.180 diff -u -p -r1.180 raw_ip.c --- sys/netinet/raw_ip.c 7 Oct 2007 20:44:23 -0000 1.180 +++ sys/netinet/raw_ip.c 18 Jun 2008 22:31:09 -0000 @@ -37,13 +37,13 @@ __FBSDID("$FreeBSD: src/sys/netinet/raw_ #include "opt_mac.h" #include -#include #include #include #include #include #include #include +#include /* Must come after sys/proc.h */ #include #include #include @@ -226,8 +226,7 @@ rip_input(struct mbuf *m, int off) inp->inp_faddr.s_addr != ip->ip_src.s_addr) goto docontinue; if (jailed(inp->inp_socket->so_cred)) - if (htonl(prison_getip(inp->inp_socket->so_cred)) != - ip->ip_dst.s_addr) + if (!jailed_ip4(inp->inp_socket->so_cred, &ip->ip_dst)) goto docontinue; if (last) { struct mbuf *n; @@ -287,11 +286,16 @@ rip_output(struct mbuf *m, struct socket 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_UNLOCK(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 { @@ -301,13 +305,10 @@ rip_output(struct mbuf *m, struct socket } INP_LOCK(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_UNLOCK(inp); - m_freem(m); - return (EPERM); - } + if (jailed_ip4(inp->inp_socket->so_cred, &ip->ip_src) != 1) { + INP_UNLOCK(inp); + m_freem(m); + return (EPERM); } /* don't allow both user specified and setsockopt options, and don't allow packet length sizes that will crash */ @@ -717,13 +718,8 @@ rip_bind(struct socket *so, struct socka 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) || Index: sys/netinet/sctp_pcb.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/netinet/sctp_pcb.c,v retrieving revision 1.62.2.2.2.1 diff -u -p -r1.62.2.2.2.1 sctp_pcb.c --- sys/netinet/sctp_pcb.c 31 Jan 2008 17:21:50 -0000 1.62.2.2.2.1 +++ sys/netinet/sctp_pcb.c 18 Jun 2008 22:31:09 -0000 @@ -2381,7 +2381,6 @@ sctp_inpcb_bind(struct socket *so, struc struct sctp_inpcb *inp, *inp_tmp; struct inpcb *ip_inp; int bindall; - int prison = 0; uint16_t lport; int error; uint32_t vrf_id; @@ -2408,9 +2407,6 @@ sctp_inpcb_bind(struct socket *so, struc if (p == NULL) panic("null proc/thread"); #endif - if (p && jailed(p->td_ucred)) { - prison = 1; - } if (addr != NULL) { if (addr->sa_family == AF_INET) { struct sockaddr_in *sin; @@ -2426,17 +2422,13 @@ sctp_inpcb_bind(struct socket *so, struc } 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; @@ -2453,10 +2445,13 @@ sctp_inpcb_bind(struct socket *so, struc } 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) != 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 */ Index: sys/netinet/sctp_usrreq.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/netinet/sctp_usrreq.c,v retrieving revision 1.48.2.3 diff -u -p -r1.48.2.3 sctp_usrreq.c --- sys/netinet/sctp_usrreq.c 9 Dec 2007 20:23:47 -0000 1.48.2.3 +++ sys/netinet/sctp_usrreq.c 18 Jun 2008 22:31:09 -0000 @@ -516,6 +516,13 @@ sctp_attach(struct socket *so, int proto uint32_t flags; #endif + /* + * XXX-BZ SCTP inside jails needs more review. + * Once we are sure everything is safe, we can drop this again. + */ + if (jailed(p->td_ucred)) + return EOPNOTSUPP; + inp = (struct sctp_inpcb *)so->so_pcb; if (inp != 0) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); @@ -3844,12 +3851,9 @@ sctp_setopt(struct socket *so, int optna 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) { @@ -3859,9 +3863,10 @@ sctp_setopt(struct socket *so, int optna 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); @@ -3870,7 +3875,11 @@ sctp_setopt(struct socket *so, int optna 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_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, @@ -3882,12 +3891,9 @@ sctp_setopt(struct socket *so, int optna 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); @@ -3896,9 +3902,10 @@ sctp_setopt(struct socket *so, int optna 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); @@ -3907,7 +3914,11 @@ sctp_setopt(struct socket *so, int optna 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_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, @@ -4000,13 +4011,29 @@ sctp_connect(struct socket *so, struct s 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_ASOC_CREATE_LOCK(inp); create_lock_on = 1; Index: sys/netinet/tcp_usrreq.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/netinet/tcp_usrreq.c,v retrieving revision 1.163.2.1.2.1 diff -u -p -r1.163.2.1.2.1 tcp_usrreq.c --- sys/netinet/tcp_usrreq.c 26 Jan 2008 13:57:33 -0000 1.163.2.1.2.1 +++ sys/netinet/tcp_usrreq.c 18 Jun 2008 22:31:09 -0000 @@ -460,8 +460,8 @@ tcp_usr_connect(struct socket *so, struc 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); @@ -526,6 +526,10 @@ tcp6_usr_connect(struct socket *so, stru 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(tp); @@ -534,6 +538,10 @@ tcp6_usr_connect(struct socket *so, stru 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(tp); Index: sys/netinet/udp_usrreq.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/netinet/udp_usrreq.c,v retrieving revision 1.218 diff -u -p -r1.218 udp_usrreq.c --- sys/netinet/udp_usrreq.c 7 Oct 2007 20:44:24 -0000 1.218 +++ sys/netinet/udp_usrreq.c 18 Jun 2008 22:31:09 -0000 @@ -868,9 +868,10 @@ udp_output(struct inpcb *inp, struct mbu if (addr) { sin = (struct sockaddr_in *)addr; - 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 (inp->inp_faddr.s_addr != INADDR_ANY) { error = EISCONN; goto release; @@ -1077,8 +1078,11 @@ udp_connect(struct socket *so, struct so 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_UNLOCK(inp); + INP_INFO_WUNLOCK(&udbinfo); + return (EAFNOSUPPORT); + } error = in_pcbconnect(inp, nam, td->td_ucred); if (error == 0) soisconnected(so); Index: sys/netinet6/icmp6.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/netinet6/icmp6.c,v retrieving revision 1.80 diff -u -p -r1.80 icmp6.c --- sys/netinet6/icmp6.c 5 Jul 2007 16:29:39 -0000 1.80 +++ sys/netinet6/icmp6.c 18 Jun 2008 22:31:09 -0000 @@ -2125,7 +2125,7 @@ icmp6_reflect(struct mbuf *m, size_t off sin6.sin6_addr = ip6->ip6_dst; /* zone ID should be embedded */ bzero(&ro, sizeof(ro)); - src = in6_selectsrc(&sin6, NULL, NULL, &ro, NULL, &outif, &e); + src = in6_selectsrc(&sin6, NULL, NULL, NULL, &ro, &outif, &e); if (ro.ro_rt) RTFREE(ro.ro_rt); /* XXX: we could use this */ if (src == NULL) { Index: sys/netinet6/in6_pcb.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/netinet6/in6_pcb.c,v retrieving revision 1.84.2.1 diff -u -p -r1.84.2.1 in6_pcb.c --- sys/netinet6/in6_pcb.c 21 Dec 2007 14:25:43 -0000 1.84.2.1 +++ sys/netinet6/in6_pcb.c 18 Jun 2008 22:31:09 -0000 @@ -144,6 +144,9 @@ in6_pcbbind(register struct inpcb *inp, if ((error = sa6_embedscope(sin6, ip6_use_defzone)) != 0) return(error); + if (prison_ip6(cred, &sin6->sin6_addr) != 0) + return (EINVAL); + lport = sin6->sin6_port; if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { /* @@ -188,7 +191,7 @@ in6_pcbbind(register struct inpcb *inp, PRIV_NETINET_REUSEPORT, 0) != 0) { t = in6_pcblookup_local(pcbinfo, &sin6->sin6_addr, lport, - INPLOOKUP_WILDCARD); + INPLOOKUP_WILDCARD, cred); if (t && ((t->inp_vflag & INP_TIMEWAIT) == 0) && (so->so_type != SOCK_STREAM || @@ -206,7 +209,7 @@ in6_pcbbind(register struct inpcb *inp, in6_sin6_2_sin(&sin, sin6); t = in_pcblookup_local(pcbinfo, sin.sin_addr, lport, - INPLOOKUP_WILDCARD); + INPLOOKUP_WILDCARD, cred); if (t && ((t->inp_vflag & INP_TIMEWAIT) == 0) && @@ -218,8 +221,10 @@ in6_pcbbind(register struct inpcb *inp, return (EADDRINUSE); } } + if (prison_ip6(cred, &sin6->sin6_addr) != 0) + return (EADDRNOTAVAIL); t = in6_pcblookup_local(pcbinfo, &sin6->sin6_addr, - lport, wild); + lport, wild, cred); if (t && (reuseport & ((t->inp_vflag & INP_TIMEWAIT) ? intotw(t)->tw_so_options : t->inp_socket->so_options)) == 0) @@ -230,7 +235,7 @@ in6_pcbbind(register struct inpcb *inp, in6_sin6_2_sin(&sin, sin6); t = in_pcblookup_local(pcbinfo, sin.sin_addr, - lport, wild); + lport, wild, cred); if (t && t->inp_vflag & INP_TIMEWAIT) { if ((reuseport & intotw(t)->tw_so_options) == 0 && @@ -250,12 +255,13 @@ in6_pcbbind(register struct inpcb *inp, } inp->in6p_laddr = sin6->sin6_addr; } + if (prison_ip6(cred, &inp->in6p_laddr) != 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; @@ -309,6 +315,8 @@ in6_pcbladdr(register struct inpcb *inp, 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 @@ -316,14 +324,14 @@ in6_pcbladdr(register struct inpcb *inp, * Is it the intended behavior? */ *plocal_addr6 = in6_selectsrc(sin6, inp->in6p_outputopts, - inp->in6p_moptions, NULL, - &inp->in6p_laddr, &ifp, &error); + inp, inp->inp_socket->so_cred, + NULL, &ifp, &error); if (ifp && scope_ambiguous && (error = in6_setscope(&sin6->sin6_addr, ifp, NULL)) != 0) { return(error); } - if (*plocal_addr6 == 0) { + if (*plocal_addr6 == NULL) { if (error == 0) error = EADDRNOTAVAIL; return (error); @@ -676,7 +684,7 @@ in6_pcbnotify(struct inpcbinfo *pcbinfo, */ struct inpcb * in6_pcblookup_local(struct inpcbinfo *pcbinfo, struct in6_addr *laddr, - u_int lport_arg, int wild_okay) + u_int lport_arg, int wild_okay, struct ucred *cred) { register struct inpcb *inp; int matchwild = 3, wildcard; @@ -693,16 +701,24 @@ in6_pcblookup_local(struct inpcbinfo *pc 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. @@ -731,8 +747,17 @@ in6_pcblookup_local(struct inpcbinfo *pc */ 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( @@ -740,8 +765,10 @@ in6_pcblookup_local(struct inpcbinfo *pc 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++; @@ -750,9 +777,11 @@ in6_pcblookup_local(struct inpcbinfo *pc match = inp; matchwild = wildcard; if (matchwild == 0) { + NOP_INP_RUNLOCK(inp); break; } } + NOP_INP_RUNLOCK(inp); } } return (match); @@ -834,11 +863,11 @@ in6_rtchange(struct inpcb *inp, int errn */ 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; @@ -852,47 +881,155 @@ in6_pcblookup_hash(struct inpcbinfo *pcb /* * 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) { - if (faith && (inp->inp_flags & INP_FAITH) == 0) + } + + 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 (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 (IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, - laddr)) - return (inp); - else if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) + } + 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)) { + 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); } Index: sys/netinet6/in6_pcb.h =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/netinet6/in6_pcb.h,v retrieving revision 1.19 diff -u -p -r1.19 in6_pcb.h --- sys/netinet6/in6_pcb.h 11 May 2007 10:20:50 -0000 1.19 +++ sys/netinet6/in6_pcb.h 18 Jun 2008 22:31:09 -0000 @@ -81,7 +81,8 @@ int in6_pcbladdr __P((struct inpcb *, st struct in6_addr **)); struct inpcb * in6_pcblookup_local __P((struct inpcbinfo *, - struct in6_addr *, u_int, int)); + struct in6_addr *, u_int, int, + struct ucred *)); struct inpcb * in6_pcblookup_hash __P((struct inpcbinfo *, struct in6_addr *, u_int, struct in6_addr *, Index: sys/netinet6/in6_src.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/netinet6/in6_src.c,v retrieving revision 1.46 diff -u -p -r1.46 in6_src.c --- sys/netinet6/in6_src.c 5 Jul 2007 16:29:39 -0000 1.46 +++ sys/netinet6/in6_src.c 18 Jun 2008 22:31:09 -0000 @@ -77,6 +77,7 @@ #include #include #include +#include #include #include @@ -158,8 +159,8 @@ static struct in6_addrpolicy *match_addr struct in6_addr * in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, - struct ip6_moptions *mopts, struct route_in6 *ro, - struct in6_addr *laddr, struct ifnet **ifpp, int *errorp) + struct inpcb *inp, struct ucred *cred, + struct route_in6 *ro, struct ifnet **ifpp, int *errorp) { struct in6_addr dst; struct ifnet *ifp = NULL; @@ -169,12 +170,18 @@ in6_selectsrc(struct sockaddr_in6 *dstso struct in6_addrpolicy *dst_policy = NULL, *best_policy = NULL; u_int32_t odstzone; int prefer_tempaddr; + struct ip6_moptions *mopts; dst = dstsock->sin6_addr; /* make a copy for local operation */ *errorp = 0; if (ifpp) *ifpp = NULL; + if (inp != NULL) + mopts = inp->in6p_moptions; + else + mopts = NULL; + /* * If the source address is explicitly specified by the caller, * check if the requested source address is indeed a unicast address @@ -208,6 +215,10 @@ in6_selectsrc(struct sockaddr_in6 *dstso if (*errorp != 0) return (NULL); } + if (cred != NULL && prison_ip6(cred, &srcsock.sin6_addr) != 0) { + *errorp = EADDRNOTAVAIL; + return (NULL); + } ia6 = (struct in6_ifaddr *)ifa_ifwithaddr((struct sockaddr *)(&srcsock)); if (ia6 == NULL || @@ -224,8 +235,13 @@ in6_selectsrc(struct sockaddr_in6 *dstso /* * Otherwise, if the socket has already bound the source, just use it. */ - if (laddr && !IN6_IS_ADDR_UNSPECIFIED(laddr)) - return (laddr); + if (inp != NULL && !IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) { + if (cred != NULL && prison_ip6(cred, &inp->in6p_laddr) != 0) { + *errorp = EADDRNOTAVAIL; + return (NULL); + } + return (&inp->in6p_laddr); + } /* * If the address is not specified, choose the best one based on @@ -275,6 +291,10 @@ in6_selectsrc(struct sockaddr_in6 *dstso if (!ip6_use_deprecated && IFA6_IS_DEPRECATED(ia)) continue; + if (cred != NULL && + prison_ip6(cred, &ia->ia_addr.sin6_addr) != 0) + continue; + /* Rule 1: Prefer same address */ if (IN6_ARE_ADDR_EQUAL(&dst, &ia->ia_addr.sin6_addr)) { ia_best = ia; @@ -748,6 +768,9 @@ in6_pcbsetport(struct in6_addr *laddr, s INP_INFO_WLOCK_ASSERT(pcbinfo); INP_LOCK_ASSERT(inp); + if (prison_ip6(cred, laddr) != 0) + return(EINVAL); + /* XXX: this is redundant when called from in6_pcbbind */ if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0) wild = INPLOOKUP_WILDCARD; @@ -797,7 +820,7 @@ in6_pcbsetport(struct in6_addr *laddr, s *lastport = first; lport = htons(*lastport); } while (in6_pcblookup_local(pcbinfo, &inp->in6p_laddr, - lport, wild)); + lport, wild, cred)); } else { /* * counting up @@ -817,10 +840,15 @@ in6_pcbsetport(struct in6_addr *laddr, s if (*lastport < first || *lastport > last) *lastport = first; lport = htons(*lastport); - } while (in6_pcblookup_local(pcbinfo, - &inp->in6p_laddr, lport, wild)); + } while (in6_pcblookup_local(pcbinfo, &inp->in6p_laddr, + lport, wild, cred)); } +#if 0 /* XXX-BZ why did I leave this here? */ + if (prison_ip6(cred, &inp->in6p_laddr) != 0) + return (EINVAL); +#endif + inp->inp_lport = lport; if (in_pcbinshash(inp) != 0) { inp->in6p_laddr = in6addr_any; Index: sys/netinet6/ip6_var.h =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/netinet6/ip6_var.h,v retrieving revision 1.39 diff -u -p -r1.39 ip6_var.h --- sys/netinet6/ip6_var.h 5 Jul 2007 16:29:40 -0000 1.39 +++ sys/netinet6/ip6_var.h 18 Jun 2008 22:31:09 -0000 @@ -390,9 +390,9 @@ int rip6_usrreq __P((struct socket *, int dest6_input __P((struct mbuf **, int *, int)); int none_input __P((struct mbuf **, int *, int)); -struct in6_addr *in6_selectsrc __P((struct sockaddr_in6 *, - struct ip6_pktopts *, struct ip6_moptions *, struct route_in6 *, - struct in6_addr *, struct ifnet **, int *)); +struct in6_addr *in6_selectsrc __P((struct sockaddr_in6 *, struct ip6_pktopts *, + struct inpcb *inp, struct ucred *cred, + struct route_in6 *, struct ifnet **, int *)); int in6_selectroute __P((struct sockaddr_in6 *, struct ip6_pktopts *, struct ip6_moptions *, struct route_in6 *, struct ifnet **, struct rtentry **, int)); Index: sys/netinet6/nd6_nbr.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/netinet6/nd6_nbr.c,v retrieving revision 1.47 diff -u -p -r1.47 nd6_nbr.c --- sys/netinet6/nd6_nbr.c 5 Jul 2007 16:29:40 -0000 1.47 +++ sys/netinet6/nd6_nbr.c 18 Jun 2008 22:31:09 -0000 @@ -454,7 +454,7 @@ nd6_ns_output(struct ifnet *ifp, const s dst_sa.sin6_addr = ip6->ip6_dst; src = in6_selectsrc(&dst_sa, NULL, - NULL, &ro, NULL, NULL, &error); + NULL, NULL, &ro, NULL, &error); if (src == NULL) { char ip6buf[INET6_ADDRSTRLEN]; nd6log((LOG_DEBUG, @@ -936,7 +936,7 @@ nd6_na_output(struct ifnet *ifp, const s * Select a source whose scope is the same as that of the dest. */ bcopy(&dst_sa, &ro.ro_dst, sizeof(dst_sa)); - src = in6_selectsrc(&dst_sa, NULL, NULL, &ro, NULL, NULL, &error); + src = in6_selectsrc(&dst_sa, NULL, NULL, NULL, &ro, NULL, &error); if (src == NULL) { char ip6buf[INET6_ADDRSTRLEN]; nd6log((LOG_DEBUG, "nd6_na_output: source can't be " Index: sys/netinet6/raw_ip6.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/netinet6/raw_ip6.c,v retrieving revision 1.73 diff -u -p -r1.73 raw_ip6.c --- sys/netinet6/raw_ip6.c 5 Jul 2007 16:29:40 -0000 1.73 +++ sys/netinet6/raw_ip6.c 18 Jun 2008 22:31:09 -0000 @@ -70,6 +70,7 @@ #include #include #include +#include /* Must come after sys/proc.h */ #include #include #include @@ -168,6 +169,10 @@ docontinue: if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) && !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src)) goto docontinue; + if (jailed(in6p->inp_socket->so_cred)) + if (!jailed_ip6(in6p->inp_socket->so_cred, + &ip6->ip6_dst)) + goto docontinue; if (in6p->in6p_cksum != -1) { rip6stat.rip6s_isum++; if (in6_cksum(m, proto, *offp, @@ -388,12 +393,17 @@ rip6_output(m, va_alist) /* * Source address selection. */ - if ((in6a = in6_selectsrc(dstsock, optp, in6p->in6p_moptions, NULL, - &in6p->in6p_laddr, &oifp, &error)) == NULL) { + if ((in6a = in6_selectsrc(dstsock, optp, in6p, so->so_cred, + NULL, &oifp, &error)) == NULL) { if (error == 0) 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) { @@ -643,6 +653,8 @@ rip6_bind(struct socket *so, struct sock KASSERT(inp != NULL, ("rip6_bind: inp == NULL")); 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) @@ -699,8 +711,8 @@ rip6_connect(struct socket *so, struct s INP_LOCK(inp); /* Source address selection. XXX: need pcblookup? */ in6a = in6_selectsrc(addr, inp->in6p_outputopts, - inp->in6p_moptions, NULL, - &inp->in6p_laddr, &ifp, &error); + inp, so->so_cred, + NULL, &ifp, &error); if (in6a == NULL) { INP_UNLOCK(inp); INP_INFO_WUNLOCK(&ripcbinfo); Index: sys/netinet6/sctp6_usrreq.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/netinet6/sctp6_usrreq.c,v retrieving revision 1.41 diff -u -p -r1.41 sctp6_usrreq.c --- sys/netinet6/sctp6_usrreq.c 13 Sep 2007 10:36:43 -0000 1.41 +++ sys/netinet6/sctp6_usrreq.c 18 Jun 2008 22:31:09 -0000 @@ -638,6 +638,15 @@ sctp6_attach(struct socket *so, int prot struct sctp_inpcb *inp; uint32_t vrf_id = SCTP_DEFAULT_VRFID; + /* + * SCTP inside jails needs more review. Apart from that, jails + * do not support IPv6. + * Once we implemented 'jailv6' and are sure everything is safe, + * we can drop this again. + */ + if (jailed(p->td_ucred)) + return EOPNOTSUPP; + inp = (struct sctp_inpcb *)so->so_pcb; if (inp != NULL) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); Index: sys/netinet6/udp6_usrreq.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/netinet6/udp6_usrreq.c,v retrieving revision 1.81 diff -u -p -r1.81 udp6_usrreq.c --- sys/netinet6/udp6_usrreq.c 8 Sep 2007 08:18:24 -0000 1.81 +++ sys/netinet6/udp6_usrreq.c 18 Jun 2008 22:31:10 -0000 @@ -74,6 +74,7 @@ #include #include #include +#include /* Must come after sys/proc.h */ #include #include #include @@ -582,8 +583,8 @@ udp6_output(struct inpcb *inp, struct mb } if (!IN6_IS_ADDR_V4MAPPED(faddr)) { - laddr = in6_selectsrc(sin6, optp, inp->in6p_moptions, - NULL, &inp->in6p_laddr, &oifp, &error); + laddr = in6_selectsrc(sin6, optp, inp, td->td_ucred, + NULL, &oifp, &error); if (oifp && scope_ambiguous && (error = in6_setscope(&sin6->sin6_addr, oifp, NULL))) { @@ -849,6 +850,12 @@ udp6_connect(struct socket *so, struct s 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) { @@ -863,6 +870,13 @@ udp6_connect(struct socket *so, struct s 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) { Index: sys/security/mac_bsdextended/mac_bsdextended.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/security/mac_bsdextended/mac_bsdextended.c,v retrieving revision 1.39 diff -u -p -r1.39 mac_bsdextended.c --- sys/security/mac_bsdextended/mac_bsdextended.c 10 Sep 2007 00:00:17 -0000 1.39 +++ sys/security/mac_bsdextended/mac_bsdextended.c 18 Jun 2008 22:31:10 -0000 @@ -46,13 +46,14 @@ #include #include #include -#include #include #include #include #include #include #include +#include +#include /* Must come after sys/proc.h */ #include #include #include Index: sys/sys/jail.h =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/sys/sys/jail.h,v retrieving revision 1.29 diff -u -p -r1.29 jail.h --- sys/sys/jail.h 5 Apr 2007 23:19:13 -0000 1.29 +++ sys/sys/jail.h 18 Jun 2008 22:31:10 -0000 @@ -13,21 +13,70 @@ #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 + +#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; 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 + +/* XXX-BZ temporary put it in here like this. */ +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 @@ -65,15 +114,20 @@ 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 */ char pr_path[MAXPATHLEN]; /* (c) chroot path */ 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 +143,7 @@ extern int jail_sysvipc_allowed; 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 +156,7 @@ struct ucred; struct mount; struct sockaddr; struct statfs; +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 +165,19 @@ void prison_enforce_statfs(struct ucred 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); +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 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. Index: usr.sbin/jail/Makefile =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/usr.sbin/jail/Makefile,v retrieving revision 1.10 diff -u -p -r1.10 Makefile --- usr.sbin/jail/Makefile 17 Nov 2004 10:01:48 -0000 1.10 +++ usr.sbin/jail/Makefile 18 Jun 2008 22:31:17 -0000 @@ -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 @@ LDADD= -lutil WARNS?= 6 +.if ${MK_INET6_SUPPORT} != "no" +CFLAGS+= -DINET6 +.endif + .include Index: usr.sbin/jail/jail.8 =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/usr.sbin/jail/jail.8,v retrieving revision 1.84 diff -u -p -r1.84 jail.8 --- usr.sbin/jail/jail.8 5 Apr 2007 21:17:52 -0000 1.84 +++ usr.sbin/jail/jail.8 18 Jun 2008 22:31:17 -0000 @@ -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 @@ utility imprisons a process and all futu .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,8 @@ should run. Directory which is to be the root of the prison. .It Ar hostname Hostname of the prison. -.It Ar ip-number -IP number assigned to the prison. +.It Ar ip-addresses +None, one or more IPv4 and IPv6 addresses assigned to the prison. .It Ar command Pathname of the program which is to be executed. .El @@ -179,7 +198,9 @@ is to disable IP services on the host sy 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 +576,13 @@ command can be used to find file system 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 +650,12 @@ who contributed it to .An Robert Watson wrote the extended documentation, found a few bugs, added a few new features, and cleaned up the userland jail environment. +.Pp +.An Bjoern A. Zeeb +added multi-IP jail support for IPv4 and IPv6 based on a patch +originally done by +.An Pawel Jakub Dawidek +for IPv4. .Sh BUGS Jail currently lacks the ability to allow access to specific jail information via Index: usr.sbin/jail/jail.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/usr.sbin/jail/jail.c,v retrieving revision 1.25 diff -u -p -r1.25 jail.c --- usr.sbin/jail/jail.c 12 May 2006 15:14:43 -0000 1.25 +++ usr.sbin/jail/jail.c 18 Jun 2008 22:31:18 -0000 @@ -12,10 +12,14 @@ __FBSDID("$FreeBSD: src/usr.sbin/jail/ja #include #include +#include +#include #include +#include #include #include +#include #include #include @@ -25,12 +29,34 @@ __FBSDID("$FreeBSD: src/usr.sbin/jail/ja #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 @@ main(int argc, char **argv) login_cap_t *lcap = NULL; struct jail j; struct passwd *pwd = NULL; - struct in_addr in; gid_t groups[NGROUPS]; - int ch, i, iflag, Jflag, lflag, ngroups, securelevel, uflag, Uflag; - char path[PATH_MAX], *ep, *username, *JidFile; + int ch, error, i, ngroups, securelevel; + int hflag, iflag, Jflag, lflag, uflag, Uflag; + char path[PATH_MAX], *jailname, *ep, *username, *JidFile, *ip; static char *cleanenv; const char *shell, *p = NULL; long ltmp; FILE *fp; + struct addrinfo hints, *res0; - iflag = Jflag = lflag = uflag = Uflag = 0; + hflag = iflag = Jflag = lflag = uflag = Uflag = 0; securelevel = -1; - username = JidFile = cleanenv = NULL; + jailname = username = JidFile = cleanenv = NULL; fp = NULL; - while ((ch = getopt(argc, argv, "ils:u:U:J:")) != -1) { + while ((ch = getopt(argc, argv, "hiln:s:u:U:J:")) != -1) { switch (ch) { + case 'h': + hflag = 1; + break; case 'i': iflag = 1; break; @@ -76,6 +106,9 @@ main(int argc, char **argv) JidFile = optarg; Jflag = 1; break; + case 'n': + jailname = optarg; + break; case 's': ltmp = strtol(optarg, &ep, 0); if (*ep || ep == optarg || ltmp > INT_MAX || !ltmp) @@ -111,13 +144,62 @@ main(int argc, char **argv) err(1, "realpath: %s", argv[0]); if (chdir(path) != 0) err(1, "chdir: %s", path); + /* Initialize struct jail. */ memset(&j, 0, sizeof(j)); - j.version = 0; + j.version = JAIL_API_VERSION; j.path = path; j.hostname = argv[1]; - if (inet_aton(argv[2], &in) == 0) - errx(1, "Could not make sense of ip-number: %s", argv[2]); - j.ip_number = ntohl(in.s_addr); + if (jailname != NULL) + j.jailname = jailname; + + /* Handle IP addresses. If requested resolve hostname too. */ + bzero(&hints, sizeof(struct addrinfo)); + hints.ai_protocol = IPPROTO_TCP; + hints.ai_socktype = SOCK_STREAM; + if (JAIL_API_VERSION < 2) + hints.ai_family = PF_INET; + else + hints.ai_family = PF_UNSPEC; + /* Handle hostname. */ + if (hflag != 0) { + error = getaddrinfo(j.hostname, NULL, &hints, &res0); + if (error != 0) + errx(1, "failed to handle hostname: %s", + gai_strerror(error)); + error = add_addresses(res0); + freeaddrinfo(res0); + if (error != 0) + errx(1, "failed to add addresses."); + } + /* Handle IP addresses. */ + hints.ai_flags = AI_NUMERICHOST; + ip = strtok(argv[2], ","); + while (ip != NULL) { + error = getaddrinfo(ip, NULL, &hints, &res0); + if (error != 0) + errx(1, "failed to handle ip: %s", gai_strerror(error)); + error = add_addresses(res0); + freeaddrinfo(res0); + if (error != 0) + errx(1, "failed to add addresses."); + ip = strtok(NULL, ","); + } + /* Count IP addresses and add them to struct jail. */ + if (!STAILQ_EMPTY(&addr4)) { + j.ip4s = STAILQ_FIRST(&addr4)->count; + j.ip4 = copy_addr4(); + if (j.ip4s > 0 && j.ip4 == NULL) + errx(1, "copy_addr4()"); + } +#ifdef INET6 + if (!STAILQ_EMPTY(&addr6)) { + j.ip6s = STAILQ_FIRST(&addr6)->count; + j.ip6 = copy_addr6(); + if (j.ip6s > 0 && j.ip6 == NULL) + errx(1, "copy_addr6()"); + } +#endif + if (Jflag) { fp = fopen(JidFile, "w"); if (fp == NULL) @@ -125,7 +207,7 @@ main(int argc, char **argv) } i = jail(&j); if (i == -1) - err(1, "jail"); + err(1, "syscall failed with"); if (iflag) { printf("%d\n", i); fflush(stdout); @@ -183,8 +265,148 @@ usage(void) { (void)fprintf(stderr, "%s%s%s\n", - "usage: jail [-i] [-J jid_file] [-s securelevel] [-l -u ", - "username | -U username]", - " path hostname ip-number command ..."); + "usage: jail [-hi] [-n jailname] [-J jid_file] ", + "[-s securelevel] [-l -u username | -U username] ", + "path hostname [ip[,..]] command ..."); exit(1); } + +static int +add_addresses(struct addrinfo *res0) +{ + int error; + struct addrinfo *res; + struct addr4entry *a4p; + struct sockaddr_in *sai; +#ifdef INET6 + struct addr6entry *a6p; + struct sockaddr_in6 *sai6; +#endif + int count; + + error = 0; + for (res = res0; res && error == 0; res = res->ai_next) { + switch (res->ai_family) { + case AF_INET: + sai = (struct sockaddr_in *)(void *)res->ai_addr; + STAILQ_FOREACH(a4p, &addr4, addr4entries) { + if (bcmp(&sai->sin_addr, &a4p->ip4, + sizeof(struct in_addr)) == 0) { + err(1, "Ignoring duplicate IPv4 address."); + break; + } + } + a4p = (struct addr4entry *) malloc( + sizeof(struct addr4entry)); + if (a4p == NULL) { + error = 1; + break; + } + bzero(a4p, sizeof(struct addr4entry)); + bcopy(&sai->sin_addr, &a4p->ip4, + sizeof(struct in_addr)); + if (!STAILQ_EMPTY(&addr4)) + count = STAILQ_FIRST(&addr4)->count; + else + count = 0; + STAILQ_INSERT_TAIL(&addr4, a4p, addr4entries); + STAILQ_FIRST(&addr4)->count = count + 1; + break; +#ifdef INET6 + case AF_INET6: + sai6 = (struct sockaddr_in6 *)(void *)res->ai_addr; + STAILQ_FOREACH(a6p, &addr6, addr6entries) { + if (bcmp(&sai6->sin6_addr, &a6p->ip6, + sizeof(struct in6_addr)) == 0) { + err(1, "Ignoring duplicate IPv6 address."); + break; + } + } + a6p = (struct addr6entry *) malloc( + sizeof(struct addr6entry)); + if (a6p == NULL) { + error = 1; + break; + } + bzero(a6p, sizeof(struct addr6entry)); + bcopy(&sai6->sin6_addr, &a6p->ip6, + sizeof(struct in6_addr)); + if (!STAILQ_EMPTY(&addr6)) + count = STAILQ_FIRST(&addr6)->count; + else + count = 0; + STAILQ_INSERT_TAIL(&addr6, a6p, addr6entries); + STAILQ_FIRST(&addr6)->count = count + 1; + break; +#endif + default: + err(1, "Address family %d not supported. Ignoring.\n", + res->ai_family); + break; + } + } + + return (error); +} + +static struct in_addr * +copy_addr4(void) +{ + size_t len; + struct in_addr *ip4s, *p, ia; + struct addr4entry *a4p; + + if (STAILQ_EMPTY(&addr4)) + return NULL; + + len = STAILQ_FIRST(&addr4)->count * sizeof(struct in_addr); + + ip4s = p = (struct in_addr *)malloc(len); + if (ip4s == NULL) + return (NULL); + + bzero(p, len); + + while (!STAILQ_EMPTY(&addr4)) { + a4p = STAILQ_FIRST(&addr4); + STAILQ_REMOVE_HEAD(&addr4, addr4entries); + ia.s_addr = a4p->ip4.s_addr; + bcopy(&ia, p, sizeof(struct in_addr)); + p++; + free(a4p); + } + + return (ip4s); +} + +#ifdef INET6 +static struct in6_addr * +copy_addr6(void) +{ + size_t len; + struct in6_addr *ip6s, *p; + struct addr6entry *a6p; + + if (STAILQ_EMPTY(&addr6)) + return NULL; + + len = STAILQ_FIRST(&addr6)->count * sizeof(struct in6_addr); + + ip6s = p = (struct in6_addr *)malloc(len); + if (ip6s == NULL) + return (NULL); + + bzero(p, len); + + while (!STAILQ_EMPTY(&addr6)) { + a6p = STAILQ_FIRST(&addr6); + STAILQ_REMOVE_HEAD(&addr6, addr6entries); + bcopy(&a6p->ip6, p, sizeof(struct in6_addr)); + p++; + free(a6p); + } + + return (ip6s); +} +#endif + Index: usr.sbin/jexec/Makefile =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/usr.sbin/jexec/Makefile,v retrieving revision 1.2 diff -u -p -r1.2 Makefile --- usr.sbin/jexec/Makefile 19 Apr 2006 10:12:10 -0000 1.2 +++ usr.sbin/jexec/Makefile 18 Jun 2008 22:31:18 -0000 @@ -6,4 +6,6 @@ DPADD= ${LIBUTIL} LDADD= -lutil WARNS?= 6 +CFLAGS+= -DSUPPORT_OLD_XPRISON + .include Index: usr.sbin/jexec/jexec.8 =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/usr.sbin/jexec/jexec.8,v retrieving revision 1.4 diff -u -p -r1.4 jexec.8 --- usr.sbin/jexec/jexec.8 29 Sep 2006 17:57:02 -0000 1.4 +++ usr.sbin/jexec/jexec.8 18 Jun 2008 22:31:18 -0000 @@ -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 @@ The .Nm utility was added in .Fx 5.1 . +.Sh BUGS +If the jail is not identified by +.Ar jid +there is a possible race in between the lookup of the jail +and executing the command inside the jail. +Giving a +.Ar jid +has a similar race as another process can stop the jail and +start another one after the user looked up the +.Ar jid . Index: usr.sbin/jexec/jexec.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/usr.sbin/jexec/jexec.c,v retrieving revision 1.4 diff -u -p -r1.4 jexec.c --- usr.sbin/jexec/jexec.c 29 Sep 2006 17:04:03 -0000 1.4 +++ usr.sbin/jexec/jexec.c 18 Jun 2008 22:31:18 -0000 @@ -29,16 +29,170 @@ #include #include +#include #include #include #include #include #include +#include #include #include static void usage(void); +#ifdef SUPPORT_OLD_XPRISON +static +char *lookup_xprison_v1(char *p, char *end, int *id) +{ + struct xprison_v1 *xp; + + if (id == NULL) + errx(1, "Internal error. Invalid ID pointer."); + + if (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(char *p, char *end, int *id, char *jailname) +{ + struct xprison *xp; + int ok; + + if (id == NULL) + errx(1, "Internal error. Invalid ID pointer."); + + if (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; + } + + p = (char *)(xp + 1); + /* IPv4 addresses. */ + p += (xp->pr_ip4s * sizeof(struct in_addr)); + if (p > end) + errx(1, "Invalid length for jail"); + /* IPv6 addresses. */ + p += (xp->pr_ip6s * sizeof(struct in6_addr)); + if (p > end) + errx(1, "Invalid length for jail"); + + if (ok) + *id = xp->pr_id; + return (p); +} + +static int +lookup_jail(int jid, char *jailname) +{ + size_t i, j, len; + char *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 = (char *)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 && q + sizeof(int) < 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, 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, 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 +217,17 @@ main(int argc, char *argv[]) struct passwd *pwd = NULL; gid_t groups[NGROUPS]; int ch, ngroups, uflag, Uflag; - char *username; - ch = uflag = Uflag = 0; - username = NULL; + char *jailname, *username; - while ((ch = getopt(argc, argv, "u:U:")) != -1) { - switch (ch) { + ch = uflag = Uflag = 0; + jailname = username = NULL; + jid = -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 +244,22 @@ main(int argc, char *argv[]) argv += optind; if (argc < 2) usage(); + if (strlen(argv[0]) > 0) { + jid = (int)strtol(argv[0], NULL, 10); + if (errno) + err(1, "Unable to parse jail ID."); + } + if (jid <= 0 && jailname == NULL) { + fprintf(stderr, "Neither jail ID nor jail name given.\n"); + usage(); + } if (uflag && Uflag) usage(); if (uflag) GET_USER_INFO; - 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 +287,6 @@ usage(void) fprintf(stderr, "%s%s\n", "usage: jexec [-u username | -U username]", - " jid command ..."); + " [-n jailname] jid command ..."); exit(1); } Index: usr.sbin/jls/Makefile =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/usr.sbin/jls/Makefile,v retrieving revision 1.1 diff -u -p -r1.1 Makefile --- usr.sbin/jls/Makefile 9 Apr 2003 03:04:12 -0000 1.1 +++ usr.sbin/jls/Makefile 18 Jun 2008 22:31:18 -0000 @@ -4,4 +4,6 @@ PROG= jls MAN= jls.8 WARNS?= 6 +CFLAGS+= -DSUPPORT_OLD_XPRISON + .include Index: usr.sbin/jls/jls.8 =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/usr.sbin/jls/jls.8,v retrieving revision 1.1 diff -u -p -r1.1 jls.8 --- usr.sbin/jls/jls.8 9 Apr 2003 03:04:12 -0000 1.1 +++ usr.sbin/jls/jls.8 18 Jun 2008 22:31:18 -0000 @@ -25,20 +25,36 @@ .\" .\" $FreeBSD: src/usr.sbin/jls/jls.8,v 1.1 2003/04/09 03:04:12 mike Exp $ .\" -.Dd April 8, 2003 +.Dd January 4, 2008 .Dt JLS 8 .Os .Sh NAME .Nm jls -.Nd "list active jails" +.Nd "list jails" .Sh SYNOPSIS .Nm +.Op Fl a .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 once. +.El +.Pp +Each jail is represented by rows which contain the following columns: +.Bl -item -offset indent -compact +.It +jail identifier (JID), hostname and path +.It +jail state and name +.It +followed by one IP adddress per line. +.El .Sh SEE ALSO .Xr jail 2 , .Xr jail 8 , Index: usr.sbin/jls/jls.c =================================================================== RCS file: /shared/mirror/FreeBSD/r/ncvs/src/usr.sbin/jls/jls.c,v retrieving revision 1.5 diff -u -p -r1.5 jls.c --- usr.sbin/jls/jls.c 19 Aug 2005 11:03:49 -0000 1.5 +++ usr.sbin/jls/jls.c 18 Jun 2008 22:31:18 -0000 @@ -30,6 +30,8 @@ #include #include +#include +#include #include #include #include @@ -37,47 +39,145 @@ #include #include +#ifdef SUPPORT_OLD_XPRISON +static +char *print_xprison_v1(char *p, char *end) +{ + struct xprison_v1 *xp; + struct in_addr in; + + if (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. */ + + /* 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(char *p, char *end) +{ + struct xprison *xp; + struct in_addr *iap, in; + struct in6_addr *ia6p; + char buf[INET6_ADDRSTRLEN]; + const char *state; + uint32_t i; + + if (p + sizeof(struct xprison) > end) + errx(1, "Invalid length for jail"); + + xp = (struct xprison *)p; + printf("%6d %-29.29s %.74s\n", + xp->pr_id, xp->pr_host, xp->pr_path); + + /* Jail state and name. */ + 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; + printf("%6s %-29.29s %.74s\n", + "", (xp->pr_name != NULL) ? xp->pr_name : "", state); + + p = (char *)(xp + 1); + /* IPv4 addresses. */ + iap = (struct in_addr *)(void *)p; + p += (xp->pr_ip4s * sizeof(struct in_addr)); + if (p > end) + errx(1, "Invalid length for jail"); + for (i = 0; i < xp->pr_ip4s; i++) { + in.s_addr = iap[i].s_addr; + printf("%6s %-15.15s\n", "", inet_ntoa(in)); + } + /* IPv6 addresses. */ + ia6p = (struct in6_addr *)(void *)p; + p += (xp->pr_ip6s * sizeof(struct in6_addr)); + if (p > end) + errx(1, "Invalid length for jail"); + for (i = 0; i < xp->pr_ip6s; i++) { + inet_ntop(AF_INET6, &ia6p[i], buf, sizeof(buf)); + printf("%6s %s\n", "", buf); + } + + return (p); +} int main(void) { - struct xprison *sxp, *xp; - struct in_addr in; - size_t i, len; + size_t i, j, len; + char *p, *q; + int version; 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 = (char *)malloc(len); + if (p == NULL) err(1, "malloc()"); - if (sysctlbyname("security.jail.list", xp, &len, NULL, 0) == -1) { + if (sysctlbyname("security.jail.list", q, &len, NULL, 0) == -1) { if (errno == ENOMEM) { - free(sxp); - sxp = NULL; + free(p); + p = NULL; + len += j; continue; } err(1, "sysctlbyname(): security.jail.list"); } break; } - if (sxp == NULL) + if (p == NULL) err(1, "sysctlbyname(): security.jail.list"); - if (len < sizeof(*xp) || len % sizeof(*xp) || - xp->pr_version != XPRISON_VERSION) - errx(1, "Kernel and userland out of sync"); - - printf(" JID IP Address Hostname Path\n"); - for (i = 0; i < len / sizeof(*xp); i++) { - in.s_addr = ntohl(xp->pr_ip); - printf("%6d %-15.15s %-29.29s %.74s\n", - xp->pr_id, inet_ntoa(in), xp->pr_host, xp->pr_path); - xp++; + if (len < sizeof(int)) + errx(1, "This is no prison. Kernel and userland out of sync?"); + version = *(int *)p; + if (version > XPRISON_VERSION) + errx(1, "Sci-Fi prison. Kernel/userland out of sync?"); + + printf(" JID Hostname Path\n"); + printf(" Name State\n"); + printf(" IP Address(es)\n"); + for (; q && q + sizeof(int) < 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, 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, p + len); + break; + default: + errx(1, "Prison unknown. Kernel/userland out of sync?"); + /* NOTREACHED */ + break; + } } - free(sxp); + + free(p); exit(0); }