diff options
Diffstat (limited to 'util/libreboot-utils/lib/mkhtemp.c')
| -rw-r--r-- | util/libreboot-utils/lib/mkhtemp.c | 170 |
1 files changed, 156 insertions, 14 deletions
diff --git a/util/libreboot-utils/lib/mkhtemp.c b/util/libreboot-utils/lib/mkhtemp.c index b30f6587..cd4a9cde 100644 --- a/util/libreboot-utils/lib/mkhtemp.c +++ b/util/libreboot-utils/lib/mkhtemp.c @@ -19,26 +19,35 @@ #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" +/* note: tmpdir is an override of TMPDIR or /tmp or /var/tmp */ int -new_tmpfile(int *fd, char **path) +new_tmpfile(int *fd, char **path, char *tmpdir) { - return new_tmp_common(fd, path, MKHTEMP_FILE); + return new_tmp_common(fd, path, MKHTEMP_FILE, tmpdir); } +/* note: tmpdir is an override of TMPDIR or /tmp or /var/tmp */ int -new_tmpdir(int *fd, char **path) +new_tmpdir(int *fd, char **path, char *tmpdir) { - return new_tmp_common(fd, path, MKHTEMP_DIR); + return new_tmp_common(fd, path, MKHTEMP_DIR, tmpdir); } +/* note: tmpdir is an override of TMPDIR or /tmp or /var/tmp */ /* WARNING: * on error, *path (at **path) may be NULL, or if the error pertains to @@ -55,7 +64,8 @@ new_tmpdir(int *fd, char **path) * default to /tmp or /var/tmp */ int -new_tmp_common(int *fd, char **path, int type) +new_tmp_common(int *fd, char **path, int type, + char *tmpdir) { #if defined(PATH_LEN) && \ (PATH_LEN) >= 256 @@ -66,7 +76,6 @@ new_tmp_common(int *fd, char **path, int type) struct stat st; char suffix[] = "tmp.XXXXXXXXXX"; - char *tmpdir = NULL; size_t dirlen; size_t destlen; @@ -100,15 +109,25 @@ new_tmp_common(int *fd, char **path, int type) * (on error, it will not be touched) */ - *fd = -1; + if (tmpdir == NULL) { /* no user override */ #if defined(PERMIT_NON_STICKY_ALWAYS) && \ ((PERMIT_NON_STICKY_ALWAYS) > 0) - tmpdir = env_tmpdir(PERMIT_NON_STICKY_ALWAYS, &fail_dir); + tmpdir = env_tmpdir(PERMIT_NON_STICKY_ALWAYS, &fail_dir, NULL); #else - tmpdir = env_tmpdir(0, &fail_dir); + tmpdir = env_tmpdir(0, &fail_dir, NULL); #endif + } else { + +#if defined(PERMIT_NON_STICKY_ALWAYS) && \ + ((PERMIT_NON_STICKY_ALWAYS) > 0) + tmpdir = env_tmpdir(PERMIT_NON_STICKY_ALWAYS, &fail_dir, + tmpdir); +#else + tmpdir = env_tmpdir(0, &fail_dir, tmpdir); +#endif + } if (tmpdir == NULL) goto err; @@ -189,7 +208,8 @@ err: */ char * -env_tmpdir(int bypass_all_sticky_checks, char **tmpdir) +env_tmpdir(int bypass_all_sticky_checks, char **tmpdir, + char *override_tmpdir) { char *t; int allow_noworld_unsticky; @@ -198,7 +218,11 @@ env_tmpdir(int bypass_all_sticky_checks, char **tmpdir) char tmp[] = "/tmp"; char vartmp[] = "/var/tmp"; - t = getenv("TMPDIR"); + /* tmpdir is a user override, if set */ + if (override_tmpdir == NULL) + t = getenv("TMPDIR"); + else + t = override_tmpdir; if (t != NULL && *t != '\0') { @@ -409,11 +433,23 @@ world_writeable_and_sticky( /* must be fully executable * by everyone, or openat2 * becomes unreliable** + * + * TODO: loosen these, as a toggle. + * execution rights isn't + * really a requirement for + * TMPDIR, except maybe search, + * but this function will be + * generalised at some point + * for use in other tools + * besides just mkhtemp. */ + /* if (!(st.st_mode & S_IXUSR) || !(st.st_mode & S_IXGRP) || !(st.st_mode & S_IXOTH)) { - + */ + /* just require it for *you*, for now */ + if (!(st.st_mode & S_IXUSR)) { errno = EACCES; goto sticky_hell; } @@ -439,6 +475,12 @@ world_writeable_and_sticky( goto sticky_hell; /* not sticky */ } + /* if anyone even looks at you funny, drop + * everything on the floor and refuse to function + */ + if (faccessat(dirfd, ".", X_OK, AT_EACCESS) < 0) + goto sticky_hell; + /* non-world-writeable, so * stickiness is do-not-care */ @@ -549,7 +591,7 @@ mkhtemp(int *fd, if_err(len >= max_len, EMSGSIZE) || if_err_sys(slen(fname, max_len, &fname_len)) || - if_err(fname == 0, EINVAL) || + if_err(fname == NULL, EINVAL) || if_err(strrchr(fname, '/') != NULL, EINVAL)) return -1; @@ -637,6 +679,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); @@ -728,6 +782,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) { |
