mirror of
https://github.com/ostreedev/ostree.git
synced 2025-03-11 20:58:45 +03:00
composefs: When using signatures, delay application until first boot
We can't safely apply the fs-verity with signature until we have booted with the new initrd, because the public key that matches the signature is loaded from it. So, instead we save the .sig file next to the compoosefs, and on the first boot we detect that it is there, and the composefs file isn't fs-verity, so we apply it. Things get a bit more complex due to having to temporarily make /sysroot read-write for the fsverity operation too.
This commit is contained in:
parent
6d2dc95968
commit
7333803949
@ -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))
|
||||
|
@ -24,12 +24,18 @@
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef HAVE_LINUX_FSVERITY_H
|
||||
#include <linux/fsverity.h>
|
||||
#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_ */
|
||||
|
@ -66,6 +66,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
@ -73,6 +74,10 @@
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* 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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user