aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorConrad Meyer <cem@FreeBSD.org>2018-03-21 23:52:37 +0000
committerConrad Meyer <cem@FreeBSD.org>2018-03-21 23:52:37 +0000
commit08a7e74c7c0169d863bb2e1abcaaeae8ec589fae (patch)
tree89b2e8d77cc61374d4b43269a3b6a4edaeff6f67
parent16451ba2d7f79632408204f4cf2da79f030498f1 (diff)
getentropy(3): Fallback to kern.arandom sysctl on older kernels
On older kernels, when userspace program disables SIGSYS, catch ENOSYS and emulate getrandom(2) syscall with the kern.arandom sysctl (via existing arc4_sysctl wrapper). Special care is taken to faithfully emulate EFAULT on NULL pointers, because sysctl(3) as used by kern.arandom ignores NULL oldp. (This was caught by getentropy(3) ATF tests.) Reported by: kib Reviewed by: kib Discussed with: delphij Sponsored by: Dell EMC Isilon Differential Revision: https://reviews.freebsd.org/D14785
Notes
svn path=/head/; revision=331334
-rw-r--r--lib/libc/gen/arc4random.c6
-rw-r--r--lib/libc/gen/getentropy.c35
-rw-r--r--lib/libc/include/libc_private.h2
-rw-r--r--lib/libc/sys/Symbol.map2
-rw-r--r--lib/libc/tests/gen/getentropy_test.c3
5 files changed, 42 insertions, 6 deletions
diff --git a/lib/libc/gen/arc4random.c b/lib/libc/gen/arc4random.c
index a48c6f819388..79b15e9bdb76 100644
--- a/lib/libc/gen/arc4random.c
+++ b/lib/libc/gen/arc4random.c
@@ -111,8 +111,8 @@ arc4_addrandom(u_char *dat, int datlen)
rs.j = rs.i;
}
-static size_t
-arc4_sysctl(u_char *buf, size_t size)
+size_t
+__arc4_sysctl(u_char *buf, size_t size)
{
int mib[2];
size_t len, done;
@@ -143,7 +143,7 @@ arc4_stir(void)
arc4_init();
rs_initialized = 1;
}
- if (arc4_sysctl(rdat, KEYSIZE) != KEYSIZE) {
+ if (__arc4_sysctl(rdat, KEYSIZE) != KEYSIZE) {
/*
* The sysctl cannot fail. If it does fail on some FreeBSD
* derivative or after some future change, just abort so that
diff --git a/lib/libc/gen/getentropy.c b/lib/libc/gen/getentropy.c
index 8484570ba78d..ec5ef12ec775 100644
--- a/lib/libc/gen/getentropy.c
+++ b/lib/libc/gen/getentropy.c
@@ -37,6 +37,39 @@ __FBSDID("$FreeBSD$");
#include "libc_private.h"
+/*
+ * 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
+ * not block by itself, but is provided by even very old kernels.
+ */
+static int
+getentropy_fallback(void *buf, size_t buflen)
+{
+ /*
+ * oldp (buf) == NULL has a special meaning for sysctl that results in
+ * no EFAULT. For compatibility with the kernel getrandom(2), detect
+ * this case and return the appropriate error.
+ */
+ if (buf == NULL && buflen > 0) {
+ errno = EFAULT;
+ return (-1);
+ }
+ if (__arc4_sysctl(buf, buflen) != buflen) {
+ if (errno == EFAULT)
+ return (-1);
+ /*
+ * This cannot happen. _arc4_sysctl() spins until the random
+ * device is seeded and then repeatedly reads until the full
+ * 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
+ * fails. In this case, exceping the user-provided-a-bogus-
+ * buffer EFAULT, give up (like for arc4random(3)'s arc4_stir).
+ */
+ abort();
+ }
+ return (0);
+}
+
int
getentropy(void *buf, size_t buflen)
{
@@ -53,7 +86,7 @@ getentropy(void *buf, size_t buflen)
if (errno == EINTR)
continue;
else if (errno == ENOSYS)
- abort();
+ return (getentropy_fallback(buf, buflen));
else
return (-1);
}
diff --git a/lib/libc/include/libc_private.h b/lib/libc/include/libc_private.h
index aaa40deeb083..0985b5ed8741 100644
--- a/lib/libc/include/libc_private.h
+++ b/lib/libc/include/libc_private.h
@@ -405,6 +405,8 @@ int __sys_futimens(int fd, const struct timespec *times) __hidden;
int __sys_utimensat(int fd, const char *path,
const struct timespec *times, int flag) __hidden;
+__size_t __arc4_sysctl(unsigned char *, __size_t);
+
/* execve() with PATH processing to implement posix_spawnp() */
int _execvpe(const char *, char * const *, char * const *);
diff --git a/lib/libc/sys/Symbol.map b/lib/libc/sys/Symbol.map
index 1a711c99ba8a..bfc0b7e7eb2c 100644
--- a/lib/libc/sys/Symbol.map
+++ b/lib/libc/sys/Symbol.map
@@ -628,8 +628,6 @@ FBSDprivate_1.0 {
__sys_getppid;
_getpriority;
__sys_getpriority;
- _getrandom;
- __sys_getrandom;
_getresgid;
__sys_getresgid;
_getresuid;
diff --git a/lib/libc/tests/gen/getentropy_test.c b/lib/libc/tests/gen/getentropy_test.c
index 731bed775263..1290e6e50529 100644
--- a/lib/libc/tests/gen/getentropy_test.c
+++ b/lib/libc/tests/gen/getentropy_test.c
@@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <errno.h>
+#include <signal.h>
#include <unistd.h>
#include <atf-c.h>
@@ -77,6 +78,8 @@ ATF_TC_BODY(getentropy_sizes, tc)
ATF_TP_ADD_TCS(tp)
{
+ signal(SIGSYS, SIG_IGN);
+
ATF_TP_ADD_TC(tp, getentropy_count);
ATF_TP_ADD_TC(tp, getentropy_fault);
ATF_TP_ADD_TC(tp, getentropy_sizes);