Skip to content

Commit

Permalink
Merge pull request #394 from cgwalters/add-fd-require-verity
Browse files Browse the repository at this point in the history
lib: Add lcfs_fd_measure_fsverity
  • Loading branch information
cgwalters authored Nov 19, 2024
2 parents de87a68 + 69078b9 commit 640eb95
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 29 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ jobs:
./hacking/installdeps.sh
meson setup build --werror
meson compile -C build
meson test -C build --timeout-multiplier 10
env CFS_TEST_ARCH_EMULATION=${{ matrix.arch }} meson test -C build --timeout-multiplier 10
- name: Upload log
uses: actions/upload-artifact@v4
if: always()
Expand Down
15 changes: 5 additions & 10 deletions libcomposefs/lcfs-mount.c
Original file line number Diff line number Diff line change
Expand Up @@ -210,20 +210,15 @@ static errint_t lcfs_validate_mount_options(struct lcfs_mount_state_s *state)

static errint_t lcfs_validate_verity_fd(struct lcfs_mount_state_s *state)
{
char buf[sizeof(struct fsverity_digest) + MAX_DIGEST_SIZE];
struct fsverity_digest *fsv = (struct fsverity_digest *)&buf;
int res;

if (state->expected_digest_len != 0) {
fsv->digest_size = MAX_DIGEST_SIZE;
res = ioctl(state->fd, FS_IOC_MEASURE_VERITY, fsv);
if (res == -1) {
if (errno == ENODATA || errno == EOPNOTSUPP || errno == ENOTTY)
return -ENOVERITY;
return -errno;
uint8_t found_digest[LCFS_DIGEST_SIZE];
res = lcfs_fd_measure_fsverity(found_digest, state->fd);
if (res < 0) {
return res;
}
if (fsv->digest_size != state->expected_digest_len ||
memcmp(state->expected_digest, fsv->digest, fsv->digest_size) != 0)
if (memcmp(state->expected_digest, found_digest, LCFS_DIGEST_SIZE) != 0)
return -EWRONGVERITY;
}

Expand Down
53 changes: 35 additions & 18 deletions libcomposefs/lcfs-writer.c
Original file line number Diff line number Diff line change
Expand Up @@ -564,42 +564,59 @@ int lcfs_compute_fsverity_from_fd(uint8_t *digest, int fd)
return lcfs_compute_fsverity_from_content(digest, &_fd, fsverity_read_cb);
}

// Given a file descriptor, first query the kernel for its fsverity digest. If
// it is not available in the kernel, perform an in-memory computation. The file
// position will always be reset to zero if needed.
int lcfs_fd_get_fsverity(uint8_t *digest, int fd)
// Given a file descriptor, query the kernel for its fsverity digest. It
// is an error if fsverity is not enabled.
int lcfs_fd_measure_fsverity(uint8_t *digest, int fd)
{
char buf[sizeof(struct fsverity_digest) + MAX_DIGEST_SIZE];
struct fsverity_digest *fsv = (struct fsverity_digest *)&buf;
union {
struct fsverity_digest fsv;
char buf[sizeof(struct fsverity_digest) + MAX_DIGEST_SIZE];
} result;

// First, ask the kernel if the file already has fsverity; if so we just return
// that.
fsv->digest_size = MAX_DIGEST_SIZE;
int res = ioctl(fd, FS_IOC_MEASURE_VERITY, fsv);
result.fsv.digest_size = MAX_DIGEST_SIZE;
int res = ioctl(fd, FS_IOC_MEASURE_VERITY, &result);
if (res == -1) {
// Under this condition, the file didn't have fsverity enabled or the
// kernel doesn't support it at all. We need to compute it in the current process.
if (errno == ENODATA || errno == EOPNOTSUPP || errno == ENOTTY) {
// For consistency ensure we start from the beginning. We could
// avoid this by using pread() in the future.
if (lseek(fd, 0, SEEK_SET) < 0)
return -errno;
return lcfs_compute_fsverity_from_fd(digest, fd);
// Canonicalize errno
errno = ENOVERITY;
}
// In this case, we found an unexpected error
return -errno;
}
// The file has fsverity enabled, but with an unexpected different algorithm (e.g. sha512).
// This is going to be a weird corner case. For now, we error out.
if (fsv->digest_size != LCFS_DIGEST_SIZE) {
if (result.fsv.digest_size != LCFS_DIGEST_SIZE) {
return -EWRONGVERITY;
}

memcpy(digest, buf + sizeof(struct fsverity_digest), LCFS_DIGEST_SIZE);
memcpy(digest, result.buf + sizeof(struct fsverity_digest), LCFS_DIGEST_SIZE);

return 0;
}

// Given a file descriptor, first query the kernel for its fsverity digest. If
// it is not available in the kernel, perform an in-memory computation. The file
// position will always be reset to zero if needed.
int lcfs_fd_get_fsverity(uint8_t *digest, int fd)
{
int res = lcfs_fd_measure_fsverity(digest, fd);
if (res == 0) {
return 0;
}
// Under this condition, the file didn't have fsverity enabled or the
// kernel doesn't support it at all. We need to compute it in the current process.
if (errno == ENODATA || errno == EOPNOTSUPP || errno == ENOTTY) {
// For consistency ensure we start from the beginning. We could
// avoid this by using pread() in the future.
if (lseek(fd, 0, SEEK_SET) < 0)
return -errno;
return lcfs_compute_fsverity_from_fd(digest, fd);
}
// In this case, we found an unexpected error
return -errno;
}

int lcfs_compute_fsverity_from_data(uint8_t *digest, uint8_t *data, size_t data_len)
{
FsVerityContext *ctx;
Expand Down
1 change: 1 addition & 0 deletions libcomposefs/lcfs-writer.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ LCFS_EXTERN int lcfs_compute_fsverity_from_content(uint8_t *digest, void *file,
LCFS_EXTERN int lcfs_compute_fsverity_from_fd(uint8_t *digest, int fd);
LCFS_EXTERN int lcfs_compute_fsverity_from_data(uint8_t *digest, uint8_t *data,
size_t data_len);
LCFS_EXTERN int lcfs_fd_measure_fsverity(uint8_t *digest, int fd);
LCFS_EXTERN int lcfs_fd_get_fsverity(uint8_t *digest, int fd);

LCFS_EXTERN int lcfs_node_set_from_content(struct lcfs_node_s *node, int dirfd,
Expand Down
20 changes: 20 additions & 0 deletions tests/test-lcfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
#define _GNU_SOURCE

#include "lcfs-writer.h"
#include "lcfs-mount.h"
#include <assert.h>
#include <unistd.h>
#include <errno.h>

static inline void lcfs_node_unrefp(struct lcfs_node_s **nodep)
Expand Down Expand Up @@ -75,8 +77,26 @@ static void test_add_uninitialized_child(void)
assert(errno == EINVAL);
}

// Verifies that lcfs_fd_measure_fsverity fails on a fd without fsverity
static void test_no_verity(void)
{
char buf[] = "/tmp/test-verity.XXXXXX";
int tmpfd = mkstemp(buf);
assert(tmpfd > 0);

uint8_t digest[LCFS_DIGEST_SIZE];
int r = lcfs_fd_measure_fsverity(digest, tmpfd);
int errsv = errno;
assert(r != 0);
// We may get ENOSYS from qemu userspace emulation not implementing the ioctl
if (getenv("CFS_TEST_ARCH_EMULATION") == NULL)
assert(errsv == ENOVERITY);
close(tmpfd);
}

int main(int argc, char **argv)
{
test_basic();
test_no_verity();
test_add_uninitialized_child();
}

0 comments on commit 640eb95

Please sign in to comment.