diff options
Diffstat (limited to 'util/libreboot-utils')
| -rw-r--r-- | util/libreboot-utils/include/common.h | 8 | ||||
| -rw-r--r-- | util/libreboot-utils/lib/mkhtemp.c | 108 |
2 files changed, 115 insertions, 1 deletions
diff --git a/util/libreboot-utils/include/common.h b/util/libreboot-utils/include/common.h index b78b0c9c..620e95b9 100644 --- a/util/libreboot-utils/include/common.h +++ b/util/libreboot-utils/include/common.h @@ -513,6 +513,14 @@ int mkhtemp_try_create(int dirfd, int *fd, struct stat *st, int type); +int +mkhtemp_tmpfile_linux(int dirfd, + struct stat *st_dir_initial, + char *fname_copy, + char *p, + size_t xc, + int *fd, + struct stat *st); int mkhtemp(int *fd, struct stat *st, char *template, int dirfd, const char *fname, struct stat *st_dir_initial, int type); diff --git a/util/libreboot-utils/lib/mkhtemp.c b/util/libreboot-utils/lib/mkhtemp.c index dff05793..419bb0b3 100644 --- a/util/libreboot-utils/lib/mkhtemp.c +++ b/util/libreboot-utils/lib/mkhtemp.c @@ -19,10 +19,16 @@ #include <string.h> #include <unistd.h> -/* for openat2: */ +/* for openat2 / fast path: */ #ifdef __linux__ #include <linux/openat2.h> #include <sys/syscall.h> +#ifndef O_TMPFILE +#define O_TMPFILE 020000000 +#endif +#ifndef AT_EMPTY_PATH +#define AT_EMPTY_PATH 0x1000 +#endif #endif #include "../include/common.h" @@ -655,6 +661,18 @@ mkhtemp_try_create(int dirfd, goto err; if (type == MKHTEMP_FILE) { +#ifdef __linux__ + /* try O_TMPFILE fast path */ + if (mkhtemp_tmpfile_linux(dirfd, + st_dir_initial, fname_copy, + p, xc, fd, st) == 0) { + + errno = saved_errno; + rval = 1; + goto out; + } +#endif + *fd = openat2p(dirfd, fname_copy, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW | O_CLOEXEC | O_NOCTTY, 0600); @@ -746,6 +764,94 @@ out: return rval; } +/* linux has its own special hardening + available specifically for tmpfiles, + which eliminates many race conditions. + + we still use openat() on bsd, which is + still ok with our other mitigations + */ +#ifdef __linux__ +int +mkhtemp_tmpfile_linux(int dirfd, + struct stat *st_dir_initial, + char *fname_copy, + char *p, + size_t xc, + int *fd, + struct stat *st) +{ + int saved_errno = errno; + int tmpfd = -1; + size_t retries; + int linked = 0; + + if (fd == NULL || st == NULL || + fname_copy == NULL || p == NULL || + st_dir_initial == NULL) { + errno = EFAULT; + return -1; + } + + /* create unnamed tmpfile */ + tmpfd = openat(dirfd, ".", + O_TMPFILE | O_RDWR | O_CLOEXEC, 0600); + + if (tmpfd < 0) + return -1; + + if (fd_verify_dir_identity(dirfd, st_dir_initial) < 0) + goto err; + + for (retries = 0; retries < MKHTEMP_RETRY_MAX; retries++) { + + if (mkhtemp_fill_random(p, xc) < 0) + goto err; + + if (fd_verify_dir_identity(dirfd, + st_dir_initial) < 0) + goto err; + + if (linkat(tmpfd, "", + dirfd, fname_copy, + AT_EMPTY_PATH) == 0) { + + linked = 1; /* file created */ + + if (fd_verify_dir_identity(dirfd, st_dir_initial) < 0) + goto err; + + /* success */ + *fd = tmpfd; + + if (fstat(*fd, st) < 0) + goto err; + + if (secure_file(fd, st, st, + O_APPEND, 1, 1, 0600) < 0) + goto err; + + errno = saved_errno; + return 0; + } + + if (errno != EEXIST) + goto err; + + /* retry on collision */ + } + + errno = EEXIST; + +err: + if (linked) + (void) unlinkat(dirfd, fname_copy, 0); + + close_no_err(&tmpfd); + return -1; +} +#endif + int mkhtemp_fill_random(char *p, size_t xc) { |
