mirror of
https://github.com/archlinuxarm/PKGBUILDs.git
synced 2024-11-08 22:45:43 +00:00
331 lines
12 KiB
Diff
331 lines
12 KiB
Diff
|
From 8edd5ca21ae45a59021a259932dd22a4281e155e Mon Sep 17 00:00:00 2001
|
||
|
From: Theodore Ts'o <tytso@mit.edu>
|
||
|
Date: Thu, 17 Jul 2014 04:13:05 -0400
|
||
|
Subject: [PATCH 6/8] random: introduce getrandom(2) system call
|
||
|
|
||
|
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 the case. And so with this new
|
||
|
interface, we can provide the functionality of blocking until the
|
||
|
urandom pool has been initialized. Any userspace program which uses
|
||
|
this new functionality must take care to assure that if it is used
|
||
|
during the boot process, that it will not cause the init 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 uses. It should not be used for Monte Carlo
|
||
|
simulations or other programs/algorithms which are doing
|
||
|
probabilistic sampling.
|
||
|
|
||
|
If the GRND_RANDOM flags bit is set, then draw from the
|
||
|
/dev/random pool instead of the /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 block, or return an error with errno set to EAGAIN if
|
||
|
the GRND_NONBLOCK bit is set in flags.
|
||
|
|
||
|
If the GRND_RANDOM bit is not set, then the /dev/urandom pool
|
||
|
will be used. Unlike using read(2) to fetch data from
|
||
|
/dev/urandom, if the urandom pool has not been sufficiently
|
||
|
initialized, getrandom(2) will block (or return -1 with the
|
||
|
errno set to EAGAIN if the GRND_NONBLOCK bit is set in flags).
|
||
|
|
||
|
The getentropy(2) system call in OpenBSD can be emulated using
|
||
|
the following function:
|
||
|
|
||
|
int getentropy(void *buf, size_t buflen)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
if (buflen > 256)
|
||
|
goto failure;
|
||
|
ret = getrandom(buf, buflen, 0);
|
||
|
if (ret < 0)
|
||
|
return ret;
|
||
|
if (ret == buflen)
|
||
|
return 0;
|
||
|
failure:
|
||
|
errno = EIO;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
RETURN VALUE
|
||
|
On success, the number of bytes that was filled in the buf is
|
||
|
returned. This may not be all the bytes requested by the
|
||
|
caller via buflen if insufficient entropy was present in the
|
||
|
/dev/random pool, or if the system call was interrupted by a
|
||
|
signal.
|
||
|
|
||
|
On error, -1 is returned, and errno is set appropriately.
|
||
|
|
||
|
ERRORS
|
||
|
EINVAL An invalid flag was passed to getrandom(2)
|
||
|
|
||
|
EFAULT buf is outside the accessible address space.
|
||
|
|
||
|
EAGAIN The requested entropy was not available, and
|
||
|
getentropy(2) would have blocked if the
|
||
|
GRND_NONBLOCK flag was not set.
|
||
|
|
||
|
EINTR While blocked waiting for entropy, the call was
|
||
|
interrupted by a signal handler; see the description
|
||
|
of how interrupted read(2) calls on "slow" devices
|
||
|
are handled with and without the SA_RESTART flag
|
||
|
in the signal(7) man page.
|
||
|
|
||
|
NOTES
|
||
|
For small requests (buflen <= 256) getrandom(2) will not
|
||
|
return EINTR when reading from the urandom pool once the
|
||
|
entropy pool has been initialized, and it will return all of
|
||
|
the bytes that have been requested. This is the recommended
|
||
|
way to use getrandom(2), and is designed for compatibility
|
||
|
with OpenBSD's getentropy() system call.
|
||
|
|
||
|
However, if you are using GRND_RANDOM, then getrandom(2) may
|
||
|
block until the entropy accounting determines that sufficient
|
||
|
environmental noise has been gathered such that getrandom(2)
|
||
|
will be operating as a NRBG instead of a DRBG for those people
|
||
|
who are working in the NIST SP 800-90 regime. Since it may
|
||
|
block for a long time, these guarantees do *not* apply. The
|
||
|
user may want to interrupt a hanging process using a signal,
|
||
|
so blocking until all of the requested bytes are returned
|
||
|
would be unfriendly.
|
||
|
|
||
|
For this reason, the user of getrandom(2) MUST always check
|
||
|
the return value, in case it returns some error, or if fewer
|
||
|
bytes than requested was returned. In the case of
|
||
|
!GRND_RANDOM and small request, the latter should never
|
||
|
happen, but the careful userspace code (and all crypto code
|
||
|
should be careful) should check for this anyway!
|
||
|
|
||
|
Finally, unless you are doing long-term key generation (and
|
||
|
perhaps not even then), you probably shouldn't be using
|
||
|
GRND_RANDOM. The cryptographic algorithms used for
|
||
|
/dev/urandom are quite conservative, and so should be
|
||
|
sufficient for all purposes. The disadvantage of GRND_RANDOM
|
||
|
is that it can block, and the increased complexity required to
|
||
|
deal with partially fulfilled getrandom(2) requests.
|
||
|
|
||
|
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
|
||
|
Reviewed-by: Zach Brown <zab@zabbo.net>
|
||
|
---
|
||
|
arch/x86/syscalls/syscall_32.tbl | 1 +
|
||
|
arch/x86/syscalls/syscall_64.tbl | 1 +
|
||
|
drivers/char/random.c | 40 ++++++++++++++++++++++++++++++++++++---
|
||
|
include/linux/syscalls.h | 3 +++
|
||
|
include/uapi/asm-generic/unistd.h | 4 +++-
|
||
|
include/uapi/linux/random.h | 9 +++++++++
|
||
|
6 files changed, 54 insertions(+), 4 deletions(-)
|
||
|
|
||
|
diff --git a/arch/x86/syscalls/syscall_32.tbl b/arch/x86/syscalls/syscall_32.tbl
|
||
|
index 3934f1ed30ac..7af3997e33de 100644
|
||
|
--- a/arch/x86/syscalls/syscall_32.tbl
|
||
|
+++ b/arch/x86/syscalls/syscall_32.tbl
|
||
|
@@ -361,3 +361,4 @@
|
||
|
352 i386 sched_getattr sys_sched_getattr
|
||
|
353 i386 renameat2
|
||
|
354 i386 seccomp sys_seccomp
|
||
|
+355 i386 getrandom sys_getrandom
|
||
|
diff --git a/arch/x86/syscalls/syscall_64.tbl b/arch/x86/syscalls/syscall_64.tbl
|
||
|
index a1e46ce5cacf..0251a2c7486b 100644
|
||
|
--- a/arch/x86/syscalls/syscall_64.tbl
|
||
|
+++ b/arch/x86/syscalls/syscall_64.tbl
|
||
|
@@ -324,6 +324,7 @@
|
||
|
315 common sched_getattr sys_sched_getattr
|
||
|
316 common renameat2
|
||
|
317 common seccomp sys_seccomp
|
||
|
+318 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 d9e8504894ee..08279b68375a 100644
|
||
|
--- a/drivers/char/random.c
|
||
|
+++ b/drivers/char/random.c
|
||
|
@@ -257,6 +257,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>
|
||
|
@@ -401,6 +403,7 @@ static struct poolinfo {
|
||
|
*/
|
||
|
static DECLARE_WAIT_QUEUE_HEAD(random_read_wait);
|
||
|
static DECLARE_WAIT_QUEUE_HEAD(random_write_wait);
|
||
|
+static DECLARE_WAIT_QUEUE_HEAD(urandom_init_wait);
|
||
|
static struct fasync_struct *fasync;
|
||
|
|
||
|
/**********************************************************************
|
||
|
@@ -657,6 +660,7 @@ retry:
|
||
|
r->entropy_total = 0;
|
||
|
if (r == &nonblocking_pool) {
|
||
|
prandom_reseed_late();
|
||
|
+ wake_up_interruptible(&urandom_init_wait);
|
||
|
pr_notice("random: %s pool is initialized\n", r->name);
|
||
|
}
|
||
|
}
|
||
|
@@ -1137,13 +1141,14 @@ static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf,
|
||
|
{
|
||
|
ssize_t ret = 0, i;
|
||
|
__u8 tmp[EXTRACT_SIZE];
|
||
|
+ int large_request = (nbytes > 256);
|
||
|
|
||
|
trace_extract_entropy_user(r->name, nbytes, ENTROPY_BITS(r), _RET_IP_);
|
||
|
xfer_secondary_pool(r, nbytes);
|
||
|
nbytes = account(r, nbytes, 0, 0);
|
||
|
|
||
|
while (nbytes) {
|
||
|
- if (need_resched()) {
|
||
|
+ if (large_request && need_resched()) {
|
||
|
if (signal_pending(current)) {
|
||
|
if (ret == 0)
|
||
|
ret = -ERESTARTSYS;
|
||
|
@@ -1285,7 +1290,7 @@ void rand_initialize_disk(struct gendisk *disk)
|
||
|
#endif
|
||
|
|
||
|
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;
|
||
|
|
||
|
@@ -1304,7 +1309,7 @@ random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
|
||
|
return n;
|
||
|
/* Pool is (near) empty. Maybe wait and retry. */
|
||
|
|
||
|
- if (file->f_flags & O_NONBLOCK)
|
||
|
+ if (nonblock)
|
||
|
return -EAGAIN;
|
||
|
|
||
|
wait_event_interruptible(random_read_wait,
|
||
|
@@ -1316,6 +1321,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;
|
||
|
@@ -1456,6 +1467,29 @@ const struct file_operations urandom_fops = {
|
||
|
.llseek = noop_llseek,
|
||
|
};
|
||
|
|
||
|
+SYSCALL_DEFINE3(getrandom, char __user *, buf, size_t, count,
|
||
|
+ unsigned int, flags)
|
||
|
+{
|
||
|
+ if (flags & ~(GRND_NONBLOCK|GRND_RANDOM))
|
||
|
+ return -EINVAL;
|
||
|
+
|
||
|
+ if (count > INT_MAX)
|
||
|
+ count = INT_MAX;
|
||
|
+
|
||
|
+ if (flags & GRND_RANDOM)
|
||
|
+ return _random_read(flags & GRND_NONBLOCK, buf, count);
|
||
|
+
|
||
|
+ if (unlikely(nonblocking_pool.initialized == 0)) {
|
||
|
+ if (flags & GRND_NONBLOCK)
|
||
|
+ return -EAGAIN;
|
||
|
+ wait_event_interruptible(urandom_init_wait,
|
||
|
+ nonblocking_pool.initialized);
|
||
|
+ if (signal_pending(current))
|
||
|
+ return -ERESTARTSYS;
|
||
|
+ }
|
||
|
+ return urandom_read(NULL, buf, count, NULL);
|
||
|
+}
|
||
|
+
|
||
|
/***************************************************************
|
||
|
* Random UUID interface
|
||
|
*
|
||
|
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
|
||
|
index 029b012aa075..07f6e749fef6 100644
|
||
|
--- a/include/linux/syscalls.h
|
||
|
+++ b/include/linux/syscalls.h
|
||
|
@@ -855,6 +855,9 @@ 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);
|
||
|
+
|
||
|
asmlinkage long sys_seccomp(unsigned int op, unsigned int flags,
|
||
|
const char __user *uargs);
|
||
|
#endif
|
||
|
diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
|
||
|
index de5ce24594dd..fb7247c39241 100644
|
||
|
--- a/include/uapi/asm-generic/unistd.h
|
||
|
+++ b/include/uapi/asm-generic/unistd.h
|
||
|
@@ -703,9 +703,11 @@ __SYSCALL(__NR_seccomp, sys_seccomp)
|
||
|
|
||
|
#define __NR_seccomp 277
|
||
|
__SYSCALL(__NR_seccomp, sys_seccomp)
|
||
|
+#define __NR_getrandom 278
|
||
|
+__SYSCALL(__NR_getrandom, sys_getrandom)
|
||
|
|
||
|
#undef __NR_syscalls
|
||
|
-#define __NR_syscalls 278
|
||
|
+#define __NR_syscalls 279
|
||
|
|
||
|
/*
|
||
|
* All syscalls below here should go away really,
|
||
|
diff --git a/include/uapi/linux/random.h b/include/uapi/linux/random.h
|
||
|
index fff3528a078f..3f93d1695e7f 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)
|
||
|
+ *
|
||
|
+ * GRND_NONBLOCK Don't block and return EAGAIN instead
|
||
|
+ * GRND_RANDOM Use the /dev/random pool instead of /dev/urandom
|
||
|
+ */
|
||
|
+#define GRND_NONBLOCK 0x0001
|
||
|
+#define GRND_RANDOM 0x0002
|
||
|
+
|
||
|
#endif /* _UAPI_LINUX_RANDOM_H */
|
||
|
--
|
||
|
2.13.0
|
||
|
|