CVE-2012-6536: Exposure of Sensitive Information to an Unauthorized Actor (CWE-200)
value: net/xfrm/xfrm_user.c in the Linux kernel before 3.6 does not verify that the actual Netlink message length is consistent with a certain header field, which allows local users to obtain sensitive information from kernel heap memory by leveraging the CAP_NET_ADMIN capability and providing a (1) new or (2) updated state.
CWE description: The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information.
Published: 2013-03-15T20:55Z
Repository: https://github.com/torvalds/linux
Language: C
Files changed: include/linux/xfrm.h, net/xfrm/xfrm_user.c
Commit date: 2012-09-20 18:08:40 -0400
Commit message: 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>

Diff: diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h
index 22e61fdf75a2b..28e493b5b94c9 100644
--- a/include/linux/xfrm.h
+++ b/include/linux/xfrm.h
@@ -82,10 +82,12 @@ struct xfrm_replay_state {
 	__u32	oseq;
 	__u32	seq;
 	__u32	bitmap;
 };
 
+#define XFRMA_REPLAY_ESN_MAX	4096
+
 struct xfrm_replay_state_esn {
 	unsigned int	bmp_len;
 	__u32		oseq;
 	__u32		seq;
 	__u32		oseq_hi;
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 8024b3dea8c2d..5927065e97cf9 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -121,13 +121,25 @@ static inline int verify_sec_ctx_len(struct nlattr **attrs)
 
 static inline int verify_replay(struct xfrm_usersa_info *p,
 				struct nlattr **attrs)
 {
 	struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL];
+	struct xfrm_replay_state_esn *rs;
 
-	if ((p->flags & XFRM_STATE_ESN) && !rt)
-		return -EINVAL;
+	if (p->flags & XFRM_STATE_ESN) {
+		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)
 		return 0;
 
 	if (p->id.proto != IPPROTO_ESP)
@@ -368,44 +380,51 @@ static int attach_aead(struct xfrm_algo_aead **algpp, u8 *props,
 
 static inline int xfrm_replay_verify_len(struct xfrm_replay_state_esn *replay_esn,
 					 struct nlattr *rp)
 {
 	struct xfrm_replay_state_esn *up;
+	int ulen;
 
 	if (!replay_esn || !rp)
 		return 0;
 
 	up = nla_data(rp);
+	ulen = xfrm_replay_state_esn_len(up);
 
-	if (xfrm_replay_state_esn_len(replay_esn) !=
-			xfrm_replay_state_esn_len(up))
+	if (nla_len(rp) < ulen || xfrm_replay_state_esn_len(replay_esn) != ulen)
 		return -EINVAL;
 
 	return 0;
 }
 
 static int xfrm_alloc_replay_state_esn(struct xfrm_replay_state_esn **replay_esn,
 				       struct xfrm_replay_state_esn **preplay_esn,
 				       struct nlattr *rta)
 {
 	struct xfrm_replay_state_esn *p, *pp, *up;
+	int klen, ulen;
 
 	if (!rta)
 		return 0;
 
 	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)
 		return -ENOMEM;
 
-	pp = kmemdup(up, xfrm_replay_state_esn_len(up), GFP_KERNEL);
+	pp = kzalloc(klen, GFP_KERNEL);
 	if (!pp) {
 		kfree(p);
 		return -ENOMEM;
 	}
 
+	memcpy(p, up, ulen);
+	memcpy(pp, up, ulen);
+
 	*replay_esn = p;
 	*preplay_esn = pp;
 
 	return 0;
 }

Vulnerable code: 	if ((p->flags & XFRM_STATE_ESN) && !rt)
		return -EINVAL;
	if (xfrm_replay_state_esn_len(replay_esn) !=
			xfrm_replay_state_esn_len(up))
	p = kmemdup(up, xfrm_replay_state_esn_len(up), GFP_KERNEL);
	pp = kmemdup(up, xfrm_replay_state_esn_len(up), GFP_KERNEL);
Fixed code: #define XFRMA_REPLAY_ESN_MAX	4096

	struct xfrm_replay_state_esn *rs;
	if (p->flags & XFRM_STATE_ESN) {
		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;
	}
	int ulen;
	ulen = xfrm_replay_state_esn_len(up);
	if (nla_len(rp) < ulen || xfrm_replay_state_esn_len(replay_esn) != ulen)
	int klen, ulen;
	klen = xfrm_replay_state_esn_len(up);
	ulen = nla_len(rta) >= klen ? klen : sizeof(*up);
	p = kzalloc(klen, GFP_KERNEL);
	pp = kzalloc(klen, GFP_KERNEL);
	memcpy(p, up, ulen);
	memcpy(pp, up, ulen);