Blame lib/nettle/sysrng-linux.c

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