xfrm_user: ensure user supplied esn replay window is valid
The current code fails to ensure that the netlink message actually contains as many bytes as the header indicates. If a user creates a new state or updates an existing one but does not supply the bytes for the whole ESN replay window, the kernel copies random heap bytes into the replay bitmap, the ones happen to follow the XFRMA_REPLAY_ESN_VAL netlink attribute. This leads to following issues: 1. The replay window has random bits set confusing the replay handling code later on. 2. A malicious user could use this flaw to leak up to ~3.5kB of heap memory when she has access to the XFRM netlink interface (requires CAP_NET_ADMIN). Known users of the ESN replay window are strongSwan and Steffen's iproute2 patch (<http://patchwork.ozlabs.org/patch/85962/>). The latter uses the interface with a bitmap supplied while the former does not. strongSwan is therefore prone to run into issue 1. To fix both issues without breaking existing userland allow using the XFRMA_REPLAY_ESN_VAL netlink attribute with either an empty bitmap or a fully specified one. For the former case we initialize the in-kernel bitmap with zero, for the latter we copy the user supplied bitmap. For state updates the full bitmap must be supplied. To prevent overflows in the bitmap length calculation the maximum size of bmp_len is limited to 128 by this patch -- resulting in a maximum replay window of 4096 packets. This should be sufficient for all real life scenarios (RFC 4303 recommends a default replay window size of 64). Cc: Steffen Klassert <steffen.klassert@secunet.com> Cc: Martin Willi <martin@revosec.ch> Cc: Ben Hutchings <bhutchings@solarflare.com> Signed-off-by: Mathias Krause <minipli@googlemail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
1f86840f89
commit
ecd7918745
|
@ -84,6 +84,8 @@ struct xfrm_replay_state {
|
||||||
__u32 bitmap;
|
__u32 bitmap;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define XFRMA_REPLAY_ESN_MAX 4096
|
||||||
|
|
||||||
struct xfrm_replay_state_esn {
|
struct xfrm_replay_state_esn {
|
||||||
unsigned int bmp_len;
|
unsigned int bmp_len;
|
||||||
__u32 oseq;
|
__u32 oseq;
|
||||||
|
|
|
@ -123,9 +123,21 @@ static inline int verify_replay(struct xfrm_usersa_info *p,
|
||||||
struct nlattr **attrs)
|
struct nlattr **attrs)
|
||||||
{
|
{
|
||||||
struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL];
|
struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL];
|
||||||
|
struct xfrm_replay_state_esn *rs;
|
||||||
|
|
||||||
if ((p->flags & XFRM_STATE_ESN) && !rt)
|
if (p->flags & XFRM_STATE_ESN) {
|
||||||
return -EINVAL;
|
if (!rt)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
rs = nla_data(rt);
|
||||||
|
|
||||||
|
if (rs->bmp_len > XFRMA_REPLAY_ESN_MAX / sizeof(rs->bmp[0]) / 8)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (nla_len(rt) < xfrm_replay_state_esn_len(rs) &&
|
||||||
|
nla_len(rt) != sizeof(*rs))
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
if (!rt)
|
if (!rt)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -370,14 +382,15 @@ static inline int xfrm_replay_verify_len(struct xfrm_replay_state_esn *replay_es
|
||||||
struct nlattr *rp)
|
struct nlattr *rp)
|
||||||
{
|
{
|
||||||
struct xfrm_replay_state_esn *up;
|
struct xfrm_replay_state_esn *up;
|
||||||
|
int ulen;
|
||||||
|
|
||||||
if (!replay_esn || !rp)
|
if (!replay_esn || !rp)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
up = nla_data(rp);
|
up = nla_data(rp);
|
||||||
|
ulen = xfrm_replay_state_esn_len(up);
|
||||||
|
|
||||||
if (xfrm_replay_state_esn_len(replay_esn) !=
|
if (nla_len(rp) < ulen || xfrm_replay_state_esn_len(replay_esn) != ulen)
|
||||||
xfrm_replay_state_esn_len(up))
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -388,22 +401,28 @@ static int xfrm_alloc_replay_state_esn(struct xfrm_replay_state_esn **replay_esn
|
||||||
struct nlattr *rta)
|
struct nlattr *rta)
|
||||||
{
|
{
|
||||||
struct xfrm_replay_state_esn *p, *pp, *up;
|
struct xfrm_replay_state_esn *p, *pp, *up;
|
||||||
|
int klen, ulen;
|
||||||
|
|
||||||
if (!rta)
|
if (!rta)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
up = nla_data(rta);
|
up = nla_data(rta);
|
||||||
|
klen = xfrm_replay_state_esn_len(up);
|
||||||
|
ulen = nla_len(rta) >= klen ? klen : sizeof(*up);
|
||||||
|
|
||||||
p = kmemdup(up, xfrm_replay_state_esn_len(up), GFP_KERNEL);
|
p = kzalloc(klen, GFP_KERNEL);
|
||||||
if (!p)
|
if (!p)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
pp = kmemdup(up, xfrm_replay_state_esn_len(up), GFP_KERNEL);
|
pp = kzalloc(klen, GFP_KERNEL);
|
||||||
if (!pp) {
|
if (!pp) {
|
||||||
kfree(p);
|
kfree(p);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memcpy(p, up, ulen);
|
||||||
|
memcpy(pp, up, ulen);
|
||||||
|
|
||||||
*replay_esn = p;
|
*replay_esn = p;
|
||||||
*preplay_esn = pp;
|
*preplay_esn = pp;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue