summaryrefslogtreecommitdiff
path: root/util/nvmutil/lib/file.c
diff options
context:
space:
mode:
Diffstat (limited to 'util/nvmutil/lib/file.c')
-rw-r--r--util/nvmutil/lib/file.c195
1 files changed, 117 insertions, 78 deletions
diff --git a/util/nvmutil/lib/file.c b/util/nvmutil/lib/file.c
index b605d488..6546c252 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
*/
@@ -412,16 +453,20 @@ env_tmpdir(int bypass_all_sticky_checks)
if (t != NULL && *t != '\0') {
if (tmpdir_policy(t,
- &allow_noworld_unsticky) == 0) {
-
- if (world_writeable_and_sticky(t,
- allow_noworld_unsticky,
- bypass_all_sticky_checks)) {
+ &allow_noworld_unsticky) < 0) {
+ errno = EPERM;
+ return NULL; /* errno already set */
+ }
- errno = saved_errno;
- return t;
- }
+ if (!world_writeable_and_sticky(t,
+ allow_noworld_unsticky,
+ bypass_all_sticky_checks)) {
+ errno = EPERM;
+ return NULL;
}
+
+ errno = saved_errno;
+ return t;
}
allow_noworld_unsticky = 0;
@@ -629,9 +674,6 @@ world_writeable_and_sticky(
/* unhinged leah mode:
*/
- if (is_owner(&st) < 0)
- goto sticky_hell;
-
if (st.st_mode & S_IWOTH) { /* world writeable */
/* if world-writeable, only
@@ -801,8 +843,7 @@ mkhtemp(int *fd,
return -1;
}
- if (fname_len > len ||
- fname_len > (len - xc)) {
+ if (fname_len > len) {
errno = EOVERFLOW;
return -1;
}
@@ -927,8 +968,8 @@ mkhtemp_try_create(int dirfd,
goto err;
/* ^ NOTE: opening the directory here
- will never set errno=EEXIST,
- since we're not creating it */
+ will never set errno=EEXIST,
+ since we're not creating it */
dir_created = 1;
@@ -936,6 +977,11 @@ mkhtemp_try_create(int dirfd,
if (fd_verify_dir_identity(dirfd, st_dir_initial) < 0)
goto err;
+ *fd = openat2p(dirfd, fname_copy,
+ O_RDONLY | O_DIRECTORY | O_CLOEXEC, 0);
+ if (*fd < 0)
+ goto err;
+
if (fstat(*fd, &st_open) < 0)
goto err;
@@ -944,11 +990,6 @@ mkhtemp_try_create(int dirfd,
goto err;
}
- *fd = openat2p(dirfd, fname_copy,
- O_RDONLY | O_DIRECTORY | O_CLOEXEC, 0);
- if (*fd < 0)
- goto err;
-
/* NOTE: could check nlink count here,
* but it's not very reliable here. skipped.
*/
@@ -1960,25 +2001,22 @@ int
fs_resolve_at(int dirfd, const char *path, int flags)
{
int nextfd = -1;
+ int curfd;
const char *p;
- char name[256]; /* TODO: make configurable */
+ char name[256];
int saved_errno = errno;
- int saved_close_errno;
int r;
int is_last;
- if (dirfd < 0 ||
- path == NULL ||
- *path == '\0') {
-
+ if (dirfd < 0 || path == NULL || *path == '\0') {
errno = EINVAL;
return -1;
}
p = path;
+ curfd = dirfd; /* start here */
for (;;) {
-
r = fs_next_component(&p, name, sizeof(name));
if (r < 0)
goto err;
@@ -1987,30 +2025,32 @@ fs_resolve_at(int dirfd, const char *path, int flags)
is_last = (*p == '\0');
- nextfd = fs_open_component(dirfd,
- name, flags, is_last);
+ nextfd = fs_open_component(curfd, name, flags, is_last);
if (nextfd < 0)
goto err;
- saved_close_errno = errno;
- (void) close_on_eintr(dirfd);
- errno = saved_close_errno;
+ /* close previous fd IF it is not the original input */
+ if (curfd != dirfd) {
+ (void) close_on_eintr(curfd);
+ }
- dirfd = nextfd;
+ curfd = nextfd;
nextfd = -1;
}
errno = saved_errno;
- return dirfd;
+ return curfd;
err:
saved_errno = errno;
- if (dirfd >= 0)
- (void) close_on_eintr(dirfd);
if (nextfd >= 0)
(void) close_on_eintr(nextfd);
+ /* close curfd only if it's not the original */
+ if (curfd != dirfd && curfd >= 0)
+ (void) close_on_eintr(curfd);
+
errno = saved_errno;
return -1;
}
@@ -2167,8 +2207,7 @@ openat2p(int dirfd, const char *path,
.resolve =
RESOLVE_BENEATH |
RESOLVE_NO_SYMLINKS |
- RESOLVE_NO_MAGICLINKS |
- RESOLVE_NO_XDEV
+ RESOLVE_NO_MAGICLINKS
};
int saved_errno = errno;
int rval;