NetworkManager/shared/nm-glib-aux/nm-random-utils.c
Thomas Haller 88071abb43
all: unify comment style for SPDX-License-Identifier tag
Our coding style recommends C style comments (/* */) instead of C++
(//). Also, systemd (which we partly fork) uses C style comments for
the SPDX-License-Identifier.

Unify the style.

  $ sed -i '1 s#// SPDX-License-Identifier: \([^ ]\+\)$#/* SPDX-License-Identifier: \1 */#' -- $(git ls-files -- '*.[hc]' '*.[hc]pp')
2020-09-29 16:50:53 +02:00

149 lines
4.2 KiB
C

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