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