[kernel] r18060 - in dists/squeeze-security/linux-2.6/debian: . patches/bugfix/all patches/series

Dann Frazier dannf at alioth.debian.org
Wed Sep 7 04:57:40 UTC 2011


Author: dannf
Date: Wed Sep  7 04:57:39 2011
New Revision: 18060

Log:
ipv6: make fragment identifications less predictable (CVE-2011-2699)

Added:
   dists/squeeze-security/linux-2.6/debian/patches/bugfix/all/inetpeer-optimize-inet_getid.patch
   dists/squeeze-security/linux-2.6/debian/patches/bugfix/all/ipv6-make-fragment-identifications-less-predictable.patch
   dists/squeeze-security/linux-2.6/debian/patches/series/35squeeze2
Modified:
   dists/squeeze-security/linux-2.6/debian/changelog

Modified: dists/squeeze-security/linux-2.6/debian/changelog
==============================================================================
--- dists/squeeze-security/linux-2.6/debian/changelog	Wed Sep  7 04:57:33 2011	(r18059)
+++ dists/squeeze-security/linux-2.6/debian/changelog	Wed Sep  7 04:57:39 2011	(r18060)
@@ -1,3 +1,9 @@
+linux-2.6 (2.6.32-35squeeze2) UNRELEASED; urgency=low
+
+  * ipv6: make fragment identifications less predictable (CVE-2011-2699)
+
+ -- dann frazier <dannf at debian.org>  Tue, 06 Sep 2011 15:58:45 -0600
+
 linux-2.6 (2.6.32-35squeeze1) stable-security; urgency=high
 
   [ dann frazier ]

Added: dists/squeeze-security/linux-2.6/debian/patches/bugfix/all/inetpeer-optimize-inet_getid.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/squeeze-security/linux-2.6/debian/patches/bugfix/all/inetpeer-optimize-inet_getid.patch	Wed Sep  7 04:57:39 2011	(r18060)
@@ -0,0 +1,147 @@
+commit 2c1409a0a2b88585ec0c03f1de0aafa178c56313
+Author: Eric Dumazet <eric.dumazet at gmail.com>
+Date:   Thu Nov 12 09:33:09 2009 +0000
+
+    inetpeer: Optimize inet_getid()
+    
+    While investigating for network latencies, I found inet_getid() was a
+    contention point for some workloads, as inet_peer_idlock is shared
+    by all inet_getid() users regardless of peers.
+    
+    One way to fix this is to make ip_id_count an atomic_t instead
+    of __u16, and use atomic_add_return().
+    
+    In order to keep sizeof(struct inet_peer) = 64 on 64bit arches
+    tcp_ts_stamp is also converted to __u32 instead of "unsigned long".
+    
+    Signed-off-by: Eric Dumazet <eric.dumazet at gmail.com>
+    Signed-off-by: David S. Miller <davem at davemloft.net>
+    [dannf: backported to Debian's 2.6.32]
+
+diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h
+index 15e1f8f..7f0571e 100644
+--- a/include/net/inetpeer.h
++++ b/include/net/inetpeer.h
+@@ -18,15 +18,15 @@ struct inet_peer
+ 	/* group together avl_left,avl_right,v4daddr to speedup lookups */
+ 	struct inet_peer	*avl_left, *avl_right;
+ 	__be32			v4daddr;	/* peer's address */
+-	__u16			avl_height;
+-	__u16			ip_id_count;	/* IP ID for the next packet */
++	__u32			avl_height;
+ 	struct list_head	unused;
+ 	__u32			dtime;		/* the time of last use of not
+ 						 * referenced entries */
+ 	atomic_t		refcnt;
+ 	atomic_t		rid;		/* Frag reception counter */
++	atomic_t		ip_id_count;	/* IP ID for the next packet */
+ 	__u32			tcp_ts;
+-	unsigned long		tcp_ts_stamp;
++	__u32			tcp_ts_stamp;
+ };
+ 
+ void			inet_initpeers(void) __init;
+@@ -37,17 +37,11 @@ struct inet_peer	*inet_getpeer(__be32 daddr, int create);
+ /* can be called from BH context or outside */
+ extern void inet_putpeer(struct inet_peer *p);
+ 
+-extern spinlock_t inet_peer_idlock;
+ /* can be called with or without local BH being disabled */
+ static inline __u16	inet_getid(struct inet_peer *p, int more)
+ {
+-	__u16 id;
+-
+-	spin_lock_bh(&inet_peer_idlock);
+-	id = p->ip_id_count;
+-	p->ip_id_count += 1 + more;
+-	spin_unlock_bh(&inet_peer_idlock);
+-	return id;
++	more++;
++	return atomic_add_return(more, &p->ip_id_count) - more;
+ }
+ 
+ #endif /* _NET_INETPEER_H */
+diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c
+index b1fbe18..6bcfe52 100644
+--- a/net/ipv4/inetpeer.c
++++ b/net/ipv4/inetpeer.c
+@@ -67,9 +67,6 @@
+  *		ip_id_count: idlock
+  */
+ 
+-/* Exported for inet_getid inline function.  */
+-DEFINE_SPINLOCK(inet_peer_idlock);
+-
+ static struct kmem_cache *peer_cachep __read_mostly;
+ 
+ #define node_height(x) x->avl_height
+@@ -390,7 +387,7 @@ struct inet_peer *inet_getpeer(__be32 daddr, int create)
+ 	n->v4daddr = daddr;
+ 	atomic_set(&n->refcnt, 1);
+ 	atomic_set(&n->rid, 0);
+-	n->ip_id_count = secure_ip_id(daddr);
++	atomic_set(&n->ip_id_count, secure_ip_id(daddr));
+ 	n->tcp_ts_stamp = 0;
+ 
+ 	write_lock_bh(&peer_pool_lock);
+diff --git a/net/ipv4/route.c b/net/ipv4/route.c
+index 6c8f6c9..46fe5f6 100644
+--- a/net/ipv4/route.c
++++ b/net/ipv4/route.c
+@@ -2860,7 +2860,7 @@ static int rt_fill_info(struct net *net,
+ 	error = rt->u.dst.error;
+ 	expires = rt->u.dst.expires ? rt->u.dst.expires - jiffies : 0;
+ 	if (rt->peer) {
+-		id = rt->peer->ip_id_count;
++		id = atomic_read(&rt->peer->ip_id_count) & 0xffff;
+ 		if (rt->peer->tcp_ts_stamp) {
+ 			ts = rt->peer->tcp_ts;
+ 			tsage = get_seconds() - rt->peer->tcp_ts_stamp;
+diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
+index ea69003..f58c9da 100644
+--- a/net/ipv4/tcp_ipv4.c
++++ b/net/ipv4/tcp_ipv4.c
+@@ -204,7 +204,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
+ 		 * when trying new connection.
+ 		 */
+ 		if (peer != NULL &&
+-		    peer->tcp_ts_stamp + TCP_PAWS_MSL >= get_seconds()) {
++		    (u32)get_seconds() - peer->tcp_ts_stamp <= TCP_PAWS_MSL) {
+ 			tp->rx_opt.ts_recent_stamp = peer->tcp_ts_stamp;
+ 			tp->rx_opt.ts_recent = peer->tcp_ts;
+ 		}
+@@ -1304,7 +1304,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
+ 		    (dst = inet_csk_route_req(sk, req)) != NULL &&
+ 		    (peer = rt_get_peer((struct rtable *)dst)) != NULL &&
+ 		    peer->v4daddr == saddr) {
+-			if (get_seconds() < peer->tcp_ts_stamp + TCP_PAWS_MSL &&
++			if ((u32)get_seconds() - peer->tcp_ts_stamp < TCP_PAWS_MSL &&
+ 			    (s32)(peer->tcp_ts - req->ts_recent) >
+ 							TCP_PAWS_WINDOW) {
+ 				NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED);
+@@ -1724,9 +1724,9 @@ int tcp_v4_remember_stamp(struct sock *sk)
+ 
+ 	if (peer) {
+ 		if ((s32)(peer->tcp_ts - tp->rx_opt.ts_recent) <= 0 ||
+-		    (peer->tcp_ts_stamp + TCP_PAWS_MSL < get_seconds() &&
+-		     peer->tcp_ts_stamp <= tp->rx_opt.ts_recent_stamp)) {
+-			peer->tcp_ts_stamp = tp->rx_opt.ts_recent_stamp;
++		    ((u32)get_seconds() - peer->tcp_ts_stamp > TCP_PAWS_MSL &&
++		     peer->tcp_ts_stamp <= (u32)tp->rx_opt.ts_recent_stamp)) {
++			peer->tcp_ts_stamp = (u32)tp->rx_opt.ts_recent_stamp;
+ 			peer->tcp_ts = tp->rx_opt.ts_recent;
+ 		}
+ 		if (release_it)
+@@ -1745,9 +1745,9 @@ int tcp_v4_tw_remember_stamp(struct inet_timewait_sock *tw)
+ 		const struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw);
+ 
+ 		if ((s32)(peer->tcp_ts - tcptw->tw_ts_recent) <= 0 ||
+-		    (peer->tcp_ts_stamp + TCP_PAWS_MSL < get_seconds() &&
+-		     peer->tcp_ts_stamp <= tcptw->tw_ts_recent_stamp)) {
+-			peer->tcp_ts_stamp = tcptw->tw_ts_recent_stamp;
++		    ((u32)get_seconds() - peer->tcp_ts_stamp > TCP_PAWS_MSL &&
++		     peer->tcp_ts_stamp <= (u32)tcptw->tw_ts_recent_stamp)) {
++			peer->tcp_ts_stamp = (u32)tcptw->tw_ts_recent_stamp;
+ 			peer->tcp_ts	   = tcptw->tw_ts_recent;
+ 		}
+ 		inet_putpeer(peer);

Added: dists/squeeze-security/linux-2.6/debian/patches/bugfix/all/ipv6-make-fragment-identifications-less-predictable.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/squeeze-security/linux-2.6/debian/patches/bugfix/all/ipv6-make-fragment-identifications-less-predictable.patch	Wed Sep  7 04:57:39 2011	(r18060)
@@ -0,0 +1,235 @@
+commit 87c48fa3b4630905f98268dde838ee43626a060c
+Author: Eric Dumazet <eric.dumazet at gmail.com>
+Date:   Thu Jul 21 21:25:58 2011 -0700
+
+    ipv6: make fragment identifications less predictable
+    
+    IPv6 fragment identification generation is way beyond what we use for
+    IPv4 : It uses a single generator. Its not scalable and allows DOS
+    attacks.
+    
+    Now inetpeer is IPv6 aware, we can use it to provide a more secure and
+    scalable frag ident generator (per destination, instead of system wide)
+    
+    This patch :
+    1) defines a new secure_ipv6_id() helper
+    2) extends inet_getid() to provide 32bit results
+    3) extends ipv6_select_ident() with a new dest parameter
+    
+    Reported-by: Fernando Gont <fernando at gont.com.ar>
+    Signed-off-by: Eric Dumazet <eric.dumazet at gmail.com>
+    Signed-off-by: David S. Miller <davem at davemloft.net>
+    [dannf: backported to Debian's 2.6.32]
+
+diff --git a/drivers/char/random.c b/drivers/char/random.c
+index 908ac1f..552ceff 100644
+--- a/drivers/char/random.c
++++ b/drivers/char/random.c
+@@ -1562,6 +1562,21 @@ __u32 secure_ip_id(__be32 daddr)
+ 	return half_md4_transform(hash, keyptr->secret);
+ }
+ 
++__u32 secure_ipv6_id(const __be32 daddr[4])
++{
++	const struct keydata *keyptr;
++	__u32 hash[4];
++
++	keyptr = get_keyptr();
++
++	hash[0] = (__force __u32)daddr[0];
++	hash[1] = (__force __u32)daddr[1];
++	hash[2] = (__force __u32)daddr[2];
++	hash[3] = (__force __u32)daddr[3];
++
++	return half_md4_transform(hash, keyptr->secret);
++}
++
+ #ifdef CONFIG_INET
+ 
+ __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr,
+diff --git a/include/linux/random.h b/include/linux/random.h
+index 25d02fe..234b708 100644
+--- a/include/linux/random.h
++++ b/include/linux/random.h
+@@ -54,6 +54,7 @@ extern void get_random_bytes(void *buf, int nbytes);
+ void generate_random_uuid(unsigned char uuid_out[16]);
+ 
+ extern __u32 secure_ip_id(__be32 daddr);
++extern __u32 secure_ipv6_id(const __be32 daddr[4]);
+ extern u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport);
+ extern u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr,
+ 				      __be16 dport);
+diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h
+index 7f0571e..b57e782 100644
+--- a/include/net/inetpeer.h
++++ b/include/net/inetpeer.h
+@@ -32,16 +32,23 @@ struct inet_peer
+ void			inet_initpeers(void) __init;
+ 
+ /* can be called with or without local BH being disabled */
+-struct inet_peer	*inet_getpeer(__be32 daddr, int create);
++struct inet_peer	*inet_getpeer(const __be32 daddr, int create);
+ 
+ /* can be called from BH context or outside */
+ extern void inet_putpeer(struct inet_peer *p);
+ 
+ /* can be called with or without local BH being disabled */
+-static inline __u16	inet_getid(struct inet_peer *p, int more)
++static inline int inet_getid(struct inet_peer *p, int more)
+ {
++	int old, new;
+ 	more++;
+-	return atomic_add_return(more, &p->ip_id_count) - more;
++	do {
++		old = atomic_read(&p->ip_id_count);
++		new = old + more;
++		if (!new)
++			new = 1;
++	} while (atomic_cmpxchg(&p->ip_id_count, old, new) != old);
++	return new;
+ }
+ 
+ #endif /* _NET_INETPEER_H */
+diff --git a/include/net/ipv6.h b/include/net/ipv6.h
+index 639bbf0..52d86da 100644
+--- a/include/net/ipv6.h
++++ b/include/net/ipv6.h
+@@ -449,17 +449,7 @@ static inline int ipv6_addr_diff(const struct in6_addr *a1, const struct in6_add
+ 	return __ipv6_addr_diff(a1, a2, sizeof(struct in6_addr));
+ }
+ 
+-static __inline__ void ipv6_select_ident(struct frag_hdr *fhdr)
+-{
+-	static u32 ipv6_fragmentation_id = 1;
+-	static DEFINE_SPINLOCK(ip6_id_lock);
+-
+-	spin_lock_bh(&ip6_id_lock);
+-	fhdr->identification = htonl(ipv6_fragmentation_id);
+-	if (++ipv6_fragmentation_id == 0)
+-		ipv6_fragmentation_id = 1;
+-	spin_unlock_bh(&ip6_id_lock);
+-}
++extern void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt);
+ 
+ /*
+  *	Prototypes exported by ipv6
+diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c
+index 6bcfe52..38acb4b 100644
+--- a/net/ipv4/inetpeer.c
++++ b/net/ipv4/inetpeer.c
+@@ -358,7 +358,7 @@ static int cleanup_once(unsigned long ttl)
+ }
+ 
+ /* Called with or without local BH being disabled. */
+-struct inet_peer *inet_getpeer(__be32 daddr, int create)
++struct inet_peer *inet_getpeer(const __be32 daddr, int create)
+ {
+ 	struct inet_peer *p, *n;
+ 	struct inet_peer **stack[PEER_MAXDEPTH], ***stackptr;
+@@ -387,7 +387,10 @@ struct inet_peer *inet_getpeer(__be32 daddr, int create)
+ 	n->v4daddr = daddr;
+ 	atomic_set(&n->refcnt, 1);
+ 	atomic_set(&n->rid, 0);
+-	atomic_set(&n->ip_id_count, secure_ip_id(daddr));
++	atomic_set(&n->ip_id_count,
++			(daddr->family == AF_INET) ?
++				secure_ip_id(daddr) :
++				secure_ipv6_id(daddr));
+ 	n->tcp_ts_stamp = 0;
+ 
+ 	write_lock_bh(&peer_pool_lock);
+diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
+index eca3ef7..9ab64cc 100644
+--- a/net/ipv6/ip6_output.c
++++ b/net/ipv6/ip6_output.c
+@@ -604,6 +604,31 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
+ 	return offset;
+ }
+ 
++void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt)
++{
++	static atomic_t ipv6_fragmentation_id;
++	int old, new;
++
++	if (rt) {
++		struct inet_peer *peer;
++
++		if (!rt->rt6i_peer)
++			rt6_bind_peer(rt, 1);
++		peer = rt->rt6i_peer;
++		if (peer) {
++			fhdr->identification = htonl(inet_getid(peer, 0));
++			return;
++		}
++	}
++	do {
++		old = atomic_read(&ipv6_fragmentation_id);
++		new = old + 1;
++		if (!new)
++			new = 1;
++	} while (atomic_cmpxchg(&ipv6_fragmentation_id, old, new) != old);
++	fhdr->identification = htonl(new);
++}
++
+ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
+ {
+ 	struct sk_buff *frag;
+@@ -689,7 +714,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
+ 		skb_reset_network_header(skb);
+ 		memcpy(skb_network_header(skb), tmp_hdr, hlen);
+ 
+-		ipv6_select_ident(fh);
++		ipv6_select_ident(fh, rt);
+ 		fh->nexthdr = nexthdr;
+ 		fh->reserved = 0;
+ 		fh->frag_off = htons(IP6_MF);
+@@ -835,7 +860,7 @@ slow_path:
+ 		fh->nexthdr = nexthdr;
+ 		fh->reserved = 0;
+ 		if (!frag_id) {
+-			ipv6_select_ident(fh);
++			ipv6_select_ident(fh, rt);
+ 			frag_id = fh->identification;
+ 		} else
+ 			fh->identification = frag_id;
+@@ -1039,7 +1064,8 @@ static inline int ip6_ufo_append_data(struct sock *sk,
+ 			int getfrag(void *from, char *to, int offset, int len,
+ 			int odd, struct sk_buff *skb),
+ 			void *from, int length, int hh_len, int fragheaderlen,
+-			int transhdrlen, int mtu,unsigned int flags)
++			int transhdrlen, int mtu,unsigned int flags,
++			struct rt6_info *rt)
+ 
+ {
+ 	struct sk_buff *skb;
+@@ -1084,7 +1110,7 @@ static inline int ip6_ufo_append_data(struct sock *sk,
+ 		skb_shinfo(skb)->gso_size = (mtu - fragheaderlen -
+ 					     sizeof(struct frag_hdr)) & ~7;
+ 		skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
+-		ipv6_select_ident(&fhdr);
++		ipv6_select_ident(&fhdr, rt);
+ 		skb_shinfo(skb)->ip6_frag_id = fhdr.identification;
+ 		__skb_queue_tail(&sk->sk_write_queue, skb);
+ 
+@@ -1233,7 +1259,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
+ 
+ 		err = ip6_ufo_append_data(sk, getfrag, from, length, hh_len,
+ 					  fragheaderlen, transhdrlen, mtu,
+-					  flags);
++					  flags, rt);
+ 		if (err)
+ 			goto error;
+ 		return 0;
+diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
+index 154dd6b..f5ff5d3 100644
+--- a/net/ipv6/udp.c
++++ b/net/ipv6/udp.c
+@@ -1167,7 +1167,7 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, int features)
+ 	fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen);
+ 	fptr->nexthdr = nexthdr;
+ 	fptr->reserved = 0;
+-	ipv6_select_ident(fptr);
++	ipv6_select_ident(fptr, (struct rt6_info *)skb_dst(skb));
+ 
+ 	/* Fragment the skb. ipv6 header and the remaining fields of the
+ 	 * fragment header are updated in ipv6_gso_segment()

Added: dists/squeeze-security/linux-2.6/debian/patches/series/35squeeze2
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/squeeze-security/linux-2.6/debian/patches/series/35squeeze2	Wed Sep  7 04:57:39 2011	(r18060)
@@ -0,0 +1,2 @@
++ bugfix/all/inetpeer-optimize-inet_getid.patch
++ bugfix/all/ipv6-make-fragment-identifications-less-predictable.patch



More information about the Kernel-svn-changes mailing list