aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>2014-07-17 04:13:05 -0400
committerTheodore Ts'o <tytso@mit.edu>2014-08-05 16:41:22 -0400
commitc6e9d6f38894798696f23c8084ca7edbf16ee895 (patch)
tree425518fa900aeea8e0d85688903c680049a81fea
parent9dda727d37ff6ce1628dd2b8557ad011c49db377 (diff)
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>
-rw-r--r--arch/x86/syscalls/syscall_32.tbl1
-rw-r--r--arch/x86/syscalls/syscall_64.tbl1
-rw-r--r--drivers/char/random.c40
-rw-r--r--include/linux/syscalls.h3
-rw-r--r--include/uapi/asm-generic/unistd.h4
-rw-r--r--include/uapi/linux/random.h9
6 files changed, 54 insertions, 4 deletions
diff --git a/arch/x86/syscalls/syscall_32.tbl b/arch/x86/syscalls/syscall_32.tbl
index d6b867921612..5b46a618aeb1 100644
--- a/arch/x86/syscalls/syscall_32.tbl
+++ b/arch/x86/syscalls/syscall_32.tbl
@@ -360,3 +360,4 @@
360351 i386 sched_setattr sys_sched_setattr 360351 i386 sched_setattr sys_sched_setattr
361352 i386 sched_getattr sys_sched_getattr 361352 i386 sched_getattr sys_sched_getattr
362353 i386 renameat2 sys_renameat2 362353 i386 renameat2 sys_renameat2
363355 i386 getrandom sys_getrandom
diff --git a/arch/x86/syscalls/syscall_64.tbl b/arch/x86/syscalls/syscall_64.tbl
index ec255a1646d2..0dc4bf891460 100644
--- a/arch/x86/syscalls/syscall_64.tbl
+++ b/arch/x86/syscalls/syscall_64.tbl
@@ -323,6 +323,7 @@
323314 common sched_setattr sys_sched_setattr 323314 common sched_setattr sys_sched_setattr
324315 common sched_getattr sys_sched_getattr 324315 common sched_getattr sys_sched_getattr
325316 common renameat2 sys_renameat2 325316 common renameat2 sys_renameat2
326318 common getrandom sys_getrandom
326 327
327# 328#
328# x32-specific system call numbers start at 512 to avoid cache impact 329# 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 aa22fe551c2a..7d1682ea1e86 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -258,6 +258,8 @@
258#include <linux/kmemcheck.h> 258#include <linux/kmemcheck.h>
259#include <linux/workqueue.h> 259#include <linux/workqueue.h>
260#include <linux/irq.h> 260#include <linux/irq.h>
261#include <linux/syscalls.h>
262#include <linux/completion.h>
261 263
262#include <asm/processor.h> 264#include <asm/processor.h>
263#include <asm/uaccess.h> 265#include <asm/uaccess.h>
@@ -404,6 +406,7 @@ static struct poolinfo {
404 */ 406 */
405static DECLARE_WAIT_QUEUE_HEAD(random_read_wait); 407static DECLARE_WAIT_QUEUE_HEAD(random_read_wait);
406static DECLARE_WAIT_QUEUE_HEAD(random_write_wait); 408static DECLARE_WAIT_QUEUE_HEAD(random_write_wait);
409static DECLARE_WAIT_QUEUE_HEAD(urandom_init_wait);
407static struct fasync_struct *fasync; 410static struct fasync_struct *fasync;
408 411
409/********************************************************************** 412/**********************************************************************
@@ -657,6 +660,7 @@ retry:
657 r->entropy_total = 0; 660 r->entropy_total = 0;
658 if (r == &nonblocking_pool) { 661 if (r == &nonblocking_pool) {
659 prandom_reseed_late(); 662 prandom_reseed_late();
663 wake_up_interruptible(&urandom_init_wait);
660 pr_notice("random: %s pool is initialized\n", r->name); 664 pr_notice("random: %s pool is initialized\n", r->name);
661 } 665 }
662 } 666 }
@@ -1174,13 +1178,14 @@ static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf,
1174{ 1178{
1175 ssize_t ret = 0, i; 1179 ssize_t ret = 0, i;
1176 __u8 tmp[EXTRACT_SIZE]; 1180 __u8 tmp[EXTRACT_SIZE];
1181 int large_request = (nbytes > 256);
1177 1182
1178 trace_extract_entropy_user(r->name, nbytes, ENTROPY_BITS(r), _RET_IP_); 1183 trace_extract_entropy_user(r->name, nbytes, ENTROPY_BITS(r), _RET_IP_);
1179 xfer_secondary_pool(r, nbytes); 1184 xfer_secondary_pool(r, nbytes);
1180 nbytes = account(r, nbytes, 0, 0); 1185 nbytes = account(r, nbytes, 0, 0);
1181 1186
1182 while (nbytes) { 1187 while (nbytes) {
1183 if (need_resched()) { 1188 if (large_request && need_resched()) {
1184 if (signal_pending(current)) { 1189 if (signal_pending(current)) {
1185 if (ret == 0) 1190 if (ret == 0)
1186 ret = -ERESTARTSYS; 1191 ret = -ERESTARTSYS;
@@ -1355,7 +1360,7 @@ static int arch_random_refill(void)
1355} 1360}
1356 1361
1357static ssize_t 1362static ssize_t
1358random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) 1363_random_read(int nonblock, char __user *buf, size_t nbytes)
1359{ 1364{
1360 ssize_t n; 1365 ssize_t n;
1361 1366
@@ -1379,7 +1384,7 @@ random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
1379 if (arch_random_refill()) 1384 if (arch_random_refill())
1380 continue; 1385 continue;
1381 1386
1382 if (file->f_flags & O_NONBLOCK) 1387 if (nonblock)
1383 return -EAGAIN; 1388 return -EAGAIN;
1384 1389
1385 wait_event_interruptible(random_read_wait, 1390 wait_event_interruptible(random_read_wait,
@@ -1391,6 +1396,12 @@ random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
1391} 1396}
1392 1397
1393static ssize_t 1398static ssize_t
1399random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
1400{
1401 return _random_read(file->f_flags & O_NONBLOCK, buf, nbytes);
1402}
1403
1404static ssize_t
1394urandom_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) 1405urandom_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
1395{ 1406{
1396 int ret; 1407 int ret;
@@ -1533,6 +1544,29 @@ const struct file_operations urandom_fops = {
1533 .llseek = noop_llseek, 1544 .llseek = noop_llseek,
1534}; 1545};
1535 1546
1547SYSCALL_DEFINE3(getrandom, char __user *, buf, size_t, count,
1548 unsigned int, flags)
1549{
1550 if (flags & ~(GRND_NONBLOCK|GRND_RANDOM))
1551 return -EINVAL;
1552
1553 if (count > INT_MAX)
1554 count = INT_MAX;
1555
1556 if (flags & GRND_RANDOM)
1557 return _random_read(flags & GRND_NONBLOCK, buf, count);
1558
1559 if (unlikely(nonblocking_pool.initialized == 0)) {
1560 if (flags & GRND_NONBLOCK)
1561 return -EAGAIN;
1562 wait_event_interruptible(urandom_init_wait,
1563 nonblocking_pool.initialized);
1564 if (signal_pending(current))
1565 return -ERESTARTSYS;
1566 }
1567 return urandom_read(NULL, buf, count, NULL);
1568}
1569
1536/*************************************************************** 1570/***************************************************************
1537 * Random UUID interface 1571 * Random UUID interface
1538 * 1572 *
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index b0881a0ed322..43324a897cf2 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -866,4 +866,7 @@ asmlinkage long sys_process_vm_writev(pid_t pid,
866asmlinkage long sys_kcmp(pid_t pid1, pid_t pid2, int type, 866asmlinkage long sys_kcmp(pid_t pid1, pid_t pid2, int type,
867 unsigned long idx1, unsigned long idx2); 867 unsigned long idx1, unsigned long idx2);
868asmlinkage long sys_finit_module(int fd, const char __user *uargs, int flags); 868asmlinkage long sys_finit_module(int fd, const char __user *uargs, int flags);
869asmlinkage long sys_getrandom(char __user *buf, size_t count,
870 unsigned int flags);
871
869#endif 872#endif
diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
index 333640608087..1d104a2ca643 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)
699__SYSCALL(__NR_sched_getattr, sys_sched_getattr) 699__SYSCALL(__NR_sched_getattr, sys_sched_getattr)
700#define __NR_renameat2 276 700#define __NR_renameat2 276
701__SYSCALL(__NR_renameat2, sys_renameat2) 701__SYSCALL(__NR_renameat2, sys_renameat2)
702#define __NR_getrandom 278
703__SYSCALL(__NR_getrandom, sys_getrandom)
702 704
703#undef __NR_syscalls 705#undef __NR_syscalls
704#define __NR_syscalls 277 706#define __NR_syscalls 279
705 707
706/* 708/*
707 * All syscalls below here should go away really, 709 * 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 {
40 __u32 buf[0]; 40 __u32 buf[0];
41}; 41};
42 42
43/*
44 * Flags for getrandom(2)
45 *
46 * GRND_NONBLOCK Don't block and return EAGAIN instead
47 * GRND_RANDOM Use the /dev/random pool instead of /dev/urandom
48 */
49#define GRND_NONBLOCK 0x0001
50#define GRND_RANDOM 0x0002
51
43#endif /* _UAPI_LINUX_RANDOM_H */ 52#endif /* _UAPI_LINUX_RANDOM_H */