Skip to content

Commit

Permalink
WIP: Initial work on user-space signatures
Browse files Browse the repository at this point in the history
  • Loading branch information
alexlarsson committed Jul 6, 2023
1 parent 9104c54 commit bd99da2
Show file tree
Hide file tree
Showing 9 changed files with 154 additions and 5 deletions.
6 changes: 3 additions & 3 deletions Makefile-libostree.am
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,9 @@ endif # USE_GPGME
symbol_files = $(top_srcdir)/src/libostree/libostree-released.sym

# Uncomment this include when adding new development symbols.
#if BUILDOPT_IS_DEVEL_BUILD
#symbol_files += $(top_srcdir)/src/libostree/libostree-devel.sym
#endif
if BUILDOPT_IS_DEVEL_BUILD
symbol_files += $(top_srcdir)/src/libostree/libostree-devel.sym
endif

# http://blog.jgc.org/2007/06/escaping-comma-and-space-in-gnu-make.html
wl_versionscript_arg = -Wl,--version-script=
Expand Down
5 changes: 5 additions & 0 deletions Makefile-switchroot.am
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ ostree_prepare_root_CFLAGS += $(AM_CFLAGS) -Isrc/switchroot -I$(srcdir)/composef
ostree_prepare_root_SOURCES += src/switchroot/ostree-prepare-root.c
ostree_prepare_root_CPPFLAGS += $(OT_INTERNAL_GIO_UNIX_CFLAGS) -I $(srcdir)/libglnx
ostree_prepare_root_LDADD += $(AM_LDFLAGS) $(OT_INTERNAL_GIO_UNIX_LIBS) libglnx.la
if USE_LIBSODIUM
ostree_prepare_root_CFLAGS += $(OT_DEP_LIBSODIUM_CFLAGS)
ostree_prepare_root_LDADD += $(OT_DEP_LIBSODIUM_LIBS)
endif # USE_LIBSODIUM

endif # BUILDOPT_USE_STATIC_COMPILER


Expand Down
5 changes: 5 additions & 0 deletions src/libostree/libostree-devel.sym
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,8 @@ global:
someostree_symbol_deleteme;
} LIBOSTREE_2021.$LASTSTABLE;
*/

LIBOSTREE_2023.6 {
global:
ostree_composefs_sign_metadata;
} LIBOSTREE_2023.4;
48 changes: 48 additions & 0 deletions src/libostree/ostree-repo-composefs.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
#include "ostree-core-private.h"
#include "ostree-repo-file.h"
#include "ostree-repo-private.h"
#include "ostree-sign.h"
#include "ostree-autocleanups.h"

#ifdef HAVE_COMPOSEFS
#include <libcomposefs/lcfs-writer.h>
Expand Down Expand Up @@ -612,3 +614,49 @@ ostree_repo_commit_add_composefs_metadata (OstreeRepo *self, guint format_versio
return composefs_not_supported (error);
#endif
}

/**
* ostree_composefs_sign_metadata:
* @dict: A GVariant builder of type a{sv}
* @secret_key: ed25519 secret key to use
* @cancellable: Cancellable
* @error: Error
*
* After ostree_repo_commit_add_composefs_metadata() has added the
* composefs digest to the metadata dict, this can be called to add
* an ed25519 signature to the digest. This signature will be written
* out during deploy and can be verified at boot.
*/
_OSTREE_PUBLIC
gboolean
ostree_composefs_sign_metadata (GVariantDict *dict, GVariant *secret_key,
GCancellable *cancellable, GError **error)
{
g_autoptr (GVariant) digest = NULL;
g_autoptr (GBytes) digest_bytes = NULL;
g_autoptr (GBytes) signature = NULL;
g_autoptr (OstreeSign) sign = NULL;

digest = g_variant_dict_lookup_value (dict, OSTREE_COMPOSEFS_DIGEST_KEY_V0, G_VARIANT_TYPE_BYTESTRING);
if (digest == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "No composefs digest in metadata to sign");
return FALSE;
}

sign = ostree_sign_get_by_name (OSTREE_SIGN_NAME_ED25519, error);
if (sign == NULL)
return FALSE;

if (!ostree_sign_set_sk (sign, secret_key, error))
return FALSE;

digest_bytes = g_variant_get_data_as_bytes (digest);
if (!ostree_sign_data (sign, digest_bytes, &signature, cancellable, error))
return glnx_prefix_error (error, "Not able to sign the composefs digest");

g_variant_dict_insert_value (dict, OSTREE_COMPOSEFS_SIGN_KEY_V0,
ot_gvariant_new_ay_bytes (signature));

return TRUE;
}
1 change: 1 addition & 0 deletions src/libostree/ostree-repo-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ G_BEGIN_DECLS
#define OSTREE_COMPOSEFS_META_PREFIX "ostree.composefs"
// The fs-verity digest of the composefs, version 0
#define OSTREE_COMPOSEFS_DIGEST_KEY_V0 OSTREE_COMPOSEFS_META_PREFIX ".digest.v0"
#define OSTREE_COMPOSEFS_SIGN_KEY_V0 OSTREE_COMPOSEFS_META_PREFIX ".sign.v0"

#define _OSTREE_INTEGRITY_SECTION "ex-integrity"

Expand Down
3 changes: 3 additions & 0 deletions src/libostree/ostree-repo.h
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,9 @@ _OSTREE_PUBLIC
gboolean ostree_repo_commit_add_composefs_metadata (OstreeRepo *self, guint format_version,
GVariantDict *dict, OstreeRepoFile *repo_root,
GCancellable *cancellable, GError **error);
_OSTREE_PUBLIC
gboolean ostree_composefs_sign_metadata (GVariantDict *dict, GVariant *secret_key,
GCancellable *cancellable, GError **error);

_OSTREE_PUBLIC
gboolean ostree_repo_write_commit (OstreeRepo *self, const char *parent, const char *subject,
Expand Down
14 changes: 14 additions & 0 deletions src/libostree/ostree-sysroot-deploy.c
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,8 @@ checkout_deployment_tree (OstreeSysroot *sysroot, OstreeRepo *repo, OstreeDeploy
g_autoptr (GVariant) metadata = g_variant_get_child_value (commit_variant, 0);
g_autoptr (GVariant) metadata_composefs = g_variant_lookup_value (
metadata, OSTREE_COMPOSEFS_DIGEST_KEY_V0, G_VARIANT_TYPE_BYTESTRING);
g_autoptr (GVariant) metadata_composefs_sig = g_variant_lookup_value (
metadata, OSTREE_COMPOSEFS_SIGN_KEY_V0, G_VARIANT_TYPE_BYTESTRING);

/* Create a composefs image and put in deploy dir as .ostree.cfs */
g_autoptr (OstreeComposefsTarget) target = ostree_composefs_target_new ();
Expand Down Expand Up @@ -697,6 +699,18 @@ checkout_deployment_tree (OstreeSysroot *sysroot, OstreeRepo *repo, OstreeDeploy
error))
return FALSE;

if (metadata_composefs && metadata_composefs_sig)
{
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);

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;
}

/* This is where the erofs image will be temporarily mounted */
g_autofree char *composefs_mnt_path
= g_strdup_printf ("%s/.ostree.mnt", checkout_target_name);
Expand Down
21 changes: 20 additions & 1 deletion src/ostree/ot-builtin-commit.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ static char **opt_key_ids;
static char *opt_sign_name;
static gboolean opt_generate_sizes;
static gboolean opt_composefs_metadata;
static char *opt_composefs_key_path;
static gboolean opt_disable_fsync;
static char *opt_timestamp;

Expand Down Expand Up @@ -164,6 +165,8 @@ static GOptionEntry options[] = {
"Generate size information along with commit metadata", NULL },
{ "generate-composefs-metadata", 0, 0, G_OPTION_ARG_NONE, &opt_composefs_metadata,
"Generate composefs commit metadata", NULL },
{ "sign-composefs", 0, 0, G_OPTION_ARG_STRING, &opt_composefs_key_path,
"Sign the composefs digest. Implies --generate-composefs-metadata", "PATH" },
{ "disable-fsync", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &opt_disable_fsync,
"Do not invoke fsync()", NULL },
{ "fsync", 0, 0, G_OPTION_ARG_CALLBACK, parse_fsync_cb, "Specify how to invoke fsync()",
Expand Down Expand Up @@ -875,7 +878,7 @@ ostree_builtin_commit (int argc, char **argv, OstreeCommandInvocation *invocatio
metadata = g_variant_ref_sink (g_variant_dict_end (&bootmeta));
}

if (opt_composefs_metadata)
if (opt_composefs_metadata || opt_composefs_key_path)
{
g_autoptr (GVariant) old_metadata = g_steal_pointer (&metadata);
g_auto (GVariantDict) newmeta;
Expand All @@ -884,6 +887,22 @@ ostree_builtin_commit (int argc, char **argv, OstreeCommandInvocation *invocatio
repo, 0, &newmeta, OSTREE_REPO_FILE (root), cancellable, error))
goto out;

if (opt_composefs_key_path)
{
g_autofree char *key_data;
gsize key_size;

if (!g_file_get_contents (opt_composefs_key_path, &key_data, &key_size, error))
{
glnx_prefix_error (error, "Failed to open '%s'", opt_composefs_key_path);
goto out;
}

g_autoptr (GVariant) secret_key = ot_gvariant_new_bytearray ((guchar *)key_data, key_size);
if (!ostree_composefs_sign_metadata (&newmeta,secret_key, cancellable, error))
goto out;
}

metadata = g_variant_ref_sink (g_variant_dict_end (&newmeta));
}

Expand Down
56 changes: 55 additions & 1 deletion src/switchroot/ostree-prepare-root.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,16 @@
#include <sys/types.h>
#include <unistd.h>

#ifdef HAVE_LIBSODIUM
#include <sodium.h>
#endif

/* 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)

#define SHA256_DIGEST_LEN (32)

// The name of the composefs metadata root
#define OSTREE_COMPOSEFS_NAME ".ostree.cfs"

Expand Down Expand Up @@ -167,6 +173,8 @@ main (int argc, char *argv[])

const char *root_arg = NULL;
bool we_mounted_proc = false;
g_autoptr (GError) error = NULL;

if (argc < 2)
err (EXIT_FAILURE, "usage: ostree-prepare-root SYSROOT");
root_arg = argv[1];
Expand Down Expand Up @@ -270,9 +278,54 @@ main (int argc, char *argv[])
objdirs,
1,
};
glnx_autofd int cfs_fd = -1;

cfs_fd = open (OSTREE_COMPOSEFS_NAME, O_RDONLY | O_CLOEXEC);
if (cfs_fd == -1)
{
#ifdef USE_LIBSYSTEMD
sd_journal_send ("MESSAGE=Failed to open '%s': %s", OSTREE_COMPOSEFS_NAME, strerror (errno), NULL);
#endif
goto nocomposefs;
}

if (composefs_mode == OSTREE_COMPOSEFS_MODE_SIGNED)
errx (EXIT_FAILURE, "composefs signature not supported");
{
const char *pubkey_path = "/etc/ostree/composefs.pub";
char buf[sizeof (struct fsverity_digest) + SHA256_DIGEST_LEN];
struct fsverity_digest *d = (struct fsverity_digest *)&buf;
g_autofree char *pubkey;
gsize pubkey_size;
g_autofree char *sig;
gsize sig_size;

if (!g_file_get_contents (OSTREE_COMPOSEFS_NAME ".sig", &sig, &sig_size, &error))
err (EXIT_FAILURE, "failed to load '%s': %s", OSTREE_COMPOSEFS_NAME ".sig", error->message);

if (sig_size != crypto_core_ed25519_HASHBYTES)
err (EXIT_FAILURE, "Invalid signature file '%s'", OSTREE_COMPOSEFS_NAME ".sig");

if (!g_file_get_contents (pubkey_path, &pubkey, &pubkey_size, &error))
err (EXIT_FAILURE, "failed to load '%s': %s", pubkey_path, error->message);

d->digest_size = SHA256_DIGEST_LEN;
if (ioctl (cfs_fd, FS_IOC_MEASURE_VERITY, d) < 0)
err (EXIT_FAILURE, "Failed to get fs-verity digest for '%s'", OSTREE_COMPOSEFS_NAME);

if (d->digest_size != SHA256_DIGEST_LEN ||
d->digest_algorithm != FS_VERITY_HASH_ALG_SHA256)
err (EXIT_FAILURE, "Invalid fs-verity digest type for '%s'", OSTREE_COMPOSEFS_NAME);

#ifdef HAVE_LIBSODIUM
if (sodium_init () < 0)
err (EXIT_FAILURE, "Failed to init libsodiume");

if (crypto_sign_verify_detached ((unsigned char *)sig, d->digest, d->digest_size, (unsigned char *)pubkey) != 0)
err (EXIT_FAILURE, "Mismatched signature for composefs image");
#else
err (EXIT_FAILURE, "libsodiume missin, signatures not supported")
#endif
}

cfs_options.flags = LCFS_MOUNT_FLAGS_READONLY;

Expand Down Expand Up @@ -318,6 +371,7 @@ main (int argc, char *argv[])
sd_journal_send ("MESSAGE=Mounting composefs image failed: %s", strerror (errno), NULL);
#endif
}
nocomposefs:
#else
err (EXIT_FAILURE, "Composefs not supported");
#endif
Expand Down

0 comments on commit bd99da2

Please sign in to comment.