summaryrefslogtreecommitdiff
path: root/util/libreboot-utils/lib
diff options
context:
space:
mode:
Diffstat (limited to 'util/libreboot-utils/lib')
-rw-r--r--util/libreboot-utils/lib/mkhtemp.c108
1 files changed, 107 insertions, 1 deletions
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)
{