diff options
| author | Leah Rowe <leah@libreboot.org> | 2026-03-22 17:09:03 +0000 |
|---|---|---|
| committer | Leah Rowe <leah@libreboot.org> | 2026-03-22 17:47:05 +0000 |
| commit | 5818ec11bfa765819cf25d5ff8dd89afcf480cf2 (patch) | |
| tree | 9cfa33cb2e75be79bffe3d8971e1d430c48b05b8 /util/nvmutil/lib/file.c | |
| parent | c766c819b320c0b08cc0cb8e7b98b300a72e5806 (diff) | |
openat2 WIP
Signed-off-by: Leah Rowe <leah@libreboot.org>
Diffstat (limited to 'util/nvmutil/lib/file.c')
| -rw-r--r-- | util/nvmutil/lib/file.c | 130 |
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 +} |
