#ifdef HAVE_COMPOSEFS
if (repo->composefs_wanted != OT_TRISTATE_NO)
{
- gboolean apply_composefs_signature;
g_autofree guchar *fsverity_digest = NULL;
g_auto (GLnxTmpfile) tmpf = {
0,
if (!ostree_repo_load_commit (repo, revision, &commit_variant, NULL, error))
return FALSE;
- if (!ot_keyfile_get_boolean_with_default (repo->config, _OSTREE_INTEGRITY_SECTION,
- "composefs-apply-sig", TRUE,
- &apply_composefs_signature, error))
- return FALSE;
-
g_autoptr (GVariant) metadata = g_variant_get_child_value (commit_variant, 0);
g_autoptr (GVariant) metadata_composefs
= g_variant_lookup_value (metadata, "ostree.composefs", G_VARIANT_TYPE_BYTESTRING);
if (!glnx_fchmod (tmpf.fd, 0644, error))
return FALSE;
- 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. */
+ if (!_ostree_tmpf_fsverity (repo, &tmpf, NULL, error))
+ return FALSE;
+ if (metadata_composefs && metadata_composefs_sig)
+ {
+ g_autofree char *composefs_digest_path
+ = g_strdup_printf ("%s/.ostree.cfs.digest", checkout_target_name);
g_autofree char *composefs_sig_path
= g_strdup_printf ("%s/.ostree.cfs.sig", checkout_target_name);
+ g_autoptr (GBytes) digest = g_variant_get_data_as_bytes (metadata_composefs);
g_autoptr (GBytes) sig = g_variant_get_data_as_bytes (metadata_composefs_sig);
+ if (!glnx_file_replace_contents_at (osdeploy_dfd, composefs_digest_path,
+ g_bytes_get_data (digest, NULL),
+ g_bytes_get_size (digest), 0, cancellable, error))
+ return FALSE;
+
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;
- }
- else
- {
- if (!_ostree_tmpf_fsverity (repo, &tmpf, NULL, error))
- return FALSE;
+
+ /* The signature should be applied as a fs-verity signature to the digest file. However
+ * we can't do that until boot, because we can't guarantee that the public key is
+ * loaded into the keyring until we boot the new initrd. So the signature is applied
+ * in ostree-prepare-root on first boot.
+ */
}
if (!glnx_link_tmpfile_at (&tmpf, GLNX_LINK_TMPFILE_REPLACE, osdeploy_dfd, composefs_cfs_path,
#ifdef HAVE_COMPOSEFS
#include <libcomposefs/lcfs-mount.h>
+#include <libcomposefs/lcfs-writer.h>
#endif
#include "ostree-mount-util.h"
return deploy_path;
}
+#ifdef HAVE_COMPOSEFS
+static void
+apply_digest_signature (const char *digestfile, const char *sigfile)
+{
+ unsigned char *signature;
+ size_t signature_len;
+ int digest_is_readonly;
+ int digest_fd;
+
+ signature = read_file (sigfile, &signature_len);
+ if (signature == NULL)
+ err (EXIT_FAILURE, "Missing signaure file %s", sigfile);
+
+ /* If we're read-only we temporarily make read-write bind mount to sign */
+ digest_is_readonly = path_is_on_readonly_fs (digestfile);
+ if (digest_is_readonly)
+ {
+ if (mount (digestfile, digestfile, NULL, MS_BIND | MS_SILENT, NULL) < 0)
+ err (EXIT_FAILURE, "failed to bind mount %s (for signing)", digestfile);
+ if (mount (digestfile, digestfile, NULL, MS_REMOUNT | MS_SILENT, NULL) < 0)
+ err (EXIT_FAILURE, "failed to remount %s read-write (for signing)", digestfile);
+ }
+
+ /* Ensure we re-open after any bindmounts */
+ digest_fd = open (digestfile, O_RDONLY | O_CLOEXEC);
+ if (digest_fd < 0)
+ err (EXIT_FAILURE, "failed to open %s", digestfile);
+
+ fsverity_sign (digest_fd, signature, signature_len);
+
+ close (digest_fd);
+
+ if (digest_is_readonly && umount2 (digestfile, MNT_DETACH) < 0)
+ err (EXIT_FAILURE, "failed to unmount %s (after signing)", digestfile);
+
+ free (signature);
+
+#ifdef USE_LIBSYSTEMD
+ sd_journal_send ("MESSAGE=Applied fsverity signature %s to %s", sigfile, digestfile, NULL);
+#endif
+}
+
+static void
+ensure_digest_fd_is_signed (int digest_fd)
+{
+ struct fsverity_read_metadata_arg read_metadata = { 0 };
+ char sig_data[1];
+ int res;
+
+ /* We verify there is a signature by reading one byte of it. */
+
+ read_metadata.metadata_type = FS_VERITY_METADATA_TYPE_SIGNATURE;
+ read_metadata.offset = 0;
+ read_metadata.length = sizeof (sig_data);
+ read_metadata.buf_ptr = (size_t)&sig_data;
+
+ res = ioctl (digest_fd, FS_IOC_READ_VERITY_METADATA, &read_metadata);
+ if (res == -1)
+ {
+ if (errno == ENODATA)
+ err (EXIT_FAILURE, "Digest file is unexpectedly not signed");
+ else
+ err (EXIT_FAILURE, "Failed to get signature from digest file");
+ }
+}
+
+static char *
+read_signed_digest (const char *digestfile, const char *sigfile)
+{
+ unsigned fd_flags;
+ int digest_fd;
+ unsigned char buf[LCFS_DIGEST_SIZE];
+ char *digest;
+ ssize_t bytes_read;
+
+ digest_fd = open (digestfile, O_RDONLY | O_CLOEXEC);
+ if (digest_fd < 0)
+ err (EXIT_FAILURE, "failed to open %s", digestfile);
+
+ /* Check if file is already fsverity */
+ if (ioctl (digest_fd, FS_IOC_GETFLAGS, &fd_flags) < 0)
+ err (EXIT_FAILURE, "failed to get fd flags for %s", digestfile);
+
+ /* If it is not, apply signature */
+ if ((fd_flags & FS_VERITY_FL) == 0)
+ {
+ close (digest_fd);
+
+ apply_digest_signature (digestfile, sigfile);
+
+ /* Reopen */
+ digest_fd = open (digestfile, O_RDONLY | O_CLOEXEC);
+ if (digest_fd < 0)
+ err (EXIT_FAILURE, "failed to reopen %s", digestfile);
+ }
+
+ /* By now we know its fs-verify enabled, also ensure it is signed
+ * with a key in the keyring */
+ ensure_digest_fd_is_signed (digest_fd);
+
+ /* Load the expected digest */
+ do
+ bytes_read = read (digest_fd, buf, LCFS_DIGEST_SIZE);
+ while (bytes_read == -1 && errno == EINTR);
+ if (bytes_read == -1)
+ err (EXIT_FAILURE, "Failed to read digest file");
+
+ if (bytes_read != LCFS_DIGEST_SIZE)
+ err (EXIT_FAILURE, "Digest file has wrong size");
+
+ digest = malloc (LCFS_DIGEST_SIZE * 2 + 1);
+ if (digest == NULL)
+ err (EXIT_FAILURE, "Out of memory");
+
+ bin2hex (digest, buf, LCFS_DIGEST_SIZE);
+
+#ifdef USE_LIBSYSTEMD
+ sd_journal_send ("MESSAGE=Signed digest file found for root", NULL);
+#endif
+
+ return digest;
+}
+#endif
+
static int
pivot_root (const char *new_root, const char *put_old)
{
objdirs,
1,
};
- int cfs_fd;
- unsigned cfs_flags;
-
- cfs_fd = open (OSTREE_COMPOSEFS_NAME, O_RDONLY | O_CLOEXEC);
- if (cfs_fd < 0)
- {
- if (errno == ENOENT)
- goto nocfs;
-
- err (EXIT_FAILURE, "failed to open %s", OSTREE_COMPOSEFS_NAME);
- }
- /* Check if file is already fsverity */
- if (ioctl (cfs_fd, FS_IOC_GETFLAGS, &cfs_flags) < 0)
- err (EXIT_FAILURE, "failed to get %s flags", OSTREE_COMPOSEFS_NAME);
-
- /* It is not, apply signature (if it exists) */
- if ((cfs_flags & FS_VERITY_FL) == 0)
+ if (composefs_mode == OSTREE_COMPOSEFS_MODE_SIGNED)
{
- const char signame[] = OSTREE_COMPOSEFS_NAME ".sig";
- unsigned char *signature;
- size_t signature_len;
-
- signature = read_file (signame, &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)");
-
-#ifdef USE_LIBSYSTEMD
- sd_journal_send ("MESSAGE=Applied fsverity signature %s", signame, NULL);
-#endif
- }
- else
- {
-#ifdef USE_LIBSYSTEMD
- sd_journal_send ("MESSAGE=No fsverity signature found for root", NULL);
-#endif
- }
+ composefs_digest
+ = read_signed_digest (OSTREE_COMPOSEFS_NAME ".digest", OSTREE_COMPOSEFS_NAME ".sig");
+ composefs_mode = OSTREE_COMPOSEFS_MODE_DIGEST;
}
cfs_options.flags = LCFS_MOUNT_FLAGS_READONLY;
err (EXIT_FAILURE, "failed to assemble /boot/loader path");
cfs_options.image_mountdir = srcpath;
- if (composefs_mode == OSTREE_COMPOSEFS_MODE_SIGNED)
- {
- cfs_options.flags |= LCFS_MOUNT_FLAGS_REQUIRE_SIGNATURE | LCFS_MOUNT_FLAGS_REQUIRE_VERITY;
- }
- else if (composefs_mode == OSTREE_COMPOSEFS_MODE_DIGEST)
+ if (composefs_mode == OSTREE_COMPOSEFS_MODE_DIGEST)
{
cfs_options.flags |= LCFS_MOUNT_FLAGS_REQUIRE_VERITY;
cfs_options.expected_digest = composefs_digest;
}
- if (lcfs_mount_fd (cfs_fd, TMP_SYSROOT, &cfs_options) == 0)
+#ifdef USE_LIBSYSTEMD
+ if (composefs_mode == OSTREE_COMPOSEFS_MODE_MAYBE)
+ sd_journal_send ("MESSAGE=Trying to mount composefs rootfs", NULL);
+ else if (composefs_mode == OSTREE_COMPOSEFS_MODE_DIGEST)
+ sd_journal_send ("MESSAGE=Mounting composefs rootfs with expected digest '%s'",
+ composefs_digest, NULL);
+ else
+ sd_journal_send ("MESSAGE=Mounting composefs rootfs", NULL);
+#endif
+
+ if (lcfs_mount_image (OSTREE_COMPOSEFS_NAME, TMP_SYSROOT, &cfs_options) == 0)
{
int fd = open (_OSTREE_COMPOSEFS_ROOT_STAMP, O_WRONLY | O_CREAT | O_CLOEXEC, 0644);
if (fd < 0)
using_composefs = 1;
}
-
- close (cfs_fd);
-
- nocfs:
+ else
+ {
+#ifdef USE_LIBSYSTEMD
+ if (errno == ENOVERITY)
+ sd_journal_send ("MESSAGE=No verity in composefs image", NULL);
+ else if (errno == EWRONGVERITY)
+ sd_journal_send ("MESSAGE=Wrong verity digest in composefs image", NULL);
+ else if (errno == ENOSIGNATURE)
+ sd_journal_send ("MESSAGE=Missing signature in composefs image", NULL);
+ else
+ sd_journal_send ("MESSAGE=Mounting composefs image failed: %s", strerror (errno), NULL);
+#endif
+ }
#else
err (EXIT_FAILURE, "Composefs not supported");
#endif