aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-06-15 21:38:46 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-06-15 21:38:46 +0000
commit5cf2665928c8a445ae618d4eea618dcfaff583be (patch)
tree4cdaa88e6505f5a9cc97967952415b32a3383eff
parent802267b91d9dc1f571fa6a37df10e1b909f18aa7 (diff)
parent596ec1420a085648278e390b61ed1cadb5d08fc3 (diff)
downloadfsverity-utils-android12-mainline-tzdata3-release.tar.gz
Change-Id: I85efe739cc509903ddc7d490dbadfc75958428ee
-rw-r--r--.github/workflows/ci.yml169
-rw-r--r--.gitignore4
-rw-r--r--Android.bp6
-rw-r--r--METADATA10
-rw-r--r--Makefile56
-rw-r--r--NEWS.md22
-rw-r--r--README.md66
-rw-r--r--common/fsverity_uapi.h14
-rw-r--r--include/libfsverity.h106
-rw-r--r--lib/compute_digest.c130
-rw-r--r--lib/lib_private.h5
-rw-r--r--lib/libfsverity.pc.in2
-rw-r--r--lib/sign_digest.c96
-rw-r--r--lib/utils.c34
-rw-r--r--man/fsverity.1.md215
-rw-r--r--programs/cmd_digest.c7
-rw-r--r--programs/cmd_dump_metadata.c163
-rw-r--r--programs/cmd_sign.c57
-rw-r--r--programs/fsverity.c101
-rw-r--r--programs/fsverity.h13
-rw-r--r--programs/test_compute_digest.c133
-rw-r--r--programs/utils.c59
-rw-r--r--programs/utils.h3
-rwxr-xr-xscripts/do-release.sh95
-rwxr-xr-xscripts/run-sparse.sh2
-rwxr-xr-xscripts/run-tests.sh364
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
diff --git a/.gitignore b/.gitignore
index 3ea5ca6..04f9a6f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,13 +1,9 @@
-*.[1-9]
*.a
-*.exe
*.o
*.patch
*.so
*.so.*
/.build-config
-/boringssl
-/boringssl.tar.gz
/fsverity
/fsverity.sig
/run-tests.log
diff --git a/Android.bp b/Android.bp
index d7abc42..025de57 100644
--- a/Android.bp
+++ b/Android.bp
@@ -63,12 +63,6 @@ cc_library {
export_include_dirs: ["include"],
- apex_available: [
- "//apex_available:platform",
- "com.android.compos",
- ],
- recovery_available: true,
-
srcs: [
"lib/*.c",
],
diff --git a/METADATA b/METADATA
index 14e8928..6fb345f 100644
--- a/METADATA
+++ b/METADATA
@@ -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
}
}
diff --git a/Makefile b/Makefile
index 2304a21..0354f62 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/NEWS.md b/NEWS.md
index c63dff9..8745745 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -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.
diff --git a/README.md b/README.md
index ffa25fd..2b63488 100644
--- a/README.md
+++ b/README.md
@@ -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 **)&params->salt,
&params->salt_size);
- case OPT_OUT_MERKLE_TREE:
- case OPT_OUT_DESCRIPTOR:
- return parse_out_metadata_option(opt_char, arg,
- &params->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,
- &params, &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!"