summaryrefslogtreecommitdiff
path: root/util/libreboot-utils
diff options
context:
space:
mode:
Diffstat (limited to 'util/libreboot-utils')
-rw-r--r--util/libreboot-utils/.gitignore1
-rw-r--r--util/libreboot-utils/Makefile26
-rw-r--r--util/libreboot-utils/README.md254
-rw-r--r--util/libreboot-utils/include/common.h10
-rw-r--r--util/libreboot-utils/lib/file.c103
-rw-r--r--util/libreboot-utils/lib/mkhtemp.c52
-rw-r--r--util/libreboot-utils/lib/num.c41
-rw-r--r--util/libreboot-utils/lib/rand.c86
-rw-r--r--util/libreboot-utils/lib/string.c51
-rw-r--r--util/libreboot-utils/lottery.c32
-rw-r--r--util/libreboot-utils/mkhtemp.c67
11 files changed, 458 insertions, 265 deletions
diff --git a/util/libreboot-utils/.gitignore b/util/libreboot-utils/.gitignore
index fbf110f9..fbfbd130 100644
--- a/util/libreboot-utils/.gitignore
+++ b/util/libreboot-utils/.gitignore
@@ -1,6 +1,7 @@
/nvm
/nvmutil
/mkhtemp
+/lottery
*.bin
*.o
*.d
diff --git a/util/libreboot-utils/Makefile b/util/libreboot-utils/Makefile
index 692ebf0f..872a37c4 100644
--- a/util/libreboot-utils/Makefile
+++ b/util/libreboot-utils/Makefile
@@ -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
diff --git a/util/libreboot-utils/README.md b/util/libreboot-utils/README.md
new file mode 100644
index 00000000..6e94035b
--- /dev/null
+++ b/util/libreboot-utils/README.md
@@ -0,0 +1,254 @@
+Mkhtemp - Hardened mktemp
+-------------------------
+
+Just like normal mktemp, but hardened.
+
+Create new files and directories randomly as determined by
+the user's TMPDIR, or fallback. These temporary files and
+directories can be generated from e.g. shell scripts, running
+mkhtemp. There is also a library that you could use in your
+program. Portable to Linux and BSD. **WORK IN PROGRESS.
+This is a very new project. Expect bugs - a stable release
+will be announced, when the code has matured.**
+
+A brief summary of *why* mkhtemp is more secure (more
+details provided later in this readme - please also
+read the source code):
+
+Detect and mitigate symlink attacks, directory access
+race conditions, unsecure TMPDIR (e.g. bad enforce sticky
+bit policy on world writeable dirs), implement in user
+space a virtual sandbox (block directory escape and resolve
+paths by walking from `/` manually instead of relying on
+the kernel/system), voluntarily error out (halt all
+operation) if accessing files you don't own - that's why
+sticky bits are checked for example, even when you're root.
+
+It... blocks symlinks, relative paths, attempts to prevent
+directory escape (outside of the directory that the file
+you're creating is in), basically implementing an analog
+of something like e.g. unveil, but in userspace!
+
+Mkhtemp is designed to be the most secure implementation
+possible, of mktemp, offering a heavy amount of hardening
+over traditional mktemp. Written in C89, and the plan is
+very much to keep this code portable over time - patches
+very much welcome.
+
+i.e. please read the source code
+
+```
+/*
+ * WARNING: WORK IN PROGRESS.
+ * Do not use this software in
+ * your distro yet. It's ready
+ * when it's ready. Read the src.
+ *
+ * What you see is an early beta.
+ *
+ * Please do not merge this in
+ * your Linux distro package repo
+ * yet (unless maybe you're AUR).
+ */
+```
+
+Supported mktemp flags:
+
+```
+mkhtemp: usage: mkhtemp [-d] [-p dir] [template]
+
+ -p DIR <-- set directory, overriding TMPDIR
+ -d <-- make a directory instead of a file
+ -q <-- silence errors (exit status unchanged)
+```
+
+The rest of them will be added later (the same ones
+that GNU and BSD mktemp implement). With these options,
+you can generate files/directories already.
+
+You can also write a template at the end. e.g.
+
+```
+mkhtemp -d -p path/to/directory vickysomething_XXXXXXXXXXX
+```
+
+On most sane/normal setups, the program should already
+actually work, but please know that it's very different
+internally than every other mktemp implementation.
+
+Read the source code if you're interested. As of this
+time of writing, mkhtemp is very new, and under
+development. A stable release will be announced when ready.
+
+### What does mkhtemp do differently?
+
+This software attempts to provide mitigation against
+several TOCTOU-based
+attacks e.g. directory rename / symlink / re-mount, and
+generally provides much higher strictness than previous
+implementations such as mktemp, mkstemp or even mkdtemp.
+It uses several modern features by default, e.g. openat2
+and `O_TMPFILE` (plus `O_EXCL`) on Linux, with additional
+hardening; BSD projects only have openat so the code uses
+that there, but some (not all) of the kinds of checks
+Openat2 enforces are done manually (in userspace).
+
+File system sandboxing in userspace (pathless discovery,
+and operations are done only with FDs). At startup, the
+root directory is opened, and then everything is relative
+to that.
+
+Many programs rely on mktemp, and they use TMPDIR in a way
+that is quite insecure. Mkhtemp intends to change that,
+quite dramatically, with: userspace sandbox (and use OS
+level options e.g. OBSD pledge where available), constant
+identity/ownership checks on files, MUCH stricter ownership
+restrictions (e.g. enforce sticky bit policy on world-
+writeable tmpdirs), preventing operation on other people's
+files (only your own files) - even root is restricted,
+depending on how the code is compiled. Please read the code.
+
+Basically, the gist of it is that normal mktemp *trusts*
+your system is set up properly. It will just run however
+you tell it to, on whatever directory you tell it to, and
+if you're able to write to it, it will write to it.
+Some implementations (e.g. OpenBSD one) do some checks,
+but not all of them do *all* checks. The purpose of
+mkhtemp is to be as strict as possible, while still being
+reliable enough that people can use it. Instead of catering
+to legacy requirements, mkhtemp says that systems should
+be secure. So if you're running in an insecure environment,
+the goal of mkhtemp is to *exit* when you run it; better
+this than files being corrupted.
+
+Security and reliability are the same thing. They both
+mean that your computer is behaving as it should, in a
+manner that you can predict.
+
+It doesn't matter how many containers you have, or how
+memory-safe your programming language is, the same has
+been true forever: code equals bugs, and code usually
+has the same percentage of bugs, so more code equals
+more bugs. Therefore, highly secure systems (such as
+OpenBSD) typically try to keep their code as small and
+clean as possible, so that they can audit it. Mkhtemp
+assumes that your system is hostile, and is designed
+accordingly.
+
+What?
+-----
+
+This is the utility version, which makes use of the also-
+included library. No docs yet - source code are the docs,
+and the (ever evolving, and hardening) specification.
+
+This was written from scratch, for use in nvmutil, and
+it is designed to be portable (BSD, Linux). Patches
+very much welcome.
+
+Caution
+-------
+
+This is a new utility. Expect bugs.
+
+```
+WARNING: This is MUCH stricter than every other mktemp
+ implementation, even more so than mkdtemp or
+ the OpenBSD version of mkstemp. It *will* break,
+ or more specifically, reveal the flaws in, almost
+ every major critical infrastructure, because most
+ people already use mktemp extremely insecurely.
+```
+
+This tool is written by me, for me, and also Libreboot, but
+it will be summitted for review to various Linux distros
+and BSD projects once it has reached maturity.
+
+### Why was this written?
+
+Atomic writes were implemented in nvmutil (Libreboot's
+Intel GbE NVM editor), but one element remained: the
+program mktemp, itself, which has virtually no securitty
+checks whatsoever. GNU and BSD implementations use
+mkstemp now, which is a bit more secure, and they offer
+additional hardening, but I wanted to be reasonably
+assured that my GbE files were not being corrupted in
+any way, and that naturally led to writing a hardened
+tool. It was originally just going to be for nvmutil,
+but then it became its own standard utility.
+
+Existing implementations of mktemp just simply do not
+have sufficient checks in place to prevent misuse. This
+tool, mkhtemp, intentionally focuses on being secure
+instead of easy. For individuals just running Linux on
+their personal machine, it might not make much difference,
+but corporations and projects running computers for lots
+of big infrastructure need something reliable, since
+mktemp is just one of those things everyone uses.
+Every big program needs to make temporary files.
+
+But the real reason I wrote this tool is because, it's
+fun, and because I wanted to challenge myself.
+
+Roadmap
+-------
+
+Some things that are in the near future for mkhtemp
+development:
+
+Thoroughly document every known case of CVEs in the wild,
+and major attacks against individuals/projects/corporations
+that were made possible by mktemp - that mkhtemp might
+have prevented. There are several.
+
+More hardening; still a lot more that can be done, depending
+on OS. E.g. integrate FreeBSD capsicum.
+
+Another example: although usually reliable, comparing the
+inode and device of a file/directory isn't by itself sufficient.
+There are other checks that mkhtemp does; for example I could
+implement it so that directories are more aggressively re-
+opened by mkhtemp itself, mid-operation. This re-opening
+would be quite expensive computationally, but it would then
+allow us to re-check everything, since we store state from
+when the program starts.
+
+Tidy up the code: the current code was thrown together in
+a week, and needs tidying. A proper specification should be
+written, to define how it works, and then the code should
+be auditted for compliance. A lot of the functions are
+also quite complex and do a lot; they could be split up.
+
+Right now, mkhtemp mainly returns a file descriptor and
+a path, after operation, ironic given the methods it uses
+while opening your file/dir. After it's done, you then have
+to handle everything again. Mkhtemp could keep everything
+open instead, and continue to provide verification; in
+other words, it could provide a completely unified way for
+Linux/BSD programs to open files, write to them atomically,
+and close. Programs like Vim will do this for example, or
+other text editors, but every program has its own way. So
+what mkhtemp could do is provide a well-defined API alongside
+its mktemp hardening. Efforts would be made to avoid
+feature creep, and ensure that the code remains small and
+nimble.
+
+Compatibility mode: another thing is that mkhtemp is a bit
+too strict for some users, so it may break some setups. What
+it could do is provide a compatibility mode, and in this
+mode, behave like regular mktemp. That way, it could become
+a drop-in replacement on Linux distros (and BSDs if they
+want it), while providing a more hardened version and
+recommending that where possible.
+
+~~Rewrite it in rust~~ (nothing against it though, I just like C89 for some reason)
+
+Also, generally document the history of mktemp, and how
+mkhtemp works in comparison.
+
+Also a manpage.
+
+Once all this is done, and the project is fully polished,
+then it will be ready for your Linux distro. For now, I
+just use it in nvmutil (and I also use it on my personal
+computer).
diff --git a/util/libreboot-utils/include/common.h b/util/libreboot-utils/include/common.h
index 6d6d8d09..71f28fad 100644
--- a/util/libreboot-utils/include/common.h
+++ b/util/libreboot-utils/include/common.h
@@ -83,7 +83,7 @@ int fchmod(int fd, mode_t mode);
#endif
#ifndef REAL_POS_IO
-#define REAL_POS_IO 0
+#define REAL_POS_IO 1
#endif
#ifndef LOOP_EAGAIN
@@ -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,
@@ -385,9 +387,12 @@ int dcat(const char *s, size_t n,
unsigned short hextonum(char ch_s);
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(void);
+size_t rsize(void);
/* Helper functions for command: dump
*/
@@ -469,11 +474,8 @@ int io_args(int fd, void *mem, size_t nrw,
off_t off, int rw_type);
int check_file(int fd, struct stat *st);
ssize_t rw_over_nrw(ssize_t r, size_t nrw);
-#if !defined(REAL_POS_IO) || \
- REAL_POS_IO < 1
off_t lseek_on_eintr(int fd, off_t off,
int whence, int loop_eagain, int loop_eintr);
-#endif
int try_err(int loop_err, int errval);
/* Error handling and cleanup
diff --git a/util/libreboot-utils/lib/file.c b/util/libreboot-utils/lib/file.c
index 552618d6..5fdef7b3 100644
--- a/util/libreboot-utils/lib/file.c
+++ b/util/libreboot-utils/lib/file.c
@@ -69,29 +69,6 @@ err_same_file:
return -1;
}
-/* open() but with abort traps
- */
-/* TODO: also support other things here than files.
- and then use, throughout the program.
- in particular, use of openat might help
- (split the path)
- (see: link attack mitigations throughout nvmutil)
-
- make it return, and handle the return value/errno
-
- (this could return e.g. EINTR)
-
- TODO: this function is not used by mkhtemp, nor will
- it probably be, it's currently used by nvmutil,
- for opening intel gbe nvm config files. i can
- probably remove it though and unify witth some
- of the verification code now used for mkhtemp
-
-TODO: and don't abort. return -1. and handle in the caller.
-
-minor obstacle: the mkhtemp code always requires absolute
-paths, whereas the gbe editor takes relative paths.
- */
void
xopen(int *fd_ptr, const char *path, int flags, struct stat *st)
{
@@ -108,10 +85,6 @@ xopen(int *fd_ptr, const char *path, int flags, struct stat *st)
err_no_cleanup(0, errno, "%s: file not seekable", path);
}
-/* fsync() the directory of a file,
- * useful for atomic writes
- */
-
int
fsync_dir(const char *path)
{
@@ -196,19 +169,6 @@ err_fsync_dir:
return -1;
}
-/*
- * Safe I/O functions wrapping around
- * read(), write() and providing a portable
- * analog of both pread() and pwrite().
- * These functions are designed for maximum
- * robustness, checking NULL inputs, overflowed
- * outputs, and all kinds of errors that the
- * standard libc functions don't.
- *
- * Looping on EINTR and EAGAIN is supported.
- * EINTR/EAGAIN looping is done indefinitely.
- */
-
/* rw_file_exact() - Read perfectly or die
*
* Read/write, and absolutely insist on an
@@ -243,6 +203,7 @@ rw_file_exact(int fd, unsigned char *mem, size_t nrw,
size_t retries_on_zero;
int saved_errno = errno;
+ errno = 0;
rval = 0;
@@ -317,12 +278,6 @@ err_rw_file_exact:
/* prw() - portable read-write with more
* safety checks than barebones libc
*
- * portable pwrite/pread on request, or real
- * pwrite/pread libc functions can be used.
- * the portable (non-libc) pread/pwrite is not
- * thread-safe, because it does not prevent or
- * mitigate race conditions on file descriptors
- *
* If you need real pwrite/pread, just compile
* with flag: REAL_POS_IO=1
*
@@ -340,6 +295,11 @@ err_rw_file_exact:
* a change was detected, assuming
* nothing else is touching it now
* off_reset 0: never reset if changed
+ *
+ * REAL_POS_IO is enabled by default in common.h
+ * and the fallback version was written for fun.
+ * You should just use the real one (REAL_POS_IO 1),
+ * since it is generally more reliable.
*/
ssize_t
@@ -359,6 +319,7 @@ prw(int fd, void *mem, size_t nrw,
off_t off_last;
#endif
int saved_errno = errno;
+ errno = 0;
if (io_args(fd, mem, nrw, off, rw_type)
== -1)
@@ -568,8 +529,6 @@ err_rw_over_nrw:
return -1;
}
-#if !defined(REAL_POS_IO) || \
- REAL_POS_IO < 1
off_t
lseek_on_eintr(int fd, off_t off, int whence,
int loop_eagain, int loop_eintr)
@@ -588,10 +547,9 @@ lseek_on_eintr(int fd, off_t off, int whence,
return old;
}
-#endif
/* two functions that reduce sloccount by
- * two hundred lines... no, now three. */
+ * two hundred lines */
int
if_err(int condition, int errval)
{
@@ -603,13 +561,6 @@ if_err(int condition, int errval)
return 1;
}
-/* technically pointless, but stylistically
- * pleasing alongside if_err chains.
- * use this one for syscalls that are
- * expected to set errno
- * also use it for non-system calls
- * that act like them, e.g. prw() or
- * rw_write_exact() */
int
if_err_sys(int condition)
{
@@ -665,10 +616,8 @@ close_warn(int *fd, char *s)
return 0;
}
-/* TODO: remove this. giant liability.
- make close calls always err instead,
- when they fail. otherwise we hide bugs!
- */
+/* TODO: remove this, and just check
+ * err on every close. */
void
close_no_err(int *fd)
{
@@ -685,7 +634,6 @@ close_no_err(int *fd)
/* TODO: make fd a pointer insttead
and automatically reset -1 here */
-/* BUT DO NOT reset -1 on error */
int
close_on_eintr(int fd)
{
@@ -732,11 +680,10 @@ fs_rename_at(int olddirfd, const char *old,
return renameat(olddirfd, old, newdirfd, new);
}
-/* secure open, based on
- * relative path to root
+/* secure open, based on relative path to root
*
- * always a fixed fd for /
- * see: rootfs()
+ * always a fixed fd for / see: rootfs()
+ * and fs_resolve_at()
*/
int
fs_open(const char *path, int flags)
@@ -751,12 +698,8 @@ fs_open(const char *path, int flags)
return fs_resolve_at(fs->rootfd, path + 1, flags);
}
-/* singleton function
- * that returns a fixed
- * descriptor of /
- *
- * used throughout, for
- * repeated integrity checks
+/* singleton function that returns a fixed descriptor of /
+ * used throughout, for repeated integrity checks
*/
struct filesystem *
rootfs(void)
@@ -778,8 +721,7 @@ rootfs(void)
return &global_fs;
}
-/* filesystem sandboxing.
- * (in userspace)
+/* filesystem sandboxing in userspace
*/
int
fs_resolve_at(int dirfd, const char *path, int flags)
@@ -818,10 +760,9 @@ fs_resolve_at(int dirfd, const char *path, int flags)
if (nextfd < 0)
goto err;
- /* close previous fd IF it is not the original input */
- if (curfd != dirfd) {
+ /* close previous fd if not the original input */
+ if (curfd != dirfd)
(void) close_on_eintr(curfd);
- }
curfd = nextfd;
nextfd = -1;
@@ -899,8 +840,6 @@ fs_open_component(int dirfd, const char *name,
(is_last ? flags : (O_RDONLY | O_DIRECTORY)) |
O_NOFOLLOW | O_CLOEXEC, (flags & O_CREAT) ? 0600 : 0);
- /* the patient always lies
- */
if (!is_last) {
if (if_err(fd < 0, EBADF) ||
@@ -972,9 +911,6 @@ fs_dirname_basename(const char *path,
/* portable wrapper for use of openat2 on linux,
* with fallback for others e.g. openbsd
- *
- * BONUS: arg checks
- * TODO: consider EINTR/EAGAIN retry loop
*/
int
openat2p(int dirfd, const char *path,
@@ -1025,8 +961,7 @@ retry:
}
int
-mkdirat_on_eintr( /* <-- say that 10 times to please the demon */
- int dirfd,
+mkdirat_on_eintr(int dirfd,
const char *path, mode_t mode)
{
int saved_errno = errno;
diff --git a/util/libreboot-utils/lib/mkhtemp.c b/util/libreboot-utils/lib/mkhtemp.c
index a669e208..0e0169e4 100644
--- a/util/libreboot-utils/lib/mkhtemp.c
+++ b/util/libreboot-utils/lib/mkhtemp.c
@@ -51,22 +51,6 @@ new_tmpdir(int *fd, char **path, char *tmpdir,
tmpdir, template);
}
-/* note: tmpdir is an override of TMPDIR or /tmp or /var/tmp */
-/* WARNING:
- * on error, *path (at **path) may be
- NULL, or if the error pertains to
- an actual TMPDIR, set. if set, it
- will be using *static* memory and
- must not be freed. on success,
- a pointer to heap memory is set
- instead.
- * see:
- * env_tmpdir()
- * this is for error reports if e.g.
- * TMPDIR isn't found (but is set)
- * if TMPDIR isn't set, it will
- * default to /tmp or /var/tmp
- */
int
new_tmp_common(int *fd, char **path, int type,
char *tmpdir, const char *template)
@@ -443,20 +427,8 @@ world_writeable_and_sticky(
goto sticky_hell;
}
- /* must be fully executable
- * by everyone, or openat2
- * becomes unreliable**
- *
- * TODO: loosen these, as a toggle.
- * execution rights isn't
- * really a requirement for
- * TMPDIR, except maybe search,
- * but this function will be
- * generalised at some point
- * for use in other tools
- * besides just mkhtemp.
- */
- /*
+ /* all of these checks are probably
+ * redundant (execution rights)
if (!(st.st_mode & S_IXUSR) ||
!(st.st_mode & S_IXGRP) ||
!(st.st_mode & S_IXOTH)) {
@@ -473,7 +445,7 @@ world_writeable_and_sticky(
if (bypass_all_sticky_checks)
goto sticky_heaven; /* normal == no security */
- /* unhinged leah mode:
+ /* extremely not-libc mode:
*/
if (st.st_mode & S_IWOTH) { /* world writeable */
@@ -488,9 +460,7 @@ world_writeable_and_sticky(
goto sticky_hell; /* not sticky */
}
- /* if anyone even looks at you funny, drop
- * everything on the floor and refuse to function
- */
+ /* for good measure */
if (faccessat(dirfd, ".", X_OK, AT_EACCESS) < 0)
goto sticky_hell;
@@ -503,7 +473,6 @@ world_writeable_and_sticky(
goto sticky_hell; /* heaven visa denied */
sticky_heaven:
-/* i like the one in hamburg better */
close_no_err(&dirfd);
errno = saved_errno;
@@ -515,10 +484,7 @@ sticky_hell:
if (errno == saved_errno)
errno = EPERM;
- saved_errno = errno;
-
close_no_err(&dirfd);
-
errno = saved_errno;
return 0;
@@ -909,11 +875,9 @@ retry_rand:
/* WARNING: **ONCE** per file.
*
- * !!! DO NOT RUN TWICE PER FILE. BEWARE OF THE DEMON !!!
- * watch out for spikes!
- */
-/* TODO: bad_flags can be negative, and is
- * ignored if it is. should we err instead?
+ * some of these checks will trip up
+ * if you do them twice; all of them
+ * only need to be done once anyway.
*/
int secure_file(int *fd,
struct stat *st,
@@ -945,7 +909,7 @@ int secure_file(int *fd,
if (check_seek) { /***********/
if (lseek(*fd, 0, SEEK_CUR) == (off_t)-1)
goto err_demons;
- } /* don't release the demon */
+ } /* don't release the demon! */
if (if_err(st->st_nlink != 1, ELOOP) ||
if_err(st->st_uid != geteuid() && geteuid() != 0, EPERM) ||
diff --git a/util/libreboot-utils/lib/num.c b/util/libreboot-utils/lib/num.c
index 92710c35..79d6b409 100644
--- a/util/libreboot-utils/lib/num.c
+++ b/util/libreboot-utils/lib/num.c
@@ -1,12 +1,8 @@
/* SPDX-License-Identifier: MIT
* Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
*
- * Numerical functions.
- * NOTE: randomness was moved to rand.c
- */
-
-/*
-TODO: properly handle errno in this file
+ * Non-randomisation-related numerical functions.
+ * For rand functions, see: rand.c
*/
#ifdef __OpenBSD__
@@ -27,42 +23,11 @@ TODO: properly handle errno in this file
#include "../include/common.h"
-/* TODO:
- * make this and errno handling more
- * flexible
-
- in particular:
- hextonum could be modified to
- write into a buffer instead,
- with the converted numbers,
- of an arbitrary length
- */
unsigned short
hextonum(char ch_s)
{
int saved_errno = errno;
- /* rlong() can return error,
- but preserves errno if no
- error. we need to detect
- this because it handles
- /dev/urandom sometimes
-
- therefore, if it's zero
- at start, we know if there
- was an err at the end, by
- return value zero, if errno
- was set; this is technically
- valid, since zero is also
- a valid random number!
-
- it's an edge case that i had
- to fix. i'll rewrite the code
- better later. for now, it
- should be ok.
- */
- errno = 0;
-
unsigned char ch;
size_t rval;
@@ -85,8 +50,6 @@ hextonum(char ch_s)
if (ch == '?' || ch == 'x') {
rset(&rval, sizeof(rval));
- if (errno > 0)
- goto err_hextonum;
goto hextonum_success;
}
diff --git a/util/libreboot-utils/lib/rand.c b/util/libreboot-utils/lib/rand.c
index 392ec2ba..58cb211e 100644
--- a/util/libreboot-utils/lib/rand.c
+++ b/util/libreboot-utils/lib/rand.c
@@ -20,6 +20,9 @@
#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>
@@ -69,48 +72,62 @@
* or your program dies.
*/
-#define ELOTTERY ECANCELED
+#define MAX_ALLOC (2 << 16)
int
-win_lottery(void)
+win_lottery(void) /* are u lucky? */
{
- int saved_errno = errno;
- size_t size1;
- char *s1 = NULL;
- size_t size2;
- char *s2 = NULL;
- size_t pool = BUFSIZ;
- int rval;
+ size_t size = rsize();
+ size_t size2 = rsize();
+ char *s = NULL;
+
+ if (size &&
+ size == size2 &&
+ size <= MAX_ALLOC << 1) {
+
+ if (!memcmp(s = mkrbuf(size << 1),
+ s + size, size))
+ size2 = 1; /* winner! */
+ else
+ size2 = 0;
+ } else {
+ return 0;
+ }
- rset(&size1, sizeof(size1));
- rset(&size2, sizeof(size2));
+ free_if_null(&s);
+ return (int)size2;
+}
- size1 %= pool, size2 %= pool;
- s1 = mkrstr(size1), s2 = mkrstr(size2);
+size_t
+rsize(void)
+{
+ size_t rval = 0;
- if (scmp(s1, s2, BUFSIZ + 2, &rval) < 0)
- goto err;
- if (rval == 0)
- goto win;
+ /* clamp rand to prevent modulo bias */
+ size_t limit = SIZE_MAX - (SIZE_MAX % MAX_ALLOC);
+
+ do {
+ rset(&rval, sizeof(rval));
+ } while (rval >= limit);
- free_if_null(&s1), free_if_null(&s2);
+ return rval % MAX_ALLOC;
+}
- fprintf(stderr, "Sorry, you lose! Try again.\n");
- return 0;
-win:
- free_if_null(&s1), free_if_null(&s2);
- err_no_cleanup(0, ELOTTERY,
- "Congratulations! you won the errno lottery");
+void *
+rmalloc(size_t *rval)
+{
+ /* clamp rand to prevent modulo bias */
+ size_t limit = SIZE_MAX - (SIZE_MAX % MAX_ALLOC);
- exit(1);
- return -1;
-err:
- /* the lottery won you */
- free_if_null(&s1), free_if_null(&s2);
- err_no_cleanup(0, EFAULT, "lottery won you");
- exit(1);
- return -1;
+ if (if_err(rval == NULL, EFAULT))
+ return NULL;
+
+ do {
+ rset(rval, sizeof(*rval));
+ } while (*rval >= limit || *rval == 0);
+
+ return mkrstr(*rval %= MAX_ALLOC);
}
char *
@@ -122,7 +139,7 @@ mkrstr(size_t n) /* emulates spkmodem-decode */
if (n == 0)
err_no_cleanup(0, EPERM, "mkrbuf: zero-byte request");
- if (n == SIZE_MAX)
+ if (n >= SIZE_MAX - 1)
err_no_cleanup(0, EOVERFLOW, "mkrbuf: overflow");
if (if_err((s = mkrbuf(n + 1)) == NULL, EFAULT))
@@ -145,6 +162,9 @@ mkrbuf(size_t n)
if (n == 0)
err_no_cleanup(0, EPERM, "mkrbuf: zero-byte request");
+ if (n >= SIZE_MAX - 1)
+ err_no_cleanup(0, EOVERFLOW, "integer overflow in mkrbuf");
+
if ((buf = malloc(n)) == NULL)
err_no_cleanup(0, ENOMEM, "mkrbuf: malloc");
diff --git a/util/libreboot-utils/lib/string.c b/util/libreboot-utils/lib/string.c
index 0329c6c3..b639f0a4 100644
--- a/util/libreboot-utils/lib/string.c
+++ b/util/libreboot-utils/lib/string.c
@@ -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,
diff --git a/util/libreboot-utils/lottery.c b/util/libreboot-utils/lottery.c
new file mode 100644
index 00000000..4c3b0f70
--- /dev/null
+++ b/util/libreboot-utils/lottery.c
@@ -0,0 +1,32 @@
+/* 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 "include/common.h"
+
+int
+main(int argc, char *argv[])
+{
+ int lucky;
+#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);
+
+ lucky = win_lottery();
+
+ printf("%s\n", lucky ? "You won!" : "You lose! Sorry!");
+ return lucky ? EXIT_SUCCESS : EXIT_FAILURE;
+}/*
+
+ ( >:3 )
+ /| |\
+ / \ */
diff --git a/util/libreboot-utils/mkhtemp.c b/util/libreboot-utils/mkhtemp.c
index 261227cb..7564800a 100644
--- a/util/libreboot-utils/mkhtemp.c
+++ b/util/libreboot-utils/mkhtemp.c
@@ -1,47 +1,17 @@
/* SPDX-License-Identifier: MIT
* Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
*
- * WORK IN PROGRESS (proof of concept), or, v0.0000001
- *
- * Mkhtemp - Hardened mktemp. Create files and directories
- * randomly as determined by user's TMPDIR, or fallback. It
- * attemps to provide mitigation against several TOCTOU-based
- * attacks e.g. directory rename / symlink attacks, and it
- * generally provides much higher strictness than previous
- * implementations such as mktemp, mkstemp or even mkdtemp.
- *
- * It uses several modern features by default, e.g. openat2
- * and O_TMPFILE on Linux, with additional hardening; BSD
- * projects only have openat so the code uses that there.
+ * Hardened mktemp (mkhtemp!)
*
- * Many programs rely on mktemp, and they use TMPDIR in a way
- * that is quite insecure. Mkhtemp intends to change that,
- * quite dramatically, with: userspace sandbox (and use OS
- * level options e.g. OBSD pledge where available), constant
- * identity/ownership checks on files, MUCH stricter ownership
- * restrictions (e.g. enforce sticky bit policy on world-
- * writeable tmpdirs), preventing operation on other people's
- * files (only your own files) - even root is restricted,
- * depending on how the code is compiled. Please read the code.
- *
- * This is the utility version, which makes use of the also-
- * included library. No docs yet - source code are the docs,
- * and the (ever evolving, and hardening) specification.
- *
- * This was written from scratch, for use in nvmutil, and
- * it is designed to be portable (BSD, Linux). Patches
- * very much welcome.
+ * WORK IN PROGRESS (proof of concept), or, v0.0000001
+ * DO NOT PUT THIS IN YOUR LINUX DISTRO YET.
*
- * WARNING: This is MUCH stricter than every other mktemp
- * implementation, even more so than mkdtemp or
- * the OpenBSD version of mkstemp. It *will* break,
- * or more specifically, reveal the flaws in, almost
- * every major critical infrastructure, because most
- * people already use mktemp extremely insecurely.
+ * I will remove this notice when the code is mature, and
+ * probably contact several of your projects myself.
*
- * This tool is written by me, for me, and also Libreboot, but
- * it will be summitted for review to various Linux distros
- * and BSD projects once it has reached maturity.
+ * See README. This is an ongoing project; no proper docs
+ * yet, and no manpage (yet!) - the code is documentation,
+ * while the specification that it implements evolves.
*/
#if defined(__linux__) && !defined(_GNU_SOURCE)
@@ -187,25 +157,6 @@ err_usage:
"usage: %s [-d] [-p dir] [template]\n", getnvmprogname());
}/*
-
( >:3 )
/| |\
- / \
-
-
-
-
-
- */
-
-
-
-
-
-
-
-
-
-
-
-
+ / \ */