Blame lib/random.c

Packit Service a9384c
/*
Packit Service a9384c
 * cryptsetup kernel RNG access functions
Packit Service a9384c
 *
Packit Service a9384c
 * Copyright (C) 2010-2020 Red Hat, Inc. All rights reserved.
Packit Service a9384c
 *
Packit Service a9384c
 * This program is free software; you can redistribute it and/or
Packit Service a9384c
 * modify it under the terms of the GNU General Public License
Packit Service a9384c
 * as published by the Free Software Foundation; either version 2
Packit Service a9384c
 * of the License, or (at your option) any later version.
Packit Service a9384c
 *
Packit Service a9384c
 * This program is distributed in the hope that it will be useful,
Packit Service a9384c
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service a9384c
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service a9384c
 * GNU General Public License for more details.
Packit Service a9384c
 *
Packit Service a9384c
 * You should have received a copy of the GNU General Public License
Packit Service a9384c
 * along with this program; if not, write to the Free Software
Packit Service a9384c
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Packit Service a9384c
 */
Packit Service a9384c
Packit Service a9384c
#include <stdlib.h>
Packit Service a9384c
#include <string.h>
Packit Service a9384c
#include <errno.h>
Packit Service a9384c
#include <assert.h>
Packit Service a9384c
#include <sys/select.h>
Packit Service a9384c
Packit Service a9384c
#include "libcryptsetup.h"
Packit Service a9384c
#include "internal.h"
Packit Service a9384c
Packit Service a9384c
static int random_initialised = 0;
Packit Service a9384c
Packit Service a9384c
#define URANDOM_DEVICE	"/dev/urandom"
Packit Service a9384c
static int urandom_fd = -1;
Packit Service a9384c
Packit Service a9384c
#define RANDOM_DEVICE	"/dev/random"
Packit Service a9384c
static int random_fd = -1;
Packit Service a9384c
Packit Service a9384c
/* Read random chunk - gathered data usually appears with this granularity */
Packit Service a9384c
#define RANDOM_DEVICE_CHUNK	8
Packit Service a9384c
Packit Service a9384c
/* Timeout to print warning if no random data (entropy) */
Packit Service a9384c
#define RANDOM_DEVICE_TIMEOUT	5
Packit Service a9384c
Packit Service a9384c
/* URANDOM_DEVICE access */
Packit Service a9384c
static int _get_urandom(struct crypt_device *ctx __attribute__((unused)),
Packit Service a9384c
			char *buf, size_t len)
Packit Service a9384c
{
Packit Service a9384c
	int r;
Packit Service a9384c
	size_t old_len = len;
Packit Service a9384c
	char *old_buf = buf;
Packit Service a9384c
Packit Service a9384c
	assert(urandom_fd != -1);
Packit Service a9384c
Packit Service a9384c
	while(len) {
Packit Service a9384c
		r = read(urandom_fd, buf, len);
Packit Service a9384c
		if (r == -1 && errno != EINTR)
Packit Service a9384c
			return -EINVAL;
Packit Service a9384c
		if (r > 0) {
Packit Service a9384c
			len -= r;
Packit Service a9384c
			buf += r;
Packit Service a9384c
		}
Packit Service a9384c
	}
Packit Service a9384c
Packit Service a9384c
	assert(len == 0);
Packit Service a9384c
	assert((size_t)(buf - old_buf) == old_len);
Packit Service a9384c
Packit Service a9384c
	return 0;
Packit Service a9384c
}
Packit Service a9384c
Packit Service a9384c
static void _get_random_progress(struct crypt_device *ctx, int warn,
Packit Service a9384c
				 size_t expected_len, size_t read_len)
Packit Service a9384c
{
Packit Service a9384c
	if (warn)
Packit Service a9384c
		log_std(ctx,
Packit Service a9384c
			_("System is out of entropy while generating volume key.\n"
Packit Service a9384c
			  "Please move mouse or type some text in another window "
Packit Service a9384c
			  "to gather some random events.\n"));
Packit Service a9384c
Packit Service a9384c
	log_std(ctx, _("Generating key (%d%% done).\n"),
Packit Service a9384c
		(int)((expected_len - read_len) * 100 / expected_len));
Packit Service a9384c
}
Packit Service a9384c
Packit Service a9384c
/* RANDOM_DEVICE access */
Packit Service a9384c
static int _get_random(struct crypt_device *ctx, char *buf, size_t len)
Packit Service a9384c
{
Packit Service a9384c
	int r, warn_once = 1;
Packit Service a9384c
	size_t n, old_len = len;
Packit Service a9384c
	char *old_buf = buf;
Packit Service a9384c
	fd_set fds;
Packit Service a9384c
	struct timeval tv;
Packit Service a9384c
Packit Service a9384c
	assert(random_fd != -1);
Packit Service a9384c
Packit Service a9384c
	while (len) {
Packit Service a9384c
		FD_ZERO(&fds);
Packit Service a9384c
		FD_SET(random_fd, &fds);
Packit Service a9384c
Packit Service a9384c
		tv.tv_sec = RANDOM_DEVICE_TIMEOUT;
Packit Service a9384c
		tv.tv_usec = 0;
Packit Service a9384c
Packit Service a9384c
		r = select(random_fd + 1, &fds, NULL, NULL, &tv;;
Packit Service a9384c
		if(r == -1)
Packit Service a9384c
			return -EINVAL;
Packit Service a9384c
Packit Service a9384c
		if(!r) {
Packit Service a9384c
			_get_random_progress(ctx, warn_once, old_len, len);
Packit Service a9384c
			warn_once = 0;
Packit Service a9384c
			continue;
Packit Service a9384c
		}
Packit Service a9384c
Packit Service a9384c
		do {
Packit Service a9384c
			n = RANDOM_DEVICE_CHUNK;
Packit Service a9384c
			if (len < RANDOM_DEVICE_CHUNK)
Packit Service a9384c
				n = len;
Packit Service a9384c
Packit Service a9384c
			r = read(random_fd, buf, n);
Packit Service a9384c
Packit Service a9384c
			if (r == -1 && errno == EINTR) {
Packit Service a9384c
				r = 0;
Packit Service a9384c
				continue;
Packit Service a9384c
			}
Packit Service a9384c
Packit Service a9384c
			/* bogus read? */
Packit Service a9384c
			if(r > (int)n)
Packit Service a9384c
				return -EINVAL;
Packit Service a9384c
Packit Service a9384c
			/* random device is opened with O_NONBLOCK, EAGAIN is expected */
Packit Service a9384c
			if (r == -1 && (errno != EAGAIN && errno != EWOULDBLOCK))
Packit Service a9384c
				return -EINVAL;
Packit Service a9384c
Packit Service a9384c
			if (r > 0) {
Packit Service a9384c
				len -= r;
Packit Service a9384c
				buf += r;
Packit Service a9384c
			}
Packit Service a9384c
		} while (len && r > 0);
Packit Service a9384c
	}
Packit Service a9384c
Packit Service a9384c
	assert(len == 0);
Packit Service a9384c
	assert((size_t)(buf - old_buf) == old_len);
Packit Service a9384c
Packit Service a9384c
	if (!warn_once)
Packit Service a9384c
		_get_random_progress(ctx, 0, old_len, len);
Packit Service a9384c
Packit Service a9384c
	return 0;
Packit Service a9384c
}
Packit Service a9384c
/* Initialisation of both RNG file descriptors is mandatory */
Packit Service a9384c
int crypt_random_init(struct crypt_device *ctx)
Packit Service a9384c
{
Packit Service a9384c
	if (random_initialised)
Packit Service a9384c
		return 0;
Packit Service a9384c
Packit Service a9384c
	/* Used for CRYPT_RND_NORMAL */
Packit Service a9384c
	if(urandom_fd == -1)
Packit Service a9384c
		urandom_fd = open(URANDOM_DEVICE, O_RDONLY | O_CLOEXEC);
Packit Service a9384c
	if(urandom_fd == -1)
Packit Service a9384c
		goto fail;
Packit Service a9384c
Packit Service a9384c
	/* Used for CRYPT_RND_KEY */
Packit Service a9384c
	if(random_fd == -1)
Packit Service a9384c
		random_fd = open(RANDOM_DEVICE, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
Packit Service a9384c
	if(random_fd == -1)
Packit Service a9384c
		goto fail;
Packit Service a9384c
Packit Service a9384c
	if (crypt_fips_mode())
Packit Service a9384c
		log_verbose(ctx, _("Running in FIPS mode."));
Packit Service a9384c
Packit Service a9384c
	random_initialised = 1;
Packit Service a9384c
	return 0;
Packit Service a9384c
fail:
Packit Service a9384c
	crypt_random_exit();
Packit Service a9384c
	log_err(ctx, _("Fatal error during RNG initialisation."));
Packit Service a9384c
	return -ENOSYS;
Packit Service a9384c
}
Packit Service a9384c
Packit Service a9384c
int crypt_random_get(struct crypt_device *ctx, char *buf, size_t len, int quality)
Packit Service a9384c
{
Packit Service a9384c
	int status, rng_type;
Packit Service a9384c
Packit Service a9384c
	switch(quality) {
Packit Service a9384c
	case CRYPT_RND_NORMAL:
Packit Service a9384c
		status = _get_urandom(ctx, buf, len);
Packit Service a9384c
		break;
Packit Service a9384c
	case CRYPT_RND_SALT:
Packit Service a9384c
		if (crypt_fips_mode())
Packit Service a9384c
			status = crypt_backend_rng(buf, len, quality, 1);
Packit Service a9384c
		else
Packit Service a9384c
			status = _get_urandom(ctx, buf, len);
Packit Service a9384c
		break;
Packit Service a9384c
	case CRYPT_RND_KEY:
Packit Service a9384c
		if (crypt_fips_mode()) {
Packit Service a9384c
			status = crypt_backend_rng(buf, len, quality, 1);
Packit Service a9384c
			break;
Packit Service a9384c
		}
Packit Service a9384c
		rng_type = ctx ? crypt_get_rng_type(ctx) :
Packit Service a9384c
				 crypt_random_default_key_rng();
Packit Service a9384c
		switch (rng_type) {
Packit Service a9384c
		case CRYPT_RNG_URANDOM:
Packit Service a9384c
			status = _get_urandom(ctx, buf, len);
Packit Service a9384c
			break;
Packit Service a9384c
		case CRYPT_RNG_RANDOM:
Packit Service a9384c
			status = _get_random(ctx, buf, len);
Packit Service a9384c
			break;
Packit Service a9384c
		default:
Packit Service a9384c
			abort();
Packit Service a9384c
		}
Packit Service a9384c
		break;
Packit Service a9384c
	default:
Packit Service a9384c
		log_err(ctx, _("Unknown RNG quality requested."));
Packit Service a9384c
		return -EINVAL;
Packit Service a9384c
	}
Packit Service a9384c
Packit Service a9384c
	if (status)
Packit Service a9384c
		log_err(ctx, _("Error reading from RNG."));
Packit Service a9384c
Packit Service a9384c
	return status;
Packit Service a9384c
}
Packit Service a9384c
Packit Service a9384c
void crypt_random_exit(void)
Packit Service a9384c
{
Packit Service a9384c
	random_initialised = 0;
Packit Service a9384c
Packit Service a9384c
	if(random_fd != -1) {
Packit Service a9384c
		(void)close(random_fd);
Packit Service a9384c
		random_fd = -1;
Packit Service a9384c
	}
Packit Service a9384c
Packit Service a9384c
	if(urandom_fd != -1) {
Packit Service a9384c
		(void)close(urandom_fd);
Packit Service a9384c
		urandom_fd = -1;
Packit Service a9384c
	}
Packit Service a9384c
}
Packit Service a9384c
Packit Service a9384c
int crypt_random_default_key_rng(void)
Packit Service a9384c
{
Packit Service a9384c
	/* coverity[pointless_string_compare] */
Packit Service a9384c
	if (!strcmp(DEFAULT_RNG, RANDOM_DEVICE))
Packit Service a9384c
		return CRYPT_RNG_RANDOM;
Packit Service a9384c
Packit Service a9384c
	/* coverity[pointless_string_compare] */
Packit Service a9384c
	if (!strcmp(DEFAULT_RNG, URANDOM_DEVICE))
Packit Service a9384c
		return CRYPT_RNG_URANDOM;
Packit Service a9384c
Packit Service a9384c
	/* RNG misconfiguration is fatal */
Packit Service a9384c
	abort();
Packit Service a9384c
}