]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: add RootHashSignature service parameter 16249/head
authorLuca Boccassi <luca.boccassi@microsoft.com>
Mon, 8 Jun 2020 13:02:55 +0000 (14:02 +0100)
committerLuca Boccassi <luca.boccassi@microsoft.com>
Thu, 25 Jun 2020 07:45:21 +0000 (08:45 +0100)
Allow to explicitly pass root hash signature as a unit option. Takes precedence
over implicit checks.

14 files changed:
man/systemd.exec.xml
src/core/dbus-execute.c
src/core/execute.c
src/core/execute.h
src/core/load-fragment-gperf.gperf.m4
src/core/load-fragment.c
src/core/load-fragment.h
src/core/namespace.c
src/core/namespace.h
src/shared/bus-unit-util.c
src/systemctl/systemctl.c
src/test/test-namespace.c
src/test/test-ns.c
test/fuzz/fuzz-unit-file/directives.service

index de0cfac2a9a289b896f74ba9067d7662ea6dac9f..c828109d01a1a027b3390faa7d6900ec02da2f87 100644 (file)
         <xi:include href="system-only.xml" xpointer="singular"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>RootHashSignature=</varname></term>
+
+        <listitem><para>Takes a PKCS7 formatted binary signature of the <varname>RootHash=</varname> option as a path
+        to a DER encoded signature file or as an ASCII base64 string encoding of the DER encoded signature, prefixed
+        by <literal>base64:</literal>. The dm-verity volume will only be opened if the signature of the root hash
+        signature is valid and created by a public key present in the kernel keyring. If this option is not specified,
+        but a file with the <filename>.roothash.p7s</filename> suffix is found next to the image file, bearing otherwise
+        the same name (except if the image has the <filename>.raw</filename> suffix, in which case the signature file
+        must not have it in its name), the signature is read from it and automatically used.</para>
+
+        <xi:include href="system-only.xml" xpointer="singular"/></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>RootVerity=</varname></term>
 
index 45b5c0c13aa7fd6ddb3475da39e2810f8e2d22d4..41d64e8004789c6a358da53d937ec995b07bb2f3 100644 (file)
@@ -765,6 +765,25 @@ static int property_get_root_hash(
         return sd_bus_message_append_array(reply, 'y', c->root_hash, c->root_hash_size);
 }
 
+static int property_get_root_hash_sig(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        ExecContext *c = userdata;
+
+        assert(bus);
+        assert(c);
+        assert(property);
+        assert(reply);
+
+        return sd_bus_message_append_array(reply, 'y', c->root_hash_sig, c->root_hash_sig_size);
+}
+
 const sd_bus_vtable bus_exec_vtable[] = {
         SD_BUS_VTABLE_START(0),
         SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -809,6 +828,8 @@ const sd_bus_vtable bus_exec_vtable[] = {
         SD_BUS_PROPERTY("RootImage", "s", NULL, offsetof(ExecContext, root_image), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("RootHash", "ay", property_get_root_hash, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("RootHashPath", "s", NULL, offsetof(ExecContext, root_hash_path), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RootHashSignature", "ay", property_get_root_hash_sig, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RootHashSignaturePath", "s", NULL, offsetof(ExecContext, root_hash_sig_path), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("RootVerity", "s", NULL, offsetof(ExecContext, root_verity), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("OOMScoreAdjust", "i", property_get_oom_score_adjust, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("CoredumpFilter", "t", property_get_coredump_filter, 0, SD_BUS_VTABLE_PROPERTY_CONST),
@@ -1326,6 +1347,53 @@ int bus_exec_context_set_transient_property(
                 return bus_set_transient_path(u, "RootHash", &c->root_hash_path, message, flags, error);
         }
 
+        if (streq(name, "RootHashSignature")) {
+                const void *roothash_sig_decoded;
+                size_t roothash_sig_decoded_size;
+
+                r = sd_bus_message_read_array(message, 'y', &roothash_sig_decoded, &roothash_sig_decoded_size);
+                if (r < 0)
+                        return r;
+
+                if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+                        _cleanup_free_ char *encoded = NULL;
+
+                        if (roothash_sig_decoded_size == 0) {
+                                c->root_hash_sig_path = mfree(c->root_hash_sig_path);
+                                c->root_hash_sig = mfree(c->root_hash_sig);
+                                c->root_hash_sig_size = 0;
+
+                                unit_write_settingf(u, flags, name, "RootHashSignature=");
+                        } else {
+                                _cleanup_free_ void *p;
+                                ssize_t len;
+
+                                len = base64mem(roothash_sig_decoded, roothash_sig_decoded_size, &encoded);
+                                if (len < 0)
+                                        return -ENOMEM;
+
+                                p = memdup(roothash_sig_decoded, roothash_sig_decoded_size);
+                                if (!p)
+                                        return -ENOMEM;
+
+                                free_and_replace(c->root_hash_sig, p);
+                                c->root_hash_sig_size = roothash_sig_decoded_size;
+                                c->root_hash_sig_path = mfree(c->root_hash_sig_path);
+
+                                unit_write_settingf(u, flags, name, "RootHashSignature=base64:%s", encoded);
+                        }
+                }
+
+                return 1;
+        }
+
+        if (streq(name, "RootHashSignaturePath")) {
+                c->root_hash_sig_size = 0;
+                c->root_hash_sig = mfree(c->root_hash_sig);
+
+                return bus_set_transient_path(u, "RootHashSignature", &c->root_hash_sig_path, message, flags, error);
+        }
+
         if (streq(name, "RootVerity"))
                 return bus_set_transient_path(u, name, &c->root_verity, message, flags, error);
 
index 0765f112fd0ba3807656de78deb90589aa480456..4bee1b19665edcdb8f9597f8d7b393dd335b8a34 100644 (file)
@@ -2667,7 +2667,9 @@ static int apply_mount_namespace(
                             needs_sandboxing ? context->protect_home : PROTECT_HOME_NO,
                             needs_sandboxing ? context->protect_system : PROTECT_SYSTEM_NO,
                             context->mount_flags,
-                            context->root_hash, context->root_hash_size, context->root_hash_path, context->root_verity,
+                            context->root_hash, context->root_hash_size, context->root_hash_path,
+                            context->root_hash_sig, context->root_hash_sig_size, context->root_hash_sig_path,
+                            context->root_verity,
                             DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK,
                             error_path);
 
@@ -4200,6 +4202,9 @@ void exec_context_done(ExecContext *c) {
         c->root_hash = mfree(c->root_hash);
         c->root_hash_size = 0;
         c->root_hash_path = mfree(c->root_hash_path);
+        c->root_hash_sig = mfree(c->root_hash_sig);
+        c->root_hash_sig_size = 0;
+        c->root_hash_sig_path = mfree(c->root_hash_sig_path);
         c->root_verity = mfree(c->root_verity);
         c->tty_path = mfree(c->tty_path);
         c->syslog_identifier = mfree(c->syslog_identifier);
@@ -4615,6 +4620,17 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
         if (c->root_hash_path)
                 fprintf(f, "%sRootHash: %s\n", prefix, c->root_hash_path);
 
+        if (c->root_hash_sig) {
+                _cleanup_free_ char *encoded = NULL;
+                ssize_t len;
+                len = base64mem(c->root_hash_sig, c->root_hash_sig_size, &encoded);
+                if (len)
+                        fprintf(f, "%sRootHashSignature: base64:%s\n", prefix, encoded);
+        }
+
+        if (c->root_hash_sig_path)
+                fprintf(f, "%sRootHashSignature: %s\n", prefix, c->root_hash_sig_path);
+
         if (c->root_verity)
                 fprintf(f, "%sRootVerity: %s\n", prefix, c->root_verity);
 
index 805709f2aa99db2de139928f3686afd5eea6946a..6bd71c17404e8cc9d18fe724ace655c467e4708f 100644 (file)
@@ -155,9 +155,9 @@ struct ExecContext {
         char **unset_environment;
 
         struct rlimit *rlimit[_RLIMIT_MAX];
-        char *working_directory, *root_directory, *root_image, *root_verity, *root_hash_path;
-        void *root_hash;
-        size_t root_hash_size;
+        char *working_directory, *root_directory, *root_image, *root_verity, *root_hash_path, *root_hash_sig_path;
+        void *root_hash, *root_hash_sig;
+        size_t root_hash_size, root_hash_sig_size;
         bool working_directory_missing_ok:1;
         bool working_directory_home:1;
 
index 9cf959edd57432fb603c35e6ce7f2da14ee8e76c..12ae78eb7dc5d4552665ac3d1916faa3f8bb1244 100644 (file)
@@ -24,6 +24,7 @@ m4_define(`EXEC_CONTEXT_CONFIG_ITEMS',
 $1.RootDirectory,                config_parse_unit_path_printf,      true,                          offsetof($1, exec_context.root_directory)
 $1.RootImage,                    config_parse_unit_path_printf,      true,                          offsetof($1, exec_context.root_image)
 $1.RootHash,                     config_parse_exec_root_hash,        0,                             offsetof($1, exec_context)
+$1.RootHashSignature,            config_parse_exec_root_hash_sig,    0,                             offsetof($1, exec_context)
 $1.RootVerity,                   config_parse_unit_path_printf,      true,                          offsetof($1, exec_context.root_verity)
 $1.User,                         config_parse_user_group_compat,     0,                             offsetof($1, exec_context.user)
 $1.Group,                        config_parse_user_group_compat,     0,                             offsetof($1, exec_context.group)
index 09065ccb366d12582296b02622dc42be6dbbb545..0445a3a2c952362f474d034372777a67db8b8c9b 100644 (file)
@@ -1472,6 +1472,66 @@ int config_parse_exec_root_hash(
         return 0;
 }
 
+int config_parse_exec_root_hash_sig(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        _cleanup_free_ void *roothash_sig_decoded = NULL;
+        char *value;
+        ExecContext *c = data;
+        size_t roothash_sig_decoded_size = 0;
+        int r;
+
+        assert(data);
+        assert(filename);
+        assert(line);
+        assert(rvalue);
+
+        if (isempty(rvalue)) {
+                /* Reset if the empty string is assigned */
+                c->root_hash_sig_path = mfree(c->root_hash_sig_path);
+                c->root_hash_sig = mfree(c->root_hash_sig);
+                c->root_hash_sig_size = 0;
+                return 0;
+        }
+
+        if (path_is_absolute(rvalue)) {
+                /* We have the path to a roothash signature to load and decode, eg: RootHashSignature=/foo/bar.roothash.p7s */
+                _cleanup_free_ char *p = NULL;
+
+                p = strdup(rvalue);
+                if (!p)
+                        return -ENOMEM;
+
+                free_and_replace(c->root_hash_sig_path, p);
+                c->root_hash_sig = mfree(c->root_hash_sig);
+                c->root_hash_sig_size = 0;
+                return 0;
+        }
+
+        if (!(value = startswith(rvalue, "base64:")))
+                return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "Failed to decode RootHashSignature=, not a path but doesn't start with 'base64:', ignoring: %s", rvalue);
+
+        /* We have a roothash signature to decode, eg: RootHashSignature=base64:012345789abcdef */
+        r = unbase64mem(value, strlen(value), &roothash_sig_decoded, &roothash_sig_decoded_size);
+        if (r < 0)
+                return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to decode RootHashSignature=, ignoring: %s", rvalue);
+
+        free_and_replace(c->root_hash_sig, roothash_sig_decoded);
+        c->root_hash_sig_size = roothash_sig_decoded_size;
+        c->root_hash_sig_path = mfree(c->root_hash_sig_path);
+
+        return 0;
+}
+
 int config_parse_exec_cpu_affinity(const char *unit,
                                    const char *filename,
                                    unsigned line,
index f0e109da3ac3f729a151d0aa03539b7f83760b52..ac3940a1b7fc092335fc6e0400020b2e4d14c9ae 100644 (file)
@@ -45,6 +45,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_sched_prio);
 CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_affinity);
 CONFIG_PARSER_PROTOTYPE(config_parse_exec_secure_bits);
 CONFIG_PARSER_PROTOTYPE(config_parse_exec_root_hash);
+CONFIG_PARSER_PROTOTYPE(config_parse_exec_root_hash_sig);
 CONFIG_PARSER_PROTOTYPE(config_parse_capability_set);
 CONFIG_PARSER_PROTOTYPE(config_parse_exec_mount_flags);
 CONFIG_PARSER_PROTOTYPE(config_parse_timer);
index a9985c56572b31d98fb0a522ca112a9b8feb1b67..785a46947895647a81e6a7678d23bd90ee8fd72f 100644 (file)
@@ -1260,6 +1260,9 @@ int setup_namespace(
                 const void *root_hash,
                 size_t root_hash_size,
                 const char *root_hash_path,
+                const void *root_hash_sig,
+                size_t root_hash_sig_size,
+                const char *root_hash_sig_path,
                 const char *root_verity,
                 DissectImageFlags dissect_image_flags,
                 char **error_path) {
@@ -1299,7 +1302,7 @@ int setup_namespace(
                 if (r < 0)
                         return log_debug_errno(r, "Failed to create loop device for root image: %m");
 
-                r = verity_metadata_load(root_image, root_hash_path, root_hash ? NULL : &root_hash_decoded, root_hash ? NULL : &root_hash_size, root_verity ? NULL : &verity_data, &hash_sig_path);
+                r = verity_metadata_load(root_image, root_hash_path, root_hash ? NULL : &root_hash_decoded, root_hash ? NULL : &root_hash_size, root_verity ? NULL : &verity_data, root_hash_sig || root_hash_sig_path ? NULL : &hash_sig_path);
                 if (r < 0)
                         return log_debug_errno(r, "Failed to load root hash: %m");
                 dissect_image_flags |= root_verity || verity_data ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0;
@@ -1308,7 +1311,7 @@ int setup_namespace(
                 if (r < 0)
                         return log_debug_errno(r, "Failed to dissect image: %m");
 
-                r = dissected_image_decrypt(dissected_image, NULL, root_hash ?: root_hash_decoded, root_hash_size, root_verity ?: verity_data, hash_sig_path, NULL, 0, dissect_image_flags, &decrypted_image);
+                r = dissected_image_decrypt(dissected_image, NULL, root_hash ?: root_hash_decoded, root_hash_size, root_verity ?: verity_data, root_hash_sig_path ?: hash_sig_path, root_hash_sig, root_hash_sig_size, dissect_image_flags, &decrypted_image);
                 if (r < 0)
                         return log_debug_errno(r, "Failed to decrypt dissected image: %m");
         }
index a687be5bfda92776dfefbf67690c5cf46f0a778d..b04b9b442ea3bddeb1b9a4855081ec77869fd091 100644 (file)
@@ -91,6 +91,9 @@ int setup_namespace(
                 const void *root_hash,
                 size_t root_hash_size,
                 const char *root_hash_path,
+                const void *root_hash_sig,
+                size_t root_hash_sig_size,
+                const char *root_hash_sig_path,
                 const char *root_verity,
                 DissectImageFlags dissected_image_flags,
                 char **error_path);
index 6cfac821468f230e65ec5bf1db9c4e32c791d01a..f2652ed9a59dafeef5a03940b338a96d066056f0 100644 (file)
@@ -1434,6 +1434,26 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                 return bus_append_byte_array(m, field, roothash_decoded, roothash_decoded_size);
         }
 
+        if (streq(field, "RootHashSignature")) {
+                _cleanup_free_ void *roothash_sig_decoded = NULL;
+                char *value;
+                size_t roothash_sig_decoded_size = 0;
+
+                /* We have the path to a roothash signature to load and decode, eg: RootHash=/foo/bar.roothash.p7s */
+                if (path_is_absolute(eq))
+                        return bus_append_string(m, "RootHashSignaturePath", eq);
+
+                if (!(value = startswith(eq, "base64:")))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to decode RootHashSignature= '%s', not a path but doesn't start with 'base64:': %m", eq);
+
+                /* We have a roothash signature to decode, eg: RootHashSignature=base64:012345789abcdef */
+                r = unbase64mem(value, strlen(value), &roothash_sig_decoded, &roothash_sig_decoded_size);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to decode RootHashSignature= '%s': %m", eq);
+
+                return bus_append_byte_array(m, field, roothash_sig_decoded, roothash_sig_decoded_size);
+        }
+
         return 0;
 }
 
index 4d793d75e20acaf703c821e634571914b60753b1..678982c61c9d5f5b74e3853c9ccffe228088e962 100644 (file)
@@ -5188,7 +5188,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
 
                         return 1;
 
-                } else if (contents[0] == SD_BUS_TYPE_BYTE && streq(name, "StandardInputData")) {
+                } else if (contents[0] == SD_BUS_TYPE_BYTE && STR_IN_SET(name, "StandardInputData", "RootHashSignature")) {
                         _cleanup_free_ char *h = NULL;
                         const void *p;
                         size_t sz;
index d0b4ec27644710d161cacc2d85e4742944c878f2..ad135e146feae336bb4d10adca87869e7ea8d0e7 100644 (file)
@@ -157,6 +157,9 @@ static void test_protect_kernel_logs(void) {
                                     NULL,
                                     NULL,
                                     0,
+                                    NULL,
+                                    NULL,
+                                    0,
                                     NULL);
                 assert_se(r == 0);
 
index ba2c2ed53b77b40c4431707b7136344af6d23b99..cbc41b7a385ef57a115f46207effc0dbb17d60f7 100644 (file)
@@ -81,6 +81,9 @@ int main(int argc, char *argv[]) {
                             NULL,
                             NULL,
                             0,
+                            NULL,
+                            NULL,
+                            0,
                             NULL);
         if (r < 0) {
                 log_error_errno(r, "Failed to set up namespace: %m");
index 492d2a033bb3d078cddb92ea065649b0969372a4..dbff9ab2cc7d6740ad625b99aaa709c77bdfe901 100644 (file)
@@ -197,6 +197,7 @@ RootDirectory=
 RootDirectoryStartOnly=
 RootImage=
 RootHash=
+RootHashSignature=
 RootVerity=
 RuntimeMaxSec=
 SELinuxContextFromNet=