mirror of
https://github.com/ostreedev/ostree.git
synced 2026-02-05 09:44:55 +01:00
prepare-root: Factor out composefs handling into otcore
I'm thinking about creating ostree-prepare-soft-reboot.c. Prepare for this by factoring out shared helper functions.
This commit is contained in:
@@ -22,5 +22,11 @@ libotcore_la_SOURCES = \
|
||||
src/libotcore/otcore-spki-verify.c \
|
||||
$(NULL)
|
||||
|
||||
libotcore_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/libglnx -I$(srcdir)/src/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(OT_INTERNAL_GIO_UNIX_CFLAGS) $(OT_INTERNAL_GPGME_CFLAGS) $(OT_DEP_CRYPTO_LIBS) $(LIBSYSTEMD_CFLAGS)
|
||||
# Note that this also uses *includes* from libostree, so there's a partial
|
||||
# circular dependency.
|
||||
libotcore_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/libglnx -I$(srcdir)/src/libotutil -I$(srcdir)/src/libostree -DLOCALEDIR=\"$(datadir)/locale\" $(OT_INTERNAL_GIO_UNIX_CFLAGS) $(OT_INTERNAL_GPGME_CFLAGS) $(OT_DEP_CRYPTO_LIBS) $(LIBSYSTEMD_CFLAGS)
|
||||
libotcore_la_LIBADD = $(OT_INTERNAL_GIO_UNIX_LIBS) $(OT_INTERNAL_GPGME_LIBS) $(LIBSYSTEMD_LIBS) $(OT_DEP_CRYPTO_LIBS)
|
||||
|
||||
if USE_COMPOSEFS
|
||||
libotcore_la_LIBADD += $(OT_DEP_COMPOSEFS_LIBS)
|
||||
endif
|
||||
@@ -18,6 +18,14 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "otcore.h"
|
||||
#include <errno.h>
|
||||
#include <ostree-core.h>
|
||||
#include <ostree-repo-private.h>
|
||||
|
||||
#ifdef HAVE_COMPOSEFS
|
||||
#include <libcomposefs/lcfs-mount.h>
|
||||
#include <libcomposefs/lcfs-writer.h>
|
||||
#endif
|
||||
|
||||
// This key is used by default if present in the initramfs to verify
|
||||
// the signature on the target commit object. When composefs is
|
||||
@@ -247,3 +255,237 @@ otcore_load_composefs_config (const char *cmdline, GKeyFile *config, gboolean lo
|
||||
|
||||
return g_steal_pointer (&ret);
|
||||
}
|
||||
|
||||
#ifdef HAVE_COMPOSEFS
|
||||
static GVariant *
|
||||
load_variant (const char *root_mountpoint, const char *digest, const char *extension,
|
||||
const GVariantType *type, GError **error)
|
||||
{
|
||||
g_autofree char *path = g_strdup_printf ("%s/ostree/repo/objects/%.2s/%s.%s", root_mountpoint,
|
||||
digest, digest + 2, extension);
|
||||
|
||||
char *data = NULL;
|
||||
gsize data_size;
|
||||
if (!g_file_get_contents (path, &data, &data_size, error))
|
||||
return NULL;
|
||||
|
||||
return g_variant_ref_sink (g_variant_new_from_data (type, data, data_size, FALSE, g_free, data));
|
||||
}
|
||||
|
||||
// Given a mount point, directly load the .commit object. At the current time this tool
|
||||
// doesn't link to libostree.
|
||||
static gboolean
|
||||
load_commit_for_deploy (const char *root_mountpoint, const char *deploy_path, GVariant **commit_out,
|
||||
GVariant **commitmeta_out, GError **error)
|
||||
{
|
||||
g_autoptr (GError) local_error = NULL;
|
||||
g_autofree char *digest = g_path_get_basename (deploy_path);
|
||||
char *dot = strchr (digest, '.');
|
||||
if (dot != NULL)
|
||||
*dot = 0;
|
||||
|
||||
g_autoptr (GVariant) commit_v
|
||||
= load_variant (root_mountpoint, digest, "commit", OSTREE_COMMIT_GVARIANT_FORMAT, error);
|
||||
if (commit_v == NULL)
|
||||
return FALSE;
|
||||
|
||||
g_autoptr (GVariant) commitmeta_v = load_variant (root_mountpoint, digest, "commitmeta",
|
||||
G_VARIANT_TYPE ("a{sv}"), &local_error);
|
||||
if (commitmeta_v == NULL)
|
||||
{
|
||||
if (g_error_matches (local_error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
|
||||
glnx_throw (error, "No commitmeta for commit %s", digest);
|
||||
else
|
||||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*commit_out = g_steal_pointer (&commit_v);
|
||||
*commitmeta_out = g_steal_pointer (&commitmeta_v);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* validate_signature:
|
||||
* @data: The raw data whose signature must be validated
|
||||
* @signatures: A variant of type "ay" (byte array) containing signatures
|
||||
* @pubkeys: an array of type GBytes*
|
||||
*
|
||||
* Verify that @data is signed using @signatures and @pubkeys.
|
||||
*/
|
||||
static gboolean
|
||||
validate_signature (GBytes *data, GVariant *signatures, GPtrArray *pubkeys, GError **error)
|
||||
{
|
||||
g_assert (data);
|
||||
g_assert (signatures);
|
||||
g_assert (pubkeys);
|
||||
|
||||
for (gsize j = 0; j < pubkeys->len; j++)
|
||||
{
|
||||
GBytes *pubkey = pubkeys->pdata[j];
|
||||
g_assert (pubkey);
|
||||
|
||||
for (gsize i = 0; i < g_variant_n_children (signatures); i++)
|
||||
{
|
||||
g_autoptr (GVariant) child = g_variant_get_child_value (signatures, i);
|
||||
g_autoptr (GBytes) signature = g_variant_get_data_as_bytes (child);
|
||||
bool valid = false;
|
||||
|
||||
if (!otcore_validate_ed25519_signature (data, pubkey, signature, &valid, error))
|
||||
return glnx_prefix_error (error, "signature verification failed");
|
||||
// At least one valid signature is enough.
|
||||
if (valid)
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Output a friendly message based on an errno for common cases
|
||||
static const char *
|
||||
composefs_error_message (int errsv)
|
||||
{
|
||||
switch (errsv)
|
||||
{
|
||||
case ENOVERITY:
|
||||
return "fsverity not enabled on composefs image";
|
||||
case EWRONGVERITY:
|
||||
return "Wrong fsverity digest in composefs image";
|
||||
case ENOSIGNATURE:
|
||||
return "Missing signature for fsverity in composefs image";
|
||||
default:
|
||||
return strerror (errsv);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
gboolean
|
||||
otcore_mount_composefs (ComposefsConfig *composefs_config, GVariantBuilder *metadata_builder,
|
||||
gboolean root_transient, const char *root_mountpoint,
|
||||
const char *deploy_path, const char *mount_target,
|
||||
bool *out_using_composefs, GError **error)
|
||||
{
|
||||
bool using_composefs = FALSE;
|
||||
#ifdef HAVE_COMPOSEFS
|
||||
/* We construct the new sysroot in /sysroot.tmp, which is either the composefs
|
||||
mount or a bind mount of the deploy-dir */
|
||||
if (composefs_config->enabled == OT_TRISTATE_NO)
|
||||
return TRUE;
|
||||
|
||||
const char *objdirs[] = { "/sysroot/ostree/repo/objects" };
|
||||
struct lcfs_mount_options_s cfs_options = {
|
||||
objdirs,
|
||||
1,
|
||||
};
|
||||
|
||||
cfs_options.flags = 0;
|
||||
cfs_options.image_mountdir = OSTREE_COMPOSEFS_LOWERMNT;
|
||||
if (mkdirat (AT_FDCWD, OSTREE_COMPOSEFS_LOWERMNT, 0700) < 0 && errno != EEXIST)
|
||||
return glnx_throw (error, "Failed to create %s", OSTREE_COMPOSEFS_LOWERMNT);
|
||||
|
||||
g_autofree char *expected_digest = NULL;
|
||||
|
||||
// For now we just stick the transient root on the default /run tmpfs;
|
||||
// however, see
|
||||
// https://github.com/systemd/systemd/blob/604b2001081adcbd64ee1fbe7de7a6d77c5209fe/src/basic/mountpoint-util.h#L36
|
||||
// which bumps up these defaults for the rootfs a bit.
|
||||
g_autofree char *root_upperdir
|
||||
= root_transient ? g_build_filename (OTCORE_RUN_OSTREE_PRIVATE, "root/upper", NULL) : NULL;
|
||||
g_autofree char *root_workdir
|
||||
= root_transient ? g_build_filename (OTCORE_RUN_OSTREE_PRIVATE, "root/work", NULL) : NULL;
|
||||
|
||||
// Propagate these options for transient root, if provided
|
||||
if (root_transient)
|
||||
{
|
||||
if (!glnx_shutil_mkdir_p_at (AT_FDCWD, root_upperdir, 0755, NULL, error))
|
||||
return glnx_prefix_error (error, "Failed to create %s", root_upperdir);
|
||||
if (!glnx_shutil_mkdir_p_at (AT_FDCWD, root_workdir, 0700, NULL, error))
|
||||
return glnx_prefix_error (error, "Failed to create %s", root_workdir);
|
||||
|
||||
cfs_options.workdir = root_workdir;
|
||||
cfs_options.upperdir = root_upperdir;
|
||||
}
|
||||
else
|
||||
{
|
||||
cfs_options.flags = LCFS_MOUNT_FLAGS_READONLY;
|
||||
}
|
||||
|
||||
if (composefs_config->is_signed)
|
||||
{
|
||||
const char *composefs_pubkey = composefs_config->signature_pubkey;
|
||||
g_autoptr (GVariant) commit = NULL;
|
||||
g_autoptr (GVariant) commitmeta = NULL;
|
||||
|
||||
if (!load_commit_for_deploy (root_mountpoint, deploy_path, &commit, &commitmeta, error))
|
||||
return glnx_prefix_error (error, "Error loading signatures from repo");
|
||||
|
||||
g_autoptr (GVariant) signatures = g_variant_lookup_value (
|
||||
commitmeta, OSTREE_SIGN_METADATA_ED25519_KEY, G_VARIANT_TYPE ("aay"));
|
||||
if (signatures == NULL)
|
||||
return glnx_throw (error, "Signature validation requested, but no signatures in commit");
|
||||
|
||||
g_autoptr (GBytes) commit_data = g_variant_get_data_as_bytes (commit);
|
||||
if (!validate_signature (commit_data, signatures, composefs_config->pubkeys, error))
|
||||
return glnx_throw (error, "No valid signatures found for public key");
|
||||
|
||||
g_print ("composefs+ostree: Validated commit signature using '%s'\n", composefs_pubkey);
|
||||
g_variant_builder_add (metadata_builder, "{sv}", OTCORE_RUN_BOOTED_KEY_COMPOSEFS_SIGNATURE,
|
||||
g_variant_new_string (composefs_pubkey));
|
||||
|
||||
g_autoptr (GVariant) metadata = g_variant_get_child_value (commit, 0);
|
||||
g_autoptr (GVariant) cfs_digest_v = g_variant_lookup_value (
|
||||
metadata, OSTREE_COMPOSEFS_DIGEST_KEY_V0, G_VARIANT_TYPE_BYTESTRING);
|
||||
if (cfs_digest_v == NULL || g_variant_get_size (cfs_digest_v) != OSTREE_SHA256_DIGEST_LEN)
|
||||
return glnx_throw (error, "Signature validation requested, but no valid digest in commit");
|
||||
const guint8 *cfs_digest_buf = ot_variant_get_data (cfs_digest_v, error);
|
||||
if (!cfs_digest_buf)
|
||||
return glnx_prefix_error (error, "Failed to query digest");
|
||||
|
||||
expected_digest = g_malloc (OSTREE_SHA256_STRING_LEN + 1);
|
||||
ot_bin2hex (expected_digest, cfs_digest_buf, g_variant_get_size (cfs_digest_v));
|
||||
|
||||
g_assert (composefs_config->require_verity);
|
||||
cfs_options.flags |= LCFS_MOUNT_FLAGS_REQUIRE_VERITY;
|
||||
g_print ("composefs: Verifying digest: %s\n", expected_digest);
|
||||
cfs_options.expected_fsverity_digest = expected_digest;
|
||||
}
|
||||
else if (composefs_config->require_verity)
|
||||
{
|
||||
cfs_options.flags |= LCFS_MOUNT_FLAGS_REQUIRE_VERITY;
|
||||
}
|
||||
|
||||
if (lcfs_mount_image (OSTREE_COMPOSEFS_NAME, mount_target, &cfs_options) == 0)
|
||||
{
|
||||
using_composefs = true;
|
||||
bool using_verity = (cfs_options.flags & LCFS_MOUNT_FLAGS_REQUIRE_VERITY) > 0;
|
||||
g_variant_builder_add (metadata_builder, "{sv}", OTCORE_RUN_BOOTED_KEY_COMPOSEFS,
|
||||
g_variant_new_boolean (true));
|
||||
g_variant_builder_add (metadata_builder, "{sv}", OTCORE_RUN_BOOTED_KEY_COMPOSEFS_VERITY,
|
||||
g_variant_new_boolean (using_verity));
|
||||
g_print ("composefs: mounted successfully (verity=%s)\n", using_verity ? "true" : "false");
|
||||
}
|
||||
else
|
||||
{
|
||||
int errsv = errno;
|
||||
g_assert (composefs_config->enabled != OT_TRISTATE_NO);
|
||||
if (composefs_config->enabled == OT_TRISTATE_MAYBE && errsv == ENOENT)
|
||||
{
|
||||
g_print ("composefs: No image present\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *errmsg = composefs_error_message (errsv);
|
||||
return glnx_throw (error, "composefs: failed to mount: %s", errmsg);
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* if composefs is configured as "maybe", we should continue */
|
||||
if (composefs_config->enabled == OT_TRISTATE_YES)
|
||||
return glnx_throw (error, "composefs: enabled at runtime, but support is not compiled in");
|
||||
#endif
|
||||
*out_using_composefs = using_composefs;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -78,6 +78,27 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (ComposefsConfig, otcore_free_composefs_config)
|
||||
ComposefsConfig *otcore_load_composefs_config (const char *cmdline, GKeyFile *config,
|
||||
gboolean load_keys, GError **error);
|
||||
|
||||
/**
|
||||
* otcore_mount_composefs:
|
||||
* @composefs_config: Configuration for composefs.
|
||||
* @metadata_builder: (transfer none): GVariantBuilder to add metadata to.
|
||||
* @root_transient: Whether the root filesystem is transient.
|
||||
* @root_mountpoint: The mount point of the physical root filesystem.
|
||||
* @deploy_path: The path to the deployment.
|
||||
* @mount_target: The target path to mount the composefs image.
|
||||
* @out_using_composefs: (out): Whether composefs was successfully used.
|
||||
* @error: (out): Return location for a GError, or %NULL.
|
||||
*
|
||||
* Mounts a composefs image based on the provided configuration.
|
||||
*
|
||||
* Returns: %TRUE on success, %FALSE on error.
|
||||
*/
|
||||
gboolean otcore_mount_composefs (ComposefsConfig *composefs_config,
|
||||
GVariantBuilder *metadata_builder, gboolean root_transient,
|
||||
const char *root_mountpoint, const char *deploy_path,
|
||||
const char *mount_target, bool *out_using_composefs,
|
||||
GError **error);
|
||||
|
||||
// Our directory with transient state (eventually /run/ostree-booted should be a link to
|
||||
// /run/ostree/booted)
|
||||
#define OTCORE_RUN_OSTREE "/run/ostree"
|
||||
|
||||
@@ -90,11 +90,6 @@
|
||||
// A temporary mount point
|
||||
#define TMP_SYSROOT "/sysroot.tmp"
|
||||
|
||||
#ifdef HAVE_COMPOSEFS
|
||||
#include <libcomposefs/lcfs-mount.h>
|
||||
#include <libcomposefs/lcfs-writer.h>
|
||||
#endif
|
||||
|
||||
#include "ostree-mount-util.h"
|
||||
|
||||
static GOptionEntry options[] = { { NULL } };
|
||||
@@ -149,113 +144,6 @@ resolve_deploy_path (const char *kernel_cmdline, const char *root_mountpoint)
|
||||
return deploy_path;
|
||||
}
|
||||
|
||||
#ifdef HAVE_COMPOSEFS
|
||||
static GVariant *
|
||||
load_variant (const char *root_mountpoint, const char *digest, const char *extension,
|
||||
const GVariantType *type, GError **error)
|
||||
{
|
||||
g_autofree char *path = g_strdup_printf ("%s/ostree/repo/objects/%.2s/%s.%s", root_mountpoint,
|
||||
digest, digest + 2, extension);
|
||||
|
||||
char *data = NULL;
|
||||
gsize data_size;
|
||||
if (!g_file_get_contents (path, &data, &data_size, error))
|
||||
return NULL;
|
||||
|
||||
return g_variant_ref_sink (g_variant_new_from_data (type, data, data_size, FALSE, g_free, data));
|
||||
}
|
||||
|
||||
// Given a mount point, directly load the .commit object. At the current time this tool
|
||||
// doesn't link to libostree.
|
||||
static gboolean
|
||||
load_commit_for_deploy (const char *root_mountpoint, const char *deploy_path, GVariant **commit_out,
|
||||
GVariant **commitmeta_out, GError **error)
|
||||
{
|
||||
g_autoptr (GError) local_error = NULL;
|
||||
g_autofree char *digest = g_path_get_basename (deploy_path);
|
||||
char *dot = strchr (digest, '.');
|
||||
if (dot != NULL)
|
||||
*dot = 0;
|
||||
|
||||
g_autoptr (GVariant) commit_v
|
||||
= load_variant (root_mountpoint, digest, "commit", OSTREE_COMMIT_GVARIANT_FORMAT, error);
|
||||
if (commit_v == NULL)
|
||||
return FALSE;
|
||||
|
||||
g_autoptr (GVariant) commitmeta_v = load_variant (root_mountpoint, digest, "commitmeta",
|
||||
G_VARIANT_TYPE ("a{sv}"), &local_error);
|
||||
if (commitmeta_v == NULL)
|
||||
{
|
||||
if (g_error_matches (local_error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
|
||||
glnx_throw (error, "No commitmeta for commit %s", digest);
|
||||
else
|
||||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*commit_out = g_steal_pointer (&commit_v);
|
||||
*commitmeta_out = g_steal_pointer (&commitmeta_v);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* validate_signature:
|
||||
* @data: The raw data whose signature must be validated
|
||||
* @signatures: A variant of type "ay" (byte array) containing signatures
|
||||
* @pubkeys: an array of type GBytes*
|
||||
*
|
||||
* Verify that @data is signed using @signatures and @pubkeys.
|
||||
*/
|
||||
static gboolean
|
||||
validate_signature (GBytes *data, GVariant *signatures, GPtrArray *pubkeys)
|
||||
{
|
||||
g_assert (data);
|
||||
g_assert (signatures);
|
||||
g_assert (pubkeys);
|
||||
|
||||
for (gsize j = 0; j < pubkeys->len; j++)
|
||||
{
|
||||
GBytes *pubkey = pubkeys->pdata[j];
|
||||
g_assert (pubkey);
|
||||
|
||||
for (gsize i = 0; i < g_variant_n_children (signatures); i++)
|
||||
{
|
||||
g_autoptr (GError) local_error = NULL;
|
||||
g_autoptr (GVariant) child = g_variant_get_child_value (signatures, i);
|
||||
g_autoptr (GBytes) signature = g_variant_get_data_as_bytes (child);
|
||||
bool valid = false;
|
||||
|
||||
if (!otcore_validate_ed25519_signature (data, pubkey, signature, &valid, &local_error))
|
||||
errx (EXIT_FAILURE, "signature verification failed: %s", local_error->message);
|
||||
// At least one valid signature is enough.
|
||||
if (valid)
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Output a friendly message based on an errno for common cases
|
||||
static const char *
|
||||
composefs_error_message (int errsv)
|
||||
{
|
||||
switch (errsv)
|
||||
{
|
||||
case ENOVERITY:
|
||||
return "fsverity not enabled on composefs image";
|
||||
case EWRONGVERITY:
|
||||
return "Wrong fsverity digest in composefs image";
|
||||
case ENOSIGNATURE:
|
||||
return "Missing signature for fsverity in composefs image";
|
||||
default:
|
||||
return strerror (errsv);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
@@ -386,130 +274,9 @@ main (int argc, char *argv[])
|
||||
|
||||
// Tracks if we did successfully enable it at runtime
|
||||
bool using_composefs = false;
|
||||
|
||||
#ifdef HAVE_COMPOSEFS
|
||||
/* We construct the new sysroot in /sysroot.tmp, which is either the composefs
|
||||
mount or a bind mount of the deploy-dir */
|
||||
if (composefs_config->enabled != OT_TRISTATE_NO)
|
||||
{
|
||||
const char *objdirs[] = { "/sysroot/ostree/repo/objects" };
|
||||
g_autofree char *cfs_digest = NULL;
|
||||
struct lcfs_mount_options_s cfs_options = {
|
||||
objdirs,
|
||||
1,
|
||||
};
|
||||
|
||||
cfs_options.flags = 0;
|
||||
cfs_options.image_mountdir = OSTREE_COMPOSEFS_LOWERMNT;
|
||||
if (mkdirat (AT_FDCWD, OSTREE_COMPOSEFS_LOWERMNT, 0700) < 0 && errno != EEXIST)
|
||||
err (EXIT_FAILURE, "Failed to create %s", OSTREE_COMPOSEFS_LOWERMNT);
|
||||
|
||||
g_autofree char *expected_digest = NULL;
|
||||
|
||||
// For now we just stick the transient root on the default /run tmpfs;
|
||||
// however, see
|
||||
// https://github.com/systemd/systemd/blob/604b2001081adcbd64ee1fbe7de7a6d77c5209fe/src/basic/mountpoint-util.h#L36
|
||||
// which bumps up these defaults for the rootfs a bit.
|
||||
g_autofree char *root_upperdir
|
||||
= root_transient ? g_build_filename (OTCORE_RUN_OSTREE_PRIVATE, "root/upper", NULL)
|
||||
: NULL;
|
||||
g_autofree char *root_workdir
|
||||
= root_transient ? g_build_filename (OTCORE_RUN_OSTREE_PRIVATE, "root/work", NULL) : NULL;
|
||||
|
||||
// Propagate these options for transient root, if provided
|
||||
if (root_transient)
|
||||
{
|
||||
if (!glnx_shutil_mkdir_p_at (AT_FDCWD, root_upperdir, 0755, NULL, &error))
|
||||
errx (EXIT_FAILURE, "Failed to create %s: %s", root_upperdir, error->message);
|
||||
if (!glnx_shutil_mkdir_p_at (AT_FDCWD, root_workdir, 0700, NULL, &error))
|
||||
errx (EXIT_FAILURE, "Failed to create %s: %s", root_workdir, error->message);
|
||||
|
||||
cfs_options.workdir = root_workdir;
|
||||
cfs_options.upperdir = root_upperdir;
|
||||
}
|
||||
else
|
||||
{
|
||||
cfs_options.flags = LCFS_MOUNT_FLAGS_READONLY;
|
||||
}
|
||||
|
||||
if (composefs_config->is_signed)
|
||||
{
|
||||
const char *composefs_pubkey = composefs_config->signature_pubkey;
|
||||
g_autoptr (GError) local_error = NULL;
|
||||
g_autoptr (GVariant) commit = NULL;
|
||||
g_autoptr (GVariant) commitmeta = NULL;
|
||||
|
||||
if (!load_commit_for_deploy (root_mountpoint, deploy_path, &commit, &commitmeta,
|
||||
&local_error))
|
||||
errx (EXIT_FAILURE, "Error loading signatures from repo: %s", local_error->message);
|
||||
|
||||
g_autoptr (GVariant) signatures = g_variant_lookup_value (
|
||||
commitmeta, OSTREE_SIGN_METADATA_ED25519_KEY, G_VARIANT_TYPE ("aay"));
|
||||
if (signatures == NULL)
|
||||
errx (EXIT_FAILURE, "Signature validation requested, but no signatures in commit");
|
||||
|
||||
g_autoptr (GBytes) commit_data = g_variant_get_data_as_bytes (commit);
|
||||
if (!validate_signature (commit_data, signatures, composefs_config->pubkeys))
|
||||
errx (EXIT_FAILURE, "No valid signatures found for public key");
|
||||
|
||||
g_print ("composefs+ostree: Validated commit signature using '%s'\n", composefs_pubkey);
|
||||
g_variant_builder_add (&metadata_builder, "{sv}",
|
||||
OTCORE_RUN_BOOTED_KEY_COMPOSEFS_SIGNATURE,
|
||||
g_variant_new_string (composefs_pubkey));
|
||||
|
||||
g_autoptr (GVariant) metadata = g_variant_get_child_value (commit, 0);
|
||||
g_autoptr (GVariant) cfs_digest_v = g_variant_lookup_value (
|
||||
metadata, OSTREE_COMPOSEFS_DIGEST_KEY_V0, G_VARIANT_TYPE_BYTESTRING);
|
||||
if (cfs_digest_v == NULL || g_variant_get_size (cfs_digest_v) != OSTREE_SHA256_DIGEST_LEN)
|
||||
errx (EXIT_FAILURE, "Signature validation requested, but no valid digest in commit");
|
||||
const guint8 *cfs_digest_buf = ot_variant_get_data (cfs_digest_v, &error);
|
||||
if (!cfs_digest_buf)
|
||||
errx (EXIT_FAILURE, "Failed to query digest: %s", error->message);
|
||||
|
||||
expected_digest = g_malloc (OSTREE_SHA256_STRING_LEN + 1);
|
||||
ot_bin2hex (expected_digest, cfs_digest_buf, g_variant_get_size (cfs_digest_v));
|
||||
|
||||
g_assert (composefs_config->require_verity);
|
||||
cfs_options.flags |= LCFS_MOUNT_FLAGS_REQUIRE_VERITY;
|
||||
g_print ("composefs: Verifying digest: %s\n", expected_digest);
|
||||
cfs_options.expected_fsverity_digest = expected_digest;
|
||||
}
|
||||
else if (composefs_config->require_verity)
|
||||
{
|
||||
cfs_options.flags |= LCFS_MOUNT_FLAGS_REQUIRE_VERITY;
|
||||
}
|
||||
|
||||
if (lcfs_mount_image (OSTREE_COMPOSEFS_NAME, TMP_SYSROOT, &cfs_options) == 0)
|
||||
{
|
||||
using_composefs = true;
|
||||
bool using_verity = (cfs_options.flags & LCFS_MOUNT_FLAGS_REQUIRE_VERITY) > 0;
|
||||
g_variant_builder_add (&metadata_builder, "{sv}", OTCORE_RUN_BOOTED_KEY_COMPOSEFS,
|
||||
g_variant_new_boolean (true));
|
||||
g_variant_builder_add (&metadata_builder, "{sv}", OTCORE_RUN_BOOTED_KEY_COMPOSEFS_VERITY,
|
||||
g_variant_new_boolean (using_verity));
|
||||
g_print ("composefs: mounted successfully (verity=%s)\n",
|
||||
using_verity ? "true" : "false");
|
||||
}
|
||||
else
|
||||
{
|
||||
int errsv = errno;
|
||||
g_assert (composefs_config->enabled != OT_TRISTATE_NO);
|
||||
if (composefs_config->enabled == OT_TRISTATE_MAYBE && errsv == ENOENT)
|
||||
{
|
||||
g_print ("composefs: No image present\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *errmsg = composefs_error_message (errsv);
|
||||
errx (EXIT_FAILURE, "composefs: failed to mount: %s", errmsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* if composefs is configured as "maybe", we should continue */
|
||||
if (composefs_config->enabled == OT_TRISTATE_YES)
|
||||
errx (EXIT_FAILURE, "composefs: enabled at runtime, but support is not compiled in");
|
||||
#endif
|
||||
if (!otcore_mount_composefs (composefs_config, &metadata_builder, root_transient, root_mountpoint,
|
||||
deploy_path, TMP_SYSROOT, &using_composefs, &error))
|
||||
errx (EXIT_FAILURE, "Failed to mount composefs: %s", error->message);
|
||||
|
||||
if (!using_composefs)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user