diff options
author | Ben Noordhuis <info@bnoordhuis.nl> | 2019-06-22 15:18:17 +0200 |
---|---|---|
committer | Saúl Ibarra Corretgé <s@saghul.net> | 2019-09-18 08:09:14 +0200 |
commit | 4ed2a78f0eb1c9d2a0c98ea4a88c6824e5958dc6 (patch) | |
tree | ce6b8b3282f3ac65d6bbeba5cec36463b68f06e5 /src/unix | |
parent | fd1502f5630591bf8ce79502df9b5d76868dfd3b (diff) | |
download | libuv-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.h | 6 | ||||
-rw-r--r-- | src/unix/linux-syscalls.c | 25 | ||||
-rw-r--r-- | src/unix/linux-syscalls.h | 1 | ||||
-rw-r--r-- | src/unix/random-devurandom.c | 93 | ||||
-rw-r--r-- | src/unix/random-getentropy.c | 57 | ||||
-rw-r--r-- | src/unix/random-getrandom.c | 88 | ||||
-rw-r--r-- | src/unix/random-sysctl.c | 91 |
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; +} |