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:
Luca Boccassi 2020-06-08 14:02:55 +01:00
parent c2923fdcd7
commit d4d55b0d13
14 changed files with 200 additions and 7 deletions

View File

@ -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>

View 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);

View 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);

View 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;

View 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)

View 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,

View 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);

View 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");
}

View 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);

View 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;
}

View 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;

View File

@ -157,6 +157,9 @@ static void test_protect_kernel_logs(void) {
NULL,
NULL,
0,
NULL,
NULL,
0,
NULL);
assert_se(r == 0);

View 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");

View File

@ -197,6 +197,7 @@ RootDirectory=
RootDirectoryStartOnly=
RootImage=
RootHash=
RootHashSignature=
RootVerity=
RuntimeMaxSec=
SELinuxContextFromNet=