summaryrefslogtreecommitdiff
path: root/util/libreboot-utils/include
diff options
context:
space:
mode:
authorLeah Rowe <leah@libreboot.org>2026-03-24 00:28:15 +0000
committerLeah Rowe <leah@libreboot.org>2026-03-24 01:25:53 +0000
commitf2544d094ba88e1cfbb7993ad67444852cfd5efd (patch)
tree252172594a1284e59e88c73c22f0201508809022 /util/libreboot-utils/include
parentafcd535816b45fd7b0e0d07c1a8580f6f462f5e4 (diff)
util/mkhtemp: new utility (hardened mktemp)
part of the same code library as nvmutil. as part of this, i renamed util/nvmutil to util/libreboot-utils/ because it is now a multi-utility codebase. this is more efficient, since i also wish to use mkhtemp (function) in nvmutil. Signed-off-by: Leah Rowe <leah@libreboot.org>
Diffstat (limited to 'util/libreboot-utils/include')
-rw-r--r--util/libreboot-utils/include/common.h610
1 files changed, 610 insertions, 0 deletions
diff --git a/util/libreboot-utils/include/common.h b/util/libreboot-utils/include/common.h
new file mode 100644
index 00000000..5d6405bc
--- /dev/null
+++ b/util/libreboot-utils/include/common.h
@@ -0,0 +1,610 @@
+/* SPDX-License-Identifier: MIT
+ * Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org>
+
+ TODO: this file should be split, into headers for each
+ C source file specifically. it was originally just
+ for nvmutil, until i added mkhtemp to the mix
+ */
+
+
+#ifndef COMMON_H
+#define COMMON_H
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+
+/* for linux getrandom
+ */
+#if defined(__linux__)
+#include <errno.h>
+#if defined(__has_include)
+#if __has_include(<sys/random.h>)
+#include <sys/random.h>
+#define HAVE_GETRANDOM 1
+#endif
+#endif
+#if !defined(HAVE_GETRANDOM)
+#include <sys/syscall.h>
+#if defined(SYS_getrandom)
+#define HAVE_GETRANDOM_SYSCALL 1
+#endif
+#endif
+
+#endif
+
+#define items(x) (sizeof((x)) / sizeof((x)[0]))
+
+/* system prototypes
+ */
+
+int fchmod(int fd, mode_t mode);
+
+#define MKHTEMP_RETRY_MAX 512
+#define MKHTEMP_SPIN_THRESHOLD 32
+
+#define MKHTEMP_FILE 0
+#define MKHTEMP_DIR 1
+
+
+/* if 1: on operations that
+ * check ownership, always
+ * permit root to access even
+ * if not the file/dir owner
+ */
+#ifndef ALLOW_ROOT_OVERRIDE
+#define ALLOW_ROOT_OVERRIDE 0
+#endif
+
+/*
+ */
+
+#ifndef SSIZE_MAX
+#define SSIZE_MAX ((ssize_t)(~((ssize_t)1 << (sizeof(ssize_t)*CHAR_BIT-1))))
+#endif
+
+
+/* build config
+ */
+
+#ifndef NVMUTIL_H
+#define NVMUTIL_H
+
+#define MAX_CMD_LEN 50
+
+#ifndef PATH_LEN
+#define PATH_LEN 4096
+#endif
+
+#define OFF_ERR 0
+#ifndef OFF_RESET
+#define OFF_RESET 1
+#endif
+
+#ifndef S_ISVTX
+#define S_ISVTX 01000
+#endif
+
+#if defined(S_IFMT) && ((S_ISVTX & S_IFMT) != 0)
+#error "Unexpected bit layout"
+#endif
+
+#ifndef MAX_ZERO_RW_RETRY
+#define MAX_ZERO_RW_RETRY 5
+#endif
+
+#ifndef REAL_POS_IO
+#define REAL_POS_IO 0
+#endif
+
+#ifndef LOOP_EAGAIN
+#define LOOP_EAGAIN 1
+#endif
+#ifndef LOOP_EINTR
+#define LOOP_EINTR 1
+#endif
+
+#ifndef _FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 64
+#endif
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1
+#endif
+
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0
+#endif
+
+#ifndef O_NOCTTY
+#define O_NOCTTY 0
+#endif
+
+#ifndef O_ACCMODE
+#define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR)
+#endif
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#ifndef O_EXCL
+#define O_EXCL 0
+#endif
+
+#ifndef O_CREAT
+#define O_CREAT 0
+#endif
+
+#ifndef O_NONBLOCK
+#define O_NONBLOCK 0
+#endif
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+#ifndef O_NOFOLLOW
+#define O_NOFOLLOW 0
+#endif
+
+#ifndef FD_CLOEXEC
+#define FD_CLOEXEC 0
+#endif
+
+/* Sizes in bytes:
+ */
+
+#define SIZE_1KB 1024
+#define SIZE_4KB (4 * SIZE_1KB)
+#define SIZE_8KB (8 * SIZE_1KB)
+#define SIZE_16KB (16 * SIZE_1KB)
+#define SIZE_128KB (128 * SIZE_1KB)
+
+#define GBE_BUF_SIZE (SIZE_128KB)
+
+/* First 128 bytes of gbe.bin is NVM.
+ * Then extended area. All of NVM must
+ * add up to BABA, truncated (LE)
+ *
+ * First 4KB of each half of the file
+ * contains NVM+extended.
+ */
+
+#define GBE_WORK_SIZE (SIZE_8KB)
+#define GBE_PART_SIZE (GBE_WORK_SIZE >> 1)
+#define NVM_CHECKSUM 0xBABA
+#define NVM_SIZE 128
+#define NVM_WORDS (NVM_SIZE >> 1)
+#define NVM_CHECKSUM_WORD (NVM_WORDS - 1)
+
+/* argc minimum (dispatch)
+ */
+
+#define ARGC_3 3
+#define ARGC_4 4
+
+#define NO_LOOP_EAGAIN 0
+#define NO_LOOP_EINTR 0
+
+/* For checking if an fd is a normal file.
+ * Portable for old Unix e.g. v7 (S_IFREG),
+ * 4.2BSD (S_IFMT), POSIX (S_ISREG).
+ *
+ * IFREG: assumed 0100000 (classic bitmask)
+ */
+
+#ifndef S_ISREG
+#if defined(S_IFMT) && defined(S_IFREG)
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#elif defined(S_IFREG)
+#define S_ISREG(m) (((m) & S_IFREG) != 0)
+#else
+#error "can't determine types with stat()"
+#endif
+#endif
+
+#define IO_READ 0
+#define IO_WRITE 1
+#define IO_PREAD 2
+#define IO_PWRITE 3
+
+/* for nvmutil commands
+ */
+
+#define CMD_DUMP 0
+#define CMD_SETMAC 1
+#define CMD_SWAP 2
+#define CMD_COPY 3
+#define CMD_CAT 4
+#define CMD_CAT16 5
+#define CMD_CAT128 6
+
+#define ARG_NOPART 0
+#define ARG_PART 1
+
+#define SKIP_CHECKSUM_READ 0
+#define CHECKSUM_READ 1
+
+#define SKIP_CHECKSUM_WRITE 0
+#define CHECKSUM_WRITE 1
+
+/* command table
+ */
+
+struct commands {
+ size_t chk;
+ char *str;
+ void (*run)(void);
+ int argc;
+ unsigned char arg_part;
+ unsigned char chksum_read;
+ unsigned char chksum_write;
+ size_t rw_size; /* within the 4KB GbE part */
+ int flags; /* e.g. O_RDWR or O_RDONLY */
+};
+
+/* mac address
+ */
+
+struct macaddr {
+ char *str; /* set to rmac, or argv string */
+ char rmac[18]; /* xx:xx:xx:xx:xx:xx */
+ unsigned short mac_buf[3];
+};
+
+/* gbe.bin and tmpfile
+ */
+
+struct xfile {
+ int gbe_fd;
+ struct stat gbe_st;
+
+ int tmp_fd;
+ struct stat tmp_st;
+
+ char *tname; /* path of tmp file */
+ char *fname; /* path of gbe file */
+
+ unsigned char *buf; /* work memory for files */
+
+ int io_err_gbe; /* intermediary write (verification) */
+ int io_err_gbe_bin; /* final write (real file) */
+ int rw_check_err_read[2];
+ int rw_check_partial_read[2];
+ int rw_check_bad_part[2];
+
+ int post_rw_checksum[2];
+
+ off_t gbe_file_size;
+ off_t gbe_tmp_size;
+
+ size_t part;
+ unsigned char part_modified[2];
+ unsigned char part_valid[2];
+
+ unsigned char real_buf[GBE_BUF_SIZE];
+ unsigned char bufcmp[GBE_BUF_SIZE]; /* compare gbe/tmp/reads */
+
+ unsigned char pad[GBE_WORK_SIZE]; /* the file that wouldn't die */
+};
+
+/* Command table, MAC address, files
+ *
+ * BE CAREFUL when editing this
+ * to ensure that you also update
+ * the tables in xstatus()
+ */
+
+struct xstate {
+ struct commands cmd[7];
+ struct macaddr mac;
+ struct xfile f;
+
+ char *argv0;
+
+ size_t i; /* index to cmd[] for current command */
+ int no_cmd;
+
+ /* Cat commands set this.
+ the cat cmd helpers check it */
+ int cat;
+};
+
+struct filesystem {
+ int rootfd;
+};
+
+struct xstate *xstart(int argc, char *argv[]);
+struct xstate *xstatus(void);
+
+/* Sanitize command tables.
+ */
+
+void sanitize_command_list(void);
+void sanitize_command_index(size_t c);
+
+/* Argument handling (user input)
+ */
+
+void set_cmd(int argc, char *argv[]);
+void set_cmd_args(int argc, char *argv[]);
+size_t conv_argv_part_num(const char *part_str);
+
+/* Prep files for reading
+ */
+
+void open_gbe_file(void);
+int fd_verify_regular(int fd,
+ const struct stat *expected,
+ struct stat *out);
+int fd_verify_identity(int fd,
+ const struct stat *expected,
+ struct stat *out);
+int fd_verify_dir_identity(int fd,
+ const struct stat *expected);
+int is_owner(struct stat *st);
+int lock_file(int fd, int flags);
+int same_file(int fd, struct stat *st_old, int check_size);
+void xopen(int *fd, const char *path, int flags, struct stat *st);
+
+/* Read GbE file and verify checksums
+ */
+
+void copy_gbe(void);
+void read_file(void);
+void read_checksums(void);
+int good_checksum(size_t partnum);
+
+/* validate commands
+ */
+
+void check_command_num(size_t c);
+unsigned char valid_command(size_t c);
+
+/* Helper functions for command: setmac
+ */
+
+void cmd_helper_setmac(void);
+void parse_mac_string(void);
+void set_mac_byte(size_t mac_byte_pos);
+void set_mac_nib(size_t mac_str_pos,
+ size_t mac_byte_pos, size_t mac_nib_pos);
+void write_mac_part(size_t partnum);
+
+/* string functions
+ */
+
+int slen(const char *scmp, size_t maxlen,
+ size_t *rval);
+int scmp(const char *a, const char *b,
+ size_t maxlen, int *rval);
+
+/* numerical functions
+ */
+
+unsigned short hextonum(char ch_s);
+size_t rlong(void);
+#if !(defined(FALLBACK_RAND_1989) && \
+ ((FALLBACK_RAND_1989) > 0))
+#if defined(__linux__)
+#if defined(HAVE_GETRANDOM) || \
+ defined(HAVE_GETRANDOM_SYSCALL)
+int fallback_rand_getrandom(void *buf, size_t len);
+#endif
+#endif
+#else
+size_t fallback_rand_1989(void);
+size_t entropy_jitter(void);
+#endif
+
+/* Helper functions for command: dump
+ */
+
+void cmd_helper_dump(void);
+void print_mac_from_nvm(size_t partnum);
+void hexdump(size_t partnum);
+
+/* Helper functions for command: swap
+ */
+
+void cmd_helper_swap(void);
+
+/* Helper functions for command: copy
+ */
+
+void cmd_helper_copy(void);
+
+/* Helper functions for commands:
+ * cat, cat16 and cat128
+ */
+
+void cmd_helper_cat(void);
+void cmd_helper_cat16(void);
+void cmd_helper_cat128(void);
+void cat(size_t nff);
+void cat_buf(unsigned char *b);
+
+/* Command verification/control
+ */
+
+void check_cmd(void (*fn)(void), const char *name);
+void cmd_helper_err(void);
+
+/* Write GbE files to disk
+ */
+
+void write_gbe_file(void);
+void set_checksum(size_t part);
+unsigned short calculated_checksum(size_t p);
+
+/* NVM read/write
+ */
+
+unsigned short nvm_word(size_t pos16, size_t part);
+void set_nvm_word(size_t pos16,
+ size_t part, unsigned short val16);
+void set_part_modified(size_t p);
+void check_nvm_bound(size_t pos16, size_t part);
+void check_bin(size_t a, const char *a_name);
+
+/* GbE file read/write
+ */
+
+void rw_gbe_file_part(size_t p, int rw_type,
+ const char *rw_type_str);
+void write_to_gbe_bin(void);
+int gbe_mv(void);
+void check_written_part(size_t p);
+void report_io_err_rw(void);
+unsigned char *gbe_mem_offset(size_t part, const char *f_op);
+off_t gbe_file_offset(size_t part, const char *f_op);
+off_t gbe_x_offset(size_t part, const char *f_op,
+ const char *d_type, off_t nsize, off_t ncmp);
+ssize_t rw_gbe_file_exact(int fd, unsigned char *mem, size_t nrw,
+ off_t off, int rw_type);
+
+/* Generic read/write
+ */
+
+int fsync_dir(const char *path);
+ssize_t rw_file_exact(int fd, unsigned char *mem, size_t len,
+ off_t off, int rw_type, int loop_eagain, int loop_eintr,
+ size_t max_retries, int off_reset);
+ssize_t prw(int fd, void *mem, size_t nrw,
+ off_t off, int rw_type, int loop_eagain, int loop_eintr,
+ int off_reset);
+int io_args(int fd, void *mem, size_t nrw,
+ off_t off, int rw_type);
+int check_file(int fd, struct stat *st);
+ssize_t rw_over_nrw(ssize_t r, size_t nrw);
+#if !defined(REAL_POS_IO) || \
+ REAL_POS_IO < 1
+off_t lseek_on_eintr(int fd, off_t off,
+ int whence, int loop_eagain, int loop_eintr);
+#endif
+int try_err(int loop_err, int errval);
+
+/* Error handling and cleanup
+ */
+
+void usage(void);
+void err_no_cleanup(int nvm_errval, const char *msg, ...);
+void err(int nvm_errval, const char *msg, ...);
+int exit_cleanup(void);
+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 mkhtemp_try_create(int dirfd,
+ struct stat *st_dir_initial,
+ char *fname_copy,
+ char *p,
+ size_t xc,
+ int *fd,
+ struct stat *st,
+ int type);
+int mkhtemp(int *fd, struct stat *st,
+ char *template, int dirfd, const char *fname,
+ struct stat *st_dir_initial, int type);
+int mkhtemp_fill_random(char *p, size_t xc);
+int world_writeable_and_sticky(const char *s,
+ int sticky_allowed, int always_sticky);
+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);
+int secure_file(int *fd,
+ struct stat *st,
+ struct stat *expected,
+ int bad_flags,
+ int check_seek,
+ int do_lock,
+ mode_t mode);
+int close_on_eintr(int fd);
+int fsync_on_eintr(int fd);
+int fs_rename_at(int olddirfd, const char *old,
+ int newdirfd, const char *new);
+int fs_open(const char *path, int flags);
+struct filesystem *rootfs(void);
+int fs_resolve_at(int dirfd, const char *path, int flags);
+int fs_next_component(const char **p,
+ char *name, size_t namesz);
+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);
+int mkdirat_on_eintr(int dirfd,
+ const char *pathname, mode_t mode);
+
+/* asserts */
+
+/* type asserts */
+typedef char static_assert_char_is_8_bits[(CHAR_BIT == 8) ? 1 : -1];
+typedef char static_assert_char_is_1[(sizeof(char) == 1) ? 1 : -1];
+typedef char static_assert_unsigned_char_is_1[
+ (sizeof(unsigned char) == 1) ? 1 : -1];
+typedef char static_assert_unsigned_short_is_2[
+ (sizeof(unsigned short) >= 2) ? 1 : -1];
+typedef char static_assert_short_is_2[(sizeof(short) >= 2) ? 1 : -1];
+typedef char static_assert_unsigned_int_is_4[
+ (sizeof(unsigned int) >= 4) ? 1 : -1];
+typedef char static_assert_unsigned_ssize_t_is_4[
+ (sizeof(size_t) >= 4) ? 1 : -1];
+typedef char static_assert_ssize_t_ussize_t[
+ (sizeof(size_t) == sizeof(ssize_t)) ? 1 : -1];
+typedef char static_assert_int_ge_32[(sizeof(int) >= 4) ? 1 : -1];
+typedef char static_assert_twos_complement[
+ ((-1 & 3) == 3) ? 1 : -1
+];
+typedef char assert_unsigned_ssize_t_ptr[
+ (sizeof(size_t) >= sizeof(void *)) ? 1 : -1
+];
+
+/*
+ * We set _FILE_OFFSET_BITS 64, but we only handle
+ * but we only need smaller files, so require 4-bytes.
+ * Some operating systems ignore the define, hence assert:
+ */
+typedef char static_assert_off_t_is_32[(sizeof(off_t) >= 4) ? 1 : -1];
+
+/*
+ * asserts (variables/defines sanity check)
+ */
+typedef char assert_argc3[(ARGC_3==3)?1:-1];
+typedef char assert_argc4[(ARGC_4==4)?1:-1];
+typedef char assert_read[(IO_READ==0)?1:-1];
+typedef char assert_write[(IO_WRITE==1)?1:-1];
+typedef char assert_pread[(IO_PREAD==2)?1:-1];
+typedef char assert_pwrite[(IO_PWRITE==3)?1:-1];
+typedef char assert_pathlen[(PATH_LEN>=256)?1:-1];
+/* commands */
+typedef char assert_cmd_dump[(CMD_DUMP==0)?1:-1];
+typedef char assert_cmd_setmac[(CMD_SETMAC==1)?1:-1];
+typedef char assert_cmd_swap[(CMD_SWAP==2)?1:-1];
+typedef char assert_cmd_copy[(CMD_COPY==3)?1:-1];
+typedef char assert_cmd_cat[(CMD_CAT==4)?1:-1];
+typedef char assert_cmd_cat16[(CMD_CAT16==5)?1:-1];
+typedef char assert_cmd_cat128[(CMD_CAT128==6)?1:-1];
+/* bool */
+typedef char bool_arg_nopart[(ARG_NOPART==0)?1:-1];
+typedef char bool_arg_part[(ARG_PART==1)?1:-1];
+typedef char bool_skip_checksum_read[(SKIP_CHECKSUM_READ==0)?1:-1];
+typedef char bool_checksum_read[(CHECKSUM_READ==1)?1:-1];
+typedef char bool_skip_checksum_write[(SKIP_CHECKSUM_WRITE==0)?1:-1];
+typedef char bool_checksum_write[(CHECKSUM_WRITE==1)?1:-1];
+typedef char bool_loop_eintr[(LOOP_EINTR==1||LOOP_EINTR==0)?1:-1];
+typedef char bool_loop_eagain[(LOOP_EAGAIN==1||LOOP_EAGAIN==0)?1:-1];
+typedef char bool_no_loop_eintr[(NO_LOOP_EINTR==0)?1:-1];
+typedef char bool_no_loop_eagain[(NO_LOOP_EAGAIN==0)?1:-1];
+typedef char bool_off_err[(OFF_ERR==0)?1:-1];
+typedef char bool_off_reset[(OFF_RESET==0||OFF_RESET==1)?1:-1];
+
+#endif
+#endif