diff options
Diffstat (limited to 'lib/nettle/sysrng-linux.c')
-rw-r--r-- | lib/nettle/sysrng-linux.c | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/lib/nettle/sysrng-linux.c b/lib/nettle/sysrng-linux.c new file mode 100644 index 0000000..6b3971c --- /dev/null +++ b/lib/nettle/sysrng-linux.c @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2010-2016 Free Software Foundation, Inc. + * Copyright (C) 2015-2016 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +/* The Linux style system random generator: That is, + * getrandom() -> /dev/urandom, where "->" indicates fallback. + */ + +#ifndef RND_NO_INCLUDES +# include "gnutls_int.h" +# include "errors.h" +# include <num.h> +# include <errno.h> +# include <rnd-common.h> +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +/* gnulib wants to claim strerror even if it cannot provide it. WTF */ +#undef strerror + +#include <time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <fcntl.h> + +get_entropy_func _rnd_get_system_entropy = NULL; + +#if defined(__linux__) +# ifdef HAVE_GETRANDOM +# include <sys/random.h> +# else +# include <sys/syscall.h> +# undef getrandom +# if defined(SYS_getrandom) +# define getrandom(dst,s,flags) syscall(SYS_getrandom, (void*)dst, (size_t)s, (unsigned int)flags) +# else +static ssize_t _getrandom0(void *buf, size_t buflen, unsigned int flags) +{ + errno = ENOSYS; + return -1; +} +# define getrandom(dst,s,flags) _getrandom0(dst,s,flags) +# endif +# endif + + +static unsigned have_getrandom(void) +{ + char c; + int ret; + ret = getrandom(&c, 1, 1/*GRND_NONBLOCK*/); + if (ret == 1 || (ret == -1 && errno == EAGAIN)) + return 1; + return 0; +} + +/* returns exactly the amount of bytes requested */ +static int force_getrandom(void *buf, size_t buflen, unsigned int flags) +{ + int left = buflen; + int ret; + uint8_t *p = buf; + + while (left > 0) { + ret = getrandom(p, left, flags); + if (ret == -1) { + if (errno != EINTR) + return ret; + } + + if (ret > 0) { + left -= ret; + p += ret; + } + } + + return buflen; +} + +static int _rnd_get_system_entropy_getrandom(void* _rnd, size_t size) +{ + int ret; + ret = force_getrandom(_rnd, size, 0); + if (ret == -1) { + int e = errno; + gnutls_assert(); + _gnutls_debug_log + ("Failed to use getrandom: %s\n", + strerror(e)); + return GNUTLS_E_RANDOM_DEVICE_ERROR; + } + + return 0; +} +#else /* not linux */ +# define have_getrandom() 0 +#endif + +static int _rnd_get_system_entropy_urandom(void* _rnd, size_t size) +{ + uint8_t* rnd = _rnd; + uint32_t done; + int urandom_fd; + + urandom_fd = open("/dev/urandom", O_RDONLY); + if (urandom_fd < 0) { + _gnutls_debug_log("Cannot open /dev/urandom!\n"); + return GNUTLS_E_RANDOM_DEVICE_ERROR; + } + + for (done = 0; done < size;) { + int res; + do { + res = read(urandom_fd, rnd + done, size - done); + } while (res < 0 && errno == EINTR); + + if (res <= 0) { + int e = errno; + if (res < 0) { + _gnutls_debug_log + ("Failed to read /dev/urandom: %s\n", + strerror(e)); + } else { + _gnutls_debug_log + ("Failed to read /dev/urandom: end of file\n"); + } + + close(urandom_fd); + return GNUTLS_E_RANDOM_DEVICE_ERROR; + } + + done += res; + } + + close(urandom_fd); + return 0; +} + +int _rnd_system_entropy_init(void) +{ + int urandom_fd; + +#if defined(__linux__) + /* Enable getrandom() usage if available */ + if (have_getrandom()) { + _rnd_get_system_entropy = _rnd_get_system_entropy_getrandom; + _gnutls_debug_log("getrandom random generator was selected\n"); + return 0; + } else { + _gnutls_debug_log("getrandom is not available\n"); + } +#endif + + /* Fallback: /dev/urandom */ + + /* Check that we can open it */ + urandom_fd = open("/dev/urandom", O_RDONLY); + if (urandom_fd < 0) { + _gnutls_debug_log("Cannot open /dev/urandom during initialization!\n"); + return gnutls_assert_val(GNUTLS_E_RANDOM_DEVICE_ERROR); + } + close(urandom_fd); + + _rnd_get_system_entropy = _rnd_get_system_entropy_urandom; + _gnutls_debug_log("/dev/urandom random generator was selected\n"); + + return 0; +} + +void _rnd_system_entropy_deinit(void) +{ + /* A no-op now when we open and close /dev/urandom every time */ + return; +} + |