Compare commits

..

27 Commits

Author SHA1 Message Date
Leah Rowe
f9e206c959 more cleanup on rand.c
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-26 02:25:21 +00:00
Leah Rowe
4e942b0832 cleanup
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-26 01:52:22 +00:00
Leah Rowe
83165f8ad8 fix makefile
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-26 01:45:18 +00:00
Leah Rowe
43b110a81b libreboot-utils: tidy up rand.c
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-26 01:39:35 +00:00
Leah Rowe
f45a349da4 libreboot-utils: new function, scatn()
concatenate an arbitrary number of strings,
pointed to by char **

i'll use this and the next function, dcatn,
in an upcoming feature planned for mkhtemp.

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-26 01:12:50 +00:00
Leah Rowe
7cd588a445 rmalloc
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 23:53:51 +00:00
Leah Rowe
7284da1461 util/libreboot-utils: randomisation test
to test the effectiveness of the rand function

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 22:29:15 +00:00
Leah Rowe
491e41598a cleanup
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 21:51:50 +00:00
Leah Rowe
886b1aabbd mkrstr
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 21:29:23 +00:00
Leah Rowe
6adcb9fa3a lbutils: new function, mkrbuf (random malloc)
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 21:08:40 +00:00
Leah Rowe
8d8d8871ce lbutils: close fd on rset failure
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 20:54:24 +00:00
Leah Rowe
0f66f08632 further clarify intentt
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 20:50:59 +00:00
Leah Rowe
7c0f6c160f lbutils, rset: err if zero bytes requested
similar to the logic about other failure states

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 20:49:10 +00:00
Leah Rowe
51e8b62442 dot
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 20:23:49 +00:00
Leah Rowe
9a9f8535b1 lbutils: also check null!
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 20:21:02 +00:00
Leah Rowe
f103b5cf6f lbutils: clarify design regarding urandom/getrandom
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 19:59:27 +00:00
Leah Rowe
3dd6dd0969 lbutils, rand: err on zero return (fatal)
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 19:51:46 +00:00
Leah Rowe
dbfd80497d cleanup
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 19:50:01 +00:00
Leah Rowe
302ebbedee lbutils: cast to prevent ub in rset()
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 19:40:27 +00:00
Leah Rowe
f5398025ae cleanup
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 19:38:22 +00:00
Leah Rowe
5f0021ce46 fix offset on urandom falback
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 19:30:06 +00:00
Leah Rowe
dd599d8f32 libreboot-utils: tidy up rand
make it more efficient. much lower rejection
rate now, about 2-5%. deal with bias, but also
get numbers in bulk. not too many.

i'd say this is about right in terms of performance
balance. 64 bytes == 8 large integers.

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 19:26:48 +00:00
Leah Rowe
556dd3348d cleanup
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 18:06:51 +00:00
Leah Rowe
7d6bee5626 libreboot-utils: replace rlong() with rset()
now you can send an arbitrary number of bytes
with random numbers

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 18:01:38 +00:00
Leah Rowe
32a18b3944 libreboot-utils: tidy up rand
also re-add /dev/urandom support, as a config option

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 17:52:53 +00:00
Leah Rowe
79f2dd197f util/mkhtemp: use /dev/urandom *if enabled*
build-time option. do not allow fallback; on
a system where getrandom is used, it should
be used exclusively.

on some systems, getrandom may not be available,
even if they have a newer kernel.

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 17:17:39 +00:00
Leah Rowe
210922bc91 util/mkhtemp: extremely hardened mkhtemp
This will also be used in lbmk itself at some point,
which currently just uses regular mktemp, for tmpdir
handling during the build process.

Renamed util/nvmutil to util/libreboot-utils, which
now contains two tools. The new tool, mkhtemp, is a
hardened implementation of mktemp, which nvmutil
also uses now. Still experimental, but good enough
for nvmutil.

Mkhtemp attempts to provide TOCTOU resistance on
Linux, by using modern features in Linux such as
Openat2 (syscall) with O_EXCL and O_TMPFILE,
and many various security checks e.g.
inode/dev during creation. Checks are done constantly,
to try to detect race conditions. The code is very
strict about things like sticky bits in world writeable
directories, also ownership (it can be made to bar even
root access on files and directories it doesn't own).

It's a security-first implementation of mktemp, likely
even more secure than the OpenBSD mkstemp, but more
auditing and testing is needed - more features are
also planned, including a compatibility mode to make
it also work like traditional mktemp/mkstemp. The
intention, once this becomes stable, is that it will
become a modern drop-in replacement for mkstemp on
Linux and BSD systems.

Some legacy code has been removed, and in general
cleaned up. I wrote mkhtemp for nvmutil, as part of
its atomic write behaviour, but mktemp was the last
remaining liability, so I rewrote that too!

Docs/manpage/website will be made for mkhtemp once
the code is mature.

Other changes have also been made. This is from another
experimental branch of Libreboot, that I'm pushing
early. For example, nvmutil's state machine has been
tidied up, moving more logic back into main.

Mktemp is historically prone to race conditions,
e.g. symlink attacks, directory replacement, remounting
during operation, all sorts of things. Mkhtemp has
been written to solve, or otherwise mitigate, that
problem. Mkhtemp is currently experimental and will
require a major cleanup at some point, but it
already works well enough, and you can in fact use
it; at this time, the -d, -p and -q flags are
supported, and you can add a custom template at
the end, e.g.

mkhtemp -p test -d

Eventually, I will make this have complete parity
with the GNU and BSD implementations, so that it is
fully useable on existing setups, while optionally
providing the hardening as well.

A lot of code has also been tidied up. I didn't
track the changes I made with this one, because
it was a major re-write of nvmutil; it is now
libreboot-utils, and I will continue to write
more programs in here over time. It's basically
now a bunch of hardened wrappers around various
libc functions, e.g. there is also a secure I/O
wrapper for read/write.

There is a custom randomisation function, rlong,
which simply uses arc4random or getrandom, on
BSD and Linux respectively. Efforts are made to
make it as reliable as possible, to the extent
that it never returns with failure; in the unlikely
event that it fails, it aborts. It also sleeps
between failure, to mitigate certain DoS attacks.

You can just go in util/libreboot-utils and
type make, then you will have the nvmutil and
mkhtemp binaries, which you can just use. It
all works. Everything was massively rewritten.

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 12:32:57 +00:00
9 changed files with 312 additions and 97 deletions

View File

@@ -1,6 +1,7 @@
/nvm
/nvmutil
/mkhtemp
/lottery
*.bin
*.o
*.d

View File

@@ -25,6 +25,7 @@ HELLFLAGS = $(STRICT) -Weverything
PROG = nvmutil
PROGMKH = mkhtemp
PROGLOT = lottery
OBJS_NVMUTIL = \
obj/nvmutil.o \
@@ -48,11 +49,19 @@ OBJS_MKHTEMP = \
obj/lib/mkhtemp.o \
obj/lib/rand.o
OBJS_LOTTERY = \
obj/lottery.o \
obj/lib/file.o \
obj/lib/string.o \
obj/lib/num.o \
obj/lib/mkhtemp.o \
obj/lib/rand.o
# default mode
CFLAGS_MODE = $(PORTABLE)
CC_MODE = $(CC)
all: $(PROG) $(PROGMKH)
all: $(PROG) $(PROGMKH) $(PROGLOT)
$(PROG): $(OBJS_NVMUTIL)
$(CC_MODE) $(OBJS_NVMUTIL) -o $(PROG) $(LDFLAGS)
@@ -60,9 +69,13 @@ $(PROG): $(OBJS_NVMUTIL)
$(PROGMKH): $(OBJS_MKHTEMP)
$(CC_MODE) $(OBJS_MKHTEMP) -o $(PROGMKH) $(LDFLAGS)
$(PROGLOT): $(OBJS_LOTTERY)
$(CC_MODE) $(OBJS_LOTTERY) -o $(PROGLOT) $(LDFLAGS)
# ensure obj directory exists
$(OBJS_NVMUTIL): obj
$(OBJS_MKHTEMP): obj
$(OBJS_LOTTERY): obj
obj:
mkdir obj || true
@@ -76,6 +89,9 @@ obj/nvmutil.o: nvmutil.c
obj/mkhtemp.o: mkhtemp.c
$(CC_MODE) $(CFLAGS_MODE) -c mkhtemp.c -o obj/mkhtemp.o
obj/lottery.o: lottery.c
$(CC_MODE) $(CFLAGS_MODE) -c lottery.c -o obj/lottery.o
# library/helper objects
obj/lib/state.o: lib/state.c
@@ -113,19 +129,23 @@ obj/lib/rand.o: lib/rand.c
# install
install: $(PROG) $(PROGMKH)
install: $(PROG) $(PROGMKH) $(PROGLOT)
$(INSTALL) -d $(DESTDIR)$(PREFIX)/bin
$(INSTALL) $(PROG) $(DESTDIR)$(PREFIX)/bin/$(PROG)
chmod 755 $(DESTDIR)$(PREFIX)/bin/$(PROG)
$(INSTALL) $(PROGMKH) $(DESTDIR)$(PREFIX)/bin/$(PROGMKH)
chmod 755 $(DESTDIR)$(PREFIX)/bin/$(PROGMKH)
$(INSTALL) $(PROGLOT) $(DESTDIR)$(PREFIX)/bin/$(PROGLOT)
chmod 755 $(DESTDIR)$(PREFIX)/bin/$(PROGLOT)
uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/$(PROG)
rm -f $(DESTDIR)$(PREFIX)/bin/$(PROGMKH)
rm -f $(DESTDIR)$(PREFIX)/bin/$(PROGLOT)
clean:
rm -f $(PROG) $(PROGMKH) $(OBJS_NVMUTIL) $(OBJS_MKHTEMP)
rm -f $(PROG) $(PROGMKH) $(OBJS_NVMUTIL) $(OBJS_MKHTEMP) \
$(OBJS_LOTTERY) $(PROGLOT)
distclean: clean

View File

@@ -375,6 +375,8 @@ int scmp(const char *a, const char *b,
size_t maxlen, int *rval);
int sdup(const char *s,
size_t n, char **dest);
int scatn(ssize_t sc, const char **sv,
size_t max, char **rval);
int scat(const char *s1, const char *s2,
size_t n, char **dest);
int dcat(const char *s, size_t n,
@@ -384,7 +386,12 @@ int dcat(const char *s, size_t n,
*/
unsigned short hextonum(char ch_s);
size_t rlong(void);
void *mkrbuf(size_t n);
void *rmalloc(size_t *size); /* don't ever use this */
void rset(void *buf, size_t n);
void *mkrbuf(size_t n);
char *mkrstr(size_t n);
int win_lottery(char **buf);
/* Helper functions for command: dump
*/

View File

@@ -634,15 +634,8 @@ mkhtemp(int *fd,
st_dir_initial, fname_copy,
p, xc, fd, st, type);
if (r == 0) {
if (retries >= MKHTEMP_SPIN_THRESHOLD) {
/* usleep can return EINTR */
close_errno = errno;
usleep((useconds_t)rlong() & 0x3FF);
errno = close_errno;
}
if (r == 0)
continue;
}
if (r < 0)
goto err;
@@ -886,37 +879,32 @@ err:
int
mkhtemp_fill_random(char *p, size_t xc)
{
static char ch[] =
static const char ch[] =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
size_t chx = 0;
size_t r;
unsigned char scratch[64];
/* clamp rand to prevent modulo bias
*/
size_t limit = ((size_t)-1) - (((size_t)-1) % (sizeof(ch) - 1));
int saved_errno = errno;
size_t off = 0;
size_t i;
/* clamp rand to prevent modulo bias */
size_t limit = 256 - (256 % (sizeof(ch) - 1));
if (if_err(p == NULL, EFAULT))
return -1;
for (chx = 0; chx < xc; chx++) {
retry_rand:
/* on bsd: uses arc4random
on linux: uses getrandom
*never returns error*
*/
r = rlong(); /* always returns successful */
if (r >= limit)
goto retry_rand;
rset(scratch, sizeof(scratch));
p[chx] = ch[r % (sizeof(ch) - 1)];
for (i = 0; i < sizeof(scratch) && off < xc; i++) {
if (scratch[i] < limit)
p[off++] = ch[scratch[i] % (sizeof(ch) - 1)];
}
errno = saved_errno;
return 0;
if (off < xc)
goto retry_rand;
return 0;
}
/* WARNING: **ONCE** per file.

View File

@@ -84,7 +84,7 @@ hextonum(char ch_s)
if (ch == '?' || ch == 'x') {
rval = rlong();
rset(&rval, sizeof(rval));
if (errno > 0)
goto err_hextonum;

View File

@@ -12,103 +12,204 @@
#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 */
#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"
/* Random numbers
*/
/* when calling this: save errno
* first, then set errno to zero.
* on error, this function will
* set errno and possibly return
/* Regarding Linux getrandom/urandom:
*
* 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 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
*
* for general high reliability, we must abort on
* failure. in practise, it will likely never fail.
* the arc4random call on bsd never returns error.
* 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.
*/
size_t
rlong(void)
int
win_lottery(char **buf) /* are u lucky? */
{
size_t size = 0;
int rval;
char *s1 = rmalloc(&size);
char *s2 = rmalloc(&size);
if (scmp(s1, s2, BUFSIZ + 2, &rval) >= 0 &&
rval == 0)
rval = 1; /* winner! */
else
rval = 0;
(void) scat(s1, s2, BUFSIZ << 1, buf);
free_if_null(&s1);
free_if_null(&s2);
return rval;
}
void *
rmalloc(size_t *rval)
{
if (if_err(rval == NULL, EFAULT))
return NULL;
rset(rval, sizeof(*rval));
return mkrstr(*rval %= BUFSIZ);
}
char *
mkrstr(size_t n) /* emulates spkmodem-decode */
{
char *s;
size_t i;
if (n == 0)
err_no_cleanup(0, EPERM, "mkrbuf: zero-byte request");
if (n == SIZE_MAX)
err_no_cleanup(0, EOVERFLOW, "mkrbuf: overflow");
if (if_err((s = mkrbuf(n + 1)) == NULL, EFAULT))
err_no_cleanup(0, EFAULT, "mkrstr: null");
for (i = 0; i < n; i++)
while(*(s + i) == '\0')
rset(s + i, 1);
*(s + n) = '\0';
return s;
}
void *
mkrbuf(size_t n)
{
void *buf;
if (n == 0)
err_no_cleanup(0, EPERM, "mkrbuf: zero-byte request");
if ((buf = malloc(n)) == NULL)
err_no_cleanup(0, ENOMEM, "mkrbuf: malloc");
rset(buf, n);
return buf; /* basically malloc() but with rand */
}
void
rset(void *buf, size_t n)
{
size_t rval;
int saved_errno = errno;
errno = 0;
if (if_err(buf == NULL, EFAULT))
goto err;
if (n == 0)
err_no_cleanup(0, EPERM, "rset: zero-byte request");
#if (defined(__OpenBSD__) || defined(__FreeBSD__) || \
defined(__NetBSD__) || defined(__APPLE__) || \
defined(__DragonFly__))
defined(__DragonFly__)) && !(defined(USE_URANDOM) && \
((USE_URANDOM) > 0))
arc4random_buf(&rval, sizeof(size_t));
arc4random_buf(buf, n);
goto out;
#elif defined(__linux__)
#else
size_t off = 0;
size_t len = sizeof(rval);
ssize_t rc;
ssize_t rc = 0;
#if defined(USE_URANDOM) && \
((USE_URANDOM) > 0)
int fd = -1;
if ((fd = open("/dev/urandom", O_RDONLY)) < 0)
goto err;
retry_rand:
rc = (ssize_t)syscall(SYS_getrandom,
(char *)&rval + off, len - off, 0);
if (rc < 0) {
if (errno == EINTR || errno == EAGAIN) {
usleep(100);
if ((rc = read(fd, (unsigned char *)buf + off, n - off)) < 0) {
#elif defined(__linux__)
retry_rand:
if ((rc = (ssize_t)syscall(SYS_getrandom,
(unsigned char *)buf + off, n - off, 0)) < 0) {
#else
#error Unsupported operating system (possibly unsecure randomisation)
#endif
if (errno == EINTR ||
errno == EAGAIN)
goto retry_rand;
}
goto err; /* possibly unsupported by kernel */
}
if ((off += (size_t)rc) < len)
if (rc == 0)
goto err; /* prevent infinite loop on fatal err */
if ((off += (size_t)rc) < n)
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)
#if defined(USE_URANDOM) && \
((USE_URANDOM) > 0)
close_no_err(&fd);
#endif
goto out;
#endif
out:
errno = saved_errno;
return rval;
return;
err:
#if defined(USE_URANDOM) && \
((USE_URANDOM) > 0)
close_no_err(&fd);
#endif
err_no_cleanup(0, ECANCELED,
"Randomisation failure, possibly unsupported in your kernel");
exit(EXIT_FAILURE);
}
#endif

View File

@@ -117,6 +117,57 @@ sdup(const char *s,
return 0;
}
/* concatenate N number of strings */
/* slen already checks null/termination */
int
scatn(ssize_t sc, const char **sv,
size_t max, char **rval)
{
ssize_t i = 0;
size_t ts = 0;
size_t *size = NULL;
char *ct = NULL;
int saved_errno = errno;
if (if_err(sc <= 0, EINVAL) ||
if_err(sc > SIZE_MAX / sizeof(size_t), EOVERFLOW) ||
if_err(sv == NULL, EINVAL) ||
if_err((size = malloc(sizeof(size_t) * sc)) == NULL, ENOMEM))
goto err;
for (i = 0; i < sc; i++, ts += size[i])
if (if_err(sv[i] == NULL, EINVAL) ||
slen(sv[i], max, &size[i]) < 0 ||
if_err(size[i] > max - 1, EOVERFLOW) ||
if_err((size[i] + ts) < ts, EOVERFLOW))
goto err;
if (if_err(ts > SIZE_MAX - 1, EOVERFLOW) ||
if_err(ts > max - 1, EOVERFLOW) ||
if_err((ct = malloc(ts + 1)) == NULL, ENOMEM))
goto err;
for (ts = i = 0; i < sc; i++, ts += size[i])
memcpy(ct + ts, sv[i], size[i]);
*(ct + ts) = '\0';
*rval = ct;
errno = saved_errno;
return 0;
err:
if (ct != NULL)
free(ct);
if (size != NULL)
free(size);
if (errno == saved_errno)
errno = EFAULT;
return -1;
}
/* strict strcat */
int
scat(const char *s1, const char *s2,

View File

@@ -0,0 +1,41 @@
/* SPDX-License-Identifier: MIT
* Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
*/
#ifdef __OpenBSD__
#include <sys/param.h> /* pledge(2) */
#endif
#include <stdio.h>
#include <stdlib.h>
#include "include/common.h"
int
main(int argc, char *argv[])
{
char *s1 = NULL;
int rval = 0;
#if defined(__OpenBSD__) && defined(OpenBSD)
#if (OpenBSD) >= 509
if (pledge("stdio", NULL) == -1)
err_no_cleanup(0, errno, "openbsd won it");
#endif
#endif
setvbuf(stdout, NULL, _IONBF, 0);
if (win_lottery(&s1))
rval = 1;
if (s1 != NULL) {
printf("%s\n\n", s1);
free(s1);
}
printf("%s\n", rval ? "You won!" : "You lose! Sorry!");
return rval? EXIT_SUCCESS : EXIT_FAILURE;
}/*
( >:3 )
/| |\
/ \ */

View File

@@ -44,8 +44,14 @@ main(int argc, char *argv[])
#if (OpenBSD) >= 604
if (pledge("stdio flock rpath wpath cpath unveil", NULL) == -1)
err_no_cleanup(0, errno, "pledge plus unveil, main");
#if defined(USE_URANDOM) && \
((USE_URANDOM) > 0)
if (unveil("/dev/null", "r") == -1)
err_no_cleanup(0, errno, "unveil r: /dev/null");
#else
if (unveil("/dev/urandom", "r") == -1)
err_no_cleanup(0, errno, "unveil r: /dev/urandom");
#endif
#elif (OpenBSD) >= 509
if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
err_no_cleanup(0, errno, "pledge, main");