|
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 |
}
|