Blame shared/nm-glib-aux/nm-random-utils.c

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
}