linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] OpenBSD Networking-related randomization port
@ 2005-01-28 17:17 Lorenzo Hernández García-Hierro
  2005-01-28 17:40 ` Adrian Bunk
                   ` (5 more replies)
  0 siblings, 6 replies; 43+ messages in thread
From: Lorenzo Hernández García-Hierro @ 2005-01-28 17:17 UTC (permalink / raw)
  To: linux-kernel; +Cc: torvalds


[-- Attachment #1.1: Type: text/plain, Size: 2045 bytes --]

Hi,

Attached you can find a split up patch ported from grSecurity [1], as
Linus commented that he wouldn't get a whole-sale patch, I was working
on it and also studying what features of grSecurity can be implemented
without a development or maintenance overhead, aka less-invasive
implementations.

It adds support for advanced networking-related randomization, in
concrete it adds support for TCP ISNs randomization, RPC XIDs
randomization, IP IDs randomization and finally a sub-key under the
Cryptographic options menu for Linux PRNG [2] enhancements (useful now
and also for future patch submissions), which currently has an only-one
option for poll sizes increasing (x2).

As it's impact is minimal (in performance and development/maintenance
terms), I recommend to merge it, as it gives a basic prevention for the
so-called system fingerprinting (which is used most by "kids" to know
how old and insecure could be a target system, many time used as the
first, even only-one, data to decide if attack or not the target host)
among other things.

There's only a missing feature that is present on grSecurity, the
sources ports randomization which seems achieved now by some changes
that can be checked out in the Linux BKBits repository:
http://linux.bkbits.net:8080/linux-2.6/diffs/net/ipv4/tcp_ipv4.c@1.105?nav=index.html|src/|src/net|src/net/ipv4|hist/net/ipv4/tcp_ipv4.c
(net/ipv4/tcp_ipv4.c@1.105)

I'm not sure of the effectiveness of that changes, but I just prefer to
keep it as most simple as possible.If there are thoughts on reverting to
the old schema, and using obsd_rand.c code instead, just drop me a line
and I will modify the patch.

I've uploaded the patches and obsd_rand.c source to:
http://cvs.tuxedo-es.org/cgi-bin/viewcvs.cgi/obsd-netrand/

References:	
 [1]: http://www.grsecurity.net
 [2]: http://en.wikipedia.org/wiki/Pseudorandom_number_generator

Cheers,
-- 
Lorenzo Hernández García-Hierro <lorenzo@gnu.org> 
[1024D/6F2B2DEC] & [2048g/9AE91A22][http://tuxedo-es.org]

[-- Attachment #1.2: openbsd-netrand-2.6.11-rc2.patch --]
[-- Type: text/x-patch, Size: 16210 bytes --]

diff -Nur linux-2.6.11-rc2/crypto/Kconfig linux-2.6.11-rc2.tx1/crypto/Kconfig
--- linux-2.6.11-rc2/crypto/Kconfig	2005-01-26 19:54:06.000000000 +0100
+++ linux-2.6.11-rc2.tx1/crypto/Kconfig	2005-01-28 17:05:39.000000000 +0100
@@ -4,6 +4,26 @@
 
 menu "Cryptographic options"
 
+config RANDOM
+	bool "Random Numbers Generator enhancements"
+	help
+	  By enabling this option, you will be able to select
+	  a few enhancements for the Linux Random Numbers Generator
+	  (./drivers/char/random.{h,c}).
+	  
+	  If unsure: say Y.
+	  
+config RANDOM_INCREASED_POOLSIZES
+	bool "Larger entropy pools"
+	depends on RANDOM
+	help
+	  If you say Y here, the entropy pools used for many features of Linux
+          will be doubled in size. It is recommended that you say Y
+          here.  Saying Y here has a similar effect as modifying
+          /proc/sys/kernel/random/poolsize.
+	  
+	  If unsure: say Y.
+
 config CRYPTO
 	bool "Cryptographic API"
 	help
diff -Nur linux-2.6.11-rc2/drivers/char/random.c linux-2.6.11-rc2.tx1/drivers/char/random.c
--- linux-2.6.11-rc2/drivers/char/random.c	2005-01-26 19:54:07.000000000 +0100
+++ linux-2.6.11-rc2.tx1/drivers/char/random.c	2005-01-28 17:03:02.000000000 +0100
@@ -255,10 +255,20 @@
 
 /*
  * Configuration information
+ * If "increased pool sizes" option is enabled,
+ * then poll size default values get increased x2. 
  */
+ 
+#ifdef CONFIG_RANDOM_INCREASED_POOLSIZES
+#define DEFAULT_POOL_SIZE 1024
+#define SECONDARY_POOL_SIZE 256
+#define BATCH_ENTROPY_SIZE 512
+#else
 #define DEFAULT_POOL_SIZE 512
 #define SECONDARY_POOL_SIZE 128
 #define BATCH_ENTROPY_SIZE 256
+#endif
+
 #define USE_SHA
 
 /*
diff -Nur linux-2.6.11-rc2/include/linux/random.h linux-2.6.11-rc2.tx1/include/linux/random.h
--- linux-2.6.11-rc2/include/linux/random.h	2005-01-26 19:54:17.000000000 +0100
+++ linux-2.6.11-rc2.tx1/include/linux/random.h	2005-01-27 23:45:34.000000000 +0100
@@ -42,6 +42,12 @@
 
 #ifdef __KERNEL__
 
+#ifdef CONFIG_NET_SECURITY
+extern unsigned long obsd_get_random_long(void);
+extern __u16 ip_randomid(void);
+extern __u32 ip_randomisn(void);
+#endif
+
 extern void rand_initialize_irq(int irq);
 
 extern void add_input_randomness(unsigned int type, unsigned int code,
diff -Nur linux-2.6.11-rc2/net/ipv4/tcp_ipv4.c linux-2.6.11-rc2.tx1/net/ipv4/tcp_ipv4.c
--- linux-2.6.11-rc2/net/ipv4/tcp_ipv4.c	2005-01-26 19:54:19.000000000 +0100
+++ linux-2.6.11-rc2.tx1/net/ipv4/tcp_ipv4.c	2005-01-28 17:16:09.000000000 +0100
@@ -539,10 +539,14 @@
 
 static inline __u32 tcp_v4_init_sequence(struct sock *sk, struct sk_buff *skb)
 {
+#ifdef CONFIG_NET_RANDISN
+		return ip_randomisn();
+#else
 	return secure_tcp_sequence_number(skb->nh.iph->daddr,
 					  skb->nh.iph->saddr,
 					  skb->h.th->dest,
 					  skb->h.th->source);
+#endif
 }
 
 /* called with local bh disabled */
@@ -833,14 +837,21 @@
 	tcp_v4_setup_caps(sk, &rt->u.dst);
 	tp->ext2_header_len = rt->u.dst.header_len;
 
-	if (!tp->write_seq)
+	if (!tp->write_seq) {
+#ifdef CONFIG_NET_RANDISN
+			tp->write_seq = ip_randomisn();
+#else
 		tp->write_seq = secure_tcp_sequence_number(inet->saddr,
 							   inet->daddr,
 							   inet->sport,
 							   usin->sin_port);
-
+#endif
+	}
+#ifdef CONFIG_NET_RANDID
+		inet->id = htons(ip_randomid());
+#else
 	inet->id = tp->write_seq ^ jiffies;
-
+#endif
 	err = tcp_connect(sk);
 	rt = NULL;
 	if (err)
@@ -1579,8 +1590,11 @@
 	if (newinet->opt)
 		newtp->ext_header_len = newinet->opt->optlen;
 	newtp->ext2_header_len = dst->header_len;
+#ifdef CONFIG_NET_RANDID
+	newinet->id = htons(ip_randomid());
+#else
 	newinet->id = newtp->write_seq ^ jiffies;
-
+#endif
 	tcp_sync_mss(newsk, dst_pmtu(dst));
 	newtp->advmss = dst_metric(dst, RTAX_ADVMSS);
 	tcp_initialize_rcv_mss(newsk);
diff -Nur linux-2.6.11-rc2/net/ipv4/udp.c linux-2.6.11-rc2.tx1/net/ipv4/udp.c
--- linux-2.6.11-rc2/net/ipv4/udp.c	2005-01-26 19:54:19.000000000 +0100
+++ linux-2.6.11-rc2.tx1/net/ipv4/udp.c	2005-01-27 23:02:32.000000000 +0100
@@ -94,6 +94,7 @@
 #include <linux/inet.h>
 #include <linux/ipv6.h>
 #include <linux/netdevice.h>
+#include <linux/random.h>
 #include <net/snmp.h>
 #include <net/tcp.h>
 #include <net/protocol.h>
diff -Nur linux-2.6.11-rc2/net/Kconfig linux-2.6.11-rc2.tx1/net/Kconfig
--- linux-2.6.11-rc2/net/Kconfig	2005-01-26 19:54:17.000000000 +0100
+++ linux-2.6.11-rc2.tx1/net/Kconfig	2005-01-28 17:04:30.608652680 +0100
@@ -81,6 +81,59 @@
 
 	  Say Y unless you know what you are doing.
 
+config NET_SECURITY
+	bool "Network Protections"
+	depends on NET
+	select INET
+	help
+	 By enabling this option, you will be able to choose a few options
+	 that will enhance the TCP/IP stack and network-related randomization.
+	 An example of results of this, is that the system will be a difficult
+	 subject of (remote) fingerprinting.
+	 
+	 These protections come from the grSecurity project by Brad Spengler,
+	 and based on OpenBSD extended networking security features.
+	 
+	 Ported to vanilla sources by Lorenzo Hernández García-Hierro
+	 <lorenzo@gnu.org>.
+	 Information at http://tuxedo-es.org and http://grsecurity.net.
+	 
+	 Short answer: say Y.
+
+config NET_RANDISN
+        bool "Truly random TCP ISN selection"
+        depends on NET_SECURITY
+        help
+          If you say Y here, Linux's default selection of TCP Initial Sequence
+          Numbers (ISNs) will be replaced with that of OpenBSD.  Linux uses
+          an MD4 hash based on the connection plus a time value to create the
+          ISN, while OpenBSD's selection is random.  If the sysctl option is
+          enabled, a sysctl option with name "rand_isns" is created.
+
+config NET_RANDID
+        bool "Randomized IP IDs"
+        depends on NET_SECURITY
+        help
+          If you say Y here, all the id field on all outgoing packets
+          will be randomized.  This hinders os fingerprinters and
+          keeps your machine from being used as a bounce for an untraceable
+          portscan.  Ids are used for fragmented packets, fragments belonging
+          to the same packet have the same id.  By default linux only
+          increments the id value on each packet sent to an individual host.
+          We use a port of the OpenBSD random ip id code to achieve the
+          randomness, while keeping the possibility of id duplicates to
+          near none.
+          
+config NET_RANDRPC
+        bool "Randomized RPC XIDs"
+        depends on NET_SECURITY
+        help
+          If you say Y here, the method of determining XIDs for RPC requests will
+          be randomized, instead of using linux's default behavior of simply
+          incrementing the XID.  If you want your RPC connections to be more
+          secure, say Y here.
+
+
 config INET
 	bool "TCP/IP networking"
 	---help---
diff -Nur linux-2.6.11-rc2/net/Makefile linux-2.6.11-rc2.tx1/net/Makefile
--- linux-2.6.11-rc2/net/Makefile	2005-01-26 19:50:49.000000000 +0100
+++ linux-2.6.11-rc2.tx1/net/Makefile	2005-01-27 23:04:02.000000000 +0100
@@ -11,6 +11,7 @@
 
 tmp-$(CONFIG_COMPAT) 		:= compat.o
 obj-$(CONFIG_NET)		+= $(tmp-y)
+obj-$(CONFIG_NET_SECURITY)	+= obsd_rand.o
 
 # LLC has to be linked before the files in net/802/
 obj-$(CONFIG_LLC)		+= llc/
diff -Nur linux-2.6.11-rc2/net/obsd_rand.c linux-2.6.11-rc2.tx1/net/obsd_rand.c
--- linux-2.6.11-rc2/net/obsd_rand.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.11-rc2.tx1/net/obsd_rand.c	2005-01-28 17:43:50.452901824 +0100
@@ -0,0 +1,269 @@
+/* $Id: openbsd-netrand-2.6.11-rc2.patch,v 1.2 2005/01/28 16:51:02 lorenzo Exp $
+ * Copyright (c) 2005 Lorenzo Hernandez Garcia-Hierro <lorenzo@gnu.org>.
+ * All rights reserved.
+ *
+ * Added some macros and stolen code from random.c, for individual and less
+ * "invasive" implementation.Also removed the get_random_long() macro definition,
+ * which is not good if we can simply call back obsd_get_random_long().
+ *
+ * Copyright (c) 1996, 1997, 2000-2002 Michael Shalayeff.
+ * 
+ * Version 1.90, last modified 28-Jan-05
+ *    
+ * Copyright Theodore Ts'o, 1994, 1995, 1996, 1997, 1998, 1999.
+ * All rights reserved.
+ *
+ * Copyright 1998 Niels Provos <provos@citi.umich.edu>
+ * All rights reserved.
+ * Theo de Raadt <deraadt@openbsd.org> came up with the idea of using
+ * such a mathematical system to generate more random (yet non-repeating)
+ * ids to solve the resolver/named problem.  But Niels designed the
+ * actual system based on the constraints.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/timer.h>
+#include <linux/smp_lock.h>
+#include <linux/random.h>
+
+#define RU_OUT 180
+#define RU_MAX 30000
+#define RU_GEN 2
+#define RU_N 32749
+#define RU_AGEN 7
+#define RU_M 31104
+#define PFAC_N 3
+
+/*
+ * Stolen from ./drivers/char/random.c
+ */
+
+/* FOO, GEEK and HECK are basic geekish MD4 functions: foo selection, geek majority, heck parity */
+#define FOO(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define GEEK(x, y, z) (((x) & (y)) + (((x) ^ (y)) & (z)))
+#define HECK(x, y, z) ((x) ^ (y) ^ (z))
+#define OBROUND(f, a, b, c, d, x, s)	\
+	(a += f(b, c, d) + x, a = (a << s) | (a >> (32 - s)))
+#define obK1 0
+#define obK2 013240474631UL
+#define obK3 015666365641UL
+#define OB_REKEY_INTERVAL (300 * HZ)
+
+
+const static __u16 pfacts[PFAC_N] = { 2, 3, 2729 };
+
+static __u16 ru_x;
+static __u16 ru_seed, ru_seed2;
+static __u16 ru_a, ru_b;
+static __u16 ru_g;
+static __u16 ru_counter = 0;
+static __u16 ru_msb = 0;
+static unsigned long ru_reseed = 0;
+static __u32 tmp;
+
+#define TCP_RNDISS_ROUNDS	15
+#define TCP_RNDISS_OUT		7200
+#define TCP_RNDISS_MAX		30000
+
+static __u8 tcp_rndiss_sbox[128];
+static __u16 tcp_rndiss_msb;
+static __u16 tcp_rndiss_cnt;
+static unsigned long tcp_rndiss_reseed;
+
+static __u16 pmod(__u16, __u16, __u16);
+static void ip_initid(void);
+__u16 ip_randomid(void);
+
+/*
+ * Basic cut-down MD4 transform.  Returns only 32 bits of result.
+ */
+static __u32 half_md4_transform (__u32 const buf[4], __u32 const in[8])
+{
+	__u32 a = buf[0], b = buf[1], c = buf[2], d = buf[3];
+
+	/* Round 1 */
+	OBROUND(FOO, a, b, c, d, in[0] + obK1,  3);
+	OBROUND(FOO, d, a, b, c, in[1] + obK1,  7);
+	OBROUND(FOO, c, d, a, b, in[2] + obK1, 11);
+	OBROUND(FOO, b, c, d, a, in[3] + obK1, 19);
+	OBROUND(FOO, a, b, c, d, in[4] + obK1,  3);
+	OBROUND(FOO, d, a, b, c, in[5] + obK1,  7);
+	OBROUND(FOO, c, d, a, b, in[6] + obK1, 11);
+	OBROUND(FOO, b, c, d, a, in[7] + obK1, 19);
+
+	/* Round 2 */
+	OBROUND(GEEK, a, b, c, d, in[1] + obK2,  3);
+	OBROUND(GEEK, d, a, b, c, in[3] + obK2,  5);
+	OBROUND(GEEK, c, d, a, b, in[5] + obK2,  9);
+	OBROUND(GEEK, b, c, d, a, in[7] + obK2, 13);
+	OBROUND(GEEK, a, b, c, d, in[0] + obK2,  3);
+	OBROUND(GEEK, d, a, b, c, in[2] + obK2,  5);
+	OBROUND(GEEK, c, d, a, b, in[4] + obK2,  9);
+	OBROUND(GEEK, b, c, d, a, in[6] + obK2, 13);
+
+	/* Round 3 */
+	OBROUND(HECK, a, b, c, d, in[3] + obK3,  3);
+	OBROUND(HECK, d, a, b, c, in[7] + obK3,  9);
+	OBROUND(HECK, c, d, a, b, in[2] + obK3, 11);
+	OBROUND(HECK, b, c, d, a, in[6] + obK3, 15);
+	OBROUND(HECK, a, b, c, d, in[1] + obK3,  3);
+	OBROUND(HECK, d, a, b, c, in[5] + obK3,  9);
+	OBROUND(HECK, c, d, a, b, in[0] + obK3, 11);
+	OBROUND(HECK, b, c, d, a, in[4] + obK3, 15);
+
+	return buf[1] + b;	/* "most hashed" word */
+	/* Alternative: return sum of all words? */
+}
+
+unsigned long obsd_get_random_long(void)
+{
+	static time_t   rekey_time;
+	static __u32    secret[12];
+	time_t          t;
+
+	/*
+	 * Pick a random secret every OB_REKEY_INTERVAL seconds.
+	 */
+	t = get_seconds();
+	if (!rekey_time || (t - rekey_time) > OB_REKEY_INTERVAL) {
+		rekey_time = t;
+		get_random_bytes(secret, sizeof(secret));
+	}
+
+	secret[1] = half_md4_transform(secret+8, secret);
+	secret[0] = half_md4_transform(secret+8, secret);
+	return *(unsigned long *)secret;
+}
+
+static __u16
+pmod(__u16 gen, __u16 exp, __u16 mod)
+{
+	__u16 s, t, u;
+
+	s = 1;
+	t = gen;
+	u = exp;
+
+	while (u) {
+		if (u & 1)
+			s = (s * t) % mod;
+		u >>= 1;
+		t = (t * t) % mod;
+	}
+	return (s);
+}
+
+static void
+ip_initid(void)
+{
+	__u16 j, i;
+	int noprime = 1;
+
+	ru_x = ((tmp = obsd_get_random_long()) & 0xFFFF) % RU_M;
+
+	ru_seed = (tmp >> 16) & 0x7FFF;
+	ru_seed2 = obsd_get_random_long() & 0x7FFF;
+
+	ru_b = ((tmp = obsd_get_random_long()) & 0xfffe) | 1;
+	ru_a = pmod(RU_AGEN, (tmp >> 16) & 0xfffe, RU_M);
+	while (ru_b % 3 == 0)
+		ru_b += 2;
+
+	j = (tmp = obsd_get_random_long()) % RU_N;
+	tmp = tmp >> 16;
+
+	while (noprime) {
+		for (i = 0; i < PFAC_N; i++)
+			if (j % pfacts[i] == 0)
+				break;
+
+		if (i >= PFAC_N)
+			noprime = 0;
+		else
+			j = (j + 1) % RU_N;
+	}
+
+	ru_g = pmod(RU_GEN, j, RU_N);
+	ru_counter = 0;
+
+	ru_reseed = xtime.tv_sec + RU_OUT;
+	ru_msb = ru_msb == 0x8000 ? 0 : 0x8000;
+}
+
+__u16
+ip_randomid(void)
+{
+	int i, n;
+
+	if (ru_counter >= RU_MAX || time_after(get_seconds(), ru_reseed))
+		ip_initid();
+
+	if (!tmp)
+		tmp = obsd_get_random_long();
+
+	n = tmp & 0x3;
+	tmp = tmp >> 2;
+	if (ru_counter + n >= RU_MAX)
+		ip_initid();
+	for (i = 0; i <= n; i++)
+		ru_x = (ru_a * ru_x + ru_b) % RU_M;
+	ru_counter += i;
+
+	return ((ru_seed ^ pmod(ru_g, ru_seed2 ^ ru_x, RU_N)) | ru_msb);
+}
+
+static __u16
+tcp_rndiss_encrypt(__u16 val)
+{
+	__u16 sum = 0, i;
+
+	for (i = 0; i < TCP_RNDISS_ROUNDS; i++) {
+		sum += 0x79b9;
+		val ^= ((__u16) tcp_rndiss_sbox[(val ^ sum) & 0x7f]) << 7;
+		val = ((val & 0xff) << 7) | (val >> 8);
+	}
+
+	return val;
+}
+
+static void
+tcp_rndiss_init(void)
+{
+	get_random_bytes(tcp_rndiss_sbox, sizeof (tcp_rndiss_sbox));
+	tcp_rndiss_reseed = get_seconds() + TCP_RNDISS_OUT;
+	tcp_rndiss_msb = tcp_rndiss_msb == 0x8000 ? 0 : 0x8000;
+	tcp_rndiss_cnt = 0;
+}
+
+__u32
+ip_randomisn(void)
+{
+	if (tcp_rndiss_cnt >= TCP_RNDISS_MAX ||
+	    time_after(get_seconds(), tcp_rndiss_reseed))
+		tcp_rndiss_init();
+
+	return (((tcp_rndiss_encrypt(tcp_rndiss_cnt++) |
+		  tcp_rndiss_msb) << 16) | (obsd_get_random_long() & 0x7fff));
+}
diff -Nur linux-2.6.11-rc2/net/sunrpc/xprt.c linux-2.6.11-rc2.tx1/net/sunrpc/xprt.c
--- linux-2.6.11-rc2/net/sunrpc/xprt.c	2005-01-26 19:54:20.000000000 +0100
+++ linux-2.6.11-rc2.tx1/net/sunrpc/xprt.c	2005-01-28 00:07:18.000000000 +0100
@@ -1342,7 +1342,11 @@
  */
 static inline u32 xprt_alloc_xid(struct rpc_xprt *xprt)
 {
+#ifdef CONFIG_NET_RANDRPC
+		return (u32) obsd_get_random_long();
+#else
 	return xprt->xid++;
+#endif
 }
 
 static inline void xprt_init_xid(struct rpc_xprt *xprt)

[-- Attachment #2: Esta parte del mensaje está firmada digitalmente --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 43+ messages in thread
* Re: [PATCH] OpenBSD Networking-related randomization port
@ 2005-01-28 19:24 Hank Leininger
  0 siblings, 0 replies; 43+ messages in thread
From: Hank Leininger @ 2005-01-28 19:24 UTC (permalink / raw)
  To: linux-kernel; +Cc: Lorenzo

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 2005-01-28, Stephen Hemminger said on linux-kernel:

> > "Randomized IP IDs hinders OS fingerprinting and will keep your
> > machine from being a bounce for an untraceable portscan."
> > 
> > References: [1]:
> > http://www.gentoo.org/proj/en/hardened/grsecurity.xml

> This is a very transitory effect, it works only because your machine
> is then different from the typical Linux machine; therefore the
> scanner will go on to the next obvious ones. But if this gets
> incorporated widely then the rarity factor goes away and this defense
> becomes useless.

This is true and not true.  Its utility for hindering OS fingerprints
will indeed diminish towards zero as it gets more popular.  But the bit
about keeping you from being used for untraceable port scans is real.

The way those scans work is basically:

  - I send something boring to you, an innocent bystander, and get your
    current IPID from your response
  - I send SYN packets to my target spoofed with your source address
    - If the target filters that port, it drops the SYN on the floor
    - If the target is not listening, it sends you an RST; you drop it on
      the floor
    - If the target is listening, it sends you a SYNACK; you reply with
      an RST which increments your IPID
  - I send another something boring to you, get your IPID from your
    response
  - I draw the conclusion that you did, or did not send an RST inbetween,
    thus I know if you received a SYNACK and the port is listening

It's rather nice, because the target saw only packets / a portscan
coming from you, and if anything you saw some unsolicited SYNACKs, and
whatever the "something boring" I've chosen to send to you for the IPID
heartbeats.

Of course this is only effective if:
  - the bystander (you) is quiescent
  - the bystander uses predictable (incrementing) IPIDs
  - the bystander will answer to "something boring" (ICMP, SYNs from
    source port 20, SYNACKs from source port 80, UDP from source port 53,
    whatever)

Only the user can do something about the first; distributions can do
something about the last, but the kernel can do something about the
middle.

And in a more general sense: why do I need to know how busy your network
stack is?  How popular (IPID, pkts/sec) your corporate webserver is?
How busy (TCP source ports, conns/sec) your outbound proxy is?  I don't.
If I want to know that stuff I should go pound sand.  That's why
network-stack randomness is a Good Thing[TM] if you can get it.

Thanks,

Hank Leininger <hlein@progressive-comp.com>
E407 AEF4 761E D39C D401  D4F4 22F8 EF11 861A A6F1
-----BEGIN PGP SIGNATURE-----

iD8DBQFB+pF/IvjvEYYapvERArpGAJ9fe8zWMEKbHdobvRS3Xa9V02LxLACfUx4i
VQnx7UmriLwF5niX4FCIr7A=
=HX4V
-----END PGP SIGNATURE-----

^ permalink raw reply	[flat|nested] 43+ messages in thread
* Re: [PATCH] OpenBSD Networking-related randomization port
@ 2005-01-29  7:24 linux
  0 siblings, 0 replies; 43+ messages in thread
From: linux @ 2005-01-29  7:24 UTC (permalink / raw)
  To: lorenzo; +Cc: linux-kernel

> It adds support for advanced networking-related randomization, in
> concrete it adds support for TCP ISNs randomization

Er... did you read the existing Linux TCP ISN generation code?
Which is quite thoroughly randomized already?

I'm not sure how the OpenBSD code is better in any way.  (Notice that it
uses the same "half_md4_transform" as Linux; you just added another copy.)
Is there a design note on how the design was chosen?


I don't wish to be *too* discouraging to someone who's *trying* to help,
but could you *please* check a little more carefully in future to
make sire it's actually an improvement?

I fear there's some ignorance of what the TCP ISN does, why it's chosen
the way it is, and what the current Linux algorithm is designed to do.
So here's a summary of what's going on.  But even as a summary, it's
pretty long...


First, a little background on the selection of the TCP ISN...

TCP is designed to work in an environment where packets are delayed.
If a packet is delayed enough, TCP will retransmit it.  If one of
the copies floats around the Internet for long enough and then arrives
long after it is expected, this is a "delayed duplicate".

TCP connections are between (host, port, host port) quadruples, and
packets that don't match some "current connection" in all four fields
will have no effect on the current connection.  This is why systems try
to avoid re-using source port numbers when making connections to
well-known destination ports.

However, sometimes the source port number is explicitly specified and
must be reused.  The problem then arises, how do we avoid having any
possible delayed packets from the previous use of this address pair show
up during the current connection and confuse the heck out of things by
acknowledging data that was never received, or shutting down a connection
that's supposed to stay open, or something like that?

First of all, protocols assume a maximum packet lifetime in the Internet.
The "Maximum Segment Lifetime" was originally specified as 120 seconds,
but many implementations optimize this to 60 or 30 seconds.  The longest
time that a response can be delayed is 2*MSL - one delay for the packet
eliciting the response, and another for the response.

In truth, there are few really-hard guarantees on how long a packet can
be delayed.  IP does have a TTL field, and a requirement that a packet's
TTL field be decremented for each hop between routers *or each second of
delay within a router*, but that latter portion isn't widely implemented.
Still, it is an identified design goal, and is pretty reliable in
practice.


The solution is twofold: First, refuse to accept packets whose
acks aren't in the current transmission window.  That is, if the
last ack I got was for byte 1000, and I have sent 1100 bytes
(numbers 0 through 1099), then if the incoming packet's ack isn't
somewhere between 1000 and 1100, it's not relevant.  If it's
950, it might be an old ack from the current connection (which
doesn't include anything interesting), but in any case it can be
safely ignored, and should be.

The only remaining issue is, how to choose the first sequence number
to use in a connection, the Initial Sequence Number (ISN)?

If you start every connection at zero, then you have the risk that
packets from an old connection between the same endpoints will
show up at a bad time, with in-range sequence numbers, and confuse
the current connection.

So what you do is, start at a sequence number higher than the
last one used in the old connection.  Then there can't be any
confusion.  But this requires remembering the last sequence number
used on every connection ever.  And there are at least 2^48 addresses
allowed to connect to each port on the local machine.  At 4 bytes
per sequence number, that's a Petabyte of storage...

Well, first of all, after 2*MSL, you can forget about it and use
whatever sequence number you like, because you know that there won't
be any old packets floating around to crash the party.

But still, it can be quite a burden on a busy web server.  And you might
crash and lose all your notes.  Do you want to have to wait 2*MSL before
rebooting?


So the TCP designers (I'm not on page 27 of RFC 793, if you want to follow
along) specified a time of day based ISN.  If you use a clock to generate
an ISN which counts up faster than your network connection can send
data (and thus crank up its sequence numbers), you can be sure that your
ISN is always higher than the last one used by an old connection without
having to remember it explicitly.

RFC 793 specifies a 250,000 bytes/second counting rate.  Most
implementations since Ethernet used a 1,000,000 byte/second counting
rate, which matches the capabilities of 10base5 and 10base2 quite well,
and is easy to get from the gettimeofday() call.

Note that there are two risks with this.  First, if the connection actually
manages to go faster than the ISN clock, the next connection's ISN will
be in the middle of the space the earlier connection used.  Fortunately,
the kind of links where significant routing delay appear are generally
slower ones where 1 Mbyte/sec is a not too unreasonable limit.  Your
gigabit LAN isn't going to be delaying packets by seconds.

The second is that a connection will be made and do nothing for 4294
seconds until the ISN clock is about to wrap around and then start
doing packets "ahead of" the ISN clock.  If it then closes the connection
and a new one opens, once again you have sequence number overlap.

If you read old networking papers, there were a bunch of proposals for
occasional sequence number renegotiation to solve this problem, but in the
end people decided to not worry about it, and it hasn't been a problem
in practice.



Anyway... fast forward out of the peace and love decade and welcome to
the modern Internet, with people *trying* to mess up TCP connections.
This kind of attack from within was, unfortunately, not one of the
scenarios that the initial Internet designers considered, and it's
been a bit of a problem since.

All this careful worry about packets left over from an old connection
randomly showing up and messing things up, when we have people *creating*
packets deliberately crafted to mess things up!  A whole separate problem.
In particular, using the simple timer-based algorithm, I can connect to
a server, look at the ISN it offers me, and know that thats the same
ISN it's offering to other people connecting at the same time.  So I
can create packets with a forged source address and approximately-valid
sequence numbers and bombard the connection with them, cutting off that
server's connection to some third party.  Even if I can't see any of
the traffic on the connection.

So people sat down and did some thinking.  How to deal with this?
Harder yet, how to deal with this without redesigning TCP from scratch?

Well, if a person wants to mess up their *own* connections, we can't
stop them.  The fundamental problem is that an attacker A can figure
out the sequence numbers that machines B and C are using to talk to
each other.  So we'd like to make the sequence numbers for every
connection unique and not related to the sequence numbers used on any
other connections.  So A can talk to B and A can talk to C and still not
be able to figure out the sequence numbers that B and C are using between
themselves.


Fortunately, it is entirely possible to combine this with the clock-based
algorithm and get the best of both worlds!  All we need is a random offset,
unique for every (address, port, address, port) quadruple, to add to
the clock value, and we have all of the clock-based guarantees preserved
within every pair of endpoints, but unrelated endpoints have their ISNs
at some unpredictable offset relative to each other.

And for generating such a random offset, we can use cryptography.
Keep a single secret key, and hash together the endpoint addresses,
and you can generate a random 32-bit ISN offset.  Add that to the
current time, and everything is golden.  A can connect to B and
see and ISN, but would need to do some serious cryptanalysis to
figure out what ISN B is using to talk to C.


Linux actually adds one refinement.  For speed, it uses a very
stripped-down cryptographic hash function.  To guard against that
being broken, it generates a new secret every 5 minutes.  So an
attacker only has 5 minutes to break it.

The cryptographic offset is divided into 2 parts.  The high 8 bits are
a sequence number, incremented every time the secret is regenerated.
The low 24 bits are produced by the hash.  So 5 minutes after booting,
the secret offset changes from 0x00yyyyyy to 0x01zzzzzz.  This is at
least +1, and at most +0x1ffffff.  On average, the count is going up
by 2^24 = 16 million every 300 seconds.  Which just adds a bit to the
basic "1 million per second" ISN timer.

The cost is that the per-connection part of the ISN offset is limited
to 24 and not 32 bits, but a cryptanalytic attack is pretty much
precluded by the every-5-minutes thing.  The rekey time and the number of
really-unpredictable bits have to add up to not wrapping the ISN space
too fast.  (Although the 24 bits could be increased to 28 bits without
quite doubling the ISN counting speed.  Or 27 bits if you want plenty
of margin.  Could I suggest that as an improvement?)

--- drivers/char/random.c	2004-12-04 09:24:19.000000000 +0000
+++ drivers/char/random.c	2005-01-29 07:20:37.000000000 +0000
@@ -2183,26 +2183,26 @@
 #define REKEY_INTERVAL	(300*HZ)
 /*
  * Bit layout of the tcp sequence numbers (before adding current time):
- * bit 24-31: increased after every key exchange
- * bit 0-23: hash(source,dest)
+ * bit 27-31: increased after every key exchange
+ * bit 0-26: hash(source,dest)
  *
  * The implementation is similar to the algorithm described
  * in the Appendix of RFC 1185, except that
  * - it uses a 1 MHz clock instead of a 250 kHz clock
  * - it performs a rekey every 5 minutes, which is equivalent
- * 	to a (source,dest) tulple dependent forward jump of the
+ * 	to a (source,dest) tuple dependent forward jump of the
  * 	clock by 0..2^(HASH_BITS+1)
  *
- * Thus the average ISN wraparound time is 68 minutes instead of
- * 4.55 hours.
+ * Thus the average ISN wraparound time is 49 minutes instead of
+ * 4.77 hours.
  *
  * SMP cleanup and lock avoidance with poor man's RCU.
  * 			Manfred Spraul <manfred@colorfullife.com>
  * 		
  */
-#define COUNT_BITS	8
+#define COUNT_BITS	5
 #define COUNT_MASK	( (1<<COUNT_BITS)-1)
-#define HASH_BITS	24
+#define HASH_BITS	27
 #define HASH_MASK	( (1<<HASH_BITS)-1 )
 
 static struct keydata {


Anyway, in comparison, the algorithm in your patch (and presumably
OpenBSD, although I haven't personally compared it) uses a clock
offset generated fresh for each connection.  There's a global counter
(tcp_rndiss_cnt; I notice you don't have any SMP locking on it) which
is incremented every time an ISN is needed.  It's rekeyed periodically,
and the high bit (tcp_rndiss_msb) of the delta is used like the COUNT_BITS
in the Linux algorithm.

The ISN is generated as the high sequence bit, then 15 bits of encrypted
count (with some homebrew cipher I don't recognize), then a constant
zero bit (am I reading that right), then the 15 low-order bits are
purely random.


It's a slightly different algorithm, but it does a very similar function.
The main downsides are that the sequence number can easily go backwards
(there's no guarantee that consecutive calls will return increasing
numbers since tcp_rndiss_encrypt scrambles the high 15 bits), and
that it's not SMP-safe.  Two processors could read and use the same
tcp_rndiss_cnt value at the same time, or (more likely) both call
tcp_rndiss_init at the same time and end up toggling tcp_rndiss_msb twice,
thereby destroying the no-rollback property it's trying to achieve.

Oh, and the single sequence bit in the offsets means that the
TCP ISN will wrap around very fast.  Every 10 minutes, or every
60000 TCP connections, whichever comes first.

Regarding the first issue, it's possible that the OpenBSD network stack
takes care to remember all connections for 2*MSL and continues the
sequence number if the endpoints are reused, thereby avoiding a call to
ip_randomisn entirely.

But the second deserves some attention.  The Linux code takes some care
to avoid having to lock anything in the common case.  The global count
makes that difficult.

^ permalink raw reply	[flat|nested] 43+ messages in thread

end of thread, other threads:[~2005-02-13  1:41 UTC | newest]

Thread overview: 43+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-01-28 17:17 [PATCH] OpenBSD Networking-related randomization port Lorenzo Hernández García-Hierro
2005-01-28 17:40 ` Adrian Bunk
2005-01-28 17:47   ` Lorenzo Hernández García-Hierro
2005-01-28 18:18     ` Stephen Hemminger
2005-01-28 18:54       ` Lorenzo Hernández García-Hierro
2005-01-28 19:09     ` Adrian Bunk
2005-01-28 18:02 ` Stephen Hemminger
2005-01-28 18:31   ` Lorenzo Hernández García-Hierro
2005-01-28 18:52     ` Stephen Hemminger
2005-01-28 18:58       ` Lorenzo Hernández García-Hierro
2005-01-28 20:34       ` Lorenzo Hernández García-Hierro
2005-01-28 20:45         ` David S. Miller
2005-01-28 21:34           ` Stephen Hemminger
2005-01-28 21:45             ` David S. Miller
2005-01-29  6:59             ` Andi Kleen
2005-01-28 20:47         ` Arjan van de Ven
2005-01-28 22:12           ` Lorenzo Hernández García-Hierro
2005-01-29  8:04             ` Arjan van de Ven
2005-01-29  8:05             ` Arjan van de Ven
2005-01-29  9:15           ` Valdis.Kletnieks
2005-01-31 16:50             ` Adrian Bunk
2005-01-31 17:23               ` Lorenzo Hernández García-Hierro
2005-01-31 20:11                 ` Ingo Molnar
2005-01-31 23:27                   ` linux
2005-02-12 22:29                     ` Andi Kleen
2005-02-12 23:25                       ` linux
2005-02-13  0:18                         ` Roland Dreier
2005-02-13  1:41                           ` linux
2005-02-02 17:17                   ` linux
2005-02-02 17:38                     ` Lorenzo Hernández García-Hierro
2005-02-03 19:51                       ` Stephen Hemminger
2005-02-03 20:14                         ` Lennert Buytenhek
2005-01-31 19:42               ` Valdis.Kletnieks
2005-01-31 20:03                 ` Lorenzo Hernández García-Hierro
2005-02-01 23:22                   ` Matt Mackall
2005-01-28 18:04 ` Jörn Engel
2005-01-28 18:07 ` Arjan van de Ven
2005-01-28 18:36   ` Lorenzo Hernández García-Hierro
2005-02-01 14:54   ` Bill Davidsen
2005-01-28 19:24 ` Horst von Brand
2005-01-29 18:16 ` Florian Weimer
2005-01-28 19:24 Hank Leininger
2005-01-29  7:24 linux

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).