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"
|
cbcfgsdir="config/coreboot"
|
||||||
tmpromdel="$XBMK_CACHE/DO_NOT_FLASH"
|
tmpromdel="$XBMK_CACHE/DO_NOT_FLASH"
|
||||||
nvmutil="util/nvmutil/nvmutil"
|
nvmutil="util/libreboot-utils/nvmutil"
|
||||||
ifdtool="elf/coreboot/default/ifdtool"
|
ifdtool="elf/coreboot/default/ifdtool"
|
||||||
|
|
||||||
checkvars="CONFIG_GBE_BIN_PATH"
|
checkvars="CONFIG_GBE_BIN_PATH"
|
||||||
@@ -197,8 +197,8 @@ modify_mac()
|
|||||||
x_ cp "${CONFIG_GBE_BIN_PATH##*../}" "$xbtmp/gbe"
|
x_ cp "${CONFIG_GBE_BIN_PATH##*../}" "$xbtmp/gbe"
|
||||||
|
|
||||||
if [ -n "$new_mac" ] && [ "$new_mac" != "restore" ]; then
|
if [ -n "$new_mac" ] && [ "$new_mac" != "restore" ]; then
|
||||||
x_ make -C util/nvmutil clean
|
x_ make -C util/libreboot-utils clean
|
||||||
x_ make -C util/nvmutil
|
x_ make -C util/libreboot-utils
|
||||||
|
|
||||||
x_ "$nvmutil" "$xbtmp/gbe" setmac "$new_mac"
|
x_ "$nvmutil" "$xbtmp/gbe" setmac "$new_mac"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/nvm
|
/nvm
|
||||||
/nvmutil
|
/nvmutil
|
||||||
|
/mkhtemp
|
||||||
*.bin
|
*.bin
|
||||||
*.o
|
*.o
|
||||||
*.d
|
*.d
|
||||||
@@ -24,8 +24,9 @@ STRICT = $(WARN) -std=c90 -pedantic -Werror
|
|||||||
HELLFLAGS = $(STRICT) -Weverything
|
HELLFLAGS = $(STRICT) -Weverything
|
||||||
|
|
||||||
PROG = nvmutil
|
PROG = nvmutil
|
||||||
|
PROGMKH = mkhtemp
|
||||||
|
|
||||||
OBJS = \
|
OBJS_NVMUTIL = \
|
||||||
obj/nvmutil.o \
|
obj/nvmutil.o \
|
||||||
obj/lib/state.o \
|
obj/lib/state.o \
|
||||||
obj/lib/file.o \
|
obj/lib/file.o \
|
||||||
@@ -38,17 +39,28 @@ OBJS = \
|
|||||||
obj/lib/word.o \
|
obj/lib/word.o \
|
||||||
obj/lib/mkhtemp.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
|
# default mode
|
||||||
CFLAGS_MODE = $(PORTABLE)
|
CFLAGS_MODE = $(PORTABLE)
|
||||||
CC_MODE = $(CC)
|
CC_MODE = $(CC)
|
||||||
|
|
||||||
all: $(PROG)
|
all: $(PROG) $(PROGMKH)
|
||||||
|
|
||||||
$(PROG): $(OBJS)
|
$(PROG): $(OBJS_NVMUTIL)
|
||||||
$(CC_MODE) $(OBJS) -o $(PROG) $(LDFLAGS)
|
$(CC_MODE) $(OBJS_NVMUTIL) -o $(PROG) $(LDFLAGS)
|
||||||
|
|
||||||
|
$(PROGMKH): $(OBJS_MKHTEMP)
|
||||||
|
$(CC_MODE) $(OBJS_MKHTEMP) -o $(PROGMKH) $(LDFLAGS)
|
||||||
|
|
||||||
# ensure obj directory exists
|
# ensure obj directory exists
|
||||||
$(OBJS): obj
|
$(OBJS_NVMUTIL): obj
|
||||||
|
$(OBJS_MKHTEMP): obj
|
||||||
|
|
||||||
obj:
|
obj:
|
||||||
mkdir obj || true
|
mkdir obj || true
|
||||||
@@ -59,6 +71,9 @@ obj:
|
|||||||
obj/nvmutil.o: nvmutil.c
|
obj/nvmutil.o: nvmutil.c
|
||||||
$(CC_MODE) $(CFLAGS_MODE) -c nvmutil.c -o obj/nvmutil.o
|
$(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
|
# library/helper objects
|
||||||
|
|
||||||
obj/lib/state.o: lib/state.c
|
obj/lib/state.o: lib/state.c
|
||||||
@@ -93,16 +108,19 @@ obj/lib/mkhtemp.o: lib/mkhtemp.c
|
|||||||
|
|
||||||
# install
|
# install
|
||||||
|
|
||||||
install: $(PROG)
|
install: $(PROG) $(PROGMKH)
|
||||||
$(INSTALL) -d $(DESTDIR)$(PREFIX)/bin
|
$(INSTALL) -d $(DESTDIR)$(PREFIX)/bin
|
||||||
$(INSTALL) $(PROG) $(DESTDIR)$(PREFIX)/bin/$(PROG)
|
$(INSTALL) $(PROG) $(DESTDIR)$(PREFIX)/bin/$(PROG)
|
||||||
chmod 755 $(DESTDIR)$(PREFIX)/bin/$(PROG)
|
chmod 755 $(DESTDIR)$(PREFIX)/bin/$(PROG)
|
||||||
|
$(INSTALL) $(PROGMKH) $(DESTDIR)$(PREFIX)/bin/$(PROGMKH)
|
||||||
|
chmod 755 $(DESTDIR)$(PREFIX)/bin/$(PROGMKH)
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
rm -f $(DESTDIR)$(PREFIX)/bin/$(PROG)
|
rm -f $(DESTDIR)$(PREFIX)/bin/$(PROG)
|
||||||
|
rm -f $(DESTDIR)$(PREFIX)/bin/$(PROGMKH)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(PROG) $(OBJS)
|
rm -f $(PROG) $(PROGMKH) $(OBJS_NVMUTIL) $(OBJS_MKHTEMP)
|
||||||
|
|
||||||
distclean: clean
|
distclean: clean
|
||||||
|
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
* Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org>
|
* 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_tmpfile(int *fd, char **path);
|
||||||
int new_tmpdir(int *fd, char **path);
|
int new_tmpdir(int *fd, char **path);
|
||||||
static int new_tmp_common(int *fd, char **path, int type);
|
int new_tmp_common(int *fd, char **path, int type);
|
||||||
static int mkhtemp_try_create(int dirfd,
|
int mkhtemp_try_create(int dirfd,
|
||||||
struct stat *st_dir_initial,
|
struct stat *st_dir_initial,
|
||||||
char *fname_copy,
|
char *fname_copy,
|
||||||
char *p,
|
char *p,
|
||||||
@@ -39,7 +39,7 @@ new_tmpdir(int *fd, char **path)
|
|||||||
return new_tmp_common(fd, path, MKHTEMP_DIR);
|
return new_tmp_common(fd, path, MKHTEMP_DIR);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
int
|
||||||
new_tmp_common(int *fd, char **path, int type)
|
new_tmp_common(int *fd, char **path, int type)
|
||||||
{
|
{
|
||||||
#if defined(PATH_LEN) && \
|
#if defined(PATH_LEN) && \
|
||||||
@@ -466,13 +466,13 @@ sticky_hell:
|
|||||||
/* mk(h)temp - hardened mktemp.
|
/* mk(h)temp - hardened mktemp.
|
||||||
* like mkstemp, but (MUCH) harder.
|
* like mkstemp, but (MUCH) harder.
|
||||||
*
|
*
|
||||||
* designed to resist TOCTOU attacsk
|
* designed to resist TOCTOU attacks
|
||||||
* e.g. directory race / symlink attack
|
* e.g. directory race / symlink attack
|
||||||
*
|
*
|
||||||
* extremely strict and even implements
|
* extremely strict and even implements
|
||||||
* some limited userspace-level sandboxing,
|
* some limited userspace-level sandboxing,
|
||||||
* similar to openbsd unveil (which you
|
* similar in spirit to openbsd unveil,
|
||||||
* can also use with this in your program)
|
* though unveil is from kernel space.
|
||||||
*
|
*
|
||||||
* supports both files and directories.
|
* supports both files and directories.
|
||||||
* file: type = MKHTEMP_FILE (0)
|
* file: type = MKHTEMP_FILE (0)
|
||||||
@@ -661,7 +661,7 @@ success:
|
|||||||
return (*fd >= 0) ? *fd : -1;
|
return (*fd >= 0) ? *fd : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
int
|
||||||
mkhtemp_try_create(int dirfd,
|
mkhtemp_try_create(int dirfd,
|
||||||
struct stat *st_dir_initial,
|
struct stat *st_dir_initial,
|
||||||
char *fname_copy,
|
char *fname_copy,
|
||||||
@@ -152,33 +152,6 @@ xstatus(void)
|
|||||||
return x;
|
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
|
void
|
||||||
err(int nvm_errval, const char *msg, ...)
|
err(int nvm_errval, const char *msg, ...)
|
||||||
{
|
{
|
||||||
@@ -8,8 +8,11 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <stdarg.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "../include/common.h"
|
#include "../include/common.h"
|
||||||
@@ -112,3 +115,32 @@ slen(const char *s,
|
|||||||
*rval = ch;
|
*rval = ch;
|
||||||
return 0;
|
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;
|
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/pledge.2
|
||||||
https://man.openbsd.org/unveil.2 */
|
https://man.openbsd.org/unveil.2 */
|
||||||
#if defined(__OpenBSD__) && defined(OpenBSD)
|
#if defined(__OpenBSD__) && defined(OpenBSD)
|
||||||
Reference in New Issue
Block a user