X Tutup
Skip to content

Commit 8a129c8

Browse files
committed
os-release: add new PORTABLE_PREFIXES= field for declaring valid portable service match prefixes
1 parent 60c5f70 commit 8a129c8

File tree

5 files changed

+153
-43
lines changed

5 files changed

+153
-43
lines changed

docs/PORTABLE_SERVICES.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,20 @@ image. To facilitate 3 and 4 you also need to include a boot loader in the
247247
image. As mentioned, `mkosi -b` takes care of all of that for you, but any
248248
other image generator should work too.
249249

250+
The
251+
[os-release(5)](https://www.freedesktop.org/software/systemd/man/os-release.html)
252+
file may optionally be extended with a `PORTABLE_PREFIXES=` field listing all
253+
supported portable service prefixes for the image (see above). This is useful
254+
for informational purposes (as it allows recognizing portable service images
255+
from their contents as such), but is also useful to protect the image from
256+
being used under a wrong name and prefix. This is particularly relevant if the
257+
images are cryptographically authenticated (via Verity or a similar mechanism)
258+
as this way the (not necessarily authenticated) image file name can be
259+
validated against the (authenticated) image contents. If the field is not
260+
specified the image will work fine, but is not necessarily recognizable as
261+
portable service image, and any set of units included in the image may be
262+
attached, there are no restrictions enforced.
263+
250264
## Extension Images
251265

252266
Portable services can be delivered as one or multiple images that extend the base

man/os-release.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,17 @@
419419
regular systems and to portable service environments, but not to initrd
420420
environments.</para></listitem>
421421
</varlistentry>
422+
423+
<varlistentry>
424+
<term><varname>PORTABLE_PREFIXES=</varname></term>
425+
<listitem><para>Takes a space-separated list of one or more valid prefix match strings for the
426+
<ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services</ulink> logic. This field
427+
serves two purposes: it's informational, identifying portable service images as such (and thus
428+
allowing them to be distinguished from other OS images, such as bootable system images); whenever a
429+
portable service image is attached the specified or implied portable service prefix is checked
430+
against this list, to enforce restrictions how images may be attached to a
431+
system.</para></listitem>
432+
</varlistentry>
422433
</variablelist>
423434
</refsect2>
424435

src/portable/portable.c

Lines changed: 126 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "discover-image.h"
1414
#include "dissect-image.h"
1515
#include "env-file.h"
16+
#include "env-util.h"
1617
#include "errno-list.h"
1718
#include "escape.h"
1819
#include "extension-release.h"
@@ -509,20 +510,20 @@ static int extract_image_and_extensions(
509510
OrderedHashmap **ret_extension_images,
510511
PortableMetadata **ret_os_release,
511512
Hashmap **ret_unit_files,
513+
char ***ret_valid_prefixes,
512514
sd_bus_error *error) {
513515

514516
_cleanup_free_ char *id = NULL, *version_id = NULL, *sysext_level = NULL;
515517
_cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
516518
_cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL;
517519
_cleanup_hashmap_free_ Hashmap *unit_files = NULL;
520+
_cleanup_strv_free_ char **valid_prefixes = NULL;
518521
_cleanup_(image_unrefp) Image *image = NULL;
519522
Image *ext;
520523
int r;
521524

522525
assert(name_or_path);
523526
assert(matches);
524-
assert(ret_image);
525-
assert(ret_extension_images);
526527

527528
r = image_find_harder(IMAGE_PORTABLE, name_or_path, NULL, &image);
528529
if (r < 0)
@@ -553,10 +554,12 @@ static int extract_image_and_extensions(
553554
if (r < 0)
554555
return r;
555556

556-
/* If we are layering extension images on top of a runtime image, check that the os-release and extension-release metadata
557-
* match, otherwise reject it immediately as invalid, or it will fail when the units are started. */
558-
if (validate_sysext) {
557+
/* If we are layering extension images on top of a runtime image, check that the os-release and
558+
* extension-release metadata match, otherwise reject it immediately as invalid, or it will fail when
559+
* the units are started. Also, collect valid portable prefixes if caller requested that. */
560+
if (validate_sysext || ret_valid_prefixes) {
559561
_cleanup_fclose_ FILE *f = NULL;
562+
_cleanup_free_ char *prefixes = NULL;
560563

561564
r = take_fdopen_unlocked(&os_release->fd, "r", &f);
562565
if (r < 0)
@@ -565,16 +568,24 @@ static int extract_image_and_extensions(
565568
r = parse_env_file(f, os_release->name,
566569
"ID", &id,
567570
"VERSION_ID", &version_id,
568-
"SYSEXT_LEVEL", &sysext_level);
571+
"SYSEXT_LEVEL", &sysext_level,
572+
"PORTABLE_PREFIXES", &prefixes);
569573
if (r < 0)
570574
return r;
575+
576+
if (prefixes) {
577+
valid_prefixes = strv_split(prefixes, WHITESPACE);
578+
if (!valid_prefixes)
579+
return -ENOMEM;
580+
}
571581
}
572582

573583
ORDERED_HASHMAP_FOREACH(ext, extension_images) {
574584
_cleanup_(portable_metadata_unrefp) PortableMetadata *extension_release_meta = NULL;
575585
_cleanup_hashmap_free_ Hashmap *extra_unit_files = NULL;
576586
_cleanup_strv_free_ char **extension_release = NULL;
577587
_cleanup_fclose_ FILE *f = NULL;
588+
const char *e;
578589

579590
r = portable_extract_by_path(ext->path, /* path_is_extension= */ true, matches, &extension_release_meta, &extra_unit_files, error);
580591
if (r < 0)
@@ -584,7 +595,7 @@ static int extract_image_and_extensions(
584595
if (r < 0)
585596
return r;
586597

587-
if (!validate_sysext)
598+
if (!validate_sysext && !ret_valid_prefixes)
588599
continue;
589600

590601
r = take_fdopen_unlocked(&extension_release_meta->fd, "r", &f);
@@ -595,19 +606,40 @@ static int extract_image_and_extensions(
595606
if (r < 0)
596607
return r;
597608

598-
r = extension_release_validate(ext->path, id, version_id, sysext_level, "portable", extension_release);
599-
if (r == 0)
600-
return sd_bus_error_set_errnof(error, SYNTHETIC_ERRNO(ESTALE), "Image %s extension-release metadata does not match the root's", ext->path);
601-
if (r < 0)
602-
return sd_bus_error_set_errnof(error, r, "Failed to compare image %s extension-release metadata with the root's os-release: %m", ext->path);
609+
if (validate_sysext) {
610+
r = extension_release_validate(ext->path, id, version_id, sysext_level, "portable", extension_release);
611+
if (r == 0)
612+
return sd_bus_error_set_errnof(error, SYNTHETIC_ERRNO(ESTALE), "Image %s extension-release metadata does not match the root's", ext->path);
613+
if (r < 0)
614+
return sd_bus_error_set_errnof(error, r, "Failed to compare image %s extension-release metadata with the root's os-release: %m", ext->path);
615+
}
616+
617+
e = strv_env_pairs_get(extension_release, "PORTABLE_PREFIXES");
618+
if (e) {
619+
_cleanup_strv_free_ char **l = NULL;
620+
621+
l = strv_split(e, WHITESPACE);
622+
if (!l)
623+
return -ENOMEM;
624+
625+
r = strv_extend_strv(&valid_prefixes, l, true);
626+
if (r < 0)
627+
return r;
628+
}
603629
}
604630

605-
*ret_image = TAKE_PTR(image);
606-
*ret_extension_images = TAKE_PTR(extension_images);
631+
strv_sort(valid_prefixes);
632+
633+
if (ret_image)
634+
*ret_image = TAKE_PTR(image);
635+
if (ret_extension_images)
636+
*ret_extension_images = TAKE_PTR(extension_images);
607637
if (ret_os_release)
608638
*ret_os_release = TAKE_PTR(os_release);
609639
if (ret_unit_files)
610640
*ret_unit_files = TAKE_PTR(unit_files);
641+
if (ret_valid_prefixes)
642+
*ret_valid_prefixes = TAKE_PTR(valid_prefixes);
611643

612644
return 0;
613645
}
@@ -618,23 +650,29 @@ int portable_extract(
618650
char **extension_image_paths,
619651
PortableMetadata **ret_os_release,
620652
Hashmap **ret_unit_files,
653+
char ***ret_valid_prefixes,
621654
sd_bus_error *error) {
622655

623656
_cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
624657
_cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL;
625658
_cleanup_hashmap_free_ Hashmap *unit_files = NULL;
659+
_cleanup_(strv_freep) char **valid_prefixes = NULL;
626660
_cleanup_(image_unrefp) Image *image = NULL;
627661
int r;
628662

629-
r = extract_image_and_extensions(name_or_path,
630-
matches,
631-
extension_image_paths,
632-
/* validate_sysext= */ false,
633-
&image,
634-
&extension_images,
635-
&os_release,
636-
&unit_files,
637-
error);
663+
assert(name_or_path);
664+
665+
r = extract_image_and_extensions(
666+
name_or_path,
667+
matches,
668+
extension_image_paths,
669+
/* validate_sysext= */ false,
670+
&image,
671+
&extension_images,
672+
&os_release,
673+
&unit_files,
674+
ret_valid_prefixes ? &valid_prefixes : NULL,
675+
error);
638676
if (r < 0)
639677
return r;
640678

@@ -651,8 +689,12 @@ int portable_extract(
651689
isempty(extensions) ? "" : extensions);
652690
}
653691

654-
*ret_os_release = TAKE_PTR(os_release);
655-
*ret_unit_files = TAKE_PTR(unit_files);
692+
if (ret_os_release)
693+
*ret_os_release = TAKE_PTR(os_release);
694+
if (ret_unit_files)
695+
*ret_unit_files = TAKE_PTR(unit_files);
696+
if (ret_valid_prefixes)
697+
*ret_valid_prefixes = TAKE_PTR(valid_prefixes);
656698

657699
return 0;
658700
}
@@ -1211,6 +1253,18 @@ static int install_image_and_extensions_symlinks(
12111253
return 0;
12121254
}
12131255

1256+
static bool prefix_matches_compatible(char **matches, char **valid_prefixes) {
1257+
char **m;
1258+
1259+
/* Checks if all 'matches' are included in the list of 'valid_prefixes' */
1260+
1261+
STRV_FOREACH(m, matches)
1262+
if (!strv_contains(valid_prefixes, *m))
1263+
return false;
1264+
1265+
return true;
1266+
}
1267+
12141268
int portable_attach(
12151269
sd_bus *bus,
12161270
const char *name_or_path,
@@ -1225,33 +1279,63 @@ int portable_attach(
12251279
_cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL;
12261280
_cleanup_hashmap_free_ Hashmap *unit_files = NULL;
12271281
_cleanup_(lookup_paths_free) LookupPaths paths = {};
1282+
_cleanup_strv_free_ char **valid_prefixes = NULL;
12281283
_cleanup_(image_unrefp) Image *image = NULL;
12291284
PortableMetadata *item;
12301285
int r;
12311286

1232-
r = extract_image_and_extensions(name_or_path,
1233-
matches,
1234-
extension_image_paths,
1235-
/* validate_sysext= */ true,
1236-
&image,
1237-
&extension_images,
1238-
/* os_release= */ NULL,
1239-
&unit_files,
1240-
error);
1287+
r = extract_image_and_extensions(
1288+
name_or_path,
1289+
matches,
1290+
extension_image_paths,
1291+
/* validate_sysext= */ true,
1292+
&image,
1293+
&extension_images,
1294+
/* os_release= */ NULL,
1295+
&unit_files,
1296+
&valid_prefixes,
1297+
error);
12411298
if (r < 0)
12421299
return r;
12431300

1301+
if (valid_prefixes && !prefix_matches_compatible(matches, valid_prefixes)) {
1302+
_cleanup_free_ char *matches_joined = NULL, *extensions_joined = NULL, *valid_prefixes_joined = NULL;
1303+
1304+
matches_joined = strv_join(matches, "', '");
1305+
if (!matches_joined)
1306+
return -ENOMEM;
1307+
1308+
extensions_joined = strv_join(extension_image_paths, ", ");
1309+
if (!extensions_joined)
1310+
return -ENOMEM;
1311+
1312+
valid_prefixes_joined = strv_join(valid_prefixes, ", ");
1313+
if (!valid_prefixes_joined)
1314+
return -ENOMEM;
1315+
1316+
return sd_bus_error_setf(
1317+
error,
1318+
SD_BUS_ERROR_INVALID_ARGS,
1319+
"Selected matches '%s' are not compatible with portable service image '%s%s%s', refusing. (Acceptable prefix matches are: %s)",
1320+
matches_joined,
1321+
image->path,
1322+
isempty(extensions_joined) ? "" : "' or any of its extensions '",
1323+
strempty(extensions_joined),
1324+
valid_prefixes_joined);
1325+
}
1326+
12441327
if (hashmap_isempty(unit_files)) {
1245-
_cleanup_free_ char *extensions = strv_join(extension_image_paths, ", ");
1246-
if (!extensions)
1328+
_cleanup_free_ char *extensions_joined = strv_join(extension_image_paths, ", ");
1329+
if (!extensions_joined)
12471330
return -ENOMEM;
12481331

1249-
return sd_bus_error_setf(error,
1250-
SD_BUS_ERROR_INVALID_ARGS,
1251-
"Couldn't find any matching unit files in image '%s%s%s', refusing.",
1252-
image->path,
1253-
isempty(extensions) ? "" : "' or any of its extensions '",
1254-
isempty(extensions) ? "" : extensions);
1332+
return sd_bus_error_setf(
1333+
error,
1334+
SD_BUS_ERROR_INVALID_ARGS,
1335+
"Couldn't find any matching unit files in image '%s%s%s', refusing.",
1336+
image->path,
1337+
isempty(extensions_joined) ? "" : "' or any of its extensions '",
1338+
strempty(extensions_joined));
12551339
}
12561340

12571341
r = lookup_paths_init(&paths, UNIT_FILE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, NULL);

src/portable/portable.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(PortableMetadata*, portable_metadata_unref);
6565

6666
int portable_metadata_hashmap_to_sorted_array(Hashmap *unit_files, PortableMetadata ***ret);
6767

68-
int portable_extract(const char *image, char **matches, char **extension_image_paths, PortableMetadata **ret_os_release, Hashmap **ret_unit_files, sd_bus_error *error);
68+
int portable_extract(const char *image, char **matches, char **extension_image_paths, PortableMetadata **ret_os_release, Hashmap **ret_unit_files, char ***ret_valid_prefixes, sd_bus_error *error);
6969

7070
int portable_attach(sd_bus *bus, const char *name_or_path, char **matches, const char *profile, char **extension_images, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error);
7171
int portable_detach(sd_bus *bus, const char *name_or_path, char **extension_image_paths, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error);

src/portable/portabled-image-bus.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ int bus_image_common_get_metadata(
162162
extension_images,
163163
&os_release,
164164
&unit_files,
165+
NULL,
165166
error);
166167
if (r < 0)
167168
return r;

0 commit comments

Comments
 (0)
X Tutup