From: Manfred Spraul <manfred@colorfullife.com>
To: "David S. Miller" <davem@redhat.com>
Cc: Netdev <netdev@oss.sgi.com>
Subject: [PATCH] fix secure tcp sequence number generation
Date: Sat, 02 Oct 2004 20:10:22 +0200 [thread overview]
Message-ID: <415EEF0E.3080808@colorfullife.com> (raw)
[-- Attachment #1: Type: text/plain, Size: 1268 bytes --]
Hi Dave,
Ted's recent random.c update broke the periodic rekeying:
schedule_work() doesn't provide synchronization. Additionally the first
syn values after boot are generated with secret 0 - not good.
Attached is a big cleanup. Linus asked me to send to to you for merging:
Description:
The tcp sequence number generator needs a random seed that is reset every
few minutes. Since the sequence numbers should be constantly increasing,
for each rekey 2^24 is added to the sequence number.
The actual use of the sequence number generator is lockless,
synchronization is achieved by having two copies of the control structure.
The attached patch:
- fixes a race in rekey_seq_generator(): schedule_work doesn't
provide synchronization.
- Uses schedule_delayed_work() for the rekey: simplifies synchronization
and speeds up the hot path.
- replaces do_gettimeofday with get_seconds(): get_seconds is faster and
usec resolution is not required.
- removes tmpdata - not needed with new locking.
- Adds a late_initcall for the first initialization after boot.
init_call would be too early, I've checked that the late_initcall runs
before net/ipv4/ipconfig.c, i.e. the BOOTP/DHCP autoconfiguration.
Signed-Off-By: Manfred Spraul <manfred@colorfullife.com>
[-- Attachment #2: patch-random-cleanup --]
[-- Type: text/plain, Size: 4296 bytes --]
--- 2.6/drivers/char/random.c 2004-10-02 19:30:18.583688301 +0200
+++ build-2.6/drivers/char/random.c 2004-10-02 19:09:54.288451293 +0200
@@ -2184,7 +2184,7 @@
#undef K3
/* This should not be decreased so low that ISNs wrap too fast. */
-#define REKEY_INTERVAL 300
+#define REKEY_INTERVAL (300*HZ)
/*
* Bit layout of the tcp sequence numbers (before adding current time):
* bit 24-31: increased after every key exchange
@@ -2210,49 +2210,55 @@
#define HASH_MASK ( (1<<HASH_BITS)-1 )
static struct keydata {
- time_t rekey_time;
__u32 count; // already shifted to the final position
__u32 secret[12];
} ____cacheline_aligned ip_keydata[2];
-static spinlock_t ip_lock = SPIN_LOCK_UNLOCKED;
static unsigned int ip_cnt;
-static void rekey_seq_generator(void *private_)
-{
- struct keydata *keyptr, tmp;
- struct timeval tv;
+static void rekey_seq_generator(void *private_);
- do_gettimeofday(&tv);
- get_random_bytes(tmp.secret, sizeof(tmp.secret));
+static DECLARE_WORK(rekey_work, rekey_seq_generator, NULL);
- spin_lock_bh(&ip_lock);
- keyptr = &ip_keydata[ip_cnt&1];
+/*
+ * Lock avoidance:
+ * The ISN generation runs lockless - it's just a hash over random data.
+ * State changes happen every 5 minutes when the random key is replaced.
+ * Synchronization is performed by having two copies of the hash function
+ * state and rekey_seq_generator always updates the inactive copy.
+ * The copy is then activated by updating ip_cnt.
+ * The implementation breaks down if someone blocks the thread
+ * that processes SYN requests for more than 5 minutes. Should never
+ * happen, and even if that happens only a not perfectly compliant
+ * ISN is generated, nothing fatal.
+ */
+static void rekey_seq_generator(void *private_)
+{
+ struct keydata *keyptr = &ip_keydata[1^(ip_cnt&1)];
- keyptr = &ip_keydata[1^(ip_cnt&1)];
- keyptr->rekey_time = tv.tv_sec;
- memcpy(keyptr->secret, tmp.secret, sizeof(keyptr->secret));
+ get_random_bytes(keyptr->secret, sizeof(keyptr->secret));
keyptr->count = (ip_cnt&COUNT_MASK)<<HASH_BITS;
- mb();
+ smp_wmb();
ip_cnt++;
-
- spin_unlock_bh(&ip_lock);
+ schedule_delayed_work(&rekey_work, REKEY_INTERVAL);
}
-static DECLARE_WORK(rekey_work, rekey_seq_generator, NULL);
-
-static inline struct keydata *check_and_rekey(time_t time)
+static inline struct keydata *get_keyptr(void)
{
struct keydata *keyptr = &ip_keydata[ip_cnt&1];
- rmb();
- if (!keyptr->rekey_time || (time - keyptr->rekey_time) > REKEY_INTERVAL) {
- schedule_work(&rekey_work);
- }
+ smp_rmb();
return keyptr;
}
+static __init int seqgen_init(void)
+{
+ rekey_seq_generator(NULL);
+ return 0;
+}
+late_initcall(seqgen_init);
+
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
__u32 secure_tcpv6_sequence_number(__u32 *saddr, __u32 *daddr,
__u16 sport, __u16 dport)
@@ -2260,14 +2266,12 @@
struct timeval tv;
__u32 seq;
__u32 hash[12];
- struct keydata *keyptr;
+ struct keydata *keyptr = get_keyptr();
/* The procedure is the same as for IPv4, but addresses are longer.
* Thus we must use twothirdsMD4Transform.
*/
- do_gettimeofday(&tv); /* We need the usecs below... */
- keyptr = check_and_rekey(tv.tv_sec);
memcpy(hash, saddr, 16);
hash[4]=(sport << 16) + dport;
@@ -2275,6 +2279,8 @@
seq = twothirdsMD4Transform(daddr, hash) & HASH_MASK;
seq += keyptr->count;
+
+ do_gettimeofday(&tv);
seq += tv.tv_usec + tv.tv_sec*1000000;
return seq;
@@ -2288,13 +2294,7 @@
struct timeval tv;
__u32 seq;
__u32 hash[4];
- struct keydata *keyptr;
-
- /*
- * Pick a random secret every REKEY_INTERVAL seconds.
- */
- do_gettimeofday(&tv); /* We need the usecs below... */
- keyptr = check_and_rekey(tv.tv_sec);
+ struct keydata *keyptr = get_keyptr();
/*
* Pick a unique starting offset for each TCP connection endpoints
@@ -2317,6 +2317,7 @@
* That's funny, Linux has one built in! Use it!
* (Networks are faster now - should this be increased?)
*/
+ do_gettimeofday(&tv);
seq += tv.tv_usec + tv.tv_sec*1000000;
#if 0
printk("init_seq(%lx, %lx, %d, %d) = %d\n",
@@ -2335,7 +2336,7 @@
struct keydata *keyptr;
__u32 hash[4];
- keyptr = check_and_rekey(get_seconds());
+ keyptr = get_keyptr();
/*
* Pick a unique starting offset for each IP destination.
next reply other threads:[~2004-10-02 18:10 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2004-10-02 18:10 Manfred Spraul [this message]
2004-10-03 22:00 ` [PATCH] fix secure tcp sequence number generation David S. Miller
2004-10-04 4:06 ` Manfred Spraul
2004-10-05 20:27 ` David S. Miller
2004-10-05 20:41 ` Manfred Spraul
2004-10-05 21:31 ` David S. Miller
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=415EEF0E.3080808@colorfullife.com \
--to=manfred@colorfullife.com \
--cc=davem@redhat.com \
--cc=netdev@oss.sgi.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.