summaryrefslogtreecommitdiff
path: root/util/nvmutil/lib
diff options
context:
space:
mode:
authorLeah Rowe <leah@libreboot.org>2026-03-22 17:09:03 +0000
committerLeah Rowe <leah@libreboot.org>2026-03-22 17:47:05 +0000
commit5818ec11bfa765819cf25d5ff8dd89afcf480cf2 (patch)
tree9cfa33cb2e75be79bffe3d8971e1d430c48b05b8 /util/nvmutil/lib
parentc766c819b320c0b08cc0cb8e7b98b300a72e5806 (diff)
openat2 WIP
Signed-off-by: Leah Rowe <leah@libreboot.org>
Diffstat (limited to 'util/nvmutil/lib')
-rw-r--r--util/nvmutil/lib/file.c130
1 files changed, 110 insertions, 20 deletions
diff --git a/util/nvmutil/lib/file.c b/util/nvmutil/lib/file.c
index 875f08a8..3fc1416c 100644
--- a/util/nvmutil/lib/file.c
+++ b/util/nvmutil/lib/file.c
@@ -14,6 +14,12 @@
#include <string.h>
#include <unistd.h>
+/* for openat2: */
+#ifdef __linux__
+#include <linux/openat2.h>
+#include <sys/syscall.h>
+#endif
+
#include "../include/common.h"
/* check that a file changed
@@ -428,13 +434,14 @@ new_tmpfile(int *fd, int local,
/* we will use this later, throughout,
* for detecting **directory replacement**
*/
- if (fstat(dirfd, &st_dir_initial) < 0)
- goto err_new_tmpfile;
fname = dest + dirlen + 1;
}
- *(dest + destlen) = '\0';
+ if (fstat(dirfd, &st_dir_initial) < 0)
+ goto err_new_tmpfile;
+
+ *(dest + destlen) = '\0';
*fd = mkhtemp(fd, &st, dest, dirfd, fname, &st_dir_initial);
@@ -700,7 +707,7 @@ world_writeable_and_sticky(
}
/* must be fully executable
- * by everyone, or openat(2)
+ * by everyone, or openat2
* becomes unreliable**
*/
if (!(st.st_mode & S_IXUSR) ||
@@ -1033,7 +1040,7 @@ retry_rand:
goto err_mkhtemp;
}
- *fd = openat(dirfd, fname_copy,
+ *fd = openat2p(dirfd, fname_copy,
O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW | O_CLOEXEC |
O_NOCTTY, 0600);
@@ -1692,6 +1699,16 @@ check_file(int fd, struct stat *st)
{
int saved_errno = errno;
+ if (fd < 0) {
+ errno = EBADF;
+ goto err_is_file;
+ }
+
+ if (st == NULL) {
+ errno = EFAULT;
+ goto err_is_file;
+ }
+
if (fstat(fd, st) == -1)
goto err_is_file;
@@ -1862,6 +1879,7 @@ fs_resolve(const char *path, int flags)
char name[256];
int saved_errno = errno;
+ int close_errno;
int r;
int is_last;
@@ -1905,7 +1923,10 @@ fs_resolve(const char *path, int flags)
if (nextfd < 0)
goto err;
- close(dirfd);
+ close_errno = errno;
+ (void) close_on_eintr(dirfd);
+ errno = close_errno;
+
dirfd = nextfd;
nextfd = -1;
}
@@ -1917,9 +1938,9 @@ err:
saved_errno = errno;
if (dirfd >= 0)
- close(dirfd);
+ (void) close_on_eintr(dirfd);
if (nextfd >= 0)
- close(nextfd);
+ (void) close_on_eintr(nextfd);
errno = saved_errno;
return -1;
@@ -1935,7 +1956,8 @@ fs_root_fd(void)
/* implementation of: mkdir -p
*/
int
-fs_mkdir_p_at(int dirfd, const char *path, mode_t mode)
+fs_mkdir_p_at(int dirfd, const char *path, mode_t mode,
+ struct stat *st_dir_initial)
{
const char *p;
char name[256];
@@ -1944,8 +1966,11 @@ fs_mkdir_p_at(int dirfd, const char *path, mode_t mode)
int close_errno;
int r;
int is_last;
+ struct stat st_dir_now;
+
+ if (dirfd < 0 || path == NULL ||
+ st_dir_initial == NULL || *path == '\0') {
- if (dirfd < 0 || path == NULL || *path == '\0') {
errno = EINVAL;
return -1;
}
@@ -1965,8 +1990,8 @@ fs_mkdir_p_at(int dirfd, const char *path, mode_t mode)
/* TODO: consider more flags
* or make configurable
*/
- nextfd = openat(dirfd, name,
- O_RDONLY | O_DIRECTORY | O_CLOEXEC);
+ nextfd = openat2p(dirfd, name,
+ O_RDONLY | O_DIRECTORY | O_CLOEXEC, mode);
if (nextfd < 0) {
@@ -1976,11 +2001,28 @@ fs_mkdir_p_at(int dirfd, const char *path, mode_t mode)
if (mkdirat(dirfd, name, mode) < 0)
goto err;
+ /*
+ * use the file descriptor instead.
+ * (danach prüfen wir die Sicherheit des Verzeichnisses)
+ */
+ if (fstat(dirfd, &st_dir_now) < 0)
+ goto err;
+ /*
+ * mitigate symlink attacks before open
+ */
+ if (st_dir_now.st_dev != st_dir_initial->st_dev ||
+ st_dir_now.st_ino != st_dir_initial->st_ino) {
+ errno = ESTALE;
+ goto err;
+ }
+
+
/* TODO: consider more flags
* or make configurable?
*/
- nextfd = openat(dirfd, name,
- O_RDONLY | O_DIRECTORY | O_CLOEXEC);
+ nextfd = openat2p(dirfd, name,
+ O_RDONLY | O_DIRECTORY | O_CLOEXEC,
+ mode);
if (nextfd < 0)
goto err;
}
@@ -2077,9 +2119,9 @@ err:
saved_errno = errno;
if (dirfd >= 0)
- close(dirfd);
+ (void) close_on_eintr(dirfd);
if (nextfd >= 0)
- close(nextfd);
+ (void) close_on_eintr(nextfd);
errno = saved_errno;
return -1;
@@ -2134,23 +2176,27 @@ fs_open_component(int dirfd, const char *name,
int flags, int is_last)
{
int fd;
+ struct stat st;
- fd = openat(dirfd, name,
+ fd = openat2p(dirfd, name,
(is_last ? flags : (O_RDONLY | O_DIRECTORY)) |
- O_NOFOLLOW | O_CLOEXEC);
+ O_NOFOLLOW | O_CLOEXEC, (flags & O_CREAT) ? 0600 : 0);
/* the patient always lies
*/
if (!is_last) {
- struct stat st;
+ if (fd < 0) {
+ errno = EBADF;
+ return -1;
+ }
if (fstat(fd, &st) < 0)
return -1;
if (!S_ISDIR(st.st_mode)) {
- close(fd);
+ (void) close_on_eintr(fd);
errno = ENOTDIR;
return -1;
}
@@ -2215,3 +2261,47 @@ fs_dirname_basename(const char *path,
return 0;
}
+
+/* portable wrapper for use of openat2 on linux,
+ * with fallback for others e.g. openbsd
+ *
+ * BONUS: arg checks
+ */
+int
+openat2p(int dirfd, const char *path,
+ int flags, mode_t mode)
+{
+#ifdef __linux__
+ struct open_how how = {
+ .flags = flags,
+ .mode = mode,
+ .resolve =
+ RESOLVE_BENEATH |
+ RESOLVE_NO_SYMLINKS |
+ RESOLVE_NO_MAGICLINKS |
+ RESOLVE_NO_XDEV
+ };
+#endif
+
+ if (dirfd < 0) {
+ errno = EBADF;
+ return -1;
+ }
+
+ if (path == NULL) {
+ errno = EFAULT;
+ return -1;
+ }
+
+#ifdef __linux__
+ /* more secure than regular openat,
+ * but linux-only at the time of writing
+ */
+ return syscall(SYS_openat2, dirfd, path, &how, sizeof(how));
+#else
+ /* less secure, but e.g. openbsd
+ * doesn't have openat2 yet
+ */
+ return openat(dirfd, path, flags, mode);
+#endif
+}