aboutsummaryrefslogtreecommitdiffstats
path: root/net/xfrm
diff options
context:
space:
mode:
authorMathias Krause <minipli@googlemail.com>2012-09-20 06:01:49 -0400
committerDavid S. Miller <davem@davemloft.net>2012-09-20 18:08:40 -0400
commitecd7918745234e423dd87fcc0c077da557909720 (patch)
tree7435863b7aa3822b3c927264c5cb00da7783896b /net/xfrm
parent1f86840f897717f86d523a13e99a447e6a5d2fa5 (diff)
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>
Diffstat (limited to 'net/xfrm')
-rw-r--r--net/xfrm/xfrm_user.c31
1 files changed, 25 insertions, 6 deletions
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 8024b3dea8c2..5927065e97cf 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -123,9 +123,21 @@ static inline int verify_replay(struct xfrm_usersa_info *p,
123 struct nlattr **attrs) 123 struct nlattr **attrs)
124{ 124{
125 struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL]; 125 struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL];
126 struct xfrm_replay_state_esn *rs;
126 127
127 if ((p->flags & XFRM_STATE_ESN) && !rt) 128 if (p->flags & XFRM_STATE_ESN) {
128 return -EINVAL; 129 if (!rt)
130 return -EINVAL;
131
132 rs = nla_data(rt);
133
134 if (rs->bmp_len > XFRMA_REPLAY_ESN_MAX / sizeof(rs->bmp[0]) / 8)
135 return -EINVAL;
136
137 if (nla_len(rt) < xfrm_replay_state_esn_len(rs) &&
138 nla_len(rt) != sizeof(*rs))
139 return -EINVAL;
140 }
129 141
130 if (!rt) 142 if (!rt)
131 return 0; 143 return 0;
@@ -370,14 +382,15 @@ static inline int xfrm_replay_verify_len(struct xfrm_replay_state_esn *replay_es
370 struct nlattr *rp) 382 struct nlattr *rp)
371{ 383{
372 struct xfrm_replay_state_esn *up; 384 struct xfrm_replay_state_esn *up;
385 int ulen;
373 386
374 if (!replay_esn || !rp) 387 if (!replay_esn || !rp)
375 return 0; 388 return 0;
376 389
377 up = nla_data(rp); 390 up = nla_data(rp);
391 ulen = xfrm_replay_state_esn_len(up);
378 392
379 if (xfrm_replay_state_esn_len(replay_esn) != 393 if (nla_len(rp) < ulen || xfrm_replay_state_esn_len(replay_esn) != ulen)
380 xfrm_replay_state_esn_len(up))
381 return -EINVAL; 394 return -EINVAL;
382 395
383 return 0; 396 return 0;
@@ -388,22 +401,28 @@ static int xfrm_alloc_replay_state_esn(struct xfrm_replay_state_esn **replay_esn
388 struct nlattr *rta) 401 struct nlattr *rta)
389{ 402{
390 struct xfrm_replay_state_esn *p, *pp, *up; 403 struct xfrm_replay_state_esn *p, *pp, *up;
404 int klen, ulen;
391 405
392 if (!rta) 406 if (!rta)
393 return 0; 407 return 0;
394 408
395 up = nla_data(rta); 409 up = nla_data(rta);
410 klen = xfrm_replay_state_esn_len(up);
411 ulen = nla_len(rta) >= klen ? klen : sizeof(*up);
396 412
397 p = kmemdup(up, xfrm_replay_state_esn_len(up), GFP_KERNEL); 413 p = kzalloc(klen, GFP_KERNEL);
398 if (!p) 414 if (!p)
399 return -ENOMEM; 415 return -ENOMEM;
400 416
401 pp = kmemdup(up, xfrm_replay_state_esn_len(up), GFP_KERNEL); 417 pp = kzalloc(klen, GFP_KERNEL);
402 if (!pp) { 418 if (!pp) {
403 kfree(p); 419 kfree(p);
404 return -ENOMEM; 420 return -ENOMEM;
405 } 421 }
406 422
423 memcpy(p, up, ulen);
424 memcpy(pp, up, ulen);
425
407 *replay_esn = p; 426 *replay_esn = p;
408 *preplay_esn = pp; 427 *preplay_esn = pp;
409 428