malmond / rpms / rpm

Forked from rpms/rpm 4 years ago
Clone

Blame SOURCES/0028-plugins-fsverity-Install-fsverity-signatures.patch

657fb1
From 77e0c68c383904c390e6d99bc747dcfc20a1521c Mon Sep 17 00:00:00 2001
657fb1
From: Jes Sorensen <jsorensen@fb.com>
657fb1
Date: Mon, 20 Apr 2020 11:11:25 -0400
657fb1
Subject: [PATCH 28/33] plugins/fsverity: Install fsverity signatures
657fb1
657fb1
This plugin installs fsverity signatures for regular files, when a signature
657fb1
is found in the RPM. It tries to enable them unconditionally, but fails
657fb1
gracefully if fsverity isn't supported or enabled.
657fb1
657fb1
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
657fb1
---
657fb1
 configure.ac        |   5 ++
657fb1
 macros.in           |   4 ++
657fb1
 plugins/Makefile.am |   6 ++
657fb1
 plugins/fsverity.c  | 177 ++++++++++++++++++++++++++++++++++++++++++++++++++++
657fb1
 4 files changed, 192 insertions(+)
657fb1
 create mode 100644 plugins/fsverity.c
657fb1
657fb1
diff --git a/configure.ac b/configure.ac
657fb1
index 699d8de13..242cb8804 100644
657fb1
--- a/configure.ac
657fb1
+++ b/configure.ac
657fb1
@@ -1041,6 +1041,11 @@ AS_IF([test "$enable_plugins" != no],[
657fb1
 ])
657fb1
 AM_CONDITIONAL(IMA, [test "x$ac_cv_func_lsetxattr" = xyes])
657fb1
 
657fb1
+AS_IF([test "$enable_plugins" != no],[
657fb1
+AC_CHECK_HEADERS([linux/fsverity.h],[FSVERITY_IOCTL="yes"])
657fb1
+])
657fb1
+AM_CONDITIONAL(FSVERITY_IOCTL,[test "x$FSVERITY_IOCTL" = xyes])
657fb1
+
657fb1
 user_with_uid0=$(awk -F: '$3==0 {print $1;exit}' /etc/passwd)
657fb1
 group_with_gid0=$(awk -F: '$3==0 {print $1;exit}' /etc/group)
657fb1
 AC_DEFINE_UNQUOTED([UID_0_USER],["$user_with_uid0"],[Get the user name having userid 0])
657fb1
diff --git a/macros.in b/macros.in
657fb1
index 8dcb26a9b..317f91d96 100644
657fb1
--- a/macros.in
657fb1
+++ b/macros.in
657fb1
@@ -767,6 +767,9 @@ package or when debugging this package.\
657fb1
 # performance for rotational disks)
657fb1
 #%_flush_io	0
657fb1
 
657fb1
+# Set to 1 to have fsverity signatures written for %config files.
657fb1
+#%_fsverity_sign_config_files	0
657fb1
+
657fb1
 #
657fb1
 # Default output format string for rpm -qa
657fb1
 #
657fb1
@@ -1184,6 +1187,7 @@ package or when debugging this package.\
657fb1
 %__transaction_selinux		%{__plugindir}/selinux.so
657fb1
 %__transaction_syslog		%{__plugindir}/syslog.so
657fb1
 %__transaction_ima		%{__plugindir}/ima.so
657fb1
+%__transaction_fsverity		%{__plugindir}/fsverity.so
657fb1
 %__transaction_prioreset	%{__plugindir}/prioreset.so
657fb1
 
657fb1
 #------------------------------------------------------------------------------
657fb1
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
657fb1
index ab4eee34f..22285be97 100644
657fb1
--- a/plugins/Makefile.am
657fb1
+++ b/plugins/Makefile.am
657fb1
@@ -42,3 +42,9 @@ ima_la_sources = ima.c
657fb1
 ima_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la
657fb1
 plugins_LTLIBRARIES += ima.la
657fb1
 endif
657fb1
+
657fb1
+if FSVERITY_IOCTL
657fb1
+fsverity_la_sources = fsverity.c
657fb1
+fsverity_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la
657fb1
+plugins_LTLIBRARIES += fsverity.la
657fb1
+endif
657fb1
diff --git a/plugins/fsverity.c b/plugins/fsverity.c
657fb1
new file mode 100644
657fb1
index 000000000..15ddcf33e
657fb1
--- /dev/null
657fb1
+++ b/plugins/fsverity.c
657fb1
@@ -0,0 +1,177 @@
657fb1
+/**
657fb1
+ * Copyright (C) 2020 Facebook
657fb1
+ *
657fb1
+ * Author: Jes Sorensen <jsorensen@fb.com>
657fb1
+ */
657fb1
+
657fb1
+#include "system.h"
657fb1
+
657fb1
+#include <errno.h>
657fb1
+#include <fcntl.h>
657fb1
+#include <sys/ioctl.h>
657fb1
+#include <linux/fsverity.h>
657fb1
+
657fb1
+#include <rpm/rpmfi.h>
657fb1
+#include <rpm/rpmte.h>
657fb1
+#include <rpm/rpmfiles.h>
657fb1
+#include <rpm/rpmtypes.h>
657fb1
+#include <rpm/rpmlog.h>
657fb1
+#include <rpmio/rpmstring.h>
657fb1
+#include <rpmio/rpmmacro.h>
657fb1
+
657fb1
+#include "lib/rpmfs.h"
657fb1
+#include "lib/rpmplugin.h"
657fb1
+#include "lib/rpmte_internal.h"
657fb1
+
657fb1
+#include "sign/rpmsignverity.h"
657fb1
+
657fb1
+static int sign_config_files = 0;
657fb1
+
657fb1
+/*
657fb1
+ * This unconditionally tries to apply the fsverity signature to a file,
657fb1
+ * but fails gracefully if the file system doesn't support it or the
657fb1
+ * verity feature flag isn't enabled in the file system (ext4).
657fb1
+ */
657fb1
+static rpmRC fsverity_fsm_file_prepare(rpmPlugin plugin, rpmfi fi,
657fb1
+				       const char *path, const char *dest,
657fb1
+				       mode_t file_mode, rpmFsmOp op)
657fb1
+{
657fb1
+    struct fsverity_enable_arg arg;
657fb1
+    const unsigned char * signature = NULL;
657fb1
+    size_t len;
657fb1
+    int rc = RPMRC_OK;
657fb1
+    int fd;
657fb1
+    rpmFileAction action = XFO_ACTION(op);
657fb1
+    char *buffer;
657fb1
+
657fb1
+    /* Ignore skipped files and unowned directories */
657fb1
+    if (XFA_SKIPPING(action) || (op & FAF_UNOWNED)) {
657fb1
+	rpmlog(RPMLOG_DEBUG, "fsverity skipping early: path %s dest %s\n",
657fb1
+	       path, dest);
657fb1
+	goto exit;
657fb1
+    }
657fb1
+
657fb1
+    /*
657fb1
+     * Do not install signatures for config files unless the
657fb1
+     * user explicitly asks for it.
657fb1
+     */
657fb1
+    if (rpmfiFFlags(fi) & RPMFILE_CONFIG) {
657fb1
+	if (!(rpmfiFMode(fi) & (S_IXUSR|S_IXGRP|S_IXOTH)) &&
657fb1
+	    !sign_config_files) {
657fb1
+	    rpmlog(RPMLOG_DEBUG, "fsverity skipping: path %s dest %s\n",
657fb1
+		   path, dest);
657fb1
+
657fb1
+	    goto exit;
657fb1
+	}
657fb1
+    }
657fb1
+
657fb1
+    /*
657fb1
+     * Right now fsverity doesn't deal with symlinks or directories, so do
657fb1
+     * not try to install signatures for non regular files.
657fb1
+     */
657fb1
+    if (!S_ISREG(rpmfiFMode(fi))) {
657fb1
+	rpmlog(RPMLOG_DEBUG, "fsverity skipping non regular: path %s dest %s\n",
657fb1
+	       path, dest);
657fb1
+	goto exit;
657fb1
+    }
657fb1
+
657fb1
+    signature = rpmfiVSignature(fi, &len;;
657fb1
+    if (!signature || !len) {
657fb1
+	rpmlog(RPMLOG_DEBUG, "fsverity no signature for: path %s dest %s\n",
657fb1
+	       path, dest);
657fb1
+	goto exit;
657fb1
+    }
657fb1
+
657fb1
+    memset(&arg, 0, sizeof(arg));
657fb1
+    arg.version = 1;
657fb1
+    arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256;
657fb1
+    arg.block_size = RPM_FSVERITY_BLKSZ;
657fb1
+    arg.sig_ptr = (uintptr_t)signature;
657fb1
+    arg.sig_size = len;
657fb1
+
657fb1
+    buffer = pgpHexStr(signature, arg.sig_size);
657fb1
+    rpmlog(RPMLOG_DEBUG, "applying signature: %s\n", buffer);
657fb1
+    free(buffer);
657fb1
+
657fb1
+    fd = open(path, O_RDONLY);
657fb1
+    if (fd < 0) {
657fb1
+	rpmlog(RPMLOG_ERR, "failed to open path %s\n", path);
657fb1
+	goto exit;
657fb1
+    }
657fb1
+
657fb1
+    /*
657fb1
+     * Enable fsverity on the file.
657fb1
+     * fsverity not supported by file system (ENOTTY) and fsverity not
657fb1
+     * enabled on file system are expected and not considered
657fb1
+     * errors. Every other non-zero error code will result in the
657fb1
+     * installation failing.
657fb1
+     */
657fb1
+    if (ioctl(fd, FS_IOC_ENABLE_VERITY, &arg) != 0) {
657fb1
+	switch(errno) {
657fb1
+	case EBADMSG:
657fb1
+	    rpmlog(RPMLOG_DEBUG, "invalid or malformed fsverity signature for %s\n", path);
657fb1
+	    rc = RPMRC_FAIL;
657fb1
+	    break;
657fb1
+	case EEXIST:
657fb1
+	    rpmlog(RPMLOG_DEBUG, "fsverity signature already enabled %s\n",
657fb1
+		   path);
657fb1
+	    rc = RPMRC_FAIL;
657fb1
+	    break;
657fb1
+	case EINVAL:
657fb1
+	    rpmlog(RPMLOG_DEBUG, "invalid arguments for ioctl %s\n", path);
657fb1
+	    rc = RPMRC_FAIL;
657fb1
+	    break;
657fb1
+	case EKEYREJECTED:
657fb1
+	    rpmlog(RPMLOG_DEBUG, "signature doesn't match file %s\n", path);
657fb1
+	    rc = RPMRC_FAIL;
657fb1
+	    break;
657fb1
+	case EMSGSIZE:
657fb1
+	    rpmlog(RPMLOG_DEBUG, "invalid signature size for %s\n", path);
657fb1
+	    rc = RPMRC_FAIL;
657fb1
+	    break;
657fb1
+	case ENOPKG:
657fb1
+	    rpmlog(RPMLOG_DEBUG, "unsupported signature algoritm (%i) for %s\n",
657fb1
+		   arg.hash_algorithm, path);
657fb1
+	    rc = RPMRC_FAIL;
657fb1
+	    break;
657fb1
+	case ETXTBSY:
657fb1
+	    rpmlog(RPMLOG_DEBUG, "file is open by other process %s\n",
657fb1
+		   path);
657fb1
+	    rc = RPMRC_FAIL;
657fb1
+	    break;
657fb1
+	case ENOTTY:
657fb1
+	    rpmlog(RPMLOG_DEBUG, "fsverity not supported by file system for %s\n",
657fb1
+		   path);
657fb1
+	    break;
657fb1
+	case EOPNOTSUPP:
657fb1
+	    rpmlog(RPMLOG_DEBUG, "fsverity not enabled on file system for %s\n",
657fb1
+		   path);
657fb1
+	    break;
657fb1
+	default:
657fb1
+	    rpmlog(RPMLOG_DEBUG, "failed to enable verity (errno %i) for %s\n",
657fb1
+		   errno, path);
657fb1
+	    rc = RPMRC_FAIL;
657fb1
+	    break;
657fb1
+	}
657fb1
+    }
657fb1
+
657fb1
+    rpmlog(RPMLOG_DEBUG, "fsverity enabled signature for: path %s dest %s\n",
657fb1
+	   path, dest);
657fb1
+    close(fd);
657fb1
+exit:
657fb1
+    return rc;
657fb1
+}
657fb1
+
657fb1
+static rpmRC fsverity_init(rpmPlugin plugin, rpmts ts)
657fb1
+{
657fb1
+    sign_config_files = rpmExpandNumeric("%{?_fsverity_sign_config_files}");
657fb1
+
657fb1
+    rpmlog(RPMLOG_DEBUG, "fsverity_init\n");
657fb1
+
657fb1
+    return RPMRC_OK;
657fb1
+}
657fb1
+
657fb1
+struct rpmPluginHooks_s fsverity_hooks = {
657fb1
+    .init = fsverity_init,
657fb1
+    .fsm_file_prepare = fsverity_fsm_file_prepare,
657fb1
+};
657fb1
-- 
657fb1
2.13.5
657fb1