Commit 8444cf71 authored by Thomas Egerer's avatar Thomas Egerer Committed by David S. Miller

xfrm: Allow different selector family in temporary state

The family parameter xfrm_state_find is used to find a state matching a
certain policy. This value is set to the template's family
(encap_family) right before xfrm_state_find is called.
The family parameter is however also used to construct a temporary state
in xfrm_state_find itself which is wrong for inter-family scenarios
because it produces a selector for the wrong family. Since this selector
is included in the xfrm_user_acquire structure, user space programs
misinterpret IPv6 addresses as IPv4 and vice versa.
This patch splits up the original init_tempsel function into a part that
initializes the selector respectively the props and id of the temporary
state, to allow for differing ip address families whithin the state.
Signed-off-by: default avatarThomas Egerer <thomas.egerer@secunet.com>
Signed-off-by: default avatarSteffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 842c74bf
...@@ -298,8 +298,8 @@ struct xfrm_state_afinfo { ...@@ -298,8 +298,8 @@ struct xfrm_state_afinfo {
const struct xfrm_type *type_map[IPPROTO_MAX]; const struct xfrm_type *type_map[IPPROTO_MAX];
struct xfrm_mode *mode_map[XFRM_MODE_MAX]; struct xfrm_mode *mode_map[XFRM_MODE_MAX];
int (*init_flags)(struct xfrm_state *x); int (*init_flags)(struct xfrm_state *x);
void (*init_tempsel)(struct xfrm_state *x, struct flowi *fl, void (*init_tempsel)(struct xfrm_selector *sel, struct flowi *fl);
struct xfrm_tmpl *tmpl, void (*init_temprop)(struct xfrm_state *x, struct xfrm_tmpl *tmpl,
xfrm_address_t *daddr, xfrm_address_t *saddr); xfrm_address_t *daddr, xfrm_address_t *saddr);
int (*tmpl_sort)(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n); int (*tmpl_sort)(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n);
int (*state_sort)(struct xfrm_state **dst, struct xfrm_state **src, int n); int (*state_sort)(struct xfrm_state **dst, struct xfrm_state **src, int n);
......
...@@ -21,21 +21,25 @@ static int xfrm4_init_flags(struct xfrm_state *x) ...@@ -21,21 +21,25 @@ static int xfrm4_init_flags(struct xfrm_state *x)
} }
static void static void
__xfrm4_init_tempsel(struct xfrm_state *x, struct flowi *fl, __xfrm4_init_tempsel(struct xfrm_selector *sel, struct flowi *fl)
struct xfrm_tmpl *tmpl, {
sel->daddr.a4 = fl->fl4_dst;
sel->saddr.a4 = fl->fl4_src;
sel->dport = xfrm_flowi_dport(fl);
sel->dport_mask = htons(0xffff);
sel->sport = xfrm_flowi_sport(fl);
sel->sport_mask = htons(0xffff);
sel->family = AF_INET;
sel->prefixlen_d = 32;
sel->prefixlen_s = 32;
sel->proto = fl->proto;
sel->ifindex = fl->oif;
}
static void
xfrm4_init_temprop(struct xfrm_state *x, struct xfrm_tmpl *tmpl,
xfrm_address_t *daddr, xfrm_address_t *saddr) xfrm_address_t *daddr, xfrm_address_t *saddr)
{ {
x->sel.daddr.a4 = fl->fl4_dst;
x->sel.saddr.a4 = fl->fl4_src;
x->sel.dport = xfrm_flowi_dport(fl);
x->sel.dport_mask = htons(0xffff);
x->sel.sport = xfrm_flowi_sport(fl);
x->sel.sport_mask = htons(0xffff);
x->sel.family = AF_INET;
x->sel.prefixlen_d = 32;
x->sel.prefixlen_s = 32;
x->sel.proto = fl->proto;
x->sel.ifindex = fl->oif;
x->id = tmpl->id; x->id = tmpl->id;
if (x->id.daddr.a4 == 0) if (x->id.daddr.a4 == 0)
x->id.daddr.a4 = daddr->a4; x->id.daddr.a4 = daddr->a4;
...@@ -70,6 +74,7 @@ static struct xfrm_state_afinfo xfrm4_state_afinfo = { ...@@ -70,6 +74,7 @@ static struct xfrm_state_afinfo xfrm4_state_afinfo = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.init_flags = xfrm4_init_flags, .init_flags = xfrm4_init_flags,
.init_tempsel = __xfrm4_init_tempsel, .init_tempsel = __xfrm4_init_tempsel,
.init_temprop = xfrm4_init_temprop,
.output = xfrm4_output, .output = xfrm4_output,
.extract_input = xfrm4_extract_input, .extract_input = xfrm4_extract_input,
.extract_output = xfrm4_extract_output, .extract_output = xfrm4_extract_output,
......
...@@ -20,23 +20,27 @@ ...@@ -20,23 +20,27 @@
#include <net/addrconf.h> #include <net/addrconf.h>
static void static void
__xfrm6_init_tempsel(struct xfrm_state *x, struct flowi *fl, __xfrm6_init_tempsel(struct xfrm_selector *sel, struct flowi *fl)
struct xfrm_tmpl *tmpl,
xfrm_address_t *daddr, xfrm_address_t *saddr)
{ {
/* Initialize temporary selector matching only /* Initialize temporary selector matching only
* to current session. */ * to current session. */
ipv6_addr_copy((struct in6_addr *)&x->sel.daddr, &fl->fl6_dst); ipv6_addr_copy((struct in6_addr *)&sel->daddr, &fl->fl6_dst);
ipv6_addr_copy((struct in6_addr *)&x->sel.saddr, &fl->fl6_src); ipv6_addr_copy((struct in6_addr *)&sel->saddr, &fl->fl6_src);
x->sel.dport = xfrm_flowi_dport(fl); sel->dport = xfrm_flowi_dport(fl);
x->sel.dport_mask = htons(0xffff); sel->dport_mask = htons(0xffff);
x->sel.sport = xfrm_flowi_sport(fl); sel->sport = xfrm_flowi_sport(fl);
x->sel.sport_mask = htons(0xffff); sel->sport_mask = htons(0xffff);
x->sel.family = AF_INET6; sel->family = AF_INET6;
x->sel.prefixlen_d = 128; sel->prefixlen_d = 128;
x->sel.prefixlen_s = 128; sel->prefixlen_s = 128;
x->sel.proto = fl->proto; sel->proto = fl->proto;
x->sel.ifindex = fl->oif; sel->ifindex = fl->oif;
}
static void
xfrm6_init_temprop(struct xfrm_state *x, struct xfrm_tmpl *tmpl,
xfrm_address_t *daddr, xfrm_address_t *saddr)
{
x->id = tmpl->id; x->id = tmpl->id;
if (ipv6_addr_any((struct in6_addr*)&x->id.daddr)) if (ipv6_addr_any((struct in6_addr*)&x->id.daddr))
memcpy(&x->id.daddr, daddr, sizeof(x->sel.daddr)); memcpy(&x->id.daddr, daddr, sizeof(x->sel.daddr));
...@@ -168,6 +172,7 @@ static struct xfrm_state_afinfo xfrm6_state_afinfo = { ...@@ -168,6 +172,7 @@ static struct xfrm_state_afinfo xfrm6_state_afinfo = {
.eth_proto = htons(ETH_P_IPV6), .eth_proto = htons(ETH_P_IPV6),
.owner = THIS_MODULE, .owner = THIS_MODULE,
.init_tempsel = __xfrm6_init_tempsel, .init_tempsel = __xfrm6_init_tempsel,
.init_temprop = xfrm6_init_temprop,
.tmpl_sort = __xfrm6_tmpl_sort, .tmpl_sort = __xfrm6_tmpl_sort,
.state_sort = __xfrm6_state_sort, .state_sort = __xfrm6_state_sort,
.output = xfrm6_output, .output = xfrm6_output,
......
...@@ -1175,9 +1175,8 @@ xfrm_tmpl_resolve_one(struct xfrm_policy *policy, struct flowi *fl, ...@@ -1175,9 +1175,8 @@ xfrm_tmpl_resolve_one(struct xfrm_policy *policy, struct flowi *fl,
tmpl->mode == XFRM_MODE_BEET) { tmpl->mode == XFRM_MODE_BEET) {
remote = &tmpl->id.daddr; remote = &tmpl->id.daddr;
local = &tmpl->saddr; local = &tmpl->saddr;
family = tmpl->encap_family; if (xfrm_addr_any(local, tmpl->encap_family)) {
if (xfrm_addr_any(local, family)) { error = xfrm_get_saddr(net, &tmp, remote, tmpl->encap_family);
error = xfrm_get_saddr(net, &tmp, remote, family);
if (error) if (error)
goto fail; goto fail;
local = &tmp; local = &tmp;
......
...@@ -656,7 +656,7 @@ void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si) ...@@ -656,7 +656,7 @@ void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si)
EXPORT_SYMBOL(xfrm_sad_getinfo); EXPORT_SYMBOL(xfrm_sad_getinfo);
static int static int
xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl, xfrm_init_tempstate(struct xfrm_state *x, struct flowi *fl,
struct xfrm_tmpl *tmpl, struct xfrm_tmpl *tmpl,
xfrm_address_t *daddr, xfrm_address_t *saddr, xfrm_address_t *daddr, xfrm_address_t *saddr,
unsigned short family) unsigned short family)
...@@ -664,7 +664,15 @@ xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl, ...@@ -664,7 +664,15 @@ xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
if (!afinfo) if (!afinfo)
return -1; return -1;
afinfo->init_tempsel(x, fl, tmpl, daddr, saddr); afinfo->init_tempsel(&x->sel, fl);
if (family != tmpl->encap_family) {
xfrm_state_put_afinfo(afinfo);
afinfo = xfrm_state_get_afinfo(tmpl->encap_family);
if (!afinfo)
return -1;
}
afinfo->init_temprop(x, tmpl, daddr, saddr);
xfrm_state_put_afinfo(afinfo); xfrm_state_put_afinfo(afinfo);
return 0; return 0;
} }
...@@ -790,37 +798,38 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, ...@@ -790,37 +798,38 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
int error = 0; int error = 0;
struct xfrm_state *best = NULL; struct xfrm_state *best = NULL;
u32 mark = pol->mark.v & pol->mark.m; u32 mark = pol->mark.v & pol->mark.m;
unsigned short encap_family = tmpl->encap_family;
to_put = NULL; to_put = NULL;
spin_lock_bh(&xfrm_state_lock); spin_lock_bh(&xfrm_state_lock);
h = xfrm_dst_hash(net, daddr, saddr, tmpl->reqid, family); h = xfrm_dst_hash(net, daddr, saddr, tmpl->reqid, encap_family);
hlist_for_each_entry(x, entry, net->xfrm.state_bydst+h, bydst) { hlist_for_each_entry(x, entry, net->xfrm.state_bydst+h, bydst) {
if (x->props.family == family && if (x->props.family == encap_family &&
x->props.reqid == tmpl->reqid && x->props.reqid == tmpl->reqid &&
(mark & x->mark.m) == x->mark.v && (mark & x->mark.m) == x->mark.v &&
!(x->props.flags & XFRM_STATE_WILDRECV) && !(x->props.flags & XFRM_STATE_WILDRECV) &&
xfrm_state_addr_check(x, daddr, saddr, family) && xfrm_state_addr_check(x, daddr, saddr, encap_family) &&
tmpl->mode == x->props.mode && tmpl->mode == x->props.mode &&
tmpl->id.proto == x->id.proto && tmpl->id.proto == x->id.proto &&
(tmpl->id.spi == x->id.spi || !tmpl->id.spi)) (tmpl->id.spi == x->id.spi || !tmpl->id.spi))
xfrm_state_look_at(pol, x, fl, family, daddr, saddr, xfrm_state_look_at(pol, x, fl, encap_family, daddr, saddr,
&best, &acquire_in_progress, &error); &best, &acquire_in_progress, &error);
} }
if (best) if (best)
goto found; goto found;
h_wildcard = xfrm_dst_hash(net, daddr, &saddr_wildcard, tmpl->reqid, family); h_wildcard = xfrm_dst_hash(net, daddr, &saddr_wildcard, tmpl->reqid, encap_family);
hlist_for_each_entry(x, entry, net->xfrm.state_bydst+h_wildcard, bydst) { hlist_for_each_entry(x, entry, net->xfrm.state_bydst+h_wildcard, bydst) {
if (x->props.family == family && if (x->props.family == encap_family &&
x->props.reqid == tmpl->reqid && x->props.reqid == tmpl->reqid &&
(mark & x->mark.m) == x->mark.v && (mark & x->mark.m) == x->mark.v &&
!(x->props.flags & XFRM_STATE_WILDRECV) && !(x->props.flags & XFRM_STATE_WILDRECV) &&
xfrm_state_addr_check(x, daddr, saddr, family) && xfrm_state_addr_check(x, daddr, saddr, encap_family) &&
tmpl->mode == x->props.mode && tmpl->mode == x->props.mode &&
tmpl->id.proto == x->id.proto && tmpl->id.proto == x->id.proto &&
(tmpl->id.spi == x->id.spi || !tmpl->id.spi)) (tmpl->id.spi == x->id.spi || !tmpl->id.spi))
xfrm_state_look_at(pol, x, fl, family, daddr, saddr, xfrm_state_look_at(pol, x, fl, encap_family, daddr, saddr,
&best, &acquire_in_progress, &error); &best, &acquire_in_progress, &error);
} }
...@@ -829,7 +838,7 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, ...@@ -829,7 +838,7 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
if (!x && !error && !acquire_in_progress) { if (!x && !error && !acquire_in_progress) {
if (tmpl->id.spi && if (tmpl->id.spi &&
(x0 = __xfrm_state_lookup(net, mark, daddr, tmpl->id.spi, (x0 = __xfrm_state_lookup(net, mark, daddr, tmpl->id.spi,
tmpl->id.proto, family)) != NULL) { tmpl->id.proto, encap_family)) != NULL) {
to_put = x0; to_put = x0;
error = -EEXIST; error = -EEXIST;
goto out; goto out;
...@@ -839,9 +848,9 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, ...@@ -839,9 +848,9 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
error = -ENOMEM; error = -ENOMEM;
goto out; goto out;
} }
/* Initialize temporary selector matching only /* Initialize temporary state matching only
* to current session. */ * to current session. */
xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family); xfrm_init_tempstate(x, fl, tmpl, daddr, saddr, family);
memcpy(&x->mark, &pol->mark, sizeof(x->mark)); memcpy(&x->mark, &pol->mark, sizeof(x->mark));
error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid); error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
...@@ -856,10 +865,10 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, ...@@ -856,10 +865,10 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
x->km.state = XFRM_STATE_ACQ; x->km.state = XFRM_STATE_ACQ;
list_add(&x->km.all, &net->xfrm.state_all); list_add(&x->km.all, &net->xfrm.state_all);
hlist_add_head(&x->bydst, net->xfrm.state_bydst+h); hlist_add_head(&x->bydst, net->xfrm.state_bydst+h);
h = xfrm_src_hash(net, daddr, saddr, family); h = xfrm_src_hash(net, daddr, saddr, encap_family);
hlist_add_head(&x->bysrc, net->xfrm.state_bysrc+h); hlist_add_head(&x->bysrc, net->xfrm.state_bysrc+h);
if (x->id.spi) { if (x->id.spi) {
h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, family); h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, encap_family);
hlist_add_head(&x->byspi, net->xfrm.state_byspi+h); hlist_add_head(&x->byspi, net->xfrm.state_byspi+h);
} }
x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires; x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment