mirror of
https://codeberg.org/libreboot/lbmk.git
synced 2026-03-25 13:29:03 +02:00
util/mkhtemp: new utility (hardened mktemp)
part of the same code library as nvmutil. as part of this, i renamed util/nvmutil to util/libreboot-utils/ because it is now a multi-utility codebase. this is more efficient, since i also wish to use mkhtemp (function) in nvmutil. Signed-off-by: Leah Rowe <leah@libreboot.org>
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
|
||||
cbcfgsdir="config/coreboot"
|
||||
tmpromdel="$XBMK_CACHE/DO_NOT_FLASH"
|
||||
nvmutil="util/nvmutil/nvmutil"
|
||||
nvmutil="util/libreboot-utils/nvmutil"
|
||||
ifdtool="elf/coreboot/default/ifdtool"
|
||||
|
||||
checkvars="CONFIG_GBE_BIN_PATH"
|
||||
@@ -197,8 +197,8 @@ modify_mac()
|
||||
x_ cp "${CONFIG_GBE_BIN_PATH##*../}" "$xbtmp/gbe"
|
||||
|
||||
if [ -n "$new_mac" ] && [ "$new_mac" != "restore" ]; then
|
||||
x_ make -C util/nvmutil clean
|
||||
x_ make -C util/nvmutil
|
||||
x_ make -C util/libreboot-utils clean
|
||||
x_ make -C util/libreboot-utils
|
||||
|
||||
x_ "$nvmutil" "$xbtmp/gbe" setmac "$new_mac"
|
||||
fi
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/nvm
|
||||
/nvmutil
|
||||
/mkhtemp
|
||||
*.bin
|
||||
*.o
|
||||
*.d
|
||||
@@ -24,8 +24,9 @@ STRICT = $(WARN) -std=c90 -pedantic -Werror
|
||||
HELLFLAGS = $(STRICT) -Weverything
|
||||
|
||||
PROG = nvmutil
|
||||
PROGMKH = mkhtemp
|
||||
|
||||
OBJS = \
|
||||
OBJS_NVMUTIL = \
|
||||
obj/nvmutil.o \
|
||||
obj/lib/state.o \
|
||||
obj/lib/file.o \
|
||||
@@ -38,17 +39,28 @@ OBJS = \
|
||||
obj/lib/word.o \
|
||||
obj/lib/mkhtemp.o
|
||||
|
||||
OBJS_MKHTEMP = \
|
||||
obj/mkhtemp.o \
|
||||
obj/lib/file.o \
|
||||
obj/lib/string.o \
|
||||
obj/lib/num.o \
|
||||
obj/lib/mkhtemp.o
|
||||
|
||||
# default mode
|
||||
CFLAGS_MODE = $(PORTABLE)
|
||||
CC_MODE = $(CC)
|
||||
|
||||
all: $(PROG)
|
||||
all: $(PROG) $(PROGMKH)
|
||||
|
||||
$(PROG): $(OBJS)
|
||||
$(CC_MODE) $(OBJS) -o $(PROG) $(LDFLAGS)
|
||||
$(PROG): $(OBJS_NVMUTIL)
|
||||
$(CC_MODE) $(OBJS_NVMUTIL) -o $(PROG) $(LDFLAGS)
|
||||
|
||||
$(PROGMKH): $(OBJS_MKHTEMP)
|
||||
$(CC_MODE) $(OBJS_MKHTEMP) -o $(PROGMKH) $(LDFLAGS)
|
||||
|
||||
# ensure obj directory exists
|
||||
$(OBJS): obj
|
||||
$(OBJS_NVMUTIL): obj
|
||||
$(OBJS_MKHTEMP): obj
|
||||
|
||||
obj:
|
||||
mkdir obj || true
|
||||
@@ -59,6 +71,9 @@ obj:
|
||||
obj/nvmutil.o: nvmutil.c
|
||||
$(CC_MODE) $(CFLAGS_MODE) -c nvmutil.c -o obj/nvmutil.o
|
||||
|
||||
obj/mkhtemp.o: mkhtemp.c
|
||||
$(CC_MODE) $(CFLAGS_MODE) -c mkhtemp.c -o obj/mkhtemp.o
|
||||
|
||||
# library/helper objects
|
||||
|
||||
obj/lib/state.o: lib/state.c
|
||||
@@ -93,16 +108,19 @@ obj/lib/mkhtemp.o: lib/mkhtemp.c
|
||||
|
||||
# install
|
||||
|
||||
install: $(PROG)
|
||||
install: $(PROG) $(PROGMKH)
|
||||
$(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)
|
||||
|
||||
uninstall:
|
||||
rm -f $(DESTDIR)$(PREFIX)/bin/$(PROG)
|
||||
rm -f $(DESTDIR)$(PREFIX)/bin/$(PROGMKH)
|
||||
|
||||
clean:
|
||||
rm -f $(PROG) $(OBJS)
|
||||
rm -f $(PROG) $(PROGMKH) $(OBJS_NVMUTIL) $(OBJS_MKHTEMP)
|
||||
|
||||
distclean: clean
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
* Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org>
|
||||
|
||||
TODO: this file should be split, into headers for each
|
||||
C source file specifically. it was originally just
|
||||
for nvmutil, until i added mkhtemp to the mix
|
||||
*/
|
||||
|
||||
|
||||
@@ -495,8 +499,8 @@ const char *getnvmprogname(void);
|
||||
|
||||
int new_tmpfile(int *fd, char **path);
|
||||
int new_tmpdir(int *fd, char **path);
|
||||
static int new_tmp_common(int *fd, char **path, int type);
|
||||
static int mkhtemp_try_create(int dirfd,
|
||||
int new_tmp_common(int *fd, char **path, int type);
|
||||
int mkhtemp_try_create(int dirfd,
|
||||
struct stat *st_dir_initial,
|
||||
char *fname_copy,
|
||||
char *p,
|
||||
@@ -39,7 +39,7 @@ new_tmpdir(int *fd, char **path)
|
||||
return new_tmp_common(fd, path, MKHTEMP_DIR);
|
||||
}
|
||||
|
||||
static int
|
||||
int
|
||||
new_tmp_common(int *fd, char **path, int type)
|
||||
{
|
||||
#if defined(PATH_LEN) && \
|
||||
@@ -466,13 +466,13 @@ sticky_hell:
|
||||
/* mk(h)temp - hardened mktemp.
|
||||
* like mkstemp, but (MUCH) harder.
|
||||
*
|
||||
* designed to resist TOCTOU attacsk
|
||||
* designed to resist TOCTOU attacks
|
||||
* e.g. directory race / symlink attack
|
||||
*
|
||||
* extremely strict and even implements
|
||||
* some limited userspace-level sandboxing,
|
||||
* similar to openbsd unveil (which you
|
||||
* can also use with this in your program)
|
||||
* similar in spirit to openbsd unveil,
|
||||
* though unveil is from kernel space.
|
||||
*
|
||||
* supports both files and directories.
|
||||
* file: type = MKHTEMP_FILE (0)
|
||||
@@ -661,7 +661,7 @@ success:
|
||||
return (*fd >= 0) ? *fd : -1;
|
||||
}
|
||||
|
||||
static int
|
||||
int
|
||||
mkhtemp_try_create(int dirfd,
|
||||
struct stat *st_dir_initial,
|
||||
char *fname_copy,
|
||||
@@ -152,33 +152,6 @@ xstatus(void)
|
||||
return x;
|
||||
}
|
||||
|
||||
/* early init functions that
|
||||
should not access state
|
||||
WARNING:
|
||||
does not do cleanup. only
|
||||
call this during pre-init
|
||||
*/
|
||||
void
|
||||
err_no_cleanup(int nvm_errval, const char *msg, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
if (errno == 0)
|
||||
errno = nvm_errval;
|
||||
if (!errno)
|
||||
errno = ECANCELED;
|
||||
|
||||
fprintf(stderr, "nvmutil: ");
|
||||
|
||||
va_start(args, msg);
|
||||
vfprintf(stderr, msg, args);
|
||||
va_end(args);
|
||||
|
||||
fprintf(stderr, ": %s\n", strerror(errno));
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void
|
||||
err(int nvm_errval, const char *msg, ...)
|
||||
{
|
||||
@@ -8,8 +8,11 @@
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../include/common.h"
|
||||
@@ -112,3 +115,32 @@ slen(const char *s,
|
||||
*rval = ch;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* the one for nvmutil state is in state.c */
|
||||
/* this one just exits */
|
||||
void
|
||||
err_no_cleanup(int nvm_errval, const char *msg, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
#if defined(__OpenBSD__) && defined(OpenBSD)
|
||||
#if (OpenBSD) >= 509
|
||||
if (pledge("stdio", NULL) == -1)
|
||||
fprintf(stderr, "pledge failure during exit");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (!errno)
|
||||
errno = ECANCELED;
|
||||
|
||||
fprintf(stderr, "nvmutil: ");
|
||||
|
||||
va_start(args, msg);
|
||||
vfprintf(stderr, msg, args);
|
||||
va_end(args);
|
||||
|
||||
fprintf(stderr, ": %s\n", strerror(errno));
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
139
util/libreboot-utils/mkhtemp.c
Normal file
139
util/libreboot-utils/mkhtemp.c
Normal file
@@ -0,0 +1,139 @@
|
||||
/* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#if defined(__linux__) && !defined(_GNU_SOURCE)
|
||||
/* for openat2 on linux */
|
||||
#define _GNU_SOURCE 1
|
||||
#endif
|
||||
|
||||
#ifdef __OpenBSD__
|
||||
#include <sys/param.h> /* pledge(2) */
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "include/common.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
char *s = NULL;
|
||||
int fd = -1;
|
||||
char c;
|
||||
int type = MKHTEMP_FILE;
|
||||
size_t len;
|
||||
|
||||
#if defined (PATH_LEN) && \
|
||||
(PATH_LEN) >= 256
|
||||
size_t maxlen = PATH_LEN;
|
||||
#else
|
||||
size_t maxlen = 4096;
|
||||
#endif
|
||||
|
||||
/* https://man.openbsd.org/pledge.2 */
|
||||
#if defined(__OpenBSD__) && defined(OpenBSD)
|
||||
#if (OpenBSD) >= 509
|
||||
if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
|
||||
err_no_cleanup(errno, "pledge, main");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
while ((c =
|
||||
getopt(argc, argv, "d")) != -1) {
|
||||
|
||||
switch(c) {
|
||||
case 'd':
|
||||
|
||||
type = MKHTEMP_DIR;
|
||||
break;
|
||||
default:
|
||||
|
||||
err_no_cleanup(EINVAL,
|
||||
"usage: mkhtemp [-d]\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (new_tmp_common(&fd, &s, type) < 0)
|
||||
err_no_cleanup(errno, NULL);
|
||||
|
||||
#if defined(__OpenBSD__) && defined(OpenBSD)
|
||||
#if (OpenBSD) >= 509
|
||||
if (pledge("stdio", NULL) == -1)
|
||||
err_no_cleanup(errno, "pledge, exit");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (s == NULL)
|
||||
err_no_cleanup(EFAULT, "bad string initialisation");
|
||||
|
||||
if (*s == '\0')
|
||||
err_no_cleanup(EFAULT, "empty string initialisation");
|
||||
|
||||
if (slen(s, maxlen, &len) < 0)
|
||||
err_no_cleanup(EFAULT, "unterminated string initialisation");
|
||||
|
||||
printf("%s\n", s);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}/*
|
||||
|
||||
|
||||
( >:3 )
|
||||
/| |\
|
||||
/ \
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
*/
|
||||
@@ -35,16 +35,6 @@ main(int argc, char *argv[])
|
||||
|
||||
size_t c;
|
||||
|
||||
int rval;
|
||||
char *test = NULL;
|
||||
int fd = -1;
|
||||
rval = new_tmpdir(&fd, &test);
|
||||
if (rval < 0)
|
||||
err_no_cleanup(errno, "TESTERR: ");
|
||||
|
||||
printf("TEST: %s\n", test);
|
||||
exit(1);
|
||||
|
||||
/* https://man.openbsd.org/pledge.2
|
||||
https://man.openbsd.org/unveil.2 */
|
||||
#if defined(__OpenBSD__) && defined(OpenBSD)
|
||||
Reference in New Issue
Block a user