Skip to content

Commit 0389f4f

Browse files
blucapoettering
authored andcommitted
core: add RootHash and RootVerity service parameters
Allow to explicitly pass root hash (explicitly or as a file) and verity device/file as unit options. Take precedence over implicit checks.
1 parent 6fe01ce commit 0389f4f

File tree

17 files changed

+262
-25
lines changed

17 files changed

+262
-25
lines changed

man/systemd.exec.xml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,43 @@
145145
<xi:include href="system-only.xml" xpointer="singular"/></listitem>
146146
</varlistentry>
147147

148+
<varlistentry>
149+
<term><varname>RootHash=</varname></term>
150+
151+
<listitem><para>Takes a data integrity (dm-verity) root hash specified in hexadecimal, or the path to a file
152+
containing a root hash in ASCII hexadecimal format. This option enables data integrity checks using dm-verity,
153+
if the used image contains the appropriate integrity data (see above) or if <varname>RootVerity=</varname> is used.
154+
The specified hash must match the root hash of integrity data, and is usually at least 256 bits (and hence 64
155+
formatted hexadecimal characters) long (in case of SHA256 for example). If this option is not specified, but
156+
the image file carries the <literal>user.verity.roothash</literal> extended file attribute (see <citerefentry
157+
project='man-pages'><refentrytitle>xattr</refentrytitle><manvolnum>7</manvolnum></citerefentry>), then the root
158+
hash is read from it, also as formatted hexadecimal characters. If the extended file attribute is not found (or
159+
is not supported by the underlying file system), but a file with the <filename>.roothash</filename> suffix is
160+
found next to the image file, bearing otherwise the same name (except if the image has the
161+
<filename>.raw</filename> suffix, in which case the root hash file must not have it in its name), the root hash
162+
is read from it and automatically used, also as formatted hexadecimal characters.</para>
163+
164+
<xi:include href="system-only.xml" xpointer="singular"/></listitem>
165+
</varlistentry>
166+
167+
<varlistentry>
168+
<term><varname>RootVerity=</varname></term>
169+
170+
<listitem><para>Takes the path to a data integrity (dm-verity) file. This option enables data integrity checks
171+
using dm-verity, if <varname>RootImage=</varname> is used and a root-hash is passed and if the used image itself
172+
does not contains the integrity data. The integrity data must be matched by the root hash. If this option is not
173+
specified, but a file with the <filename>.verity</filename> suffix is found next to the image file, bearing otherwise
174+
the same name (except if the image has the <filename>.raw</filename> suffix, in which case the verity data file must
175+
not have it in its name), the verity data is read from it and automatically used.</para>
176+
177+
<para>This option is supported only for disk images that contain a single file system, without an enveloping partition
178+
table. Images that contain a GPT partition table should instead include both root file system and matching Verity
179+
data in the same image, implementing the
180+
[Discoverable Partition Specification](https://systemd.io/DISCOVERABLE_PARTITIONS)</para>
181+
182+
<xi:include href="system-only.xml" xpointer="singular"/></listitem>
183+
</varlistentry>
184+
148185
<varlistentry>
149186
<term><varname>MountAPIVFS=</varname></term>
150187

src/core/dbus-execute.c

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,25 @@ static int property_get_log_extra_fields(
746746
return sd_bus_message_close_container(reply);
747747
}
748748

749+
static int property_get_root_hash(
750+
sd_bus *bus,
751+
const char *path,
752+
const char *interface,
753+
const char *property,
754+
sd_bus_message *reply,
755+
void *userdata,
756+
sd_bus_error *error) {
757+
758+
ExecContext *c = userdata;
759+
760+
assert(bus);
761+
assert(c);
762+
assert(property);
763+
assert(reply);
764+
765+
return sd_bus_message_append_array(reply, 'y', c->root_hash, c->root_hash_size);
766+
}
767+
749768
const sd_bus_vtable bus_exec_vtable[] = {
750769
SD_BUS_VTABLE_START(0),
751770
SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -788,6 +807,9 @@ const sd_bus_vtable bus_exec_vtable[] = {
788807
SD_BUS_PROPERTY("WorkingDirectory", "s", property_get_working_directory, 0, SD_BUS_VTABLE_PROPERTY_CONST),
789808
SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(ExecContext, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
790809
SD_BUS_PROPERTY("RootImage", "s", NULL, offsetof(ExecContext, root_image), SD_BUS_VTABLE_PROPERTY_CONST),
810+
SD_BUS_PROPERTY("RootHash", "ay", property_get_root_hash, 0, SD_BUS_VTABLE_PROPERTY_CONST),
811+
SD_BUS_PROPERTY("RootHashPath", "s", NULL, offsetof(ExecContext, root_hash_path), SD_BUS_VTABLE_PROPERTY_CONST),
812+
SD_BUS_PROPERTY("RootVerity", "s", NULL, offsetof(ExecContext, root_verity), SD_BUS_VTABLE_PROPERTY_CONST),
791813
SD_BUS_PROPERTY("OOMScoreAdjust", "i", property_get_oom_score_adjust, 0, SD_BUS_VTABLE_PROPERTY_CONST),
792814
SD_BUS_PROPERTY("CoredumpFilter", "t", property_get_coredump_filter, 0, SD_BUS_VTABLE_PROPERTY_CONST),
793815
SD_BUS_PROPERTY("Nice", "i", property_get_nice, 0, SD_BUS_VTABLE_PROPERTY_CONST),
@@ -1258,6 +1280,55 @@ int bus_exec_context_set_transient_property(
12581280
if (streq(name, "RootImage"))
12591281
return bus_set_transient_path(u, name, &c->root_image, message, flags, error);
12601282

1283+
if (streq(name, "RootHash")) {
1284+
const void *roothash_decoded;
1285+
size_t roothash_decoded_size;
1286+
1287+
r = sd_bus_message_read_array(message, 'y', &roothash_decoded, &roothash_decoded_size);
1288+
if (r < 0)
1289+
return r;
1290+
1291+
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
1292+
_cleanup_free_ char *encoded = NULL;
1293+
1294+
if (roothash_decoded_size == 0) {
1295+
c->root_hash_path = mfree(c->root_hash_path);
1296+
c->root_hash = mfree(c->root_hash);
1297+
c->root_hash_size = 0;
1298+
1299+
unit_write_settingf(u, flags, name, "RootHash=");
1300+
} else {
1301+
_cleanup_free_ void *p;
1302+
1303+
encoded = hexmem(roothash_decoded, roothash_decoded_size);
1304+
if (!encoded)
1305+
return -ENOMEM;
1306+
1307+
p = memdup(roothash_decoded, roothash_decoded_size);
1308+
if (!p)
1309+
return -ENOMEM;
1310+
1311+
free_and_replace(c->root_hash, p);
1312+
c->root_hash_size = roothash_decoded_size;
1313+
c->root_hash_path = mfree(c->root_hash_path);
1314+
1315+
unit_write_settingf(u, flags, name, "RootHash=%s", encoded);
1316+
}
1317+
}
1318+
1319+
return 1;
1320+
}
1321+
1322+
if (streq(name, "RootHashPath")) {
1323+
c->root_hash_size = 0;
1324+
c->root_hash = mfree(c->root_hash);
1325+
1326+
return bus_set_transient_path(u, "RootHash", &c->root_hash_path, message, flags, error);
1327+
}
1328+
1329+
if (streq(name, "RootVerity"))
1330+
return bus_set_transient_path(u, name, &c->root_verity, message, flags, error);
1331+
12611332
if (streq(name, "RootDirectory"))
12621333
return bus_set_transient_path(u, name, &c->root_directory, message, flags, error);
12631334

src/core/execute.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
#include "format-util.h"
5555
#include "fs-util.h"
5656
#include "glob-util.h"
57+
#include "hexdecoct.h"
5758
#include "io-util.h"
5859
#include "ioprio.h"
5960
#include "label.h"
@@ -2666,6 +2667,7 @@ static int apply_mount_namespace(
26662667
needs_sandboxing ? context->protect_home : PROTECT_HOME_NO,
26672668
needs_sandboxing ? context->protect_system : PROTECT_SYSTEM_NO,
26682669
context->mount_flags,
2670+
context->root_hash, context->root_hash_size, context->root_hash_path, context->root_verity,
26692671
DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK,
26702672
error_path);
26712673

@@ -4195,6 +4197,10 @@ void exec_context_done(ExecContext *c) {
41954197
c->working_directory = mfree(c->working_directory);
41964198
c->root_directory = mfree(c->root_directory);
41974199
c->root_image = mfree(c->root_image);
4200+
c->root_hash = mfree(c->root_hash);
4201+
c->root_hash_size = 0;
4202+
c->root_hash_path = mfree(c->root_hash_path);
4203+
c->root_verity = mfree(c->root_verity);
41984204
c->tty_path = mfree(c->tty_path);
41994205
c->syslog_identifier = mfree(c->syslog_identifier);
42004206
c->user = mfree(c->user);
@@ -4599,6 +4605,19 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
45994605
if (c->root_image)
46004606
fprintf(f, "%sRootImage: %s\n", prefix, c->root_image);
46014607

4608+
if (c->root_hash) {
4609+
_cleanup_free_ char *encoded = NULL;
4610+
encoded = hexmem(c->root_hash, c->root_hash_size);
4611+
if (encoded)
4612+
fprintf(f, "%sRootHash: %s\n", prefix, encoded);
4613+
}
4614+
4615+
if (c->root_hash_path)
4616+
fprintf(f, "%sRootHash: %s\n", prefix, c->root_hash_path);
4617+
4618+
if (c->root_verity)
4619+
fprintf(f, "%sRootVerity: %s\n", prefix, c->root_verity);
4620+
46024621
STRV_FOREACH(e, c->environment)
46034622
fprintf(f, "%sEnvironment: %s\n", prefix, *e);
46044623

src/core/execute.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,9 @@ struct ExecContext {
155155
char **unset_environment;
156156

157157
struct rlimit *rlimit[_RLIMIT_MAX];
158-
char *working_directory, *root_directory, *root_image;
158+
char *working_directory, *root_directory, *root_image, *root_verity, *root_hash_path;
159+
void *root_hash;
160+
size_t root_hash_size;
159161
bool working_directory_missing_ok:1;
160162
bool working_directory_home:1;
161163

src/core/load-fragment-gperf.gperf.m4

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ m4_define(`EXEC_CONTEXT_CONFIG_ITEMS',
2323
`$1.WorkingDirectory, config_parse_working_directory, 0, offsetof($1, exec_context)
2424
$1.RootDirectory, config_parse_unit_path_printf, true, offsetof($1, exec_context.root_directory)
2525
$1.RootImage, config_parse_unit_path_printf, true, offsetof($1, exec_context.root_image)
26+
$1.RootHash, config_parse_exec_root_hash, 0, offsetof($1, exec_context)
27+
$1.RootVerity, config_parse_unit_path_printf, true, offsetof($1, exec_context.root_verity)
2628
$1.User, config_parse_user_group_compat, 0, offsetof($1, exec_context.user)
2729
$1.Group, config_parse_user_group_compat, 0, offsetof($1, exec_context.group)
2830
$1.SupplementaryGroups, config_parse_user_group_strv_compat, 0, offsetof($1, exec_context.supplementary_groups)

src/core/load-fragment.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "errno-list.h"
3030
#include "escape.h"
3131
#include "fd-util.h"
32+
#include "fileio.h"
3233
#include "fs-util.h"
3334
#include "hexdecoct.h"
3435
#include "io-util.h"
@@ -1413,6 +1414,64 @@ int config_parse_exec_cpu_sched_prio(const char *unit,
14131414
return 0;
14141415
}
14151416

1417+
int config_parse_exec_root_hash(
1418+
const char *unit,
1419+
const char *filename,
1420+
unsigned line,
1421+
const char *section,
1422+
unsigned section_line,
1423+
const char *lvalue,
1424+
int ltype,
1425+
const char *rvalue,
1426+
void *data,
1427+
void *userdata) {
1428+
1429+
_cleanup_free_ void *roothash_decoded = NULL;
1430+
ExecContext *c = data;
1431+
size_t roothash_decoded_size = 0;
1432+
int r;
1433+
1434+
assert(data);
1435+
assert(filename);
1436+
assert(line);
1437+
assert(rvalue);
1438+
1439+
if (isempty(rvalue)) {
1440+
/* Reset if the empty string is assigned */
1441+
c->root_hash_path = mfree(c->root_hash_path);
1442+
c->root_hash = mfree(c->root_hash);
1443+
c->root_hash_size = 0;
1444+
return 0;
1445+
}
1446+
1447+
if (path_is_absolute(rvalue)) {
1448+
/* We have the path to a roothash to load and decode, eg: RootHash=/foo/bar.roothash */
1449+
_cleanup_free_ char *p = NULL;
1450+
1451+
p = strdup(rvalue);
1452+
if (!p)
1453+
return -ENOMEM;
1454+
1455+
free_and_replace(c->root_hash_path, p);
1456+
c->root_hash = mfree(c->root_hash);
1457+
c->root_hash_size = 0;
1458+
return 0;
1459+
}
1460+
1461+
/* We have a roothash to decode, eg: RootHash=012345789abcdef */
1462+
r = unhexmem(rvalue, strlen(rvalue), &roothash_decoded, &roothash_decoded_size);
1463+
if (r < 0)
1464+
return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to decode RootHash=, ignoring: %s", rvalue);
1465+
if (roothash_decoded_size < sizeof(sd_id128_t))
1466+
return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "RootHash= is too short, ignoring: %s", rvalue);
1467+
1468+
free_and_replace(c->root_hash, roothash_decoded);
1469+
c->root_hash_size = roothash_decoded_size;
1470+
c->root_hash_path = mfree(c->root_hash_path);
1471+
1472+
return 0;
1473+
}
1474+
14161475
int config_parse_exec_cpu_affinity(const char *unit,
14171476
const char *filename,
14181477
unsigned line,

src/core/load-fragment.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_sched_policy);
4444
CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_sched_prio);
4545
CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_affinity);
4646
CONFIG_PARSER_PROTOTYPE(config_parse_exec_secure_bits);
47+
CONFIG_PARSER_PROTOTYPE(config_parse_exec_root_hash);
4748
CONFIG_PARSER_PROTOTYPE(config_parse_capability_set);
4849
CONFIG_PARSER_PROTOTYPE(config_parse_exec_mount_flags);
4950
CONFIG_PARSER_PROTOTYPE(config_parse_timer);

src/core/namespace.c

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,16 +1257,20 @@ int setup_namespace(
12571257
ProtectHome protect_home,
12581258
ProtectSystem protect_system,
12591259
unsigned long mount_flags,
1260+
const void *root_hash,
1261+
size_t root_hash_size,
1262+
const char *root_hash_path,
1263+
const char *root_verity,
12601264
DissectImageFlags dissect_image_flags,
12611265
char **error_path) {
12621266

12631267
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
12641268
_cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
12651269
_cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
1266-
_cleanup_free_ void *root_hash = NULL;
1270+
_cleanup_free_ void *root_hash_decoded = NULL;
12671271
_cleanup_free_ char *verity_data = NULL;
12681272
MountEntry *m = NULL, *mounts = NULL;
1269-
size_t n_mounts, root_hash_size = 0;
1273+
size_t n_mounts;
12701274
bool require_prefix = false;
12711275
const char *root;
12721276
int r = 0;
@@ -1295,16 +1299,16 @@ int setup_namespace(
12951299
if (r < 0)
12961300
return log_debug_errno(r, "Failed to create loop device for root image: %m");
12971301

1298-
r = verity_metadata_load(root_image, &root_hash, &root_hash_size, &verity_data);
1302+
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);
12991303
if (r < 0)
13001304
return log_debug_errno(r, "Failed to load root hash: %m");
1301-
dissect_image_flags |= verity_data ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0;
1305+
dissect_image_flags |= root_verity || verity_data ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0;
13021306

1303-
r = dissect_image(loop_device->fd, root_hash, root_hash_size, verity_data, dissect_image_flags, &dissected_image);
1307+
r = dissect_image(loop_device->fd, root_hash ?: root_hash_decoded, root_hash_size, root_verity ?: verity_data, dissect_image_flags, &dissected_image);
13041308
if (r < 0)
13051309
return log_debug_errno(r, "Failed to dissect image: %m");
13061310

1307-
r = dissected_image_decrypt(dissected_image, NULL, root_hash, root_hash_size, verity_data, dissect_image_flags, &decrypted_image);
1311+
r = dissected_image_decrypt(dissected_image, NULL, root_hash ?: root_hash_decoded, root_hash_size, root_verity ?: verity_data, dissect_image_flags, &decrypted_image);
13081312
if (r < 0)
13091313
return log_debug_errno(r, "Failed to decrypt dissected image: %m");
13101314
}

src/core/namespace.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ int setup_namespace(
8888
ProtectHome protect_home,
8989
ProtectSystem protect_system,
9090
unsigned long mount_flags,
91+
const void *root_hash,
92+
size_t root_hash_size,
93+
const char *root_hash_path,
94+
const char *root_verity,
9195
DissectImageFlags dissected_image_flags,
9296
char **error_path);
9397

src/dissect/dissect.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ static int run(int argc, char *argv[]) {
201201
if (r < 0)
202202
return log_error_errno(r, "Failed to set up loopback device: %m");
203203

204-
r = verity_metadata_load(arg_image, arg_root_hash ? NULL : &arg_root_hash, &arg_root_hash_size,
204+
r = verity_metadata_load(arg_image, NULL, arg_root_hash ? NULL : &arg_root_hash, &arg_root_hash_size,
205205
arg_verity_data ? NULL : &arg_verity_data);
206206
if (r < 0)
207207
return log_error_errno(r, "Failed to read verity artefacts for %s: %m", arg_image);

0 commit comments

Comments
 (0)