core: add RootHashSignature service parameter
Allow to explicitly pass root hash signature as a unit option. Takes precedence over implicit checks.
This commit is contained in:
parent
c2923fdcd7
commit
d4d55b0d13
|
@ -164,6 +164,20 @@
|
|||
<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>
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -157,6 +157,9 @@ static void test_protect_kernel_logs(void) {
|
|||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
NULL);
|
||||
assert_se(r == 0);
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -197,6 +197,7 @@ RootDirectory=
|
|||
RootDirectoryStartOnly=
|
||||
RootImage=
|
||||
RootHash=
|
||||
RootHashSignature=
|
||||
RootVerity=
|
||||
RuntimeMaxSec=
|
||||
SELinuxContextFromNet=
|
||||
|
|
Loading…
Reference in New Issue