Module Name:    src
Committed By:   riastradh
Date:           Thu May  7 19:05:51 UTC 2020

Modified Files:
        src/sys/dev: random.c
        src/sys/kern: kern_entropy.c
        src/sys/sys: entropy.h

Log Message:
Consolidate entropy on RNDADDDATA and writes to /dev/random.

The man page for some time has advertised:

  Writing to either /dev/random or /dev/urandom influences subsequent
  output of both devices, guaranteed to take effect at next open.

So let's make that true again.

It is a conscious choice _not_ to consolidate entropy frequently.
For example, if you have a _slow_ HWRNG, which provides 32 bits of
entropy every few seconds, and you reveal a hash that to the
adversary before any more comes in, the adversary can in principle
just keep guessing the intermediate state by a brute force search
over ~2^32 possibilities.

To mitigate this, the kernel generally tries to avoid consolidating
entropy from the per-CPU pools until doing so would bring us from
zero entropy to full entropy.

However, there are various _possible_ sources of entropy which are
just hard to give honest estimates for that are valid on ~all
machines -- like interrupt timings.  The time at which we read a seed
in, which usually happens via /etc/rc.d/random_seed early in
userland, is a reasonable time to gather this up.  An operator or
system engineer who knows another opportune moment can always issue
`sysctl -w kern.entropy.consolidate=1'.

Prompted by a suggestion from nia@ to consolidate entropy at the
first transition to userland.  I chose not to do that because it
would likely cause warning fatigue on systems that are perfectly fine
with a random seed -- doing it this way instead lets rndctl -L
trigger the consolidation automatically.  A subsequent commit will
reorder the operations in rndctl again to make it work out better.


To generate a diff of this commit:
cvs rdiff -u -r1.2 -r1.3 src/sys/dev/random.c
cvs rdiff -u -r1.12 -r1.13 src/sys/kern/kern_entropy.c
cvs rdiff -u -r1.1 -r1.2 src/sys/sys/entropy.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/random.c
diff -u src/sys/dev/random.c:1.2 src/sys/dev/random.c:1.3
--- src/sys/dev/random.c:1.2	Thu Apr 30 04:26:29 2020
+++ src/sys/dev/random.c	Thu May  7 19:05:51 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: random.c,v 1.2 2020/04/30 04:26:29 riastradh Exp $	*/
+/*	$NetBSD: random.c,v 1.3 2020/05/07 19:05:51 riastradh Exp $	*/
 
 /*-
  * Copyright (c) 2019 The NetBSD Foundation, Inc.
@@ -47,7 +47,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: random.c,v 1.2 2020/04/30 04:26:29 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: random.c,v 1.3 2020/05/07 19:05:51 riastradh Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -384,7 +384,7 @@ random_write(dev_t dev, struct uio *uio,
 {
 	kauth_cred_t cred = kauth_cred_get();
 	uint8_t *buf;
-	bool privileged = false;
+	bool privileged = false, any = false;
 	int error = 0;
 
 	/* Verify user's authorization to affect the entropy pool.  */
@@ -429,10 +429,16 @@ random_write(dev_t dev, struct uio *uio,
 		if (error)
 			break;
 		rnd_add_data(&user_rndsource, buf, n, privileged ? n*NBBY : 0);
+		any = true;
 	}
 
 	/* Zero the buffer and return it to the pool cache.  */
 	explicit_memset(buf, 0, RANDOM_BUFSIZE);
 	pool_cache_put(random_buf_pc, buf);
+
+	/* If we added anything, consolidate entropy now.  */
+	if (any)
+		entropy_consolidate();
+
 	return error;
 }

Index: src/sys/kern/kern_entropy.c
diff -u src/sys/kern/kern_entropy.c:1.12 src/sys/kern/kern_entropy.c:1.13
--- src/sys/kern/kern_entropy.c:1.12	Thu May  7 00:55:13 2020
+++ src/sys/kern/kern_entropy.c	Thu May  7 19:05:51 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: kern_entropy.c,v 1.12 2020/05/07 00:55:13 riastradh Exp $	*/
+/*	$NetBSD: kern_entropy.c,v 1.13 2020/05/07 19:05:51 riastradh Exp $	*/
 
 /*-
  * Copyright (c) 2019 The NetBSD Foundation, Inc.
@@ -60,9 +60,7 @@
  *	  transition from partial entropy to full entropy, so that
  *	  users can easily determine when to reseed.  This also
  *	  facilitates an operator explicitly causing everything to
- *	  reseed by sysctl -w kern.entropy.consolidate=1, e.g. if they
- *	  just flipped a coin 256 times and wrote `echo tthhhhhthh... >
- *	  /dev/random'.
+ *	  reseed by sysctl -w kern.entropy.consolidate=1.
  *
  *	* No entropy estimation based on the sample values, which is a
  *	  contradiction in terms and a potential source of side
@@ -77,7 +75,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_entropy.c,v 1.12 2020/05/07 00:55:13 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_entropy.c,v 1.13 2020/05/07 19:05:51 riastradh Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -241,8 +239,8 @@ static void	entropy_softintr(void *);
 static void	entropy_thread(void *);
 static uint32_t	entropy_pending(void);
 static void	entropy_pending_cpu(void *, void *, struct cpu_info *);
-static void	entropy_consolidate(void);
-static void	entropy_gather_xc(void *, void *);
+static void	entropy_do_consolidate(void);
+static void	entropy_consolidate_xc(void *, void *);
 static void	entropy_notify(void);
 static int	sysctl_entropy_consolidate(SYSCTLFN_ARGS);
 static int	sysctl_entropy_gather(SYSCTLFN_ARGS);
@@ -959,7 +957,7 @@ entropy_thread(void *cookie)
 
 		if (consolidate) {
 			/* Do it.  */
-			entropy_consolidate();
+			entropy_do_consolidate();
 
 			/* Mitigate abuse.  */
 			kpause("entropy", false, hz, NULL);
@@ -993,13 +991,13 @@ entropy_pending_cpu(void *ptr, void *coo
 }
 
 /*
- * entropy_consolidate()
+ * entropy_do_consolidate()
  *
  *	Issue a cross-call to gather entropy on all CPUs and advance
  *	the entropy epoch.
  */
 static void
-entropy_consolidate(void)
+entropy_do_consolidate(void)
 {
 	static const struct timeval interval = {.tv_sec = 60, .tv_usec = 0};
 	static struct timeval lasttime; /* serialized by E->lock */
@@ -1007,7 +1005,7 @@ entropy_consolidate(void)
 	uint64_t ticket;
 
 	/* Gather entropy on all CPUs.  */
-	ticket = xc_broadcast(0, &entropy_gather_xc, NULL, NULL);
+	ticket = xc_broadcast(0, &entropy_consolidate_xc, NULL, NULL);
 	xc_wait(ticket);
 
 	/* Acquire the lock to notify waiters.  */
@@ -1037,13 +1035,13 @@ entropy_consolidate(void)
 }
 
 /*
- * entropy_gather_xc(arg1, arg2)
+ * entropy_consolidate_xc(arg1, arg2)
  *
  *	Extract output from the local CPU's input pool and enter it
  *	into the global pool.
  */
 static void
-entropy_gather_xc(void *arg1 __unused, void *arg2 __unused)
+entropy_consolidate_xc(void *arg1 __unused, void *arg2 __unused)
 {
 	struct entropy_cpu *ec;
 	uint8_t buf[ENTPOOL_CAPACITY];
@@ -1144,19 +1142,49 @@ entropy_notify(void)
 }
 
 /*
+ * entropy_consolidate()
+ *
+ *	Trigger entropy consolidation and wait for it to complete.
+ *
+ *	This should be used sparingly, not periodically -- requiring
+ *	conscious intervention by the operator or a clear policy
+ *	decision.  Otherwise, the kernel will automatically consolidate
+ *	when enough entropy has been gathered into per-CPU pools to
+ *	transition to full entropy.
+ */
+void
+entropy_consolidate(void)
+{
+	uint64_t ticket;
+	int error;
+
+	KASSERT(E->stage == ENTROPY_HOT);
+
+	mutex_enter(&E->lock);
+	ticket = entropy_consolidate_evcnt.ev_count;
+	E->consolidate = true;
+	cv_broadcast(&E->cv);
+	while (ticket == entropy_consolidate_evcnt.ev_count) {
+		error = cv_wait_sig(&E->cv, &E->lock);
+		if (error)
+			break;
+	}
+	mutex_exit(&E->lock);
+}
+
+/*
  * sysctl -w kern.entropy.consolidate=1
  *
  *	Trigger entropy consolidation and wait for it to complete.
- *	Writable only by superuser.  This is the only way for the
- *	system to consolidate entropy if the operator knows something
- *	the kernel doesn't about how unpredictable the pending entropy
- *	pools are.
+ *	Writable only by superuser.  This, writing to /dev/random, and
+ *	ioctl(RNDADDDATA) are the only ways for the system to
+ *	consolidate entropy if the operator knows something the kernel
+ *	doesn't about how unpredictable the pending entropy pools are.
  */
 static int
 sysctl_entropy_consolidate(SYSCTLFN_ARGS)
 {
 	struct sysctlnode node = *rnode;
-	uint64_t ticket;
 	int arg;
 	int error;
 
@@ -1166,18 +1194,8 @@ sysctl_entropy_consolidate(SYSCTLFN_ARGS
 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
 	if (error || newp == NULL)
 		return error;
-	if (arg) {
-		mutex_enter(&E->lock);
-		ticket = entropy_consolidate_evcnt.ev_count;
-		E->consolidate = true;
-		cv_broadcast(&E->cv);
-		while (ticket == entropy_consolidate_evcnt.ev_count) {
-			error = cv_wait_sig(&E->cv, &E->lock);
-			if (error)
-				break;
-		}
-		mutex_exit(&E->lock);
-	}
+	if (arg)
+		entropy_consolidate();
 
 	return error;
 }
@@ -2214,9 +2232,10 @@ entropy_ioctl(unsigned long cmd, void *d
 			mutex_exit(&E->lock);
 		}
 
-		/* Enter the data.  */
+		/* Enter the data and consolidate entropy.  */
 		rnd_add_data(&seed_rndsource, rdata->data, rdata->len,
 		    entropybits);
+		entropy_consolidate();
 		break;
 	}
 	default:

Index: src/sys/sys/entropy.h
diff -u src/sys/sys/entropy.h:1.1 src/sys/sys/entropy.h:1.2
--- src/sys/sys/entropy.h:1.1	Thu Apr 30 03:28:19 2020
+++ src/sys/sys/entropy.h	Thu May  7 19:05:51 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: entropy.h,v 1.1 2020/04/30 03:28:19 riastradh Exp $	*/
+/*	$NetBSD: entropy.h,v 1.2 2020/05/07 19:05:51 riastradh Exp $	*/
 
 /*-
  * Copyright (c) 2019 The NetBSD Foundation, Inc.
@@ -48,6 +48,7 @@ struct knote;
 #define	ENTROPY_SIG	0x02
 
 void	entropy_bootrequest(void);
+void	entropy_consolidate(void);
 unsigned entropy_epoch(void);
 int	entropy_extract(void *, size_t, int);
 int	entropy_poll(int);

Reply via email to