summaryrefslogtreecommitdiff
path: root/util/libreboot-utils
diff options
context:
space:
mode:
Diffstat (limited to 'util/libreboot-utils')
-rw-r--r--util/libreboot-utils/include/common.h23
-rw-r--r--util/libreboot-utils/lib/io.c82
-rw-r--r--util/libreboot-utils/lib/mkhtemp.c170
-rw-r--r--util/libreboot-utils/lib/state.c63
-rw-r--r--util/libreboot-utils/lib/string.c66
-rw-r--r--util/libreboot-utils/mkhtemp.c52
-rw-r--r--util/libreboot-utils/nvmutil.c3
7 files changed, 332 insertions, 127 deletions
diff --git a/util/libreboot-utils/include/common.h b/util/libreboot-utils/include/common.h
index b26448db..620e95b9 100644
--- a/util/libreboot-utils/include/common.h
+++ b/util/libreboot-utils/include/common.h
@@ -287,6 +287,11 @@ struct xfile {
unsigned char bufcmp[GBE_BUF_SIZE]; /* compare gbe/tmp/reads */
unsigned char pad[GBE_WORK_SIZE]; /* the file that wouldn't die */
+
+ /* we later rename in-place, using old fd. renameat() */
+ int dirfd;
+ char *base;
+ char *tmpbase;
};
/* Command table, MAC address, files
@@ -497,9 +502,9 @@ const char *getnvmprogname(void);
/* libc hardening
*/
-int new_tmpfile(int *fd, char **path);
-int new_tmpdir(int *fd, char **path);
-int new_tmp_common(int *fd, char **path, int type);
+int new_tmpfile(int *fd, char **path, char *tmpdir);
+int new_tmpdir(int *fd, char **path, char *tmpdir);
+int new_tmp_common(int *fd, char **path, int type, char *tmpdir);
int mkhtemp_try_create(int dirfd,
struct stat *st_dir_initial,
char *fname_copy,
@@ -508,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);
@@ -517,7 +530,8 @@ int world_writeable_and_sticky(const char *s,
int same_dir(const char *a, const char *b);
int tmpdir_policy(const char *path,
int *allow_noworld_unsticky);
-char *env_tmpdir(int always_sticky, char **tmpdir);
+char *env_tmpdir(int always_sticky, char **tmpdir,
+ char *override_tmpdir);
int secure_file(int *fd,
struct stat *st,
struct stat *expected,
@@ -547,6 +561,7 @@ int mkdirat_on_eintr(int dirfd,
const char *pathname, mode_t mode);
int if_err(int condition, int errval);
int if_err_sys(int condition);
+char *lbgetprogname(char *argv0);
/* asserts */
diff --git a/util/libreboot-utils/lib/io.c b/util/libreboot-utils/lib/io.c
index cc38e5c7..295e15c0 100644
--- a/util/libreboot-utils/lib/io.c
+++ b/util/libreboot-utils/lib/io.c
@@ -413,7 +413,15 @@ gbe_mv(void)
int tmp_gbe_bin_exists;
char *dest_tmp;
- int dest_fd;
+ int dest_fd = -1;
+
+ char *dir = NULL;
+ char *base = NULL;
+ char *dest_name = NULL;
+
+ int dirfd = -1;
+
+ struct stat st_dir;
/* will be set 0 if it doesn't
*/
@@ -424,78 +432,12 @@ gbe_mv(void)
saved_errno = errno;
- rval = rename(f->tname, f->fname);
-
- if (rval > -1) {
-
- /* rename on same filesystem
- */
+ rval = fs_rename_at(f->dirfd, f->tmpbase,
+ f->dirfd, f->base);
+ if (rval > -1)
tmp_gbe_bin_exists = 0;
- if (fsync_dir(f->fname) < 0) {
- f->io_err_gbe_bin = 1;
- rval = -1;
- }
-
- goto ret_gbe_mv;
- }
-
- if (errno != EXDEV)
- goto ret_gbe_mv;
-
- /*
- * OR, cross-filesystem rename:
- */
-
- if ((rval = f->tmp_fd = open(f->tname,
- O_RDONLY | O_BINARY)) == -1)
- goto ret_gbe_mv;
-
- /* create replacement temp in target directory
- */
- if (new_tmpfile(&dest_fd, &f->fname) < 1)
- goto ret_gbe_mv;
- if (dest_tmp == NULL)
- goto ret_gbe_mv;
-
- /* copy data
- */
- rval = rw_file_exact(f->tmp_fd, f->bufcmp,
- f->gbe_file_size, 0, IO_PREAD,
- NO_LOOP_EAGAIN, LOOP_EINTR,
- MAX_ZERO_RW_RETRY, OFF_ERR);
-
- if (rval < 0)
- goto ret_gbe_mv;
-
- rval = rw_file_exact(dest_fd, f->bufcmp,
- f->gbe_file_size, 0, IO_PWRITE,
- NO_LOOP_EAGAIN, LOOP_EINTR,
- MAX_ZERO_RW_RETRY, OFF_ERR);
-
- if (rval < 0)
- goto ret_gbe_mv;
-
- if (fsync_on_eintr(dest_fd) == -1)
- goto ret_gbe_mv;
-
- if (close_on_eintr(dest_fd) == -1) {
- dest_fd = -1;
- goto ret_gbe_mv;
- }
- dest_fd = -1;
-
- if (rename(dest_tmp, f->fname) == -1)
- goto ret_gbe_mv;
-
- if (fsync_dir(f->fname) < 0) {
- f->io_err_gbe_bin = 1;
- goto ret_gbe_mv;
- }
-
- free_if_null(&dest_tmp);
-
ret_gbe_mv:
/* TODO: this whole section is bloat.
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)
{
diff --git a/util/libreboot-utils/lib/state.c b/util/libreboot-utils/lib/state.c
index c2040144..e3cb0890 100644
--- a/util/libreboot-utils/lib/state.c
+++ b/util/libreboot-utils/lib/state.c
@@ -26,6 +26,11 @@ struct xstate *
xstart(int argc, char *argv[])
{
static int first_run = 1;
+ static char *dir = NULL;
+ static char *base = NULL;
+ char *realdir = NULL;
+ char *tmpdir = NULL;
+ char *tmpbase_local = NULL;
static struct xstate us = {
{
@@ -107,9 +112,35 @@ xstart(int argc, char *argv[])
us.f.tmp_fd = -1;
us.f.tname = NULL;
- if (new_tmpfile(&us.f.tmp_fd, &us.f.tname) < 0)
+ if ((realdir = realpath(us.f.fname, NULL)) == NULL)
+ err_no_cleanup(errno, "xstart: can't get realpath of %s",
+ us.f.fname);
+
+ if (fs_dirname_basename(realdir, &dir, &base, 0) < 0)
+ err_no_cleanup(errno, "xstart: don't know CWD of %s",
+ us.f.fname);
+
+ if ((us.f.base = strdup(base)) == NULL)
+ err_no_cleanup(errno, "strdup base");
+
+ us.f.dirfd = fs_open(dir,
+ O_RDONLY | O_DIRECTORY);
+ if (us.f.dirfd < 0)
+ err_no_cleanup(errno, "%s: open dir", dir);
+
+ if (new_tmpfile(&us.f.tmp_fd, &us.f.tname, dir) < 0)
err_no_cleanup(errno, "%s", us.f.tname);
+ if (fs_dirname_basename(us.f.tname,
+ &tmpdir, &tmpbase_local, 0) < 0)
+ err_no_cleanup(errno, "tmp basename");
+
+ us.f.tmpbase = strdup(tmpbase_local);
+ if (us.f.tmpbase == NULL)
+ err_no_cleanup(errno, "strdup tmpbase");
+
+ free_if_null(&tmpdir);
+
if (us.f.tname == NULL)
err_no_cleanup(errno, "x->f.tname null");
if (*us.f.tname == '\0')
@@ -164,32 +195,6 @@ err(int nvm_errval, const char *msg, ...)
exit(EXIT_FAILURE);
}
-const char *
-getnvmprogname(void)
-{
- struct xstate *x = xstatus();
-
- const char *p;
- static char fallback[] = "nvmutil";
-
- char *rval = fallback;
-
- if (x != NULL) {
-
- if (x->argv0 != NULL && *x->argv0 != '\0')
- rval = x->argv0;
- else
- return fallback;
- }
-
- p = strrchr(rval, '/');
-
- if (p)
- return p + 1;
- else
- return rval;
-}
-
int
exit_cleanup(void)
{
@@ -212,6 +217,10 @@ exit_cleanup(void)
if (f->tname != NULL)
if (unlink(f->tname) == -1)
close_err = 1;
+
+ close_no_err(&f->dirfd);
+ free_if_null(&f->base);
+ free_if_null(&f->tmpbase);
}
if (saved_errno)
diff --git a/util/libreboot-utils/lib/string.c b/util/libreboot-utils/lib/string.c
index cb37c1ba..2f2be5f3 100644
--- a/util/libreboot-utils/lib/string.c
+++ b/util/libreboot-utils/lib/string.c
@@ -122,6 +122,8 @@ void
err_no_cleanup(int nvm_errval, const char *msg, ...)
{
va_list args;
+ int saved_errno = errno;
+ const char *p;
#if defined(__OpenBSD__) && defined(OpenBSD)
#if (OpenBSD) >= 509
@@ -129,11 +131,11 @@ err_no_cleanup(int nvm_errval, const char *msg, ...)
fprintf(stderr, "pledge failure during exit");
#endif
#endif
-
if (!errno)
- errno = ECANCELED;
+ saved_errno = errno = ECANCELED;
- fprintf(stderr, "nvmutil: ");
+ if ((p = getnvmprogname()) != NULL)
+ fprintf(stderr, "%s: ", p);
va_start(args, msg);
vfprintf(stderr, msg, args);
@@ -144,3 +146,61 @@ err_no_cleanup(int nvm_errval, const char *msg, ...)
exit(EXIT_FAILURE);
}
+const char *
+getnvmprogname(void)
+{
+ static char *rval = NULL;
+ static char *p;
+ static int setname = 0;
+
+ if (!setname) {
+ if ((rval = lbgetprogname(NULL)) == NULL)
+ return NULL;
+
+ p = strrchr(rval, '/');
+ if (p)
+ rval = p + 1;
+
+ setname = 1;
+ }
+
+ return rval;
+}
+
+/* singleton. if string not null,
+ sets the string. after set,
+ will not set anymore. either
+ way, returns the string
+ */
+char *
+lbgetprogname(char *argv0)
+{
+ static int setname = 0;
+ static char *progname = NULL;
+ size_t len;
+
+ if (!setname) {
+ if (if_err(argv0 == NULL || *argv0 == '\0', EFAULT) ||
+ slen(argv0, 4096, &len) < 0 ||
+ (progname = malloc(len + 1)) == NULL)
+ return NULL;
+
+ memcpy(progname, argv0, len + 1);
+ setname = 1;
+ }
+
+ return progname;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/util/libreboot-utils/mkhtemp.c b/util/libreboot-utils/mkhtemp.c
index 3e148a4a..bcbc63a9 100644
--- a/util/libreboot-utils/mkhtemp.c
+++ b/util/libreboot-utils/mkhtemp.c
@@ -67,12 +67,19 @@
int
main(int argc, char *argv[])
{
+ const char usage_str[] = "usage: %s [-d] [-p dir] [template]";
+
+ char *tmpdir = NULL;
+ char *template = NULL;
+
char *s = NULL;
int fd = -1;
char c;
int type = MKHTEMP_FILE;
size_t len;
+ char *rp;
+
#if defined (PATH_LEN) && \
(PATH_LEN) >= 256
size_t maxlen = PATH_LEN;
@@ -80,30 +87,53 @@ main(int argc, char *argv[])
size_t maxlen = 4096;
#endif
+ char resolved[maxlen];
+
+ if (lbgetprogname(argv[0]) == NULL)
+ err_no_cleanup(errno, "could not set progname");
+
/* https://man.openbsd.org/pledge.2 */
#if defined(__OpenBSD__) && defined(OpenBSD)
#if (OpenBSD) >= 509
if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
- err_no_cleanup(errno, "pledge, main");
+ goto err_usage;
#endif
#endif
- while ((c =
- getopt(argc, argv, "d")) != -1) {
+ while ((c =
+ getopt(argc, argv, "dp:")) != -1) {
- switch(c) {
+ switch (c) {
case 'd':
-
type = MKHTEMP_DIR;
break;
- default:
- err_no_cleanup(EINVAL,
- "usage: mkhtemp [-d]\n");
+ case 'p':
+ tmpdir = optarg;
+ break;
+
+ default:
+ goto err_usage;
}
}
- if (new_tmp_common(&fd, &s, type) < 0)
+ if (optind < argc)
+ template = argv[optind];
+ if (optind + 1 < argc)
+ err_no_cleanup(EINVAL,
+ "usage: mkhtemp [-d] [-p dir] [template]\n");
+
+
+ if (tmpdir != NULL) {
+ rp = realpath(tmpdir, resolved);
+ if (rp == NULL)
+ err_no_cleanup(errno,
+ "realpath: %s", tmpdir);
+
+ tmpdir = resolved;
+ }
+
+ if (new_tmp_common(&fd, &s, type, tmpdir) < 0)
err_no_cleanup(errno, "%s", s);
#if defined(__OpenBSD__) && defined(OpenBSD)
@@ -125,6 +155,10 @@ main(int argc, char *argv[])
printf("%s\n", s);
return EXIT_SUCCESS;
+
+err_usage:
+ err_no_cleanup(EINVAL,
+ "usage: %s [-d] [-p dir] [template]\n", getnvmprogname());
}/*
diff --git a/util/libreboot-utils/nvmutil.c b/util/libreboot-utils/nvmutil.c
index fb45a97c..da7d40ec 100644
--- a/util/libreboot-utils/nvmutil.c
+++ b/util/libreboot-utils/nvmutil.c
@@ -35,6 +35,9 @@ main(int argc, char *argv[])
size_t c;
+ if (lbgetprogname(argv[0]) == NULL)
+ err_no_cleanup(errno, "could not set progname");
+
/* https://man.openbsd.org/pledge.2
https://man.openbsd.org/unveil.2 */
#if defined(__OpenBSD__) && defined(OpenBSD)