Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lib: Add lcfs_fd_measure_fsverity #394

Merged
merged 5 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
}
Loading