Changeset View
Changeset View
Standalone View
Standalone View
lib/libc/gen/getentropy.c
Show All 25 Lines | |||||
* SUCH DAMAGE. | * SUCH DAMAGE. | ||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/random.h> | #include <sys/random.h> | ||||
#include <sys/sysctl.h> | |||||
#include <errno.h> | #include <errno.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include "libc_private.h" | #include "libc_private.h" | ||||
extern int __sysctl(int *, u_int, void *, size_t *, void *, size_t); | |||||
static size_t | |||||
arnd_sysctl(u_char *buf, size_t size) | |||||
{ | |||||
int mib[2]; | |||||
size_t len, done; | |||||
mib[0] = CTL_KERN; | |||||
mib[1] = KERN_ARND; | |||||
done = 0; | |||||
do { | |||||
len = size; | |||||
if (__sysctl(mib, 2, buf, &len, NULL, 0) == -1) | |||||
return (done); | |||||
done += len; | |||||
buf += len; | |||||
size -= len; | |||||
} while (size > 0); | |||||
return (done); | |||||
} | |||||
/* | /* | ||||
* If a newer libc is accidentally installed on an older kernel, provide high | * If a newer libc is accidentally installed on an older kernel, provide high | ||||
* quality random data anyway. The sysctl interface is not as fast and does | * quality random data anyway. The sysctl interface is not as fast and does | ||||
* not block by itself, but is provided by even very old kernels. | * not block by itself, but is provided by even very old kernels. | ||||
*/ | */ | ||||
static int | static int | ||||
getentropy_fallback(void *buf, size_t buflen) | getentropy_fallback(void *buf, size_t buflen) | ||||
{ | { | ||||
/* | /* | ||||
* oldp (buf) == NULL has a special meaning for sysctl that results in | * oldp (buf) == NULL has a special meaning for sysctl that results in | ||||
* no EFAULT. For compatibility with the kernel getrandom(2), detect | * no EFAULT. For compatibility with the kernel getrandom(2), detect | ||||
* this case and return the appropriate error. | * this case and return the appropriate error. | ||||
*/ | */ | ||||
if (buf == NULL && buflen > 0) { | if (buf == NULL && buflen > 0) { | ||||
errno = EFAULT; | errno = EFAULT; | ||||
return (-1); | return (-1); | ||||
} | } | ||||
if (__arc4_sysctl(buf, buflen) != buflen) { | if (arnd_sysctl(buf, buflen) != buflen) { | ||||
if (errno == EFAULT) | if (errno == EFAULT) | ||||
return (-1); | return (-1); | ||||
/* | /* | ||||
* This cannot happen. _arc4_sysctl() spins until the random | * This cannot happen. _arc4_sysctl() spins until the random | ||||
* device is seeded and then repeatedly reads until the full | * device is seeded and then repeatedly reads until the full | ||||
* request is satisfied. The only way for this to return a zero | * request is satisfied. The only way for this to return a zero | ||||
* byte or short read is if sysctl(2) on the kern.arandom MIB | * byte or short read is if sysctl(2) on the kern.arandom MIB | ||||
* fails. In this case, exceping the user-provided-a-bogus- | * fails. In this case, exceping the user-provided-a-bogus- | ||||
Show All 14 Lines | if (buflen > 256) { | ||||
return (-1); | return (-1); | ||||
} | } | ||||
while (buflen > 0) { | while (buflen > 0) { | ||||
rd = getrandom(buf, buflen, 0); | rd = getrandom(buf, buflen, 0); | ||||
if (rd == -1) { | if (rd == -1) { | ||||
if (errno == EINTR) | if (errno == EINTR) | ||||
continue; | continue; | ||||
else if (errno == ENOSYS || errno == ECAPMODE) | else if (errno == ENOSYS || errno == ECAPMODE) | ||||
return (getentropy_fallback(buf, buflen)); | return (getentropy_fallback(buf, buflen)); | ||||
cem: Nice catch | |||||
else | else | ||||
return (-1); | return (-1); | ||||
} | } | ||||
/* This cannot happen. */ | /* This cannot happen. */ | ||||
if (rd == 0) | if (rd == 0) | ||||
abort(); | abort(); | ||||
buf = (char *)buf + rd; | buf = (char *)buf + rd; | ||||
buflen -= rd; | buflen -= rd; | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } |
Nice catch