/* * Copyright (C) Red Hat, Inc. * * SPDX-License-Identifier: LGPL-2.0+ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "config.h" #include #include "ostree-core-private.h" #include "ostree-repo-private.h" #include "otutil.h" #include "ot-fs-utils.h" #ifdef HAVE_LINUX_FSVERITY_H #include #endif gboolean _ostree_repo_parse_fsverity_config (OstreeRepo *self, GError **error) { /* 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; } return TRUE; } /* 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; }