|
Packit Service |
b23acc |
// SPDX-License-Identifier: LGPL-2.1+
|
|
Packit Service |
b23acc |
/*
|
|
Packit Service |
b23acc |
* Copyright (C) 2017 Red Hat, Inc.
|
|
Packit Service |
b23acc |
*/
|
|
Packit Service |
b23acc |
|
|
Packit Service |
b23acc |
#include "nm-default.h"
|
|
Packit Service |
b23acc |
|
|
Packit Service |
b23acc |
#include "nm-random-utils.h"
|
|
Packit Service |
b23acc |
|
|
Packit Service |
b23acc |
#include <fcntl.h>
|
|
Packit Service |
b23acc |
|
|
Packit Service |
b23acc |
#if USE_SYS_RANDOM_H
|
|
Packit Service |
b23acc |
#include <sys/random.h>
|
|
Packit Service |
b23acc |
#else
|
|
Packit Service |
b23acc |
#include <linux/random.h>
|
|
Packit Service |
b23acc |
#endif
|
|
Packit Service |
b23acc |
|
|
Packit Service |
b23acc |
#include "nm-shared-utils.h"
|
|
Packit Service |
b23acc |
|
|
Packit Service |
b23acc |
/*****************************************************************************/
|
|
Packit Service |
b23acc |
|
|
Packit Service |
b23acc |
/**
|
|
Packit Service |
b23acc |
* nm_utils_random_bytes:
|
|
Packit Service |
b23acc |
* @p: the buffer to fill
|
|
Packit Service |
b23acc |
* @n: the number of bytes to write to @p.
|
|
Packit Service |
b23acc |
*
|
|
Packit Service |
b23acc |
* Uses getrandom() or reads /dev/urandom to fill the buffer
|
|
Packit Service |
b23acc |
* with random data. If all fails, as last fallback it uses
|
|
Packit Service |
b23acc |
* GRand to fill the buffer with pseudo random numbers.
|
|
Packit Service |
b23acc |
* The function always succeeds in writing some random numbers
|
|
Packit Service |
b23acc |
* to the buffer. The return value of FALSE indicates that the
|
|
Packit Service |
b23acc |
* obtained bytes are probably not of good randomness.
|
|
Packit Service |
b23acc |
*
|
|
Packit Service |
b23acc |
* Returns: whether the written bytes are good. If you
|
|
Packit Service |
b23acc |
* don't require good randomness, you can ignore the return
|
|
Packit Service |
b23acc |
* value.
|
|
Packit Service |
b23acc |
*
|
|
Packit Service |
b23acc |
* Note that if calling getrandom() fails because there is not enough
|
|
Packit Service |
b23acc |
* entropy (at early boot), the function will read /dev/urandom.
|
|
Packit Service |
b23acc |
* Which of course, still has low entropy, and cause kernel to log
|
|
Packit Service |
b23acc |
* a warning.
|
|
Packit Service |
b23acc |
*/
|
|
Packit Service |
b23acc |
gboolean
|
|
Packit Service |
b23acc |
nm_utils_random_bytes (void *p, size_t n)
|
|
Packit Service |
b23acc |
{
|
|
Packit Service |
b23acc |
int fd;
|
|
Packit Service |
b23acc |
int r;
|
|
Packit Service |
b23acc |
gboolean has_high_quality = TRUE;
|
|
Packit Service |
b23acc |
gboolean urandom_success;
|
|
Packit Service |
b23acc |
guint8 *buf = p;
|
|
Packit Service |
b23acc |
gboolean avoid_urandom = FALSE;
|
|
Packit Service |
b23acc |
|
|
Packit Service |
b23acc |
g_return_val_if_fail (p, FALSE);
|
|
Packit Service |
b23acc |
g_return_val_if_fail (n > 0, FALSE);
|
|
Packit Service |
b23acc |
|
|
Packit Service |
b23acc |
#if HAVE_GETRANDOM
|
|
Packit Service |
b23acc |
{
|
|
Packit Service |
b23acc |
static gboolean have_syscall = TRUE;
|
|
Packit Service |
b23acc |
|
|
Packit Service |
b23acc |
if (have_syscall) {
|
|
Packit Service |
b23acc |
r = getrandom (buf, n, GRND_NONBLOCK);
|
|
Packit Service |
b23acc |
if (r > 0) {
|
|
Packit Service |
b23acc |
if ((size_t) r == n)
|
|
Packit Service |
b23acc |
return TRUE;
|
|
Packit Service |
b23acc |
|
|
Packit Service |
b23acc |
/* no or partial read. There is not enough entropy.
|
|
Packit Service |
b23acc |
* Fill the rest reading from urandom, and remember that
|
|
Packit Service |
b23acc |
* some bits are not high quality. */
|
|
Packit Service |
b23acc |
nm_assert (r < n);
|
|
Packit Service |
b23acc |
buf += r;
|
|
Packit Service |
b23acc |
n -= r;
|
|
Packit Service |
b23acc |
has_high_quality = FALSE;
|
|
Packit Service |
b23acc |
|
|
Packit Service |
b23acc |
/* At this point, we don't want to read /dev/urandom, because
|
|
Packit Service |
b23acc |
* the entropy pool is low (early boot?), and asking for more
|
|
Packit Service |
b23acc |
* entropy causes kernel messages to be logged.
|
|
Packit Service |
b23acc |
*
|
|
Packit Service |
b23acc |
* We use our fallback via GRand. Note that g_rand_new() also
|
|
Packit Service |
b23acc |
* tries to seed itself with data from /dev/urandom, but since
|
|
Packit Service |
b23acc |
* we reuse the instance, it shouldn't matter. */
|
|
Packit Service |
b23acc |
avoid_urandom = TRUE;
|
|
Packit Service |
b23acc |
} else {
|
|
Packit Service |
b23acc |
if (errno == ENOSYS) {
|
|
Packit Service |
b23acc |
/* no support for getrandom(). We don't know whether
|
|
Packit Service |
b23acc |
* we urandom will give us good quality. Assume yes. */
|
|
Packit Service |
b23acc |
have_syscall = FALSE;
|
|
Packit Service |
b23acc |
} else {
|
|
Packit Service |
b23acc |
/* unknown error. We'll read urandom below, but we don't have
|
|
Packit Service |
b23acc |
* high-quality randomness. */
|
|
Packit Service |
b23acc |
has_high_quality = FALSE;
|
|
Packit Service |
b23acc |
}
|
|
Packit Service |
b23acc |
}
|
|
Packit Service |
b23acc |
}
|
|
Packit Service |
b23acc |
}
|
|
Packit Service |
b23acc |
#endif
|
|
Packit Service |
b23acc |
|
|
Packit Service |
b23acc |
urandom_success = FALSE;
|
|
Packit Service |
b23acc |
if (!avoid_urandom) {
|
|
Packit Service |
b23acc |
fd_open:
|
|
Packit Service |
b23acc |
fd = open ("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOCTTY);
|
|
Packit Service |
b23acc |
if (fd < 0) {
|
|
Packit Service |
b23acc |
r = errno;
|
|
Packit Service |
b23acc |
if (r == EINTR)
|
|
Packit Service |
b23acc |
goto fd_open;
|
|
Packit Service |
b23acc |
} else {
|
|
Packit Service |
b23acc |
r = nm_utils_fd_read_loop_exact (fd, buf, n, TRUE);
|
|
Packit Service |
b23acc |
nm_close (fd);
|
|
Packit Service |
b23acc |
if (r >= 0)
|
|
Packit Service |
b23acc |
urandom_success = TRUE;
|
|
Packit Service |
b23acc |
}
|
|
Packit Service |
b23acc |
}
|
|
Packit Service |
b23acc |
|
|
Packit Service |
b23acc |
if (!urandom_success) {
|
|
Packit Service |
b23acc |
static _nm_thread_local GRand *rand = NULL;
|
|
Packit Service |
b23acc |
gsize i;
|
|
Packit Service |
b23acc |
int j;
|
|
Packit Service |
b23acc |
|
|
Packit Service |
b23acc |
/* we failed to fill the bytes reading from urandom.
|
|
Packit Service |
b23acc |
* Fill the bits using GRand pseudo random numbers.
|
|
Packit Service |
b23acc |
*
|
|
Packit Service |
b23acc |
* We don't have good quality.
|
|
Packit Service |
b23acc |
*/
|
|
Packit Service |
b23acc |
has_high_quality = FALSE;
|
|
Packit Service |
b23acc |
|
|
Packit Service |
b23acc |
if (G_UNLIKELY (!rand))
|
|
Packit Service |
b23acc |
rand = g_rand_new ();
|
|
Packit Service |
b23acc |
|
|
Packit Service |
b23acc |
nm_assert (n > 0);
|
|
Packit Service |
b23acc |
i = 0;
|
|
Packit Service |
b23acc |
for (;;) {
|
|
Packit Service |
b23acc |
const union {
|
|
Packit Service |
b23acc |
guint32 v32;
|
|
Packit Service |
b23acc |
guint8 v8[4];
|
|
Packit Service |
b23acc |
} v = {
|
|
Packit Service |
b23acc |
.v32 = g_rand_int (rand),
|
|
Packit Service |
b23acc |
};
|
|
Packit Service |
b23acc |
|
|
Packit Service |
b23acc |
for (j = 0; j < 4; ) {
|
|
Packit Service |
b23acc |
buf[i++] = v.v8[j++];
|
|
Packit Service |
b23acc |
if (i >= n)
|
|
Packit Service |
b23acc |
goto done;
|
|
Packit Service |
b23acc |
}
|
|
Packit Service |
b23acc |
}
|
|
Packit Service |
b23acc |
done:
|
|
Packit Service |
b23acc |
;
|
|
Packit Service |
b23acc |
}
|
|
Packit Service |
b23acc |
|
|
Packit Service |
b23acc |
return has_high_quality;
|
|
Packit Service |
b23acc |
}
|