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