Merge pull request #1959 from cgwalters/ostree-verity
Initial fs-verity support
This commit is contained in:
commit
96fb1decf4
|
|
@ -246,6 +246,10 @@ LIBARCHIVE_DEPENDENCY="libarchive >= 2.8.0"
|
|||
# What's in RHEL7.2.
|
||||
FUSE_DEPENDENCY="fuse >= 2.9.2"
|
||||
|
||||
AC_CHECK_HEADERS([linux/fsverity.h])
|
||||
AS_IF([test x$ac_cv_header_linux_fsverity_h = xyes ],
|
||||
[OSTREE_FEATURES="$OSTREE_FEATURES ex-fsverity"])
|
||||
|
||||
# check for gtk-doc
|
||||
m4_ifdef([GTK_DOC_CHECK], [
|
||||
GTK_DOC_CHECK([1.15], [--flavour no-tmpl])
|
||||
|
|
@ -618,6 +622,7 @@ echo "
|
|||
HTTP backend: $fetcher_backend
|
||||
\"ostree trivial-httpd\": $enable_trivial_httpd_cmdline
|
||||
SELinux: $with_selinux
|
||||
fs-verity: $ac_cv_header_linux_fsverity_h
|
||||
cryptographic checksums: $with_crypto
|
||||
systemd: $with_libsystemd
|
||||
libmount: $with_libmount
|
||||
|
|
|
|||
|
|
@ -32,6 +32,10 @@
|
|||
#include <glib/gprintf.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/fs.h>
|
||||
#include <ext2fs/ext2_fs.h>
|
||||
#ifdef HAVE_LINUX_FSVERITY_H
|
||||
#include <linux/fsverity.h>
|
||||
#endif
|
||||
|
||||
#include "otutil.h"
|
||||
#include "ostree.h"
|
||||
|
|
@ -168,6 +172,113 @@ ot_security_smack_reset_fd (int fd)
|
|||
#endif
|
||||
}
|
||||
|
||||
/* Wrapper around the fsverity ioctl, compressing the result to
|
||||
* "success, unsupported or error". This is used for /boot where
|
||||
* we enable verity if supported.
|
||||
* */
|
||||
gboolean
|
||||
_ostree_tmpf_fsverity_core (GLnxTmpfile *tmpf,
|
||||
_OstreeFeatureSupport fsverity_requested,
|
||||
gboolean *supported,
|
||||
GError **error)
|
||||
{
|
||||
/* Set this by default to simplify the code below */
|
||||
if (supported)
|
||||
*supported = FALSE;
|
||||
|
||||
if (fsverity_requested == _OSTREE_FEATURE_NO)
|
||||
return TRUE;
|
||||
|
||||
#ifdef HAVE_LINUX_FSVERITY_H
|
||||
GLNX_AUTO_PREFIX_ERROR ("fsverity", error);
|
||||
|
||||
/* fs-verity requires a read-only file descriptor */
|
||||
if (!glnx_tmpfile_reopen_rdonly (tmpf, error))
|
||||
return FALSE;
|
||||
|
||||
struct fsverity_enable_arg arg = { 0, };
|
||||
arg.version = 1;
|
||||
arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; /* TODO configurable? */
|
||||
arg.block_size = 4096; /* FIXME query */
|
||||
arg.salt_size = 0; /* TODO store salt in ostree repo config */
|
||||
arg.salt_ptr = 0;
|
||||
arg.sig_size = 0; /* We don't currently expect use of in-kernel signature verification */
|
||||
arg.sig_ptr = 0;
|
||||
|
||||
if (ioctl (tmpf->fd, FS_IOC_ENABLE_VERITY, &arg) < 0)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case ENOTTY:
|
||||
case EOPNOTSUPP:
|
||||
return TRUE;
|
||||
default:
|
||||
return glnx_throw_errno_prefix (error, "ioctl(FS_IOC_ENABLE_VERITY)");
|
||||
}
|
||||
}
|
||||
|
||||
if (supported)
|
||||
*supported = TRUE;
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Enable verity on a file, respecting the "wanted" and "supported" states.
|
||||
* The main idea here is to optimize out pointlessly calling the ioctl()
|
||||
* over and over in cases where it's not supported for the repo's filesystem,
|
||||
* as well as to support "opportunistic" use (requested and if filesystem supports).
|
||||
* */
|
||||
gboolean
|
||||
_ostree_tmpf_fsverity (OstreeRepo *self,
|
||||
GLnxTmpfile *tmpf,
|
||||
GError **error)
|
||||
{
|
||||
#ifdef HAVE_LINUX_FSVERITY_H
|
||||
g_mutex_lock (&self->txn_lock);
|
||||
_OstreeFeatureSupport fsverity_wanted = self->fs_verity_wanted;
|
||||
_OstreeFeatureSupport fsverity_supported = self->fs_verity_supported;
|
||||
g_mutex_unlock (&self->txn_lock);
|
||||
|
||||
switch (fsverity_wanted)
|
||||
{
|
||||
case _OSTREE_FEATURE_YES:
|
||||
{
|
||||
if (fsverity_supported == _OSTREE_FEATURE_NO)
|
||||
return glnx_throw (error, "fsverity required but filesystem does not support it");
|
||||
}
|
||||
break;
|
||||
case _OSTREE_FEATURE_MAYBE:
|
||||
break;
|
||||
case _OSTREE_FEATURE_NO:
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean supported = FALSE;
|
||||
if (!_ostree_tmpf_fsverity_core (tmpf, fsverity_wanted, &supported, error))
|
||||
return FALSE;
|
||||
|
||||
if (!supported)
|
||||
{
|
||||
if (G_UNLIKELY (fsverity_wanted == _OSTREE_FEATURE_YES))
|
||||
return glnx_throw (error, "fsverity required but filesystem does not support it");
|
||||
|
||||
/* If we got here, we must be trying "opportunistic" use of fs-verity */
|
||||
g_assert_cmpint (fsverity_wanted, ==, _OSTREE_FEATURE_MAYBE);
|
||||
g_mutex_lock (&self->txn_lock);
|
||||
self->fs_verity_supported = _OSTREE_FEATURE_NO;
|
||||
g_mutex_unlock (&self->txn_lock);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
g_mutex_lock (&self->txn_lock);
|
||||
self->fs_verity_supported = _OSTREE_FEATURE_YES;
|
||||
g_mutex_unlock (&self->txn_lock);
|
||||
#else
|
||||
g_assert_cmpint (self->fs_verity_wanted, !=, _OSTREE_FEATURE_YES);
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Given an O_TMPFILE regular file, link it into place. */
|
||||
gboolean
|
||||
_ostree_repo_commit_tmpf_final (OstreeRepo *self,
|
||||
|
|
@ -185,6 +296,9 @@ _ostree_repo_commit_tmpf_final (OstreeRepo *self,
|
|||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (!_ostree_tmpf_fsverity (self, tmpf, error))
|
||||
return FALSE;
|
||||
|
||||
if (!glnx_link_tmpfile_at (tmpf, GLNX_LINK_TMPFILE_NOREPLACE_IGNORE_EXIST,
|
||||
dest_dfd, tmpbuf, error))
|
||||
return FALSE;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <sys/statvfs.h>
|
||||
#include "config.h"
|
||||
#include "otutil.h"
|
||||
#include "ostree-ref.h"
|
||||
#include "ostree-repo.h"
|
||||
|
|
@ -95,6 +96,12 @@ typedef struct {
|
|||
fsblkcnt_t max_blocks;
|
||||
} OstreeRepoTxn;
|
||||
|
||||
typedef enum {
|
||||
_OSTREE_FEATURE_NO,
|
||||
_OSTREE_FEATURE_MAYBE,
|
||||
_OSTREE_FEATURE_YES,
|
||||
} _OstreeFeatureSupport;
|
||||
|
||||
/**
|
||||
* OstreeRepo:
|
||||
*
|
||||
|
|
@ -125,6 +132,8 @@ struct OstreeRepo {
|
|||
GMutex txn_lock;
|
||||
OstreeRepoTxn txn;
|
||||
gboolean txn_locked;
|
||||
_OstreeFeatureSupport fs_verity_wanted;
|
||||
_OstreeFeatureSupport fs_verity_supported;
|
||||
|
||||
GMutex cache_lock;
|
||||
guint dirmeta_cache_refcount;
|
||||
|
|
@ -481,4 +490,15 @@ OstreeRepoAutoLock * _ostree_repo_auto_lock_push (OstreeRepo *self,
|
|||
void _ostree_repo_auto_lock_cleanup (OstreeRepoAutoLock *lock);
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoAutoLock, _ostree_repo_auto_lock_cleanup)
|
||||
|
||||
gboolean
|
||||
_ostree_tmpf_fsverity_core (GLnxTmpfile *tmpf,
|
||||
_OstreeFeatureSupport fsverity_requested,
|
||||
gboolean *supported,
|
||||
GError **error);
|
||||
|
||||
gboolean
|
||||
_ostree_tmpf_fsverity (OstreeRepo *self,
|
||||
GLnxTmpfile *tmpf,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include "libglnx.h"
|
||||
#include "otutil.h"
|
||||
#include <glnx-console.h>
|
||||
#include <linux/magic.h>
|
||||
|
||||
#include "ostree-core-private.h"
|
||||
#include "ostree-sysroot-private.h"
|
||||
|
|
@ -47,6 +48,7 @@
|
|||
#include <glib/gstdio.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/statfs.h>
|
||||
|
||||
#define REPO_LOCK_DISABLED (-2)
|
||||
#define REPO_LOCK_BLOCKING (-1)
|
||||
|
|
@ -3033,6 +3035,34 @@ reload_core_config (OstreeRepo *self,
|
|||
}
|
||||
}
|
||||
|
||||
/* Currently experimental */
|
||||
static const char fsverity_key[] = "ex-fsverity";
|
||||
self->fs_verity_wanted = _OSTREE_FEATURE_NO;
|
||||
#ifdef HAVE_LINUX_FSVERITY_H
|
||||
self->fs_verity_supported = _OSTREE_FEATURE_MAYBE;
|
||||
#else
|
||||
self->fs_verity_supported = _OSTREE_FEATURE_NO;
|
||||
#endif
|
||||
gboolean fsverity_required = FALSE;
|
||||
if (!ot_keyfile_get_boolean_with_default (self->config, fsverity_key, "required",
|
||||
FALSE, &fsverity_required, error))
|
||||
return FALSE;
|
||||
if (fsverity_required)
|
||||
{
|
||||
self->fs_verity_wanted = _OSTREE_FEATURE_YES;
|
||||
if (self->fs_verity_supported == _OSTREE_FEATURE_NO)
|
||||
return glnx_throw (error, "fsverity required, but libostree compiled without support");
|
||||
}
|
||||
else
|
||||
{
|
||||
gboolean fsverity_opportunistic = FALSE;
|
||||
if (!ot_keyfile_get_boolean_with_default (self->config, fsverity_key, "opportunistic",
|
||||
FALSE, &fsverity_opportunistic, error))
|
||||
return FALSE;
|
||||
if (fsverity_opportunistic)
|
||||
self->fs_verity_wanted = _OSTREE_FEATURE_MAYBE;
|
||||
}
|
||||
|
||||
{
|
||||
g_clear_pointer (&self->collection_id, g_free);
|
||||
if (!ot_keyfile_get_value_with_default (self->config, "core", "collection-id",
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
|
||||
#include "otutil.h"
|
||||
#include "ostree.h"
|
||||
#include "ostree-repo-private.h"
|
||||
#include "ostree-sysroot-private.h"
|
||||
#include "ostree-sepolicy-private.h"
|
||||
#include "ostree-bootloader-zipl.h"
|
||||
|
|
@ -104,7 +105,8 @@ sysroot_flags_to_copy_flags (GLnxFileCopyFlags defaults,
|
|||
* hardlink if we're on the same partition.
|
||||
*/
|
||||
static gboolean
|
||||
install_into_boot (OstreeSePolicy *sepolicy,
|
||||
install_into_boot (OstreeRepo *repo,
|
||||
OstreeSePolicy *sepolicy,
|
||||
int src_dfd,
|
||||
const char *src_subpath,
|
||||
int dest_dfd,
|
||||
|
|
@ -113,10 +115,20 @@ install_into_boot (OstreeSePolicy *sepolicy,
|
|||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
if (linkat (src_dfd, src_subpath, dest_dfd, dest_subpath, 0) != 0)
|
||||
{
|
||||
if (G_IN_SET (errno, EMLINK, EXDEV))
|
||||
{
|
||||
if (linkat (src_dfd, src_subpath, dest_dfd, dest_subpath, 0) == 0)
|
||||
return TRUE; /* Note early return */
|
||||
if (!G_IN_SET (errno, EMLINK, EXDEV))
|
||||
return glnx_throw_errno_prefix (error, "linkat(%s)", dest_subpath);
|
||||
|
||||
/* Otherwise, copy */
|
||||
struct stat src_stbuf;
|
||||
if (!glnx_fstatat (src_dfd, src_subpath, &src_stbuf, AT_SYMLINK_NOFOLLOW, error))
|
||||
return FALSE;
|
||||
|
||||
glnx_autofd int src_fd = -1;
|
||||
if (!glnx_openat_rdonly (src_dfd, src_subpath, FALSE, &src_fd, error))
|
||||
return FALSE;
|
||||
|
||||
/* Be sure we relabel when copying the kernel, as in current
|
||||
* e.g. Fedora it might be labeled module_object_t or usr_t,
|
||||
* but policy may not allow other processes to read from that
|
||||
|
|
@ -132,13 +144,38 @@ install_into_boot (OstreeSePolicy *sepolicy,
|
|||
boot_path, S_IFREG | 0644,
|
||||
error))
|
||||
return FALSE;
|
||||
return glnx_file_copy_at (src_dfd, src_subpath, NULL, dest_dfd, dest_subpath,
|
||||
GLNX_FILE_COPY_NOXATTRS | GLNX_FILE_COPY_DATASYNC,
|
||||
cancellable, error);
|
||||
}
|
||||
else
|
||||
return glnx_throw_errno_prefix (error, "linkat(%s)", dest_subpath);
|
||||
}
|
||||
|
||||
g_auto(GLnxTmpfile) tmp_dest = { 0, };
|
||||
if (!glnx_open_tmpfile_linkable_at (dest_dfd, ".", O_WRONLY | O_CLOEXEC,
|
||||
&tmp_dest, error))
|
||||
return FALSE;
|
||||
|
||||
if (glnx_regfile_copy_bytes (src_fd, tmp_dest.fd, (off_t) -1) < 0)
|
||||
return glnx_throw_errno_prefix (error, "regfile copy");
|
||||
|
||||
/* Kernel data should always be root-owned */
|
||||
if (fchown (tmp_dest.fd, src_stbuf.st_uid, src_stbuf.st_gid) != 0)
|
||||
return glnx_throw_errno_prefix (error, "fchown");
|
||||
|
||||
if (fchmod (tmp_dest.fd, src_stbuf.st_mode & 07777) != 0)
|
||||
return glnx_throw_errno_prefix (error, "fchmod");
|
||||
|
||||
if (fdatasync (tmp_dest.fd) < 0)
|
||||
return glnx_throw_errno_prefix (error, "fdatasync");
|
||||
|
||||
/* Today we don't have a config flag to *require* verity on /boot,
|
||||
* and at least for Fedora CoreOS we're not likely to do fsverity on
|
||||
* /boot soon due to wanting to support mounting it from old Linux
|
||||
* kernels. So change "required" to "maybe".
|
||||
*/
|
||||
_OstreeFeatureSupport boot_verity = _OSTREE_FEATURE_NO;
|
||||
if (repo->fs_verity_wanted != _OSTREE_FEATURE_NO)
|
||||
boot_verity = _OSTREE_FEATURE_MAYBE;
|
||||
if (!_ostree_tmpf_fsverity_core (&tmp_dest, boot_verity, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
if (!glnx_link_tmpfile_at (&tmp_dest, GLNX_LINK_TMPFILE_NOREPLACE, dest_dfd, dest_subpath, error))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
|
@ -1666,7 +1703,7 @@ install_deployment_kernel (OstreeSysroot *sysroot,
|
|||
return FALSE;
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
if (!install_into_boot (sepolicy, kernel_layout->boot_dfd, kernel_layout->kernel_srcpath,
|
||||
if (!install_into_boot (repo, sepolicy, kernel_layout->boot_dfd, kernel_layout->kernel_srcpath,
|
||||
bootcsum_dfd, kernel_layout->kernel_namever,
|
||||
sysroot->debug_flags,
|
||||
cancellable, error))
|
||||
|
|
@ -1683,7 +1720,7 @@ install_deployment_kernel (OstreeSysroot *sysroot,
|
|||
return FALSE;
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
if (!install_into_boot (sepolicy, kernel_layout->boot_dfd, kernel_layout->initramfs_srcpath,
|
||||
if (!install_into_boot (repo, sepolicy, kernel_layout->boot_dfd, kernel_layout->initramfs_srcpath,
|
||||
bootcsum_dfd, kernel_layout->initramfs_namever,
|
||||
sysroot->debug_flags,
|
||||
cancellable, error))
|
||||
|
|
@ -1698,7 +1735,7 @@ install_deployment_kernel (OstreeSysroot *sysroot,
|
|||
return FALSE;
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
if (!install_into_boot (sepolicy, kernel_layout->boot_dfd, kernel_layout->devicetree_srcpath,
|
||||
if (!install_into_boot (repo, sepolicy, kernel_layout->boot_dfd, kernel_layout->devicetree_srcpath,
|
||||
bootcsum_dfd, kernel_layout->devicetree_namever,
|
||||
sysroot->debug_flags,
|
||||
cancellable, error))
|
||||
|
|
@ -1712,7 +1749,7 @@ install_deployment_kernel (OstreeSysroot *sysroot,
|
|||
return FALSE;
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
if (!install_into_boot (sepolicy, kernel_layout->boot_dfd, kernel_layout->kernel_hmac_srcpath,
|
||||
if (!install_into_boot (repo, sepolicy, kernel_layout->boot_dfd, kernel_layout->kernel_hmac_srcpath,
|
||||
bootcsum_dfd, kernel_layout->kernel_hmac_namever,
|
||||
sysroot->debug_flags,
|
||||
cancellable, error))
|
||||
|
|
|
|||
Loading…
Reference in New Issue