summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--util/nvmutil/include/common.h2
-rw-r--r--util/nvmutil/lib/file.c121
2 files changed, 83 insertions, 40 deletions
diff --git a/util/nvmutil/include/common.h b/util/nvmutil/include/common.h
index a546e464..4aca1772 100644
--- a/util/nvmutil/include/common.h
+++ b/util/nvmutil/include/common.h
@@ -494,6 +494,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,
struct stat *st_dir_initial,
char *fname_copy,
diff --git a/util/nvmutil/lib/file.c b/util/nvmutil/lib/file.c
index b605d488..5dfe5947 100644
--- a/util/nvmutil/lib/file.c
+++ b/util/nvmutil/lib/file.c
@@ -1,7 +1,8 @@
/* SPDX-License-Identifier: MIT
* Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
*
- * Pathless i/o
+ * Pathless i/o, and some stuff you probably never saw.
+ * Be nice to the demon.
*/
#if defined(__linux__) && !defined(_GNU_SOURCE)
@@ -241,17 +242,62 @@ err_fsync_dir:
return -1;
}
-/* hardened tmpfile creation
+/* hardened tmpfile and tmpdir creation.
+ * obsessively hardened, to the point that
+ * you could even say it is unhinged.
+ *
+ * much stricter than mkstemp.
+ *
+ * userspace sandboxing. TOCTOU-aware,
+ * much stricter than typical libc/mkstemp.
+ * TODO: write docs! right now the documentation
+ * is both tthe code and the specification.
+ * this code will likely be hardened more, over
+ * time, until a matured stable release can be
+ * made. i intend for this to go in linux distros
+ * and BSD source trees eventually, as a standalone
+ * utility.
+ *
+ * NOTE TO LINUX DISTROS / BSDs: more testing
+ * and auditing needed before putting this
+ * in your project. this comment will be removed
+ * after the code has matured for a while. this
+ * is a rough implementation, already carefully
+ * audited by one person: me
+ *
+ * expect bugs. one day, when this is ready,
+ * i will make a lot more of the options configurable
+ * at runtime, and add a special compatibility mode
+ * so that it can also run like regular mktemp,
+ * for compatibility; running mkhtemp with all the
+ * hardening means you get very non-standard behaviour,
+ * e.g. it's much stricter about ownership/permissions,
+ * sticky bits, and regards as fault conditions, what
+ * would be considered normal in mkstemp/mktemp.
+ *
+ * a full manual and specification will be written when
+ * this code is fully matured, and then i will probably
+ * start spamming your mailing lists myself ;)
*/
/* returns opened fd, sets fd and *path at pointers *fd and *path */
/* also sets external stat */
+
int
new_tmpfile(int *fd, char **path)
{
-/* TODO:
- * directory support (currently only files)
- */
+ return new_tmp_common(fd, path, MKHTEMP_FILE);
+}
+
+int
+new_tmpdir(int *fd, char **path)
+{
+ return new_tmp_common(fd, path, MKHTEMP_DIR);
+}
+
+static int
+new_tmp_common(int *fd, char **path, int type)
+{
#if defined(PATH_LEN) && \
(PATH_LEN) >= 256
size_t maxlen = PATH_LEN;
@@ -267,7 +313,7 @@ new_tmpfile(int *fd, char **path)
int close_errno;
size_t dirlen;
size_t destlen;
- char *dest = NULL; /* final path */
+ char *dest = NULL; /* final path (will be written into "path") */
int saved_errno = errno;
int dirfd = -1;
const char *fname = NULL;
@@ -276,26 +322,25 @@ new_tmpfile(int *fd, char **path)
if (path == NULL || fd == NULL) {
errno = EFAULT;
- goto err_new_tmpfile;
+ goto err;
}
- /* don't mess with someone's file
- * if already opened
- */
+ /* don't mess with someone elses file */
if (*fd >= 0) {
errno = EEXIST;
- goto err_new_tmpfile;
+ goto err;
}
/* regarding **path:
- * the pointer (to the pointer)
- * must nott be null, but we don't
- * care about the pointer it points
- * to. you should expect it to be
- * replaced upon successful return
- *
- * (on error, it will not be touched)
- */
+ * the pointer (to the pointer)
+ * must nott be null, but we don't
+ * care about the pointer it points
+ * to. you should expect it to be
+ * replaced upon successful return
+ *
+ * (on error, it will not be touched)
+ */
+
*fd = -1;
@@ -306,30 +351,28 @@ new_tmpfile(int *fd, char **path)
tmpdir = env_tmpdir(0);
#endif
if (tmpdir == NULL)
- goto err_new_tmpfile;
+ goto err;
if (slen(tmpdir, maxlen, &dirlen) < 0)
- goto err_new_tmpfile;
+ goto err;
if (*tmpdir == '\0')
- goto err_new_tmpfile;
+ goto err;
if (*tmpdir != '/')
- goto err_new_tmpfile;
+ goto err;
- /* using sizeof (not slen) adds an extra byte,
- * useful because we either want '.' or '/'
+ /* sizeof adds an extra byte, useful
+ * because we also want '.' or '/'
*/
destlen = dirlen + sizeof(suffix);
- if (destlen > maxlen - 1) { /* -1 for NULL */
-
+ if (destlen > maxlen - 1) {
errno = EOVERFLOW;
- goto err_new_tmpfile;
+ goto err;
}
- dest = malloc(destlen + 1); /* +1 for NULL */
+ dest = malloc(destlen + 1);
if (dest == NULL) {
-
errno = ENOMEM;
- goto err_new_tmpfile;
+ goto err;
}
memcpy(dest, tmpdir, dirlen);
@@ -337,21 +380,20 @@ new_tmpfile(int *fd, char **path)
memcpy(dest + dirlen + 1, suffix, sizeof(suffix) - 1);
*(dest + destlen) = '\0';
- /* new tmpfile name */
fname = dest + dirlen + 1;
dirfd = fs_open(tmpdir,
O_RDONLY | O_DIRECTORY);
if (dirfd < 0)
- goto err_new_tmpfile;
+ goto err;
if (fstat(dirfd, &st_dir_initial) < 0)
- goto err_new_tmpfile;
+ goto err;
*fd = mkhtemp(fd, &st, dest, dirfd,
- fname, &st_dir_initial, MKHTEMP_FILE);
+ fname, &st_dir_initial, type);
if (*fd < 0)
- goto err_new_tmpfile;
+ goto err;
if (dirfd >= 0) {
close_errno = errno;
@@ -361,12 +403,11 @@ new_tmpfile(int *fd, char **path)
}
errno = saved_errno;
-
*path = dest;
return 0;
-
-err_new_tmpfile:
+
+err:
if (errno != saved_errno)
saved_errno = errno;
@@ -393,10 +434,10 @@ err_new_tmpfile:
}
errno = saved_errno;
-
return -1;
}
+
/* hardened TMPDIR parsing
*/