aboutsummaryrefslogtreecommitdiff
path: root/src/unix
diff options
context:
space:
mode:
authorBen Noordhuis <info@bnoordhuis.nl>2019-06-22 15:18:17 +0200
committerSaúl Ibarra Corretgé <s@saghul.net>2019-09-18 08:09:14 +0200
commit4ed2a78f0eb1c9d2a0c98ea4a88c6824e5958dc6 (patch)
treece6b8b3282f3ac65d6bbeba5cec36463b68f06e5 /src/unix
parentfd1502f5630591bf8ce79502df9b5d76868dfd3b (diff)
downloadlibuv-4ed2a78f0eb1c9d2a0c98ea4a88c6824e5958dc6.tar.gz
libuv-4ed2a78f0eb1c9d2a0c98ea4a88c6824e5958dc6.zip
unix,win: add uv_random()
Add an API for obtaining cryptographically strong random data from the system PRNG. Co-authored-by: Saúl Ibarra Corretgé <s@saghul.net> Refs: https://github.com/libuv/libuv/pull/1055 PR-URL: https://github.com/libuv/libuv/pull/2347 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com>
Diffstat (limited to 'src/unix')
-rw-r--r--src/unix/internal.h6
-rw-r--r--src/unix/linux-syscalls.c25
-rw-r--r--src/unix/linux-syscalls.h1
-rw-r--r--src/unix/random-devurandom.c93
-rw-r--r--src/unix/random-getentropy.c57
-rw-r--r--src/unix/random-getrandom.c88
-rw-r--r--src/unix/random-sysctl.c91
7 files changed, 361 insertions, 0 deletions
diff --git a/src/unix/internal.h b/src/unix/internal.h
index 26061647..47f22000 100644
--- a/src/unix/internal.h
+++ b/src/unix/internal.h
@@ -270,6 +270,12 @@ uv_handle_type uv__handle_type(int fd);
FILE* uv__open_file(const char* path);
int uv__getpwuid_r(uv_passwd_t* pwd);
+/* random */
+int uv__random_devurandom(void* buf, size_t buflen);
+int uv__random_getrandom(void* buf, size_t buflen);
+int uv__random_getentropy(void* buf, size_t buflen);
+int uv__random_readpath(const char* path, void* buf, size_t buflen);
+int uv__random_sysctl(void* buf, size_t buflen);
#if defined(__APPLE__)
int uv___stream_fd(const uv_stream_t* handle);
diff --git a/src/unix/linux-syscalls.c b/src/unix/linux-syscalls.c
index 5637cf98..95038786 100644
--- a/src/unix/linux-syscalls.c
+++ b/src/unix/linux-syscalls.c
@@ -203,6 +203,22 @@
# endif
#endif /* __NR_statx */
+#ifndef __NR_getrandom
+# if defined(__x86_64__)
+# define __NR_getrandom 318
+# elif defined(__i386__)
+# define __NR_getrandom 355
+# elif defined(__aarch64__)
+# define __NR_getrandom 384
+# elif defined(__arm__)
+# define __NR_getrandom (UV_SYSCALL_BASE + 384)
+# elif defined(__ppc__)
+# define __NR_getrandom 359
+# elif defined(__s390__)
+# define __NR_getrandom 349
+# endif
+#endif /* __NR_getrandom */
+
int uv__accept4(int fd, struct sockaddr* addr, socklen_t* addrlen, int flags) {
#if defined(__i386__)
unsigned long args[4];
@@ -367,3 +383,12 @@ int uv__statx(int dirfd,
return errno = ENOSYS, -1;
#endif
}
+
+
+ssize_t uv__getrandom(void* buf, size_t buflen, unsigned flags) {
+#if defined(__NR_getrandom)
+ return syscall(__NR_getrandom, buf, buflen, flags);
+#else
+ return errno = ENOSYS, -1;
+#endif
+}
diff --git a/src/unix/linux-syscalls.h b/src/unix/linux-syscalls.h
index 7e58bfa2..b7729b82 100644
--- a/src/unix/linux-syscalls.h
+++ b/src/unix/linux-syscalls.h
@@ -148,5 +148,6 @@ int uv__statx(int dirfd,
int flags,
unsigned int mask,
struct uv__statx* statxbuf);
+ssize_t uv__getrandom(void* buf, size_t buflen, unsigned flags);
#endif /* UV_LINUX_SYSCALL_H_ */
diff --git a/src/unix/random-devurandom.c b/src/unix/random-devurandom.c
new file mode 100644
index 00000000..bfc40d20
--- /dev/null
+++ b/src/unix/random-devurandom.c
@@ -0,0 +1,93 @@
+/* Copyright libuv contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <sys/stat.h>
+#include <unistd.h>
+
+static uv_once_t once = UV_ONCE_INIT;
+static int status;
+
+
+int uv__random_readpath(const char* path, void* buf, size_t buflen) {
+ struct stat s;
+ size_t pos;
+ ssize_t n;
+ int fd;
+
+ fd = uv__open_cloexec(path, O_RDONLY);
+
+ if (fd == -1)
+ return UV__ERR(errno);
+
+ if (fstat(fd, &s)) {
+ uv__close(fd);
+ return UV__ERR(errno);
+ }
+
+ if (!S_ISCHR(s.st_mode)) {
+ uv__close(fd);
+ return UV_EIO;
+ }
+
+ for (pos = 0; pos != buflen; pos += n) {
+ do
+ n = read(fd, (char*) buf + pos, buflen - pos);
+ while (n == -1 && errno == EINTR);
+
+ if (n == -1) {
+ uv__close(fd);
+ return UV__ERR(errno);
+ }
+
+ if (n == 0) {
+ uv__close(fd);
+ return UV_EIO;
+ }
+ }
+
+ uv__close(fd);
+ return 0;
+}
+
+
+static void uv__random_devurandom_init(void) {
+ char c;
+
+ /* Linux's and NetBSD's random(4) man page suggests applications should read
+ * at least once from /dev/random before switching to /dev/urandom in order
+ * to seed the system RNG. Reads from /dev/random can of course block
+ * indefinitely until entropy is available but that's the point.
+ */
+ status = uv__random_readpath("/dev/random", &c, 1);
+}
+
+
+int uv__random_devurandom(void* buf, size_t buflen) {
+ uv_once(&once, uv__random_devurandom_init);
+
+ if (status != 0)
+ return status;
+
+ return uv__random_readpath("/dev/urandom", buf, buflen);
+}
diff --git a/src/unix/random-getentropy.c b/src/unix/random-getentropy.c
new file mode 100644
index 00000000..c45d9fd4
--- /dev/null
+++ b/src/unix/random-getentropy.c
@@ -0,0 +1,57 @@
+/* Copyright libuv contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <stddef.h>
+#include <dlfcn.h>
+
+typedef int (*uv__getentropy_cb)(void *, size_t);
+
+static uv__getentropy_cb uv__getentropy;
+static uv_once_t once = UV_ONCE_INIT;
+
+
+static void uv__random_getentropy_init(void) {
+ uv__getentropy = (uv__getentropy_cb) dlsym(RTLD_DEFAULT, "getentropy");
+}
+
+
+int uv__random_getentropy(void* buf, size_t buflen) {
+ size_t pos;
+ size_t stride;
+
+ uv_once(&once, uv__random_getentropy_init);
+
+ if (uv__getentropy == NULL)
+ return UV_ENOSYS;
+
+ /* getentropy() returns an error for requests > 256 bytes. */
+ for (pos = 0, stride = 256; pos + stride < buflen; pos += stride)
+ if (uv__getentropy((char *) buf + pos, stride))
+ return UV__ERR(errno);
+
+ if (uv__getentropy((char *) buf + pos, buflen - pos))
+ return UV__ERR(errno);
+
+ return 0;
+}
diff --git a/src/unix/random-getrandom.c b/src/unix/random-getrandom.c
new file mode 100644
index 00000000..bcc94089
--- /dev/null
+++ b/src/unix/random-getrandom.c
@@ -0,0 +1,88 @@
+/* Copyright libuv contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#ifdef __linux__
+
+#include "linux-syscalls.h"
+
+#define uv__random_getrandom_init() 0
+
+#else /* !__linux__ */
+
+#include <stddef.h>
+#include <dlfcn.h>
+
+typedef ssize_t (*uv__getrandom_cb)(void *, size_t, unsigned);
+
+static uv__getrandom_cb uv__getrandom;
+static uv_once_t once = UV_ONCE_INIT;
+
+static void uv__random_getrandom_init_once(void) {
+ uv__getrandom = (uv__getrandom_cb) dlsym(RTLD_DEFAULT, "getrandom");
+}
+
+static int uv__random_getrandom_init(void) {
+ uv_once(&once, uv__random_getrandom_init_once);
+
+ if (uv__getrandom == NULL)
+ return UV_ENOSYS;
+
+ return 0;
+}
+
+#endif /* !__linux__ */
+
+int uv__random_getrandom(void* buf, size_t buflen) {
+ ssize_t n;
+ size_t pos;
+ int rc;
+
+ rc = uv__random_getrandom_init();
+ if (rc != 0)
+ return rc;
+
+ for (pos = 0; pos != buflen; pos += n) {
+ do {
+ n = buflen - pos;
+
+ /* Most getrandom() implementations promise that reads <= 256 bytes
+ * will always succeed and won't be interrupted by signals.
+ * It's therefore useful to split it up in smaller reads because
+ * one big read may, in theory, continuously fail with EINTR.
+ */
+ if (n > 256)
+ n = 256;
+
+ n = uv__getrandom((char *) buf + pos, n, 0);
+ } while (n == -1 && errno == EINTR);
+
+ if (n == -1)
+ return UV__ERR(errno);
+
+ if (n == 0)
+ return UV_EIO;
+ }
+
+ return 0;
+}
diff --git a/src/unix/random-sysctl.c b/src/unix/random-sysctl.c
new file mode 100644
index 00000000..7af2e320
--- /dev/null
+++ b/src/unix/random-sysctl.c
@@ -0,0 +1,91 @@
+/* Copyright libuv contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <syscall.h>
+#include <unistd.h>
+
+
+struct uv__sysctl_args {
+ int* name;
+ int nlen;
+ void* oldval;
+ size_t* oldlenp;
+ void* newval;
+ size_t newlen;
+ unsigned long unused[4];
+};
+
+
+/* TODO(bnoordhuis) Use {CTL_KERN, KERN_ARND} on FreeBSD (and NetBSD?) */
+int uv__random_sysctl(void* buf, size_t buflen) {
+ static int name[] = {1 /*CTL_KERN*/, 40 /*KERN_RANDOM*/, 6 /*RANDOM_UUID*/};
+ struct uv__sysctl_args args;
+ char uuid[16];
+ char* p;
+ char* pe;
+ size_t n;
+
+ p = buf;
+ pe = p + buflen;
+
+ while (p < pe) {
+ memset(&args, 0, sizeof(args));
+
+ args.name = name;
+ args.nlen = ARRAY_SIZE(name);
+ args.oldval = uuid;
+ args.oldlenp = &n;
+ n = sizeof(uuid);
+
+ /* Emits a deprecation warning with some kernels but that seems like
+ * an okay trade-off for the fallback of the fallback: this function is
+ * only called when neither getrandom(2) nor /dev/urandom are available.
+ * Fails with ENOSYS on kernels configured without CONFIG_SYSCTL_SYSCALL.
+ */
+ if (syscall(SYS__sysctl, &args) == -1)
+ return UV__ERR(errno);
+
+ if (n != sizeof(uuid))
+ return UV_EIO; /* Can't happen. */
+
+ /* uuid[] is now a type 4 UUID. Bytes 6 and 8 (counting from zero) contain
+ * 4 and 5 bits of entropy, respectively. For ease of use, we skip those
+ * and only use 14 of the 16 bytes.
+ */
+ uuid[6] = uuid[14];
+ uuid[8] = uuid[15];
+
+ n = pe - p;
+ if (n > 14)
+ n = 14;
+
+ memcpy(p, uuid, n);
+ p += n;
+ }
+
+ return 0;
+}