linux-crypto.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH, RFC] random: introduce getrandom(2) system call
@ 2014-07-17  9:18 Theodore Ts'o
  2014-07-17 10:57 ` Hannes Frederic Sowa
                   ` (11 more replies)
  0 siblings, 12 replies; 50+ messages in thread
From: Theodore Ts'o @ 2014-07-17  9:18 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-abi, linux-crypto, beck, Theodore Ts'o

The getrandom(2) system call was requested by the LibreSSL Portable
developers.  It is analoguous to the getentropy(2) system call in
OpenBSD.

The rationale of this system call is to provide resiliance against
file descriptor exhaustion attacks, where the attacker consumes all
available file descriptors, forcing the use of the fallback code where
/dev/[u]random is not available.  Since the fallback code is often not
well-tested, it is better to eliminate this potential failure mode
entirely.

The other feature provided by this new system call is the ability to
request randomness from the /dev/urandom entropy pool, but to block
until at least 128 bits of entropy has been accumulated in the
/dev/urandom entropy pool.  Historically, the emphasis in the
/dev/urandom development has been to ensure that urandom pool is
initialized as quickly as possible after system boot, and preferably
before the init scripts start execution.  This is because changing
/dev/urandom reads to block represents an interface change that could
potentially break userspace which is not acceptable.  In practice, on
most x86 desktop and server systems, in general the entropy pool can
be initialized before it is needed (and in modern kernels, we will
printk a warning message if not).  However, on an embedded system,
this may not be hte case.  And so with a new interface, we can provide
this requested functionality of blocking until the urandom pool has
been initialized.  Any userspace program which uses this new
functionality must make sure that if it is used in early boot, that it
will not cause the boot up scripts or other portions of the system
startup to hang indefinitely.

SYNOPSIS
	#include <linux/random.h>

	int getrandom(void *buf, size_t buflen, unsigned int flags);

DESCRIPTION

	The system call getrandom() fills the buffer pointed to by buf
	with up to buflen random bytes which can be used to seed user
	space random number generators (i.e., DRBG's) or for other
	cryptographic processes.  It should not be used Monte Carlo
	simulations or for other probabilistic sampling applications.

	If the GRND_RANDOM flags bit is set, then draw from the
	/dev/random pool instead of /dev/urandom pool.  The
	/dev/random pool is limited based on the entropy that can be
	obtained from environmental noise, so if there is insufficient
	entropy, the requested number of bytes may not be returned.
	If there is no entropy available at all, getrandom(2) will
	either return an error with errno set to EAGAIN, or block if
	the GRND_BLOCK flags bit is set.

	If the GRND_RANDOM flags bit is not set, then the /dev/raundom
	pool will be used.  Unlike reading from the /dev/urandom, if
	the urandom pool has not been sufficiently initialized,
	getrandom(2) will either return an error with errno set to
	EGAIN, or block if the GRND_BLOCK flags bit is set.

RETURN VALUE
       On success, the number of bytes that was returned is returned.

       On error, -1 is returned, and errno is set appropriately

ERRORS
	EINVAL		The buflen value was invalid.

	EFAULT		buf is outside your accessible address space.

	EAGAIN		The requested entropy was not available, and the
			getentropy(2) would have blocked if GRND_BLOCK flag
			was set.

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 arch/x86/syscalls/syscall_32.tbl  |  1 +
 arch/x86/syscalls/syscall_64.tbl  |  1 +
 drivers/char/random.c             | 35 +++++++++++++++++++++++++++++++++--
 include/linux/syscalls.h          |  3 +++
 include/uapi/asm-generic/unistd.h |  4 +++-
 include/uapi/linux/random.h       |  9 +++++++++
 6 files changed, 50 insertions(+), 3 deletions(-)

diff --git a/arch/x86/syscalls/syscall_32.tbl b/arch/x86/syscalls/syscall_32.tbl
index d6b8679..f484e39 100644
--- a/arch/x86/syscalls/syscall_32.tbl
+++ b/arch/x86/syscalls/syscall_32.tbl
@@ -360,3 +360,4 @@
 351	i386	sched_setattr		sys_sched_setattr
 352	i386	sched_getattr		sys_sched_getattr
 353	i386	renameat2		sys_renameat2
+354	i386	getrandom		sys_getrandom
diff --git a/arch/x86/syscalls/syscall_64.tbl b/arch/x86/syscalls/syscall_64.tbl
index ec255a1..6705032 100644
--- a/arch/x86/syscalls/syscall_64.tbl
+++ b/arch/x86/syscalls/syscall_64.tbl
@@ -323,6 +323,7 @@
 314	common	sched_setattr		sys_sched_setattr
 315	common	sched_getattr		sys_sched_getattr
 316	common	renameat2		sys_renameat2
+317	common	getrandom		sys_getrandom
 
 #
 # x32-specific system call numbers start at 512 to avoid cache impact
diff --git a/drivers/char/random.c b/drivers/char/random.c
index aa22fe5..76a56f6 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -258,6 +258,8 @@
 #include <linux/kmemcheck.h>
 #include <linux/workqueue.h>
 #include <linux/irq.h>
+#include <linux/syscalls.h>
+#include <linux/completion.h>
 
 #include <asm/processor.h>
 #include <asm/uaccess.h>
@@ -469,6 +471,8 @@ static struct entropy_store nonblocking_pool = {
 					push_to_pool),
 };
 
+DECLARE_COMPLETION(urandom_initialized);
+
 static __u32 const twist_table[8] = {
 	0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
 	0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 };
@@ -657,6 +661,7 @@ retry:
 		r->entropy_total = 0;
 		if (r == &nonblocking_pool) {
 			prandom_reseed_late();
+			complete_all(&urandom_initialized);
 			pr_notice("random: %s pool is initialized\n", r->name);
 		}
 	}
@@ -1355,7 +1360,7 @@ static int arch_random_refill(void)
 }
 
 static ssize_t
-random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
+_random_read(int nonblock, char __user *buf, size_t nbytes)
 {
 	ssize_t n;
 
@@ -1379,7 +1384,7 @@ random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
 		if (arch_random_refill())
 			continue;
 
-		if (file->f_flags & O_NONBLOCK)
+		if (nonblock)
 			return -EAGAIN;
 
 		wait_event_interruptible(random_read_wait,
@@ -1391,6 +1396,12 @@ random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
 }
 
 static ssize_t
+random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
+{
+	return _random_read(file->f_flags & O_NONBLOCK, buf, nbytes);
+}
+
+static ssize_t
 urandom_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
 {
 	int ret;
@@ -1533,6 +1544,26 @@ const struct file_operations urandom_fops = {
 	.llseek = noop_llseek,
 };
 
+SYSCALL_DEFINE3(getrandom, char __user *, buf, size_t, count,
+		unsigned int, flags)
+{
+	int r;
+
+	if (count > 256)
+		return -EINVAL;
+
+	if (flags & GRND_RANDOM) {
+		return _random_read(!(flags & GRND_BLOCK), buf, count);
+	}
+	if (flags & GRND_BLOCK) {
+		r = wait_for_completion_interruptible(&urandom_initialized);
+		if (r)
+			return r;
+	} else if (!completion_done(&urandom_initialized))
+		return -EAGAIN;
+	return urandom_read(NULL, buf, count, NULL);
+}
+
 /***************************************************************
  * Random UUID interface
  *
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index b0881a0..cd82f72f 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -866,4 +866,7 @@ asmlinkage long sys_process_vm_writev(pid_t pid,
 asmlinkage long sys_kcmp(pid_t pid1, pid_t pid2, int type,
 			 unsigned long idx1, unsigned long idx2);
 asmlinkage long sys_finit_module(int fd, const char __user *uargs, int flags);
+asmlinkage long sys_getrandom(char __user * buf, size_t count,
+			      unsigned int flags);
+
 #endif
diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
index 3336406..2926b1d 100644
--- a/include/uapi/asm-generic/unistd.h
+++ b/include/uapi/asm-generic/unistd.h
@@ -699,9 +699,11 @@ __SYSCALL(__NR_sched_setattr, sys_sched_setattr)
 __SYSCALL(__NR_sched_getattr, sys_sched_getattr)
 #define __NR_renameat2 276
 __SYSCALL(__NR_renameat2, sys_renameat2)
+#define __NR_getrandom 277
+__SYSCALL(__NR_getrandom, sys_getrandom)
 
 #undef __NR_syscalls
-#define __NR_syscalls 277
+#define __NR_syscalls 278
 
 /*
  * All syscalls below here should go away really,
diff --git a/include/uapi/linux/random.h b/include/uapi/linux/random.h
index fff3528..6c13442 100644
--- a/include/uapi/linux/random.h
+++ b/include/uapi/linux/random.h
@@ -40,4 +40,13 @@ struct rand_pool_info {
 	__u32	buf[0];
 };
 
+/*
+ * Flags for getrandom(2)
+ *
+ * GAND_BLOCK 		Allow getrandom(2) to block
+ * GAND_RANDOM		Use the /dev/random pool instead of /dev/urandom
+ */
+#define GRND_BLOCK	0x0001
+#define GRND_RANDOM	0x0002
+
 #endif /* _UAPI_LINUX_RANDOM_H */
-- 
2.0.0

^ permalink raw reply related	[flat|nested] 50+ messages in thread
* Re: [PATCH, RFC] random: introduce getrandom(2) system call
@ 2014-07-17 18:48 Mark Kettenis
  2014-07-17 20:35 ` Andy Lutomirski
  0 siblings, 1 reply; 50+ messages in thread
From: Mark Kettenis @ 2014-07-17 18:48 UTC (permalink / raw)
  To: Theodore Ts'o; +Cc: linux-kernel, linux-crypto, beck

On Thu, Jul 17, 2014, Theodore Ts'o wrote:
> 
> The getrandom(2) system call is a superset of getentropy(2).  When we
> add the support for this into glibc, it won't be terribly difficult
> nor annoying to drop the following in alongside the standard support
> needed for any new system call:
> 
> int getentropy(void *buf, size_t buflen)
> {
> 	int	ret;
> 
> 	ret = getentropy(buf, buflen, 0);
> 	return (ret > 0) ? 0 : ret;
> }

I'm sure you meant to use getrandom() there ;)

Since for LibreSSL we'd want a getentropy() that cannot fail the
getrandom() call should use GRND_BLOCK flag.  Actually it makes sense
(to me) to make blocking the default behaviour and have a
BRND_NONBLOCK flag.  Much in the same way as you need to specify
O_NONBLOCK if you want non-blocking behaviour for files.

^ permalink raw reply	[flat|nested] 50+ messages in thread
* Re: [PATCH, RFC] random: introduce getrandom(2) system call
@ 2014-07-20 16:26 George Spelvin
  2014-07-20 17:03 ` George Spelvin
  2014-07-20 17:24 ` Theodore Ts'o
  0 siblings, 2 replies; 50+ messages in thread
From: George Spelvin @ 2014-07-20 16:26 UTC (permalink / raw)
  To: tytso; +Cc: linux, linux-crypto, linux-kernel

One basic question... why limit this to /dev/random?

If we're trying to avoid fd exhaustion attacks, wouldn't an "atomically
read a file into a buffer" system call (that could be used on
/dev/urandom, or /etc/hostname, or /proc/foo, or...) be more useful?

E.g.

ssize_t readat(int dirfd, char const *path, struct stat *st,
	char *buf, size_t len, int flags);

It's basically equivalent to openat(), optional fstat() (if st is non-NULL),
read(), close(), but it doesn't allocate an fd number.

Is it necessary to have a system call just for entropy?

If you want a "urandom that blocks until seeded", you can always create
another device node for the purpose.

> The main argument I can see for putting in a limit is to encourage the
> "proper" use of the interface.  In practice, anything larger than 128
> probably means the interface is getting misused, either due to a bug
> or some other kind of oversight.

Agreed.  Even 1024 bits is excessive.  32 bytes is the "real" maximum
that people should be asking for with current primitives, so an interface
limitation to 64 is quite defensible.  (But 128 isn't *wildly* excessive.)

If you do stick with a random-specific call, specifying the entropy
in bits (with some specified convention for the last fractional byte)
is anothet interesting idea.  Perhaps too prone to bugs, though.
(People thinking it's bytes and producing low-entropy keys.)

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

end of thread, other threads:[~2014-07-30 12:50 UTC | newest]

Thread overview: 50+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-07-17  9:18 [PATCH, RFC] random: introduce getrandom(2) system call Theodore Ts'o
2014-07-17 10:57 ` Hannes Frederic Sowa
2014-07-17 12:52   ` Theodore Ts'o
2014-07-17 13:15     ` Hannes Frederic Sowa
2014-07-17 12:09 ` Tobias Klauser
2014-07-17 12:52   ` Theodore Ts'o
2014-07-17 16:12 ` Christoph Hellwig
2014-07-17 17:01   ` Theodore Ts'o
2014-07-17 17:05     ` Bob Beck
2014-07-17 17:34       ` Theodore Ts'o
2014-07-17 17:45         ` Bob Beck
2014-07-17 17:46           ` Bob Beck
2014-07-17 17:57             ` Bob Beck
2014-07-17 22:30           ` Theodore Ts'o
2014-07-17 19:56         ` Bob Beck
2014-07-21  0:25     ` Dwayne Litzenberger
2014-07-21  7:18       ` Theodore Ts'o
2014-07-17 19:31 ` Greg KH
2014-07-17 19:33 ` Greg KH
2014-07-17 19:48 ` Zach Brown
     [not found]   ` <20140717194812.GC24196-fypN+1c5dIyjpB87vu3CluTW4wlIGRCZ@public.gmane.org>
2014-07-17 20:54     ` Theodore Ts'o
     [not found]       ` <20140717205417.GT1491-AKGzg7BKzIDYtjvyW6yDsg@public.gmane.org>
2014-07-17 21:39         ` Zach Brown
2014-07-17 20:27 ` Andy Lutomirski
     [not found]   ` <53C8319A.8090108-kltTT9wpgjJwATOyAt5JVQ@public.gmane.org>
2014-07-17 21:14     ` Theodore Ts'o
2014-07-18 16:36 ` Rolf Eike Beer
2014-07-20 15:50 ` Andi Kleen
2014-07-20 17:06   ` Theodore Ts'o
2014-07-20 17:27 ` Andreas Schwab
2014-07-20 17:41   ` Theodore Ts'o
2014-07-21  6:18 ` Dwayne Litzenberger
2014-07-23  8:42 ` Manuel Schölling
  -- strict thread matches above, loose matches on Subject: below --
2014-07-17 18:48 Mark Kettenis
2014-07-17 20:35 ` Andy Lutomirski
2014-07-17 21:28   ` Theodore Ts'o
2014-07-17 21:37     ` Andy Lutomirski
2014-07-17 22:21   ` David Lang
2014-07-20 16:26 George Spelvin
2014-07-20 17:03 ` George Spelvin
2014-07-20 21:32   ` Hannes Frederic Sowa
2014-07-21 11:21     ` George Spelvin
2014-07-21 15:27       ` Hannes Frederic Sowa
2014-07-22  1:02         ` Hannes Frederic Sowa
2014-07-22  4:44           ` Theodore Ts'o
2014-07-22  9:49             ` Hannes Frederic Sowa
2014-07-22 22:59               ` Theodore Ts'o
2014-07-23  9:47                 ` Hannes Frederic Sowa
2014-07-23 11:52                   ` George Spelvin
2014-07-23 12:10                     ` Hannes Frederic Sowa
2014-07-30 12:50                       ` Pavel Machek
2014-07-20 17:24 ` Theodore Ts'o

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).