Changeset View
Changeset View
Standalone View
Standalone View
head/sys/kern/sys_getrandom.c
Show All 32 Lines | |||||
#include <sys/errno.h> | #include <sys/errno.h> | ||||
#include <sys/limits.h> | #include <sys/limits.h> | ||||
#include <sys/proc.h> | #include <sys/proc.h> | ||||
#include <sys/random.h> | #include <sys/random.h> | ||||
#include <sys/sysproto.h> | #include <sys/sysproto.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/uio.h> | #include <sys/uio.h> | ||||
#define GRND_VALIDFLAGS (GRND_NONBLOCK | GRND_RANDOM) | #define GRND_VALIDFLAGS (GRND_NONBLOCK | GRND_RANDOM | GRND_INSECURE) | ||||
/* | /* | ||||
* random_read_uio(9) returns EWOULDBLOCK if a nonblocking request would block, | * random_read_uio(9) returns EWOULDBLOCK if a nonblocking request would block, | ||||
* but the Linux API name is EAGAIN. On FreeBSD, they have the same numeric | * but the Linux API name is EAGAIN. On FreeBSD, they have the same numeric | ||||
* value for now. | * value for now. | ||||
*/ | */ | ||||
CTASSERT(EWOULDBLOCK == EAGAIN); | CTASSERT(EWOULDBLOCK == EAGAIN); | ||||
static int | static int | ||||
kern_getrandom(struct thread *td, void *user_buf, size_t buflen, | kern_getrandom(struct thread *td, void *user_buf, size_t buflen, | ||||
unsigned int flags) | unsigned int flags) | ||||
{ | { | ||||
struct uio auio; | struct uio auio; | ||||
struct iovec aiov; | struct iovec aiov; | ||||
int error; | int error; | ||||
if ((flags & ~GRND_VALIDFLAGS) != 0) | if ((flags & ~GRND_VALIDFLAGS) != 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (buflen > IOSIZE_MAX) | if (buflen > IOSIZE_MAX) | ||||
return (EINVAL); | return (EINVAL); | ||||
/* | |||||
* Linux compatibility: We have two choices for handling Linux's | |||||
* GRND_INSECURE. | |||||
* | |||||
* 1. We could ignore it completely (like GRND_RANDOM). However, this | |||||
* might produce the surprising result of GRND_INSECURE requests | |||||
* blocking, when the Linux API does not block. | |||||
* | |||||
* 2. Alternatively, we could treat GRND_INSECURE requests as requests | |||||
* for GRND_NONBLOCk. Here, the surprising result for Linux programs | |||||
* is that invocations with unseeded random(4) will produce EAGAIN, | |||||
* rather than garbage. | |||||
* | |||||
* Honoring the flag in the way Linux does seems fraught. If we | |||||
* actually use the output of a random(4) implementation prior to | |||||
* seeding, we leak some entropy about the initial seed to attackers. | |||||
* This seems unacceptable -- it defeats the purpose of blocking on | |||||
* initial seeding. | |||||
* | |||||
* Secondary to that concern, before seeding we may have arbitrarily | |||||
* little entropy collected; producing output from zero or a handful of | |||||
* entropy bits does not seem particularly useful to userspace. | |||||
* | |||||
* If userspace can accept garbage, insecure non-random bytes, they can | |||||
* create their own insecure garbage with srandom(time(NULL)) or | |||||
* similar. Asking the kernel to produce it from the secure | |||||
* getrandom(2) API seems inane. | |||||
* | |||||
* We elect to emulate GRND_INSECURE as an alternative spelling of | |||||
* GRND_NONBLOCK (2). | |||||
*/ | |||||
if ((flags & GRND_INSECURE) != 0) | |||||
flags |= GRND_NONBLOCK; | |||||
if (buflen == 0) { | if (buflen == 0) { | ||||
td->td_retval[0] = 0; | td->td_retval[0] = 0; | ||||
return (0); | return (0); | ||||
} | } | ||||
aiov.iov_base = user_buf; | aiov.iov_base = user_buf; | ||||
aiov.iov_len = buflen; | aiov.iov_len = buflen; | ||||
Show All 27 Lines |