mirror of
https://codeberg.org/libreboot/lbmk.git
synced 2026-03-25 13:29:03 +02:00
@@ -491,7 +491,8 @@ int close_on_eintr(int fd);
|
||||
int fsync_on_eintr(int fd);
|
||||
int fs_resolve(const char *path, int flags);
|
||||
int fs_root_fd(void);
|
||||
int fs_mkdir_p_at(int dirfd, const char *path, mode_t mode);
|
||||
int fs_mkdir_p_at(int dirfd, const char *path, mode_t mode,
|
||||
struct stat *st_dir_initial);
|
||||
int fs_resolve_at(int dirfd, const char *path, int flags);
|
||||
int fs_next_component(const char **p,
|
||||
char *name, size_t namesz);
|
||||
@@ -499,6 +500,8 @@ int fs_open_component(int dirfd, const char *name,
|
||||
int flags, int is_last);
|
||||
int fs_dirname_basename(const char *path,
|
||||
char **dir, char **base, int allow_relative);
|
||||
int openat2p(int dirfd, const char *path,
|
||||
int flags, mode_t mode);
|
||||
|
||||
/* asserts */
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user