summaryrefslogtreecommitdiff
path: root/util
diff options
context:
space:
mode:
Diffstat (limited to 'util')
-rw-r--r--util/libreboot-utils/Makefile9
-rw-r--r--util/libreboot-utils/include/common.h33
-rw-r--r--util/libreboot-utils/lib/mkhtemp.c53
-rw-r--r--util/libreboot-utils/lib/num.c324
-rw-r--r--util/libreboot-utils/lib/rand.c114
-rw-r--r--util/libreboot-utils/lib/string.c151
6 files changed, 255 insertions, 429 deletions
diff --git a/util/libreboot-utils/Makefile b/util/libreboot-utils/Makefile
index f741d3f5..692ebf0f 100644
--- a/util/libreboot-utils/Makefile
+++ b/util/libreboot-utils/Makefile
@@ -37,14 +37,16 @@ OBJS_NVMUTIL = \
obj/lib/io.o \
obj/lib/checksum.o \
obj/lib/word.o \
- obj/lib/mkhtemp.o
+ obj/lib/mkhtemp.o \
+ obj/lib/rand.o
OBJS_MKHTEMP = \
obj/mkhtemp.o \
obj/lib/file.o \
obj/lib/string.o \
obj/lib/num.o \
- obj/lib/mkhtemp.o
+ obj/lib/mkhtemp.o \
+ obj/lib/rand.o
# default mode
CFLAGS_MODE = $(PORTABLE)
@@ -106,6 +108,9 @@ obj/lib/word.o: lib/word.c
obj/lib/mkhtemp.o: lib/mkhtemp.c
$(CC_MODE) $(CFLAGS_MODE) -c lib/mkhtemp.c -o obj/lib/mkhtemp.o
+obj/lib/rand.o: lib/rand.c
+ $(CC_MODE) $(CFLAGS_MODE) -c lib/rand.c -o obj/lib/rand.o
+
# install
install: $(PROG) $(PROGMKH)
diff --git a/util/libreboot-utils/include/common.h b/util/libreboot-utils/include/common.h
index da086425..fb3aa886 100644
--- a/util/libreboot-utils/include/common.h
+++ b/util/libreboot-utils/include/common.h
@@ -13,24 +13,13 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
+#include <errno.h>
/* for linux getrandom
*/
#if defined(__linux__)
-#include <errno.h>
-#if defined(__has_include)
-#if __has_include(<sys/random.h>)
#include <sys/random.h>
-#define HAVE_GETRANDOM 1
-#endif
-#endif
-#if !defined(HAVE_GETRANDOM)
#include <sys/syscall.h>
-#if defined(SYS_getrandom)
-#define HAVE_GETRANDOM_SYSCALL 1
-#endif
-#endif
-
#endif
#define items(x) (sizeof((x)) / sizeof((x)[0]))
@@ -384,24 +373,18 @@ int slen(const char *scmp, size_t maxlen,
size_t *rval);
int scmp(const char *a, const char *b,
size_t maxlen, int *rval);
-
+int sdup(const char *s,
+ size_t n, char **dest);
+int scat(const char *s1, const char *s2,
+ size_t n, char **dest);
+int dcat(const char *s, size_t n,
+ size_t off, char **dest1,
+ char **dest2);
/* numerical functions
*/
unsigned short hextonum(char ch_s);
size_t rlong(void);
-#if !(defined(FALLBACK_RAND_1989) && \
- ((FALLBACK_RAND_1989) > 0))
-#if defined(__linux__)
-#if defined(HAVE_GETRANDOM) || \
- defined(HAVE_GETRANDOM_SYSCALL)
-int fallback_rand_getrandom(void *buf, size_t len);
-#endif
-#endif
-#else
-size_t fallback_rand_1989(void);
-size_t entropy_jitter(void);
-#endif
/* Helper functions for command: dump
*/
diff --git a/util/libreboot-utils/lib/mkhtemp.c b/util/libreboot-utils/lib/mkhtemp.c
index 2fb2f01a..191d657c 100644
--- a/util/libreboot-utils/lib/mkhtemp.c
+++ b/util/libreboot-utils/lib/mkhtemp.c
@@ -886,53 +886,30 @@ err:
int
mkhtemp_fill_random(char *p, size_t xc)
{
- size_t chx = 0;
- int rand_failures = 0;
-
- size_t r;
-
- int saved_rand_error = 0;
static char ch[] =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ size_t chx = 0;
+ size_t r;
+
/* clamp rand to prevent modulo bias
- * (reduced risk of entropy leak)
*/
size_t limit = ((size_t)-1) - (((size_t)-1) % (sizeof(ch) - 1));
-
int saved_errno = errno;
- if (p == NULL) {
- errno = EFAULT;
- goto err_mkhtemp_fill_random;
- }
+ if (if_err(p == NULL, EFAULT))
+ return -1;
for (chx = 0; chx < xc; chx++) {
- do {
- saved_rand_error = errno;
- rand_failures = 0;
retry_rand:
- errno = 0;
-
- /* on bsd: uses arc4random
- on linux: uses getrandom
- on OLD linux: /dev/urandom
- on old/other unix: /dev/urandom
- */
- r = rlong();
-
- if (errno > 0) {
- if (++rand_failures <= 8)
- goto retry_rand;
-
- goto err_mkhtemp_fill_random;
- }
-
- rand_failures = 0;
- errno = saved_rand_error;
-
- } while (r >= limit);
+ /* on bsd: uses arc4random
+ on linux: uses getrandom
+ *never returns error*
+ */
+ r = rlong(); /* always returns successful */
+ if (r >= limit)
+ goto retry_rand;
p[chx] = ch[r % (sizeof(ch) - 1)];
}
@@ -940,12 +917,6 @@ retry_rand:
errno = saved_errno;
return 0;
-err_mkhtemp_fill_random:
-
- if (errno == saved_errno)
- errno = ECANCELED;
-
- return -1;
}
/* WARNING: **ONCE** per file.
diff --git a/util/libreboot-utils/lib/num.c b/util/libreboot-utils/lib/num.c
index 0b76e257..41a08f0b 100644
--- a/util/libreboot-utils/lib/num.c
+++ b/util/libreboot-utils/lib/num.c
@@ -2,6 +2,7 @@
* Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
*
* Numerical functions.
+ * NOTE: randomness was moved to rand.c
*/
/*
@@ -12,10 +13,6 @@ TODO: properly handle errno in this file
#include <sys/param.h>
#endif
#include <sys/types.h>
-#if defined(FALLBACK_RAND_1989) && \
- (FALLBACK_RAND_1989) > 0
-#include <sys/time.h>
-#endif
#include <errno.h>
#if !((defined(__OpenBSD__) && (OpenBSD) >= 201) || \
@@ -26,10 +23,6 @@ TODO: properly handle errno in this file
#include <limits.h>
#include <stddef.h>
#include <string.h>
-#if defined(FALLBACK_RAND_1989) && \
- (FALLBACK_RAND_1989) > 0
-#include <time.h>
-#endif
#include <unistd.h>
#include "../include/common.h"
@@ -117,321 +110,6 @@ err_hextonum:
/* caller just checks >15. */
}
-/* Random numbers
- */
-
-/* when calling this: save errno
- * first, then set errno to zero.
- * on error, this function will
- * set errno and possibly return
- *
- * rlong also preserves errno
- * and leaves it unchanged on
- * success, so if you do it
- * right, you can detect error.
- * this is because it uses
- * /dev/urandom which can err.
- * ditto getrandom (EINTR),
- * theoretically.
- */
-
-size_t
-rlong(void)
-{
-#if !(defined(FALLBACK_RAND_1989) && \
- ((FALLBACK_RAND_1989) > 0))
-#if (defined(__OpenBSD__) && (OpenBSD) >= 201) || \
- defined(__FreeBSD__) || \
- defined(__NetBSD__) || defined(__APPLE__)
-
- int saved_errno = errno;
- size_t rval;
-
- arc4random_buf(&rval, sizeof(size_t));
-
- errno = saved_errno;
- return rval;
-#else
- static int fd = -1;
- static ssize_t nr = -1;
- static size_t off = 0;
-#if defined (BUFSIZ)
- static char rbuf[BUFSIZ];
-#else
-#ifndef PORTABLE
- static char rbuf[4096];
-#elif ((PORTABLE) > 0)
- static char rbuf[256]; /* scarce memory on old systems */
-#else
- static char rbuf[4096]; /* typical 32-bit BUFSIZ */
-#endif
-#endif
- size_t rval;
- ssize_t new_nr;
-
- int retries = 0;
- int max_retries = 100;
-
- int saved_errno = errno;
-
-#if defined(__linux__)
-#if defined(HAVE_GETRANDOM) || \
- defined(HAVE_GETRANDOM_SYSCALL)
-
- /* linux getrandom()
- *
- * we *can* use arc4random on
- * modern linux, but not on
- * every libc. better use the
- * official linux function
- */
-
- if (fallback_rand_getrandom(&rval, sizeof(rval)) == 0) {
- errno = saved_errno;
- return rval;
- }
-
- /*
- * now fall back to urandom if getrandom failed:
- */
-#endif
-#endif
-
- /* reading from urandom is inherently
- * unreliable on old systems, even if
- * newer systems make it more reliable
- *
- * modern linux/bsd make it safe, but
- * we have to assume that someone is
- * compiling this on linux from 1999
- *
- * this logic therefore applies various
- * tricks to mitigate possible os bugs
- */
-
-retry_urandom_read:
-
- if (++retries > max_retries)
- goto err_rlong;
-
- if (nr < 0 || nr < (ssize_t)sizeof(size_t)) {
-
- if (fd < 0) {
-
- fd = open("/dev/urandom",
- O_RDONLY | O_BINARY | O_NOFOLLOW |
- O_CLOEXEC | O_NOCTTY);
-
-#ifdef USE_OLD_DEV_RANDOM
-#if (USE_OLD_DEV_RANDOM) > 0
- /* WARNING:
- * /dev/random may block
- * forever and does **NOT**
- * guarantee better entropy
- * on old systems
- *
- * only use it if needed
- */
-
- if (fd < 0)
- fd = open("/dev/random",
- O_RDONLY | O_BINARY | O_NOFOLLOW |
- O_CLOEXEC | O_NOCTTY);
-#endif
-#endif
-
- if (fd < 0)
- goto retry_urandom_read;
-
- retries = 0;
- }
-
- new_nr = rw_file_exact(fd, (unsigned char *)rbuf,
- sizeof(rbuf), 0, IO_READ, LOOP_EAGAIN,
- LOOP_EINTR, MAX_ZERO_RW_RETRY, OFF_ERR);
-
- if (new_nr < 0 || new_nr < (ssize_t)sizeof(rbuf))
- goto retry_urandom_read;
-
- /* only reset buffer after successful refill */
- nr = new_nr;
- off = 0;
-
- /* don't re-use same fd, to mitaget
- * fd injection */
- close_no_err(&fd);
- }
-
- fd = -1;
- retries = 0;
-
- memcpy(&rval, rbuf + off, sizeof(size_t));
-
- nr -= (ssize_t)sizeof(size_t);
- off += sizeof(size_t);
-
- errno = saved_errno;
-
- return rval;
-
-err_rlong:
-
- if (errno == saved_errno)
- errno = EIO;
-
- return 0;
-
-#endif
-#else /* FALLBACK_RAND_1989 */
- /* your computer is from a museum
- */
- size_t mix = 0;
- int nr = 0;
- int saved_errno = errno;
-
- /* 100 times, for entropy
- */
- for (nr = 0; nr < 100; nr++)
- mix ^= fallback_rand_1989();
-
- errno = saved_errno;
- return mix;
-#endif
-}
-
-#if !(defined(FALLBACK_RAND_1989) && \
- ((FALLBACK_RAND_1989) > 0))
-#if defined(__linux__)
-#if defined(HAVE_GETRANDOM) || \
- defined(HAVE_GETRANDOM_SYSCALL)
-int /* yes, linux is a fallback */
-fallback_rand_getrandom(void *buf, size_t len)
-{
- size_t off = 0;
- ssize_t rval = -1;
- int saved_errno = errno;
-
- if (!len) {
- errno = EINVAL;
- return -1;
- }
-
- if (buf == NULL) {
- errno = EFAULT;
- return -1;
- }
-
-#if defined(HAVE_GETRANDOM) || \
- defined(HAVE_GETRANDOM_SYSCALL)
-
- while (off < len) {
-
-#if defined(HAVE_GETRANDOM)
- rval = (ssize_t)getrandom((char *)buf + off, len - off, 0);
-#elif defined(HAVE_GETRANDOM_SYSCALL)
- rval = (ssize_t)syscall(SYS_getrandom,
- (char *)buf + off, len - off, 0);
-#endif
-
- if (rval < 0) {
- if (errno == EINTR || errno == EAGAIN)
- continue;
-
- errno = EIO;
- return -1; /* unsupported by kernel */
- }
-
- if (rval == 0) {
- errno = EIO;
- return -1;
- }
-
- off += (size_t)rval;
- }
-
- errno = saved_errno;
- return 0;
-
-#else
- (void)buf;
- (void)len;
-
- errno = EIO;
- return -1;
-#endif
-}
-#endif
-#endif
-#else
-size_t
-fallback_rand_1989(void)
-{
- static size_t mix = 0;
- static size_t counter = 0;
-
- struct timeval tv;
-
- /* nobody should use this
- * (not crypto-safe)
- */
-
- gettimeofday(&tv, NULL);
-
- mix ^= (size_t)tv.tv_sec
- ^ (size_t)tv.tv_usec
- ^ (size_t)getpid()
- ^ (size_t)&mix
- ^ counter++
- ^ entropy_jitter();
-
- /*
- * Stack addresses can vary between
- * calls, thus increasing entropy.
- */
- mix ^= (size_t)&mix;
- mix ^= (size_t)&tv;
- mix ^= (size_t)&counter;
-
- return mix;
-}
-
-size_t
-entropy_jitter(void)
-{
- size_t mix;
-
- struct timeval a, b;
- ssize_t mix_diff;
-
- int c;
-
- mix = 0;
-
- gettimeofday(&a, NULL);
-
- for (c = 0; c < 32; c++) {
-
- getpid();
- gettimeofday(&b, NULL);
-
- /*
- * prevent negative numbers to prevent overflow,
- * which would bias rand to large numbers
- */
- mix_diff = (ssize_t)(b.tv_usec - a.tv_usec);
- if (mix_diff < 0)
- mix_diff = -mix_diff;
-
- mix ^= (size_t)(mix_diff);
-
- mix ^= (size_t)&mix;
-
- }
-
- return mix;
-}
-#endif
-
void
check_bin(size_t a, const char *a_name)
{
diff --git a/util/libreboot-utils/lib/rand.c b/util/libreboot-utils/lib/rand.c
new file mode 100644
index 00000000..a8fbb706
--- /dev/null
+++ b/util/libreboot-utils/lib/rand.c
@@ -0,0 +1,114 @@
+/* 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>
+
+#include <errno.h>
+#if !((defined(__OpenBSD__) && (OpenBSD) >= 201) || \
+ defined(__FreeBSD__) || \
+ defined(__NetBSD__) || defined(__APPLE__))
+#include <fcntl.h> /* if not arc4random: /dev/urandom */
+#endif
+#include <limits.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "../include/common.h"
+
+/* Random numbers
+ */
+
+/* when calling this: save errno
+ * first, then set errno to zero.
+ * on error, this function will
+ * set errno and possibly return
+ *
+ * rlong also preserves errno
+ * and leaves it unchanged on
+ * success, so if you do it
+ * right, you can detect error.
+ * this is because it uses
+ * /dev/urandom which can err.
+ * ditto getrandom (EINTR),
+ * theoretically.
+ */
+
+/* for the linux version: we use only the
+ * syscall, because we cannot trust /dev/urandom
+ * to be as robust, and some libc implementations
+ * may default to /dev/urandom under fault conditions.
+ *
+ * for general high reliability, we must abort on
+ * failure. in practise, it will likely never fail.
+ * the arc4random call on bsd never returns error.
+ */
+
+size_t
+rlong(void)
+{
+ size_t rval;
+ int saved_errno = errno;
+ errno = 0;
+
+#if (defined(__OpenBSD__) || defined(__FreeBSD__) || \
+ defined(__NetBSD__) || defined(__APPLE__) || \
+ defined(__DragonFly__))
+
+ arc4random_buf(&rval, sizeof(size_t));
+ goto out;
+
+#elif defined(__linux__)
+
+ size_t off = 0;
+ size_t len = sizeof(rval);
+ ssize_t rc;
+
+retry_rand:
+ rc = (ssize_t)syscall(SYS_getrandom,
+ (char *)&rval + off, len - off, 0);
+
+ if (rc < 0) {
+ if (errno == EINTR || errno == EAGAIN) {
+ usleep(100);
+ goto retry_rand;
+ }
+
+ goto err; /* possibly unsupported by kernel */
+ }
+
+ if ((off += (size_t)rc) < len)
+ goto retry_rand;
+
+ goto out;
+err:
+ /*
+ * getrandom can return with error, butt arc4random
+ * doesn't. generally, getrandom will be reliably,
+ * but we of course have to maintain parity with
+ * BSD. So a rand failure is to be interpreted as
+ * a major systems failure, and we act accordingly.
+ */
+ err_no_cleanup(1, ECANCELED,
+ "Randomisation failure, possibly unsupported in your kernel.");
+ exit(EXIT_FAILURE);
+
+#else
+#error Unsupported operating system (possibly unsecure randomisation)
+#endif
+
+out:
+ errno = saved_errno;
+ return rval;
+}
+#endif
diff --git a/util/libreboot-utils/lib/string.c b/util/libreboot-utils/lib/string.c
index ea7ca30a..0329c6c3 100644
--- a/util/libreboot-utils/lib/string.c
+++ b/util/libreboot-utils/lib/string.c
@@ -14,25 +14,12 @@
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
+#include <limits.h>
+#include <stdint.h>
#include "../include/common.h"
-/* scmp() - strict string comparison
- *
- * strict string comparison
- * similar to strncmp, but null and
- * unterminated inputs do not produce
- * a return value; on error, errno is
- * set and -1 is returned.
- *
- * the real return value is stored in
- * the 4th argument by pointer.
- *
- * the value at rval pointer is set,
- * only upon success. callers should
- * check the return value accordingly.
- */
-
+/* strict strcmp */
int
scmp(const char *a,
const char *b,
@@ -46,9 +33,8 @@ scmp(const char *a,
if (a == NULL ||
b == NULL ||
rval == NULL) {
-
errno = EFAULT;
- return -1;
+ goto err;
}
for (ch = 0; ch < maxlen; ch++) {
@@ -67,27 +53,14 @@ scmp(const char *a,
}
}
- /* block unterminated strings */
+err:
errno = EFAULT;
+ if (rval != NULL)
+ *rval = -1;
return -1;
}
-/* slen() - strict strict length
- *
- * strict string length calculation
- * similar to strnlen, but null and
- * unterminated inputs do not produce
- * a return value; on error, errno is
- * set and -1 is returned.
- *
- * the real return value is stored in
- * the 3rd argument by pointer.
- *
- * the value at rval pointer is set,
- * only upon success. callers should
- * check the return value accordingly.
- */
-
+/* strict strlen */
int
slen(const char *s,
size_t maxlen,
@@ -97,9 +70,8 @@ slen(const char *s,
if (s == NULL ||
rval == NULL) {
-
errno = EFAULT;
- return -1;
+ goto err;
}
for (ch = 0;
@@ -109,11 +81,114 @@ slen(const char *s,
if (ch == maxlen) {
/* unterminated */
errno = EFAULT;
- return -1;
+ goto err;
}
*rval = ch;
return 0;
+err:
+ if (rval != NULL)
+ *rval = 0;
+ return -1;
+}
+
+/* strict strdup */
+int
+sdup(const char *s,
+ size_t n, char **dest)
+{
+ size_t size;
+ char *rval;
+
+ if (dest == NULL ||
+ slen(s, n, &size) < 0 ||
+ if_err(size == SIZE_MAX, EOVERFLOW) ||
+ (rval = malloc(size + 1)) == NULL) {
+
+ if (dest != NULL)
+ *dest = NULL;
+ return -1;
+ }
+
+ memcpy(rval, s, size);
+ *(rval + size) = '\0';
+
+ *dest = rval;
+ return 0;
+}
+
+/* strict strcat */
+int
+scat(const char *s1, const char *s2,
+ size_t n, char **dest)
+{
+ size_t size1;
+ size_t size2;
+ char *rval;
+
+ if (dest == NULL ||
+ slen(s1, n, &size1) < 0 ||
+ slen(s2, n, &size2) < 0 ||
+ if_err(size1 > SIZE_MAX - size2 - 1, EOVERFLOW) ||
+ (rval = malloc(size1 + size2 + 1)) == NULL) {
+
+ if (dest != NULL)
+ *dest = NULL;
+ return -1;
+ }
+
+ memcpy(rval, s1, size1);
+ memcpy(rval + size1, s2, size2);
+ *(rval + size1 + size2) = '\0';
+
+ *dest = rval;
+ return 0;
+}
+
+/* strict split/de-cat - off is where
+ 2nd buffer will start from */
+int
+dcat(const char *s, size_t n,
+ size_t off, char **dest1,
+ char **dest2)
+{
+ size_t size;
+ char *rval1 = NULL;
+ char *rval2 = NULL;
+
+ if (dest1 == NULL || dest2 == NULL ||
+ slen(s, n, &size) < 0 ||
+ if_err(size == SIZE_MAX, EOVERFLOW) ||
+ if_err(off >= size, EOVERFLOW) ||
+ (rval1 = malloc(off + 1)) == NULL ||
+ (rval2 = malloc(size - off + 1)) == NULL) {
+
+ goto err;
+ }
+
+ memcpy(rval1, s, off);
+ *(rval1 + off) = '\0';
+
+ memcpy(rval2, s + off, size - off);
+ *(rval2 + size - off) = '\0';
+
+ *dest1 = rval1;
+ *dest2 = rval2;
+
+ return 0;
+
+err:
+ if (rval1 != NULL)
+ free(rval1);
+ if (rval2 != NULL)
+ free(rval2);
+
+ if (dest1 != NULL)
+ *dest1 = NULL;
+ if (dest2 != NULL)
+ *dest2 = NULL;
+
+ return -1;
}
/* the one for nvmutil state is in state.c */