]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/image-policy.c
cryptenroll: allow to use a public key on a token
[thirdparty/systemd.git] / src / shared / image-policy.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "alloc-util.h"
4 #include "extract-word.h"
5 #include "image-policy.h"
6 #include "logarithm.h"
7 #include "sort-util.h"
8 #include "string-util.h"
9 #include "strv.h"
10
11 /* Rationale for the chosen syntax:
12 *
13 * → one line, so that it can be reasonably added to a shell command line, for example via `systemd-dissect
14 * --image-policy=…` or to the kernel command line via `systemd.image_policy=`.
15 *
16 * → no use of "," or ";" as separators, so that it can be included in mount/fstab-style option strings and
17 * doesn't require escaping. Instead, separators are ":", "=", "+" which should be fine both in shell
18 * command lines and in mount/fstab style option strings.
19 */
20
21 static int partition_policy_compare(const PartitionPolicy *a, const PartitionPolicy *b) {
22 return CMP(ASSERT_PTR(a)->designator, ASSERT_PTR(b)->designator);
23 }
24
25 static const PartitionPolicy* image_policy_bsearch(const ImagePolicy *policy, PartitionDesignator designator) {
26 if (!policy)
27 return NULL;
28
29 return typesafe_bsearch(
30 &(const PartitionPolicy) { .designator = designator },
31 ASSERT_PTR(policy)->policies,
32 ASSERT_PTR(policy)->n_policies,
33 partition_policy_compare);
34 }
35
36 PartitionPolicyFlags partition_policy_flags_extend(PartitionPolicyFlags flags) {
37 /* If some parts of a flags field are left unspecified, let's fill in all options. */
38
39 /* If no protection flag is set, then this means all are set */
40 if ((flags & _PARTITION_POLICY_USE_MASK) == 0)
41 flags |= PARTITION_POLICY_OPEN;
42
43 /* If the gpt flags bits are not specified, set both options for each */
44 if ((flags & _PARTITION_POLICY_READ_ONLY_MASK) == 0)
45 flags |= PARTITION_POLICY_READ_ONLY_ON|PARTITION_POLICY_READ_ONLY_OFF;
46
47 if ((flags & _PARTITION_POLICY_GROWFS_MASK) == 0)
48 flags |= PARTITION_POLICY_GROWFS_ON|PARTITION_POLICY_GROWFS_OFF;
49
50 return flags;
51 }
52
53 static PartitionPolicyFlags partition_policy_normalized_flags(const PartitionPolicy *policy) {
54 PartitionPolicyFlags flags = ASSERT_PTR(policy)->flags;
55
56 /* This normalizes the per-partition policy flags. This means if the user left some things
57 * unspecified, we'll fill in the appropriate "dontcare" policy instead. We'll also mask out bits
58 * that do not make any sense for specific partition types. */
59
60 flags = partition_policy_flags_extend(flags);
61
62 /* If this is a verity or verity signature designator, then mask off all protection bits, this after
63 * all needs no protection, because it *is* the protection */
64 if (partition_verity_to_data(policy->designator) >= 0 ||
65 partition_verity_sig_to_data(policy->designator) >= 0)
66 flags &= ~(PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED);
67
68 /* if this designator has no verity concept, then mask off verity protection flags */
69 if (partition_verity_of(policy->designator) < 0)
70 flags &= ~(PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED);
71
72 /* If the partition must be absent, then the gpt flags don't matter */
73 if ((flags & _PARTITION_POLICY_USE_MASK) == PARTITION_POLICY_ABSENT)
74 flags &= ~(_PARTITION_POLICY_READ_ONLY_MASK|_PARTITION_POLICY_GROWFS_MASK);
75
76 return flags;
77 }
78
79 PartitionPolicyFlags image_policy_get(const ImagePolicy *policy, PartitionDesignator designator) {
80 PartitionDesignator data_designator = _PARTITION_DESIGNATOR_INVALID;
81 const PartitionPolicy *pp;
82
83 /* No policy means: everything may be used in any mode */
84 if (!policy)
85 return partition_policy_normalized_flags(
86 &(const PartitionPolicy) {
87 .flags = PARTITION_POLICY_OPEN,
88 .designator = designator,
89 });
90
91 pp = image_policy_bsearch(policy, designator);
92 if (pp)
93 return partition_policy_normalized_flags(pp);
94
95 /* Hmm, so this didn't work, then let's see if we can derive some policy from the underlying data
96 * partition in case of verity/signature partitions */
97
98 data_designator = partition_verity_to_data(designator);
99 if (data_designator >= 0) {
100 PartitionPolicyFlags data_flags;
101
102 /* So we are asked for the policy for a verity partition, and there's no explicit policy for
103 * that case. Let's synthesize a policy from the protection setting for the underlying data
104 * partition. */
105
106 data_flags = image_policy_get(policy, data_designator);
107 if (data_flags < 0)
108 return data_flags;
109
110 /* We need verity if verity or verity with sig is requested */
111 if (!(data_flags & (PARTITION_POLICY_SIGNED|PARTITION_POLICY_VERITY)))
112 return _PARTITION_POLICY_FLAGS_INVALID;
113
114 /* If the data partition may be unused or absent, then the verity partition may too. Also, inherit the partition flags policy */
115 return partition_policy_normalized_flags(
116 &(const PartitionPolicy) {
117 .flags = PARTITION_POLICY_UNPROTECTED | (data_flags & (PARTITION_POLICY_UNUSED|PARTITION_POLICY_ABSENT)) |
118 (data_flags & _PARTITION_POLICY_PFLAGS_MASK),
119 .designator = designator,
120 });
121 }
122
123 data_designator = partition_verity_sig_to_data(designator);
124 if (data_designator >= 0) {
125 PartitionPolicyFlags data_flags;
126
127 /* Similar case as for verity partitions, but slightly more strict rules */
128
129 data_flags = image_policy_get(policy, data_designator);
130 if (data_flags < 0)
131 return data_flags;
132
133 if (!(data_flags & PARTITION_POLICY_SIGNED))
134 return _PARTITION_POLICY_FLAGS_INVALID;
135
136 return partition_policy_normalized_flags(
137 &(const PartitionPolicy) {
138 .flags = PARTITION_POLICY_UNPROTECTED | (data_flags & (PARTITION_POLICY_UNUSED|PARTITION_POLICY_ABSENT)) |
139 (data_flags & _PARTITION_POLICY_PFLAGS_MASK),
140 .designator = designator,
141 });
142 }
143
144 return _PARTITION_POLICY_FLAGS_INVALID; /* got nothing */
145 }
146
147 PartitionPolicyFlags image_policy_get_exhaustively(const ImagePolicy *policy, PartitionDesignator designator) {
148 PartitionPolicyFlags flags;
149
150 /* This is just like image_policy_get() but whenever there is no policy for a specific designator, we
151 * return the default policy. */
152
153 flags = image_policy_get(policy, designator);
154 if (flags < 0)
155 return partition_policy_normalized_flags(
156 &(const PartitionPolicy) {
157 .flags = image_policy_default(policy),
158 .designator = designator,
159 });
160
161 return flags;
162 }
163
164 static PartitionPolicyFlags policy_flag_from_string_one(const char *s) {
165 assert(s);
166
167 /* This is a bitmask (i.e. not dense), hence we don't use the "string-table.h" stuff here. */
168
169 if (streq(s, "verity"))
170 return PARTITION_POLICY_VERITY;
171 if (streq(s, "signed"))
172 return PARTITION_POLICY_SIGNED;
173 if (streq(s, "encrypted"))
174 return PARTITION_POLICY_ENCRYPTED;
175 if (streq(s, "unprotected"))
176 return PARTITION_POLICY_UNPROTECTED;
177 if (streq(s, "unused"))
178 return PARTITION_POLICY_UNUSED;
179 if (streq(s, "absent"))
180 return PARTITION_POLICY_ABSENT;
181 if (streq(s, "open")) /* shortcut alias */
182 return PARTITION_POLICY_OPEN;
183 if (streq(s, "ignore")) /* ditto */
184 return PARTITION_POLICY_IGNORE;
185 if (streq(s, "read-only-on"))
186 return PARTITION_POLICY_READ_ONLY_ON;
187 if (streq(s, "read-only-off"))
188 return PARTITION_POLICY_READ_ONLY_OFF;
189 if (streq(s, "growfs-on"))
190 return PARTITION_POLICY_GROWFS_ON;
191 if (streq(s, "growfs-off"))
192 return PARTITION_POLICY_GROWFS_OFF;
193
194 return _PARTITION_POLICY_FLAGS_INVALID;
195 }
196
197 PartitionPolicyFlags partition_policy_flags_from_string(const char *s) {
198 PartitionPolicyFlags flags = 0;
199 int r;
200
201 assert(s);
202
203 if (empty_or_dash(s))
204 return 0;
205
206 for (;;) {
207 _cleanup_free_ char *f = NULL;
208 PartitionPolicyFlags ff;
209
210 r = extract_first_word(&s, &f, "+", EXTRACT_DONT_COALESCE_SEPARATORS);
211 if (r < 0)
212 return r;
213 if (r == 0)
214 break;
215
216 ff = policy_flag_from_string_one(strstrip(f));
217 if (ff < 0)
218 return -EBADRQC; /* recognizable error */
219
220 flags |= ff;
221 }
222
223 return flags;
224 }
225
226 static ImagePolicy* image_policy_new(size_t n_policies) {
227 ImagePolicy *p;
228
229 if (n_policies > (SIZE_MAX - offsetof(ImagePolicy, policies)) / sizeof(PartitionPolicy)) /* overflow check */
230 return NULL;
231
232 p = malloc(offsetof(ImagePolicy, policies) + sizeof(PartitionPolicy) * n_policies);
233 if (!p)
234 return NULL;
235
236 *p = (ImagePolicy) {
237 .default_flags = PARTITION_POLICY_IGNORE,
238 };
239 return p;
240 }
241
242 int image_policy_from_string(const char *s, ImagePolicy **ret) {
243 _cleanup_free_ ImagePolicy *p = NULL;
244 uint64_t dmask = 0;
245 ImagePolicy *t;
246 PartitionPolicyFlags symbolic_policy;
247 int r;
248
249 assert(s);
250 assert_cc(sizeof(dmask) * 8 >= _PARTITION_DESIGNATOR_MAX);
251
252 /* Recognizable errors:
253 *
254 * ENOTUNIQ → Two or more rules for the same partition
255 * EBADSLT → Unknown partition designator
256 * EBADRQC → Unknown policy flags
257 */
258
259 /* First, let's handle "symbolic" policies, i.e. "-", "*", "~" */
260 if (empty_or_dash(s))
261 /* ignore policy: everything may exist, but nothing used */
262 symbolic_policy = PARTITION_POLICY_IGNORE;
263 else if (streq(s, "*"))
264 /* allow policy: everything is allowed */
265 symbolic_policy = PARTITION_POLICY_OPEN;
266 else if (streq(s, "~"))
267 /* deny policy: nothing may exist */
268 symbolic_policy = PARTITION_POLICY_ABSENT;
269 else
270 symbolic_policy = _PARTITION_POLICY_FLAGS_INVALID;
271
272 if (symbolic_policy >= 0) {
273 if (!ret)
274 return 0;
275
276 p = image_policy_new(0);
277 if (!p)
278 return -ENOMEM;
279
280 p->default_flags = symbolic_policy;
281 *ret = TAKE_PTR(p);
282 return 0;
283 }
284
285 /* Allocate the policy at maximum size, i.e. for all designators. We might overshoot a bit, but the
286 * items are cheap, and we can return unused space to libc once we know we don't need it */
287 p = image_policy_new(_PARTITION_DESIGNATOR_MAX);
288 if (!p)
289 return -ENOMEM;
290
291 const char *q = s;
292 bool default_specified = false;
293 for (;;) {
294 _cleanup_free_ char *e = NULL, *d = NULL;
295 PartitionDesignator designator;
296 PartitionPolicyFlags flags;
297 char *f, *ds, *fs;
298
299 r = extract_first_word(&q, &e, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
300 if (r < 0)
301 return r;
302 if (r == 0)
303 break;
304
305 f = e;
306 r = extract_first_word((const char**) &f, &d, "=", EXTRACT_DONT_COALESCE_SEPARATORS);
307 if (r < 0)
308 return r;
309 if (r == 0)
310 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Expected designator name followed by '='; got instead: %s", e);
311 if (!f) /* no separator? */
312 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Missing '=' in policy expression: %s", e);
313
314 ds = strstrip(d);
315 if (isempty(ds)) {
316 /* Not partition name? then it's the default policy */
317 if (default_specified)
318 return log_debug_errno(SYNTHETIC_ERRNO(ENOTUNIQ), "Default partition policy flags specified more than once.");
319
320 designator = _PARTITION_DESIGNATOR_INVALID;
321 default_specified = true;
322 } else {
323 designator = partition_designator_from_string(ds);
324 if (designator < 0)
325 return log_debug_errno(SYNTHETIC_ERRNO(EBADSLT), "Unknown partition designator: %s", ds); /* recognizable error */
326 if (dmask & (UINT64_C(1) << designator))
327 return log_debug_errno(SYNTHETIC_ERRNO(ENOTUNIQ), "Partition designator specified more than once: %s", ds);
328 dmask |= UINT64_C(1) << designator;
329 }
330
331 fs = strstrip(f);
332 flags = partition_policy_flags_from_string(fs);
333 if (flags == -EBADRQC)
334 return log_debug_errno(flags, "Unknown partition policy flag: %s", fs);
335 if (flags < 0)
336 return log_debug_errno(flags, "Failed to parse partition policy flags '%s': %m", fs);
337
338 if (designator < 0)
339 p->default_flags = flags;
340 else {
341 p->policies[p->n_policies++] = (PartitionPolicy) {
342 .designator = designator,
343 .flags = flags,
344 };
345 }
346 };
347
348 assert(p->n_policies <= _PARTITION_DESIGNATOR_MAX);
349
350 /* Return unused space to libc */
351 t = realloc(p, offsetof(ImagePolicy, policies) + sizeof(PartitionPolicy) * p->n_policies);
352 if (t)
353 p = t;
354
355 typesafe_qsort(p->policies, p->n_policies, partition_policy_compare);
356
357 if (ret)
358 *ret = TAKE_PTR(p);
359
360 return 0;
361 }
362
363 int partition_policy_flags_to_string(PartitionPolicyFlags flags, bool simplify, char **ret) {
364 _cleanup_free_ char *buf = NULL;
365 const char *l[CONST_LOG2U(_PARTITION_POLICY_MASK) + 1]; /* one string per known flag at most */
366 size_t m = 0;
367
368 assert(ret);
369
370 if (flags < 0)
371 return -EINVAL;
372
373 /* If 'simplify' is false we'll output the precise value of every single flag.
374 *
375 * If 'simplify' is true we'll try to make the output shorter, by doing the following:
376 *
377 * → we'll spell the long form "verity+signed+encrypted+unprotected+unused+absent" via its
378 * equivalent shortcut form "open" (which we happily parse btw, see above)
379 *
380 * → we'll spell the long form "unused+absent" via its shortcut "ignore" (which we are also happy
381 * to parse)
382 *
383 * → if the read-only/growfs policy flags are both set, we suppress them. this thus removes the
384 * distinction between "user explicitly declared don't care" and "we implied don't care because
385 * user didn't say anything".
386 *
387 * net result: the resulting string is shorter, but the effective policy declared that way will have
388 * the same results as the long form. */
389
390 if (simplify && (flags & _PARTITION_POLICY_USE_MASK) == PARTITION_POLICY_OPEN)
391 l[m++] = "open";
392 else if (simplify && (flags & _PARTITION_POLICY_USE_MASK) == PARTITION_POLICY_IGNORE)
393 l[m++] = "ignore";
394 else {
395 if (flags & PARTITION_POLICY_VERITY)
396 l[m++] = "verity";
397 if (flags & PARTITION_POLICY_SIGNED)
398 l[m++] = "signed";
399 if (flags & PARTITION_POLICY_ENCRYPTED)
400 l[m++] = "encrypted";
401 if (flags & PARTITION_POLICY_UNPROTECTED)
402 l[m++] = "unprotected";
403 if (flags & PARTITION_POLICY_UNUSED)
404 l[m++] = "unused";
405 if (flags & PARTITION_POLICY_ABSENT)
406 l[m++] = "absent";
407 }
408
409 if (!simplify || (!(flags & PARTITION_POLICY_READ_ONLY_ON) != !(flags & PARTITION_POLICY_READ_ONLY_OFF))) {
410 if (flags & PARTITION_POLICY_READ_ONLY_ON)
411 l[m++] = "read-only-on";
412 if (flags & PARTITION_POLICY_READ_ONLY_OFF)
413 l[m++] = "read-only-off";
414 }
415
416 if (!simplify || (!(flags & PARTITION_POLICY_GROWFS_ON) != !(flags & PARTITION_POLICY_GROWFS_OFF))) {
417 if (flags & PARTITION_POLICY_GROWFS_OFF)
418 l[m++] = "growfs-off";
419 if (flags & PARTITION_POLICY_GROWFS_ON)
420 l[m++] = "growfs-on";
421 }
422
423 if (m == 0)
424 buf = strdup("-");
425 else {
426 assert(m+1 < ELEMENTSOF(l));
427 l[m] = NULL;
428
429 buf = strv_join((char**) l, "+");
430 }
431 if (!buf)
432 return -ENOMEM;
433
434 *ret = TAKE_PTR(buf);
435 return 0;
436 }
437
438 static bool partition_policy_flags_extended_equal(PartitionPolicyFlags a, PartitionPolicyFlags b) {
439 return partition_policy_flags_extend(a) == partition_policy_flags_extend(b);
440 }
441
442 static int image_policy_flags_all_match(const ImagePolicy *policy, PartitionPolicyFlags expected) {
443
444 if (expected < 0)
445 return -EINVAL;
446
447 if (!partition_policy_flags_extended_equal(image_policy_default(policy), expected))
448 return false;
449
450 for (PartitionDesignator d = 0; d < _PARTITION_DESIGNATOR_MAX; d++) {
451 PartitionPolicyFlags f, w;
452
453 f = image_policy_get_exhaustively(policy, d);
454 if (f < 0)
455 return f;
456
457 w = partition_policy_normalized_flags(
458 &(const PartitionPolicy) {
459 .flags = expected,
460 .designator = d,
461 });
462 if (w < 0)
463 return w;
464 if (f != w)
465 return false;
466 }
467
468 return true;
469 }
470
471 bool image_policy_equiv_ignore(const ImagePolicy *policy) {
472 /* Checks if this is the ignore policy (or equivalent to it), i.e. everything is ignored, aka '-', aka '' */
473 return image_policy_flags_all_match(policy, PARTITION_POLICY_IGNORE);
474 }
475
476 bool image_policy_equiv_allow(const ImagePolicy *policy) {
477 /* Checks if this is the allow policy (or equivalent to it), i.e. everything is allowed, aka '*' */
478 return image_policy_flags_all_match(policy, PARTITION_POLICY_OPEN);
479 }
480
481 bool image_policy_equiv_deny(const ImagePolicy *policy) {
482 /* Checks if this is the deny policy (or equivalent to it), i.e. everything must be absent, aka '~' */
483 return image_policy_flags_all_match(policy, PARTITION_POLICY_ABSENT);
484 }
485
486 int image_policy_to_string(const ImagePolicy *policy, bool simplify, char **ret) {
487 _cleanup_free_ char *s = NULL;
488 int r;
489
490 assert(ret);
491
492 if (simplify) {
493 const char *fixed;
494
495 if (image_policy_equiv_allow(policy))
496 fixed = "*";
497 else if (image_policy_equiv_ignore(policy))
498 fixed = "-";
499 else if (image_policy_equiv_deny(policy))
500 fixed = "~";
501 else
502 fixed = NULL;
503
504 if (fixed) {
505 s = strdup(fixed);
506 if (!s)
507 return -ENOMEM;
508
509 *ret = TAKE_PTR(s);
510 return 0;
511 }
512 }
513
514 for (size_t i = 0; i < image_policy_n_entries(policy); i++) {
515 const PartitionPolicy *p = policy->policies + i;
516 _cleanup_free_ char *f = NULL;
517 const char *t;
518
519 assert(i == 0 || p->designator > policy->policies[i-1].designator); /* Validate perfect ordering */
520
521 assert_se(t = partition_designator_to_string(p->designator));
522
523 if (simplify) {
524 /* Skip policy entries that match the default anyway */
525 PartitionPolicyFlags df;
526
527 df = partition_policy_normalized_flags(
528 &(const PartitionPolicy) {
529 .flags = image_policy_default(policy),
530 .designator = p->designator,
531 });
532 if (df < 0)
533 return df;
534
535 if (df == p->flags)
536 continue;
537 }
538
539 r = partition_policy_flags_to_string(p->flags, simplify, &f);
540 if (r < 0)
541 return r;
542
543 if (!strextend(&s, isempty(s) ? "" : ":", t, "=", f))
544 return -ENOMEM;
545 }
546
547 if (!simplify || !partition_policy_flags_extended_equal(image_policy_default(policy), PARTITION_POLICY_IGNORE)) {
548 _cleanup_free_ char *df = NULL;
549
550 r = partition_policy_flags_to_string(image_policy_default(policy), simplify, &df);
551 if (r < 0)
552 return r;
553
554 if (!strextend(&s, isempty(s) ? "" : ":", "=", df))
555 return -ENOMEM;
556 }
557
558 if (isempty(s)) { /* no rule and default policy? then let's return "-" */
559 s = strdup("-");
560 if (!s)
561 return -ENOMEM;
562 }
563
564 *ret = TAKE_PTR(s);
565 return 0;
566 }
567
568 bool image_policy_equal(const ImagePolicy *a, const ImagePolicy *b) {
569 if (a == b)
570 return true;
571 if (image_policy_n_entries(a) != image_policy_n_entries(b))
572 return false;
573 if (image_policy_default(a) != image_policy_default(b))
574 return false;
575 for (size_t i = 0; i < image_policy_n_entries(a); i++) {
576 if (a->policies[i].designator != b->policies[i].designator)
577 return false;
578 if (a->policies[i].flags != b->policies[i].flags)
579 return false;
580 }
581
582 return true;
583 }
584
585 int image_policy_equivalent(const ImagePolicy *a, const ImagePolicy *b) {
586
587 /* The image_policy_equal() function checks if the policy is defined the exact same way. This
588 * function here instead looks at the outcome of the two policies instead. Where does this come to
589 * different results you ask? We imply some logic regarding Verity/Encryption: when no rule is
590 * defined for a verity partition we can synthesize it from the protection level of the data
591 * partition it protects. Or: any per-partition rule that is identical to the default rule is
592 * redundant, and will be recognized as such by image_policy_equivalent() but not by
593 * image_policy_equal()- */
594
595 if (!partition_policy_flags_extended_equal(image_policy_default(a), image_policy_default(b)))
596 return false;
597
598 for (PartitionDesignator d = 0; d < _PARTITION_DESIGNATOR_MAX; d++) {
599 PartitionPolicyFlags f, w;
600
601 f = image_policy_get_exhaustively(a, d);
602 if (f < 0)
603 return f;
604
605 w = image_policy_get_exhaustively(b, d);
606 if (w < 0)
607 return w;
608
609 if (f != w)
610 return false;
611 }
612
613 return true;
614 }
615
616 int config_parse_image_policy(
617 const char *unit,
618 const char *filename,
619 unsigned line,
620 const char *section,
621 unsigned section_line,
622 const char *lvalue,
623 int ltype,
624 const char *rvalue,
625 void *data,
626 void *userdata) {
627
628 _cleanup_(image_policy_freep) ImagePolicy *np = NULL;
629 ImagePolicy **p = ASSERT_PTR(data);
630 int r;
631
632 assert(rvalue);
633
634 if (isempty(rvalue)) {
635 *p = image_policy_free(*p);
636 return 0;
637 }
638
639 r = image_policy_from_string(rvalue, &np);
640 if (r == -ENOTUNIQ)
641 return log_syntax(unit, LOG_ERR, filename, line, r, "Duplicate rule in image policy, refusing: %s", rvalue);
642 if (r == -EBADSLT)
643 return log_syntax(unit, LOG_ERR, filename, line, r, "Unknown partition type in image policy, refusing: %s", rvalue);
644 if (r == -EBADRQC)
645 return log_syntax(unit, LOG_ERR, filename, line, r, "Unknown partition policy flag in image policy, refusing: %s", rvalue);
646 if (r < 0)
647 return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse image policy, refusing: %s", rvalue);
648
649 return free_and_replace_full(*p, np, image_policy_free);
650 }
651
652 int parse_image_policy_argument(const char *s, ImagePolicy **policy) {
653 _cleanup_(image_policy_freep) ImagePolicy *np = NULL;
654 int r;
655
656 assert(s);
657 assert(policy);
658
659 /*
660 * This function is intended to be used in command line parsers.
661 *
662 * NOTE THAT THIS WILL FREE THE PREVIOUS ARGUMENT POINTER ON SUCCESS!
663 * Hence, do not pass in uninitialized pointers.
664 */
665
666 r = image_policy_from_string(s, &np);
667 if (r == -ENOTUNIQ)
668 return log_error_errno(r, "Duplicate rule in image policy: %s", s);
669 if (r == -EBADSLT)
670 return log_error_errno(r, "Unknown partition type in image policy: %s", s);
671 if (r == -EBADRQC)
672 return log_error_errno(r, "Unknown partition policy flag in image policy: %s", s);
673 if (r < 0)
674 return log_error_errno(r, "Failed to parse image policy: %s", s);
675
676 return free_and_replace_full(*policy, np, image_policy_free);
677 }
678
679 const ImagePolicy image_policy_allow = {
680 /* Allow policy */
681 .n_policies = 0,
682 .default_flags = PARTITION_POLICY_OPEN,
683 };
684
685 const ImagePolicy image_policy_deny = {
686 /* Deny policy */
687 .n_policies = 0,
688 .default_flags = PARTITION_POLICY_ABSENT,
689 };
690
691 const ImagePolicy image_policy_ignore = {
692 /* Ignore policy */
693 .n_policies = 0,
694 .default_flags = PARTITION_POLICY_IGNORE,
695 };
696
697 const ImagePolicy image_policy_sysext = {
698 /* For system extensions, honour root file system, and /usr/ and ignore everything else. After all,
699 * we are only interested in /usr/ + /opt/ trees anyway, and that's really the only place they can
700 * be. */
701 .n_policies = 2,
702 .policies = {
703 { PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
704 { PARTITION_USR, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
705 },
706 .default_flags = PARTITION_POLICY_IGNORE,
707 };
708
709 const ImagePolicy image_policy_sysext_strict = {
710 /* For system extensions, requiring signing */
711 .n_policies = 2,
712 .policies = {
713 { PARTITION_ROOT, PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT },
714 { PARTITION_USR, PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT },
715 },
716 .default_flags = PARTITION_POLICY_IGNORE,
717 };
718
719 const ImagePolicy image_policy_confext = {
720 /* For configuration extensions, honour root file system, and ignore everything else. After all, we
721 * are only interested in the /etc/ tree anyway, and that's really the only place it can be. */
722 .n_policies = 1,
723 .policies = {
724 { PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
725 },
726 .default_flags = PARTITION_POLICY_IGNORE,
727 };
728
729 const ImagePolicy image_policy_confext_strict = {
730 .n_policies = 1,
731 .policies = {
732 { PARTITION_ROOT, PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT },
733 },
734 .default_flags = PARTITION_POLICY_IGNORE,
735 };
736
737 const ImagePolicy image_policy_container = {
738 /* For systemd-nspawn containers we use all partitions, with the exception of swap */
739 .n_policies = 8,
740 .policies = {
741 { PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
742 { PARTITION_USR, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
743 { PARTITION_HOME, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
744 { PARTITION_SRV, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
745 { PARTITION_ESP, PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
746 { PARTITION_XBOOTLDR, PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
747 { PARTITION_TMP, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
748 { PARTITION_VAR, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
749 },
750 .default_flags = PARTITION_POLICY_IGNORE,
751 };
752
753 const ImagePolicy image_policy_host = {
754 /* For the host policy we basically use everything */
755 .n_policies = 9,
756 .policies = {
757 { PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
758 { PARTITION_USR, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
759 { PARTITION_HOME, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
760 { PARTITION_SRV, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
761 { PARTITION_ESP, PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
762 { PARTITION_XBOOTLDR, PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
763 { PARTITION_SWAP, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
764 { PARTITION_TMP, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
765 { PARTITION_VAR, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
766 },
767 .default_flags = PARTITION_POLICY_IGNORE,
768 };
769
770 const ImagePolicy image_policy_service = {
771 /* For RootImage= in services we skip ESP/XBOOTLDR and swap */
772 .n_policies = 6,
773 .policies = {
774 { PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
775 { PARTITION_USR, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
776 { PARTITION_HOME, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
777 { PARTITION_SRV, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
778 { PARTITION_TMP, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
779 { PARTITION_VAR, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
780 },
781 .default_flags = PARTITION_POLICY_IGNORE,
782 };