diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-06-15 21:38:46 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-06-15 21:38:46 +0000 |
commit | 5cf2665928c8a445ae618d4eea618dcfaff583be (patch) | |
tree | 4cdaa88e6505f5a9cc97967952415b32a3383eff | |
parent | 802267b91d9dc1f571fa6a37df10e1b909f18aa7 (diff) | |
parent | 596ec1420a085648278e390b61ed1cadb5d08fc3 (diff) | |
download | fsverity-utils-android12-mainline-tzdata3-release.tar.gz |
Snap for 8730993 from 596ec1420a085648278e390b61ed1cadb5d08fc3 to mainline-tzdata3-releaseaml_tz3_314012070aml_tz3_314012050aml_tz3_314012010aml_tz3_313110000aml_tz3_312511020aml_tz3_312511010aml_tz3_312410020aml_tz3_312410010android12-mainline-tzdata3-releaseaml_tz3_314012010
Change-Id: I85efe739cc509903ddc7d490dbadfc75958428ee
-rw-r--r-- | .github/workflows/ci.yml | 169 | ||||
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | Android.bp | 6 | ||||
-rw-r--r-- | METADATA | 10 | ||||
-rw-r--r-- | Makefile | 56 | ||||
-rw-r--r-- | NEWS.md | 22 | ||||
-rw-r--r-- | README.md | 66 | ||||
-rw-r--r-- | common/fsverity_uapi.h | 14 | ||||
-rw-r--r-- | include/libfsverity.h | 106 | ||||
-rw-r--r-- | lib/compute_digest.c | 130 | ||||
-rw-r--r-- | lib/lib_private.h | 5 | ||||
-rw-r--r-- | lib/libfsverity.pc.in | 2 | ||||
-rw-r--r-- | lib/sign_digest.c | 96 | ||||
-rw-r--r-- | lib/utils.c | 34 | ||||
-rw-r--r-- | man/fsverity.1.md | 215 | ||||
-rw-r--r-- | programs/cmd_digest.c | 7 | ||||
-rw-r--r-- | programs/cmd_dump_metadata.c | 163 | ||||
-rw-r--r-- | programs/cmd_sign.c | 57 | ||||
-rw-r--r-- | programs/fsverity.c | 101 | ||||
-rw-r--r-- | programs/fsverity.h | 13 | ||||
-rw-r--r-- | programs/test_compute_digest.c | 133 | ||||
-rw-r--r-- | programs/utils.c | 59 | ||||
-rw-r--r-- | programs/utils.h | 3 | ||||
-rwxr-xr-x | scripts/do-release.sh | 95 | ||||
-rwxr-xr-x | scripts/run-sparse.sh | 2 | ||||
-rwxr-xr-x | scripts/run-tests.sh | 364 |
26 files changed, 277 insertions, 1655 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 309013a..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,169 +0,0 @@ -# SPDX-License-Identifier: MIT -# Copyright 2021 Google LLC -# -# Use of this source code is governed by an MIT-style -# license that can be found in the LICENSE file or at -# https://opensource.org/licenses/MIT. - -name: CI -on: [pull_request] - -jobs: - static-linking-test: - name: Test building static library - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - run: scripts/run-tests.sh static_linking - - dynamic-linking-test: - name: Test building dynamic library - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - run: scripts/run-tests.sh dynamic_linking - - cplusplus-test: - name: Test using library from C++ program - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - run: scripts/run-tests.sh cplusplus - - uninstall-test: - name: Test uninstalling - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - run: scripts/run-tests.sh uninstall - - dash-test: - name: Test building using the dash shell - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - run: scripts/run-tests.sh dash - - license-test: - name: Test for correct license info - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - run: scripts/run-tests.sh license - - gcc-test: - name: Test with gcc - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - run: scripts/run-tests.sh gcc - - clang-test: - name: Test with clang - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y clang - - run: scripts/run-tests.sh clang - - _32bit-test: - name: Test building 32-bit binaries - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Install dependencies - run: | - sudo dpkg --add-architecture i386 - sudo apt-get update - sudo apt-get install -y gcc-multilib libssl-dev:i386 - - run: scripts/run-tests.sh 32bit - - sanitizers-test: - name: Test with sanitizers enabled - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y clang llvm - - run: scripts/run-tests.sh sanitizers - - valgrind-test: - name: Test with valgrind enabled - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y valgrind - - run: scripts/run-tests.sh valgrind - - boringssl-test: - name: Test with BoringSSL - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Cache BoringSSL build - uses: actions/cache@v2 - with: - key: boringssl - path: boringssl - - run: make boringssl - - run: scripts/run-tests.sh boringssl - - char-test: - name: Test with unsigned/signed char - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - run: scripts/run-tests.sh unsigned_char signed_char - - # FIXME: need a Windows build of libcrypto for this to work - #windows-build-test: - #name: Windows build tests - #runs-on: ubuntu-latest - #steps: - #- uses: actions/checkout@v2 - #- name: Install dependencies - #run: | - #sudo apt-get update - #sudo apt-get install -y gcc-mingw-w64-i686 gcc-mingw-w64-x86-64 - # - run: scripts/run-tests.sh windows_build - - sparse-test: - name: Run sparse - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y sparse - - run: scripts/run-tests.sh sparse - - clang-analyzer-test: - name: Run clang static analyzer - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y clang-tools - - run: scripts/run-tests.sh clang_analyzer - - shellcheck-test: - name: Run shellcheck - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y shellcheck - - run: scripts/run-tests.sh shellcheck @@ -1,13 +1,9 @@ -*.[1-9] *.a -*.exe *.o *.patch *.so *.so.* /.build-config -/boringssl -/boringssl.tar.gz /fsverity /fsverity.sig /run-tests.log @@ -63,12 +63,6 @@ cc_library { export_include_dirs: ["include"], - apex_available: [ - "//apex_available:platform", - "com.android.compos", - ], - recovery_available: true, - srcs: [ "lib/*.c", ], @@ -5,12 +5,12 @@ third_party { type: GIT value: "https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/fsverity-utils.git" } - version: "20e87c13075a8e5660a8d69fd6c93d4f7c5f01a5" - license_note: "would be NOTICE save for common/fsverity_uapi.h" + version: "v1.3" + # would be NOTICE save for common/fsverity_uapi.h license_type: RESTRICTED last_upgrade_date { - year: 2022 - month: 2 - day: 7 + year: 2021 + month: 1 + day: 19 } } @@ -25,9 +25,6 @@ # Define LIBDIR to override where to install libraries, like './configure # --libdir' in autotools-based projects (default: PREFIX/lib) # -# Define MANDIR to override where to install man pages, like './configure -# --mandir' in autotools-based projects (default: PREFIX/share/man) -# # Define DESTDIR to override the installation destination directory # (default: empty string) # @@ -44,13 +41,8 @@ ifneq ($(findstring -mingw,$(shell $(CC) -dumpmachine 2>/dev/null)),) MINGW = 1 endif -# Set the CFLAGS. First give the warning-related flags (unconditionally, though -# the user can override any of them by specifying the opposite flag); then give -# the user-specified CFLAGS, defaulting to -O2 if none were specified. -# -# Use -Wno-deprecated-declarations to avoid warnings about the Engine API having -# been deprecated in OpenSSL 3.0; the replacement isn't ready yet. CFLAGS ?= -O2 + override CFLAGS := -Wall -Wundef \ $(call cc-option,-Wdeclaration-after-statement) \ $(call cc-option,-Wimplicit-fallthrough) \ @@ -59,7 +51,6 @@ override CFLAGS := -Wall -Wundef \ $(call cc-option,-Wstrict-prototypes) \ $(call cc-option,-Wunused-parameter) \ $(call cc-option,-Wvla) \ - $(call cc-option,-Wno-deprecated-declarations) \ $(CFLAGS) override CPPFLAGS := -Iinclude -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE $(CPPFLAGS) @@ -70,14 +61,12 @@ QUIET_CCLD = @echo ' CCLD ' $@; QUIET_AR = @echo ' AR ' $@; QUIET_LN = @echo ' LN ' $@; QUIET_GEN = @echo ' GEN ' $@; -QUIET_PANDOC = @echo ' PANDOC ' $@; endif USE_SHARED_LIB ?= PREFIX ?= /usr/local BINDIR ?= $(PREFIX)/bin INCDIR ?= $(PREFIX)/include LIBDIR ?= $(PREFIX)/lib -MANDIR ?= $(PREFIX)/share/man DESTDIR ?= ifneq ($(MINGW),1) PKGCONF ?= pkg-config @@ -103,7 +92,6 @@ FSVERITY := fsverity$(EXEEXT) fi DEFAULT_TARGETS := -EXTRA_TARGETS := COMMON_HEADERS := $(wildcard common/*.h) LDLIBS := $(shell "$(PKGCONF)" libcrypto --libs 2>/dev/null || echo -lcrypto) CFLAGS += $(shell "$(PKGCONF)" libcrypto --cflags 2>/dev/null || echo) @@ -172,7 +160,6 @@ FSVERITY_PROG_OBJ := $(PROG_COMMON_OBJ) \ programs/fsverity.o ifneq ($(MINGW),1) FSVERITY_PROG_OBJ += \ - programs/cmd_dump_metadata.o \ programs/cmd_enable.o \ programs/cmd_measure.o endif @@ -199,37 +186,9 @@ DEFAULT_TARGETS += $(FSVERITY) $(TEST_PROGRAMS): %$(EXEEXT): programs/%.o $(PROG_COMMON_OBJ) libfsverity.a $(QUIET_CCLD) $(CC) -o $@ $+ $(CFLAGS) $(LDFLAGS) $(LDLIBS) -EXTRA_TARGETS += $(TEST_PROGRAMS) - ############################################################################## -#### Manual pages - -man/fsverity.1:man/fsverity.1.md - $(QUIET_PANDOC) pandoc $+ -s -t man > $@ - -MAN_PAGES := man/fsverity.1 -EXTRA_TARGETS += $(MAN_PAGES) - -############################################################################## - -# Support for downloading and building BoringSSL. The purpose of this is to -# allow testing builds of fsverity-utils that link to BoringSSL instead of -# OpenSSL, without having to use a system that uses BoringSSL natively. - -boringssl: - rm -rf boringssl boringssl.tar.gz - curl -s -o boringssl.tar.gz \ - https://boringssl.googlesource.com/boringssl/+archive/refs/heads/master.tar.gz - mkdir boringssl - tar xf boringssl.tar.gz -C boringssl - cmake -B boringssl/build boringssl - $(MAKE) -C boringssl/build $(MAKEFLAGS) - -############################################################################## - -SPECIAL_TARGETS := all test_programs check install install-man uninstall \ - help clean +SPECIAL_TARGETS := all test_programs check install uninstall help clean FORCE: @@ -273,10 +232,6 @@ install:all > $(DESTDIR)$(LIBDIR)/pkgconfig/libfsverity.pc chmod 644 $(DESTDIR)$(LIBDIR)/pkgconfig/libfsverity.pc -install-man:$(MAN_PAGES) - install -d $(DESTDIR)$(MANDIR)/man1 - install -m644 $+ $(DESTDIR)$(MANDIR)/man1/ - uninstall: rm -f $(DESTDIR)$(BINDIR)/$(FSVERITY) rm -f $(DESTDIR)$(LIBDIR)/libfsverity.a @@ -284,18 +239,15 @@ uninstall: rm -f $(DESTDIR)$(LIBDIR)/libfsverity.so rm -f $(DESTDIR)$(LIBDIR)/pkgconfig/libfsverity.pc rm -f $(DESTDIR)$(INCDIR)/libfsverity.h - for page in $(notdir $(MAN_PAGES)); do \ - rm -f $(DESTDIR)$(MANDIR)/man1/$$page; \ - done help: @echo "Available targets:" @echo "------------------" - @for target in $(DEFAULT_TARGETS) $(EXTRA_TARGETS) $(SPECIAL_TARGETS); \ + @for target in $(DEFAULT_TARGETS) $(TEST_PROGRAMS) $(SPECIAL_TARGETS); \ do \ echo $$target; \ done clean: - rm -f $(DEFAULT_TARGETS) $(EXTRA_TARGETS) \ + rm -f $(DEFAULT_TARGETS) $(TEST_PROGRAMS) \ lib/*.o programs/*.o .build-config fsverity.sig @@ -1,27 +1,5 @@ # fsverity-utils release notes -## Version 1.5 - -* Made the `fsverity sign` command and the `libfsverity_sign_digest()` function - support PKCS#11 tokens. - -* Avoided a compiler error when building with musl libc. - -* Avoided compiler warnings when building with OpenSSL 3.0. - -* Improved documentation and test scripts. - -## Version 1.4 - -* Added a manual page for the `fsverity` utility. - -* Added the `fsverity dump_metadata` subcommand. - -* Added the `--out-merkle-tree` and `--out-descriptor` options to - `fsverity digest` and `fsverity sign`. - -* Added metadata callbacks support to `libfsverity_compute_digest()`. - ## Version 1.3 * Added a `fsverity digest` subcommand. @@ -24,22 +24,20 @@ See `libfsverity.h` for the API of this library. ## Building and installing -To build fsverity-utils, first install the needed build dependencies. For -example, on Debian-based systems, run: +fsverity-utils uses the OpenSSL library, so you first must install the +needed development files. For example, on Debian-based systems, run: ```bash sudo apt-get install libssl-dev - sudo apt-get install pandoc # optional ``` -OpenSSL must be version 1.0.0 or later. This is the only runtime dependency. +OpenSSL must be version 1.0.0 or later. Then, to build and install fsverity-utils: ```bash make sudo make install - sudo make install-man # optional ``` By default, the following targets are built and installed: the program @@ -47,9 +45,6 @@ By default, the following targets are built and installed: the program `libfsverity.so`. You can also run `make check` to build and run the tests, or `make help` to display all available build targets. -`make install-man` installs the `fsverity.1` manual page. This step requires -that `pandoc` be installed. - By default, `fsverity` is statically linked to `libfsverity`. You can use `make USE_SHARED_LIB=1` to use dynamic linking instead. @@ -68,9 +63,6 @@ A Windows build of OpenSSL/libcrypto needs to be available. ## Examples -Full usage information for `fsverity` can be found in the manual page -(`man fsverity`). Here, we just show some typical examples. - ### Basic use ```bash @@ -102,44 +94,11 @@ against a trusted value. ### Using builtin signatures -First, note that fs-verity is essentially just a way of hashing a -file; it doesn't mandate a specific way of handling signatures. -There are several possible ways that signatures could be handled: - -* Do it entirely in userspace -* Use IMA appraisal (work-in-progress) -* Use fs-verity built-in signatures - -Any such solution needs two parts: (a) a policy that determines which -files are required to have fs-verity enabled and have a valid -signature, and (b) enforcement of the policy. Each part could happen -either in a trusted userspace program(s) or in the kernel. - -fs-verity built-in signatures (which are supported when the kernel was -built with `CONFIG_FS_VERITY_BUILTIN_SIGNATURES=y`) are a hybrid -solution where the policy of which files are required to be signed is -determined and enforced by a trusted userspace program, but the actual -signature verification happens in the kernel. Specifically, with -built-in signatures, the filesystem supports storing a signed file -digest in each file's verity metadata. Before allowing access to the -file, the filesystem will automatically verify the signature against -the set of X.509 certificates in the ".fs-verity" kernel keyring. If -set, the sysctl `fs.verity.require_signatures=1` will make the kernel -enforce that every verity file has a valid built-in signature. - -fs-verity built-in signatures are primarily intended as a -proof-of-concept; they reuse the kernel code that verifies the -signatures of loadable kernel modules. This solution still requires a -trusted userspace program to enforce that particular files have -fs-verity enabled. Also, this solution uses PKCS#7 signatures, which -are complex and prone to security bugs. - -Thus, if possible one of the other solutions should be used instead. -For example, the trusted userspace program could verify signatures -itself, using a simple signature format using a modern algorithm such -as Ed25519. - -That being said, here are some examples of using built-in signatures: +With `CONFIG_FS_VERITY_BUILTIN_SIGNATURES=y`, the filesystem supports +automatically verifying a signed file digest that has been included in +the verity metadata. The signature is verified against the set of +X.509 certificates that have been loaded into the ".fs-verity" kernel +keyring. Here's an example: ```bash # Generate a new certificate and private key: @@ -170,6 +129,15 @@ That being said, here are some examples of using built-in signatures: fsverity digest file --compact --for-builtin-sig | tr -d '\n' | xxd -p -r | openssl smime -sign -in /dev/stdin ... ``` +By default, it's not required that verity files have a signature. +This can be changed with `sysctl fs.verity.require_signatures=1`. +When set, it's guaranteed that the contents of every verity file has +been signed by one of the certificates in the keyring. + +Note: applications generally still need to check whether the file +they're accessing really is a verity file, since an attacker could +replace a verity file with a regular one. + ### With IMA IMA support for fs-verity is planned. diff --git a/common/fsverity_uapi.h b/common/fsverity_uapi.h index c59a897..a739c9a 100644 --- a/common/fsverity_uapi.h +++ b/common/fsverity_uapi.h @@ -85,21 +85,7 @@ struct fsverity_formatted_digest { __u8 digest[]; }; -#define FS_VERITY_METADATA_TYPE_MERKLE_TREE 1 -#define FS_VERITY_METADATA_TYPE_DESCRIPTOR 2 -#define FS_VERITY_METADATA_TYPE_SIGNATURE 3 - -struct fsverity_read_metadata_arg { - __u64 metadata_type; - __u64 offset; - __u64 length; - __u64 buf_ptr; - __u64 __reserved; -}; - #define FS_IOC_ENABLE_VERITY _IOW('f', 133, struct fsverity_enable_arg) #define FS_IOC_MEASURE_VERITY _IOWR('f', 134, struct fsverity_digest) -#define FS_IOC_READ_VERITY_METADATA \ - _IOWR('f', 135, struct fsverity_read_metadata_arg) #endif /* _UAPI_LINUX_FSVERITY_H */ diff --git a/include/libfsverity.h b/include/libfsverity.h index a0a1527..6c42e5e 100644 --- a/include/libfsverity.h +++ b/include/libfsverity.h @@ -22,7 +22,7 @@ extern "C" { #include <stdint.h> #define FSVERITY_UTILS_MAJOR_VERSION 1 -#define FSVERITY_UTILS_MINOR_VERSION 5 +#define FSVERITY_UTILS_MINOR_VERSION 3 #define FS_VERITY_HASH_ALG_SHA256 1 #define FS_VERITY_HASH_ALG_SHA512 2 @@ -61,18 +61,8 @@ struct libfsverity_merkle_tree_params { /** @reserved1: must be 0 */ uint64_t reserved1[8]; - /** - * @metadata_callbacks: if non-NULL, this gives a set of callback - * functions to which libfsverity_compute_digest() will pass the Merkle - * tree blocks and fs-verity descriptor after they are computed. - * Normally this isn't useful, but this can be needed in rare cases - * where the metadata needs to be consumed by something other than one - * of the native Linux kernel implementations of fs-verity. - */ - const struct libfsverity_metadata_callbacks *metadata_callbacks; - /** @reserved2: must be 0 */ - uintptr_t reserved2[7]; + uintptr_t reserved2[8]; }; struct libfsverity_digest { @@ -81,75 +71,11 @@ struct libfsverity_digest { uint8_t digest[]; /* the actual digest */ }; -/** - * struct libfsverity_signature_params - certificate and private key information - * - * Zero this, then set @certfile. Then, to specify the private key by key file, - * set @keyfile. Alternatively, to specify the private key by PKCS#11 token, - * set @pkcs11_engine, @pkcs11_module, and optionally @pkcs11_keyid. - * - * Support for PKCS#11 tokens is unavailable when libfsverity was linked to - * BoringSSL rather than OpenSSL. - */ struct libfsverity_signature_params { - - /** @keyfile: the path to the key file in PEM format, when applicable */ - const char *keyfile; - - /** @certfile: the path to the certificate file in PEM format */ - const char *certfile; - - /** @reserved1: must be 0 */ - uint64_t reserved1[8]; - - /** - * @pkcs11_engine: the path to the PKCS#11 engine .so file, when - * applicable - */ - const char *pkcs11_engine; - - /** - * @pkcs11_module: the path to the PKCS#11 module .so file, when - * applicable - */ - const char *pkcs11_module; - - /** @pkcs11_keyid: the PKCS#11 key identifier, when applicable */ - const char *pkcs11_keyid; - - /** @reserved2: must be 0 */ - uintptr_t reserved2[5]; -}; - -struct libfsverity_metadata_callbacks { - - /** @ctx: context passed to the below callbacks (opaque to library) */ - void *ctx; - - /** - * @merkle_tree_size: if non-NULL, called with the total size of the - * Merkle tree in bytes, prior to any call to @merkle_tree_block. Must - * return 0 on success, or a negative errno value on failure. - */ - int (*merkle_tree_size)(void *ctx, uint64_t size); - - /** - * @merkle_tree_block: if non-NULL, called with each block of the - * Merkle tree after it is computed. The offset is the offset in bytes - * to the block within the Merkle tree, using the Merkle tree layout - * used by FS_IOC_READ_VERITY_METADATA. The offsets won't necessarily - * be in increasing order. Must return 0 on success, or a negative - * errno value on failure. - */ - int (*merkle_tree_block)(void *ctx, const void *block, size_t size, - uint64_t offset); - - /** - * @descriptor: if non-NULL, called with the fs-verity descriptor after - * it is computed. Must return 0 on success, or a negative errno value - * on failure. - */ - int (*descriptor)(void *ctx, const void *descriptor, size_t size); + const char *keyfile; /* path to key file (PEM format) */ + const char *certfile; /* path to certificate (PEM format) */ + uint64_t reserved1[8]; /* must be 0 */ + uintptr_t reserved2[8]; /* must be 0 */ }; /* @@ -175,8 +101,7 @@ typedef int (*libfsverity_read_fn_t)(void *fd, void *buf, size_t count); * * Returns: * * 0 for success, -EINVAL for invalid input arguments, -ENOMEM if libfsverity - * failed to allocate memory, or an error returned by @read_fn or by one of - * the @params->metadata_callbacks. + * failed to allocate memory, or an error returned by @read_fn. * * digest_ret returns a pointer to the digest on success. The digest object * is allocated by libfsverity and must be freed by the caller using free(). */ @@ -186,15 +111,16 @@ libfsverity_compute_digest(void *fd, libfsverity_read_fn_t read_fn, struct libfsverity_digest **digest_ret); /** - * libfsverity_sign_digest() - Sign a file for built-in signature verification - * Sign a file digest in a way that is compatible with the Linux - * kernel's fs-verity built-in signature verification support. The - * resulting signature will be a PKCS#7 message in DER format. Note - * that this is not the only way to do signatures with fs-verity. For - * more details, refer to the fsverity-utils README and to - * Documentation/filesystems/fsverity.rst in the kernel source tree. + * libfsverity_sign_digest() - Sign previously computed digest of a file + * This signature is used by the filesystem to validate the signed file + * digest against a public key loaded into the .fs-verity kernel + * keyring, when CONFIG_FS_VERITY_BUILTIN_SIGNATURES is enabled. The + * signature is formatted as PKCS#7 stored in DER format. See + * Documentation/filesystems/fsverity.rst in the kernel source tree for + * further details. * @digest: pointer to previously computed digest - * @sig_params: pointer to the certificate and private key information + * @sig_params: struct libfsverity_signature_params providing filenames of + * the keyfile and certificate file. Reserved fields must be zero. * @sig_ret: Pointer to pointer for signed digest * @sig_size_ret: Pointer to size of signed return digest * diff --git a/lib/compute_digest.c b/lib/compute_digest.c index c5b0100..a4f649c 100644 --- a/lib/compute_digest.c +++ b/lib/compute_digest.c @@ -24,8 +24,9 @@ struct block_buffer { /* * Hash a block, writing the result to the next level's pending block buffer. + * Returns true if the next level's block became full, else false. */ -static void hash_one_block(struct hash_ctx *hash, struct block_buffer *cur, +static bool hash_one_block(struct hash_ctx *hash, struct block_buffer *cur, u32 block_size, const u8 *salt, u32 salt_size) { struct block_buffer *next = cur + 1; @@ -40,60 +41,8 @@ static void hash_one_block(struct hash_ctx *hash, struct block_buffer *cur, next->filled += hash->alg->digest_size; cur->filled = 0; -} - -static bool block_is_full(const struct block_buffer *block, u32 block_size, - struct hash_ctx *hash) -{ - /* Would the next hash put us over the limit? */ - return block->filled + hash->alg->digest_size > block_size; -} - -static int report_merkle_tree_size(const struct libfsverity_metadata_callbacks *cbs, - u64 size) -{ - if (cbs && cbs->merkle_tree_size) { - int err = cbs->merkle_tree_size(cbs->ctx, size); - if (err) { - libfsverity_error_msg("error processing Merkle tree size"); - return err; - } - } - return 0; -} - -static int report_merkle_tree_block(const struct libfsverity_metadata_callbacks *cbs, - const struct block_buffer *block, - u32 block_size, u64 *level_offset) -{ - - if (cbs && cbs->merkle_tree_block) { - int err = cbs->merkle_tree_block(cbs->ctx, block->data, - block_size, - *level_offset * block_size); - - if (err) { - libfsverity_error_msg("error processing Merkle tree block"); - return err; - } - (*level_offset)++; - } - return 0; -} - -static int report_descriptor(const struct libfsverity_metadata_callbacks *cbs, - const void *descriptor, size_t size) -{ - if (cbs && cbs->descriptor) { - int err = cbs->descriptor(cbs->ctx, descriptor, size); - - if (err) { - libfsverity_error_msg("error processing fs-verity descriptor"); - return err; - } - } - return 0; + return next->filled + hash->alg->digest_size > block_size; } /* @@ -103,7 +52,6 @@ static int report_descriptor(const struct libfsverity_metadata_callbacks *cbs, static int compute_root_hash(void *fd, libfsverity_read_fn_t read_fn, u64 file_size, struct hash_ctx *hash, u32 block_size, const u8 *salt, u32 salt_size, - const struct libfsverity_metadata_callbacks *metadata_cbs, u8 *root_hash) { const u32 hashes_per_block = block_size / hash->alg->digest_size; @@ -112,7 +60,6 @@ static int compute_root_hash(void *fd, libfsverity_read_fn_t read_fn, u64 blocks; int num_levels = 0; int level; - u64 level_offset[FS_VERITY_MAX_LEVELS]; struct block_buffer _buffers[1 + FS_VERITY_MAX_LEVELS + 1] = {}; struct block_buffer *buffers = &_buffers[1]; u64 offset; @@ -121,7 +68,7 @@ static int compute_root_hash(void *fd, libfsverity_read_fn_t read_fn, /* Root hash of empty file is all 0's */ if (file_size == 0) { memset(root_hash, 0, hash->alg->digest_size); - return report_merkle_tree_size(metadata_cbs, 0); + return 0; } if (salt_size != 0) { @@ -131,39 +78,15 @@ static int compute_root_hash(void *fd, libfsverity_read_fn_t read_fn, memcpy(padded_salt, salt, salt_size); } - /* Compute number of levels and the number of blocks in each level. */ - blocks = DIV_ROUND_UP(file_size, block_size); - while (blocks > 1) { + /* Compute number of levels */ + for (blocks = DIV_ROUND_UP(file_size, block_size); blocks > 1; + blocks = DIV_ROUND_UP(blocks, hashes_per_block)) { if (WARN_ON(num_levels >= FS_VERITY_MAX_LEVELS)) { err = -EINVAL; goto out; } - blocks = DIV_ROUND_UP(blocks, hashes_per_block); - /* - * Temporarily use level_offset[] to store the number of blocks - * in each level. It will be overwritten later. - */ - level_offset[num_levels++] = blocks; - } - - /* - * Compute the starting block of each level, using the convention where - * the root level is first, i.e. the convention used by - * FS_IOC_READ_VERITY_METADATA. At the same time, compute the total - * size of the Merkle tree. These values are only needed for the - * metadata callbacks (if they were given), as the hash computation - * itself doesn't prescribe an ordering of the levels and doesn't - * prescribe any special meaning to the total size of the Merkle tree. - */ - offset = 0; - for (level = num_levels - 1; level >= 0; level--) { - blocks = level_offset[level]; - level_offset[level] = offset; - offset += blocks; + num_levels++; } - err = report_merkle_tree_size(metadata_cbs, offset * block_size); - if (err) - goto out; /* * Allocate the block buffers. Buffer "-1" is for data blocks. @@ -189,33 +112,21 @@ static int compute_root_hash(void *fd, libfsverity_read_fn_t read_fn, goto out; } - hash_one_block(hash, &buffers[-1], block_size, - padded_salt, padded_salt_size); - for (level = 0; level < num_levels; level++) { - if (!block_is_full(&buffers[level], block_size, hash)) - break; - hash_one_block(hash, &buffers[level], block_size, - padded_salt, padded_salt_size); - err = report_merkle_tree_block(metadata_cbs, - &buffers[level], - block_size, - &level_offset[level]); - if (err) + level = -1; + while (hash_one_block(hash, &buffers[level], block_size, + padded_salt, padded_salt_size)) { + level++; + if (WARN_ON(level >= num_levels)) { + err = -EINVAL; goto out; + } } } /* Finish all nonempty pending tree blocks */ for (level = 0; level < num_levels; level++) { - if (buffers[level].filled != 0) { + if (buffers[level].filled != 0) hash_one_block(hash, &buffers[level], block_size, padded_salt, padded_salt_size); - err = report_merkle_tree_block(metadata_cbs, - &buffers[level], - block_size, - &level_offset[level]); - if (err) - goto out; - } } /* Root hash was filled by the last call to hash_one_block() */ @@ -306,13 +217,8 @@ libfsverity_compute_digest(void *fd, libfsverity_read_fn_t read_fn, } err = compute_root_hash(fd, read_fn, params->file_size, hash, - block_size, params->salt, params->salt_size, - params->metadata_callbacks, desc.root_hash); - if (err) - goto out; - - err = report_descriptor(params->metadata_callbacks, - &desc, sizeof(desc)); + block_size, params->salt, + params->salt_size, desc.root_hash); if (err) goto out; diff --git a/lib/lib_private.h b/lib/lib_private.h index 8532636..7768eea 100644 --- a/lib/lib_private.h +++ b/lib/lib_private.h @@ -58,11 +58,14 @@ void *libfsverity_zalloc(size_t size); void *libfsverity_memdup(const void *mem, size_t size); __cold void -libfsverity_do_error_msg(const char *format, va_list va); +libfsverity_do_error_msg(const char *format, va_list va, int err); __printf(1, 2) __cold void libfsverity_error_msg(const char *format, ...); +__printf(1, 2) __cold void +libfsverity_error_msg_errno(const char *format, ...); + __cold void libfsverity_warn_on(const char *condition, const char *file, int line); diff --git a/lib/libfsverity.pc.in b/lib/libfsverity.pc.in index 4c9bb20..be3ef44 100644 --- a/lib/libfsverity.pc.in +++ b/lib/libfsverity.pc.in @@ -4,7 +4,7 @@ includedir=@INCDIR@ Name: libfsverity Description: fs-verity library -Version: 1.5 +Version: 1.3 Libs: -L${libdir} -lfsverity Requires.private: libcrypto Cflags: -I${includedir} diff --git a/lib/sign_digest.c b/lib/sign_digest.c index d726772..9a35256 100644 --- a/lib/sign_digest.c +++ b/lib/sign_digest.c @@ -19,10 +19,6 @@ #include <openssl/pkcs7.h> #include <string.h> -#ifndef OPENSSL_IS_BORINGSSL -#include <openssl/engine.h> -#endif - static int print_openssl_err_cb(const char *str, size_t len __attribute__((unused)), void *u __attribute__((unused))) @@ -38,7 +34,7 @@ error_msg_openssl(const char *format, ...) va_list va; va_start(va, format); - libfsverity_do_error_msg(format, va); + libfsverity_do_error_msg(format, va, 0); va_end(va); if (ERR_peek_error() == 0) @@ -85,11 +81,6 @@ static int read_certificate(const char *certfile, X509 **cert_ret) X509 *cert; int err; - if (!certfile) { - libfsverity_error_msg("no certificate specified"); - return -EINVAL; - } - errno = 0; bio = BIO_new_file(certfile, "r"); if (!bio) { @@ -221,15 +212,6 @@ out: return err; } -static int -load_pkcs11_private_key(const struct libfsverity_signature_params *sig_params - __attribute__((unused)), - EVP_PKEY **pkey_ret __attribute__((unused))) -{ - libfsverity_error_msg("BoringSSL doesn't support PKCS#11 tokens"); - return -EINVAL; -} - #else /* OPENSSL_IS_BORINGSSL */ static BIO *new_mem_buf(const void *buf, size_t size) @@ -333,79 +315,16 @@ out: return err; } -static int -load_pkcs11_private_key(const struct libfsverity_signature_params *sig_params, - EVP_PKEY **pkey_ret) -{ - ENGINE *engine; - - if (!sig_params->pkcs11_engine) { - libfsverity_error_msg("no PKCS#11 engine specified"); - return -EINVAL; - } - if (!sig_params->pkcs11_module) { - libfsverity_error_msg("no PKCS#11 module specified"); - return -EINVAL; - } - ENGINE_load_dynamic(); - engine = ENGINE_by_id("dynamic"); - if (!engine) { - error_msg_openssl("failed to initialize OpenSSL PKCS#11 engine"); - return -EINVAL; - } - if (!ENGINE_ctrl_cmd_string(engine, "SO_PATH", - sig_params->pkcs11_engine, 0) || - !ENGINE_ctrl_cmd_string(engine, "ID", "pkcs11", 0) || - !ENGINE_ctrl_cmd_string(engine, "LIST_ADD", "1", 0) || - !ENGINE_ctrl_cmd_string(engine, "LOAD", NULL, 0) || - !ENGINE_ctrl_cmd_string(engine, "MODULE_PATH", - sig_params->pkcs11_module, 0) || - !ENGINE_init(engine)) { - error_msg_openssl("failed to initialize OpenSSL PKCS#11 engine"); - ENGINE_free(engine); - return -EINVAL; - } - *pkey_ret = ENGINE_load_private_key(engine, sig_params->pkcs11_keyid, - NULL, NULL); - ENGINE_finish(engine); - ENGINE_free(engine); - if (!*pkey_ret) { - error_msg_openssl("failed to load private key from PKCS#11 token"); - return -EINVAL; - } - return 0; -} - #endif /* !OPENSSL_IS_BORINGSSL */ -/* Get a private key, either from disk or from a PKCS#11 token. */ -static int -get_private_key(const struct libfsverity_signature_params *sig_params, - EVP_PKEY **pkey_ret) -{ - if (sig_params->pkcs11_engine || sig_params->pkcs11_module || - sig_params->pkcs11_keyid) { - if (sig_params->keyfile) { - libfsverity_error_msg("private key must be specified either by file or by PKCS#11 token, not both"); - return -EINVAL; - } - return load_pkcs11_private_key(sig_params, pkey_ret); - } - if (!sig_params->keyfile) { - libfsverity_error_msg("no private key specified"); - return -EINVAL; - } - return read_private_key(sig_params->keyfile, pkey_ret); -} - LIBEXPORT int libfsverity_sign_digest(const struct libfsverity_digest *digest, const struct libfsverity_signature_params *sig_params, u8 **sig_ret, size_t *sig_size_ret) { const struct fsverity_hash_alg *hash_alg; - X509 *cert = NULL; EVP_PKEY *pkey = NULL; + X509 *cert = NULL; const EVP_MD *md; struct fsverity_formatted_digest *d = NULL; int err; @@ -415,6 +334,11 @@ libfsverity_sign_digest(const struct libfsverity_digest *digest, return -EINVAL; } + if (!sig_params->keyfile || !sig_params->certfile) { + libfsverity_error_msg("keyfile and certfile must be specified"); + return -EINVAL; + } + if (!libfsverity_mem_is_zeroed(sig_params->reserved1, sizeof(sig_params->reserved1)) || !libfsverity_mem_is_zeroed(sig_params->reserved2, @@ -429,11 +353,11 @@ libfsverity_sign_digest(const struct libfsverity_digest *digest, return -EINVAL; } - err = read_certificate(sig_params->certfile, &cert); + err = read_private_key(sig_params->keyfile, &pkey); if (err) goto out; - err = get_private_key(sig_params, &pkey); + err = read_certificate(sig_params->certfile, &cert); if (err) goto out; @@ -459,8 +383,8 @@ libfsverity_sign_digest(const struct libfsverity_digest *digest, err = sign_pkcs7(d, sizeof(*d) + digest->digest_size, pkey, cert, md, sig_ret, sig_size_ret); out: - X509_free(cert); EVP_PKEY_free(pkey); + X509_free(cert); free(d); return err; } diff --git a/lib/utils.c b/lib/utils.c index d506ef1..036dd60 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -51,7 +51,16 @@ libfsverity_set_error_callback(void (*cb)(const char *msg)) libfsverity_error_cb = cb; } -void libfsverity_do_error_msg(const char *format, va_list va) +#ifdef _WIN32 +static char *strerror_r(int errnum, char *buf, size_t buflen) +{ + strerror_s(buf, buflen, errnum); + + return buf; +} +#endif + +void libfsverity_do_error_msg(const char *format, va_list va, int err) { int saved_errno = errno; char *msg = NULL; @@ -62,8 +71,18 @@ void libfsverity_do_error_msg(const char *format, va_list va) if (vasprintf(&msg, format, va) < 0) goto out; - (*libfsverity_error_cb)(msg); + if (err) { + char *msg2 = NULL; + char errbuf[64]; + if (asprintf(&msg2, "%s: %s", msg, + strerror_r(err, errbuf, sizeof(errbuf))) < 0) + goto out2; + free(msg); + msg = msg2; + } + (*libfsverity_error_cb)(msg); +out2: free(msg); out: errno = saved_errno; @@ -74,7 +93,16 @@ void libfsverity_error_msg(const char *format, ...) va_list va; va_start(va, format); - libfsverity_do_error_msg(format, va); + libfsverity_do_error_msg(format, va, 0); + va_end(va); +} + +void libfsverity_error_msg_errno(const char *format, ...) +{ + va_list va; + + va_start(va, format); + libfsverity_do_error_msg(format, va, errno); va_end(va); } diff --git a/man/fsverity.1.md b/man/fsverity.1.md deleted file mode 100644 index dd54964..0000000 --- a/man/fsverity.1.md +++ /dev/null @@ -1,215 +0,0 @@ -% FSVERITY(1) fsverity-utils v1.5 | User Commands -% -% February 2022 - -# NAME - -fsverity - userspace utility for fs-verity - -# SYNOPSIS -**fsverity digest** [*OPTION*...] *FILE*... \ -**fsverity dump_metadata** [*OPTION*...] *TYPE* *FILE* \ -**fsverity enable** [*OPTION*...] *FILE* \ -**fsverity measure** *FILE*... \ -**fsverity sign** [*OPTION*...] *FILE* *OUT_SIGFILE* - -# DESCRIPTION - -**fsverity** is a userspace utility for fs-verity. fs-verity is a Linux kernel -filesystem feature that does transparent on-demand verification of the contents -of read-only files using Merkle trees. - -**fsverity** can enable fs-verity on files, retrieve the digests of fs-verity -files, and sign files for use with fs-verity (among other things). -**fsverity**'s functionality is divided among various subcommands. - -This manual page focuses on documenting all **fsverity** subcommands and -options. For examples and more information about the fs-verity kernel feature, -see the references at the end of this page. - -# OPTIONS - -**fsverity** always accepts the following options: - -**\-\-help** -: Show the help, for either one subcommand or for all subcommands. - -**\-\-version** -: Show the version of fsverity-utils. - -# SUBCOMMANDS - -## **fsverity digest** [*OPTION*...] *FILE*... - -Compute the fs-verity digest of the given file(s). This is mainly intended to -used in preparation for signing the digest. In some cases **fsverity sign** -can be used instead to digest and sign the file in one step. - -Options accepted by **fsverity digest**: - -**\-\-block-size**=*BLOCK_SIZE* -: The Merkle tree block size (in bytes) to use. This must be a power of 2 and - at least twice the size of the hash values. However, note that currently - (as of Linux kernel v5.13), the Linux kernel implementations of fs-verity - only support the case where the Merkle tree block size is equal to the - system page size, usually 4096 bytes. The default value of this option is - 4096. - -**\-\-compact** -: When printing the file digest, only print the actual digest hex string; - don't print the algorithm name and filename. - -**\-\-for-builtin-sig** -: Format the file digest in a way that is compatible with the Linux kernel's - fs-verity built-in signature verification support. This means formatting it - as a `struct fsverity_formatted_digest`. Use this option if you are using - built-in signatures but are not using **fsverity sign** to do the signing. - -**\-\-hash-alg**=*HASH_ALG* -: The hash algorithm to use to build the Merkle tree. Valid options are - sha256 and sha512. Default is sha256. - -**\-\-out-merkle-tree**=*FILE* -: Write the computed Merkle tree to the given file. The Merkle tree layout - will be the same as that used by the Linux kernel's - `FS_IOC_READ_VERITY_METADATA` ioctl. - - Normally this option isn't useful, but it can be needed in cases where the - fs-verity metadata needs to be consumed by something other than one of the - native Linux kernel implementations of fs-verity. This is not needed for - file signing. - -**\-\-out-descriptor**=*FILE* -: Write the computed fs-verity descriptor to the given file. - - Normally this option isn't useful, but it can be needed in cases where the - fs-verity metadata needs to be consumed by something other than one of the - native Linux kernel implementations of fs-verity. This is not needed for - file signing. - -**\-\-salt**=*SALT* -: The salt to use in the Merkle tree, as a hex string. The salt is a value - that is prepended to every hashed block; it can be used to personalize the - hashing for a particular file or device. The default is no salt. - -## **fsverity dump_metadata** [*OPTION*...] *TYPE* *FILE* - -Dump the fs-verity metadata of the given file. The file must have fs-verity -enabled, and the filesystem must support the `FS_IOC_READ_VERITY_METADATA` ioctl -(it was added in Linux v5.12). This subcommand normally isn't useful, but it -can be useful in cases where a userspace server program is serving a verity file -to a client which implements fs-verity compatible verification. - -*TYPE* may be "merkle\_tree", "descriptor", or "signature", indicating the type -of metadata to dump. "signature" refers to the built-in signature, if present; -userspace-managed signatures will not be included. - -Options accepted by **fsverity dump_metadata**: - -**\-\-length**=*LENGTH* -: Length in bytes to dump from the specified metadata item. Only accepted in - combination with **\-\-offset**. - -**\-\-offset**=*offset* -: Offset in bytes into the specified metadata item at which to start dumping. - Only accepted in combination with **\-\-length**. - -## **fsverity enable** [*OPTION*...] *FILE* - -Enable fs-verity on the specified file. This will only work if the filesystem -supports fs-verity. - -Options accepted by **fsverity enable**: - -**\-\-block-size**=*BLOCK_SIZE* -: Same as for **fsverity digest**. - -**\-\-hash-alg**=*HASH_ALG* -: Same as for **fsverity digest**. - -**\-\-salt**=*SALT* -: Same as for **fsverity digest**. - -**\-\-signature**=*SIGFILE* -: Specifies the built-in signature to apply to the file. *SIGFILE* must be a - file that contains the signature in PKCS#7 DER format, e.g. as produced by - the **fsverity sign** command. - - Note that this option is only needed if the Linux kernel's fs-verity - built-in signature verification support is being used. It is not needed if - the signatures will be verified in userspace, as in that case the signatures - should be stored separately. - -## **fsverity measure** *FILE*... - -Display the fs-verity digest of the given file(s). The files must have -fs-verity enabled. The output will be the same as **fsverity digest** with -the appropriate parameters, but **fsverity measure** will take constant time -for each file regardless of the size of the file. - -**fsverity measure** does not accept any options. - -## **fsverity sign** [*OPTION*...] *FILE* *OUT_SIGFILE* - -Sign the given file for fs-verity, in a way that is compatible with the Linux -kernel's fs-verity built-in signature verification support. The signature will -be written to *OUT_SIGFILE* in PKCS#7 DER format. - -The private key can be specified either by key file or by PKCS#11 token. To use -a key file, provide **\-\-key** and optionally **\-\-cert**. To use a PKCS#11 -token, provide **\-\-pkcs11-engine**, **\-\-pkcs11-module**, **\-\-cert**, and -optionally **\-\-pkcs11-keyid**. PKCS#11 token support is unavailable when -fsverity-utils was built with BoringSSL rather than OpenSSL. - -**fsverity sign** should only be used if you need compatibility with fs-verity -built-in signatures. It is not the only way to do signatures with fs-verity. -For more information, see the fsverity-utils README. - -Options accepted by **fsverity sign**: - -**\-\-block-size**=*BLOCK_SIZE* -: Same as for **fsverity digest**. - -**\-\-cert**=*CERTFILE* -: Specifies the file that contains the certificate, in PEM format. This - option is required if *KEYFILE* contains only the private key and not also - the certificate, or if a PKCS#11 token is used. - -**\-\-hash-alg**=*HASH_ALG* -: Same as for **fsverity digest**. - -**\-\-key**=*KEYFILE* -: Specifies the file that contains the private key, in PEM format. This - option is required when not using a PKCS#11 token. - -**\-\-out-descriptor**=*FILE* -: Same as for **fsverity digest**. - -**\-\-out-merkle-tree**=*FILE* -: Same as for **fsverity digest**. - -**\-\-pkcs11-engine**=*SOFILE* -: Specifies the path to the OpenSSL PKCS#11 engine file. This typically will - be a path to the libp11 .so file. This option is required when using a - PKCS#11 token. - -**\-\-pkcs11-keyid**=*KEYID* -: Specifies the key identifier in the form of a PKCS#11 URI. If not provided, - the default key associated with the token is used. This option is only - applicable when using a PKCS#11 token. - -**\-\-pkcs11-module**=*SOFILE* -: Specifies the path to the PKCS#11 token-specific module library. This - option is required when using a PKCS#11 token. - -**\-\-salt**=*SALT* -: Same as for **fsverity digest**. - -# SEE ALSO - -For example commands and more information, see the -[README file for -fsverity-utils](https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/fsverity-utils.git/tree/README.md). - -Also see the [kernel documentation for -fs-verity](https://www.kernel.org/doc/html/latest/filesystems/fsverity.html). diff --git a/programs/cmd_digest.c b/programs/cmd_digest.c index fd9f4de..1a3c769 100644 --- a/programs/cmd_digest.c +++ b/programs/cmd_digest.c @@ -18,8 +18,6 @@ static const struct option longopts[] = { {"hash-alg", required_argument, NULL, OPT_HASH_ALG}, {"block-size", required_argument, NULL, OPT_BLOCK_SIZE}, {"salt", required_argument, NULL, OPT_SALT}, - {"out-merkle-tree", required_argument, NULL, OPT_OUT_MERKLE_TREE}, - {"out-descriptor", required_argument, NULL, OPT_OUT_DESCRIPTOR}, {"compact", no_argument, NULL, OPT_COMPACT}, {"for-builtin-sig", no_argument, NULL, OPT_FOR_BUILTIN_SIG}, {NULL, 0, NULL, 0} @@ -42,8 +40,6 @@ int fsverity_cmd_digest(const struct fsverity_command *cmd, case OPT_HASH_ALG: case OPT_BLOCK_SIZE: case OPT_SALT: - case OPT_OUT_MERKLE_TREE: - case OPT_OUT_DESCRIPTOR: if (!parse_tree_param(c, optarg, &tree_params)) goto out_usage; break; @@ -118,8 +114,7 @@ int fsverity_cmd_digest(const struct fsverity_command *cmd, } status = 0; out: - if (!destroy_tree_params(&tree_params) && status == 0) - status = 1; + destroy_tree_params(&tree_params); return status; out_err: diff --git a/programs/cmd_dump_metadata.c b/programs/cmd_dump_metadata.c deleted file mode 100644 index 94cc8ec..0000000 --- a/programs/cmd_dump_metadata.c +++ /dev/null @@ -1,163 +0,0 @@ -// SPDX-License-Identifier: MIT -/* - * The 'fsverity dump_metadata' command - * - * Copyright 2021 Google LLC - * - * Use of this source code is governed by an MIT-style - * license that can be found in the LICENSE file or at - * https://opensource.org/licenses/MIT. - */ - -#include "fsverity.h" - -#include <fcntl.h> -#include <getopt.h> -#include <sys/ioctl.h> -#include <unistd.h> - -static const struct option longopts[] = { - {"offset", required_argument, NULL, OPT_OFFSET}, - {"length", required_argument, NULL, OPT_LENGTH}, - {NULL, 0, NULL, 0} -}; - -static const struct { - const char *name; - int val; -} metadata_types[] = { - {"merkle_tree", FS_VERITY_METADATA_TYPE_MERKLE_TREE}, - {"descriptor", FS_VERITY_METADATA_TYPE_DESCRIPTOR}, - {"signature", FS_VERITY_METADATA_TYPE_SIGNATURE}, -}; - -static bool parse_metadata_type(const char *name, __u64 *val_ret) -{ - size_t i; - - for (i = 0; i < ARRAY_SIZE(metadata_types); i++) { - if (strcmp(name, metadata_types[i].name) == 0) { - *val_ret = metadata_types[i].val; - return true; - } - } - error_msg("unknown metadata type: %s", name); - fputs(" Expected", stderr); - for (i = 0; i < ARRAY_SIZE(metadata_types); i++) { - if (i != 0 && ARRAY_SIZE(metadata_types) > 2) - putc(',', stderr); - putc(' ', stderr); - if (i != 0 && i == ARRAY_SIZE(metadata_types) - 1) - fputs("or ", stderr); - fprintf(stderr, "\"%s\"", metadata_types[i].name); - } - fprintf(stderr, "\n"); - return false; -} - -/* Dump the fs-verity metadata of the given file. */ -int fsverity_cmd_dump_metadata(const struct fsverity_command *cmd, - int argc, char *argv[]) -{ - bool offset_specified = false; - bool length_specified = false; - struct filedes file = { .fd = -1 }; - struct filedes stdout_filedes = { .fd = STDOUT_FILENO, - .name = "stdout" }; - struct fsverity_read_metadata_arg arg = { .length = 32768 }; - void *buf = NULL; - char *tmp; - int c; - int status; - int bytes_read; - - while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) { - switch (c) { - case OPT_OFFSET: - if (offset_specified) { - error_msg("--offset can only be specified once"); - goto out_usage; - } - errno = 0; - arg.offset = strtoull(optarg, &tmp, 10); - if (errno || *tmp) { - error_msg("invalid value for --offset"); - goto out_usage; - } - offset_specified = true; - break; - case OPT_LENGTH: - if (length_specified) { - error_msg("--length can only be specified once"); - goto out_usage; - } - errno = 0; - arg.length = strtoull(optarg, &tmp, 10); - if (errno || *tmp || arg.length > SIZE_MAX) { - error_msg("invalid value for --length"); - goto out_usage; - } - length_specified = true; - break; - default: - goto out_usage; - } - } - - argv += optind; - argc -= optind; - - if (argc != 2) - goto out_usage; - - if (!parse_metadata_type(argv[0], &arg.metadata_type)) - goto out_usage; - - if (length_specified && !offset_specified) { - error_msg("--length specified without --offset"); - goto out_usage; - } - if (offset_specified && !length_specified) { - error_msg("--offset specified without --length"); - goto out_usage; - } - - buf = xzalloc(arg.length); - arg.buf_ptr = (uintptr_t)buf; - - if (!open_file(&file, argv[1], O_RDONLY, 0)) - goto out_err; - - /* - * If --offset and --length were specified, then do only the single read - * requested. Otherwise read until EOF. - */ - do { - bytes_read = ioctl(file.fd, FS_IOC_READ_VERITY_METADATA, &arg); - if (bytes_read < 0) { - error_msg_errno("FS_IOC_READ_VERITY_METADATA failed on '%s'", - file.name); - goto out_err; - } - if (bytes_read == 0) - break; - if (!full_write(&stdout_filedes, buf, bytes_read)) - goto out_err; - arg.offset += bytes_read; - } while (!length_specified); - - status = 0; -out: - free(buf); - filedes_close(&file); - return status; - -out_err: - status = 1; - goto out; - -out_usage: - usage(cmd, stderr); - status = 2; - goto out; -} diff --git a/programs/cmd_sign.c b/programs/cmd_sign.c index aab8f00..47ba6a2 100644 --- a/programs/cmd_sign.c +++ b/programs/cmd_sign.c @@ -27,16 +27,11 @@ static bool write_signature(const char *filename, const u8 *sig, u32 sig_size) } static const struct option longopts[] = { - {"key", required_argument, NULL, OPT_KEY}, - {"cert", required_argument, NULL, OPT_CERT}, - {"pkcs11-engine", required_argument, NULL, OPT_PKCS11_ENGINE}, - {"pkcs11-module", required_argument, NULL, OPT_PKCS11_MODULE}, - {"pkcs11-keyid", required_argument, NULL, OPT_PKCS11_KEYID}, - {"hash-alg", required_argument, NULL, OPT_HASH_ALG}, - {"block-size", required_argument, NULL, OPT_BLOCK_SIZE}, - {"salt", required_argument, NULL, OPT_SALT}, - {"out-merkle-tree", required_argument, NULL, OPT_OUT_MERKLE_TREE}, - {"out-descriptor", required_argument, NULL, OPT_OUT_DESCRIPTOR}, + {"hash-alg", required_argument, NULL, OPT_HASH_ALG}, + {"block-size", required_argument, NULL, OPT_BLOCK_SIZE}, + {"salt", required_argument, NULL, OPT_SALT}, + {"key", required_argument, NULL, OPT_KEY}, + {"cert", required_argument, NULL, OPT_CERT}, {NULL, 0, NULL, 0} }; @@ -56,6 +51,12 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd, while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) { switch (c) { + case OPT_HASH_ALG: + case OPT_BLOCK_SIZE: + case OPT_SALT: + if (!parse_tree_param(c, optarg, &tree_params)) + goto out_usage; + break; case OPT_KEY: if (sig_params.keyfile != NULL) { error_msg("--key can only be specified once"); @@ -70,35 +71,6 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd, } sig_params.certfile = optarg; break; - case OPT_PKCS11_ENGINE: - if (sig_params.pkcs11_engine != NULL) { - error_msg("--pkcs11-engine can only be specified once"); - goto out_usage; - } - sig_params.pkcs11_engine = optarg; - break; - case OPT_PKCS11_MODULE: - if (sig_params.pkcs11_module != NULL) { - error_msg("--pkcs11-module can only be specified once"); - goto out_usage; - } - sig_params.pkcs11_module = optarg; - break; - case OPT_PKCS11_KEYID: - if (sig_params.pkcs11_keyid != NULL) { - error_msg("--pkcs11-keyid can only be specified once"); - goto out_usage; - } - sig_params.pkcs11_keyid = optarg; - break; - case OPT_HASH_ALG: - case OPT_BLOCK_SIZE: - case OPT_SALT: - case OPT_OUT_MERKLE_TREE: - case OPT_OUT_DESCRIPTOR: - if (!parse_tree_param(c, optarg, &tree_params)) - goto out_usage; - break; default: goto out_usage; } @@ -110,6 +82,10 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd, if (argc != 2) goto out_usage; + if (sig_params.keyfile == NULL) { + error_msg("Missing --key argument"); + goto out_usage; + } if (sig_params.certfile == NULL) sig_params.certfile = sig_params.keyfile; @@ -141,8 +117,7 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd, status = 0; out: filedes_close(&file); - if (!destroy_tree_params(&tree_params) && status == 0) - status = 1; + destroy_tree_params(&tree_params); free(digest); free(sig); return status; diff --git a/programs/fsverity.c b/programs/fsverity.c index e4e348b..b911b2e 100644 --- a/programs/fsverity.c +++ b/programs/fsverity.c @@ -11,7 +11,6 @@ #include "fsverity.h" -#include <fcntl.h> #include <limits.h> static const struct fsverity_command { @@ -28,16 +27,9 @@ static const struct fsverity_command { .usage_str = " fsverity digest FILE...\n" " [--hash-alg=HASH_ALG] [--block-size=BLOCK_SIZE] [--salt=SALT]\n" -" [--out-merkle-tree=FILE] [--out-descriptor=FILE]\n" " [--compact] [--for-builtin-sig]\n" #ifndef _WIN32 }, { - .name = "dump_metadata", - .func = fsverity_cmd_dump_metadata, - .short_desc = "Dump the fs-verity metadata of the given file", - .usage_str = -" fsverity dump_metadata TYPE FILE [--offset=OFFSET] [--length=LENGTH]\n" - }, { .name = "enable", .func = fsverity_cmd_enable, .short_desc = "Enable fs-verity on a file", @@ -56,13 +48,11 @@ static const struct fsverity_command { }, { .name = "sign", .func = fsverity_cmd_sign, - .short_desc = "Sign a file for fs-verity built-in signature verification", + .short_desc = "Sign a file for fs-verity", .usage_str = -" fsverity sign FILE OUT_SIGFILE\n" -" [--key=KEYFILE] [--cert=CERTFILE] [--pkcs11-engine=SOFILE]\n" -" [--pkcs11-module=SOFILE] [--pkcs11-keyid=KEYID]\n" +" fsverity sign FILE OUT_SIGFILE --key=KEYFILE\n" " [--hash-alg=HASH_ALG] [--block-size=BLOCK_SIZE] [--salt=SALT]\n" -" [--out-merkle-tree=FILE] [--out-descriptor=FILE]\n" +" [--cert=CERTFILE]\n" } }; @@ -204,74 +194,6 @@ static bool parse_salt_option(const char *arg, u8 **salt_ptr, return true; } -struct metadata_callback_ctx { - struct filedes merkle_tree_file; - struct filedes descriptor_file; - struct libfsverity_metadata_callbacks callbacks; -}; - -static int handle_merkle_tree_size(void *_ctx, u64 size) -{ - struct metadata_callback_ctx *ctx = _ctx; - - if (!preallocate_file(&ctx->merkle_tree_file, size)) - return -EIO; - return 0; -} - -static int handle_merkle_tree_block(void *_ctx, const void *block, size_t size, - u64 offset) -{ - struct metadata_callback_ctx *ctx = _ctx; - - if (!full_pwrite(&ctx->merkle_tree_file, block, size, offset)) - return -EIO; - return 0; -} - -static int handle_descriptor(void *_ctx, const void *descriptor, size_t size) -{ - struct metadata_callback_ctx *ctx = _ctx; - - if (!full_write(&ctx->descriptor_file, descriptor, size)) - return -EIO; - return 0; -} - -static bool parse_out_metadata_option(int opt_char, const char *arg, - const struct libfsverity_metadata_callbacks **cbs) -{ - struct metadata_callback_ctx *ctx; - struct filedes *file; - const char *opt_name; - - if (*cbs) { - ctx = (*cbs)->ctx; - } else { - ctx = xzalloc(sizeof(*ctx)); - ctx->merkle_tree_file.fd = -1; - ctx->descriptor_file.fd = -1; - ctx->callbacks.ctx = ctx; - *cbs = &ctx->callbacks; - } - - if (opt_char == OPT_OUT_MERKLE_TREE) { - file = &ctx->merkle_tree_file; - opt_name = "--out-merkle-tree"; - ctx->callbacks.merkle_tree_size = handle_merkle_tree_size; - ctx->callbacks.merkle_tree_block = handle_merkle_tree_block; - } else { - file = &ctx->descriptor_file; - opt_name = "--out-descriptor"; - ctx->callbacks.descriptor = handle_descriptor; - } - if (file->fd >= 0) { - error_msg("%s can only be specified once", opt_name); - return false; - } - return open_file(file, arg, O_WRONLY|O_CREAT|O_TRUNC, 0644); -} - bool parse_tree_param(int opt_char, const char *arg, struct libfsverity_merkle_tree_params *params) { @@ -283,30 +205,15 @@ bool parse_tree_param(int opt_char, const char *arg, case OPT_SALT: return parse_salt_option(arg, (u8 **)¶ms->salt, ¶ms->salt_size); - case OPT_OUT_MERKLE_TREE: - case OPT_OUT_DESCRIPTOR: - return parse_out_metadata_option(opt_char, arg, - ¶ms->metadata_callbacks); default: ASSERT(0); } } -bool destroy_tree_params(struct libfsverity_merkle_tree_params *params) +void destroy_tree_params(struct libfsverity_merkle_tree_params *params) { - bool ok = true; - free((u8 *)params->salt); - if (params->metadata_callbacks) { - struct metadata_callback_ctx *ctx = - params->metadata_callbacks->ctx; - - ok &= filedes_close(&ctx->merkle_tree_file); - ok &= filedes_close(&ctx->descriptor_file); - free(ctx); - } memset(params, 0, sizeof(*params)); - return ok; } int main(int argc, char *argv[]) diff --git a/programs/fsverity.h b/programs/fsverity.h index ad54cc2..45c4fe1 100644 --- a/programs/fsverity.h +++ b/programs/fsverity.h @@ -27,13 +27,6 @@ enum { OPT_FOR_BUILTIN_SIG, OPT_HASH_ALG, OPT_KEY, - OPT_LENGTH, - OPT_OFFSET, - OPT_OUT_DESCRIPTOR, - OPT_OUT_MERKLE_TREE, - OPT_PKCS11_ENGINE, - OPT_PKCS11_KEYID, - OPT_PKCS11_MODULE, OPT_SALT, OPT_SIGNATURE, }; @@ -44,10 +37,6 @@ struct fsverity_command; int fsverity_cmd_digest(const struct fsverity_command *cmd, int argc, char *argv[]); -/* cmd_dump_metadata.c */ -int fsverity_cmd_dump_metadata(const struct fsverity_command *cmd, - int argc, char *argv[]); - /* cmd_enable.c */ int fsverity_cmd_enable(const struct fsverity_command *cmd, int argc, char *argv[]); @@ -64,6 +53,6 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd, void usage(const struct fsverity_command *cmd, FILE *fp); bool parse_tree_param(int opt_char, const char *arg, struct libfsverity_merkle_tree_params *params); -bool destroy_tree_params(struct libfsverity_merkle_tree_params *params); +void destroy_tree_params(struct libfsverity_merkle_tree_params *params); #endif /* PROGRAMS_FSVERITY_H */ diff --git a/programs/test_compute_digest.c b/programs/test_compute_digest.c index 67266fa..e7f2645 100644 --- a/programs/test_compute_digest.c +++ b/programs/test_compute_digest.c @@ -13,7 +13,6 @@ #include <ctype.h> #include <inttypes.h> -#include <openssl/sha.h> struct mem_file { u8 *data; @@ -38,13 +37,6 @@ static int error_read_fn(void *fd __attribute__((unused)), return -EIO; } -static int zeroes_read_fn(void *fd __attribute__((unused)), - void *buf, size_t count) -{ - memset(buf, 0, count); - return 0; -} - static const struct test_case { u32 hash_algorithm; u32 block_size; @@ -257,130 +249,6 @@ static void test_invalid_params(void) ASSERT(d == NULL); } -static struct { - u64 merkle_tree_size; - u64 merkle_tree_block; - u64 descriptor; -} metadata_callback_counts; - -static int handle_merkle_tree_size(void *ctx, u64 size) -{ - metadata_callback_counts.merkle_tree_size++; - - /* Test that the ctx argument is passed through correctly. */ - ASSERT(ctx == (void *)1); - - /* Test that the expected Merkle tree size is reported. */ - ASSERT(size == 5 * 1024); - return 0; -} - -static int handle_merkle_tree_block(void *ctx, const void *block, size_t size, - u64 offset) -{ - u8 digest[SHA256_DIGEST_LENGTH]; - u64 count = metadata_callback_counts.merkle_tree_block++; - const char *expected_digest; - - /* Test that ->merkle_tree_size() was called first. */ - ASSERT(metadata_callback_counts.merkle_tree_size == 1); - - /* Test that the ctx argument is passed through correctly. */ - ASSERT(ctx == (void *)1); - - /* - * Test that this Merkle tree block has the expected size, offset, and - * contents. The 4 blocks at "level 0" should be reported first, in - * order; then the 1 block at "level 1" should be reported last (but the - * level 1 block should have the smallest offset). - */ - ASSERT(size == 1024); - SHA256(block, size, digest); - if (count == 4) { - /* 1 block at level 1 */ - ASSERT(offset == 0); - expected_digest = "\x68\xc5\x38\xe1\x19\x58\xd6\x5d" - "\x68\xb6\xfe\x8e\x9f\xb8\xcc\xab" - "\xec\xfd\x92\x8b\x01\xd0\x63\x44" - "\xe2\x23\xed\x41\xdd\xc4\x54\x4a"; - } else { - /* 4 blocks at level 0 */ - ASSERT(offset == 1024 + (count * 1024)); - if (count < 3) { - expected_digest = "\xf7\x89\xba\xab\x53\x85\x9f\xaf" - "\x36\xd6\xd7\x5d\x10\x42\x06\x42" - "\x94\x20\x2d\x6e\x13\xe7\x71\x6f" - "\x39\x4f\xba\x43\x4c\xcc\x49\x86"; - } else { - expected_digest = "\x00\xfe\xd0\x3c\x5d\x6e\xab\x21" - "\x31\x43\xf3\xd9\x6a\x5c\xa3\x1c" - "\x2b\x89\xf5\x68\x4e\x6c\x8e\x07" - "\x87\x3e\x5e\x97\x65\x17\xb4\x8f"; - } - } - ASSERT(!memcmp(digest, expected_digest, SHA256_DIGEST_LENGTH)); - return 0; -} - -static const u8 expected_file_digest[SHA256_DIGEST_LENGTH] = - "\x09\xcb\xba\xee\xd2\xa0\x4c\x2d\xa2\x42\xc1\x0e\x15\x68\xd9\x6f" - "\x35\x8a\x16\xaa\x1e\xbe\x8c\xf0\x28\x61\x20\xc1\x3c\x93\x66\xd1"; - -static int handle_descriptor(void *ctx, const void *descriptor, size_t size) -{ - u8 digest[SHA256_DIGEST_LENGTH]; - - metadata_callback_counts.descriptor++; - /* Test that the ctx argument is passed through correctly. */ - ASSERT(ctx == (void *)1); - - /* Test that the fs-verity descriptor is reported correctly. */ - ASSERT(size == 256); - SHA256(descriptor, size, digest); - ASSERT(!memcmp(digest, expected_file_digest, SHA256_DIGEST_LENGTH)); - return 0; -} - -static const struct libfsverity_metadata_callbacks metadata_callbacks = { - .ctx = (void *)1, /* arbitrary value for testing purposes */ - .merkle_tree_size = handle_merkle_tree_size, - .merkle_tree_block = handle_merkle_tree_block, - .descriptor = handle_descriptor, -}; - -/* Test that the libfsverity_metadata_callbacks work correctly. */ -static void test_metadata_callbacks(void) -{ - /* - * For a useful test, we want a file whose Merkle tree will have at - * least 2 levels (this one will have exactly 2). The contents of the - * file aren't too important. - */ - struct libfsverity_merkle_tree_params params = { - .version = 1, - .hash_algorithm = FS_VERITY_HASH_ALG_SHA256, - .block_size = 1024, - .file_size = 100000, - .metadata_callbacks = &metadata_callbacks, - }; - struct libfsverity_digest *d; - - ASSERT(libfsverity_compute_digest(NULL, zeroes_read_fn, - ¶ms, &d) == 0); - - /* Test that the callbacks were called the correct number of times. */ - ASSERT(metadata_callback_counts.merkle_tree_size == 1); - ASSERT(metadata_callback_counts.merkle_tree_block == 5); - ASSERT(metadata_callback_counts.descriptor == 1); - - /* Test that the computed file digest is as expected. */ - ASSERT(d->digest_algorithm == FS_VERITY_HASH_ALG_SHA256); - ASSERT(d->digest_size == SHA256_DIGEST_LENGTH); - ASSERT(!memcmp(d->digest, expected_file_digest, SHA256_DIGEST_LENGTH)); - - free(d); -} - int main(int argc, char *argv[]) { const bool update = (argc == 2 && !strcmp(argv[1], "--update")); @@ -437,7 +305,6 @@ int main(int argc, char *argv[]) } test_invalid_params(); - test_metadata_callbacks(); printf("test_compute_digest passed\n"); return 0; } diff --git a/programs/utils.c b/programs/utils.c index 116eb95..ce19b57 100644 --- a/programs/utils.c +++ b/programs/utils.c @@ -13,14 +13,10 @@ #include <errno.h> #include <fcntl.h> -#include <inttypes.h> #include <limits.h> #include <stdarg.h> #include <sys/stat.h> #include <unistd.h> -#ifdef _WIN32 -# include <windows.h> -#endif /* ========== Memory allocation ========== */ @@ -130,26 +126,6 @@ bool get_file_size(struct filedes *file, u64 *size_ret) return true; } -bool preallocate_file(struct filedes *file, u64 size) -{ - int res; - - if (size == 0) - return true; -#ifdef _WIN32 - /* Not exactly the same as posix_fallocate(), but good enough... */ - res = _chsize_s(file->fd, size); -#else - res = posix_fallocate(file->fd, 0, size); -#endif - if (res != 0) { - error_msg_errno("preallocating %" PRIu64 "-byte file '%s'", - size, file->name); - return false; - } - return true; -} - bool full_read(struct filedes *file, void *buf, size_t count) { while (count) { @@ -184,41 +160,6 @@ bool full_write(struct filedes *file, const void *buf, size_t count) return true; } -static int raw_pwrite(int fd, const void *buf, int count, u64 offset) -{ -#ifdef _WIN32 - HANDLE h = (HANDLE)_get_osfhandle(fd); - OVERLAPPED pos = { .Offset = offset, .OffsetHigh = offset >> 32 }; - DWORD written = 0; - - /* Not exactly the same as pwrite(), but good enough... */ - if (!WriteFile(h, buf, count, &written, &pos)) { - errno = EIO; - return -1; - } - return written; -#else - return pwrite(fd, buf, count, offset); -#endif -} - -bool full_pwrite(struct filedes *file, const void *buf, size_t count, - u64 offset) -{ - while (count) { - int n = raw_pwrite(file->fd, buf, min(count, INT_MAX), offset); - - if (n < 0) { - error_msg_errno("writing to '%s'", file->name); - return false; - } - buf += n; - count -= n; - offset += n; - } - return true; -} - bool filedes_close(struct filedes *file) { int res; diff --git a/programs/utils.h b/programs/utils.h index 9a5c97a..ab5005f 100644 --- a/programs/utils.h +++ b/programs/utils.h @@ -40,11 +40,8 @@ struct filedes { bool open_file(struct filedes *file, const char *filename, int flags, int mode); bool get_file_size(struct filedes *file, u64 *size_ret); -bool preallocate_file(struct filedes *file, u64 size); bool full_read(struct filedes *file, void *buf, size_t count); bool full_write(struct filedes *file, const void *buf, size_t count); -bool full_pwrite(struct filedes *file, const void *buf, size_t count, - u64 offset); bool filedes_close(struct filedes *file); int read_callback(void *file, void *buf, size_t count); diff --git a/scripts/do-release.sh b/scripts/do-release.sh index 3f68497..352fcf1 100755 --- a/scripts/do-release.sh +++ b/scripts/do-release.sh @@ -9,73 +9,38 @@ set -e -u -o pipefail cd "$(dirname "$0")/.." -usage() -{ - echo "Usage: $0 prepare|publish VERS" 1>&2 - echo " e.g. $0 prepare 1.0" 1>&2 - echo " $0 publish 1.0" 1>&2 +if [ $# != 1 ]; then + echo "Usage: $0 VERS" 1>&2 + echo " e.g. $0 1.0" 1>&2 exit 2 -} - -if [ $# != 2 ]; then - usage fi -PUBLISH=false -case $1 in -publish) - PUBLISH=true - ;; -prepare) - ;; -*) - usage - ;; -esac -VERS=$2 +VERS=$1 PKG=fsverity-utils-$VERS -prepare_release() -{ - git checkout -f - git clean -fdx - ./scripts/run-tests.sh - git clean -fdx - - major=$(echo "$VERS" | cut -d. -f1) - minor=$(echo "$VERS" | cut -d. -f2) - month=$(LC_ALL=C date +%B) - year=$(LC_ALL=C date +%Y) - - sed -E -i -e "/FSVERITY_UTILS_MAJOR_VERSION/s/[0-9]+/$major/" \ - -e "/FSVERITY_UTILS_MINOR_VERSION/s/[0-9]+/$minor/" \ - include/libfsverity.h - sed -E -i "/Version:/s/[0-9]+\.[0-9]+/$VERS/" \ - lib/libfsverity.pc.in - sed -E -i -e "/^% /s/fsverity-utils v[0-9]+(\.[0-9]+)+/fsverity-utils v$VERS/" \ - -e "/^% /s/[a-zA-Z]+ 2[0-9]{3}/$month $year/" \ - man/*.[1-9].md - git commit -a --signoff --message="v$VERS" - git tag --sign "v$VERS" --message="$PKG" - - git archive "v$VERS" --prefix="$PKG/" > "$PKG.tar" - tar xf "$PKG.tar" - ( cd "$PKG" && make check ) - rm -r "$PKG" -} - -publish_release() -{ - gpg --detach-sign --armor "$PKG.tar" - DESTDIR=/pub/linux/kernel/people/ebiggers/fsverity-utils/v$VERS - kup mkdir "$DESTDIR" - kup put "$PKG.tar" "$PKG.tar.asc" "$DESTDIR/$PKG.tar.gz" - git push - git push --tags -} - -if $PUBLISH; then - publish_release -else - prepare_release -fi +git checkout -f +git clean -fdx +./scripts/run-tests.sh +git clean -fdx + +major=$(echo "$VERS" | cut -d. -f1) +minor=$(echo "$VERS" | cut -d. -f2) +sed -E -i -e "/FSVERITY_UTILS_MAJOR_VERSION/s/[0-9]+/$major/" \ + -e "/FSVERITY_UTILS_MINOR_VERSION/s/[0-9]+/$minor/" \ + include/libfsverity.h +sed -E -i "/Version:/s/[0-9]+\.[0-9]+/$VERS/" \ + lib/libfsverity.pc.in +git commit -a --signoff --message="v$VERS" +git tag --sign "v$VERS" --message="$PKG" + +git archive "v$VERS" --prefix="$PKG/" > "$PKG.tar" +tar xf "$PKG.tar" +( cd "$PKG" && make check ) +rm -r "$PKG" + +gpg --detach-sign --armor "$PKG.tar" +DESTDIR=/pub/linux/kernel/people/ebiggers/fsverity-utils/v$VERS +kup mkdir "$DESTDIR" +kup put "$PKG.tar" "$PKG.tar.asc" "$DESTDIR/$PKG.tar.gz" +git push +git push --tags diff --git a/scripts/run-sparse.sh b/scripts/run-sparse.sh index b8d37c1..f75b837 100755 --- a/scripts/run-sparse.sh +++ b/scripts/run-sparse.sh @@ -8,7 +8,7 @@ set -e -u -o pipefail -find programs lib -name '*.c' | while read -r file; do +find . -name '*.c' | while read -r file; do sparse "$file" -gcc-base-dir "$(gcc --print-file-name=)" \ -Iinclude -D_FILE_OFFSET_BITS=64 -Wbitwise -D_GNU_SOURCE done diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh index e2a4e38..530fe34 100755 --- a/scripts/run-tests.sh +++ b/scripts/run-tests.sh @@ -17,13 +17,11 @@ set -e -u -o pipefail cd "$(dirname "$0")/.." -log() -{ +log() { echo "[$(date)] $*" 1>&2 } -fail() -{ +fail() { echo "FAIL: $*" 1>&2 exit 1 } @@ -40,44 +38,31 @@ exec 2> >(tee -ia run-tests.log 1>&2) MAKE="make -j$(getconf _NPROCESSORS_ONLN)" -TEST_FUNCS=() +log "Build and test with statically linking" +$MAKE CFLAGS="-Werror" +if ldd fsverity | grep libfsverity.so; then + fail "fsverity binary should be statically linked to libfsverity by default" +fi +./fsverity --version -static_linking_test() -{ - log "Build and test with statically linking" - $MAKE CFLAGS="-Werror" - if ldd fsverity | grep libfsverity.so; then - fail "fsverity binary should be statically linked to libfsverity by default" - fi - ./fsverity --version - - log "Check that all global symbols are prefixed with \"libfsverity_\"" - if nm libfsverity.a | grep ' T ' | grep -v " libfsverity_"; then - fail "Some global symbols are not prefixed with \"libfsverity_\"" - fi -} -TEST_FUNCS+=(static_linking_test) +log "Check that all global symbols are prefixed with \"libfsverity_\"" +if nm libfsverity.a | grep ' T ' | grep -v " libfsverity_"; then + fail "Some global symbols are not prefixed with \"libfsverity_\"" +fi -dynamic_linking_test() -{ - log "Build and test with dynamic linking" - $MAKE CFLAGS="-Werror" USE_SHARED_LIB=1 check - if ! ldd fsverity | grep libfsverity.so; then - fail "fsverity binary should be dynamically linked to libfsverity when USE_SHARED_LIB=1" - fi - - log "Check that all exported symbols are prefixed with \"libfsverity_\"" - if nm libfsverity.so | grep ' T ' | grep -v " libfsverity_"; then - fail "Some exported symbols are not prefixed with \"libfsverity_\"" - fi -} -TEST_FUNCS+=(dynamic_linking_test) +log "Build and test with dynamic linking" +$MAKE CFLAGS="-Werror" USE_SHARED_LIB=1 check +if ! ldd fsverity | grep libfsverity.so; then + fail "fsverity binary should be dynamically linked to libfsverity when USE_SHARED_LIB=1" +fi -cplusplus_test() -{ - $MAKE CFLAGS="-Werror" libfsverity.so - log "Test using libfsverity from C++ program" - cat > "$TMPDIR/test.cc" <<EOF +log "Check that all exported symbols are prefixed with \"libfsverity_\"" +if nm libfsverity.so | grep ' T ' | grep -v " libfsverity_"; then + fail "Some exported symbols are not prefixed with \"libfsverity_\"" +fi + +log "Test using libfsverity from C++ program" +cat > "$TMPDIR/test.cc" <<EOF #include <libfsverity.h> #include <iostream> int main() @@ -85,231 +70,118 @@ int main() std::cout << libfsverity_get_digest_size(FS_VERITY_HASH_ALG_SHA256) << std::endl; } EOF - c++ -Wall -Werror "$TMPDIR/test.cc" -Iinclude -L. -lfsverity -o "$TMPDIR/test" - [ "$(LD_LIBRARY_PATH=. "$TMPDIR/test")" = "32" ] - rm "${TMPDIR:?}"/* -} -TEST_FUNCS+=(cplusplus_test) - -untracked_files_test() -{ - log "Check that build doesn't produce untracked files" - $MAKE CFLAGS="-Werror" all test_programs - if git status --short | grep -q '^??'; then - git status - fail "Build produced untracked files (check 'git status'). Missing gitignore entry?" - fi -} -TEST_FUNCS+=(untracked_files_test) - -uninstall_test() -{ - log "Test that 'make uninstall' uninstalls all files" - make DESTDIR="$TMPDIR" install - if [ "$(find "$TMPDIR" -type f -o -type l | wc -l)" = 0 ]; then - fail "'make install' didn't install any files" - fi - make DESTDIR="$TMPDIR" uninstall - if [ "$(find "$TMPDIR" -type f -o -type l | wc -l)" != 0 ]; then - fail "'make uninstall' didn't uninstall all files" - fi - rm -r "${TMPDIR:?}"/* -} -TEST_FUNCS+=(uninstall_test) - -dash_test() -{ - log "Build, install, and uninstall with dash" - make clean SHELL=/bin/dash - make DESTDIR="$TMPDIR" SHELL=/bin/dash install - make DESTDIR="$TMPDIR" SHELL=/bin/dash uninstall -} -TEST_FUNCS+=(dash_test) - -license_test() -{ - log "Check that all files have license and copyright info" - list="$TMPDIR/filelist" - filter_license_info() { - # files to exclude from license and copyright info checks - grep -E -v '(\.gitignore|LICENSE|.*\.md|testdata|fsverity_uapi\.h|libfsverity\.pc\.in)' - } - git grep -L 'SPDX-License-Identifier: MIT' \ - | filter_license_info > "$list" || true - if [ -s "$list" ]; then - fail "The following files are missing an appropriate SPDX license identifier: $(<"$list")" - fi - # For now some people still prefer a free-form license statement, not just SPDX. - git grep -L 'Use of this source code is governed by an MIT-style' \ - | filter_license_info > "$list" || true - if [ -s "$list" ]; then - fail "The following files are missing an appropriate license statement: $(<"$list")" - fi - git grep -L '\<Copyright\>' | filter_license_info > "$list" || true - if [ -s "$list" ]; then - fail "The following files are missing a copyright statement: $(<"$list")" - fi - rm "$list" -} -TEST_FUNCS+=(license_test) +c++ -Wall -Werror "$TMPDIR/test.cc" -Iinclude -L. -lfsverity -o "$TMPDIR/test" +[ "$(LD_LIBRARY_PATH=. "$TMPDIR/test")" = "32" ] +rm "${TMPDIR:?}"/* + +log "Check that build doesn't produce untracked files" +$MAKE CFLAGS="-Werror" all test_programs +if git status --short | grep -q '^??'; then + git status + fail "Build produced untracked files (check 'git status'). Missing gitignore entry?" +fi -gcc_test() -{ - log "Build and test with gcc (-O2)" - $MAKE CC=gcc CFLAGS="-O2 -Werror" check +log "Test that 'make uninstall' uninstalls all files" +make DESTDIR="$TMPDIR" install +if [ "$(find "$TMPDIR" -type f -o -type l | wc -l)" = 0 ]; then + fail "'make install' didn't install any files" +fi +make DESTDIR="$TMPDIR" uninstall +if [ "$(find "$TMPDIR" -type f -o -type l | wc -l)" != 0 ]; then + fail "'make uninstall' didn't uninstall all files" +fi +rm -r "${TMPDIR:?}"/* + +log "Build, install, and uninstall with dash" +make clean SHELL=/bin/dash +make DESTDIR="$TMPDIR" SHELL=/bin/dash install +make DESTDIR="$TMPDIR" SHELL=/bin/dash uninstall + +log "Check that all files have license and copyright info" +list="$TMPDIR/filelist" +filter_license_info() { + # files to exclude from license and copyright info checks + grep -E -v '(\.gitignore|LICENSE|NEWS|README|testdata|fsverity_uapi\.h|libfsverity\.pc\.in)' +} +git grep -L 'SPDX-License-Identifier: MIT' \ + | filter_license_info > "$list" || true +if [ -s "$list" ]; then + fail "The following files are missing an appropriate SPDX license identifier: $(<"$list")" +fi +# For now some people still prefer a free-form license statement, not just SPDX. +git grep -L 'Use of this source code is governed by an MIT-style' \ + | filter_license_info > "$list" || true +if [ -s "$list" ]; then + fail "The following files are missing an appropriate license statement: $(<"$list")" +fi +git grep -L '\<Copyright\>' | filter_license_info > "$list" || true +if [ -s "$list" ]; then + fail "The following files are missing a copyright statement: $(<"$list")" +fi +rm "$list" - log "Build and test with gcc (-O3)" - $MAKE CC=gcc CFLAGS="-O3 -Werror" check -} -TEST_FUNCS+=(gcc_test) +log "Build and test with gcc (-O2)" +$MAKE CC=gcc CFLAGS="-O2 -Werror" check -clang_test() -{ - log "Build and test with clang (-O2)" - $MAKE CC=clang CFLAGS="-O2 -Werror" check +log "Build and test with gcc (-O3)" +$MAKE CC=gcc CFLAGS="-O3 -Werror" check - log "Build and test with clang (-O3)" - $MAKE CC=clang CFLAGS="-O3 -Werror" check -} -TEST_FUNCS+=(clang_test) +log "Build and test with gcc (32-bit)" +$MAKE CC=gcc CFLAGS="-O2 -Werror -m32" check -32bit_test() -{ - log "Build and test with gcc (32-bit)" - $MAKE CC=gcc CFLAGS="-O2 -Werror -m32" check -} -TEST_FUNCS+=(32bit_test) +log "Build and test with clang (-O2)" +$MAKE CC=clang CFLAGS="-O2 -Werror" check -sanitizers_test() -{ - log "Build and test with clang + UBSAN" - $MAKE CC=clang \ - CFLAGS="-O2 -Werror -fsanitize=undefined -fno-sanitize-recover=undefined" \ - check - - log "Build and test with clang + ASAN" - $MAKE CC=clang \ - CFLAGS="-O2 -Werror -fsanitize=address -fno-sanitize-recover=address" \ - check - - log "Build and test with clang + unsigned integer overflow sanitizer" - $MAKE CC=clang \ - CFLAGS="-O2 -Werror -fsanitize=unsigned-integer-overflow -fno-sanitize-recover=unsigned-integer-overflow" \ - check - - log "Build and test with clang + CFI" - $MAKE CC=clang CFLAGS="-O2 -Werror -fsanitize=cfi -flto -fvisibility=hidden" \ - AR=llvm-ar check -} -TEST_FUNCS+=(sanitizers_test) +log "Build and test with clang (-O3)" +$MAKE CC=clang CFLAGS="-O3 -Werror" check -valgrind_test() -{ - log "Build and test with valgrind" - $MAKE TEST_WRAPPER_PROG="valgrind --quiet --error-exitcode=100 --leak-check=full --errors-for-leak-kinds=all" \ - CFLAGS="-O2 -Werror" check -} -TEST_FUNCS+=(valgrind_test) +log "Build and test with clang + UBSAN" +$MAKE CC=clang \ + CFLAGS="-O2 -Werror -fsanitize=undefined -fno-sanitize-recover=undefined" \ + check -boringssl_test() -{ - log "Build and test using BoringSSL instead of OpenSSL" - log "-> Building BoringSSL" - $MAKE boringssl - log "-> Building fsverity-utils linked to BoringSSL" - $MAKE CFLAGS="-O2 -Werror" LDFLAGS="-Lboringssl/build/crypto" \ - CPPFLAGS="-Iboringssl/include" LDLIBS="-lcrypto -lpthread" check -} -TEST_FUNCS+=(boringssl_test) +log "Build and test with clang + ASAN" +$MAKE CC=clang \ + CFLAGS="-O2 -Werror -fsanitize=address -fno-sanitize-recover=address" \ + check -openssl1_test() -{ - log "Build and test using OpenSSL 1.0" - $MAKE CFLAGS="-O2 -Werror" LDFLAGS="-L/usr/lib/openssl-1.0" \ - CPPFLAGS="-I/usr/include/openssl-1.0" check -} -TEST_FUNCS+=(openssl1_test) +log "Build and test with clang + unsigned integer overflow sanitizer" +$MAKE CC=clang \ + CFLAGS="-O2 -Werror -fsanitize=unsigned-integer-overflow -fno-sanitize-recover=unsigned-integer-overflow" \ + check -openssl3_test() -{ - log "Build and test using OpenSSL 3.0" - OSSL3=$HOME/src/openssl/inst/usr/local - LD_LIBRARY_PATH="$OSSL3/lib64" $MAKE CFLAGS="-O2 -Werror" \ - LDFLAGS="-L$OSSL3/lib64" CPPFLAGS="-I$OSSL3/include" check -} -TEST_FUNCS+=(openssl3_test) +log "Build and test with clang + CFI" +$MAKE CC=clang CFLAGS="-O2 -Werror -fsanitize=cfi -flto -fvisibility=hidden" \ + check -unsigned_char_test() -{ - log "Build and test using -funsigned-char" - $MAKE CFLAGS="-O2 -Werror -funsigned-char" check -} -TEST_FUNCS+=(unsigned_char_test) +log "Build and test with valgrind" +$MAKE TEST_WRAPPER_PROG="valgrind --quiet --error-exitcode=100 --leak-check=full --errors-for-leak-kinds=all" \ + CFLAGS="-O2 -Werror" check -signed_char_test() -{ - log "Build and test using -fsigned-char" - $MAKE CFLAGS="-O2 -Werror -fsigned-char" check -} -TEST_FUNCS+=(signed_char_test) +log "Build and test using BoringSSL instead of OpenSSL" +BSSL=$HOME/src/boringssl +$MAKE CFLAGS="-O2 -Werror" LDFLAGS="-L$BSSL/build/crypto" \ + CPPFLAGS="-I$BSSL/include" LDLIBS="-lcrypto -lpthread" check -windows_build_test() -{ - log "Cross-compile for Windows (32-bit)" - $MAKE CC=i686-w64-mingw32-gcc CFLAGS="-O2 -Werror" +log "Build and test using -funsigned-char" +$MAKE CFLAGS="-O2 -Werror -funsigned-char" check - log "Cross-compile for Windows (64-bit)" - $MAKE CC=x86_64-w64-mingw32-gcc CFLAGS="-O2 -Werror" -} -TEST_FUNCS+=(windows_build_test) +log "Build and test using -fsigned-char" +$MAKE CFLAGS="-O2 -Werror -fsigned-char" check -sparse_test() -{ - log "Run sparse" - ./scripts/run-sparse.sh -} -TEST_FUNCS+=(sparse_test) +log "Cross-compile for Windows (32-bit)" +$MAKE CC=i686-w64-mingw32-gcc CFLAGS="-O2 -Werror" -clang_analyzer_test() -{ - log "Run clang static analyzer" - scan-build --status-bugs make CFLAGS="-O2 -Werror" all test_programs -} -TEST_FUNCS+=(clang_analyzer_test) +log "Cross-compile for Windows (64-bit)" +$MAKE CC=x86_64-w64-mingw32-gcc CFLAGS="-O2 -Werror" -shellcheck_test() -{ - log "Run shellcheck" - shellcheck scripts/*.sh 1>&2 -} -TEST_FUNCS+=(shellcheck_test) +log "Run sparse" +./scripts/run-sparse.sh -test_exists() -{ - local tst=$1 - local func - for func in "${TEST_FUNCS[@]}"; do - if [ "${tst}_test" = "$func" ]; then - return 0 - fi - done - return 1 -} +log "Run clang static analyzer" +scan-build --status-bugs make CFLAGS="-O2 -Werror" all test_programs -if [[ $# == 0 ]]; then - for func in "${TEST_FUNCS[@]}"; do - eval "$func" - done -else - for tst; do - if ! test_exists "$tst"; then - echo 1>&2 "Unknown test: $tst" - exit 2 - fi - done - for tst; do - eval "${tst}_test" - done -fi +log "Run shellcheck" +shellcheck scripts/*.sh 1>&2 log "All tests passed!" |