mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-11 00:08:50 +00:00
xfrm: Fix xfrm_state_find() wrt. wildcard source address.
The change to make xfrm_state objects hash on source address broke the case where such source addresses are wildcarded. Fix this by doing a two phase lookup, first with fully specified source address, next using saddr wildcarded. Reported-by: Nicolas Dichtel <nicolas.dichtel@dev.6wind.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
9616a75505
commit
08ec9af1c0
@ -748,12 +748,51 @@ static void xfrm_hash_grow_check(struct net *net, int have_hash_collision)
|
|||||||
schedule_work(&net->xfrm.state_hash_work);
|
schedule_work(&net->xfrm.state_hash_work);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void xfrm_state_look_at(struct xfrm_policy *pol, struct xfrm_state *x,
|
||||||
|
struct flowi *fl, unsigned short family,
|
||||||
|
xfrm_address_t *daddr, xfrm_address_t *saddr,
|
||||||
|
struct xfrm_state **best, int *acq_in_progress,
|
||||||
|
int *error)
|
||||||
|
{
|
||||||
|
/* Resolution logic:
|
||||||
|
* 1. There is a valid state with matching selector. Done.
|
||||||
|
* 2. Valid state with inappropriate selector. Skip.
|
||||||
|
*
|
||||||
|
* Entering area of "sysdeps".
|
||||||
|
*
|
||||||
|
* 3. If state is not valid, selector is temporary, it selects
|
||||||
|
* only session which triggered previous resolution. Key
|
||||||
|
* manager will do something to install a state with proper
|
||||||
|
* selector.
|
||||||
|
*/
|
||||||
|
if (x->km.state == XFRM_STATE_VALID) {
|
||||||
|
if ((x->sel.family &&
|
||||||
|
!xfrm_selector_match(&x->sel, fl, x->sel.family)) ||
|
||||||
|
!security_xfrm_state_pol_flow_match(x, pol, fl))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!*best ||
|
||||||
|
(*best)->km.dying > x->km.dying ||
|
||||||
|
((*best)->km.dying == x->km.dying &&
|
||||||
|
(*best)->curlft.add_time < x->curlft.add_time))
|
||||||
|
*best = x;
|
||||||
|
} else if (x->km.state == XFRM_STATE_ACQ) {
|
||||||
|
*acq_in_progress = 1;
|
||||||
|
} else if (x->km.state == XFRM_STATE_ERROR ||
|
||||||
|
x->km.state == XFRM_STATE_EXPIRED) {
|
||||||
|
if (xfrm_selector_match(&x->sel, fl, x->sel.family) &&
|
||||||
|
security_xfrm_state_pol_flow_match(x, pol, fl))
|
||||||
|
*error = -ESRCH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct xfrm_state *
|
struct xfrm_state *
|
||||||
xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
|
xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
|
||||||
struct flowi *fl, struct xfrm_tmpl *tmpl,
|
struct flowi *fl, struct xfrm_tmpl *tmpl,
|
||||||
struct xfrm_policy *pol, int *err,
|
struct xfrm_policy *pol, int *err,
|
||||||
unsigned short family)
|
unsigned short family)
|
||||||
{
|
{
|
||||||
|
static xfrm_address_t saddr_wildcard = { };
|
||||||
struct net *net = xp_net(pol);
|
struct net *net = xp_net(pol);
|
||||||
unsigned int h;
|
unsigned int h;
|
||||||
struct hlist_node *entry;
|
struct hlist_node *entry;
|
||||||
@ -773,40 +812,27 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
|
|||||||
xfrm_state_addr_check(x, daddr, saddr, family) &&
|
xfrm_state_addr_check(x, daddr, saddr, 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))
|
||||||
/* Resolution logic:
|
xfrm_state_look_at(pol, x, fl, family, daddr, saddr,
|
||||||
1. There is a valid state with matching selector.
|
&best, &acquire_in_progress, &error);
|
||||||
Done.
|
}
|
||||||
2. Valid state with inappropriate selector. Skip.
|
if (best)
|
||||||
|
goto found;
|
||||||
|
|
||||||
Entering area of "sysdeps".
|
h = xfrm_dst_hash(net, daddr, &saddr_wildcard, tmpl->reqid, family);
|
||||||
|
hlist_for_each_entry(x, entry, net->xfrm.state_bydst+h, bydst) {
|
||||||
3. If state is not valid, selector is temporary,
|
if (x->props.family == family &&
|
||||||
it selects only session which triggered
|
x->props.reqid == tmpl->reqid &&
|
||||||
previous resolution. Key manager will do
|
!(x->props.flags & XFRM_STATE_WILDRECV) &&
|
||||||
something to install a state with proper
|
xfrm_state_addr_check(x, daddr, saddr, family) &&
|
||||||
selector.
|
tmpl->mode == x->props.mode &&
|
||||||
*/
|
tmpl->id.proto == x->id.proto &&
|
||||||
if (x->km.state == XFRM_STATE_VALID) {
|
(tmpl->id.spi == x->id.spi || !tmpl->id.spi))
|
||||||
if ((x->sel.family && !xfrm_selector_match(&x->sel, fl, x->sel.family)) ||
|
xfrm_state_look_at(pol, x, fl, family, daddr, saddr,
|
||||||
!security_xfrm_state_pol_flow_match(x, pol, fl))
|
&best, &acquire_in_progress, &error);
|
||||||
continue;
|
|
||||||
if (!best ||
|
|
||||||
best->km.dying > x->km.dying ||
|
|
||||||
(best->km.dying == x->km.dying &&
|
|
||||||
best->curlft.add_time < x->curlft.add_time))
|
|
||||||
best = x;
|
|
||||||
} else if (x->km.state == XFRM_STATE_ACQ) {
|
|
||||||
acquire_in_progress = 1;
|
|
||||||
} else if (x->km.state == XFRM_STATE_ERROR ||
|
|
||||||
x->km.state == XFRM_STATE_EXPIRED) {
|
|
||||||
if (xfrm_selector_match(&x->sel, fl, x->sel.family) &&
|
|
||||||
security_xfrm_state_pol_flow_match(x, pol, fl))
|
|
||||||
error = -ESRCH;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
found:
|
||||||
x = best;
|
x = best;
|
||||||
if (!x && !error && !acquire_in_progress) {
|
if (!x && !error && !acquire_in_progress) {
|
||||||
if (tmpl->id.spi &&
|
if (tmpl->id.spi &&
|
||||||
|
Loading…
x
Reference in New Issue
Block a user