diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c index cbb56c8798..c0ce1e94c3 100644 --- a/src/libostree/ostree-sysroot-deploy.c +++ b/src/libostree/ostree-sysroot-deploy.c @@ -649,7 +649,6 @@ checkout_deployment_tree (OstreeSysroot *sysroot, OstreeRepo *repo, OstreeDeploy #ifdef HAVE_COMPOSEFS if (repo->composefs_wanted != OT_TRISTATE_NO) { - g_autoptr (GBytes) sig = NULL; gboolean apply_composefs_signature; g_autofree guchar *fsverity_digest = NULL; g_auto (GLnxTmpfile) tmpf = { @@ -699,22 +698,26 @@ checkout_deployment_tree (OstreeSysroot *sysroot, OstreeRepo *repo, OstreeDeploy if (!glnx_fchmod (tmpf.fd, 0644, error)) return FALSE; - if (metadata_composefs_sig) + if (metadata_composefs_sig && apply_composefs_signature) { + /* We can't apply the signature during deploy, because the corresponding public key for + this commit is not loaded into the keyring. So, we delay fs-verity application to the + first boot. */ + g_autofree char *composefs_sig_path = g_strdup_printf ("%s/.ostree.cfs.sig", checkout_target_name); + g_autoptr (GBytes) sig = g_variant_get_data_as_bytes (metadata_composefs_sig); - sig = g_variant_get_data_as_bytes (metadata_composefs_sig); - - /* Write signature to file so it can be applied later if needed */ if (!glnx_file_replace_contents_at (osdeploy_dfd, composefs_sig_path, g_bytes_get_data (sig, NULL), g_bytes_get_size (sig), 0, cancellable, error)) return FALSE; } - - if (!_ostree_tmpf_fsverity (repo, &tmpf, apply_composefs_signature ? sig : NULL, error)) - return FALSE; + else + { + if (!_ostree_tmpf_fsverity (repo, &tmpf, NULL, error)) + return FALSE; + } if (!glnx_link_tmpfile_at (&tmpf, GLNX_LINK_TMPFILE_REPLACE, osdeploy_dfd, composefs_cfs_path, error)) diff --git a/src/switchroot/ostree-mount-util.h b/src/switchroot/ostree-mount-util.h index b6ee2f56c6..fdf1a43476 100644 --- a/src/switchroot/ostree-mount-util.h +++ b/src/switchroot/ostree-mount-util.h @@ -24,12 +24,18 @@ #include #include #include +#include #include #include #include +#include #include #include +#ifdef HAVE_LINUX_FSVERITY_H +#include +#endif + #define INITRAMFS_MOUNT_VAR "/run/ostree/initramfs-mount-var" #define _OSTREE_SYSROOT_READONLY_STAMP "/run/ostree-sysroot-ro.stamp" #define _OSTREE_COMPOSEFS_ROOT_STAMP "/run/ostree-composefs-root.stamp" @@ -123,4 +129,65 @@ touch_run_ostree (void) (void)close (fd); } +static inline unsigned char * +read_file (const char *path, size_t *out_len) +{ + int fd; + + fd = open (path, O_RDONLY); + if (fd < 0) + { + if (errno == ENOENT) + return NULL; + err (EXIT_FAILURE, "failed to open %s", path); + } + + struct stat stbuf; + if (fstat (fd, &stbuf)) + err (EXIT_FAILURE, "fstat(%s) failed", path); + + size_t file_size = stbuf.st_size; + unsigned char *buf = malloc (file_size); + if (buf == NULL) + err (EXIT_FAILURE, "Out of memory"); + + size_t file_read = 0; + while (file_read < file_size) + { + ssize_t bytes_read; + do + bytes_read = read (fd, buf + file_read, file_size - file_read); + while (bytes_read == -1 && errno == EINTR); + if (bytes_read == -1) + err (EXIT_FAILURE, "read_file(%s) failed", path); + if (bytes_read == 0) + break; + + file_read += bytes_read; + } + + close (fd); + + *out_len = file_read; + return buf; +} + +static inline void +fsverity_sign (int fd, unsigned char *signature, size_t signature_len) +{ +#ifdef HAVE_LINUX_FSVERITY_H + struct fsverity_enable_arg arg = { + 0, + }; + arg.version = 1; + arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; + arg.block_size = 4096; + arg.sig_size = signature_len; + arg.sig_ptr = (uint64_t)signature; + + if (ioctl (fd, FS_IOC_ENABLE_VERITY, &arg) < 0) + err (EXIT_FAILURE, "failed to fs-verity sign file"); +#endif +} + #endif /* __OSTREE_MOUNT_UTIL_H_ */ diff --git a/src/switchroot/ostree-prepare-root.c b/src/switchroot/ostree-prepare-root.c index a65da45bab..136502cc06 100644 --- a/src/switchroot/ostree-prepare-root.c +++ b/src/switchroot/ostree-prepare-root.c @@ -66,6 +66,7 @@ #include #include #include +#include #include #include #include @@ -73,6 +74,10 @@ #include #include +/* We can't include both linux/fs.h and sys/mount.h, so define these directly */ +#define FS_VERITY_FL 0x00100000 /* Verity protected inode */ +#define FS_IOC_GETFLAGS _IOR ('f', 1, long) + #if defined(HAVE_LIBSYSTEMD) && !defined(OSTREE_PREPARE_ROOT_STATIC) #define USE_LIBSYSTEMD #endif @@ -307,6 +312,47 @@ main (int argc, char *argv[]) objdirs, 1, }; + int cfs_fd; + unsigned cfs_flags; + + cfs_fd = open (".ostree.cfs", O_RDONLY); + if (cfs_fd < 0) + { + if (errno == ENOENT) + goto nocfs; + + err (EXIT_FAILURE, "failed to open .ostree.cfs"); + } + + /* Check if file is already fsverity */ + if (ioctl (cfs_fd, FS_IOC_GETFLAGS, &cfs_flags) < 0) + err (EXIT_FAILURE, "failed to get .ostree.cfs flags"); + + /* It is not, apply signature (if it exists) */ + if ((cfs_flags & FS_VERITY_FL) == 0) + { + unsigned char *signature; + size_t signature_len; + + signature = read_file (".ostree.cfs.sig", &signature_len); + if (signature != NULL) + { + /* If we're read-only we temporarily make it read-write to sign the image */ + if (!sysroot_currently_writable + && mount (root_mountpoint, root_mountpoint, NULL, MS_REMOUNT | MS_SILENT, NULL) + < 0) + err (EXIT_FAILURE, "failed to remount rootfs writable (for signing)"); + + fsverity_sign (cfs_fd, signature, signature_len); + free (signature); + + if (!sysroot_currently_writable + && mount (root_mountpoint, root_mountpoint, NULL, + MS_REMOUNT | MS_RDONLY | MS_SILENT, NULL) + < 0) + err (EXIT_FAILURE, "failed to remount rootfs back read-only (after signing)"); + } + } cfs_options.flags = LCFS_MOUNT_FLAGS_READONLY; @@ -324,12 +370,7 @@ main (int argc, char *argv[]) cfs_options.expected_digest = composefs_digest; } - if (lcfs_mount_image (".ostree.cfs", "/sysroot.tmp", &cfs_options) < 0) - { - if (composefs_mode > OSTREE_COMPOSEFS_MODE_MAYBE) - err (EXIT_FAILURE, "Failed to mount composefs"); - } - else + if (lcfs_mount_fd (cfs_fd, TMP_SYSROOT, &cfs_options) == 0) { int fd = open (_OSTREE_COMPOSEFS_ROOT_STAMP, O_WRONLY | O_CREAT | O_CLOEXEC, 0644); if (fd < 0) @@ -338,6 +379,10 @@ main (int argc, char *argv[]) using_composefs = 1; } + + close (cfs_fd); + + nocfs: #else err (EXIT_FAILURE, "Composefs not supported"); #endif @@ -345,6 +390,9 @@ main (int argc, char *argv[]) if (!using_composefs) { + if (composefs_mode > OSTREE_COMPOSEFS_MODE_MAYBE) + err (EXIT_FAILURE, "Failed to mount composefs"); + /* The deploy root starts out bind mounted to sysroot.tmp */ if (mount (deploy_path, TMP_SYSROOT, NULL, MS_BIND | MS_SILENT, NULL) < 0) err (EXIT_FAILURE, "failed to make initial bind mount %s", deploy_path);