From 3bcceea40e03305fc48c713e91b839f6048cab29 Mon Sep 17 00:00:00 2001 From: "Bjoern A. Zeeb" Date: Sat, 23 Jan 2010 16:40:35 +0000 Subject: [PATCH] MFC r202468: Add ip4.saddrsel/ip4.nosaddrsel (and equivalent for ip6) to control whether to use source address selection (default) or the primary jail address for unbound outgoing connections. This is intended to be used by people upgrading from single-IP jails to multi-IP jails but not having to change firewall rules, application ACLs, ... but to force their connections (unless otherwise changed) to the primry jail IP they had been used for years, as well as for people prefering to implement similar policies. Note that for IPv6, if configured incorrectly, this might lead to scope violations, which single-IPv6 jails could as well, as by the design of jails. [1] Reviewed by: jamie, hrs (ipv6 part) Pointed out by: hrs [1] --- sys/kern/kern_jail.c | 110 ++++++++++++++++++++++++++++++++++++++++- sys/netinet/in_pcb.c | 7 +++ sys/netinet6/in6_src.c | 7 +++ sys/sys/jail.h | 6 +++ usr.sbin/jail/jail.8 | 16 ++++-- 5 files changed, 140 insertions(+), 6 deletions(-) diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c index c3c2568da7d..01a57f9324a 100644 --- a/sys/kern/kern_jail.c +++ b/sys/kern/kern_jail.c @@ -77,6 +77,21 @@ __FBSDID("$FreeBSD$"); MALLOC_DEFINE(M_PRISON, "prison", "Prison structures"); +/* Keep struct prison prison0 and some code in kern_jail_set() readable. */ +#ifdef INET +#ifdef INET6 +#define _PR_IP_SADDRSEL PR_IP4_SADDRSEL|PR_IP6_SADDRSEL +#else +#define _PR_IP_SADDRSEL PR_IP4_SADDRSEL +#endif +#else /* !INET */ +#ifdef INET6 +#define _PR_IP_SADDRSEL PR_IP6_SADDRSEL +#else +#define _PR_IP_SADDRSEL 0 +#endif +#endif + /* prison0 describes what is "real" about the system. */ struct prison prison0 = { .pr_id = 0, @@ -89,9 +104,9 @@ struct prison prison0 = { .pr_hostuuid = DEFAULT_HOSTUUID, .pr_children = LIST_HEAD_INITIALIZER(&prison0.pr_children), #ifdef VIMAGE - .pr_flags = PR_HOST|PR_VNET, + .pr_flags = PR_HOST|PR_VNET|_PR_IP_SADDRSEL, #else - .pr_flags = PR_HOST, + .pr_flags = PR_HOST|_PR_IP_SADDRSEL, #endif .pr_allow = PR_ALLOW_ALL, }; @@ -129,10 +144,22 @@ static int prison_restrict_ip6(struct prison *pr, struct in6_addr *newip6); */ static char *pr_flag_names[] = { [0] = "persist", +#ifdef INET + [7] = "ip4.saddrsel", +#endif +#ifdef INET6 + [8] = "ip6.saddrsel", +#endif }; static char *pr_flag_nonames[] = { [0] = "nopersist", +#ifdef INET + [7] = "ip4.nosaddrsel", +#endif +#ifdef INET6 + [8] = "ip6.nosaddrsel", +#endif }; struct jailsys_flags { @@ -1199,6 +1226,9 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) #endif } #endif + /* Source address selection is always on by default. */ + pr->pr_flags |= _PR_IP_SADDRSEL; + pr->pr_securelevel = ppr->pr_securelevel; pr->pr_allow = JAIL_DEFAULT_ALLOW & ppr->pr_allow; pr->pr_enforce_statfs = JAIL_DEFAULT_ENFORCE_STATFS; @@ -2658,6 +2688,41 @@ prison_get_ip4(struct ucred *cred, struct in_addr *ia) return (0); } +/* + * Return 1 if we should do proper source address selection or are not jailed. + * We will return 0 if we should bypass source address selection in favour + * of the primary jail IPv4 address. Only in this case *ia will be updated and + * returned in NBO. + * Return EAFNOSUPPORT, in case this jail does not allow IPv4. + */ +int +prison_saddrsel_ip4(struct ucred *cred, struct in_addr *ia) +{ + struct prison *pr; + struct in_addr lia; + int error; + + KASSERT(cred != NULL, ("%s: cred is NULL", __func__)); + KASSERT(ia != NULL, ("%s: ia is NULL", __func__)); + + if (!jailed(cred)) + return (1); + + pr = cred->cr_prison; + if (pr->pr_flags & PR_IP4_SADDRSEL) + return (1); + + lia.s_addr = INADDR_ANY; + error = prison_get_ip4(cred, &lia); + if (error) + return (error); + if (lia.s_addr == INADDR_ANY) + return (1); + + ia->s_addr = lia.s_addr; + return (0); +} + /* * Return true if pr1 and pr2 have the same IPv4 address restrictions. */ @@ -2963,6 +3028,41 @@ prison_get_ip6(struct ucred *cred, struct in6_addr *ia6) return (0); } +/* + * Return 1 if we should do proper source address selection or are not jailed. + * We will return 0 if we should bypass source address selection in favour + * of the primary jail IPv6 address. Only in this case *ia will be updated and + * returned in NBO. + * Return EAFNOSUPPORT, in case this jail does not allow IPv6. + */ +int +prison_saddrsel_ip6(struct ucred *cred, struct in6_addr *ia6) +{ + struct prison *pr; + struct in6_addr lia6; + int error; + + KASSERT(cred != NULL, ("%s: cred is NULL", __func__)); + KASSERT(ia6 != NULL, ("%s: ia6 is NULL", __func__)); + + if (!jailed(cred)) + return (1); + + pr = cred->cr_prison; + if (pr->pr_flags & PR_IP6_SADDRSEL) + return (1); + + lia6 = in6addr_any; + error = prison_get_ip6(cred, &lia6); + if (error) + return (error); + if (IN6_IS_ADDR_UNSPECIFIED(&lia6)) + return (1); + + bcopy(&lia6, ia6, sizeof(struct in6_addr)); + return (0); +} + /* * Return true if pr1 and pr2 have the same IPv6 address restrictions. */ @@ -4116,12 +4216,18 @@ SYSCTL_JAIL_PARAM_SYS_NODE(ip4, CTLFLAG_RDTUN, "Jail IPv4 address virtualization"); SYSCTL_JAIL_PARAM_STRUCT(_ip4, addr, CTLFLAG_RW, sizeof(struct in_addr), "S,in_addr,a", "Jail IPv4 addresses"); +SYSCTL_JAIL_PARAM(_ip4, saddrsel, CTLTYPE_INT | CTLFLAG_RW, + "B", "Do (not) use IPv4 source address selection rather than the " + "primary jail IPv4 address."); #endif #ifdef INET6 SYSCTL_JAIL_PARAM_SYS_NODE(ip6, CTLFLAG_RDTUN, "Jail IPv6 address virtualization"); SYSCTL_JAIL_PARAM_STRUCT(_ip6, addr, CTLFLAG_RW, sizeof(struct in6_addr), "S,in6_addr,a", "Jail IPv6 addresses"); +SYSCTL_JAIL_PARAM(_ip6, saddrsel, CTLTYPE_INT | CTLFLAG_RW, + "B", "Do (not) use IPv6 source address selection rather than the " + "primary jail IPv6 address."); #endif SYSCTL_JAIL_PARAM_NODE(allow, "Jail permission flags"); diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index 7a4ac10ad63..204d90453bd 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -552,6 +552,13 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr, KASSERT(laddr != NULL, ("%s: laddr NULL", __func__)); + /* + * Bypass source address selection and use the primary jail IP + * if requested. + */ + if (cred != NULL && !prison_saddrsel_ip4(cred, laddr)) + return (0); + error = 0; bzero(&sro, sizeof(sro)); diff --git a/sys/netinet6/in6_src.c b/sys/netinet6/in6_src.c index 8e82ef117ae..ea302a52fa4 100644 --- a/sys/netinet6/in6_src.c +++ b/sys/netinet6/in6_src.c @@ -270,6 +270,13 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, return (0); } + /* + * Bypass source address selection and use the primary jail IP + * if requested. + */ + if (cred != NULL && !prison_saddrsel_ip6(cred, srcp)) + return (0); + /* * If the address is not specified, choose the best one based on * the outgoing interface and the destination address. diff --git a/sys/sys/jail.h b/sys/sys/jail.h index 2c5d1787786..d7a0622f3cd 100644 --- a/sys/sys/jail.h +++ b/sys/sys/jail.h @@ -191,6 +191,10 @@ struct prison { #define PR_VNET 0x00000010 /* Virtual network stack */ #define PR_IP4_DISABLE 0x00000020 /* Disable IPv4 */ #define PR_IP6_DISABLE 0x00000040 /* Disable IPv6 */ +#define PR_IP4_SADDRSEL 0x00000080 /* Do IPv4 src addr sel. or use the */ + /* primary jail address. */ +#define PR_IP6_SADDRSEL 0x00000100 /* Do IPv6 src addr sel. or use the */ + /* primary jail address. */ /* Internal flag bits */ #define PR_REMOVE 0x01000000 /* In process of being removed */ @@ -362,12 +366,14 @@ int prison_get_ip4(struct ucred *cred, struct in_addr *ia); int prison_local_ip4(struct ucred *cred, struct in_addr *ia); int prison_remote_ip4(struct ucred *cred, struct in_addr *ia); int prison_check_ip4(struct ucred *cred, struct in_addr *ia); +int prison_saddrsel_ip4(struct ucred *, struct in_addr *); #ifdef INET6 int prison_equal_ip6(struct prison *, struct prison *); int prison_get_ip6(struct ucred *, struct in6_addr *); int prison_local_ip6(struct ucred *, struct in6_addr *, int); int prison_remote_ip6(struct ucred *, struct in6_addr *); int prison_check_ip6(struct ucred *, struct in6_addr *); +int prison_saddrsel_ip6(struct ucred *, struct in6_addr *); #endif int prison_check_af(struct ucred *cred, int af); int prison_if(struct ucred *cred, struct sockaddr *sa); diff --git a/usr.sbin/jail/jail.8 b/usr.sbin/jail/jail.8 index 4c1a963a7b0..f09157f4308 100644 --- a/usr.sbin/jail/jail.8 +++ b/usr.sbin/jail/jail.8 @@ -34,7 +34,7 @@ .\" .\" $FreeBSD$ .\" -.Dd October 18, 2009 +.Dd January 17, 2010 .Dt JAIL 8 .Os .Sh NAME @@ -252,6 +252,13 @@ match. It is only possible to start multiple jails with the same IP address, if none of the jails has more than this single overlapping IP address assigned to itself. +.It Va ip4.saddrsel +A boolean option to change the formerly mentioned behaviour and disable +IPv4 source address selection for the prison in favour of the primary +IPv4 address of the jail. +Source address selection is enabled by default for all jails and a +.Va ip4.nosaddrsel +setting of a parent jail is not inherited for any child jails. .It Va ip4 Control the availablity of IPv4 addresses. Possible values are @@ -267,9 +274,10 @@ Setting the .Va ip4.addr parameter implies a value of .Dq new . -.It Va ip6.addr , Va ip6 -A list of IPv6 addresses assigned to the prison, the counterpart to -.Va ip4.addr +.It Va ip6.addr , Va ip6.saddrsel , Va ip6 +A set of IPv6 options for the prison, the counterparts to +.Va ip4.addr , +.Va ip4.saddrsel and .Va ip4 above.