|
Packit |
aea12f |
/*
|
|
Packit |
aea12f |
* Copyright (C) 2010-2016 Free Software Foundation, Inc.
|
|
Packit |
aea12f |
* Copyright (C) 2015-2016 Red Hat, Inc.
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* Author: Nikos Mavrogiannopoulos
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* This file is part of GNUTLS.
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* The GNUTLS library is free software; you can redistribute it and/or
|
|
Packit |
aea12f |
* modify it under the terms of the GNU Lesser General Public License
|
|
Packit |
aea12f |
* as published by the Free Software Foundation; either version 2.1 of
|
|
Packit |
aea12f |
* the License, or (at your option) any later version.
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* This library is distributed in the hope that it will be useful, but
|
|
Packit |
aea12f |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
aea12f |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
aea12f |
* Lesser General Public License for more details.
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* You should have received a copy of the GNU Lesser General Public License
|
|
Packit |
aea12f |
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
*/
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/* The Linux style system random generator: That is,
|
|
Packit |
aea12f |
* getrandom() -> /dev/urandom, where "->" indicates fallback.
|
|
Packit |
aea12f |
*/
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
#ifndef RND_NO_INCLUDES
|
|
Packit |
aea12f |
# include "gnutls_int.h"
|
|
Packit |
aea12f |
# include "errors.h"
|
|
Packit |
aea12f |
# include <num.h>
|
|
Packit |
aea12f |
# include <errno.h>
|
|
Packit |
aea12f |
# include <rnd-common.h>
|
|
Packit |
aea12f |
#endif
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
#include <sys/types.h>
|
|
Packit |
aea12f |
#include <sys/stat.h>
|
|
Packit |
aea12f |
#include <unistd.h>
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/* gnulib wants to claim strerror even if it cannot provide it. WTF */
|
|
Packit |
aea12f |
#undef strerror
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
#include <time.h>
|
|
Packit |
aea12f |
#include <sys/types.h>
|
|
Packit |
aea12f |
#include <sys/stat.h>
|
|
Packit |
aea12f |
#include <sys/time.h>
|
|
Packit |
aea12f |
#include <fcntl.h>
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
static int _gnutls_urandom_fd = -1;
|
|
Packit |
aea12f |
static ino_t _gnutls_urandom_fd_ino = 0;
|
|
Packit |
aea12f |
static dev_t _gnutls_urandom_fd_rdev = 0;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
get_entropy_func _rnd_get_system_entropy = NULL;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
#if defined(__linux__)
|
|
Packit |
aea12f |
# ifdef HAVE_GETRANDOM
|
|
Packit |
aea12f |
# include <sys/random.h>
|
|
Packit |
aea12f |
# else
|
|
Packit |
aea12f |
# include <sys/syscall.h>
|
|
Packit |
aea12f |
# undef getrandom
|
|
Packit |
aea12f |
# if defined(SYS_getrandom)
|
|
Packit |
aea12f |
# define getrandom(dst,s,flags) syscall(SYS_getrandom, (void*)dst, (size_t)s, (unsigned int)flags)
|
|
Packit |
aea12f |
# else
|
|
Packit |
aea12f |
# define getrandom(dst,s,flags) -1
|
|
Packit |
aea12f |
# endif
|
|
Packit |
aea12f |
# endif
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
static unsigned have_getrandom(void)
|
|
Packit |
aea12f |
{
|
|
Packit |
aea12f |
char c;
|
|
Packit |
aea12f |
int ret;
|
|
Packit |
aea12f |
ret = getrandom(&c, 1, 1/*GRND_NONBLOCK*/);
|
|
Packit |
aea12f |
if (ret == 1 || (ret == -1 && errno == EAGAIN))
|
|
Packit |
aea12f |
return 1;
|
|
Packit |
aea12f |
return 0;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/* returns exactly the amount of bytes requested */
|
|
Packit |
aea12f |
static int force_getrandom(void *buf, size_t buflen, unsigned int flags)
|
|
Packit |
aea12f |
{
|
|
Packit |
aea12f |
int left = buflen;
|
|
Packit |
aea12f |
int ret;
|
|
Packit |
aea12f |
uint8_t *p = buf;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
while (left > 0) {
|
|
Packit |
aea12f |
ret = getrandom(p, left, flags);
|
|
Packit |
aea12f |
if (ret == -1) {
|
|
Packit |
aea12f |
if (errno != EINTR)
|
|
Packit |
aea12f |
return ret;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (ret > 0) {
|
|
Packit |
aea12f |
left -= ret;
|
|
Packit |
aea12f |
p += ret;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
return buflen;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
static int _rnd_get_system_entropy_getrandom(void* _rnd, size_t size)
|
|
Packit |
aea12f |
{
|
|
Packit |
aea12f |
int ret;
|
|
Packit |
aea12f |
ret = force_getrandom(_rnd, size, 0);
|
|
Packit |
aea12f |
if (ret == -1) {
|
|
Packit |
aea12f |
int e = errno;
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
_gnutls_debug_log
|
|
Packit |
aea12f |
("Failed to use getrandom: %s\n",
|
|
Packit |
aea12f |
strerror(e));
|
|
Packit |
aea12f |
return GNUTLS_E_RANDOM_DEVICE_ERROR;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
return 0;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
#else /* not linux */
|
|
Packit |
aea12f |
# define have_getrandom() 0
|
|
Packit |
aea12f |
#endif
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
static int _rnd_get_system_entropy_urandom(void* _rnd, size_t size)
|
|
Packit |
aea12f |
{
|
|
Packit |
aea12f |
uint8_t* rnd = _rnd;
|
|
Packit |
aea12f |
uint32_t done;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
for (done = 0; done < size;) {
|
|
Packit |
aea12f |
int res;
|
|
Packit |
aea12f |
do {
|
|
Packit |
aea12f |
res = read(_gnutls_urandom_fd, rnd + done, size - done);
|
|
Packit |
aea12f |
} while (res < 0 && errno == EINTR);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (res <= 0) {
|
|
Packit |
aea12f |
int e = errno;
|
|
Packit |
aea12f |
if (res < 0) {
|
|
Packit |
aea12f |
_gnutls_debug_log
|
|
Packit |
aea12f |
("Failed to read /dev/urandom: %s\n",
|
|
Packit |
aea12f |
strerror(e));
|
|
Packit |
aea12f |
} else {
|
|
Packit |
aea12f |
_gnutls_debug_log
|
|
Packit |
aea12f |
("Failed to read /dev/urandom: end of file\n");
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
return GNUTLS_E_RANDOM_DEVICE_ERROR;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
done += res;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
return 0;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/* This is called when gnutls_global_init() is called for second time.
|
|
Packit |
aea12f |
* It must check whether any resources are still available.
|
|
Packit |
aea12f |
* The particular problem it solves is to verify that the urandom fd is still
|
|
Packit |
aea12f |
* open (for applications that for some reason closed all fds */
|
|
Packit |
aea12f |
int _rnd_system_entropy_check(void)
|
|
Packit |
aea12f |
{
|
|
Packit |
aea12f |
int ret;
|
|
Packit |
aea12f |
struct stat st;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (_gnutls_urandom_fd == -1) /* not using urandom */
|
|
Packit |
aea12f |
return 0;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = fstat(_gnutls_urandom_fd, &st);
|
|
Packit |
aea12f |
if (ret < 0 || st.st_ino != _gnutls_urandom_fd_ino || st.st_rdev != _gnutls_urandom_fd_rdev) {
|
|
Packit |
aea12f |
return _rnd_system_entropy_init();
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
return 0;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
int _rnd_system_entropy_init(void)
|
|
Packit |
aea12f |
{
|
|
Packit |
aea12f |
int old;
|
|
Packit |
aea12f |
struct stat st;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
#if defined(__linux__)
|
|
Packit |
aea12f |
/* Enable getrandom() usage if available */
|
|
Packit |
aea12f |
if (have_getrandom()) {
|
|
Packit |
aea12f |
_rnd_get_system_entropy = _rnd_get_system_entropy_getrandom;
|
|
Packit |
aea12f |
_gnutls_debug_log("getrandom random generator was detected\n");
|
|
Packit |
aea12f |
return 0;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
#endif
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/* First fallback: /dev/unrandom */
|
|
Packit |
aea12f |
_gnutls_urandom_fd = open("/dev/urandom", O_RDONLY);
|
|
Packit |
aea12f |
if (_gnutls_urandom_fd < 0) {
|
|
Packit |
aea12f |
_gnutls_debug_log("Cannot open urandom!\n");
|
|
Packit |
aea12f |
return gnutls_assert_val(GNUTLS_E_RANDOM_DEVICE_ERROR);
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
old = fcntl(_gnutls_urandom_fd, F_GETFD);
|
|
Packit |
aea12f |
if (old != -1)
|
|
Packit |
aea12f |
fcntl(_gnutls_urandom_fd, F_SETFD, old | FD_CLOEXEC);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (fstat(_gnutls_urandom_fd, &st) >= 0) {
|
|
Packit |
aea12f |
_gnutls_urandom_fd_ino = st.st_ino;
|
|
Packit |
aea12f |
_gnutls_urandom_fd_rdev = st.st_rdev;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
_rnd_get_system_entropy = _rnd_get_system_entropy_urandom;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
return 0;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
void _rnd_system_entropy_deinit(void)
|
|
Packit |
aea12f |
{
|
|
Packit |
aea12f |
if (_gnutls_urandom_fd >= 0) {
|
|
Packit |
aea12f |
close(_gnutls_urandom_fd);
|
|
Packit |
aea12f |
_gnutls_urandom_fd = -1;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|