Add stand-alone dm-integrity support

This adds support for dm integrity targets and an associated
/etc/integritytab file which is required as the dm integrity device
super block doesn't include all of the required metadata to bring up
the device correctly.  See integritytab man page for details.
This commit is contained in:
Tony Asleson 2021-09-26 11:53:42 -05:00
parent 9a2a6ec4e3
commit 1f1a2243c0
14 changed files with 828 additions and 0 deletions

View File

@ -50,6 +50,10 @@ All tools:
useful for debugging. Currently only supported by
`systemd-cryptsetup-generator`.
* `$SYSTEMD_INTEGRITYTAB` — if set, use this path instead of
`/etc/integritytab`. Only useful for debugging. Currently only supported by
`systemd-integritysetup-generator`.
* `$SYSTEMD_VERITYTAB` — if set, use this path instead of
`/etc/veritytab`. Only useful for debugging. Currently only supported by
`systemd-veritysetup-generator`.

161
man/integritytab.xml Normal file
View File

@ -0,0 +1,161 @@
<?xml version="1.0"?>
<!--*-nxml-*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!--
SPDX-License-Identifier: LGPL-2.1-or-later
-->
<refentry id="integritytab" conditional='HAVE_LIBCRYPTSETUP' xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>integritytab</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>integritytab</refentrytitle>
<manvolnum>5</manvolnum>
</refmeta>
<refnamediv>
<refname>integritytab</refname>
<refpurpose>Configuration for integrity block devices</refpurpose>
</refnamediv>
<refsynopsisdiv>
<para><filename>/etc/integritytab</filename></para>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>The <filename>/etc/integritytab</filename> file describes
integrity protected block devices that are set up during
system boot.</para>
<para>Empty lines and lines starting with the <literal>#</literal>
character are ignored. Each of the remaining lines describes one
verity integrity protected block device. Fields are delimited by
white space.</para>
<para>Each line is in the form<programlisting><replaceable>volume-name</replaceable> <replaceable>block-device</replaceable>
<replaceable>[keyfile|-]</replaceable> <replaceable>[options|-]</replaceable></programlisting>
The first two fields are mandatory, the remaining two are optional and only required if user specified non-default options during integrity format.</para>
<para>The first field contains the name of the resulting integrity volume; its block device is set up
below <filename>/dev/mapper/</filename>.</para>
<para>The second field contains a path to the underlying block device, or a specification of a block device via
<literal>UUID=</literal> followed by the UUID,
<literal>PARTUUID=</literal> followed by the partition UUID,
<literal>LABEL=</literal> followed by the label,
<literal>PARTLABEL=</literal> followed by the partition label,
</para>
<para>The third field if present contains an absolute filename path to a key file or a <literal>-</literal>
to specify none. When the filename is present, the "integrity-algorithm" defaults to <literal>hmac-sha256</literal>
with the key length derived from the number of bytes in the key file. At this time the only supported integrity algorithm
when using key file is hmac-sha256. The maximum size of the key file is 4096 bytes.
</para>
<para>The fourth field, if present, is a comma-delimited list of options or a <literal>-</literal> to specify none. The following options are
recognized:</para>
<variablelist>
<varlistentry>
<term><option>allow-discards</option></term>
<listitem><para>
Allow the use of discard (TRIM) requests for the device.
This option is available since the Linux kernel version 5.7.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>journal-watermark=[0..100]%</option></term>
<listitem><para>
Journal watermark in percent. When the journal percentage exceeds this watermark, the journal flush will be started. Setting a value of
"0%" uses default value.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>journal-commit-time=[0..N]</option></term>
<listitem><para>
Commit time in milliseconds. When this time passes (and no explicit flush operation was issued), the journal is written. Setting a value of
zero uses default value.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>data-device=/dev/disk/by-...</option></term>
<listitem><para>
Specify a separate block device that contains existing data. The second field specified in the
integritytab for block device then will contain calculated integrity tags and journal for data-device,
but not the end user data.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>integrity-algorithm=[crc32c|crc32|sha1|sha256|hmac-sha256]</option></term>
<listitem><para>
The algorithm used for integrity checking. The default is crc32c. Must match option used during format.
</para></listitem>
</varlistentry>
</variablelist>
<para>At early boot and when the system manager configuration is
reloaded, this file is translated into native systemd units by
<citerefentry><refentrytitle>systemd-integritysetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
</refsect1>
<refsect1>
<title>Examples</title>
<example>
<title>/etc/integritytab</title>
<para>Set up two integrity protected block devices. </para>
<programlisting>home PARTUUID=4973d0b8-1b15-c449-96ec-94bab7f6a7b8 - journal-commit-time=10,allow-discards,journal-watermark=55%
data PARTUUID=5d4b1808-be76-774d-88af-03c4c3a41761 - allow-discards
</programlisting>
</example>
<example>
<title>/etc/integritytab</title>
<para>Set up 1 integrity protected block device using defaults </para>
<programlisting>home PARTUUID=4973d0b8-1b15-c449-96ec-94bab7f6a7b8</programlisting>
</example>
<example>
<title>/etc/integritytab</title>
<para>Set up 1 integrity device using existing data block device which contains user data </para>
<programlisting>home PARTUUID=4973d0b8-1b15-c449-96ec-94bab7f6a7b8 - data-device=/dev/disk/by-uuid/9276d9c0-d4e3-4297-b4ff-3307cd0d092f</programlisting>
</example>
<example>
<title>/etc/integritytab</title>
<para>Set up 1 integrity device using a HMAC key file using defaults </para>
<programlisting>home PARTUUID=4973d0b8-1b15-c449-96ec-94bab7f6a7b8 /etc/hmac.key</programlisting>
</example>
</refsect1>
<refsect1>
<title>See Also</title>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-integritysetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-integritysetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry project='die-net'><refentrytitle>integritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
</para>
</refsect1>
</refentry>

View File

@ -24,6 +24,7 @@ manpages = [
['hostname', '5', [], ''],
['hostnamectl', '1', [], 'ENABLE_HOSTNAMED'],
['hwdb', '7', [], 'ENABLE_HWDB'],
['integritytab', '5', [], 'HAVE_LIBCRYPTSETUP'],
['journal-remote.conf', '5', ['journal-remote.conf.d'], 'HAVE_MICROHTTPD'],
['journal-upload.conf', '5', ['journal-upload.conf.d'], 'HAVE_MICROHTTPD'],
['journalctl', '1', [], ''],
@ -882,6 +883,11 @@ manpages = [
'8',
['systemd-initctl', 'systemd-initctl.socket'],
'HAVE_SYSV_COMPAT'],
['systemd-integritysetup-generator', '8', [], 'HAVE_LIBCRYPTSETUP'],
['systemd-integritysetup@.service',
'8',
['systemd-integritysetup'],
'HAVE_LIBCRYPTSETUP'],
['systemd-journal-gatewayd.service',
'8',
['systemd-journal-gatewayd', 'systemd-journal-gatewayd.socket'],

View File

@ -0,0 +1,48 @@
<?xml version="1.0"?>
<!--*-nxml-*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<refentry id="systemd-integritysetup-generator" conditional='HAVE_LIBCRYPTSETUP'>
<refentryinfo>
<title>systemd-integritysetup-generator</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>systemd-integritysetup-generator</refentrytitle>
<manvolnum>8</manvolnum>
</refmeta>
<refnamediv>
<refname>systemd-integritysetup-generator</refname>
<refpurpose>Unit generator for integrity protected block devices</refpurpose>
</refnamediv>
<refsynopsisdiv>
<para><filename>/usr/lib/systemd/system-generators/systemd-integritysetup-generator</filename></para>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><filename>systemd-integritysetup-generator</filename> is a generator that translates <filename>/etc/integritytab</filename> entries into
native systemd units early at boot. This will create
<citerefentry><refentrytitle>systemd-integritysetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
units as necessary.</para>
<para><command>systemd-integritysetup-generator</command> implements
<citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
</refsect1>
<refsect1>
<title>See Also</title>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-integritysetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry project='die-net'><refentrytitle>integritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
</para>
</refsect1>
</refentry>

View File

@ -0,0 +1,95 @@
<?xml version="1.0"?>
<!--*-nxml-*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<refentry id="systemd-integritysetup@.service" conditional='HAVE_LIBCRYPTSETUP'>
<refentryinfo>
<title>systemd-integritysetup@.service</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>systemd-integritysetup@.service</refentrytitle>
<manvolnum>8</manvolnum>
</refmeta>
<refnamediv>
<refname>systemd-integritysetup@.service</refname>
<refname>systemd-integritysetup</refname>
<refpurpose>Disk integrity protection logic</refpurpose>
</refnamediv>
<refsynopsisdiv>
<para><filename>systemd-integritysetup@.service</filename></para>
<para><filename>/usr/lib/systemd/systemd-integritysetup</filename></para>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><filename>systemd-integritysetup@.service</filename> is a service responsible for setting up integrity
protected block devices. It should be instantiated for each device that requires integrity
protection.</para>
<para>At early boot and when the system manager configuration is reloaded, entries from /etc/integritytab are converted into
<filename>systemd-integritysetup@.service</filename> units by
<citerefentry><refentrytitle>systemd-integritysetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<para><filename>systemd-integritysetup@.service</filename> calls <command>systemd-integritysetup</command>.</para>
</refsect1>
<refsect1>
<title>Commands</title>
<para>The following commands are understood by <command>systemd-integritysetup</command>:</para>
<variablelist>
<varlistentry>
<term>
<option>attach</option>
<replaceable>volume</replaceable>
<replaceable>device</replaceable>
[<replaceable>key-file|-</replaceable>]
[<replaceable>option(s)|-</replaceable>]
</term>
<listitem><para>Create a block device <replaceable>volume</replaceable> using
<replaceable>device</replaceable>. See integritytab man page and
<ulink url="https://www.kernel.org/doc/html/latest/admin-guide/device-mapper/dm-integrity.html">
Kernel dm-integrity</ulink> documentation for details.
</para></listitem>
</varlistentry>
<varlistentry>
<term>
<option>detach</option>
<replaceable>volume</replaceable>
</term>
<listitem><para>Detach (destroy) the block device
<replaceable>volume</replaceable>.</para></listitem>
</varlistentry>
<varlistentry>
<term>
<option>help</option>
</term>
<listitem><para>Print short information about command syntax.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>See Also</title>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>integritytab</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-integritysetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry project='die-net'><refentrytitle>integritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>
</para>
</refsect1>
</refentry>

View File

@ -48,6 +48,8 @@
<filename>initrd-root-device.target</filename>,
<filename>initrd-root-fs.target</filename>,
<filename>initrd-usr-fs.target</filename>,
<filename>integritysetup-pre.target</filename>,
<filename>integritysetup.target</filename>,
<filename>kbrequest.target</filename>,
<filename>kexec.target</filename>,
<filename>local-fs-pre.target</filename>,

View File

@ -252,6 +252,7 @@ conf.set_quoted('SYSTEMD_GROWFS_PATH', rootlibexecdir / '
conf.set_quoted('SYSTEMD_HOMEWORK_PATH', rootlibexecdir / 'systemd-homework')
conf.set_quoted('SYSTEMD_IMPORT_FS_PATH', rootlibexecdir / 'systemd-import-fs')
conf.set_quoted('SYSTEMD_IMPORT_PATH', rootlibexecdir / 'systemd-import')
conf.set_quoted('SYSTEMD_INTEGRITYSETUP_PATH', rootlibexecdir / 'systemd-integritysetup')
conf.set_quoted('SYSTEMD_KBD_MODEL_MAP', pkgdatadir / 'kbd-model-map')
conf.set_quoted('SYSTEMD_LANGUAGE_FALLBACK_MAP', pkgdatadir / 'language-fallback-map')
conf.set_quoted('SYSTEMD_MAKEFS_PATH', rootlibexecdir / 'systemd-makefs')
@ -2535,6 +2536,25 @@ if conf.get('HAVE_LIBCRYPTSETUP') == 1
libp11kit],
install_rpath : rootlibexecdir,
install : true)
executable(
'systemd-integritysetup',
['src/integritysetup/integritysetup.c', 'src/integritysetup/integrity-util.c'],
include_directories : includes,
link_with : [libshared],
dependencies : [libcryptsetup],
install_rpath : rootlibexecdir,
install : true,
install_dir : rootlibexecdir)
executable(
'systemd-integritysetup-generator',
['src/integritysetup/integritysetup-generator.c', 'src/integritysetup/integrity-util.c'],
include_directories : includes,
link_with : [libshared],
install_rpath : rootlibexecdir,
install : true,
install_dir : systemgeneratordir)
endif
if conf.get('HAVE_SYSV_COMPAT') == 1

View File

@ -0,0 +1,66 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "integrity-util.h"
#include "extract-word.h"
#include "fileio.h"
#include "path-util.h"
#include "percent-util.h"
static int supported_integrity_algorithm(char *user_supplied) {
if (!STR_IN_SET(user_supplied, "crc32", "crc32c", "sha1", "sha256", "hmac-sha256"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unsupported integrity algorithm (%s)", user_supplied);
return 0;
}
int parse_integrity_options(
const char *options,
uint32_t *ret_activate_flags,
int *ret_percent,
usec_t *ret_commit_time,
char **ret_data_device,
char **ret_integrity_alg) {
int r;
for (;;) {
_cleanup_free_ char *word = NULL;
char *val;
r = extract_first_word(&options, &word, ",", EXTRACT_DONT_COALESCE_SEPARATORS | EXTRACT_UNESCAPE_SEPARATORS);
if (r < 0)
return log_error_errno(r, "Failed to parse options: %m");
if (r == 0)
break;
else if (streq(word, "allow-discards")) {
if (ret_activate_flags)
*ret_activate_flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
} else if ((val = startswith(word, "journal-watermark="))) {
r = parse_percent(val);
if (r < 0)
return log_error_errno(r, "Failed to parse journal-watermark value or value out of range (%s)", val);
if (ret_percent)
*ret_percent = r;
} else if ((val = startswith(word, "journal-commit-time="))) {
usec_t tmp_commit_time;
r = parse_sec(val, &tmp_commit_time);
if (r < 0)
return log_error_errno(r, "Failed to parse journal-commit-time value (%s)", val);
if (ret_commit_time)
*ret_commit_time = tmp_commit_time;
} else if ((val = startswith(word, "data-device="))) {
r = free_and_strdup(ret_data_device, val);
if (r < 0)
return log_oom();
} else if ((val = startswith(word, "integrity-algorithm="))) {
r = free_and_strdup(ret_integrity_alg, val);
if (r < 0)
return log_oom();
r = supported_integrity_algorithm(*ret_integrity_alg);
if (r < 0)
return r;
} else
log_warning("Encountered unknown option '%s', ignoring.", word);
}
return r;
}

View File

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <stdint.h>
#include "cryptsetup-util.h"
#include "time-util.h"
int parse_integrity_options(
const char *options,
uint32_t *ret_activate_flags,
int *ret_percent,
usec_t *ret_commit_time,
char **ret_data_device,
char **ret_integrity_alg);
#define DM_HMAC_256 "hmac(sha256)"
#define DM_MAX_KEY_SIZE 4096 /* Maximum size of key allowed for dm-integrity */

View File

@ -0,0 +1,181 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include "alloc-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fstab-util.h"
#include "generator.h"
#include "hexdecoct.h"
#include "id128-util.h"
#include "integrity-util.h"
#include "main-func.h"
#include "mkdir.h"
#include "parse-util.h"
#include "path-util.h"
#include "proc-cmdline.h"
#include "specifier.h"
#include "string-util.h"
#include "unit-name.h"
static const char *arg_dest = NULL;
static const char *arg_integritytab = NULL;
static char *arg_options = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_options, freep);
static int create_disk(
const char *name,
const char *device,
const char *key_file,
const char *options) {
_cleanup_free_ char *n = NULL, *dd = NULL, *e = NULL, *name_escaped = NULL, *key_file_escaped = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
char *dmname = NULL;
assert(name);
assert(device);
name_escaped = specifier_escape(name);
if (!name_escaped)
return log_oom();
e = unit_name_escape(name);
if (!e)
return log_oom();
r = unit_name_build("systemd-integritysetup", e, ".service", &n);
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
r = unit_name_from_path(device, ".device", &dd);
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
r = generator_open_unit_file(arg_dest, NULL, n, &f);
if (r < 0)
return r;
if (key_file) {
if (!path_is_absolute(key_file))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "key file not absolute file path %s", key_file);
key_file_escaped = specifier_escape(key_file);
if (!key_file_escaped)
return log_oom();
}
if (options) {
r = parse_integrity_options(options, NULL, NULL, NULL, NULL, NULL);
if (r < 0)
return r;
}
fprintf(f,
"[Unit]\n"
"Description=Integrity Setup for %%I\n"
"Documentation=man:integritytab(5) man:systemd-integritysetup-generator(8) man:systemd-integritysetup@.service(8)\n"
"SourcePath=%s\n"
"DefaultDependencies=no\n"
"IgnoreOnIsolate=true\n"
"After=integritysetup-pre.target systemd-udevd-kernel.socket\n"
"Before=blockdev@dev-mapper-%%i.target\n"
"Wants=blockdev@dev-mapper-%%i.target\n"
"Conflicts=umount.target\n"
"Before=integritysetup.target\n"
"BindsTo=%s\n"
"After=%s\n"
"Before=umount.target\n",
arg_integritytab,
dd, dd);
fprintf(f,
"\n"
"[Service]\n"
"Type=oneshot\n"
"RemainAfterExit=yes\n"
"TimeoutSec=0\n"
"ExecStart=" ROOTLIBEXECDIR "/systemd-integritysetup attach '%s' '%s' '%s' '%s'\n"
"ExecStop=" ROOTLIBEXECDIR "/systemd-integritysetup detach '%s'\n",
name_escaped, device, empty_to_dash(key_file_escaped), empty_to_dash(options),
name_escaped);
r = fflush_and_check(f);
if (r < 0)
return log_error_errno(r, "Failed to write unit file %s: %m", n);
r = generator_add_symlink(arg_dest, "integritysetup.target", "requires", n);
if (r < 0)
return r;
dmname = strjoina("dev-mapper-", e, ".device");
return generator_add_symlink(arg_dest, dmname, "requires", n);
}
static int add_integritytab_devices(void) {
_cleanup_fclose_ FILE *f = NULL;
unsigned integritytab_line = 0;
int r;
r = fopen_unlocked(arg_integritytab, "re", &f);
if (r < 0) {
if (errno != ENOENT)
log_error_errno(errno, "Failed to open %s: %m", arg_integritytab);
return 0;
}
for (;;) {
_cleanup_free_ char *line = NULL, *name = NULL, *device_id = NULL, *device_path = NULL, *key_file = NULL, *options = NULL;
char *l;
r = read_line(f, LONG_LINE_MAX, &line);
if (r < 0)
return log_error_errno(r, "Failed to read %s: %m", arg_integritytab);
if (r == 0)
break;
integritytab_line++;
l = strstrip(line);
if (!l)
continue;
if (IN_SET(l[0], 0, '#'))
continue;
/* The key file and the options are optional */
r = sscanf(l, "%ms %ms %ms %ms", &name, &device_id, &key_file, &options);
if (!IN_SET(r, 2, 3, 4)) {
log_error("Failed to parse %s:%u, ignoring.", l, integritytab_line);
continue;
}
device_path = fstab_node_to_udev_node(device_id);
if (!device_path) {
log_error("Failed to find device %s:%u, ignoring.", device_id, integritytab_line);
continue;
}
r = create_disk(name, device_path, empty_or_dash_to_null(key_file), empty_or_dash_to_null(options));
if (r < 0)
return r;
}
return 0;
}
static int run(const char *dest, const char *dest_early, const char *dest_late) {
assert_se(arg_dest = dest);
arg_integritytab = getenv("SYSTEMD_INTEGRITYTAB") ?: "/etc/integritytab";
return add_integritytab_devices();
}
DEFINE_MAIN_GENERATOR_FUNCTION(run);

View File

@ -0,0 +1,197 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <errno.h>
#include <stdio.h>
#include <sys/stat.h>
#include "alloc-util.h"
#include "cryptsetup-util.h"
#include "fileio.h"
#include "hexdecoct.h"
#include "integrity-util.h"
#include "log.h"
#include "main-func.h"
#include "memory-util.h"
#include "path-util.h"
#include "parse-util.h"
#include "pretty-print.h"
#include "string-util.h"
#include "terminal-util.h"
static uint32_t arg_activate_flags;
static int arg_percent;
static usec_t arg_commit_time;
static char *arg_existing_data_device;
static char *arg_integrity_algorithm;
STATIC_DESTRUCTOR_REGISTER(arg_existing_data_device, freep);
STATIC_DESTRUCTOR_REGISTER(arg_integrity_algorithm, freep);
static int help(void) {
_cleanup_free_ char *link = NULL;
int r;
r = terminal_urlify_man("systemd-integritysetup@.service", "8", &link);
if (r < 0)
return log_oom();
printf("%s attach VOLUME DEVICE [HMAC_KEY_FILE|-] [OPTIONS]\n"
"%s detach VOLUME\n\n"
"Attach or detach an integrity protected block device.\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
program_invocation_short_name,
link);
return 0;
}
static int load_key_file(
const char *key_file,
void **ret_key_file_contents,
size_t *ret_key_file_size) {
int r;
_cleanup_(erase_and_freep) char *tmp_key_file_contents = NULL;
size_t tmp_key_file_size;
if (!path_is_absolute(key_file))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "key file not absolute path: %s", key_file);
r = read_full_file_full(
AT_FDCWD, key_file, UINT64_MAX, DM_MAX_KEY_SIZE,
READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET|READ_FULL_FILE_FAIL_WHEN_LARGER,
NULL,
&tmp_key_file_contents, &tmp_key_file_size);
if (r < 0)
return log_error_errno(r, "Failed to process key file: %m");
if (ret_key_file_contents && ret_key_file_size) {
*ret_key_file_contents = TAKE_PTR(tmp_key_file_contents);
*ret_key_file_size = tmp_key_file_size;
}
return 0;
}
static const char *integrity_algorithm_select(const void *key_file_buf) {
/* To keep a bit of sanity for end users, the subset of integrity
algorithms we support will match what is used in integritysetup */
if (arg_integrity_algorithm) {
if (streq("hmac-sha256", arg_integrity_algorithm))
return DM_HMAC_256;
return arg_integrity_algorithm;
} else if (key_file_buf)
return DM_HMAC_256;
return "crc32c";
}
static int run(int argc, char *argv[]) {
_cleanup_(crypt_freep) struct crypt_device *cd = NULL;
int r;
char *action, *volume;
if (argc <= 1 ||
strv_contains(strv_skip(argv, 1), "--help") ||
strv_contains(strv_skip(argv, 1), "-h") ||
streq(argv[1], "help"))
return help();
if (argc < 3)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program requires at least two arguments.");
action = argv[1];
volume = argv[2];
log_setup();
cryptsetup_enable_logging(NULL);
umask(0022);
if (streq(action, "attach")) {
/* attach name device optional_key_file optional_options */
crypt_status_info status;
_cleanup_(erase_and_freep) void *key_buf = NULL;
const char *device, *key_file, *options;
size_t key_buf_size = 0;
if (argc < 4)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "attach requires at least three arguments.");
if (argc > 6)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "attach has a maximum of five arguments.");
device = argv[3];
key_file = (argc > 4) ? empty_or_dash_to_null(argv[4]) : NULL;
options = (argc > 5) ? empty_or_dash_to_null(argv[5]) : NULL;
if (key_file) {
r = load_key_file(key_file, &key_buf, &key_buf_size);
if (r < 0)
return r;
}
if (options) {
r = parse_integrity_options(options, &arg_activate_flags, &arg_percent,
&arg_commit_time, &arg_existing_data_device, &arg_integrity_algorithm);
if (r < 0)
return r;
}
r = crypt_init(&cd, device);
if (r < 0)
return log_error_errno(r, "Failed to open integrity device %s: %m", device);
cryptsetup_enable_logging(cd);
status = crypt_status(cd, volume);
if (IN_SET(status, CRYPT_ACTIVE, CRYPT_BUSY)) {
log_info("Volume %s already active.", volume);
return 0;
}
if (!isempty(arg_existing_data_device)) {
r = crypt_init_data_device(&cd, device, arg_existing_data_device);
if (r < 0)
return log_error_errno(r, "Failed to add separate data device: %m");
}
r = crypt_load(cd,
CRYPT_INTEGRITY,
&(struct crypt_params_integrity) {
.journal_watermark = arg_percent,
.journal_commit_time = DIV_ROUND_UP(arg_commit_time, USEC_PER_SEC),
.integrity = integrity_algorithm_select(key_buf),
});
if (r < 0)
return log_error_errno(r, "Failed to load integrity superblock: %m");
r = crypt_activate_by_volume_key(cd, volume, key_buf, key_buf_size, arg_activate_flags);
if (r < 0)
return log_error_errno(r, "Failed to set up integrity device: %m");
} else if (streq(action, "detach")) {
if (argc > 3)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "detach has a maximum of two arguments.");
r = crypt_init_by_name(&cd, volume);
if (r == -ENODEV)
return 0;
if (r < 0)
return log_error_errno(r, "crypt_init_by_name() failed: %m");
cryptsetup_enable_logging(cd);
r = crypt_deactivate(cd, volume);
if (r < 0)
return log_error_errno(r, "Failed to deactivate: %m");
} else
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown verb %s.", action);
return 0;
}
DEFINE_MAIN_FUNCTION(run);

View File

@ -0,0 +1,14 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd 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.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Local Integrity Protected Volumes (Pre)
Documentation=man:systemd.special(7)
RefuseManualStart=yes
Before=integritysetup.target

View File

@ -0,0 +1,12 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd 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.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Local Integrity Protected Volumes
Documentation=man:systemd.special(7)

View File

@ -13,6 +13,9 @@ units = [
['veritysetup-pre.target', 'HAVE_LIBCRYPTSETUP'],
['veritysetup.target', 'HAVE_LIBCRYPTSETUP',
'sysinit.target.wants/'],
['integritysetup-pre.target', 'HAVE_LIBCRYPTSETUP'],
['integritysetup.target', 'HAVE_LIBCRYPTSETUP',
'sysinit.target.wants/'],
['dev-hugepages.mount', '',
'sysinit.target.wants/'],
['dev-mqueue.mount', '',