]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/analyze/analyze-security.c
tree-wide: use ASSERT_PTR more
[thirdparty/systemd.git] / src / analyze / analyze-security.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
ec16f3b6 2
ec16f3b6
LP
3#include <sys/utsname.h>
4
1624114d 5#include "af-list.h"
57a22a3f 6#include "analyze.h"
ec16f3b6 7#include "analyze-security.h"
bb43d853 8#include "analyze-verify.h"
ec16f3b6 9#include "bus-error.h"
d0d6ac67 10#include "bus-locator.h"
807542be 11#include "bus-map-properties.h"
ec16f3b6
LP
12#include "bus-unit-util.h"
13#include "bus-util.h"
04469211 14#include "copy.h"
ec16f3b6 15#include "env-util.h"
57a22a3f
LP
16#include "fd-util.h"
17#include "fileio.h"
ec16f3b6 18#include "format-table.h"
84ebe6f0 19#include "in-addr-prefix-util.h"
ec16f3b6
LP
20#include "locale-util.h"
21#include "macro.h"
bb43d853 22#include "manager.h"
f5947a5e
YW
23#include "missing_capability.h"
24#include "missing_sched.h"
04469211 25#include "mkdir.h"
d8b4d14d 26#include "nulstr-util.h"
ec16f3b6
LP
27#include "parse-util.h"
28#include "path-util.h"
29#include "pretty-print.h"
3a5d89fa
WKI
30#if HAVE_SECCOMP
31# include "seccomp-util.h"
32#endif
1624114d 33#include "service.h"
ec16f3b6
LP
34#include "set.h"
35#include "stdio-util.h"
36#include "strv.h"
37#include "terminal-util.h"
38#include "unit-def.h"
39#include "unit-name.h"
1624114d 40#include "unit-serialize.h"
ec16f3b6 41
1624114d 42typedef struct SecurityInfo {
ec16f3b6
LP
43 char *id;
44 char *type;
45 char *load_state;
46 char *fragment_path;
47 bool default_dependencies;
48
49 uint64_t ambient_capabilities;
50 uint64_t capability_bounding_set;
51
52 char *user;
53 char **supplementary_groups;
54 bool dynamic_user;
55
56 bool ip_address_deny_all;
57 bool ip_address_allow_localhost;
58 bool ip_address_allow_other;
59
fab34748
KL
60 bool ip_filters_custom_ingress;
61 bool ip_filters_custom_egress;
62
ec16f3b6 63 char *keyring_mode;
ed125c93
LP
64 char *protect_proc;
65 char *proc_subset;
ec16f3b6
LP
66 bool lock_personality;
67 bool memory_deny_write_execute;
68 bool no_new_privileges;
69 char *notify_access;
527bd7f1 70 bool protect_hostname;
ec16f3b6
LP
71
72 bool private_devices;
73 bool private_mounts;
74 bool private_network;
75 bool private_tmp;
76 bool private_users;
77
78 bool protect_control_groups;
79 bool protect_kernel_modules;
80 bool protect_kernel_tunables;
82dce83b 81 bool protect_kernel_logs;
9f37272a 82 bool protect_clock;
ec16f3b6
LP
83
84 char *protect_home;
85 char *protect_system;
86
87 bool remove_ipc;
88
89 bool restrict_address_family_inet;
90 bool restrict_address_family_unix;
91 bool restrict_address_family_netlink;
92 bool restrict_address_family_packet;
93 bool restrict_address_family_other;
94
1624114d 95 unsigned long long restrict_namespaces;
ec16f3b6 96 bool restrict_realtime;
9d880b70 97 bool restrict_suid_sgid;
ec16f3b6
LP
98
99 char *root_directory;
100 char *root_image;
101
102 bool delegate;
103 char *device_policy;
6a59dfa1 104 char **device_allow;
ec16f3b6 105
1624114d 106 Set *system_call_architectures;
ec16f3b6 107
6b000af4 108 bool system_call_filter_allow_list;
5862e556 109 Set *system_call_filter;
ec16f3b6 110
1624114d
MG
111 mode_t _umask;
112} SecurityInfo;
ec16f3b6
LP
113
114struct security_assessor {
115 const char *id;
3838d22c 116 const char *json_field;
ec16f3b6
LP
117 const char *description_good;
118 const char *description_bad;
119 const char *description_na;
120 const char *url;
121 uint64_t weight;
122 uint64_t range;
1ace223c
SJ
123 int (*assess)(
124 const struct security_assessor *a,
1624114d 125 const SecurityInfo *info,
1ace223c
SJ
126 const void *data,
127 uint64_t *ret_badness,
128 char **ret_description);
ec16f3b6
LP
129 size_t offset;
130 uint64_t parameter;
131 bool default_dependencies_only;
132};
133
1624114d
MG
134static SecurityInfo *security_info_new(void) {
135 SecurityInfo *info = new(SecurityInfo, 1);
136 if (!info)
137 return NULL;
138
139 *info = (SecurityInfo) {
140 .default_dependencies = true,
141 .capability_bounding_set = UINT64_MAX,
142 .restrict_namespaces = UINT64_MAX,
143 ._umask = 0002,
144 };
145
146 return info;
147}
148
149static SecurityInfo *security_info_free(SecurityInfo *i) {
ec16f3b6 150 if (!i)
1624114d 151 return NULL;
ec16f3b6
LP
152
153 free(i->id);
154 free(i->type);
155 free(i->load_state);
156 free(i->fragment_path);
157
158 free(i->user);
159
160 free(i->protect_home);
161 free(i->protect_system);
162
163 free(i->root_directory);
164 free(i->root_image);
165
166 free(i->keyring_mode);
ed125c93
LP
167 free(i->protect_proc);
168 free(i->proc_subset);
ec16f3b6
LP
169 free(i->notify_access);
170
171 free(i->device_policy);
6a59dfa1 172 strv_free(i->device_allow);
ec16f3b6
LP
173
174 strv_free(i->supplementary_groups);
1624114d 175 set_free(i->system_call_architectures);
5862e556 176 set_free(i->system_call_filter);
1624114d
MG
177
178 return mfree(i);
ec16f3b6
LP
179}
180
1624114d
MG
181DEFINE_TRIVIAL_CLEANUP_FUNC(SecurityInfo*, security_info_free);
182
183static bool security_info_runs_privileged(const SecurityInfo *i) {
ec16f3b6
LP
184 assert(i);
185
186 if (STRPTR_IN_SET(i->user, "0", "root"))
187 return true;
188
189 if (i->dynamic_user)
190 return false;
191
192 return isempty(i->user);
193}
194
195static int assess_bool(
196 const struct security_assessor *a,
1624114d 197 const SecurityInfo *info,
ec16f3b6
LP
198 const void *data,
199 uint64_t *ret_badness,
200 char **ret_description) {
201
99534007 202 const bool *b = ASSERT_PTR(data);
ec16f3b6 203
ec16f3b6
LP
204 assert(ret_badness);
205 assert(ret_description);
206
207 *ret_badness = a->parameter ? *b : !*b;
208 *ret_description = NULL;
209
210 return 0;
211}
212
213static int assess_user(
214 const struct security_assessor *a,
1624114d 215 const SecurityInfo *info,
ec16f3b6
LP
216 const void *data,
217 uint64_t *ret_badness,
218 char **ret_description) {
219
220 _cleanup_free_ char *d = NULL;
221 uint64_t b;
222
223 assert(ret_badness);
224 assert(ret_description);
225
226 if (streq_ptr(info->user, NOBODY_USER_NAME)) {
227 d = strdup("Service runs under as '" NOBODY_USER_NAME "' user, which should not be used for services");
228 b = 9;
229 } else if (info->dynamic_user && !STR_IN_SET(info->user, "0", "root")) {
230 d = strdup("Service runs under a transient non-root user identity");
231 b = 0;
232 } else if (info->user && !STR_IN_SET(info->user, "0", "root", "")) {
233 d = strdup("Service runs under a static non-root user identity");
234 b = 0;
235 } else {
236 *ret_badness = 10;
237 *ret_description = NULL;
238 return 0;
239 }
240
241 if (!d)
242 return log_oom();
243
244 *ret_badness = b;
245 *ret_description = TAKE_PTR(d);
246
247 return 0;
248}
249
250static int assess_protect_home(
251 const struct security_assessor *a,
1624114d 252 const SecurityInfo *info,
ec16f3b6
LP
253 const void *data,
254 uint64_t *ret_badness,
255 char **ret_description) {
256
257 const char *description;
258 uint64_t badness;
259 char *copy;
260 int r;
261
262 assert(ret_badness);
263 assert(ret_description);
264
265 badness = 10;
266 description = "Service has full access to home directories";
267
268 r = parse_boolean(info->protect_home);
269 if (r < 0) {
270 if (streq_ptr(info->protect_home, "read-only")) {
271 badness = 5;
272 description = "Service has read-only access to home directories";
273 } else if (streq_ptr(info->protect_home, "tmpfs")) {
274 badness = 1;
275 description = "Service has access to fake empty home directories";
276 }
277 } else if (r > 0) {
278 badness = 0;
279 description = "Service has no access to home directories";
280 }
281
282 copy = strdup(description);
283 if (!copy)
284 return log_oom();
285
286 *ret_badness = badness;
287 *ret_description = copy;
288
289 return 0;
290}
291
292static int assess_protect_system(
293 const struct security_assessor *a,
1624114d 294 const SecurityInfo *info,
ec16f3b6
LP
295 const void *data,
296 uint64_t *ret_badness,
297 char **ret_description) {
298
299 const char *description;
300 uint64_t badness;
301 char *copy;
302 int r;
303
304 assert(ret_badness);
305 assert(ret_description);
306
307 badness = 10;
1e8817b3 308 description = "Service has full access to the OS file hierarchy";
ec16f3b6
LP
309
310 r = parse_boolean(info->protect_system);
311 if (r < 0) {
312 if (streq_ptr(info->protect_system, "full")) {
313 badness = 3;
1e8817b3 314 description = "Service has very limited write access to the OS file hierarchy";
ec16f3b6
LP
315 } else if (streq_ptr(info->protect_system, "strict")) {
316 badness = 0;
317 description = "Service has strict read-only access to the OS file hierarchy";
318 }
319 } else if (r > 0) {
320 badness = 5;
321 description = "Service has limited write access to the OS file hierarchy";
322 }
323
324 copy = strdup(description);
325 if (!copy)
326 return log_oom();
327
328 *ret_badness = badness;
329 *ret_description = copy;
330
331 return 0;
332}
333
334static int assess_root_directory(
335 const struct security_assessor *a,
1624114d 336 const SecurityInfo *info,
ec16f3b6
LP
337 const void *data,
338 uint64_t *ret_badness,
339 char **ret_description) {
340
341 assert(ret_badness);
342 assert(ret_description);
343
344 *ret_badness =
d909b40f 345 empty_or_root(info->root_directory) &&
d737b451 346 empty_or_root(info->root_image);
ec16f3b6
LP
347 *ret_description = NULL;
348
349 return 0;
350}
351
352static int assess_capability_bounding_set(
353 const struct security_assessor *a,
1624114d 354 const SecurityInfo *info,
ec16f3b6
LP
355 const void *data,
356 uint64_t *ret_badness,
357 char **ret_description) {
358
359 assert(ret_badness);
360 assert(ret_description);
361
362 *ret_badness = !!(info->capability_bounding_set & a->parameter);
363 *ret_description = NULL;
364
365 return 0;
366}
367
368static int assess_umask(
369 const struct security_assessor *a,
1624114d 370 const SecurityInfo *info,
ec16f3b6
LP
371 const void *data,
372 uint64_t *ret_badness,
373 char **ret_description) {
374
375 char *copy = NULL;
376 const char *d;
377 uint64_t b;
378
379 assert(ret_badness);
380 assert(ret_description);
381
382 if (!FLAGS_SET(info->_umask, 0002)) {
383 d = "Files created by service are world-writable by default";
384 b = 10;
385 } else if (!FLAGS_SET(info->_umask, 0004)) {
386 d = "Files created by service are world-readable by default";
387 b = 5;
388 } else if (!FLAGS_SET(info->_umask, 0020)) {
389 d = "Files created by service are group-writable by default";
390 b = 2;
391 } else if (!FLAGS_SET(info->_umask, 0040)) {
392 d = "Files created by service are group-readable by default";
393 b = 1;
394 } else {
395 d = "Files created by service are accessible only by service's own user by default";
396 b = 0;
397 }
398
399 copy = strdup(d);
400 if (!copy)
401 return log_oom();
402
403 *ret_badness = b;
404 *ret_description = copy;
405
406 return 0;
407}
408
409static int assess_keyring_mode(
410 const struct security_assessor *a,
1624114d 411 const SecurityInfo *info,
ec16f3b6
LP
412 const void *data,
413 uint64_t *ret_badness,
414 char **ret_description) {
415
416 assert(ret_badness);
417 assert(ret_description);
418
419 *ret_badness = !streq_ptr(info->keyring_mode, "private");
420 *ret_description = NULL;
421
422 return 0;
423}
424
ed125c93
LP
425static int assess_protect_proc(
426 const struct security_assessor *a,
1624114d 427 const SecurityInfo *info,
ed125c93
LP
428 const void *data,
429 uint64_t *ret_badness,
430 char **ret_description) {
431
432 assert(ret_badness);
433 assert(ret_description);
434
435 if (streq_ptr(info->protect_proc, "noaccess"))
436 *ret_badness = 1;
437 else if (STRPTR_IN_SET(info->protect_proc, "invisible", "ptraceable"))
438 *ret_badness = 0;
439 else
440 *ret_badness = 3;
441
442 *ret_description = NULL;
443
444 return 0;
445}
446
447static int assess_proc_subset(
448 const struct security_assessor *a,
1624114d 449 const SecurityInfo *info,
ed125c93
LP
450 const void *data,
451 uint64_t *ret_badness,
452 char **ret_description) {
453
454 assert(ret_badness);
455 assert(ret_description);
456
457 *ret_badness = !streq_ptr(info->proc_subset, "pid");
458 *ret_description = NULL;
459
460 return 0;
461}
462
ec16f3b6
LP
463static int assess_notify_access(
464 const struct security_assessor *a,
1624114d 465 const SecurityInfo *info,
ec16f3b6
LP
466 const void *data,
467 uint64_t *ret_badness,
468 char **ret_description) {
469
470 assert(ret_badness);
471 assert(ret_description);
472
473 *ret_badness = streq_ptr(info->notify_access, "all");
474 *ret_description = NULL;
475
476 return 0;
477}
478
479static int assess_remove_ipc(
480 const struct security_assessor *a,
1624114d 481 const SecurityInfo *info,
ec16f3b6
LP
482 const void *data,
483 uint64_t *ret_badness,
484 char **ret_description) {
485
486 assert(ret_badness);
487 assert(ret_description);
488
489 if (security_info_runs_privileged(info))
490 *ret_badness = UINT64_MAX;
491 else
492 *ret_badness = !info->remove_ipc;
493
494 *ret_description = NULL;
495 return 0;
496}
497
498static int assess_supplementary_groups(
499 const struct security_assessor *a,
1624114d 500 const SecurityInfo *info,
ec16f3b6
LP
501 const void *data,
502 uint64_t *ret_badness,
503 char **ret_description) {
504
505 assert(ret_badness);
506 assert(ret_description);
507
508 if (security_info_runs_privileged(info))
509 *ret_badness = UINT64_MAX;
510 else
511 *ret_badness = !strv_isempty(info->supplementary_groups);
512
513 *ret_description = NULL;
514 return 0;
515}
516
517static int assess_restrict_namespaces(
518 const struct security_assessor *a,
1624114d 519 const SecurityInfo *info,
ec16f3b6
LP
520 const void *data,
521 uint64_t *ret_badness,
522 char **ret_description) {
523
524 assert(ret_badness);
525 assert(ret_description);
526
527 *ret_badness = !!(info->restrict_namespaces & a->parameter);
528 *ret_description = NULL;
529
530 return 0;
531}
532
1449b0f8
LB
533#if HAVE_SECCOMP
534
ec16f3b6
LP
535static int assess_system_call_architectures(
536 const struct security_assessor *a,
1624114d 537 const SecurityInfo *info,
ec16f3b6
LP
538 const void *data,
539 uint64_t *ret_badness,
540 char **ret_description) {
541
542 char *d;
543 uint64_t b;
544
545 assert(ret_badness);
546 assert(ret_description);
547
1624114d 548 if (set_isempty(info->system_call_architectures)) {
ec16f3b6
LP
549 b = 10;
550 d = strdup("Service may execute system calls with all ABIs");
444d9abd 551 } else if (set_contains(info->system_call_architectures, "native") &&
1624114d 552 set_size(info->system_call_architectures) == 1) {
ec16f3b6
LP
553 b = 0;
554 d = strdup("Service may execute system calls only with native ABI");
555 } else {
556 b = 8;
557 d = strdup("Service may execute system calls with multiple ABIs");
558 }
559
560 if (!d)
561 return log_oom();
562
563 *ret_badness = b;
564 *ret_description = d;
565
566 return 0;
567}
568
5862e556 569static bool syscall_names_in_filter(Set *s, bool allow_list, const SyscallFilterSet *f, const char **ret_offending_syscall) {
ec16f3b6
LP
570 const char *syscall;
571
572 NULSTR_FOREACH(syscall, f->value) {
ec16f3b6
LP
573 if (syscall[0] == '@') {
574 const SyscallFilterSet *g;
ec16f3b6 575
95832a0f 576 assert_se(g = syscall_filter_set_find(syscall));
a9134af2 577 if (syscall_names_in_filter(s, allow_list, g, ret_offending_syscall))
95832a0f 578 return true; /* bad! */
ec16f3b6 579
95832a0f 580 continue;
ec16f3b6
LP
581 }
582
95832a0f 583 /* Let's see if the system call actually exists on this platform, before complaining */
5862e556 584 if (seccomp_syscall_resolve_name(syscall) < 0)
95832a0f
YW
585 continue;
586
5862e556 587 if (set_contains(s, syscall) == allow_list) {
ec16f3b6 588 log_debug("Offending syscall filter item: %s", syscall);
a9134af2
ZJS
589 if (ret_offending_syscall)
590 *ret_offending_syscall = syscall;
ec16f3b6
LP
591 return true; /* bad! */
592 }
593 }
594
a9134af2 595 *ret_offending_syscall = NULL;
ec16f3b6
LP
596 return false;
597}
598
599static int assess_system_call_filter(
600 const struct security_assessor *a,
1624114d 601 const SecurityInfo *info,
ec16f3b6
LP
602 const void *data,
603 uint64_t *ret_badness,
604 char **ret_description) {
605
ec16f3b6
LP
606 assert(a);
607 assert(info);
608 assert(ret_badness);
609 assert(ret_description);
610
611 assert(a->parameter < _SYSCALL_FILTER_SET_MAX);
a9134af2
ZJS
612 const SyscallFilterSet *f = syscall_filter_sets + a->parameter;
613
12619d0a 614 _cleanup_free_ char *d = NULL;
a9134af2 615 uint64_t b;
12619d0a 616 int r;
ec16f3b6 617
5862e556 618 if (!info->system_call_filter_allow_list && set_isempty(info->system_call_filter)) {
12619d0a 619 r = free_and_strdup(&d, "Service does not filter system calls");
ec16f3b6
LP
620 b = 10;
621 } else {
622 bool bad;
a9134af2 623 const char *offender = NULL;
ec16f3b6
LP
624
625 log_debug("Analyzing system call filter, checking against: %s", f->name);
a9134af2 626 bad = syscall_names_in_filter(info->system_call_filter, info->system_call_filter_allow_list, f, &offender);
ec16f3b6
LP
627 log_debug("Result: %s", bad ? "bad" : "good");
628
6b000af4 629 if (info->system_call_filter_allow_list) {
ec16f3b6 630 if (bad) {
12619d0a
ZJS
631 r = asprintf(&d, "System call allow list defined for service, and %s is included "
632 "(e.g. %s is allowed)",
633 f->name, offender);
ec16f3b6
LP
634 b = 9;
635 } else {
12619d0a
ZJS
636 r = asprintf(&d, "System call allow list defined for service, and %s is not included",
637 f->name);
ec16f3b6
LP
638 b = 0;
639 }
640 } else {
641 if (bad) {
12619d0a
ZJS
642 r = asprintf(&d, "System call deny list defined for service, and %s is not included "
643 "(e.g. %s is allowed)",
644 f->name, offender);
ec16f3b6
LP
645 b = 10;
646 } else {
12619d0a
ZJS
647 r = asprintf(&d, "System call deny list defined for service, and %s is included",
648 f->name);
01ecb367 649 b = 0;
ec16f3b6
LP
650 }
651 }
652 }
12619d0a 653 if (r < 0)
ec16f3b6
LP
654 return log_oom();
655
656 *ret_badness = b;
12619d0a 657 *ret_description = TAKE_PTR(d);
ec16f3b6
LP
658
659 return 0;
660}
661
3a5d89fa
WKI
662#endif
663
ec16f3b6
LP
664static int assess_ip_address_allow(
665 const struct security_assessor *a,
1624114d 666 const SecurityInfo *info,
ec16f3b6
LP
667 const void *data,
668 uint64_t *ret_badness,
669 char **ret_description) {
670
671 char *d = NULL;
672 uint64_t b;
673
674 assert(info);
675 assert(ret_badness);
676 assert(ret_description);
677
fab34748
KL
678 if (info->ip_filters_custom_ingress || info->ip_filters_custom_egress) {
679 d = strdup("Service defines custom ingress/egress IP filters with BPF programs");
680 b = 0;
681 } else if (!info->ip_address_deny_all) {
6b000af4 682 d = strdup("Service does not define an IP address allow list");
ec16f3b6
LP
683 b = 10;
684 } else if (info->ip_address_allow_other) {
6b000af4 685 d = strdup("Service defines IP address allow list with non-localhost entries");
ec16f3b6
LP
686 b = 5;
687 } else if (info->ip_address_allow_localhost) {
6b000af4 688 d = strdup("Service defines IP address allow list with only localhost entries");
ec16f3b6
LP
689 b = 2;
690 } else {
691 d = strdup("Service blocks all IP address ranges");
692 b = 0;
693 }
694
695 if (!d)
696 return log_oom();
697
698 *ret_badness = b;
699 *ret_description = d;
700
701 return 0;
702}
703
704static int assess_device_allow(
705 const struct security_assessor *a,
1624114d 706 const SecurityInfo *info,
ec16f3b6
LP
707 const void *data,
708 uint64_t *ret_badness,
709 char **ret_description) {
710
711 char *d = NULL;
712 uint64_t b;
713
714 assert(info);
715 assert(ret_badness);
716 assert(ret_description);
717
718 if (STRPTR_IN_SET(info->device_policy, "strict", "closed")) {
719
6a59dfa1
LB
720 if (!strv_isempty(info->device_allow)) {
721 _cleanup_free_ char *join = NULL;
722
723 join = strv_join(info->device_allow, " ");
724 if (!join)
725 return log_oom();
726
727 d = strjoin("Service has a device ACL with some special devices: ", join);
ec16f3b6
LP
728 b = 5;
729 } else {
730 d = strdup("Service has a minimal device ACL");
731 b = 0;
732 }
733 } else {
734 d = strdup("Service has no device ACL");
735 b = 10;
736 }
737
738 if (!d)
739 return log_oom();
740
741 *ret_badness = b;
742 *ret_description = d;
743
744 return 0;
745}
746
747static int assess_ambient_capabilities(
748 const struct security_assessor *a,
1624114d 749 const SecurityInfo *info,
ec16f3b6
LP
750 const void *data,
751 uint64_t *ret_badness,
752 char **ret_description) {
753
754 assert(ret_badness);
755 assert(ret_description);
756
757 *ret_badness = info->ambient_capabilities != 0;
758 *ret_description = NULL;
759
760 return 0;
761}
762
763static const struct security_assessor security_assessor_table[] = {
764 {
765 .id = "User=/DynamicUser=",
3838d22c 766 .json_field = "UserOrDynamicUser",
ec16f3b6
LP
767 .description_bad = "Service runs as root user",
768 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#User=",
769 .weight = 2000,
770 .range = 10,
771 .assess = assess_user,
772 },
773 {
774 .id = "SupplementaryGroups=",
3838d22c 775 .json_field = "SupplementaryGroups",
ec16f3b6
LP
776 .description_good = "Service has no supplementary groups",
777 .description_bad = "Service runs with supplementary groups",
778 .description_na = "Service runs as root, option does not matter",
779 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SupplementaryGroups=",
780 .weight = 200,
781 .range = 1,
782 .assess = assess_supplementary_groups,
783 },
784 {
785 .id = "PrivateDevices=",
3838d22c 786 .json_field = "PrivateDevices",
ec16f3b6
LP
787 .description_good = "Service has no access to hardware devices",
788 .description_bad = "Service potentially has access to hardware devices",
789 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateDevices=",
790 .weight = 1000,
791 .range = 1,
792 .assess = assess_bool,
1624114d 793 .offset = offsetof(SecurityInfo, private_devices),
ec16f3b6
LP
794 },
795 {
796 .id = "PrivateMounts=",
3838d22c 797 .json_field = "PrivateMounts",
ec16f3b6
LP
798 .description_good = "Service cannot install system mounts",
799 .description_bad = "Service may install system mounts",
800 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateMounts=",
801 .weight = 1000,
802 .range = 1,
803 .assess = assess_bool,
1624114d 804 .offset = offsetof(SecurityInfo, private_mounts),
ec16f3b6
LP
805 },
806 {
807 .id = "PrivateNetwork=",
3838d22c 808 .json_field = "PrivateNetwork",
ec16f3b6
LP
809 .description_good = "Service has no access to the host's network",
810 .description_bad = "Service has access to the host's network",
811 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateNetwork=",
812 .weight = 2500,
813 .range = 1,
814 .assess = assess_bool,
1624114d 815 .offset = offsetof(SecurityInfo, private_network),
ec16f3b6
LP
816 },
817 {
818 .id = "PrivateTmp=",
3838d22c 819 .json_field = "PrivateTmp",
ec16f3b6
LP
820 .description_good = "Service has no access to other software's temporary files",
821 .description_bad = "Service has access to other software's temporary files",
822 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateTmp=",
823 .weight = 1000,
824 .range = 1,
825 .assess = assess_bool,
1624114d 826 .offset = offsetof(SecurityInfo, private_tmp),
ec16f3b6
LP
827 .default_dependencies_only = true,
828 },
829 {
830 .id = "PrivateUsers=",
3838d22c 831 .json_field = "PrivateUsers",
ec16f3b6
LP
832 .description_good = "Service does not have access to other users",
833 .description_bad = "Service has access to other users",
834 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateUsers=",
835 .weight = 1000,
836 .range = 1,
837 .assess = assess_bool,
1624114d 838 .offset = offsetof(SecurityInfo, private_users),
ec16f3b6
LP
839 },
840 {
841 .id = "ProtectControlGroups=",
3838d22c 842 .json_field = "ProtectControlGroups",
ec16f3b6 843 .description_good = "Service cannot modify the control group file system",
287cf2d8 844 .description_bad = "Service may modify the control group file system",
ec16f3b6
LP
845 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectControlGroups=",
846 .weight = 1000,
847 .range = 1,
848 .assess = assess_bool,
1624114d 849 .offset = offsetof(SecurityInfo, protect_control_groups),
ec16f3b6
LP
850 },
851 {
852 .id = "ProtectKernelModules=",
3838d22c 853 .json_field = "ProtectKernelModules",
ec16f3b6
LP
854 .description_good = "Service cannot load or read kernel modules",
855 .description_bad = "Service may load or read kernel modules",
856 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelModules=",
857 .weight = 1000,
858 .range = 1,
859 .assess = assess_bool,
1624114d 860 .offset = offsetof(SecurityInfo, protect_kernel_modules),
ec16f3b6
LP
861 },
862 {
863 .id = "ProtectKernelTunables=",
3838d22c 864 .json_field = "ProtectKernelTunables",
ec16f3b6
LP
865 .description_good = "Service cannot alter kernel tunables (/proc/sys, …)",
866 .description_bad = "Service may alter kernel tunables",
867 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelTunables=",
868 .weight = 1000,
869 .range = 1,
870 .assess = assess_bool,
1624114d 871 .offset = offsetof(SecurityInfo, protect_kernel_tunables),
ec16f3b6 872 },
82dce83b
KK
873 {
874 .id = "ProtectKernelLogs=",
3838d22c 875 .json_field = "ProtectKernelLogs",
82dce83b
KK
876 .description_good = "Service cannot read from or write to the kernel log ring buffer",
877 .description_bad = "Service may read from or write to the kernel log ring buffer",
878 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelLogs=",
879 .weight = 1000,
880 .range = 1,
881 .assess = assess_bool,
1624114d 882 .offset = offsetof(SecurityInfo, protect_kernel_logs),
82dce83b 883 },
9f37272a
KK
884 {
885 .id = "ProtectClock=",
3838d22c 886 .json_field = "ProtectClock",
9f37272a
KK
887 .description_good = "Service cannot write to the hardware clock or system clock",
888 .description_bad = "Service may write to the hardware clock or system clock",
889 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectClock=",
890 .weight = 1000,
891 .range = 1,
892 .assess = assess_bool,
1624114d 893 .offset = offsetof(SecurityInfo, protect_clock),
9f37272a 894 },
ec16f3b6
LP
895 {
896 .id = "ProtectHome=",
3838d22c 897 .json_field = "ProtectHome",
ec16f3b6
LP
898 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHome=",
899 .weight = 1000,
900 .range = 10,
901 .assess = assess_protect_home,
902 .default_dependencies_only = true,
903 },
527bd7f1
TM
904 {
905 .id = "ProtectHostname=",
3838d22c 906 .json_field = "ProtectHostname",
527bd7f1
TM
907 .description_good = "Service cannot change system host/domainname",
908 .description_bad = "Service may change system host/domainname",
909 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHostname=",
910 .weight = 50,
911 .range = 1,
912 .assess = assess_bool,
1624114d 913 .offset = offsetof(SecurityInfo, protect_hostname),
527bd7f1 914 },
ec16f3b6
LP
915 {
916 .id = "ProtectSystem=",
3838d22c 917 .json_field = "ProtectSystem",
ec16f3b6
LP
918 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectSystem=",
919 .weight = 1000,
920 .range = 10,
921 .assess = assess_protect_system,
922 .default_dependencies_only = true,
923 },
924 {
925 .id = "RootDirectory=/RootImage=",
3838d22c 926 .json_field = "RootDirectoryOrRootImage",
ec16f3b6
LP
927 .description_good = "Service has its own root directory/image",
928 .description_bad = "Service runs within the host's root directory",
929 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RootDirectory=",
930 .weight = 200,
931 .range = 1,
932 .assess = assess_root_directory,
933 .default_dependencies_only = true,
934 },
935 {
936 .id = "LockPersonality=",
3838d22c 937 .json_field = "LockPersonality",
ec16f3b6
LP
938 .description_good = "Service cannot change ABI personality",
939 .description_bad = "Service may change ABI personality",
940 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#LockPersonality=",
941 .weight = 100,
942 .range = 1,
943 .assess = assess_bool,
1624114d 944 .offset = offsetof(SecurityInfo, lock_personality),
ec16f3b6
LP
945 },
946 {
947 .id = "MemoryDenyWriteExecute=",
3838d22c 948 .json_field = "MemoryDenyWriteExecute",
ec16f3b6
LP
949 .description_good = "Service cannot create writable executable memory mappings",
950 .description_bad = "Service may create writable executable memory mappings",
951 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#MemoryDenyWriteExecute=",
952 .weight = 100,
953 .range = 1,
954 .assess = assess_bool,
1624114d 955 .offset = offsetof(SecurityInfo, memory_deny_write_execute),
ec16f3b6
LP
956 },
957 {
958 .id = "NoNewPrivileges=",
3838d22c 959 .json_field = "NoNewPrivileges",
ec16f3b6
LP
960 .description_good = "Service processes cannot acquire new privileges",
961 .description_bad = "Service processes may acquire new privileges",
962 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NoNewPrivileges=",
963 .weight = 1000,
964 .range = 1,
965 .assess = assess_bool,
1624114d 966 .offset = offsetof(SecurityInfo, no_new_privileges),
ec16f3b6
LP
967 },
968 {
969 .id = "CapabilityBoundingSet=~CAP_SYS_ADMIN",
3838d22c 970 .json_field = "CapabilityBoundingSet_CAP_SYS_ADMIN",
ec16f3b6
LP
971 .description_good = "Service has no administrator privileges",
972 .description_bad = "Service has administrator privileges",
973 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
974 .weight = 1500,
975 .range = 1,
976 .assess = assess_capability_bounding_set,
977 .parameter = UINT64_C(1) << CAP_SYS_ADMIN,
978 },
979 {
980 .id = "CapabilityBoundingSet=~CAP_SET(UID|GID|PCAP)",
3838d22c 981 .json_field = "CapabilityBoundingSet_CAP_SET_UID_GID_PCAP",
ec16f3b6
LP
982 .description_good = "Service cannot change UID/GID identities/capabilities",
983 .description_bad = "Service may change UID/GID identities/capabilities",
984 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
985 .weight = 1500,
986 .range = 1,
987 .assess = assess_capability_bounding_set,
988 .parameter = (UINT64_C(1) << CAP_SETUID)|
989 (UINT64_C(1) << CAP_SETGID)|
990 (UINT64_C(1) << CAP_SETPCAP),
991 },
992 {
993 .id = "CapabilityBoundingSet=~CAP_SYS_PTRACE",
3838d22c 994 .json_field = "CapabilityBoundingSet_CAP_SYS_PTRACE",
ec16f3b6
LP
995 .description_good = "Service has no ptrace() debugging abilities",
996 .description_bad = "Service has ptrace() debugging abilities",
997 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
998 .weight = 1500,
999 .range = 1,
1000 .assess = assess_capability_bounding_set,
1001 .parameter = (UINT64_C(1) << CAP_SYS_PTRACE),
1002 },
1003 {
1004 .id = "CapabilityBoundingSet=~CAP_SYS_TIME",
3838d22c 1005 .json_field = "CapabilityBoundingSet_CAP_SYS_TIME",
ec16f3b6
LP
1006 .description_good = "Service processes cannot change the system clock",
1007 .description_bad = "Service processes may change the system clock",
1008 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1009 .weight = 1000,
1010 .range = 1,
1011 .assess = assess_capability_bounding_set,
1012 .parameter = UINT64_C(1) << CAP_SYS_TIME,
1013 },
1014 {
1015 .id = "CapabilityBoundingSet=~CAP_NET_ADMIN",
3838d22c 1016 .json_field = "CapabilityBoundingSet_CAP_NET_ADMIN",
ec16f3b6
LP
1017 .description_good = "Service has no network configuration privileges",
1018 .description_bad = "Service has network configuration privileges",
1019 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1020 .weight = 1000,
1021 .range = 1,
1022 .assess = assess_capability_bounding_set,
1023 .parameter = (UINT64_C(1) << CAP_NET_ADMIN),
1024 },
1025 {
b5ef6610 1026 .id = "CapabilityBoundingSet=~CAP_SYS_RAWIO",
3838d22c 1027 .json_field = "CapabilityBoundingSet_CAP_SYS_RAWIO",
ec16f3b6
LP
1028 .description_good = "Service has no raw I/O access",
1029 .description_bad = "Service has raw I/O access",
1030 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1031 .weight = 1000,
1032 .range = 1,
1033 .assess = assess_capability_bounding_set,
1034 .parameter = (UINT64_C(1) << CAP_SYS_RAWIO),
1035 },
1036 {
1037 .id = "CapabilityBoundingSet=~CAP_SYS_MODULE",
3838d22c 1038 .json_field = "CapabilityBoundingSet_CAP_SYS_MODULE",
ec16f3b6
LP
1039 .description_good = "Service cannot load kernel modules",
1040 .description_bad = "Service may load kernel modules",
1041 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1042 .weight = 1000,
1043 .range = 1,
1044 .assess = assess_capability_bounding_set,
1045 .parameter = (UINT64_C(1) << CAP_SYS_MODULE),
1046 },
1047 {
1048 .id = "CapabilityBoundingSet=~CAP_AUDIT_*",
3838d22c 1049 .json_field = "CapabilityBoundingSet_CAP_AUDIT",
ec16f3b6
LP
1050 .description_good = "Service has no audit subsystem access",
1051 .description_bad = "Service has audit subsystem access",
1052 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1053 .weight = 500,
1054 .range = 1,
1055 .assess = assess_capability_bounding_set,
1056 .parameter = (UINT64_C(1) << CAP_AUDIT_CONTROL) |
1057 (UINT64_C(1) << CAP_AUDIT_READ) |
1058 (UINT64_C(1) << CAP_AUDIT_WRITE),
1059 },
1060 {
1061 .id = "CapabilityBoundingSet=~CAP_SYSLOG",
3838d22c 1062 .json_field = "CapabilityBoundingSet_CAP_SYSLOG",
ec16f3b6
LP
1063 .description_good = "Service has no access to kernel logging",
1064 .description_bad = "Service has access to kernel logging",
1065 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1066 .weight = 500,
1067 .range = 1,
1068 .assess = assess_capability_bounding_set,
1069 .parameter = (UINT64_C(1) << CAP_SYSLOG),
1070 },
1071 {
1072 .id = "CapabilityBoundingSet=~CAP_SYS_(NICE|RESOURCE)",
3838d22c 1073 .json_field = "CapabilityBoundingSet_CAP_SYS_NICE_RESOURCE",
ec16f3b6
LP
1074 .description_good = "Service has no privileges to change resource use parameters",
1075 .description_bad = "Service has privileges to change resource use parameters",
1076 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1077 .weight = 500,
1078 .range = 1,
1079 .assess = assess_capability_bounding_set,
1080 .parameter = (UINT64_C(1) << CAP_SYS_NICE) |
1081 (UINT64_C(1) << CAP_SYS_RESOURCE),
1082 },
1083 {
1084 .id = "CapabilityBoundingSet=~CAP_MKNOD",
3838d22c 1085 .json_field = "CapabilityBoundingSet_CAP_MKNOD",
ec16f3b6
LP
1086 .description_good = "Service cannot create device nodes",
1087 .description_bad = "Service may create device nodes",
1088 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1089 .weight = 500,
1090 .range = 1,
1091 .assess = assess_capability_bounding_set,
1092 .parameter = (UINT64_C(1) << CAP_MKNOD),
1093 },
1094 {
1095 .id = "CapabilityBoundingSet=~CAP_(CHOWN|FSETID|SETFCAP)",
3838d22c 1096 .json_field = "CapabilityBoundingSet_CAP_CHOWN_FSETID_SETFCAP",
ec16f3b6
LP
1097 .description_good = "Service cannot change file ownership/access mode/capabilities",
1098 .description_bad = "Service may change file ownership/access mode/capabilities unrestricted",
1099 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1100 .weight = 1000,
1101 .range = 1,
1102 .assess = assess_capability_bounding_set,
1103 .parameter = (UINT64_C(1) << CAP_CHOWN) |
1104 (UINT64_C(1) << CAP_FSETID) |
1105 (UINT64_C(1) << CAP_SETFCAP),
1106 },
1107 {
1108 .id = "CapabilityBoundingSet=~CAP_(DAC_*|FOWNER|IPC_OWNER)",
3838d22c 1109 .json_field = "CapabilityBoundingSet_CAP_DAC_FOWNER_IPC_OWNER",
ec16f3b6
LP
1110 .description_good = "Service cannot override UNIX file/IPC permission checks",
1111 .description_bad = "Service may override UNIX file/IPC permission checks",
1112 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1113 .weight = 1000,
1114 .range = 1,
1115 .assess = assess_capability_bounding_set,
1116 .parameter = (UINT64_C(1) << CAP_DAC_OVERRIDE) |
1117 (UINT64_C(1) << CAP_DAC_READ_SEARCH) |
1118 (UINT64_C(1) << CAP_FOWNER) |
1119 (UINT64_C(1) << CAP_IPC_OWNER),
1120 },
1121 {
1122 .id = "CapabilityBoundingSet=~CAP_KILL",
3838d22c 1123 .json_field = "CapabilityBoundingSet_CAP_KILL",
ec16f3b6
LP
1124 .description_good = "Service cannot send UNIX signals to arbitrary processes",
1125 .description_bad = "Service may send UNIX signals to arbitrary processes",
1126 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1127 .weight = 500,
1128 .range = 1,
1129 .assess = assess_capability_bounding_set,
1130 .parameter = (UINT64_C(1) << CAP_KILL),
1131 },
1132 {
1133 .id = "CapabilityBoundingSet=~CAP_NET_(BIND_SERVICE|BROADCAST|RAW)",
3838d22c 1134 .json_field = "CapabilityBoundingSet_CAP_NET_BIND_SERVICE_BROADCAST_RAW)",
ec16f3b6
LP
1135 .description_good = "Service has no elevated networking privileges",
1136 .description_bad = "Service has elevated networking privileges",
1137 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1138 .weight = 500,
1139 .range = 1,
1140 .assess = assess_capability_bounding_set,
1141 .parameter = (UINT64_C(1) << CAP_NET_BIND_SERVICE) |
1142 (UINT64_C(1) << CAP_NET_BROADCAST) |
1143 (UINT64_C(1) << CAP_NET_RAW),
1144 },
1145 {
1146 .id = "CapabilityBoundingSet=~CAP_SYS_BOOT",
3838d22c 1147 .json_field = "CapabilityBoundingSet_CAP_SYS_BOOT",
ec16f3b6
LP
1148 .description_good = "Service cannot issue reboot()",
1149 .description_bad = "Service may issue reboot()",
1150 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1151 .weight = 100,
1152 .range = 1,
1153 .assess = assess_capability_bounding_set,
1154 .parameter = (UINT64_C(1) << CAP_SYS_BOOT),
1155 },
1156 {
1157 .id = "CapabilityBoundingSet=~CAP_MAC_*",
3838d22c 1158 .json_field = "CapabilityBoundingSet_CAP_MAC",
ec16f3b6
LP
1159 .description_good = "Service cannot adjust SMACK MAC",
1160 .description_bad = "Service may adjust SMACK MAC",
1161 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1162 .weight = 100,
1163 .range = 1,
1164 .assess = assess_capability_bounding_set,
1165 .parameter = (UINT64_C(1) << CAP_MAC_ADMIN)|
1166 (UINT64_C(1) << CAP_MAC_OVERRIDE),
1167 },
1168 {
1169 .id = "CapabilityBoundingSet=~CAP_LINUX_IMMUTABLE",
3838d22c 1170 .json_field = "CapabilityBoundingSet_CAP_LINUX_IMMUTABLE",
ec16f3b6
LP
1171 .description_good = "Service cannot mark files immutable",
1172 .description_bad = "Service may mark files immutable",
1173 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1174 .weight = 75,
1175 .range = 1,
1176 .assess = assess_capability_bounding_set,
1177 .parameter = (UINT64_C(1) << CAP_LINUX_IMMUTABLE),
1178 },
1179 {
1180 .id = "CapabilityBoundingSet=~CAP_IPC_LOCK",
3838d22c 1181 .json_field = "CapabilityBoundingSet_CAP_IPC_LOCK",
ec16f3b6
LP
1182 .description_good = "Service cannot lock memory into RAM",
1183 .description_bad = "Service may lock memory into RAM",
1184 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1185 .weight = 50,
1186 .range = 1,
1187 .assess = assess_capability_bounding_set,
1188 .parameter = (UINT64_C(1) << CAP_IPC_LOCK),
1189 },
1190 {
1191 .id = "CapabilityBoundingSet=~CAP_SYS_CHROOT",
3838d22c 1192 .json_field = "CapabilityBoundingSet_CAP_SYS_CHROOT",
ec16f3b6
LP
1193 .description_good = "Service cannot issue chroot()",
1194 .description_bad = "Service may issue chroot()",
1195 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1196 .weight = 50,
1197 .range = 1,
1198 .assess = assess_capability_bounding_set,
1199 .parameter = (UINT64_C(1) << CAP_SYS_CHROOT),
1200 },
1201 {
1202 .id = "CapabilityBoundingSet=~CAP_BLOCK_SUSPEND",
3838d22c 1203 .json_field = "CapabilityBoundingSet_CAP_BLOCK_SUSPEND",
ec16f3b6
LP
1204 .description_good = "Service cannot establish wake locks",
1205 .description_bad = "Service may establish wake locks",
1206 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1207 .weight = 25,
1208 .range = 1,
1209 .assess = assess_capability_bounding_set,
1210 .parameter = (UINT64_C(1) << CAP_BLOCK_SUSPEND),
1211 },
1212 {
1213 .id = "CapabilityBoundingSet=~CAP_WAKE_ALARM",
3838d22c 1214 .json_field = "CapabilityBoundingSet_CAP_WAKE_ALARM",
ec16f3b6
LP
1215 .description_good = "Service cannot program timers that wake up the system",
1216 .description_bad = "Service may program timers that wake up the system",
1217 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1218 .weight = 25,
1219 .range = 1,
1220 .assess = assess_capability_bounding_set,
1221 .parameter = (UINT64_C(1) << CAP_WAKE_ALARM),
1222 },
1223 {
1224 .id = "CapabilityBoundingSet=~CAP_LEASE",
3838d22c 1225 .json_field = "CapabilityBoundingSet_CAP_LEASE",
ec16f3b6
LP
1226 .description_good = "Service cannot create file leases",
1227 .description_bad = "Service may create file leases",
1228 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1229 .weight = 25,
1230 .range = 1,
1231 .assess = assess_capability_bounding_set,
1232 .parameter = (UINT64_C(1) << CAP_LEASE),
1233 },
1234 {
1235 .id = "CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG",
3838d22c 1236 .json_field = "CapabilityBoundingSet_CAP_SYS_TTY_CONFIG",
ec16f3b6
LP
1237 .description_good = "Service cannot issue vhangup()",
1238 .description_bad = "Service may issue vhangup()",
1239 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1240 .weight = 25,
1241 .range = 1,
1242 .assess = assess_capability_bounding_set,
1243 .parameter = (UINT64_C(1) << CAP_SYS_TTY_CONFIG),
1244 },
1245 {
1246 .id = "CapabilityBoundingSet=~CAP_SYS_PACCT",
3838d22c 1247 .json_field = "CapabilityBoundingSet_CAP_SYS_PACCT",
ec16f3b6
LP
1248 .description_good = "Service cannot use acct()",
1249 .description_bad = "Service may use acct()",
1250 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1251 .weight = 25,
1252 .range = 1,
1253 .assess = assess_capability_bounding_set,
1254 .parameter = (UINT64_C(1) << CAP_SYS_PACCT),
1255 },
1256 {
1257 .id = "UMask=",
3838d22c 1258 .json_field = "UMask",
ec16f3b6
LP
1259 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#UMask=",
1260 .weight = 100,
1261 .range = 10,
1262 .assess = assess_umask,
1263 },
1264 {
1265 .id = "KeyringMode=",
3838d22c 1266 .json_field = "KeyringMode",
ec16f3b6
LP
1267 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#KeyringMode=",
1268 .description_good = "Service doesn't share key material with other services",
1269 .description_bad = "Service shares key material with other service",
1270 .weight = 1000,
1271 .range = 1,
1272 .assess = assess_keyring_mode,
1273 },
ed125c93
LP
1274 {
1275 .id = "ProtectProc=",
3838d22c 1276 .json_field = "ProtectProc",
ed125c93
LP
1277 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectProc=",
1278 .description_good = "Service has restricted access to process tree (/proc hidepid=)",
1279 .description_bad = "Service has full access to process tree (/proc hidepid=)",
1280 .weight = 1000,
1281 .range = 3,
1282 .assess = assess_protect_proc,
1283 },
1284 {
1285 .id = "ProcSubset=",
3838d22c 1286 .json_field = "ProcSubset",
ed125c93
LP
1287 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProcSubset=",
1288 .description_good = "Service has no access to non-process /proc files (/proc subset=)",
1289 .description_bad = "Service has full access to non-process /proc files (/proc subset=)",
1290 .weight = 10,
1291 .range = 1,
1292 .assess = assess_proc_subset,
1293 },
ec16f3b6
LP
1294 {
1295 .id = "NotifyAccess=",
3838d22c 1296 .json_field = "NotifyAccess",
ec16f3b6
LP
1297 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NotifyAccess=",
1298 .description_good = "Service child processes cannot alter service state",
1299 .description_bad = "Service child processes may alter service state",
1300 .weight = 1000,
1301 .range = 1,
1302 .assess = assess_notify_access,
1303 },
1304 {
1305 .id = "RemoveIPC=",
3838d22c 1306 .json_field = "RemoveIPC",
ec16f3b6
LP
1307 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RemoveIPC=",
1308 .description_good = "Service user cannot leave SysV IPC objects around",
1309 .description_bad = "Service user may leave SysV IPC objects around",
1310 .description_na = "Service runs as root, option does not apply",
1311 .weight = 100,
1312 .range = 1,
1313 .assess = assess_remove_ipc,
1624114d 1314 .offset = offsetof(SecurityInfo, remove_ipc),
ec16f3b6
LP
1315 },
1316 {
1317 .id = "Delegate=",
3838d22c 1318 .json_field = "Delegate",
ec16f3b6
LP
1319 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Delegate=",
1320 .description_good = "Service does not maintain its own delegated control group subtree",
1321 .description_bad = "Service maintains its own delegated control group subtree",
1322 .weight = 100,
1323 .range = 1,
1324 .assess = assess_bool,
1624114d 1325 .offset = offsetof(SecurityInfo, delegate),
ec16f3b6
LP
1326 .parameter = true, /* invert! */
1327 },
1328 {
1329 .id = "RestrictRealtime=",
3838d22c 1330 .json_field = "RestrictRealtime",
ec16f3b6
LP
1331 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictRealtime=",
1332 .description_good = "Service realtime scheduling access is restricted",
1333 .description_bad = "Service may acquire realtime scheduling",
1334 .weight = 500,
1335 .range = 1,
1336 .assess = assess_bool,
1624114d 1337 .offset = offsetof(SecurityInfo, restrict_realtime),
ec16f3b6 1338 },
9d880b70
LP
1339 {
1340 .id = "RestrictSUIDSGID=",
3838d22c 1341 .json_field = "RestrictSUIDSGID",
9d880b70
LP
1342 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictSUIDSGID=",
1343 .description_good = "SUID/SGID file creation by service is restricted",
1344 .description_bad = "Service may create SUID/SGID files",
1345 .weight = 1000,
1346 .range = 1,
1347 .assess = assess_bool,
1624114d 1348 .offset = offsetof(SecurityInfo, restrict_suid_sgid),
9d880b70 1349 },
ec16f3b6 1350 {
c1e6f215
KL
1351 .id = "RestrictNamespaces=~user",
1352 .json_field = "RestrictNamespaces_user",
ec16f3b6
LP
1353 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1354 .description_good = "Service cannot create user namespaces",
1355 .description_bad = "Service may create user namespaces",
1356 .weight = 1500,
1357 .range = 1,
1358 .assess = assess_restrict_namespaces,
1359 .parameter = CLONE_NEWUSER,
1360 },
1361 {
c1e6f215
KL
1362 .id = "RestrictNamespaces=~mnt",
1363 .json_field = "RestrictNamespaces_mnt",
ec16f3b6
LP
1364 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1365 .description_good = "Service cannot create file system namespaces",
1366 .description_bad = "Service may create file system namespaces",
1367 .weight = 500,
1368 .range = 1,
1369 .assess = assess_restrict_namespaces,
1370 .parameter = CLONE_NEWNS,
1371 },
1372 {
c1e6f215
KL
1373 .id = "RestrictNamespaces=~ipc",
1374 .json_field = "RestrictNamespaces_ipc",
ec16f3b6
LP
1375 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1376 .description_good = "Service cannot create IPC namespaces",
1377 .description_bad = "Service may create IPC namespaces",
1378 .weight = 500,
1379 .range = 1,
1380 .assess = assess_restrict_namespaces,
1381 .parameter = CLONE_NEWIPC,
1382 },
1383 {
c1e6f215
KL
1384 .id = "RestrictNamespaces=~pid",
1385 .json_field = "RestrictNamespaces_pid",
ec16f3b6
LP
1386 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1387 .description_good = "Service cannot create process namespaces",
1388 .description_bad = "Service may create process namespaces",
1389 .weight = 500,
1390 .range = 1,
1391 .assess = assess_restrict_namespaces,
1392 .parameter = CLONE_NEWPID,
1393 },
1394 {
c1e6f215
KL
1395 .id = "RestrictNamespaces=~cgroup",
1396 .json_field = "RestrictNamespaces_cgroup",
ec16f3b6
LP
1397 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1398 .description_good = "Service cannot create cgroup namespaces",
1399 .description_bad = "Service may create cgroup namespaces",
1400 .weight = 500,
1401 .range = 1,
1402 .assess = assess_restrict_namespaces,
1403 .parameter = CLONE_NEWCGROUP,
1404 },
1405 {
c1e6f215
KL
1406 .id = "RestrictNamespaces=~net",
1407 .json_field = "RestrictNamespaces_net",
ec16f3b6
LP
1408 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1409 .description_good = "Service cannot create network namespaces",
1410 .description_bad = "Service may create network namespaces",
1411 .weight = 500,
1412 .range = 1,
1413 .assess = assess_restrict_namespaces,
1414 .parameter = CLONE_NEWNET,
1415 },
1416 {
c1e6f215
KL
1417 .id = "RestrictNamespaces=~uts",
1418 .json_field = "RestrictNamespaces_uts",
ec16f3b6
LP
1419 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1420 .description_good = "Service cannot create hostname namespaces",
1421 .description_bad = "Service may create hostname namespaces",
1422 .weight = 100,
1423 .range = 1,
1424 .assess = assess_restrict_namespaces,
1425 .parameter = CLONE_NEWUTS,
1426 },
1427 {
1428 .id = "RestrictAddressFamilies=~AF_(INET|INET6)",
3838d22c 1429 .json_field = "RestrictAddressFamilies_AF_INET_INET6",
ec16f3b6
LP
1430 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1431 .description_good = "Service cannot allocate Internet sockets",
1432 .description_bad = "Service may allocate Internet sockets",
1433 .weight = 1500,
1434 .range = 1,
1435 .assess = assess_bool,
1624114d 1436 .offset = offsetof(SecurityInfo, restrict_address_family_inet),
ec16f3b6
LP
1437 },
1438 {
1439 .id = "RestrictAddressFamilies=~AF_UNIX",
3838d22c 1440 .json_field = "RestrictAddressFamilies_AF_UNIX",
ec16f3b6
LP
1441 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1442 .description_good = "Service cannot allocate local sockets",
1443 .description_bad = "Service may allocate local sockets",
1444 .weight = 25,
1445 .range = 1,
1446 .assess = assess_bool,
1624114d 1447 .offset = offsetof(SecurityInfo, restrict_address_family_unix),
ec16f3b6
LP
1448 },
1449 {
1450 .id = "RestrictAddressFamilies=~AF_NETLINK",
3838d22c 1451 .json_field = "RestrictAddressFamilies_AF_NETLINK",
ec16f3b6
LP
1452 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1453 .description_good = "Service cannot allocate netlink sockets",
1454 .description_bad = "Service may allocate netlink sockets",
1455 .weight = 200,
1456 .range = 1,
1457 .assess = assess_bool,
1624114d 1458 .offset = offsetof(SecurityInfo, restrict_address_family_netlink),
ec16f3b6
LP
1459 },
1460 {
1461 .id = "RestrictAddressFamilies=~AF_PACKET",
3838d22c 1462 .json_field = "RestrictAddressFamilies_AF_PACKET",
ec16f3b6
LP
1463 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1464 .description_good = "Service cannot allocate packet sockets",
1465 .description_bad = "Service may allocate packet sockets",
1466 .weight = 1000,
1467 .range = 1,
1468 .assess = assess_bool,
1624114d 1469 .offset = offsetof(SecurityInfo, restrict_address_family_packet),
ec16f3b6
LP
1470 },
1471 {
1472 .id = "RestrictAddressFamilies=~…",
3838d22c 1473 .json_field = "RestrictAddressFamilies_OTHER",
ec16f3b6
LP
1474 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1475 .description_good = "Service cannot allocate exotic sockets",
1476 .description_bad = "Service may allocate exotic sockets",
1477 .weight = 1250,
1478 .range = 1,
1479 .assess = assess_bool,
1624114d 1480 .offset = offsetof(SecurityInfo, restrict_address_family_other),
ec16f3b6 1481 },
1449b0f8 1482#if HAVE_SECCOMP
ec16f3b6
LP
1483 {
1484 .id = "SystemCallArchitectures=",
3838d22c 1485 .json_field = "SystemCallArchitectures",
ec16f3b6
LP
1486 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallArchitectures=",
1487 .weight = 1000,
1488 .range = 10,
1489 .assess = assess_system_call_architectures,
1490 },
1491 {
1492 .id = "SystemCallFilter=~@swap",
3838d22c 1493 .json_field = "SystemCallFilter_swap",
ec16f3b6
LP
1494 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1495 .weight = 1000,
1496 .range = 10,
1497 .assess = assess_system_call_filter,
1498 .parameter = SYSCALL_FILTER_SET_SWAP,
1499 },
1500 {
1501 .id = "SystemCallFilter=~@obsolete",
3838d22c 1502 .json_field = "SystemCallFilter_obsolete",
ec16f3b6
LP
1503 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1504 .weight = 250,
1505 .range = 10,
1506 .assess = assess_system_call_filter,
1507 .parameter = SYSCALL_FILTER_SET_OBSOLETE,
1508 },
1509 {
1510 .id = "SystemCallFilter=~@clock",
3838d22c 1511 .json_field = "SystemCallFilter_clock",
ec16f3b6
LP
1512 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1513 .weight = 1000,
1514 .range = 10,
1515 .assess = assess_system_call_filter,
1516 .parameter = SYSCALL_FILTER_SET_CLOCK,
1517 },
1518 {
1519 .id = "SystemCallFilter=~@cpu-emulation",
3838d22c 1520 .json_field = "SystemCallFilter_cpu_emulation",
ec16f3b6
LP
1521 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1522 .weight = 250,
1523 .range = 10,
1524 .assess = assess_system_call_filter,
1525 .parameter = SYSCALL_FILTER_SET_CPU_EMULATION,
1526 },
1527 {
1528 .id = "SystemCallFilter=~@debug",
3838d22c 1529 .json_field = "SystemCallFilter_debug",
ec16f3b6
LP
1530 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1531 .weight = 1000,
1532 .range = 10,
1533 .assess = assess_system_call_filter,
1534 .parameter = SYSCALL_FILTER_SET_DEBUG,
1535 },
1536 {
1537 .id = "SystemCallFilter=~@mount",
3838d22c 1538 .json_field = "SystemCallFilter_mount",
ec16f3b6
LP
1539 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1540 .weight = 1000,
1541 .range = 10,
1542 .assess = assess_system_call_filter,
1543 .parameter = SYSCALL_FILTER_SET_MOUNT,
1544 },
1545 {
1546 .id = "SystemCallFilter=~@module",
3838d22c 1547 .json_field = "SystemCallFilter_module",
ec16f3b6
LP
1548 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1549 .weight = 1000,
1550 .range = 10,
1551 .assess = assess_system_call_filter,
1552 .parameter = SYSCALL_FILTER_SET_MODULE,
1553 },
1554 {
1555 .id = "SystemCallFilter=~@raw-io",
3838d22c 1556 .json_field = "SystemCallFilter_raw_io",
ec16f3b6
LP
1557 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1558 .weight = 1000,
1559 .range = 10,
1560 .assess = assess_system_call_filter,
1561 .parameter = SYSCALL_FILTER_SET_RAW_IO,
1562 },
1563 {
1564 .id = "SystemCallFilter=~@reboot",
3838d22c 1565 .json_field = "SystemCallFilter_reboot",
ec16f3b6
LP
1566 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1567 .weight = 1000,
1568 .range = 10,
1569 .assess = assess_system_call_filter,
1570 .parameter = SYSCALL_FILTER_SET_REBOOT,
1571 },
1572 {
1573 .id = "SystemCallFilter=~@privileged",
3838d22c 1574 .json_field = "SystemCallFilter_privileged",
ec16f3b6
LP
1575 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1576 .weight = 700,
1577 .range = 10,
1578 .assess = assess_system_call_filter,
1579 .parameter = SYSCALL_FILTER_SET_PRIVILEGED,
1580 },
1581 {
1582 .id = "SystemCallFilter=~@resources",
3838d22c 1583 .json_field = "SystemCallFilter_resources",
ec16f3b6
LP
1584 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1585 .weight = 700,
1586 .range = 10,
1587 .assess = assess_system_call_filter,
1588 .parameter = SYSCALL_FILTER_SET_RESOURCES,
1589 },
3a5d89fa 1590#endif
ec16f3b6
LP
1591 {
1592 .id = "IPAddressDeny=",
3838d22c 1593 .json_field = "IPAddressDeny",
ec16f3b6
LP
1594 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#IPAddressDeny=",
1595 .weight = 1000,
1596 .range = 10,
1597 .assess = assess_ip_address_allow,
1598 },
1599 {
1600 .id = "DeviceAllow=",
3838d22c 1601 .json_field = "DeviceAllow",
ec16f3b6
LP
1602 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#DeviceAllow=",
1603 .weight = 1000,
1604 .range = 10,
1605 .assess = assess_device_allow,
1606 },
1607 {
1608 .id = "AmbientCapabilities=",
3838d22c 1609 .json_field = "AmbientCapabilities",
ec16f3b6
LP
1610 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#AmbientCapabilities=",
1611 .description_good = "Service process does not receive ambient capabilities",
1612 .description_bad = "Service process receives ambient capabilities",
1613 .weight = 500,
1614 .range = 1,
1615 .assess = assess_ambient_capabilities,
1616 },
1617};
1618
3838d22c
MG
1619static JsonVariant* security_assessor_find_in_policy(const struct security_assessor *a, JsonVariant *policy, const char *name) {
1620 JsonVariant *item;
1621 assert(a);
1622
1623 if (!policy)
1624 return NULL;
1625 if (!json_variant_is_object(policy)) {
05403363 1626 log_debug("Specified policy is not a JSON object, ignoring.");
3838d22c
MG
1627 return NULL;
1628 }
1629
1630 item = json_variant_by_key(policy, a->json_field);
1631 if (!item)
1632 return NULL;
1633 if (!json_variant_is_object(item)) {
1634 log_debug("Item for '%s' in policy JSON object is not an object, ignoring.", a->id);
1635 return NULL;
1636 }
1637
1638 return name ? json_variant_by_key(item, name) : item;
1639}
1640
1641static uint64_t access_weight(const struct security_assessor *a, JsonVariant *policy) {
1642 JsonVariant *val;
1643
1644 assert(a);
1645
1646 val = security_assessor_find_in_policy(a, policy, "weight");
1647 if (val) {
1648 if (json_variant_is_unsigned(val))
1649 return json_variant_unsigned(val);
1650 log_debug("JSON field 'weight' of policy for %s is not an unsigned integer, ignoring.", a->id);
1651 }
1652
1653 return a->weight;
1654}
1655
1656static uint64_t access_range(const struct security_assessor *a, JsonVariant *policy) {
1657 JsonVariant *val;
1658
1659 assert(a);
1660
1661 val = security_assessor_find_in_policy(a, policy, "range");
1662 if (val) {
1663 if (json_variant_is_unsigned(val))
1664 return json_variant_unsigned(val);
1665 log_debug("JSON field 'range' of policy for %s is not an unsigned integer, ignoring.", a->id);
1666 }
1667
1668 return a->range;
1669}
1670
1671static const char *access_description_na(const struct security_assessor *a, JsonVariant *policy) {
1672 JsonVariant *val;
1673
1674 assert(a);
1675
1676 val = security_assessor_find_in_policy(a, policy, "description_na");
1677 if (val) {
1678 if (json_variant_is_string(val))
1679 return json_variant_string(val);
1680 log_debug("JSON field 'description_na' of policy for %s is not a string, ignoring.", a->id);
1681 }
1682
1683 return a->description_na;
1684}
1685
1686static const char *access_description_good(const struct security_assessor *a, JsonVariant *policy) {
1687 JsonVariant *val;
1688
1689 assert(a);
1690
1691 val = security_assessor_find_in_policy(a, policy, "description_good");
1692 if (val) {
1693 if (json_variant_is_string(val))
1694 return json_variant_string(val);
1695 log_debug("JSON field 'description_good' of policy for %s is not a string, ignoring.", a->id);
1696 }
1697
1698 return a->description_good;
1699}
1700
1701static const char *access_description_bad(const struct security_assessor *a, JsonVariant *policy) {
1702 JsonVariant *val;
1703
1704 assert(a);
1705
1706 val = security_assessor_find_in_policy(a, policy, "description_bad");
1707 if (val) {
1708 if (json_variant_is_string(val))
1709 return json_variant_string(val);
1710 log_debug("JSON field 'description_bad' of policy for %s is not a string, ignoring.", a->id);
1711 }
1712
1713 return a->description_bad;
1714}
1715
1716static int assess(const SecurityInfo *info,
1717 Table *overview_table,
1718 AnalyzeSecurityFlags flags,
1719 unsigned threshold,
4b4a8ef7
MG
1720 JsonVariant *policy,
1721 PagerFlags pager_flags,
1722 JsonFormatFlags json_format_flags) {
3838d22c 1723
ec16f3b6
LP
1724 static const struct {
1725 uint64_t exposure;
1726 const char *name;
1727 const char *color;
1728 SpecialGlyph smiley;
1729 } badness_table[] = {
9a6f746f
LP
1730 { 100, "DANGEROUS", ANSI_HIGHLIGHT_RED, SPECIAL_GLYPH_DEPRESSED_SMILEY },
1731 { 90, "UNSAFE", ANSI_HIGHLIGHT_RED, SPECIAL_GLYPH_UNHAPPY_SMILEY },
1732 { 75, "EXPOSED", ANSI_HIGHLIGHT_YELLOW, SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY },
1733 { 50, "MEDIUM", NULL, SPECIAL_GLYPH_NEUTRAL_SMILEY },
1734 { 10, "OK", ANSI_HIGHLIGHT_GREEN, SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY },
1735 { 1, "SAFE", ANSI_HIGHLIGHT_GREEN, SPECIAL_GLYPH_HAPPY_SMILEY },
1736 { 0, "PERFECT", ANSI_HIGHLIGHT_GREEN, SPECIAL_GLYPH_ECSTATIC_SMILEY },
ec16f3b6
LP
1737 };
1738
1739 uint64_t badness_sum = 0, weight_sum = 0, exposure;
1740 _cleanup_(table_unrefp) Table *details_table = NULL;
1741 size_t i;
1742 int r;
1743
1744 if (!FLAGS_SET(flags, ANALYZE_SECURITY_SHORT)) {
4b4a8ef7 1745 details_table = table_new(" ", "name", "json_field", "description", "weight", "badness", "range", "exposure");
ec16f3b6
LP
1746 if (!details_table)
1747 return log_oom();
1748
4b4a8ef7
MG
1749 r = table_set_json_field_name(details_table, 0, "set");
1750 if (r < 0)
1751 return log_error_errno(r, "Failed to set JSON field name of column 0: %m");
1752
ef1e0b9a 1753 (void) table_set_sort(details_table, (size_t) 3, (size_t) 1);
ec16f3b6
LP
1754 (void) table_set_reverse(details_table, 3, true);
1755
1756 if (getenv_bool("SYSTEMD_ANALYZE_DEBUG") <= 0)
4b4a8ef7 1757 (void) table_set_display(details_table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 7);
ec16f3b6
LP
1758 }
1759
1760 for (i = 0; i < ELEMENTSOF(security_assessor_table); i++) {
1761 const struct security_assessor *a = security_assessor_table + i;
1762 _cleanup_free_ char *d = NULL;
1763 uint64_t badness;
1764 void *data;
3838d22c
MG
1765 uint64_t weight = access_weight(a, policy);
1766 uint64_t range = access_range(a, policy);
ec16f3b6 1767
1ace223c 1768 data = (uint8_t *) info + a->offset;
ec16f3b6
LP
1769
1770 if (a->default_dependencies_only && !info->default_dependencies) {
1771 badness = UINT64_MAX;
77552b95 1772 d = strdup("Service runs in special boot phase, option is not appropriate");
ec16f3b6 1773 if (!d)
82100ef4
LB
1774 return log_oom();
1775 } else if (weight == 0) {
1776 badness = UINT64_MAX;
1777 d = strdup("Option excluded by policy, skipping");
1778 if (!d)
ec16f3b6
LP
1779 return log_oom();
1780 } else {
1781 r = a->assess(a, info, data, &badness, &d);
1782 if (r < 0)
1783 return r;
1784 }
1785
3838d22c 1786 assert(range > 0);
ec16f3b6
LP
1787
1788 if (badness != UINT64_MAX) {
3838d22c 1789 assert(badness <= range);
ec16f3b6 1790
3838d22c
MG
1791 badness_sum += DIV_ROUND_UP(badness * weight, range);
1792 weight_sum += weight;
ec16f3b6
LP
1793 }
1794
1795 if (details_table) {
4b4a8ef7
MG
1796 const char *description, *color = NULL;
1797 int checkmark;
ec16f3b6
LP
1798
1799 if (badness == UINT64_MAX) {
4b4a8ef7 1800 checkmark = -1;
3838d22c 1801 description = access_description_na(a, policy);
ec16f3b6
LP
1802 color = NULL;
1803 } else if (badness == a->range) {
4b4a8ef7 1804 checkmark = 0;
3838d22c 1805 description = access_description_bad(a, policy);
ec16f3b6
LP
1806 color = ansi_highlight_red();
1807 } else if (badness == 0) {
4b4a8ef7 1808 checkmark = 1;
3838d22c 1809 description = access_description_good(a, policy);
ec16f3b6
LP
1810 color = ansi_highlight_green();
1811 } else {
4b4a8ef7 1812 checkmark = 0;
ec16f3b6
LP
1813 description = NULL;
1814 color = ansi_highlight_red();
1815 }
1816
1817 if (d)
1818 description = d;
1819
4b4a8ef7
MG
1820 if (checkmark < 0) {
1821 r = table_add_many(details_table, TABLE_EMPTY);
1822 if (r < 0)
1823 return table_log_add_error(r);
1824 } else {
1825 r = table_add_many(details_table,
1826 TABLE_BOOLEAN_CHECKMARK, checkmark > 0,
1827 TABLE_SET_MINIMUM_WIDTH, 1,
1828 TABLE_SET_MAXIMUM_WIDTH, 1,
1829 TABLE_SET_ELLIPSIZE_PERCENT, 0,
1830 TABLE_SET_COLOR, color);
1831 if (r < 0)
1832 return table_log_add_error(r);
1833 }
3838d22c 1834
2cb434cf 1835 r = table_add_many(details_table,
4b4a8ef7
MG
1836 TABLE_STRING, a->id, TABLE_SET_URL, a->url,
1837 TABLE_STRING, a->json_field,
2cb434cf 1838 TABLE_STRING, description,
3838d22c 1839 TABLE_UINT64, weight, TABLE_SET_ALIGN_PERCENT, 100,
2cb434cf 1840 TABLE_UINT64, badness, TABLE_SET_ALIGN_PERCENT, 100,
3838d22c 1841 TABLE_UINT64, range, TABLE_SET_ALIGN_PERCENT, 100,
2cb434cf 1842 TABLE_EMPTY, TABLE_SET_ALIGN_PERCENT, 100);
ec16f3b6 1843 if (r < 0)
9c46b437 1844 return table_log_add_error(r);
ec16f3b6
LP
1845 }
1846 }
1847
28a06f5a
JS
1848 assert(weight_sum > 0);
1849
ec16f3b6
LP
1850 if (details_table) {
1851 size_t row;
1852
1853 for (row = 1; row < table_get_rows(details_table); row++) {
1854 char buf[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1];
1855 const uint64_t *weight, *badness, *range;
1856 TableCell *cell;
1857 uint64_t x;
1858
4b4a8ef7
MG
1859 assert_se(weight = table_get_at(details_table, row, 4));
1860 assert_se(badness = table_get_at(details_table, row, 5));
1861 assert_se(range = table_get_at(details_table, row, 6));
ec16f3b6
LP
1862
1863 if (*badness == UINT64_MAX || *badness == 0)
1864 continue;
1865
4b4a8ef7 1866 assert_se(cell = table_get_cell(details_table, row, 7));
ec16f3b6
LP
1867
1868 x = DIV_ROUND_UP(DIV_ROUND_UP(*badness * *weight * 100U, *range), weight_sum);
1869 xsprintf(buf, "%" PRIu64 ".%" PRIu64, x / 10, x % 10);
1870
1871 r = table_update(details_table, cell, TABLE_STRING, buf);
1872 if (r < 0)
1873 return log_error_errno(r, "Failed to update cell in table: %m");
1874 }
1875
4b4a8ef7
MG
1876 if (json_format_flags & JSON_FORMAT_OFF) {
1877 r = table_hide_column_from_display(details_table, (size_t) 2);
1878 if (r < 0)
1879 return log_error_errno(r, "Failed to set columns to display: %m");
1880 }
1881
1882 r = table_print_with_pager(details_table, json_format_flags, pager_flags, /* show_header= */true);
ec16f3b6
LP
1883 if (r < 0)
1884 return log_error_errno(r, "Failed to output table: %m");
1885 }
1886
1887 exposure = DIV_ROUND_UP(badness_sum * 100U, weight_sum);
1888
1889 for (i = 0; i < ELEMENTSOF(badness_table); i++)
1890 if (exposure >= badness_table[i].exposure)
1891 break;
1892
1893 assert(i < ELEMENTSOF(badness_table));
1894
4b4a8ef7 1895 if (details_table && (json_format_flags & JSON_FORMAT_OFF)) {
ec16f3b6
LP
1896 _cleanup_free_ char *clickable = NULL;
1897 const char *name;
1898
1899 /* If we shall output the details table, also print the brief summary underneath */
1900
1901 if (info->fragment_path) {
1902 r = terminal_urlify_path(info->fragment_path, info->id, &clickable);
1903 if (r < 0)
1904 return log_oom();
1905
1906 name = clickable;
1907 } else
1908 name = info->id;
1909
1910 printf("\n%s %sOverall exposure level for %s%s: %s%" PRIu64 ".%" PRIu64 " %s%s %s\n",
fc03e80c 1911 special_glyph(SPECIAL_GLYPH_ARROW_RIGHT),
ec16f3b6
LP
1912 ansi_highlight(),
1913 name,
1914 ansi_normal(),
1915 colors_enabled() ? strempty(badness_table[i].color) : "",
1916 exposure / 10, exposure % 10,
1917 badness_table[i].name,
1918 ansi_normal(),
1919 special_glyph(badness_table[i].smiley));
1920 }
1921
1922 fflush(stdout);
1923
1924 if (overview_table) {
1925 char buf[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1];
9c46b437 1926 _cleanup_free_ char *url = NULL;
ec16f3b6 1927
ec16f3b6 1928 if (info->fragment_path) {
ec16f3b6
LP
1929 r = file_url_from_path(info->fragment_path, &url);
1930 if (r < 0)
1931 return log_error_errno(r, "Failed to generate URL from path: %m");
ec16f3b6
LP
1932 }
1933
1934 xsprintf(buf, "%" PRIu64 ".%" PRIu64, exposure / 10, exposure % 10);
ec16f3b6 1935
9c46b437
YW
1936 r = table_add_many(overview_table,
1937 TABLE_STRING, info->id,
1938 TABLE_SET_URL, url,
1939 TABLE_STRING, buf,
1940 TABLE_SET_ALIGN_PERCENT, 100,
1941 TABLE_STRING, badness_table[i].name,
1942 TABLE_SET_COLOR, strempty(badness_table[i].color),
1943 TABLE_STRING, special_glyph(badness_table[i].smiley));
ec16f3b6 1944 if (r < 0)
9c46b437 1945 return table_log_add_error(r);
ec16f3b6
LP
1946 }
1947
dfbda879
MG
1948 /* Return error when overall exposure level is over threshold */
1949 if (exposure > threshold)
1950 return -EINVAL;
1951
ec16f3b6
LP
1952 return 0;
1953}
1954
1624114d
MG
1955static int property_read_restrict_namespaces(
1956 sd_bus *bus,
1957 const char *member,
1958 sd_bus_message *m,
1959 sd_bus_error *error,
1960 void *userdata) {
1961
99534007 1962 SecurityInfo *info = ASSERT_PTR(userdata);
1624114d
MG
1963 int r;
1964 uint64_t namespaces;
1965
1966 assert(bus);
1967 assert(member);
1968 assert(m);
1624114d
MG
1969
1970 r = sd_bus_message_read(m, "t", &namespaces);
1971 if (r < 0)
1972 return r;
1973
1974 info->restrict_namespaces = (unsigned long long) namespaces;
1975
1976 return 0;
1977}
1978
1979static int property_read_umask(
1980 sd_bus *bus,
1981 const char *member,
1982 sd_bus_message *m,
1983 sd_bus_error *error,
1984 void *userdata) {
1985
99534007 1986 SecurityInfo *info = ASSERT_PTR(userdata);
1624114d
MG
1987 int r;
1988 uint32_t umask;
1989
1990 assert(bus);
1991 assert(member);
1992 assert(m);
1624114d
MG
1993
1994 r = sd_bus_message_read(m, "u", &umask);
1995 if (r < 0)
1996 return r;
1997
1998 info->_umask = (mode_t) umask;
1999
2000 return 0;
2001}
2002
ec16f3b6
LP
2003static int property_read_restrict_address_families(
2004 sd_bus *bus,
2005 const char *member,
2006 sd_bus_message *m,
2007 sd_bus_error *error,
2008 void *userdata) {
2009
1624114d 2010 SecurityInfo *info = userdata;
6b000af4 2011 int allow_list, r;
ec16f3b6
LP
2012
2013 assert(bus);
2014 assert(member);
2015 assert(m);
2016
2017 r = sd_bus_message_enter_container(m, 'r', "bas");
2018 if (r < 0)
2019 return r;
2020
6b000af4 2021 r = sd_bus_message_read(m, "b", &allow_list);
ec16f3b6
LP
2022 if (r < 0)
2023 return r;
2024
2025 info->restrict_address_family_inet =
2026 info->restrict_address_family_unix =
2027 info->restrict_address_family_netlink =
2028 info->restrict_address_family_packet =
6b000af4 2029 info->restrict_address_family_other = allow_list;
ec16f3b6
LP
2030
2031 r = sd_bus_message_enter_container(m, 'a', "s");
2032 if (r < 0)
2033 return r;
2034
2035 for (;;) {
2036 const char *name;
2037
2038 r = sd_bus_message_read(m, "s", &name);
2039 if (r < 0)
2040 return r;
2041 if (r == 0)
2042 break;
2043
2044 if (STR_IN_SET(name, "AF_INET", "AF_INET6"))
6b000af4 2045 info->restrict_address_family_inet = !allow_list;
ec16f3b6 2046 else if (streq(name, "AF_UNIX"))
6b000af4 2047 info->restrict_address_family_unix = !allow_list;
ec16f3b6 2048 else if (streq(name, "AF_NETLINK"))
6b000af4 2049 info->restrict_address_family_netlink = !allow_list;
ec16f3b6 2050 else if (streq(name, "AF_PACKET"))
6b000af4 2051 info->restrict_address_family_packet = !allow_list;
ec16f3b6 2052 else
6b000af4 2053 info->restrict_address_family_other = !allow_list;
ec16f3b6
LP
2054 }
2055
2056 r = sd_bus_message_exit_container(m);
2057 if (r < 0)
2058 return r;
2059
2060 return sd_bus_message_exit_container(m);
2061}
2062
1624114d
MG
2063static int property_read_syscall_archs(
2064 sd_bus *bus,
2065 const char *member,
2066 sd_bus_message *m,
2067 sd_bus_error *error,
2068 void *userdata) {
2069
99534007 2070 SecurityInfo *info = ASSERT_PTR(userdata);
1624114d
MG
2071 int r;
2072
2073 assert(bus);
2074 assert(member);
2075 assert(m);
1624114d
MG
2076
2077 r = sd_bus_message_enter_container(m, 'a', "s");
2078 if (r < 0)
2079 return r;
2080
2081 for (;;) {
2082 const char *name;
2083
2084 r = sd_bus_message_read(m, "s", &name);
2085 if (r < 0)
2086 return r;
2087 if (r == 0)
2088 break;
2089
2090 r = set_put_strdup(&info->system_call_architectures, name);
2091 if (r < 0)
2092 return r;
2093 }
2094
2095 return sd_bus_message_exit_container(m);
2096}
2097
ec16f3b6
LP
2098static int property_read_system_call_filter(
2099 sd_bus *bus,
2100 const char *member,
2101 sd_bus_message *m,
2102 sd_bus_error *error,
2103 void *userdata) {
2104
1624114d 2105 SecurityInfo *info = userdata;
6b000af4 2106 int allow_list, r;
ec16f3b6
LP
2107
2108 assert(bus);
2109 assert(member);
2110 assert(m);
2111
2112 r = sd_bus_message_enter_container(m, 'r', "bas");
2113 if (r < 0)
2114 return r;
2115
6b000af4 2116 r = sd_bus_message_read(m, "b", &allow_list);
ec16f3b6
LP
2117 if (r < 0)
2118 return r;
2119
6b000af4 2120 info->system_call_filter_allow_list = allow_list;
ec16f3b6
LP
2121
2122 r = sd_bus_message_enter_container(m, 'a', "s");
2123 if (r < 0)
2124 return r;
2125
2126 for (;;) {
2127 const char *name;
2128
2129 r = sd_bus_message_read(m, "s", &name);
2130 if (r < 0)
2131 return r;
2132 if (r == 0)
2133 break;
2134
5862e556
YW
2135 /* ignore errno or action after colon */
2136 r = set_put_strndup(&info->system_call_filter, name, strchrnul(name, ':') - name);
ec16f3b6
LP
2137 if (r < 0)
2138 return r;
2139 }
2140
2141 r = sd_bus_message_exit_container(m);
2142 if (r < 0)
2143 return r;
2144
2145 return sd_bus_message_exit_container(m);
2146}
2147
2148static int property_read_ip_address_allow(
2149 sd_bus *bus,
2150 const char *member,
2151 sd_bus_message *m,
2152 sd_bus_error *error,
2153 void *userdata) {
2154
1624114d 2155 SecurityInfo *info = userdata;
ec16f3b6
LP
2156 bool deny_ipv4 = false, deny_ipv6 = false;
2157 int r;
2158
2159 assert(bus);
2160 assert(member);
2161 assert(m);
2162
2163 r = sd_bus_message_enter_container(m, 'a', "(iayu)");
2164 if (r < 0)
2165 return r;
2166
2167 for (;;) {
2168 const void *data;
2169 size_t size;
2170 int32_t family;
2171 uint32_t prefixlen;
2172
2173 r = sd_bus_message_enter_container(m, 'r', "iayu");
2174 if (r < 0)
2175 return r;
2176 if (r == 0)
2177 break;
2178
2179 r = sd_bus_message_read(m, "i", &family);
2180 if (r < 0)
2181 return r;
2182
2183 r = sd_bus_message_read_array(m, 'y', &data, &size);
2184 if (r < 0)
2185 return r;
2186
2187 r = sd_bus_message_read(m, "u", &prefixlen);
2188 if (r < 0)
2189 return r;
2190
2191 r = sd_bus_message_exit_container(m);
2192 if (r < 0)
2193 return r;
2194
2195 if (streq(member, "IPAddressAllow")) {
2196 union in_addr_union u;
2197
2198 if (family == AF_INET && size == 4 && prefixlen == 8)
2199 memcpy(&u.in, data, size);
2200 else if (family == AF_INET6 && size == 16 && prefixlen == 128)
2201 memcpy(&u.in6, data, size);
2202 else {
2203 info->ip_address_allow_other = true;
2204 continue;
2205 }
2206
2207 if (in_addr_is_localhost(family, &u))
2208 info->ip_address_allow_localhost = true;
2209 else
2210 info->ip_address_allow_other = true;
2211 } else {
2212 assert(streq(member, "IPAddressDeny"));
2213
2214 if (family == AF_INET && size == 4 && prefixlen == 0)
2215 deny_ipv4 = true;
2216 else if (family == AF_INET6 && size == 16 && prefixlen == 0)
2217 deny_ipv6 = true;
2218 }
2219 }
2220
2221 info->ip_address_deny_all = deny_ipv4 && deny_ipv6;
2222
2223 return sd_bus_message_exit_container(m);
2224}
2225
fab34748
KL
2226static int property_read_ip_filters(
2227 sd_bus *bus,
2228 const char *member,
2229 sd_bus_message *m,
2230 sd_bus_error *error,
2231 void *userdata) {
2232
1624114d 2233 SecurityInfo *info = userdata;
fab34748
KL
2234 _cleanup_(strv_freep) char **l = NULL;
2235 int r;
2236
2237 assert(bus);
2238 assert(member);
2239 assert(m);
2240
2241 r = sd_bus_message_read_strv(m, &l);
2242 if (r < 0)
2243 return r;
2244
2245 if (streq(member, "IPIngressFilterPath"))
2246 info->ip_filters_custom_ingress = !strv_isempty(l);
2247 else if (streq(member, "IPEgressFilterPath"))
3da57008 2248 info->ip_filters_custom_egress = !strv_isempty(l);
fab34748
KL
2249
2250 return 0;
2251}
2252
ec16f3b6
LP
2253static int property_read_device_allow(
2254 sd_bus *bus,
2255 const char *member,
2256 sd_bus_message *m,
2257 sd_bus_error *error,
2258 void *userdata) {
2259
1624114d 2260 SecurityInfo *info = userdata;
ec16f3b6
LP
2261 int r;
2262
2263 assert(bus);
2264 assert(member);
2265 assert(m);
2266
2267 r = sd_bus_message_enter_container(m, 'a', "(ss)");
2268 if (r < 0)
2269 return r;
2270
2271 for (;;) {
2272 const char *name, *policy;
2273
2274 r = sd_bus_message_read(m, "(ss)", &name, &policy);
2275 if (r < 0)
2276 return r;
2277 if (r == 0)
2278 break;
2279
6a59dfa1
LB
2280 r = strv_extendf(&info->device_allow, "%s:%s", name, policy);
2281 if (r < 0)
2282 return r;
ec16f3b6
LP
2283 }
2284
ec16f3b6
LP
2285 return sd_bus_message_exit_container(m);
2286}
2287
1624114d 2288static int acquire_security_info(sd_bus *bus, const char *name, SecurityInfo *info, AnalyzeSecurityFlags flags) {
ec16f3b6
LP
2289
2290 static const struct bus_properties_map security_map[] = {
1624114d
MG
2291 { "AmbientCapabilities", "t", NULL, offsetof(SecurityInfo, ambient_capabilities) },
2292 { "CapabilityBoundingSet", "t", NULL, offsetof(SecurityInfo, capability_bounding_set) },
2293 { "DefaultDependencies", "b", NULL, offsetof(SecurityInfo, default_dependencies) },
2294 { "Delegate", "b", NULL, offsetof(SecurityInfo, delegate) },
2295 { "DeviceAllow", "a(ss)", property_read_device_allow, 0 },
2296 { "DevicePolicy", "s", NULL, offsetof(SecurityInfo, device_policy) },
2297 { "DynamicUser", "b", NULL, offsetof(SecurityInfo, dynamic_user) },
2298 { "FragmentPath", "s", NULL, offsetof(SecurityInfo, fragment_path) },
2299 { "IPAddressAllow", "a(iayu)", property_read_ip_address_allow, 0 },
2300 { "IPAddressDeny", "a(iayu)", property_read_ip_address_allow, 0 },
2301 { "IPIngressFilterPath", "as", property_read_ip_filters, 0 },
2302 { "IPEgressFilterPath", "as", property_read_ip_filters, 0 },
2303 { "Id", "s", NULL, offsetof(SecurityInfo, id) },
2304 { "KeyringMode", "s", NULL, offsetof(SecurityInfo, keyring_mode) },
2305 { "ProtectProc", "s", NULL, offsetof(SecurityInfo, protect_proc) },
2306 { "ProcSubset", "s", NULL, offsetof(SecurityInfo, proc_subset) },
2307 { "LoadState", "s", NULL, offsetof(SecurityInfo, load_state) },
2308 { "LockPersonality", "b", NULL, offsetof(SecurityInfo, lock_personality) },
2309 { "MemoryDenyWriteExecute", "b", NULL, offsetof(SecurityInfo, memory_deny_write_execute) },
2310 { "NoNewPrivileges", "b", NULL, offsetof(SecurityInfo, no_new_privileges) },
2311 { "NotifyAccess", "s", NULL, offsetof(SecurityInfo, notify_access) },
2312 { "PrivateDevices", "b", NULL, offsetof(SecurityInfo, private_devices) },
2313 { "PrivateMounts", "b", NULL, offsetof(SecurityInfo, private_mounts) },
2314 { "PrivateNetwork", "b", NULL, offsetof(SecurityInfo, private_network) },
2315 { "PrivateTmp", "b", NULL, offsetof(SecurityInfo, private_tmp) },
2316 { "PrivateUsers", "b", NULL, offsetof(SecurityInfo, private_users) },
2317 { "ProtectControlGroups", "b", NULL, offsetof(SecurityInfo, protect_control_groups) },
2318 { "ProtectHome", "s", NULL, offsetof(SecurityInfo, protect_home) },
2319 { "ProtectHostname", "b", NULL, offsetof(SecurityInfo, protect_hostname) },
2320 { "ProtectKernelModules", "b", NULL, offsetof(SecurityInfo, protect_kernel_modules) },
2321 { "ProtectKernelTunables", "b", NULL, offsetof(SecurityInfo, protect_kernel_tunables) },
2322 { "ProtectKernelLogs", "b", NULL, offsetof(SecurityInfo, protect_kernel_logs) },
2323 { "ProtectClock", "b", NULL, offsetof(SecurityInfo, protect_clock) },
2324 { "ProtectSystem", "s", NULL, offsetof(SecurityInfo, protect_system) },
2325 { "RemoveIPC", "b", NULL, offsetof(SecurityInfo, remove_ipc) },
2326 { "RestrictAddressFamilies", "(bas)", property_read_restrict_address_families, 0 },
2327 { "RestrictNamespaces", "t", property_read_restrict_namespaces, 0 },
2328 { "RestrictRealtime", "b", NULL, offsetof(SecurityInfo, restrict_realtime) },
2329 { "RestrictSUIDSGID", "b", NULL, offsetof(SecurityInfo, restrict_suid_sgid) },
2330 { "RootDirectory", "s", NULL, offsetof(SecurityInfo, root_directory) },
2331 { "RootImage", "s", NULL, offsetof(SecurityInfo, root_image) },
2332 { "SupplementaryGroups", "as", NULL, offsetof(SecurityInfo, supplementary_groups) },
2333 { "SystemCallArchitectures", "as", property_read_syscall_archs, 0 },
2334 { "SystemCallFilter", "(as)", property_read_system_call_filter, 0 },
2335 { "Type", "s", NULL, offsetof(SecurityInfo, type) },
2336 { "UMask", "u", property_read_umask, 0 },
2337 { "User", "s", NULL, offsetof(SecurityInfo, user) },
ec16f3b6
LP
2338 {}
2339 };
2340
2341 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2342 _cleanup_free_ char *path = NULL;
2343 int r;
2344
2345 /* Note: this mangles *info on failure! */
2346
2347 assert(bus);
2348 assert(name);
2349 assert(info);
2350
2351 path = unit_dbus_path_from_name(name);
2352 if (!path)
2353 return log_oom();
2354
1ace223c
SJ
2355 r = bus_map_all_properties(
2356 bus,
2357 "org.freedesktop.systemd1",
2358 path,
2359 security_map,
2360 BUS_MAP_STRDUP | BUS_MAP_BOOLEAN_AS_BOOL,
2361 &error,
2362 NULL,
2363 info);
ec16f3b6
LP
2364 if (r < 0)
2365 return log_error_errno(r, "Failed to get unit properties: %s", bus_error_message(&error, r));
2366
2367 if (!streq_ptr(info->load_state, "loaded")) {
2368
2369 if (FLAGS_SET(flags, ANALYZE_SECURITY_ONLY_LOADED))
2370 return -EMEDIUMTYPE;
2371
2372 if (streq_ptr(info->load_state, "not-found"))
2373 log_error("Unit %s not found, cannot analyze.", name);
2374 else if (streq_ptr(info->load_state, "masked"))
2375 log_error("Unit %s is masked, cannot analyze.", name);
2376 else
2377 log_error("Unit %s not loaded properly, cannot analyze.", name);
2378
2379 return -EINVAL;
2380 }
2381
2382 if (FLAGS_SET(flags, ANALYZE_SECURITY_ONLY_LONG_RUNNING) && streq_ptr(info->type, "oneshot"))
2383 return -EMEDIUMTYPE;
2384
2385 if (info->private_devices ||
2386 info->private_tmp ||
2387 info->protect_control_groups ||
2388 info->protect_kernel_tunables ||
2389 info->protect_kernel_modules ||
2390 !streq_ptr(info->protect_home, "no") ||
2391 !streq_ptr(info->protect_system, "no") ||
2392 info->root_image)
2393 info->private_mounts = true;
2394
2395 if (info->protect_kernel_modules)
2396 info->capability_bounding_set &= ~(UINT64_C(1) << CAP_SYS_MODULE);
2397
82dce83b
KK
2398 if (info->protect_kernel_logs)
2399 info->capability_bounding_set &= ~(UINT64_C(1) << CAP_SYSLOG);
2400
9f37272a
KK
2401 if (info->protect_clock)
2402 info->capability_bounding_set &= ~((UINT64_C(1) << CAP_SYS_TIME) |
2403 (UINT64_C(1) << CAP_WAKE_ALARM));
2404
ec16f3b6
LP
2405 if (info->private_devices)
2406 info->capability_bounding_set &= ~((UINT64_C(1) << CAP_MKNOD) |
2407 (UINT64_C(1) << CAP_SYS_RAWIO));
2408
2409 return 0;
2410}
2411
3838d22c
MG
2412static int analyze_security_one(sd_bus *bus,
2413 const char *name,
2414 Table *overview_table,
2415 AnalyzeSecurityFlags flags,
2416 unsigned threshold,
4b4a8ef7
MG
2417 JsonVariant *policy,
2418 PagerFlags pager_flags,
2419 JsonFormatFlags json_format_flags) {
dfbda879 2420
1624114d
MG
2421 _cleanup_(security_info_freep) SecurityInfo *info = security_info_new();
2422 if (!info)
2423 return log_oom();
2424
ec16f3b6
LP
2425 int r;
2426
2427 assert(bus);
2428 assert(name);
2429
1624114d 2430 r = acquire_security_info(bus, name, info, flags);
ec16f3b6
LP
2431 if (r == -EMEDIUMTYPE) /* Ignore this one because not loaded or Type is oneshot */
2432 return 0;
2433 if (r < 0)
2434 return r;
2435
4b4a8ef7 2436 r = assess(info, overview_table, flags, threshold, policy, pager_flags, json_format_flags);
ec16f3b6
LP
2437 if (r < 0)
2438 return r;
2439
2440 return 0;
2441}
2442
1624114d
MG
2443/* Refactoring SecurityInfo so that it can make use of existing struct variables instead of reading from dbus */
2444static int get_security_info(Unit *u, ExecContext *c, CGroupContext *g, SecurityInfo **ret_info) {
2445 assert(ret_info);
2446
2447 _cleanup_(security_info_freep) SecurityInfo *info = security_info_new();
2448 if (!info)
2449 return log_oom();
2450
2451 if (u) {
2452 if (u->id) {
2453 info->id = strdup(u->id);
2454 if (!info->id)
2455 return log_oom();
2456 }
2457 if (unit_type_to_string(u->type)) {
2458 info->type = strdup(unit_type_to_string(u->type));
2459 if (!info->type)
2460 return log_oom();
2461 }
2462 if (unit_load_state_to_string(u->load_state)) {
2463 info->load_state = strdup(unit_load_state_to_string(u->load_state));
2464 if (!info->load_state)
2465 return log_oom();
2466 }
2467 if (u->fragment_path) {
2468 info->fragment_path = strdup(u->fragment_path);
2469 if (!info->fragment_path)
2470 return log_oom();
2471 }
2472 info->default_dependencies = u->default_dependencies;
2473 if (u->type == UNIT_SERVICE && notify_access_to_string(SERVICE(u)->notify_access)) {
2474 info->notify_access = strdup(notify_access_to_string(SERVICE(u)->notify_access));
2475 if (!info->notify_access)
2476 return log_oom();
2477 }
2478 }
2479
2480 if (c) {
2481 info->ambient_capabilities = c->capability_ambient_set;
2482 info->capability_bounding_set = c->capability_bounding_set;
2483 if (c->user) {
2484 info->user = strdup(c->user);
2485 if (!info->user)
2486 return log_oom();
2487 }
2488 if (c->supplementary_groups) {
2489 info->supplementary_groups = strv_copy(c->supplementary_groups);
2490 if (!info->supplementary_groups)
2491 return log_oom();
2492 }
2493 info->dynamic_user = c->dynamic_user;
2494 if (exec_keyring_mode_to_string(c->keyring_mode)) {
2495 info->keyring_mode = strdup(exec_keyring_mode_to_string(c->keyring_mode));
2496 if (!info->keyring_mode)
2497 return log_oom();
2498 }
2499 if (protect_proc_to_string(c->protect_proc)) {
2500 info->protect_proc = strdup(protect_proc_to_string(c->protect_proc));
2501 if (!info->protect_proc)
2502 return log_oom();
2503 }
2504 if (proc_subset_to_string(c->proc_subset)) {
2505 info->proc_subset = strdup(proc_subset_to_string(c->proc_subset));
2506 if (!info->proc_subset)
2507 return log_oom();
2508 }
2509 info->lock_personality = c->lock_personality;
2510 info->memory_deny_write_execute = c->memory_deny_write_execute;
2511 info->no_new_privileges = c->no_new_privileges;
2512 info->protect_hostname = c->protect_hostname;
2513 info->private_devices = c->private_devices;
2514 info->private_mounts = c->private_mounts;
2515 info->private_network = c->private_network;
2516 info->private_tmp = c->private_tmp;
2517 info->private_users = c->private_users;
2518 info->protect_control_groups = c->protect_control_groups;
2519 info->protect_kernel_modules = c->protect_kernel_modules;
2520 info->protect_kernel_tunables = c->protect_kernel_tunables;
2521 info->protect_kernel_logs = c->protect_kernel_logs;
2522 info->protect_clock = c->protect_clock;
2523 if (protect_home_to_string(c->protect_home)) {
2524 info->protect_home = strdup(protect_home_to_string(c->protect_home));
2525 if (!info->protect_home)
2526 return log_oom();
2527 }
2528 if (protect_system_to_string(c->protect_system)) {
2529 info->protect_system = strdup(protect_system_to_string(c->protect_system));
2530 if (!info->protect_system)
2531 return log_oom();
2532 }
2533 info->remove_ipc = c->remove_ipc;
2534 info->restrict_address_family_inet =
2535 info->restrict_address_family_unix =
2536 info->restrict_address_family_netlink =
2537 info->restrict_address_family_packet =
2538 info->restrict_address_family_other =
2539 c->address_families_allow_list;
2540
2541 void *key;
2542 SET_FOREACH(key, c->address_families) {
2543 int family = PTR_TO_INT(key);
2544 if (family == 0)
2545 continue;
2546 if (IN_SET(family, AF_INET, AF_INET6))
2547 info->restrict_address_family_inet = !c->address_families_allow_list;
2548 else if (family == AF_UNIX)
2549 info->restrict_address_family_unix = !c->address_families_allow_list;
2550 else if (family == AF_NETLINK)
2551 info->restrict_address_family_netlink = !c->address_families_allow_list;
2552 else if (family == AF_PACKET)
2553 info->restrict_address_family_packet = !c->address_families_allow_list;
2554 else
2555 info->restrict_address_family_other = !c->address_families_allow_list;
2556 }
2557
2558 info->restrict_namespaces = c->restrict_namespaces;
2559 info->restrict_realtime = c->restrict_realtime;
2560 info->restrict_suid_sgid = c->restrict_suid_sgid;
2561 if (c->root_directory) {
2562 info->root_directory = strdup(c->root_directory);
2563 if (!info->root_directory)
2564 return log_oom();
2565 }
2566 if (c->root_image) {
2567 info->root_image = strdup(c->root_image);
2568 if (!info->root_image)
2569 return log_oom();
2570 }
2571 info->_umask = c->umask;
444d9abd
LB
2572
2573#if HAVE_SECCOMP
2574 SET_FOREACH(key, c->syscall_archs) {
2575 const char *name;
2576
2577 name = seccomp_arch_to_string(PTR_TO_UINT32(key) - 1);
2578 if (!name)
2579 continue;
2580
2581 if (set_put_strdup(&info->system_call_architectures, name) < 0)
1624114d
MG
2582 return log_oom();
2583 }
444d9abd 2584
1624114d 2585 info->system_call_filter_allow_list = c->syscall_allow_list;
5862e556
YW
2586
2587 void *id, *num;
2588 HASHMAP_FOREACH_KEY(num, id, c->syscall_filter) {
2589 _cleanup_free_ char *name = NULL;
2590
2591 if (info->system_call_filter_allow_list && PTR_TO_INT(num) >= 0)
2592 continue;
2593
2594 name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
2595 if (!name)
2596 continue;
2597
2598 if (set_ensure_consume(&info->system_call_filter, &string_hash_ops_free, TAKE_PTR(name)) < 0)
1624114d
MG
2599 return log_oom();
2600 }
5862e556 2601#endif
1624114d
MG
2602 }
2603
2604 if (g) {
2605 info->delegate = g->delegate;
2606 if (cgroup_device_policy_to_string(g->device_policy)) {
2607 info->device_policy = strdup(cgroup_device_policy_to_string(g->device_policy));
2608 if (!info->device_policy)
2609 return log_oom();
2610 }
2611
84ebe6f0 2612 struct in_addr_prefix *i;
1624114d
MG
2613 bool deny_ipv4 = false, deny_ipv6 = false;
2614
84ebe6f0 2615 SET_FOREACH(i, g->ip_address_deny) {
1624114d
MG
2616 if (i->family == AF_INET && i->prefixlen == 0)
2617 deny_ipv4 = true;
2618 else if (i->family == AF_INET6 && i->prefixlen == 0)
2619 deny_ipv6 = true;
2620 }
2621 info->ip_address_deny_all = deny_ipv4 && deny_ipv6;
2622
2623 info->ip_address_allow_localhost = info->ip_address_allow_other = false;
84ebe6f0 2624 SET_FOREACH(i, g->ip_address_allow) {
1624114d
MG
2625 if (in_addr_is_localhost(i->family, &i->address))
2626 info->ip_address_allow_localhost = true;
2627 else
2628 info->ip_address_allow_other = true;
2629 }
2630
2631 info->ip_filters_custom_ingress = !strv_isempty(g->ip_filters_ingress);
2632 info->ip_filters_custom_egress = !strv_isempty(g->ip_filters_egress);
6a59dfa1
LB
2633
2634 LIST_FOREACH(device_allow, a, g->device_allow)
2635 if (strv_extendf(&info->device_allow,
2636 "%s:%s%s%s",
2637 a->path,
2638 a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "") < 0)
2639 return log_oom();
1624114d
MG
2640 }
2641
2642 *ret_info = TAKE_PTR(info);
2643
2644 return 0;
2645}
2646
4b4a8ef7
MG
2647static int offline_security_check(Unit *u,
2648 unsigned threshold,
2649 JsonVariant *policy,
2650 PagerFlags pager_flags,
2651 JsonFormatFlags json_format_flags) {
2652
bb43d853
MG
2653 _cleanup_(table_unrefp) Table *overview_table = NULL;
2654 AnalyzeSecurityFlags flags = 0;
2655 _cleanup_(security_info_freep) SecurityInfo *info = NULL;
2656 int r;
2657
2658 assert(u);
2659
2660 if (DEBUG_LOGGING)
2661 unit_dump(u, stdout, "\t");
2662
2663 r = get_security_info(u, unit_get_exec_context(u), unit_get_cgroup_context(u), &info);
2664 if (r < 0)
2665 return r;
2666
4b4a8ef7 2667 return assess(info, overview_table, flags, threshold, policy, pager_flags, json_format_flags);
bb43d853
MG
2668}
2669
3838d22c
MG
2670static int offline_security_checks(char **filenames,
2671 JsonVariant *policy,
b380b643 2672 LookupScope scope,
3838d22c
MG
2673 bool check_man,
2674 bool run_generators,
2675 unsigned threshold,
4b4a8ef7 2676 const char *root,
04469211 2677 const char *profile,
4b4a8ef7
MG
2678 PagerFlags pager_flags,
2679 JsonFormatFlags json_format_flags) {
3838d22c 2680
bb43d853
MG
2681 const ManagerTestRunFlags flags =
2682 MANAGER_TEST_RUN_MINIMAL |
2683 MANAGER_TEST_RUN_ENV_GENERATORS |
3f1487f5 2684 MANAGER_TEST_RUN_IGNORE_DEPENDENCIES |
bb43d853
MG
2685 run_generators * MANAGER_TEST_RUN_GENERATORS;
2686
2687 _cleanup_(manager_freep) Manager *m = NULL;
2688 Unit *units[strv_length(filenames)];
2689 _cleanup_free_ char *var = NULL;
2690 int r, k;
2691 size_t count = 0;
bb43d853
MG
2692
2693 if (strv_isempty(filenames))
2694 return 0;
2695
2696 /* set the path */
2697 r = verify_generate_path(&var, filenames);
2698 if (r < 0)
2699 return log_error_errno(r, "Failed to generate unit load path: %m");
2700
2701 assert_se(set_unit_path(var) >= 0);
2702
2703 r = manager_new(scope, flags, &m);
2704 if (r < 0)
2705 return log_error_errno(r, "Failed to initialize manager: %m");
2706
2707 log_debug("Starting manager...");
2708
2709 r = manager_startup(m, /* serialization= */ NULL, /* fds= */ NULL, root);
2710 if (r < 0)
2711 return r;
2712
04469211
LB
2713 if (profile) {
2714 /* Ensure the temporary directory is in the search path, so that we can add drop-ins. */
2715 r = strv_extend(&m->lookup_paths.search_path, m->lookup_paths.temporary_dir);
2716 if (r < 0)
2717 return log_oom();
2718 }
2719
bb43d853
MG
2720 log_debug("Loading remaining units from the command line...");
2721
2722 STRV_FOREACH(filename, filenames) {
2723 _cleanup_free_ char *prepared = NULL;
2724
2725 log_debug("Handling %s...", *filename);
2726
2727 k = verify_prepare_filename(*filename, &prepared);
2728 if (k < 0) {
2729 log_warning_errno(k, "Failed to prepare filename %s: %m", *filename);
2730 if (r == 0)
2731 r = k;
2732 continue;
2733 }
2734
04469211
LB
2735 /* When a portable image is analyzed, the profile is what provides a good chunk of
2736 * the security-related settings, but they are obviously not shipped with the image.
2737 * This allows to take them in consideration. */
2738 if (profile) {
2739 _cleanup_free_ char *unit_name = NULL, *dropin = NULL, *profile_path = NULL;
2740
2741 r = path_extract_filename(prepared, &unit_name);
2742 if (r < 0)
2743 return log_oom();
2744
2745 dropin = strjoin(m->lookup_paths.temporary_dir, "/", unit_name, ".d/profile.conf");
2746 if (!dropin)
2747 return log_oom();
2748 (void) mkdir_parents(dropin, 0755);
2749
2750 if (!is_path(profile)) {
2751 r = find_portable_profile(profile, unit_name, &profile_path);
2752 if (r < 0)
2753 return log_error_errno(r, "Failed to find portable profile %s: %m", profile);
2754 profile = profile_path;
2755 }
2756
2757 r = copy_file(profile, dropin, 0, 0644, 0, 0, 0);
2758 if (r < 0)
2759 return log_error_errno(r, "Failed to copy: %m");
2760 }
2761
bb43d853
MG
2762 k = manager_load_startable_unit_or_warn(m, NULL, prepared, &units[count]);
2763 if (k < 0) {
2764 if (r == 0)
2765 r = k;
2766 continue;
2767 }
2768
2769 count++;
2770 }
2771
2772 for (size_t i = 0; i < count; i++) {
4b4a8ef7 2773 k = offline_security_check(units[i], threshold, policy, pager_flags, json_format_flags);
bb43d853
MG
2774 if (k < 0 && r == 0)
2775 r = k;
2776 }
2777
2778 return r;
2779}
2780
57a22a3f 2781static int analyze_security(sd_bus *bus,
ecfd082b
MG
2782 char **units,
2783 JsonVariant *policy,
b380b643 2784 LookupScope scope,
ecfd082b
MG
2785 bool check_man,
2786 bool run_generators,
2787 bool offline,
2788 unsigned threshold,
2789 const char *root,
04469211 2790 const char *profile,
4b4a8ef7
MG
2791 JsonFormatFlags json_format_flags,
2792 PagerFlags pager_flags,
ecfd082b 2793 AnalyzeSecurityFlags flags) {
dfbda879 2794
ec16f3b6
LP
2795 _cleanup_(table_unrefp) Table *overview_table = NULL;
2796 int ret = 0, r;
2797
741c4c8d 2798 assert(!!bus != offline);
ec16f3b6 2799
bb43d853 2800 if (offline)
04469211 2801 return offline_security_checks(units, policy, scope, check_man, run_generators, threshold, root, profile, pager_flags, json_format_flags);
bb43d853 2802
ec16f3b6 2803 if (strv_length(units) != 1) {
9969b542 2804 overview_table = table_new("unit", "exposure", "predicate", "happy");
ec16f3b6
LP
2805 if (!overview_table)
2806 return log_oom();
2807 }
2808
2809 if (strv_isempty(units)) {
2810 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2811 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
2812 _cleanup_strv_free_ char **list = NULL;
319a4f4b 2813 size_t n = 0;
ec16f3b6 2814
d0d6ac67 2815 r = bus_call_method(
ec16f3b6 2816 bus,
d0d6ac67 2817 bus_systemd_mgr,
ec16f3b6 2818 "ListUnits",
1ace223c
SJ
2819 &error,
2820 &reply,
ec16f3b6
LP
2821 NULL);
2822 if (r < 0)
2823 return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r));
2824
2825 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
2826 if (r < 0)
2827 return bus_log_parse_error(r);
2828
2829 for (;;) {
2830 UnitInfo info;
2831 char *copy = NULL;
2832
2833 r = bus_parse_unit_info(reply, &info);
2834 if (r < 0)
2835 return bus_log_parse_error(r);
2836 if (r == 0)
2837 break;
2838
2839 if (!endswith(info.id, ".service"))
2840 continue;
2841
319a4f4b 2842 if (!GREEDY_REALLOC(list, n + 2))
ec16f3b6
LP
2843 return log_oom();
2844
2845 copy = strdup(info.id);
2846 if (!copy)
2847 return log_oom();
2848
2849 list[n++] = copy;
2850 list[n] = NULL;
2851 }
2852
2853 strv_sort(list);
2854
2855 flags |= ANALYZE_SECURITY_SHORT|ANALYZE_SECURITY_ONLY_LOADED|ANALYZE_SECURITY_ONLY_LONG_RUNNING;
2856
2857 STRV_FOREACH(i, list) {
4b4a8ef7 2858 r = analyze_security_one(bus, *i, overview_table, flags, threshold, policy, pager_flags, json_format_flags);
ec16f3b6
LP
2859 if (r < 0 && ret >= 0)
2860 ret = r;
2861 }
2862
de010b0b 2863 } else
ec16f3b6
LP
2864 STRV_FOREACH(i, units) {
2865 _cleanup_free_ char *mangled = NULL, *instance = NULL;
2866 const char *name;
2867
2868 if (!FLAGS_SET(flags, ANALYZE_SECURITY_SHORT) && i != units) {
2869 putc('\n', stdout);
2870 fflush(stdout);
2871 }
2872
df7c4eb6 2873 r = unit_name_mangle(*i, 0, &mangled);
ec16f3b6
LP
2874 if (r < 0)
2875 return log_error_errno(r, "Failed to mangle unit name '%s': %m", *i);
2876
d7a0f1f4
FS
2877 if (!endswith(mangled, ".service"))
2878 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2879 "Unit %s is not a service unit, refusing.",
2880 *i);
ec16f3b6
LP
2881
2882 if (unit_name_is_valid(mangled, UNIT_NAME_TEMPLATE)) {
2883 r = unit_name_replace_instance(mangled, "test-instance", &instance);
2884 if (r < 0)
2885 return log_oom();
2886
2887 name = instance;
2888 } else
2889 name = mangled;
2890
4b4a8ef7 2891 r = analyze_security_one(bus, name, overview_table, flags, threshold, policy, pager_flags, json_format_flags);
ec16f3b6
LP
2892 if (r < 0 && ret >= 0)
2893 ret = r;
2894 }
ec16f3b6
LP
2895
2896 if (overview_table) {
2897 if (!FLAGS_SET(flags, ANALYZE_SECURITY_SHORT)) {
2898 putc('\n', stdout);
2899 fflush(stdout);
2900 }
2901
4b4a8ef7 2902 r = table_print_with_pager(overview_table, json_format_flags, pager_flags, /* show_header= */true);
ec16f3b6
LP
2903 if (r < 0)
2904 return log_error_errno(r, "Failed to output table: %m");
2905 }
ec16f3b6
LP
2906 return ret;
2907}
57a22a3f 2908
ef38bedb 2909int verb_security(int argc, char *argv[], void *userdata) {
57a22a3f
LP
2910 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2911 _cleanup_(json_variant_unrefp) JsonVariant *policy = NULL;
2912 int r;
2913 unsigned line, column;
2914
2915 if (!arg_offline) {
2916 r = acquire_bus(&bus, NULL);
2917 if (r < 0)
2918 return bus_log_connect_error(r, arg_transport);
2919 }
2920
2921 pager_open(arg_pager_flags);
2922
2923 if (arg_security_policy) {
2924 r = json_parse_file(/*f=*/ NULL, arg_security_policy, /*flags=*/ 0, &policy, &line, &column);
2925 if (r < 0)
2926 return log_error_errno(r, "Failed to parse '%s' at %u:%u: %m", arg_security_policy, line, column);
2927 } else {
2928 _cleanup_fclose_ FILE *f = NULL;
2929 _cleanup_free_ char *pp = NULL;
2930
2931 r = search_and_fopen_nulstr("systemd-analyze-security.policy", "re", /*root=*/ NULL, CONF_PATHS_NULSTR("systemd"), &f, &pp);
2932 if (r < 0 && r != -ENOENT)
2933 return r;
2934
2935 if (f) {
2936 r = json_parse_file(f, pp, /*flags=*/ 0, &policy, &line, &column);
2937 if (r < 0)
2938 return log_error_errno(r, "[%s:%u:%u] Failed to parse JSON policy: %m", pp, line, column);
2939 }
2940 }
2941
2942 return analyze_security(bus,
2943 strv_skip(argv, 1),
2944 policy,
2945 arg_scope,
2946 arg_man,
2947 arg_generators,
2948 arg_offline,
2949 arg_threshold,
2950 arg_root,
2951 arg_profile,
2952 arg_json_format_flags,
2953 arg_pager_flags,
2954 /*flags=*/ 0);
2955}