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: Thomas Egerer <thomas.egerer@secunet.com> Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
committed by
David S. Miller
parent
842c74bffc
commit
8444cf712c
@ -656,15 +656,23 @@ void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si)
|
||||
EXPORT_SYMBOL(xfrm_sad_getinfo);
|
||||
|
||||
static int
|
||||
xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
|
||||
struct xfrm_tmpl *tmpl,
|
||||
xfrm_address_t *daddr, xfrm_address_t *saddr,
|
||||
unsigned short family)
|
||||
xfrm_init_tempstate(struct xfrm_state *x, struct flowi *fl,
|
||||
struct xfrm_tmpl *tmpl,
|
||||
xfrm_address_t *daddr, xfrm_address_t *saddr,
|
||||
unsigned short family)
|
||||
{
|
||||
struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
|
||||
if (!afinfo)
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
@ -790,37 +798,38 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
|
||||
int error = 0;
|
||||
struct xfrm_state *best = NULL;
|
||||
u32 mark = pol->mark.v & pol->mark.m;
|
||||
unsigned short encap_family = tmpl->encap_family;
|
||||
|
||||
to_put = NULL;
|
||||
|
||||
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) {
|
||||
if (x->props.family == family &&
|
||||
if (x->props.family == encap_family &&
|
||||
x->props.reqid == tmpl->reqid &&
|
||||
(mark & x->mark.m) == x->mark.v &&
|
||||
!(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->id.proto == x->id.proto &&
|
||||
(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);
|
||||
}
|
||||
if (best)
|
||||
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) {
|
||||
if (x->props.family == family &&
|
||||
if (x->props.family == encap_family &&
|
||||
x->props.reqid == tmpl->reqid &&
|
||||
(mark & x->mark.m) == x->mark.v &&
|
||||
!(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->id.proto == x->id.proto &&
|
||||
(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);
|
||||
}
|
||||
|
||||
@ -829,7 +838,7 @@ found:
|
||||
if (!x && !error && !acquire_in_progress) {
|
||||
if (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;
|
||||
error = -EEXIST;
|
||||
goto out;
|
||||
@ -839,9 +848,9 @@ found:
|
||||
error = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
/* Initialize temporary selector matching only
|
||||
/* Initialize temporary state matching only
|
||||
* 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));
|
||||
|
||||
error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
|
||||
@ -856,10 +865,10 @@ found:
|
||||
x->km.state = XFRM_STATE_ACQ;
|
||||
list_add(&x->km.all, &net->xfrm.state_all);
|
||||
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);
|
||||
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);
|
||||
}
|
||||
x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires;
|
||||
|
Reference in New Issue
Block a user