]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
verity: add support for setting up verity-protected root disks in the initrd
authorLennart Poettering <lennart@poettering.net>
Fri, 16 Dec 2016 11:57:44 +0000 (12:57 +0100)
committerLennart Poettering <lennart@poettering.net>
Wed, 21 Dec 2016 18:09:30 +0000 (19:09 +0100)
This adds a generator and a small service that will look for "roothash="
on the kernel command line and use it for setting up a very partition
for the root device.

This provides similar functionality to nspawn's existing --roothash=
switch.

.gitignore
Makefile-man.am
Makefile.am
man/kernel-command-line.xml
man/systemd-veritysetup-generator.xml [new file with mode: 0644]
man/systemd-veritysetup@.service.xml [new file with mode: 0644]
src/fstab-generator/fstab-generator.c
src/gpt-auto-generator/gpt-auto-generator.c
src/veritysetup/Makefile [new symlink]
src/veritysetup/veritysetup-generator.c [new file with mode: 0644]
src/veritysetup/veritysetup.c [new file with mode: 0644]

index f246d3e6d5558ff880e57637c93eb049775f805c..fe7859c265312ec00f1a27ec31453e9798fcfca3 100644 (file)
 /systemd-update-utmp
 /systemd-user-sessions
 /systemd-vconsole-setup
+/systemd-veritysetup
+/systemd-veritysetup-generator
 /systemd-volatile-root
 /tags
 /test-acd
index 27660ef1c25f99ce4dd79526a90f829f349c2076..c47bedd0df3dcf6ce52d7530aba8b0581ab7d64e 100644 (file)
@@ -2276,13 +2276,20 @@ if HAVE_LIBCRYPTSETUP
 MANPAGES += \
        man/crypttab.5 \
        man/systemd-cryptsetup-generator.8 \
-       man/systemd-cryptsetup@.service.8
+       man/systemd-cryptsetup@.service.8 \
+       man/systemd-veritysetup-generator.8 \
+       man/systemd-veritysetup@.service.8
 MANPAGES_ALIAS += \
-       man/systemd-cryptsetup.8
+       man/systemd-cryptsetup.8 \
+       man/systemd-veritysetup.8
 man/systemd-cryptsetup.8: man/systemd-cryptsetup@.service.8
+man/systemd-veritysetup.8: man/systemd-veritysetup@.service.8
 man/systemd-cryptsetup.html: man/systemd-cryptsetup@.service.html
        $(html-alias)
 
+man/systemd-veritysetup.html: man/systemd-veritysetup@.service.html
+       $(html-alias)
+
 endif
 
 if HAVE_MICROHTTPD
@@ -2810,6 +2817,8 @@ EXTRA_DIST += \
        man/systemd-update-utmp.service.xml \
        man/systemd-user-sessions.service.xml \
        man/systemd-vconsole-setup.service.xml \
+       man/systemd-veritysetup-generator.xml \
+       man/systemd-veritysetup@.service.xml \
        man/systemd-volatile-root.service.xml \
        man/systemd.automount.xml \
        man/systemd.device.xml \
index 92a3680461b20e3062043f4a5b3a633794f13581..c87c547e05c36ebc046f459e742cae07af2573c7 100644 (file)
@@ -4833,10 +4833,12 @@ EXTRA_DIST += \
 # ------------------------------------------------------------------------------
 if HAVE_LIBCRYPTSETUP
 rootlibexec_PROGRAMS += \
-       systemd-cryptsetup
+       systemd-cryptsetup \
+       systemd-veritysetup
 
 systemgenerator_PROGRAMS += \
-       systemd-cryptsetup-generator
+       systemd-cryptsetup-generator \
+       systemd-veritysetup-generator
 
 dist_systemunit_DATA += \
        units/cryptsetup.target \
@@ -4859,6 +4861,23 @@ systemd_cryptsetup_generator_SOURCES = \
 systemd_cryptsetup_generator_LDADD = \
        libsystemd-shared.la
 
+systemd_veritysetup_SOURCES = \
+       src/veritysetup/veritysetup.c
+
+systemd_veritysetup_CFLAGS = \
+       $(AM_CFLAGS) \
+       $(LIBCRYPTSETUP_CFLAGS)
+
+systemd_veritysetup_LDADD = \
+       libsystemd-shared.la \
+       $(LIBCRYPTSETUP_LIBS)
+
+systemd_veritysetup_generator_SOURCES = \
+       src/veritysetup/veritysetup-generator.c
+
+systemd_veritysetup_generator_LDADD = \
+       libsystemd-shared.la
+
 SYSINIT_TARGET_WANTS += \
        cryptsetup.target
 
index 7e1d408ded11d68e7ce99cad62f4c3c678ad6048..415b8d3cf9872e947bc894cc24e776d3980e83d9 100644 (file)
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>roothash=</varname></term>
+        <term><varname>systemd.verity=</varname></term>
+        <term><varname>rd.systemd.verity=</varname></term>
+        <term><varname>systemd.verity_root_data=</varname></term>
+        <term><varname>systemd.verity_root_hash=</varname></term>
+        <listitem>
+          <para>Configures the integrity protection root hash for the root file system, and other related
+          parameters. For details, see
+          <citerefentry><refentrytitle>systemd-veritysetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>systemd.gpt_auto=</varname></term>
         <term><varname>rd.systemd.gpt_auto=</varname></term>
         <citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
         <citerefentry project='die-net'><refentrytitle>plymouth</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
         <citerefentry><refentrytitle>systemd-cryptsetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+        <citerefentry><refentrytitle>systemd-veritysetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
         <citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
         <citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
         <citerefentry><refentrytitle>systemd-volatile-root.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
diff --git a/man/systemd-veritysetup-generator.xml b/man/systemd-veritysetup-generator.xml
new file mode 100644 (file)
index 0000000..87d66e9
--- /dev/null
@@ -0,0 +1,122 @@
+<?xml version="1.0"?>
+<!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!--
+  This file is part of systemd.
+
+  Copyright 2016 Lennart Poettering
+
+  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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+-->
+<refentry id="systemd-veritysetup-generator" conditional='HAVE_LIBCRYPTSETUP'>
+
+  <refentryinfo>
+    <title>systemd-veritysetup-generator</title>
+    <productname>systemd</productname>
+
+    <authorgroup>
+      <author>
+        <contrib>Developer</contrib>
+        <firstname>Lennart</firstname>
+        <surname>Poettering</surname>
+        <email>lennart@poettering.net</email>
+      </author>
+    </authorgroup>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd-veritysetup-generator</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd-veritysetup-generator</refname>
+    <refpurpose>Unit generator for integrity protected block devices</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <para><filename>/usr/lib/systemd/system-generators/systemd-veritysetup-generator</filename></para>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><filename>systemd-veritysetup-generator</filename> is a generator that translates kernel command line options
+    configuring integrity protected block devices (verity) into native systemd units early at boot and when
+    configuration of the system manager is reloaded. This will create
+    <citerefentry><refentrytitle>systemd-veritysetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    units as necessary.</para>
+
+    <para>Currently, only a single verity device may be se up with this generator, backing the root file system of the
+    OS.</para>
+
+    <para><filename>systemd-veritysetup-generator</filename> implements
+    <citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Kernel Command Line</title>
+
+    <para><filename>systemd-veritysetup-generator</filename>
+    understands the following kernel command line parameters:</para>
+
+    <variablelist class='kernel-commandline-options'>
+      <varlistentry>
+        <term><varname>systemd.verity=</varname></term>
+        <term><varname>rd.systemd.verity=</varname></term>
+
+        <listitem><para>Takes a boolean argument. Defaults to <literal>yes</literal>. If <literal>no</literal>,
+        disables the generator entirely. <varname>rd.systemd.verity=</varname> is honored only by the initial RAM disk
+        (initrd) while <varname>systemd.verity=</varname> is honored by both the host system and the
+        initrd. </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>roothash=</varname></term>
+
+        <listitem><para>Takes a root hash value for the root file system. Expects a hash value formatted in hexadecimal
+        characters, of the appropriate length (i.e. most likely 256 bit/64 characters, or longer). If not specified via
+        <varname>systemd.verity_root_data=</varname> and <varname>systemd.verity_root_hash=</varname>, the hash and
+        data devices to use are automatically derived from the specified hash value. Specifically, the data partition
+        device is looked for under a GPT partition UUID derived from the first 128bit of the root hash, the hash
+        partition device is looked for under a GPT partition UUID derived from the last 128bit of the root hash. Hence
+        it is usually sufficient to specify the root hash to boot from an integrity protected root file system, as
+        device paths are automatically determined from it — as long as the partition table is properly set up.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>systemd.verity_root_data=</varname></term>
+        <term><varname>systemd.verity_root_hash=</varname></term>
+
+        <listitem><para>These two settings take block device paths as arguments, and may be use to explicitly configure
+        the data partition and hash partition to use for setting up the integrity protection for the root file
+        system. If not specified, these paths are automatically derived from the <varname>roothash=</varname> argument
+        (see above).</para></listitem>
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-veritysetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry project='die-net'><refentrytitle>veritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
diff --git a/man/systemd-veritysetup@.service.xml b/man/systemd-veritysetup@.service.xml
new file mode 100644 (file)
index 0000000..173e535
--- /dev/null
@@ -0,0 +1,75 @@
+<?xml version="1.0"?>
+<!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!--
+  This file is part of systemd.
+
+  Copyright 2016 Lennart Poettering
+
+  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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+-->
+<refentry id="systemd-veritysetup@.service" conditional='HAVE_LIBCRYPTSETUP'>
+
+  <refentryinfo>
+    <title>systemd-veritysetup@.service</title>
+    <productname>systemd</productname>
+
+    <authorgroup>
+      <author>
+        <contrib>Developer</contrib>
+        <firstname>Lennart</firstname>
+        <surname>Poettering</surname>
+        <email>lennart@poettering.net</email>
+      </author>
+    </authorgroup>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd-veritysetup@.service</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd-veritysetup@.service</refname>
+    <refname>systemd-veritysetup</refname>
+    <refpurpose>Disk integrity protection logic</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <para><filename>systemd-veritysetup@.service</filename></para>
+    <para><filename>/usr/lib/systemd/systemd-veritysetup</filename></para>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><filename>systemd-veritysetup@.service</filename> is a service responsible for setting up integrity
+    protection (verity) 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 kernel command line configuration for
+    integrity protected block devices is translated into <filename>systemd-veritysetup@.service</filename> units by
+    <citerefentry><refentrytitle>systemd-veritysetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-veritysetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry project='die-net'><refentrytitle>veritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+     </para>
+  </refsect1>
+
+</refentry>
index f58aa27df22c4e0a7f75404648cb6cf100a563df..3c601a63e20810582795e81422ab24ba638b7eca 100644 (file)
@@ -50,6 +50,7 @@ static bool arg_fstab_enabled = true;
 static char *arg_root_what = NULL;
 static char *arg_root_fstype = NULL;
 static char *arg_root_options = NULL;
+static char *arg_root_hash = NULL;
 static int arg_root_rw = -1;
 static char *arg_usr_what = NULL;
 static char *arg_usr_fstype = NULL;
@@ -697,6 +698,13 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
 
                 free(arg_root_options);
                 arg_root_options = o;
+        } else if (streq(key, "roothash")) {
+
+                if (proc_cmdline_value_missing(key, value))
+                        return 0;
+
+                if (free_and_strdup(&arg_root_hash, value) < 0)
+                        return log_oom();
 
         } else if (streq(key, "mount.usr")) {
 
@@ -749,6 +757,24 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
         return 0;
 }
 
+static int determine_root(void) {
+        /* If we have a root hash but no root device then Verity is used, and we use the "root" DM device as root. */
+
+        if (arg_root_what)
+                return 0;
+
+        if (!arg_root_hash)
+                return 0;
+
+        arg_root_what = strdup("/dev/mapper/root");
+        if (!arg_root_what)
+                return log_oom();
+
+        log_info("Using verity root device %s.", arg_root_what);
+
+        return 1;
+}
+
 int main(int argc, char *argv[]) {
         int r = 0;
 
@@ -772,6 +798,8 @@ int main(int argc, char *argv[]) {
         if (r < 0)
                 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
 
+        (void) determine_root();
+
         /* Always honour root= and usr= in the kernel command line if we are in an initrd */
         if (in_initrd()) {
                 int k;
@@ -812,6 +840,7 @@ int main(int argc, char *argv[]) {
         free(arg_root_what);
         free(arg_root_fstype);
         free(arg_root_options);
+        free(arg_root_hash);
 
         free(arg_usr_what);
         free(arg_usr_fstype);
index 3126469f608dcc017ce79cfde3f1679a97f4541f..612a3fe77779d7c4d0d43291b40bdfb8bd34cf07 100644 (file)
@@ -718,6 +718,15 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
 
                 arg_root_enabled = streq(value, "gpt-auto");
 
+        } else if (streq(key, "roothash")) {
+
+                if (proc_cmdline_value_missing(key, value))
+                        return 0;
+
+                /* Disable root disk logic if there's roothash= defined (i.e. verity enabled) */
+
+                arg_root_enabled = false;
+
         } else if (streq(key, "rw") && !value)
                 arg_root_rw = true;
         else if (streq(key, "ro") && !value)
diff --git a/src/veritysetup/Makefile b/src/veritysetup/Makefile
new file mode 120000 (symlink)
index 0000000..d0b0e8e
--- /dev/null
@@ -0,0 +1 @@
+../Makefile
\ No newline at end of file
diff --git a/src/veritysetup/veritysetup-generator.c b/src/veritysetup/veritysetup-generator.c
new file mode 100644 (file)
index 0000000..519ac05
--- /dev/null
@@ -0,0 +1,253 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2016 Lennart Poettering
+
+  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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#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 "hexdecoct.h"
+#include "id128-util.h"
+#include "mkdir.h"
+#include "parse-util.h"
+#include "proc-cmdline.h"
+#include "string-util.h"
+#include "unit-name.h"
+
+static char *arg_dest = NULL;
+static bool arg_enabled = true;
+static char *arg_root_hash = NULL;
+static char *arg_data_what = NULL;
+static char *arg_hash_what = NULL;
+
+static int create_device(void) {
+        _cleanup_free_ char *u = NULL, *v = NULL, *d = NULL, *e = NULL;
+        _cleanup_fclose_ FILE *f = NULL;
+        const char *p, *to;
+        int r;
+
+        /* If all three pieces of information are missing, then verity is turned off */
+        if (!arg_root_hash && !arg_data_what && !arg_hash_what)
+                return 0;
+
+        /* if one of them is missing however, the data is simply incomplete and this is an error */
+        if (!arg_root_hash)
+                log_error("Verity information incomplete, root hash unspecified.");
+        if (!arg_data_what)
+                log_error("Verity information incomplete, root data device unspecified.");
+        if (!arg_hash_what)
+                log_error("Verity information incomplete, root hash device unspecified.");
+
+        if (!arg_root_hash || !arg_data_what || !arg_hash_what)
+                return -EINVAL;
+
+        log_debug("Using root verity data device %s,\n"
+                  "                  hash device %s,\n"
+                  "                and root hash %s.", arg_data_what, arg_hash_what, arg_root_hash);
+
+        p = strjoina(arg_dest, "/systemd-veritysetup@root.service");
+
+        u = fstab_node_to_udev_node(arg_data_what);
+        if (!u)
+                return log_oom();
+        v = fstab_node_to_udev_node(arg_hash_what);
+        if (!v)
+                return log_oom();
+
+        r = unit_name_from_path(u, ".device", &d);
+        if (r < 0)
+                return log_error_errno(r, "Failed to to generate unit name: %m");
+        r = unit_name_from_path(v, ".device", &e);
+        if (r < 0)
+                return log_error_errno(r, "Failed to to generate unit name: %m");
+
+        f = fopen(p, "wxe");
+        if (!f)
+                return log_error_errno(errno, "Failed to create unit file %s: %m", p);
+
+        fprintf(f,
+                "# Automatically generated by systemd-veritysetup-generator\n\n"
+                "[Unit]\n"
+                "Description=Integrity Protection Setup for %%I\n"
+                "Documentation=man:systemd-veritysetup-generator(8) man:systemd-veritysetup@.service(8)\n"
+                "SourcePath=/proc/cmdline\n"
+                "DefaultDependencies=no\n"
+                "Conflicts=umount.target\n"
+                "BindsTo=%s %s\n"
+                "IgnoreOnIsolate=true\n"
+                "After=cryptsetup-pre.target %s %s\n"
+                "Before=cryptsetup.target umount.target\n"
+                "\n[Service]\n"
+                "Type=oneshot\n"
+                "RemainAfterExit=yes\n"
+                "ExecStart=" ROOTLIBEXECDIR "/systemd-veritysetup attach root '%s' '%s' '%s'\n"
+                "ExecStop=" ROOTLIBEXECDIR "/systemd-veritysetup detach root\n",
+                d, e,
+                d, e,
+                u, v, arg_root_hash);
+
+        r = fflush_and_check(f);
+        if (r < 0)
+                return log_error_errno(r, "Failed to write file %s: %m", p);
+
+        to = strjoina(arg_dest, "/cryptsetup.target.requires/systemd-veritysetup@root.service");
+
+        (void) mkdir_parents(to, 0755);
+        if (symlink("../systemd-veritysetup@root.service", to) < 0)
+                return log_error_errno(errno, "Failed to create symlink %s: %m", to);
+
+        return 0;
+}
+
+static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
+        int r;
+
+        if (streq(key, "systemd.verity")) {
+
+                r = value ? parse_boolean(value) : 1;
+                if (r < 0)
+                        log_warning("Failed to parse verity= kernel command line switch %s. Ignoring.", value);
+                else
+                        arg_enabled = r;
+
+        } else if (streq(key, "roothash")) {
+
+                if (proc_cmdline_value_missing(key, value))
+                        return 0;
+
+                r = free_and_strdup(&arg_root_hash, value);
+                if (r < 0)
+                        return log_oom();
+
+        } else if (streq(key, "systemd.verity_root_data")) {
+
+                if (proc_cmdline_value_missing(key, value))
+                        return 0;
+
+                r = free_and_strdup(&arg_data_what, value);
+                if (r < 0)
+                        return log_oom();
+
+        } else if (streq(key, "systemd.verity_root_hash")) {
+
+                if (proc_cmdline_value_missing(key, value))
+                        return 0;
+
+                r = free_and_strdup(&arg_hash_what, value);
+                if (r < 0)
+                        return log_oom();
+        }
+
+        return 0;
+}
+
+static int determine_devices(void) {
+        _cleanup_free_ void *m = NULL;
+        sd_id128_t root_uuid, verity_uuid;
+        char ids[37];
+        size_t l;
+        int r;
+
+        /* Try to automatically derive the root data and hash device paths from the root hash */
+
+        if (!arg_root_hash)
+                return 0;
+
+        if (arg_data_what && arg_hash_what)
+                return 0;
+
+        r = unhexmem(arg_root_hash, strlen(arg_root_hash), &m, &l);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse root hash: %s", arg_root_hash);
+        if (l < sizeof(sd_id128_t)) {
+                log_debug("Root hash is shorter than 128 bits (32 characters), ignoring for discovering verity partition.");
+                return 0;
+        }
+
+        if (!arg_data_what) {
+                memcpy(&root_uuid, m, sizeof(root_uuid));
+
+                arg_data_what = strjoin("/dev/disk/by-partuuid/", id128_to_uuid_string(root_uuid, ids));
+                if (!arg_data_what)
+                        return log_oom();
+        }
+
+        if (!arg_hash_what) {
+                memcpy(&verity_uuid, (uint8_t*) m + l - sizeof(verity_uuid), sizeof(verity_uuid));
+
+                arg_hash_what = strjoin("/dev/disk/by-partuuid/", id128_to_uuid_string(verity_uuid, ids));
+                if (!arg_hash_what)
+                        return log_oom();
+        }
+
+        return 1;
+}
+
+int main(int argc, char *argv[]) {
+        int r;
+
+        if (argc > 1 && argc != 4) {
+                log_error("This program takes three or no arguments.");
+                return EXIT_FAILURE;
+        }
+
+        if (argc > 1)
+                arg_dest = argv[1];
+
+        log_set_target(LOG_TARGET_SAFE);
+        log_parse_environment();
+        log_open();
+
+        umask(0022);
+
+        r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
+        if (r < 0) {
+                log_warning_errno(r, "Failed to parse kernel command line: %m");
+                goto finish;
+        }
+
+        /* For now we only support the root device on verity. Later on we might want to add support for /etc/veritytab
+         * or similar to define additional mappings */
+
+        if (!arg_enabled) {
+                r = 0;
+                goto finish;
+        }
+
+        r = determine_devices();
+        if (r < 0)
+                goto finish;
+
+        r = create_device();
+        if (r < 0)
+                goto finish;
+
+        r = 0;
+
+finish:
+        free(arg_root_hash);
+        free(arg_data_what);
+        free(arg_hash_what);
+
+        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/veritysetup/veritysetup.c b/src/veritysetup/veritysetup.c
new file mode 100644 (file)
index 0000000..f809d51
--- /dev/null
@@ -0,0 +1,154 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2016 Lennart Poettering
+
+  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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <libcryptsetup.h>
+#include <stdio.h>
+#include <sys/stat.h>
+
+#include "log.h"
+#include "hexdecoct.h"
+#include "string-util.h"
+#include "alloc-util.h"
+
+static char *arg_root_hash = NULL;
+static char *arg_data_what = NULL;
+static char *arg_hash_what = NULL;
+
+static int help(void) {
+        printf("%s attach VOLUME DATADEVICE HASHDEVICE ROOTHASH\n"
+               "%s detach VOLUME\n\n"
+               "Attaches or detaches an integrity protected block device.\n",
+               program_invocation_short_name,
+               program_invocation_short_name);
+
+        return 0;
+}
+
+static void log_glue(int level, const char *msg, void *usrptr) {
+        log_debug("%s", msg);
+}
+
+int main(int argc, char *argv[]) {
+        struct crypt_device *cd = NULL;
+        int r;
+
+        if (argc <= 1) {
+                r = help();
+                goto finish;
+        }
+
+        if (argc < 3) {
+                log_error("This program requires at least two arguments.");
+                r = -EINVAL;
+                goto finish;
+        }
+
+        log_set_target(LOG_TARGET_AUTO);
+        log_parse_environment();
+        log_open();
+
+        umask(0022);
+
+        if (streq(argv[1], "attach")) {
+                _cleanup_free_ void *m = NULL;
+                crypt_status_info status;
+                size_t l;
+
+                if (argc < 6) {
+                        log_error("attach requires at least two arguments.");
+                        r = -EINVAL;
+                        goto finish;
+                }
+
+                r = unhexmem(argv[5], strlen(argv[5]), &m, &l);
+                if (r < 0) {
+                        log_error("Failed to parse root hash.");
+                        goto finish;
+                }
+
+                r = crypt_init(&cd, argv[4]);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to open verity device %s: %m", argv[4]);
+                        goto finish;
+                }
+
+                crypt_set_log_callback(cd, log_glue, NULL);
+
+                status = crypt_status(cd, argv[2]);
+                if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) {
+                        log_info("Volume %s already active.", argv[2]);
+                        r = 0;
+                        goto finish;
+                }
+
+                r = crypt_load(cd, CRYPT_VERITY, NULL);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to load verity superblock: %m");
+                        goto finish;
+                }
+
+                r = crypt_set_data_device(cd, argv[3]);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to configure data device: %m");
+                        goto finish;
+                }
+
+                r = crypt_activate_by_volume_key(cd, argv[2], m, l, CRYPT_ACTIVATE_READONLY);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to set up verity device: %m");
+                        goto finish;
+                }
+
+        } else if (streq(argv[1], "detach")) {
+
+                r = crypt_init_by_name(&cd, argv[2]);
+                if (r == -ENODEV) {
+                        log_info("Volume %s already inactive.", argv[2]);
+                        goto finish;
+                } else if (r < 0) {
+                        log_error_errno(r, "crypt_init_by_name() failed: %m");
+                        goto finish;
+                }
+
+                crypt_set_log_callback(cd, log_glue, NULL);
+
+                r = crypt_deactivate(cd, argv[2]);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to deactivate: %m");
+                        goto finish;
+                }
+
+        } else {
+                log_error("Unknown verb %s.", argv[1]);
+                r = -EINVAL;
+                goto finish;
+        }
+
+        r = 0;
+
+finish:
+        if (cd)
+                crypt_free(cd);
+
+        free(arg_root_hash);
+        free(arg_data_what);
+        free(arg_hash_what);
+
+        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}