[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