<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>
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),
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),
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);
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);
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);
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);
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;
$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)
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,
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);
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) {
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;
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");
}
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);
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;
}
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;
NULL,
NULL,
0,
+ NULL,
+ NULL,
+ 0,
NULL);
assert_se(r == 0);
NULL,
NULL,
0,
+ NULL,
+ NULL,
+ 0,
NULL);
if (r < 0) {
log_error_errno(r, "Failed to set up namespace: %m");
RootDirectoryStartOnly=
RootImage=
RootHash=
+RootHashSignature=
RootVerity=
RuntimeMaxSec=
SELinuxContextFromNet=