mirror of
https://codeberg.org/libreboot/lbmk.git
synced 2026-07-11 22:12:40 +02:00
d2abde5303
where possible, try not to clobber sys errno. override it only when relatively safe. also: when a syscall succeeds, it may set errno. this is rare, but permitted (nothing specified against it in specs, and the specs say that errno is undefined on success). i'm not libc, but i'm wrapping around it, so i need to be careful in how i handle the errno value. also: i removed the requirement for directories to be executable, in mkhtemp.c, because this isn't required and will only break certain setups. in world_writeable and sticky, i made the checks stricter: the faccessat check was being skipped on some paths, so i've closed that loophole now. i also generally cleaned up some code, as part of the errno handling refactoring, where it made sense to do so, plus a few other bits of code cleanup. Signed-off-by: Leah Rowe <leah@libreboot.org>
195 lines
4.4 KiB
C
195 lines
4.4 KiB
C
/* SPDX-License-Identifier: MIT
|
|
* Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
|
|
*
|
|
* Random number generation
|
|
*/
|
|
|
|
#ifndef RAND_H
|
|
#define RAND_H
|
|
|
|
#ifdef __OpenBSD__
|
|
#include <sys/param.h>
|
|
#endif
|
|
#include <sys/types.h>
|
|
|
|
#ifndef USE_URANDOM
|
|
#define USE_URANDOM 0
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#if defined(USE_URANDOM) && \
|
|
((USE_URANDOM) > 0)
|
|
#include <fcntl.h> /* if not arc4random: /dev/urandom */
|
|
#elif defined(__linux__)
|
|
#include <sys/random.h>
|
|
#include <sys/syscall.h>
|
|
#endif
|
|
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
|
|
#include "../include/common.h"
|
|
|
|
/* Regarding Linux getrandom/urandom:
|
|
*
|
|
* For maximum security guarantee, we *only*
|
|
* use getrandom via syscall, or /dev/urandom;
|
|
* use of urandom is ill advised. This is why
|
|
* we use the syscall, in case the libc version
|
|
* of getrandom() might defer to /dev/urandom
|
|
*
|
|
* We *abort* on error, for both /dev/urandom
|
|
* and getrandom(), because the BSD arc4random
|
|
* never returns with error; therefore, for the
|
|
* most parity in terms of behaviour, we abort,
|
|
* because otherwise the function would have two
|
|
* return modes: always successful (BSD), or only
|
|
* sometimes (Linux). The BSD arc4random could
|
|
* theoretically abort; it is extremely unlikely
|
|
* there, and just so on Linux, hence this design.
|
|
*
|
|
* This is important, because cryptographic code
|
|
* for example must not rely on weak randomness.
|
|
* We must therefore treat broken randomness as
|
|
* though the world is broken, and burn accordingly.
|
|
*
|
|
* Similarly, any invalid input (NULL, zero bytes
|
|
* requested) are treated as fatal errors; again,
|
|
* cryptographic code must be reliable. If your
|
|
* code erroneously requested zero bytes, you might
|
|
* then end up with a non-randomised buffer, where
|
|
* you likely intended otherwise.
|
|
*
|
|
* In other words: call rset() correctly, or your
|
|
* program dies, and rset will behave correctly,
|
|
* or your program dies.
|
|
*/
|
|
|
|
/* random string generator, with
|
|
* rejection sampling. NOTE: only
|
|
* uses ASCII-safe characters, for
|
|
* printing on a unix terminal
|
|
*
|
|
* you still shouldn't use this for
|
|
* password generation; open diceware
|
|
* passphrases are better for that
|
|
*
|
|
* NOTE: the generated strings must
|
|
* ALSO be safe for file/directory names
|
|
* on unix-like os e.g. linux/bsd
|
|
*/
|
|
char *
|
|
rchars(size_t n) /* emulates spkmodem-decode */
|
|
{
|
|
static char ch[] =
|
|
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
|
|
char *s = NULL;
|
|
size_t i;
|
|
|
|
smalloc(&s, n + 1);
|
|
for (i = 0; i < n; i++)
|
|
s[i] = ch[rsize(sizeof(ch) - 1)];
|
|
|
|
*(s + n) = '\0';
|
|
return s;
|
|
}
|
|
|
|
size_t
|
|
rsize(size_t n)
|
|
{
|
|
size_t rval = SIZE_MAX;
|
|
if (!n)
|
|
err_exit(EFAULT, "rsize: division by zero");
|
|
|
|
/* rejection sampling (clamp rand to eliminate modulo bias) */
|
|
for (; rval >= SIZE_MAX - (SIZE_MAX % n); rset(&rval, sizeof(rval)));
|
|
|
|
return rval % n;
|
|
}
|
|
|
|
void *
|
|
rmalloc(size_t n)
|
|
{
|
|
void *buf = NULL;
|
|
rset(vmalloc(&buf, n), n);
|
|
return buf; /* basically malloc() but with rand */
|
|
}
|
|
|
|
void
|
|
rset(void *buf, size_t n)
|
|
{
|
|
int saved_errno = errno;
|
|
errno = 0;
|
|
|
|
if (if_err(buf == NULL, EFAULT))
|
|
goto err;
|
|
|
|
if (n == 0)
|
|
err_exit(EPERM, "rset: zero-byte request");
|
|
|
|
#if (defined(__OpenBSD__) || defined(__FreeBSD__) || \
|
|
defined(__NetBSD__) || defined(__APPLE__) || \
|
|
defined(__DragonFly__)) && !(defined(USE_URANDOM) && \
|
|
((USE_URANDOM) > 0))
|
|
|
|
arc4random_buf(buf, n);
|
|
#else
|
|
size_t off = 0;
|
|
|
|
retry_rand:
|
|
|
|
#if defined(USE_URANDOM) && \
|
|
((USE_URANDOM) > 0)
|
|
ssize_t rc;
|
|
int fd = -1;
|
|
|
|
open_file_on_eintr("/dev/urandom", &fd, O_RDONLY, 0400, NULL);
|
|
|
|
while (rw_retry(saved_errno,
|
|
rc = read_on_eintr(fd,
|
|
(unsigned char *)buf + off, n - off, 0)));
|
|
#elif defined(__linux__)
|
|
long rc;
|
|
while (sys_retry(saved_errno,
|
|
rc = syscall(SYS_getrandom,
|
|
(unsigned char *)buf + off, n - off, 0)));
|
|
#else
|
|
#error Unsupported operating system (possibly unsecure randomisation)
|
|
#endif
|
|
|
|
if (rc < 0)
|
|
goto err; /* syscall fehler */
|
|
|
|
if (rc == 0)
|
|
goto err; /* prevent infinite loop on fatal err */
|
|
|
|
if ((off += (size_t)rc) < n)
|
|
goto retry_rand;
|
|
|
|
#if defined(USE_URANDOM) && \
|
|
((USE_URANDOM) > 0)
|
|
close_on_eintr(&fd);
|
|
#endif
|
|
|
|
#endif
|
|
reset_caller_errno(0);
|
|
return;
|
|
err:
|
|
#if defined(USE_URANDOM) && \
|
|
((USE_URANDOM) > 0)
|
|
close_on_eintr(&fd);
|
|
#endif
|
|
(void) with_fallback_errno(ECANCELED);
|
|
err_exit(errno, "Randomisierungsfehler");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
#endif
|