kern/123177: arc4rand(9) produces the same sequence on each boot

Robert Woolley freebsd-pr08 at mlists.homeunix.com
Mon Apr 28 20:30:03 UTC 2008


>Number:         123177
>Category:       kern
>Synopsis:       arc4rand(9) produces the same sequence on each boot
>Confidential:   no
>Severity:       critical
>Priority:       high
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Mon Apr 28 20:30:03 UTC 2008
>Closed-Date:
>Last-Modified:
>Originator:     Robert Woolley
>Release:        7.0
>Organization:
>Environment:
FreeBSD gumby.homeunix.com 7.0-RELEASE-p1 FreeBSD 7.0-RELEASE-p1 #18: Mon Apr 28 14:53:51 BST 2008     root at gumby.homeunix.com:/usr/obj/usr/src/sys/MUSTARD  i386
>Description:
arc4rand(9) is used as a source of secure random numbers in the kernel. As far as I can tell, it's the source of the disposable keys for "geli onetime" partitions. It's seeded the first time it's called using read_random(), which is implemented by yarrow (except on VIA Nehemiah).

The problem is that the first call happens before yarrow is seeded and the yarrow cipher-context is initialized. This results in a given kernel producing the same sequence on each boot, and it wont reseed for 5 minutes or 64KB.

It also appears that the use of an uninitialized cipher-context may be causing dubious memory accesses. The sequence alters if the kernel is modified, despite the relevant data structures being static (i.e. pre-zeroed) or explicitly set-up.

>How-To-Repeat:
In /etc/rc.d/initrandom, add some calls to "sysctl kern.arandom" after the line:

feed_dev_random "${entropy_file}"

and cycle the machine a few times. I'm seeing the same sequence each time. By this point in the boot sequence yarrow must have seeded.

(BTW a few lines up "sysctl -a" is dumped into /dev/random before the entropy file. This does cause a call to arc4rand(), but it's not first. You may wish to remove this.)
>Fix:
The patch does two things:

1.  Reseeds  arc4rand when root closes /dev/random (just after yarrow is reseeded by the entropy file).

2.  Provides a provisional yarrow cipher-context to support early calls to read_random.

Patch attached with submission follows:

diff -ur /usr/src.orig/sys/dev/random/randomdev.c /usr/src/sys/dev/random/randomdev.c
--- /usr/src.orig/sys/dev/random/randomdev.c	2006-11-06 13:41:55.000000000 +0000
+++ /usr/src/sys/dev/random/randomdev.c	2008-04-28 14:34:57.000000000 +0100
@@ -36,6 +36,7 @@
 #include <sys/filio.h>
 #include <sys/kernel.h>
 #include <sys/kthread.h>
+#include <sys/libkern.h>
 #include <sys/lock.h>
 #include <sys/malloc.h>
 #include <sys/module.h>
@@ -88,8 +89,12 @@
 {
 	if ((flags & FWRITE) && (priv_check(td, PRIV_RANDOM_RESEED) == 0)
 	    && (securelevel_gt(td->td_ucred, 0) == 0)) {
+
 		(*random_systat.reseed)();
 		random_systat.seeded = 1;
+
+		/* Reseed any other secure PRNGs */
+		arc4rand(NULL, 0, 1);
 	}
 
 	return (0);
diff -ur /usr/src.orig/sys/dev/random/yarrow.c /usr/src/sys/dev/random/yarrow.c
--- /usr/src.orig/sys/dev/random/yarrow.c	2007-11-29 16:05:38.000000000 +0000
+++ /usr/src/sys/dev/random/yarrow.c	2008-04-28 14:38:49.000000000 +0100
@@ -37,6 +37,8 @@
 #include <sys/sysctl.h>
 #include <sys/systm.h>
 
+#include <machine/cpu.h>
+
 #include <crypto/rijndael/rijndael-api-fst.h>
 #include <crypto/sha2/sha2.h>
 
@@ -160,6 +162,14 @@
 	for (i = 0; i < 2; i++)
 		yarrow_hash_init(&random_state.pool[i].hash);
 
+	/* Generate a provisional cipher key so that read_random
+	 * can be used to seed arc4rand(9) (must be securely reseeded).
+	 * We do a second get_cyclecount() later, as some CPUs need
+	 * to fall back to nanotime, which doesn't work properly yet.
+	 */
+	random_state.counter[0] = get_cyclecount();
+	yarrow_encrypt_init(&random_state.key, random_state.counter);
+
 	/* Clear the counter */
 	for (i = 0; i < 4; i++)
 		random_state.counter[i] = 0;
@@ -274,6 +284,10 @@
 	int i;
 	int retval;
 
+	/* Some entropy may be needed on the first call. */
+	if (gate)
+		random_state.counter[1] = get_cyclecount();
+
 	/* The reseed task must not be jumped on */
 	mtx_lock(&random_reseed_mtx);
 


>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list