1:255.13-alt1
[systemd_ALT.git] / src / analyze / analyze-security.c
blobe78356d240e90a8ab930424e8eb61d70d370291e
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <sys/utsname.h>
5 #include "af-list.h"
6 #include "analyze.h"
7 #include "analyze-security.h"
8 #include "analyze-verify.h"
9 #include "bus-error.h"
10 #include "bus-locator.h"
11 #include "bus-map-properties.h"
12 #include "bus-unit-util.h"
13 #include "bus-util.h"
14 #include "copy.h"
15 #include "env-util.h"
16 #include "fd-util.h"
17 #include "fileio.h"
18 #include "format-table.h"
19 #include "in-addr-prefix-util.h"
20 #include "locale-util.h"
21 #include "macro.h"
22 #include "manager.h"
23 #include "missing_capability.h"
24 #include "missing_sched.h"
25 #include "mkdir.h"
26 #include "nulstr-util.h"
27 #include "parse-util.h"
28 #include "path-util.h"
29 #include "pretty-print.h"
30 #include "seccomp-util.h"
31 #include "service.h"
32 #include "set.h"
33 #include "stdio-util.h"
34 #include "strv.h"
35 #include "terminal-util.h"
36 #include "unit-def.h"
37 #include "unit-name.h"
38 #include "unit-serialize.h"
40 typedef struct SecurityInfo {
41 char *id;
42 char *type;
43 char *load_state;
44 char *fragment_path;
45 bool default_dependencies;
47 uint64_t ambient_capabilities;
48 uint64_t capability_bounding_set;
50 char *user;
51 char **supplementary_groups;
52 bool dynamic_user;
54 bool ip_address_deny_all;
55 bool ip_address_allow_localhost;
56 bool ip_address_allow_other;
58 bool ip_filters_custom_ingress;
59 bool ip_filters_custom_egress;
61 char *keyring_mode;
62 char *protect_proc;
63 char *proc_subset;
64 bool lock_personality;
65 bool memory_deny_write_execute;
66 bool no_new_privileges;
67 char *notify_access;
68 bool protect_hostname;
70 bool private_devices;
71 bool private_mounts;
72 bool private_network;
73 bool private_tmp;
74 bool private_users;
76 bool protect_control_groups;
77 bool protect_kernel_modules;
78 bool protect_kernel_tunables;
79 bool protect_kernel_logs;
80 bool protect_clock;
82 char *protect_home;
83 char *protect_system;
85 bool remove_ipc;
87 bool restrict_address_family_inet;
88 bool restrict_address_family_unix;
89 bool restrict_address_family_netlink;
90 bool restrict_address_family_packet;
91 bool restrict_address_family_other;
93 unsigned long long restrict_namespaces;
94 bool restrict_realtime;
95 bool restrict_suid_sgid;
97 char *root_directory;
98 char *root_image;
100 bool delegate;
101 char *device_policy;
102 char **device_allow;
104 Set *system_call_architectures;
106 bool system_call_filter_allow_list;
107 Set *system_call_filter;
109 mode_t _umask;
110 } SecurityInfo;
112 struct security_assessor {
113 const char *id;
114 const char *json_field;
115 const char *description_good;
116 const char *description_bad;
117 const char *description_na;
118 const char *url;
119 uint64_t weight;
120 uint64_t range;
121 int (*assess)(
122 const struct security_assessor *a,
123 const SecurityInfo *info,
124 const void *data,
125 uint64_t *ret_badness,
126 char **ret_description);
127 size_t offset;
128 uint64_t parameter;
129 bool default_dependencies_only;
132 static SecurityInfo *security_info_new(void) {
133 SecurityInfo *info = new(SecurityInfo, 1);
134 if (!info)
135 return NULL;
137 *info = (SecurityInfo) {
138 .default_dependencies = true,
139 .capability_bounding_set = UINT64_MAX,
140 .restrict_namespaces = UINT64_MAX,
141 ._umask = 0002,
144 return info;
147 static SecurityInfo *security_info_free(SecurityInfo *i) {
148 if (!i)
149 return NULL;
151 free(i->id);
152 free(i->type);
153 free(i->load_state);
154 free(i->fragment_path);
156 free(i->user);
158 free(i->protect_home);
159 free(i->protect_system);
161 free(i->root_directory);
162 free(i->root_image);
164 free(i->keyring_mode);
165 free(i->protect_proc);
166 free(i->proc_subset);
167 free(i->notify_access);
169 free(i->device_policy);
170 strv_free(i->device_allow);
172 strv_free(i->supplementary_groups);
173 set_free(i->system_call_architectures);
174 set_free(i->system_call_filter);
176 return mfree(i);
179 DEFINE_TRIVIAL_CLEANUP_FUNC(SecurityInfo*, security_info_free);
181 static bool security_info_runs_privileged(const SecurityInfo *i) {
182 assert(i);
184 if (STRPTR_IN_SET(i->user, "0", "root"))
185 return true;
187 if (i->dynamic_user)
188 return false;
190 return isempty(i->user);
193 static int assess_bool(
194 const struct security_assessor *a,
195 const SecurityInfo *info,
196 const void *data,
197 uint64_t *ret_badness,
198 char **ret_description) {
200 const bool *b = ASSERT_PTR(data);
202 assert(ret_badness);
203 assert(ret_description);
205 *ret_badness = a->parameter ? *b : !*b;
206 *ret_description = NULL;
208 return 0;
211 static int assess_user(
212 const struct security_assessor *a,
213 const SecurityInfo *info,
214 const void *data,
215 uint64_t *ret_badness,
216 char **ret_description) {
218 _cleanup_free_ char *d = NULL;
219 uint64_t b;
221 assert(ret_badness);
222 assert(ret_description);
224 if (streq_ptr(info->user, NOBODY_USER_NAME)) {
225 d = strdup("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 = strdup("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 = strdup("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;
239 if (!d)
240 return log_oom();
242 *ret_badness = b;
243 *ret_description = TAKE_PTR(d);
245 return 0;
248 static int assess_protect_home(
249 const struct security_assessor *a,
250 const SecurityInfo *info,
251 const void *data,
252 uint64_t *ret_badness,
253 char **ret_description) {
255 const char *description;
256 uint64_t badness;
257 char *copy;
258 int r;
260 assert(ret_badness);
261 assert(ret_description);
263 badness = 10;
264 description = "Service has full access to home directories";
266 r = parse_boolean(info->protect_home);
267 if (r < 0) {
268 if (streq_ptr(info->protect_home, "read-only")) {
269 badness = 5;
270 description = "Service has read-only access to home directories";
271 } else if (streq_ptr(info->protect_home, "tmpfs")) {
272 badness = 1;
273 description = "Service has access to fake empty home directories";
275 } else if (r > 0) {
276 badness = 0;
277 description = "Service has no access to home directories";
280 copy = strdup(description);
281 if (!copy)
282 return log_oom();
284 *ret_badness = badness;
285 *ret_description = copy;
287 return 0;
290 static int assess_protect_system(
291 const struct security_assessor *a,
292 const SecurityInfo *info,
293 const void *data,
294 uint64_t *ret_badness,
295 char **ret_description) {
297 const char *description;
298 uint64_t badness;
299 char *copy;
300 int r;
302 assert(ret_badness);
303 assert(ret_description);
305 badness = 10;
306 description = "Service has full access to the OS file hierarchy";
308 r = parse_boolean(info->protect_system);
309 if (r < 0) {
310 if (streq_ptr(info->protect_system, "full")) {
311 badness = 3;
312 description = "Service has very limited write access to the OS file hierarchy";
313 } else if (streq_ptr(info->protect_system, "strict")) {
314 badness = 0;
315 description = "Service has strict read-only access to the OS file hierarchy";
317 } else if (r > 0) {
318 badness = 5;
319 description = "Service has limited write access to the OS file hierarchy";
322 copy = strdup(description);
323 if (!copy)
324 return log_oom();
326 *ret_badness = badness;
327 *ret_description = copy;
329 return 0;
332 static int assess_root_directory(
333 const struct security_assessor *a,
334 const SecurityInfo *info,
335 const void *data,
336 uint64_t *ret_badness,
337 char **ret_description) {
339 assert(ret_badness);
340 assert(ret_description);
342 *ret_badness =
343 empty_or_root(info->root_directory) &&
344 empty_or_root(info->root_image);
345 *ret_description = NULL;
347 return 0;
350 static int assess_capability_bounding_set(
351 const struct security_assessor *a,
352 const SecurityInfo *info,
353 const void *data,
354 uint64_t *ret_badness,
355 char **ret_description) {
357 assert(ret_badness);
358 assert(ret_description);
360 *ret_badness = !!(info->capability_bounding_set & a->parameter);
361 *ret_description = NULL;
363 return 0;
366 static int assess_umask(
367 const struct security_assessor *a,
368 const SecurityInfo *info,
369 const void *data,
370 uint64_t *ret_badness,
371 char **ret_description) {
373 char *copy = NULL;
374 const char *d;
375 uint64_t b;
377 assert(ret_badness);
378 assert(ret_description);
380 if (!FLAGS_SET(info->_umask, 0002)) {
381 d = "Files created by service are world-writable by default";
382 b = 10;
383 } else if (!FLAGS_SET(info->_umask, 0004)) {
384 d = "Files created by service are world-readable by default";
385 b = 5;
386 } else if (!FLAGS_SET(info->_umask, 0020)) {
387 d = "Files created by service are group-writable by default";
388 b = 2;
389 } else if (!FLAGS_SET(info->_umask, 0040)) {
390 d = "Files created by service are group-readable by default";
391 b = 1;
392 } else {
393 d = "Files created by service are accessible only by service's own user by default";
394 b = 0;
397 copy = strdup(d);
398 if (!copy)
399 return log_oom();
401 *ret_badness = b;
402 *ret_description = copy;
404 return 0;
407 static int assess_keyring_mode(
408 const struct security_assessor *a,
409 const SecurityInfo *info,
410 const void *data,
411 uint64_t *ret_badness,
412 char **ret_description) {
414 assert(ret_badness);
415 assert(ret_description);
417 *ret_badness = !streq_ptr(info->keyring_mode, "private");
418 *ret_description = NULL;
420 return 0;
423 static int assess_protect_proc(
424 const struct security_assessor *a,
425 const SecurityInfo *info,
426 const void *data,
427 uint64_t *ret_badness,
428 char **ret_description) {
430 assert(ret_badness);
431 assert(ret_description);
433 if (streq_ptr(info->protect_proc, "noaccess"))
434 *ret_badness = 1;
435 else if (STRPTR_IN_SET(info->protect_proc, "invisible", "ptraceable"))
436 *ret_badness = 0;
437 else
438 *ret_badness = 3;
440 *ret_description = NULL;
442 return 0;
445 static int assess_proc_subset(
446 const struct security_assessor *a,
447 const SecurityInfo *info,
448 const void *data,
449 uint64_t *ret_badness,
450 char **ret_description) {
452 assert(ret_badness);
453 assert(ret_description);
455 *ret_badness = !streq_ptr(info->proc_subset, "pid");
456 *ret_description = NULL;
458 return 0;
461 static int assess_notify_access(
462 const struct security_assessor *a,
463 const SecurityInfo *info,
464 const void *data,
465 uint64_t *ret_badness,
466 char **ret_description) {
468 assert(ret_badness);
469 assert(ret_description);
471 *ret_badness = streq_ptr(info->notify_access, "all");
472 *ret_description = NULL;
474 return 0;
477 static int assess_remove_ipc(
478 const struct security_assessor *a,
479 const SecurityInfo *info,
480 const void *data,
481 uint64_t *ret_badness,
482 char **ret_description) {
484 assert(ret_badness);
485 assert(ret_description);
487 if (security_info_runs_privileged(info))
488 *ret_badness = UINT64_MAX;
489 else
490 *ret_badness = !info->remove_ipc;
492 *ret_description = NULL;
493 return 0;
496 static int assess_supplementary_groups(
497 const struct security_assessor *a,
498 const SecurityInfo *info,
499 const void *data,
500 uint64_t *ret_badness,
501 char **ret_description) {
503 assert(ret_badness);
504 assert(ret_description);
506 if (security_info_runs_privileged(info))
507 *ret_badness = UINT64_MAX;
508 else
509 *ret_badness = !strv_isempty(info->supplementary_groups);
511 *ret_description = NULL;
512 return 0;
515 static int assess_restrict_namespaces(
516 const struct security_assessor *a,
517 const SecurityInfo *info,
518 const void *data,
519 uint64_t *ret_badness,
520 char **ret_description) {
522 assert(ret_badness);
523 assert(ret_description);
525 *ret_badness = !!(info->restrict_namespaces & a->parameter);
526 *ret_description = NULL;
528 return 0;
531 #if HAVE_SECCOMP
533 static int assess_system_call_architectures(
534 const struct security_assessor *a,
535 const SecurityInfo *info,
536 const void *data,
537 uint64_t *ret_badness,
538 char **ret_description) {
540 char *d;
541 uint64_t b;
543 assert(ret_badness);
544 assert(ret_description);
546 if (set_isempty(info->system_call_architectures)) {
547 b = 10;
548 d = strdup("Service may execute system calls with all ABIs");
549 } else if (set_contains(info->system_call_architectures, "native") &&
550 set_size(info->system_call_architectures) == 1) {
551 b = 0;
552 d = strdup("Service may execute system calls only with native ABI");
553 } else {
554 b = 8;
555 d = strdup("Service may execute system calls with multiple ABIs");
558 if (!d)
559 return log_oom();
561 *ret_badness = b;
562 *ret_description = d;
564 return 0;
567 static bool syscall_names_in_filter(Set *s, bool allow_list, const SyscallFilterSet *f, const char **ret_offending_syscall) {
568 NULSTR_FOREACH(syscall, f->value) {
569 if (syscall[0] == '@') {
570 const SyscallFilterSet *g;
572 assert_se(g = syscall_filter_set_find(syscall));
573 if (syscall_names_in_filter(s, allow_list, g, ret_offending_syscall))
574 return true; /* bad! */
576 continue;
579 /* Let's see if the system call actually exists on this platform, before complaining */
580 if (seccomp_syscall_resolve_name(syscall) < 0)
581 continue;
583 if (set_contains(s, syscall) == allow_list) {
584 log_debug("Offending syscall filter item: %s", syscall);
585 if (ret_offending_syscall)
586 *ret_offending_syscall = syscall;
587 return true; /* bad! */
591 *ret_offending_syscall = NULL;
592 return false;
595 static int assess_system_call_filter(
596 const struct security_assessor *a,
597 const SecurityInfo *info,
598 const void *data,
599 uint64_t *ret_badness,
600 char **ret_description) {
602 assert(a);
603 assert(info);
604 assert(ret_badness);
605 assert(ret_description);
607 assert(a->parameter < _SYSCALL_FILTER_SET_MAX);
608 const SyscallFilterSet *f = syscall_filter_sets + a->parameter;
610 _cleanup_free_ char *d = NULL;
611 uint64_t b;
612 int r;
614 if (!info->system_call_filter_allow_list && set_isempty(info->system_call_filter)) {
615 r = free_and_strdup(&d, "Service does not filter system calls");
616 b = 10;
617 } else {
618 bool bad;
619 const char *offender = NULL;
621 log_debug("Analyzing system call filter, checking against: %s", f->name);
622 bad = syscall_names_in_filter(info->system_call_filter, info->system_call_filter_allow_list, f, &offender);
623 log_debug("Result: %s", bad ? "bad" : "good");
625 if (info->system_call_filter_allow_list) {
626 if (bad) {
627 r = asprintf(&d, "System call allow list defined for service, and %s is included "
628 "(e.g. %s is allowed)",
629 f->name, offender);
630 b = 9;
631 } else {
632 r = asprintf(&d, "System call allow list defined for service, and %s is not included",
633 f->name);
634 b = 0;
636 } else {
637 if (bad) {
638 r = asprintf(&d, "System call deny list defined for service, and %s is not included "
639 "(e.g. %s is allowed)",
640 f->name, offender);
641 b = 10;
642 } else {
643 r = asprintf(&d, "System call deny list defined for service, and %s is included",
644 f->name);
645 b = 0;
649 if (r < 0)
650 return log_oom();
652 *ret_badness = b;
653 *ret_description = TAKE_PTR(d);
655 return 0;
658 #endif
660 static int assess_ip_address_allow(
661 const struct security_assessor *a,
662 const SecurityInfo *info,
663 const void *data,
664 uint64_t *ret_badness,
665 char **ret_description) {
667 char *d = NULL;
668 uint64_t b;
670 assert(info);
671 assert(ret_badness);
672 assert(ret_description);
674 if (info->ip_filters_custom_ingress || info->ip_filters_custom_egress) {
675 d = strdup("Service defines custom ingress/egress IP filters with BPF programs");
676 b = 0;
677 } else if (!info->ip_address_deny_all) {
678 d = strdup("Service does not define an IP address allow list");
679 b = 10;
680 } else if (info->ip_address_allow_other) {
681 d = strdup("Service defines IP address allow list with non-localhost entries");
682 b = 5;
683 } else if (info->ip_address_allow_localhost) {
684 d = strdup("Service defines IP address allow list with only localhost entries");
685 b = 2;
686 } else {
687 d = strdup("Service blocks all IP address ranges");
688 b = 0;
691 if (!d)
692 return log_oom();
694 *ret_badness = b;
695 *ret_description = d;
697 return 0;
700 static int assess_device_allow(
701 const struct security_assessor *a,
702 const SecurityInfo *info,
703 const void *data,
704 uint64_t *ret_badness,
705 char **ret_description) {
707 char *d = NULL;
708 uint64_t b;
710 assert(info);
711 assert(ret_badness);
712 assert(ret_description);
714 if (STRPTR_IN_SET(info->device_policy, "strict", "closed")) {
716 if (!strv_isempty(info->device_allow)) {
717 _cleanup_free_ char *join = NULL;
719 join = strv_join(info->device_allow, " ");
720 if (!join)
721 return log_oom();
723 d = strjoin("Service has a device ACL with some special devices: ", join);
724 b = 5;
725 } else {
726 d = strdup("Service has a minimal device ACL");
727 b = 0;
729 } else {
730 d = strdup("Service has no device ACL");
731 b = 10;
734 if (!d)
735 return log_oom();
737 *ret_badness = b;
738 *ret_description = d;
740 return 0;
743 static int assess_ambient_capabilities(
744 const struct security_assessor *a,
745 const SecurityInfo *info,
746 const void *data,
747 uint64_t *ret_badness,
748 char **ret_description) {
750 assert(ret_badness);
751 assert(ret_description);
753 *ret_badness = info->ambient_capabilities != 0;
754 *ret_description = NULL;
756 return 0;
759 static const struct security_assessor security_assessor_table[] = {
761 .id = "User=/DynamicUser=",
762 .json_field = "UserOrDynamicUser",
763 .description_bad = "Service runs as root user",
764 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#User=",
765 .weight = 2000,
766 .range = 10,
767 .assess = assess_user,
770 .id = "SupplementaryGroups=",
771 .json_field = "SupplementaryGroups",
772 .description_good = "Service has no supplementary groups",
773 .description_bad = "Service runs with supplementary groups",
774 .description_na = "Service runs as root, option does not matter",
775 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SupplementaryGroups=",
776 .weight = 200,
777 .range = 1,
778 .assess = assess_supplementary_groups,
781 .id = "PrivateDevices=",
782 .json_field = "PrivateDevices",
783 .description_good = "Service has no access to hardware devices",
784 .description_bad = "Service potentially has access to hardware devices",
785 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateDevices=",
786 .weight = 1000,
787 .range = 1,
788 .assess = assess_bool,
789 .offset = offsetof(SecurityInfo, private_devices),
792 .id = "PrivateMounts=",
793 .json_field = "PrivateMounts",
794 .description_good = "Service cannot install system mounts",
795 .description_bad = "Service may install system mounts",
796 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateMounts=",
797 .weight = 1000,
798 .range = 1,
799 .assess = assess_bool,
800 .offset = offsetof(SecurityInfo, private_mounts),
803 .id = "PrivateNetwork=",
804 .json_field = "PrivateNetwork",
805 .description_good = "Service has no access to the host's network",
806 .description_bad = "Service has access to the host's network",
807 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateNetwork=",
808 .weight = 2500,
809 .range = 1,
810 .assess = assess_bool,
811 .offset = offsetof(SecurityInfo, private_network),
814 .id = "PrivateTmp=",
815 .json_field = "PrivateTmp",
816 .description_good = "Service has no access to other software's temporary files",
817 .description_bad = "Service has access to other software's temporary files",
818 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateTmp=",
819 .weight = 1000,
820 .range = 1,
821 .assess = assess_bool,
822 .offset = offsetof(SecurityInfo, private_tmp),
823 .default_dependencies_only = true,
826 .id = "PrivateUsers=",
827 .json_field = "PrivateUsers",
828 .description_good = "Service does not have access to other users",
829 .description_bad = "Service has access to other users",
830 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateUsers=",
831 .weight = 1000,
832 .range = 1,
833 .assess = assess_bool,
834 .offset = offsetof(SecurityInfo, private_users),
837 .id = "ProtectControlGroups=",
838 .json_field = "ProtectControlGroups",
839 .description_good = "Service cannot modify the control group file system",
840 .description_bad = "Service may modify the control group file system",
841 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectControlGroups=",
842 .weight = 1000,
843 .range = 1,
844 .assess = assess_bool,
845 .offset = offsetof(SecurityInfo, protect_control_groups),
848 .id = "ProtectKernelModules=",
849 .json_field = "ProtectKernelModules",
850 .description_good = "Service cannot load or read kernel modules",
851 .description_bad = "Service may load or read kernel modules",
852 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelModules=",
853 .weight = 1000,
854 .range = 1,
855 .assess = assess_bool,
856 .offset = offsetof(SecurityInfo, protect_kernel_modules),
859 .id = "ProtectKernelTunables=",
860 .json_field = "ProtectKernelTunables",
861 .description_good = "Service cannot alter kernel tunables (/proc/sys, …)",
862 .description_bad = "Service may alter kernel tunables",
863 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelTunables=",
864 .weight = 1000,
865 .range = 1,
866 .assess = assess_bool,
867 .offset = offsetof(SecurityInfo, protect_kernel_tunables),
870 .id = "ProtectKernelLogs=",
871 .json_field = "ProtectKernelLogs",
872 .description_good = "Service cannot read from or write to the kernel log ring buffer",
873 .description_bad = "Service may read from or write to the kernel log ring buffer",
874 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelLogs=",
875 .weight = 1000,
876 .range = 1,
877 .assess = assess_bool,
878 .offset = offsetof(SecurityInfo, protect_kernel_logs),
881 .id = "ProtectClock=",
882 .json_field = "ProtectClock",
883 .description_good = "Service cannot write to the hardware clock or system clock",
884 .description_bad = "Service may write to the hardware clock or system clock",
885 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectClock=",
886 .weight = 1000,
887 .range = 1,
888 .assess = assess_bool,
889 .offset = offsetof(SecurityInfo, protect_clock),
892 .id = "ProtectHome=",
893 .json_field = "ProtectHome",
894 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHome=",
895 .weight = 1000,
896 .range = 10,
897 .assess = assess_protect_home,
898 .default_dependencies_only = true,
901 .id = "ProtectHostname=",
902 .json_field = "ProtectHostname",
903 .description_good = "Service cannot change system host/domainname",
904 .description_bad = "Service may change system host/domainname",
905 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHostname=",
906 .weight = 50,
907 .range = 1,
908 .assess = assess_bool,
909 .offset = offsetof(SecurityInfo, protect_hostname),
912 .id = "ProtectSystem=",
913 .json_field = "ProtectSystem",
914 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectSystem=",
915 .weight = 1000,
916 .range = 10,
917 .assess = assess_protect_system,
918 .default_dependencies_only = true,
921 .id = "RootDirectory=/RootImage=",
922 .json_field = "RootDirectoryOrRootImage",
923 .description_good = "Service has its own root directory/image",
924 .description_bad = "Service runs within the host's root directory",
925 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RootDirectory=",
926 .weight = 200,
927 .range = 1,
928 .assess = assess_root_directory,
929 .default_dependencies_only = true,
932 .id = "LockPersonality=",
933 .json_field = "LockPersonality",
934 .description_good = "Service cannot change ABI personality",
935 .description_bad = "Service may change ABI personality",
936 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#LockPersonality=",
937 .weight = 100,
938 .range = 1,
939 .assess = assess_bool,
940 .offset = offsetof(SecurityInfo, lock_personality),
943 .id = "MemoryDenyWriteExecute=",
944 .json_field = "MemoryDenyWriteExecute",
945 .description_good = "Service cannot create writable executable memory mappings",
946 .description_bad = "Service may create writable executable memory mappings",
947 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#MemoryDenyWriteExecute=",
948 .weight = 100,
949 .range = 1,
950 .assess = assess_bool,
951 .offset = offsetof(SecurityInfo, memory_deny_write_execute),
954 .id = "NoNewPrivileges=",
955 .json_field = "NoNewPrivileges",
956 .description_good = "Service processes cannot acquire new privileges",
957 .description_bad = "Service processes may acquire new privileges",
958 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NoNewPrivileges=",
959 .weight = 1000,
960 .range = 1,
961 .assess = assess_bool,
962 .offset = offsetof(SecurityInfo, no_new_privileges),
965 .id = "CapabilityBoundingSet=~CAP_SYS_ADMIN",
966 .json_field = "CapabilityBoundingSet_CAP_SYS_ADMIN",
967 .description_good = "Service has no administrator privileges",
968 .description_bad = "Service has administrator privileges",
969 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
970 .weight = 1500,
971 .range = 1,
972 .assess = assess_capability_bounding_set,
973 .parameter = UINT64_C(1) << CAP_SYS_ADMIN,
976 .id = "CapabilityBoundingSet=~CAP_SET(UID|GID|PCAP)",
977 .json_field = "CapabilityBoundingSet_CAP_SET_UID_GID_PCAP",
978 .description_good = "Service cannot change UID/GID identities/capabilities",
979 .description_bad = "Service may change UID/GID identities/capabilities",
980 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
981 .weight = 1500,
982 .range = 1,
983 .assess = assess_capability_bounding_set,
984 .parameter = (UINT64_C(1) << CAP_SETUID)|
985 (UINT64_C(1) << CAP_SETGID)|
986 (UINT64_C(1) << CAP_SETPCAP),
989 .id = "CapabilityBoundingSet=~CAP_SYS_PTRACE",
990 .json_field = "CapabilityBoundingSet_CAP_SYS_PTRACE",
991 .description_good = "Service has no ptrace() debugging abilities",
992 .description_bad = "Service has ptrace() debugging abilities",
993 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
994 .weight = 1500,
995 .range = 1,
996 .assess = assess_capability_bounding_set,
997 .parameter = (UINT64_C(1) << CAP_SYS_PTRACE),
1000 .id = "CapabilityBoundingSet=~CAP_SYS_TIME",
1001 .json_field = "CapabilityBoundingSet_CAP_SYS_TIME",
1002 .description_good = "Service processes cannot change the system clock",
1003 .description_bad = "Service processes may change the system clock",
1004 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1005 .weight = 1000,
1006 .range = 1,
1007 .assess = assess_capability_bounding_set,
1008 .parameter = UINT64_C(1) << CAP_SYS_TIME,
1011 .id = "CapabilityBoundingSet=~CAP_NET_ADMIN",
1012 .json_field = "CapabilityBoundingSet_CAP_NET_ADMIN",
1013 .description_good = "Service has no network configuration privileges",
1014 .description_bad = "Service has network configuration privileges",
1015 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1016 .weight = 1000,
1017 .range = 1,
1018 .assess = assess_capability_bounding_set,
1019 .parameter = (UINT64_C(1) << CAP_NET_ADMIN),
1022 .id = "CapabilityBoundingSet=~CAP_SYS_RAWIO",
1023 .json_field = "CapabilityBoundingSet_CAP_SYS_RAWIO",
1024 .description_good = "Service has no raw I/O access",
1025 .description_bad = "Service has raw I/O access",
1026 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1027 .weight = 1000,
1028 .range = 1,
1029 .assess = assess_capability_bounding_set,
1030 .parameter = (UINT64_C(1) << CAP_SYS_RAWIO),
1033 .id = "CapabilityBoundingSet=~CAP_SYS_MODULE",
1034 .json_field = "CapabilityBoundingSet_CAP_SYS_MODULE",
1035 .description_good = "Service cannot load kernel modules",
1036 .description_bad = "Service may load kernel modules",
1037 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1038 .weight = 1000,
1039 .range = 1,
1040 .assess = assess_capability_bounding_set,
1041 .parameter = (UINT64_C(1) << CAP_SYS_MODULE),
1044 .id = "CapabilityBoundingSet=~CAP_AUDIT_*",
1045 .json_field = "CapabilityBoundingSet_CAP_AUDIT",
1046 .description_good = "Service has no audit subsystem access",
1047 .description_bad = "Service has audit subsystem access",
1048 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1049 .weight = 500,
1050 .range = 1,
1051 .assess = assess_capability_bounding_set,
1052 .parameter = (UINT64_C(1) << CAP_AUDIT_CONTROL) |
1053 (UINT64_C(1) << CAP_AUDIT_READ) |
1054 (UINT64_C(1) << CAP_AUDIT_WRITE),
1057 .id = "CapabilityBoundingSet=~CAP_SYSLOG",
1058 .json_field = "CapabilityBoundingSet_CAP_SYSLOG",
1059 .description_good = "Service has no access to kernel logging",
1060 .description_bad = "Service has access to kernel logging",
1061 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1062 .weight = 500,
1063 .range = 1,
1064 .assess = assess_capability_bounding_set,
1065 .parameter = (UINT64_C(1) << CAP_SYSLOG),
1068 .id = "CapabilityBoundingSet=~CAP_SYS_(NICE|RESOURCE)",
1069 .json_field = "CapabilityBoundingSet_CAP_SYS_NICE_RESOURCE",
1070 .description_good = "Service has no privileges to change resource use parameters",
1071 .description_bad = "Service has privileges to change resource use parameters",
1072 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1073 .weight = 500,
1074 .range = 1,
1075 .assess = assess_capability_bounding_set,
1076 .parameter = (UINT64_C(1) << CAP_SYS_NICE) |
1077 (UINT64_C(1) << CAP_SYS_RESOURCE),
1080 .id = "CapabilityBoundingSet=~CAP_MKNOD",
1081 .json_field = "CapabilityBoundingSet_CAP_MKNOD",
1082 .description_good = "Service cannot create device nodes",
1083 .description_bad = "Service may create device nodes",
1084 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1085 .weight = 500,
1086 .range = 1,
1087 .assess = assess_capability_bounding_set,
1088 .parameter = (UINT64_C(1) << CAP_MKNOD),
1091 .id = "CapabilityBoundingSet=~CAP_(CHOWN|FSETID|SETFCAP)",
1092 .json_field = "CapabilityBoundingSet_CAP_CHOWN_FSETID_SETFCAP",
1093 .description_good = "Service cannot change file ownership/access mode/capabilities",
1094 .description_bad = "Service may change file ownership/access mode/capabilities unrestricted",
1095 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1096 .weight = 1000,
1097 .range = 1,
1098 .assess = assess_capability_bounding_set,
1099 .parameter = (UINT64_C(1) << CAP_CHOWN) |
1100 (UINT64_C(1) << CAP_FSETID) |
1101 (UINT64_C(1) << CAP_SETFCAP),
1104 .id = "CapabilityBoundingSet=~CAP_(DAC_*|FOWNER|IPC_OWNER)",
1105 .json_field = "CapabilityBoundingSet_CAP_DAC_FOWNER_IPC_OWNER",
1106 .description_good = "Service cannot override UNIX file/IPC permission checks",
1107 .description_bad = "Service may override UNIX file/IPC permission checks",
1108 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1109 .weight = 1000,
1110 .range = 1,
1111 .assess = assess_capability_bounding_set,
1112 .parameter = (UINT64_C(1) << CAP_DAC_OVERRIDE) |
1113 (UINT64_C(1) << CAP_DAC_READ_SEARCH) |
1114 (UINT64_C(1) << CAP_FOWNER) |
1115 (UINT64_C(1) << CAP_IPC_OWNER),
1118 .id = "CapabilityBoundingSet=~CAP_KILL",
1119 .json_field = "CapabilityBoundingSet_CAP_KILL",
1120 .description_good = "Service cannot send UNIX signals to arbitrary processes",
1121 .description_bad = "Service may send UNIX signals to arbitrary processes",
1122 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1123 .weight = 500,
1124 .range = 1,
1125 .assess = assess_capability_bounding_set,
1126 .parameter = (UINT64_C(1) << CAP_KILL),
1129 .id = "CapabilityBoundingSet=~CAP_NET_(BIND_SERVICE|BROADCAST|RAW)",
1130 .json_field = "CapabilityBoundingSet_CAP_NET_BIND_SERVICE_BROADCAST_RAW)",
1131 .description_good = "Service has no elevated networking privileges",
1132 .description_bad = "Service has elevated networking privileges",
1133 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1134 .weight = 500,
1135 .range = 1,
1136 .assess = assess_capability_bounding_set,
1137 .parameter = (UINT64_C(1) << CAP_NET_BIND_SERVICE) |
1138 (UINT64_C(1) << CAP_NET_BROADCAST) |
1139 (UINT64_C(1) << CAP_NET_RAW),
1142 .id = "CapabilityBoundingSet=~CAP_SYS_BOOT",
1143 .json_field = "CapabilityBoundingSet_CAP_SYS_BOOT",
1144 .description_good = "Service cannot issue reboot()",
1145 .description_bad = "Service may issue reboot()",
1146 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1147 .weight = 100,
1148 .range = 1,
1149 .assess = assess_capability_bounding_set,
1150 .parameter = (UINT64_C(1) << CAP_SYS_BOOT),
1153 .id = "CapabilityBoundingSet=~CAP_MAC_*",
1154 .json_field = "CapabilityBoundingSet_CAP_MAC",
1155 .description_good = "Service cannot adjust SMACK MAC",
1156 .description_bad = "Service may adjust SMACK MAC",
1157 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1158 .weight = 100,
1159 .range = 1,
1160 .assess = assess_capability_bounding_set,
1161 .parameter = (UINT64_C(1) << CAP_MAC_ADMIN)|
1162 (UINT64_C(1) << CAP_MAC_OVERRIDE),
1165 .id = "CapabilityBoundingSet=~CAP_LINUX_IMMUTABLE",
1166 .json_field = "CapabilityBoundingSet_CAP_LINUX_IMMUTABLE",
1167 .description_good = "Service cannot mark files immutable",
1168 .description_bad = "Service may mark files immutable",
1169 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1170 .weight = 75,
1171 .range = 1,
1172 .assess = assess_capability_bounding_set,
1173 .parameter = (UINT64_C(1) << CAP_LINUX_IMMUTABLE),
1176 .id = "CapabilityBoundingSet=~CAP_IPC_LOCK",
1177 .json_field = "CapabilityBoundingSet_CAP_IPC_LOCK",
1178 .description_good = "Service cannot lock memory into RAM",
1179 .description_bad = "Service may lock memory into RAM",
1180 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1181 .weight = 50,
1182 .range = 1,
1183 .assess = assess_capability_bounding_set,
1184 .parameter = (UINT64_C(1) << CAP_IPC_LOCK),
1187 .id = "CapabilityBoundingSet=~CAP_SYS_CHROOT",
1188 .json_field = "CapabilityBoundingSet_CAP_SYS_CHROOT",
1189 .description_good = "Service cannot issue chroot()",
1190 .description_bad = "Service may issue chroot()",
1191 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1192 .weight = 50,
1193 .range = 1,
1194 .assess = assess_capability_bounding_set,
1195 .parameter = (UINT64_C(1) << CAP_SYS_CHROOT),
1198 .id = "CapabilityBoundingSet=~CAP_BLOCK_SUSPEND",
1199 .json_field = "CapabilityBoundingSet_CAP_BLOCK_SUSPEND",
1200 .description_good = "Service cannot establish wake locks",
1201 .description_bad = "Service may establish wake locks",
1202 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1203 .weight = 25,
1204 .range = 1,
1205 .assess = assess_capability_bounding_set,
1206 .parameter = (UINT64_C(1) << CAP_BLOCK_SUSPEND),
1209 .id = "CapabilityBoundingSet=~CAP_WAKE_ALARM",
1210 .json_field = "CapabilityBoundingSet_CAP_WAKE_ALARM",
1211 .description_good = "Service cannot program timers that wake up the system",
1212 .description_bad = "Service may program timers that wake up the system",
1213 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1214 .weight = 25,
1215 .range = 1,
1216 .assess = assess_capability_bounding_set,
1217 .parameter = (UINT64_C(1) << CAP_WAKE_ALARM),
1220 .id = "CapabilityBoundingSet=~CAP_LEASE",
1221 .json_field = "CapabilityBoundingSet_CAP_LEASE",
1222 .description_good = "Service cannot create file leases",
1223 .description_bad = "Service may create file leases",
1224 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1225 .weight = 25,
1226 .range = 1,
1227 .assess = assess_capability_bounding_set,
1228 .parameter = (UINT64_C(1) << CAP_LEASE),
1231 .id = "CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG",
1232 .json_field = "CapabilityBoundingSet_CAP_SYS_TTY_CONFIG",
1233 .description_good = "Service cannot issue vhangup()",
1234 .description_bad = "Service may issue vhangup()",
1235 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1236 .weight = 25,
1237 .range = 1,
1238 .assess = assess_capability_bounding_set,
1239 .parameter = (UINT64_C(1) << CAP_SYS_TTY_CONFIG),
1242 .id = "CapabilityBoundingSet=~CAP_SYS_PACCT",
1243 .json_field = "CapabilityBoundingSet_CAP_SYS_PACCT",
1244 .description_good = "Service cannot use acct()",
1245 .description_bad = "Service may use acct()",
1246 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1247 .weight = 25,
1248 .range = 1,
1249 .assess = assess_capability_bounding_set,
1250 .parameter = (UINT64_C(1) << CAP_SYS_PACCT),
1253 .id = "CapabilityBoundingSet=~CAP_BPF",
1254 .json_field = "CapabilityBoundingSet_CAP_BPF",
1255 .description_good = "Service may not load BPF programs",
1256 .description_bad = "Service may load BPF programs",
1257 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1258 .weight = 25,
1259 .range = 1,
1260 .assess = assess_capability_bounding_set,
1261 .parameter = (UINT64_C(1) << CAP_BPF),
1264 .id = "UMask=",
1265 .json_field = "UMask",
1266 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#UMask=",
1267 .weight = 100,
1268 .range = 10,
1269 .assess = assess_umask,
1272 .id = "KeyringMode=",
1273 .json_field = "KeyringMode",
1274 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#KeyringMode=",
1275 .description_good = "Service doesn't share key material with other services",
1276 .description_bad = "Service shares key material with other service",
1277 .weight = 1000,
1278 .range = 1,
1279 .assess = assess_keyring_mode,
1282 .id = "ProtectProc=",
1283 .json_field = "ProtectProc",
1284 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectProc=",
1285 .description_good = "Service has restricted access to process tree (/proc hidepid=)",
1286 .description_bad = "Service has full access to process tree (/proc hidepid=)",
1287 .weight = 1000,
1288 .range = 3,
1289 .assess = assess_protect_proc,
1292 .id = "ProcSubset=",
1293 .json_field = "ProcSubset",
1294 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProcSubset=",
1295 .description_good = "Service has no access to non-process /proc files (/proc subset=)",
1296 .description_bad = "Service has full access to non-process /proc files (/proc subset=)",
1297 .weight = 10,
1298 .range = 1,
1299 .assess = assess_proc_subset,
1302 .id = "NotifyAccess=",
1303 .json_field = "NotifyAccess",
1304 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NotifyAccess=",
1305 .description_good = "Service child processes cannot alter service state",
1306 .description_bad = "Service child processes may alter service state",
1307 .weight = 1000,
1308 .range = 1,
1309 .assess = assess_notify_access,
1312 .id = "RemoveIPC=",
1313 .json_field = "RemoveIPC",
1314 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RemoveIPC=",
1315 .description_good = "Service user cannot leave SysV IPC objects around",
1316 .description_bad = "Service user may leave SysV IPC objects around",
1317 .description_na = "Service runs as root, option does not apply",
1318 .weight = 100,
1319 .range = 1,
1320 .assess = assess_remove_ipc,
1321 .offset = offsetof(SecurityInfo, remove_ipc),
1324 .id = "Delegate=",
1325 .json_field = "Delegate",
1326 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Delegate=",
1327 .description_good = "Service does not maintain its own delegated control group subtree",
1328 .description_bad = "Service maintains its own delegated control group subtree",
1329 .weight = 100,
1330 .range = 1,
1331 .assess = assess_bool,
1332 .offset = offsetof(SecurityInfo, delegate),
1333 .parameter = true, /* invert! */
1336 .id = "RestrictRealtime=",
1337 .json_field = "RestrictRealtime",
1338 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictRealtime=",
1339 .description_good = "Service realtime scheduling access is restricted",
1340 .description_bad = "Service may acquire realtime scheduling",
1341 .weight = 500,
1342 .range = 1,
1343 .assess = assess_bool,
1344 .offset = offsetof(SecurityInfo, restrict_realtime),
1347 .id = "RestrictSUIDSGID=",
1348 .json_field = "RestrictSUIDSGID",
1349 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictSUIDSGID=",
1350 .description_good = "SUID/SGID file creation by service is restricted",
1351 .description_bad = "Service may create SUID/SGID files",
1352 .weight = 1000,
1353 .range = 1,
1354 .assess = assess_bool,
1355 .offset = offsetof(SecurityInfo, restrict_suid_sgid),
1358 .id = "RestrictNamespaces=~user",
1359 .json_field = "RestrictNamespaces_user",
1360 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1361 .description_good = "Service cannot create user namespaces",
1362 .description_bad = "Service may create user namespaces",
1363 .weight = 1500,
1364 .range = 1,
1365 .assess = assess_restrict_namespaces,
1366 .parameter = CLONE_NEWUSER,
1369 .id = "RestrictNamespaces=~mnt",
1370 .json_field = "RestrictNamespaces_mnt",
1371 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1372 .description_good = "Service cannot create file system namespaces",
1373 .description_bad = "Service may create file system namespaces",
1374 .weight = 500,
1375 .range = 1,
1376 .assess = assess_restrict_namespaces,
1377 .parameter = CLONE_NEWNS,
1380 .id = "RestrictNamespaces=~ipc",
1381 .json_field = "RestrictNamespaces_ipc",
1382 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1383 .description_good = "Service cannot create IPC namespaces",
1384 .description_bad = "Service may create IPC namespaces",
1385 .weight = 500,
1386 .range = 1,
1387 .assess = assess_restrict_namespaces,
1388 .parameter = CLONE_NEWIPC,
1391 .id = "RestrictNamespaces=~pid",
1392 .json_field = "RestrictNamespaces_pid",
1393 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1394 .description_good = "Service cannot create process namespaces",
1395 .description_bad = "Service may create process namespaces",
1396 .weight = 500,
1397 .range = 1,
1398 .assess = assess_restrict_namespaces,
1399 .parameter = CLONE_NEWPID,
1402 .id = "RestrictNamespaces=~cgroup",
1403 .json_field = "RestrictNamespaces_cgroup",
1404 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1405 .description_good = "Service cannot create cgroup namespaces",
1406 .description_bad = "Service may create cgroup namespaces",
1407 .weight = 500,
1408 .range = 1,
1409 .assess = assess_restrict_namespaces,
1410 .parameter = CLONE_NEWCGROUP,
1413 .id = "RestrictNamespaces=~net",
1414 .json_field = "RestrictNamespaces_net",
1415 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1416 .description_good = "Service cannot create network namespaces",
1417 .description_bad = "Service may create network namespaces",
1418 .weight = 500,
1419 .range = 1,
1420 .assess = assess_restrict_namespaces,
1421 .parameter = CLONE_NEWNET,
1424 .id = "RestrictNamespaces=~uts",
1425 .json_field = "RestrictNamespaces_uts",
1426 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1427 .description_good = "Service cannot create hostname namespaces",
1428 .description_bad = "Service may create hostname namespaces",
1429 .weight = 100,
1430 .range = 1,
1431 .assess = assess_restrict_namespaces,
1432 .parameter = CLONE_NEWUTS,
1435 .id = "RestrictAddressFamilies=~AF_(INET|INET6)",
1436 .json_field = "RestrictAddressFamilies_AF_INET_INET6",
1437 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1438 .description_good = "Service cannot allocate Internet sockets",
1439 .description_bad = "Service may allocate Internet sockets",
1440 .weight = 1500,
1441 .range = 1,
1442 .assess = assess_bool,
1443 .offset = offsetof(SecurityInfo, restrict_address_family_inet),
1446 .id = "RestrictAddressFamilies=~AF_UNIX",
1447 .json_field = "RestrictAddressFamilies_AF_UNIX",
1448 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1449 .description_good = "Service cannot allocate local sockets",
1450 .description_bad = "Service may allocate local sockets",
1451 .weight = 25,
1452 .range = 1,
1453 .assess = assess_bool,
1454 .offset = offsetof(SecurityInfo, restrict_address_family_unix),
1457 .id = "RestrictAddressFamilies=~AF_NETLINK",
1458 .json_field = "RestrictAddressFamilies_AF_NETLINK",
1459 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1460 .description_good = "Service cannot allocate netlink sockets",
1461 .description_bad = "Service may allocate netlink sockets",
1462 .weight = 200,
1463 .range = 1,
1464 .assess = assess_bool,
1465 .offset = offsetof(SecurityInfo, restrict_address_family_netlink),
1468 .id = "RestrictAddressFamilies=~AF_PACKET",
1469 .json_field = "RestrictAddressFamilies_AF_PACKET",
1470 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1471 .description_good = "Service cannot allocate packet sockets",
1472 .description_bad = "Service may allocate packet sockets",
1473 .weight = 1000,
1474 .range = 1,
1475 .assess = assess_bool,
1476 .offset = offsetof(SecurityInfo, restrict_address_family_packet),
1479 .id = "RestrictAddressFamilies=~…",
1480 .json_field = "RestrictAddressFamilies_OTHER",
1481 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1482 .description_good = "Service cannot allocate exotic sockets",
1483 .description_bad = "Service may allocate exotic sockets",
1484 .weight = 1250,
1485 .range = 1,
1486 .assess = assess_bool,
1487 .offset = offsetof(SecurityInfo, restrict_address_family_other),
1489 #if HAVE_SECCOMP
1491 .id = "SystemCallArchitectures=",
1492 .json_field = "SystemCallArchitectures",
1493 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallArchitectures=",
1494 .weight = 1000,
1495 .range = 10,
1496 .assess = assess_system_call_architectures,
1499 .id = "SystemCallFilter=~@swap",
1500 .json_field = "SystemCallFilter_swap",
1501 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1502 .weight = 1000,
1503 .range = 10,
1504 .assess = assess_system_call_filter,
1505 .parameter = SYSCALL_FILTER_SET_SWAP,
1508 .id = "SystemCallFilter=~@obsolete",
1509 .json_field = "SystemCallFilter_obsolete",
1510 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1511 .weight = 250,
1512 .range = 10,
1513 .assess = assess_system_call_filter,
1514 .parameter = SYSCALL_FILTER_SET_OBSOLETE,
1517 .id = "SystemCallFilter=~@clock",
1518 .json_field = "SystemCallFilter_clock",
1519 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1520 .weight = 1000,
1521 .range = 10,
1522 .assess = assess_system_call_filter,
1523 .parameter = SYSCALL_FILTER_SET_CLOCK,
1526 .id = "SystemCallFilter=~@cpu-emulation",
1527 .json_field = "SystemCallFilter_cpu_emulation",
1528 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1529 .weight = 250,
1530 .range = 10,
1531 .assess = assess_system_call_filter,
1532 .parameter = SYSCALL_FILTER_SET_CPU_EMULATION,
1535 .id = "SystemCallFilter=~@debug",
1536 .json_field = "SystemCallFilter_debug",
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_DEBUG,
1544 .id = "SystemCallFilter=~@mount",
1545 .json_field = "SystemCallFilter_mount",
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_MOUNT,
1553 .id = "SystemCallFilter=~@module",
1554 .json_field = "SystemCallFilter_module",
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_MODULE,
1562 .id = "SystemCallFilter=~@raw-io",
1563 .json_field = "SystemCallFilter_raw_io",
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_RAW_IO,
1571 .id = "SystemCallFilter=~@reboot",
1572 .json_field = "SystemCallFilter_reboot",
1573 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1574 .weight = 1000,
1575 .range = 10,
1576 .assess = assess_system_call_filter,
1577 .parameter = SYSCALL_FILTER_SET_REBOOT,
1580 .id = "SystemCallFilter=~@privileged",
1581 .json_field = "SystemCallFilter_privileged",
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_PRIVILEGED,
1589 .id = "SystemCallFilter=~@resources",
1590 .json_field = "SystemCallFilter_resources",
1591 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1592 .weight = 700,
1593 .range = 10,
1594 .assess = assess_system_call_filter,
1595 .parameter = SYSCALL_FILTER_SET_RESOURCES,
1597 #endif
1599 .id = "IPAddressDeny=",
1600 .json_field = "IPAddressDeny",
1601 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#IPAddressDeny=",
1602 .weight = 1000,
1603 .range = 10,
1604 .assess = assess_ip_address_allow,
1607 .id = "DeviceAllow=",
1608 .json_field = "DeviceAllow",
1609 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#DeviceAllow=",
1610 .weight = 1000,
1611 .range = 10,
1612 .assess = assess_device_allow,
1615 .id = "AmbientCapabilities=",
1616 .json_field = "AmbientCapabilities",
1617 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#AmbientCapabilities=",
1618 .description_good = "Service process does not receive ambient capabilities",
1619 .description_bad = "Service process receives ambient capabilities",
1620 .weight = 500,
1621 .range = 1,
1622 .assess = assess_ambient_capabilities,
1626 static JsonVariant* security_assessor_find_in_policy(const struct security_assessor *a, JsonVariant *policy, const char *name) {
1627 JsonVariant *item;
1628 assert(a);
1630 if (!policy)
1631 return NULL;
1632 if (!json_variant_is_object(policy)) {
1633 log_debug("Specified policy is not a JSON object, ignoring.");
1634 return NULL;
1637 item = json_variant_by_key(policy, a->json_field);
1638 if (!item)
1639 return NULL;
1640 if (!json_variant_is_object(item)) {
1641 log_debug("Item for '%s' in policy JSON object is not an object, ignoring.", a->id);
1642 return NULL;
1645 return name ? json_variant_by_key(item, name) : item;
1648 static uint64_t access_weight(const struct security_assessor *a, JsonVariant *policy) {
1649 JsonVariant *val;
1651 assert(a);
1653 val = security_assessor_find_in_policy(a, policy, "weight");
1654 if (val) {
1655 if (json_variant_is_unsigned(val))
1656 return json_variant_unsigned(val);
1657 log_debug("JSON field 'weight' of policy for %s is not an unsigned integer, ignoring.", a->id);
1660 return a->weight;
1663 static uint64_t access_range(const struct security_assessor *a, JsonVariant *policy) {
1664 JsonVariant *val;
1666 assert(a);
1668 val = security_assessor_find_in_policy(a, policy, "range");
1669 if (val) {
1670 if (json_variant_is_unsigned(val))
1671 return json_variant_unsigned(val);
1672 log_debug("JSON field 'range' of policy for %s is not an unsigned integer, ignoring.", a->id);
1675 return a->range;
1678 static const char *access_description_na(const struct security_assessor *a, JsonVariant *policy) {
1679 JsonVariant *val;
1681 assert(a);
1683 val = security_assessor_find_in_policy(a, policy, "description_na");
1684 if (val) {
1685 if (json_variant_is_string(val))
1686 return json_variant_string(val);
1687 log_debug("JSON field 'description_na' of policy for %s is not a string, ignoring.", a->id);
1690 return a->description_na;
1693 static const char *access_description_good(const struct security_assessor *a, JsonVariant *policy) {
1694 JsonVariant *val;
1696 assert(a);
1698 val = security_assessor_find_in_policy(a, policy, "description_good");
1699 if (val) {
1700 if (json_variant_is_string(val))
1701 return json_variant_string(val);
1702 log_debug("JSON field 'description_good' of policy for %s is not a string, ignoring.", a->id);
1705 return a->description_good;
1708 static const char *access_description_bad(const struct security_assessor *a, JsonVariant *policy) {
1709 JsonVariant *val;
1711 assert(a);
1713 val = security_assessor_find_in_policy(a, policy, "description_bad");
1714 if (val) {
1715 if (json_variant_is_string(val))
1716 return json_variant_string(val);
1717 log_debug("JSON field 'description_bad' of policy for %s is not a string, ignoring.", a->id);
1720 return a->description_bad;
1723 static int assess(const SecurityInfo *info,
1724 Table *overview_table,
1725 AnalyzeSecurityFlags flags,
1726 unsigned threshold,
1727 JsonVariant *policy,
1728 PagerFlags pager_flags,
1729 JsonFormatFlags json_format_flags) {
1731 static const struct {
1732 uint64_t exposure;
1733 const char *name;
1734 const char *color;
1735 SpecialGlyph smiley;
1736 } badness_table[] = {
1737 { 100, "DANGEROUS", ANSI_HIGHLIGHT_RED, SPECIAL_GLYPH_DEPRESSED_SMILEY },
1738 { 90, "UNSAFE", ANSI_HIGHLIGHT_RED, SPECIAL_GLYPH_UNHAPPY_SMILEY },
1739 { 75, "EXPOSED", ANSI_HIGHLIGHT_YELLOW, SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY },
1740 { 50, "MEDIUM", NULL, SPECIAL_GLYPH_NEUTRAL_SMILEY },
1741 { 10, "OK", ANSI_HIGHLIGHT_GREEN, SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY },
1742 { 1, "SAFE", ANSI_HIGHLIGHT_GREEN, SPECIAL_GLYPH_HAPPY_SMILEY },
1743 { 0, "PERFECT", ANSI_HIGHLIGHT_GREEN, SPECIAL_GLYPH_ECSTATIC_SMILEY },
1746 uint64_t badness_sum = 0, weight_sum = 0, exposure;
1747 _cleanup_(table_unrefp) Table *details_table = NULL;
1748 size_t i;
1749 int r;
1751 if (!FLAGS_SET(flags, ANALYZE_SECURITY_SHORT)) {
1752 details_table = table_new(" ", "name", "json_field", "description", "weight", "badness", "range", "exposure");
1753 if (!details_table)
1754 return log_oom();
1756 r = table_set_json_field_name(details_table, 0, "set");
1757 if (r < 0)
1758 return log_error_errno(r, "Failed to set JSON field name of column 0: %m");
1760 (void) table_set_sort(details_table, (size_t) 3, (size_t) 1);
1761 (void) table_set_reverse(details_table, 3, true);
1763 if (getenv_bool("SYSTEMD_ANALYZE_DEBUG") <= 0)
1764 (void) table_set_display(details_table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 7);
1767 for (i = 0; i < ELEMENTSOF(security_assessor_table); i++) {
1768 const struct security_assessor *a = security_assessor_table + i;
1769 _cleanup_free_ char *d = NULL;
1770 uint64_t badness;
1771 void *data;
1772 uint64_t weight = access_weight(a, policy);
1773 uint64_t range = access_range(a, policy);
1775 data = (uint8_t *) info + a->offset;
1777 if (a->default_dependencies_only && !info->default_dependencies) {
1778 badness = UINT64_MAX;
1779 d = strdup("Service runs in special boot phase, option is not appropriate");
1780 if (!d)
1781 return log_oom();
1782 } else if (weight == 0) {
1783 badness = UINT64_MAX;
1784 d = strdup("Option excluded by policy, skipping");
1785 if (!d)
1786 return log_oom();
1787 } else {
1788 r = a->assess(a, info, data, &badness, &d);
1789 if (r < 0)
1790 return r;
1793 assert(range > 0);
1795 if (badness != UINT64_MAX) {
1796 assert(badness <= range);
1798 badness_sum += DIV_ROUND_UP(badness * weight, range);
1799 weight_sum += weight;
1802 if (details_table) {
1803 const char *description, *color = NULL;
1804 int checkmark;
1806 if (badness == UINT64_MAX) {
1807 checkmark = -1;
1808 description = access_description_na(a, policy);
1809 color = NULL;
1810 } else if (badness == a->range) {
1811 checkmark = 0;
1812 description = access_description_bad(a, policy);
1813 color = ansi_highlight_red();
1814 } else if (badness == 0) {
1815 checkmark = 1;
1816 description = access_description_good(a, policy);
1817 color = ansi_highlight_green();
1818 } else {
1819 checkmark = 0;
1820 description = NULL;
1821 color = ansi_highlight_red();
1824 if (d)
1825 description = d;
1827 if (checkmark < 0) {
1828 r = table_add_many(details_table, TABLE_EMPTY);
1829 if (r < 0)
1830 return table_log_add_error(r);
1831 } else {
1832 r = table_add_many(details_table,
1833 TABLE_BOOLEAN_CHECKMARK, checkmark > 0,
1834 TABLE_SET_MINIMUM_WIDTH, 1,
1835 TABLE_SET_MAXIMUM_WIDTH, 1,
1836 TABLE_SET_ELLIPSIZE_PERCENT, 0,
1837 TABLE_SET_COLOR, color);
1838 if (r < 0)
1839 return table_log_add_error(r);
1842 r = table_add_many(details_table,
1843 TABLE_STRING, a->id, TABLE_SET_URL, a->url,
1844 TABLE_STRING, a->json_field,
1845 TABLE_STRING, description,
1846 TABLE_UINT64, weight, TABLE_SET_ALIGN_PERCENT, 100,
1847 TABLE_UINT64, badness, TABLE_SET_ALIGN_PERCENT, 100,
1848 TABLE_UINT64, range, TABLE_SET_ALIGN_PERCENT, 100,
1849 TABLE_EMPTY, TABLE_SET_ALIGN_PERCENT, 100);
1850 if (r < 0)
1851 return table_log_add_error(r);
1855 assert(weight_sum > 0);
1857 if (details_table) {
1858 size_t row;
1860 for (row = 1; row < table_get_rows(details_table); row++) {
1861 char buf[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1];
1862 const uint64_t *weight, *badness, *range;
1863 TableCell *cell;
1864 uint64_t x;
1866 assert_se(weight = table_get_at(details_table, row, 4));
1867 assert_se(badness = table_get_at(details_table, row, 5));
1868 assert_se(range = table_get_at(details_table, row, 6));
1870 if (*badness == UINT64_MAX || *badness == 0)
1871 continue;
1873 assert_se(cell = table_get_cell(details_table, row, 7));
1875 x = DIV_ROUND_UP(DIV_ROUND_UP(*badness * *weight * 100U, *range), weight_sum);
1876 xsprintf(buf, "%" PRIu64 ".%" PRIu64, x / 10, x % 10);
1878 r = table_update(details_table, cell, TABLE_STRING, buf);
1879 if (r < 0)
1880 return log_error_errno(r, "Failed to update cell in table: %m");
1883 if (json_format_flags & JSON_FORMAT_OFF) {
1884 r = table_hide_column_from_display(details_table, (size_t) 2);
1885 if (r < 0)
1886 return log_error_errno(r, "Failed to set columns to display: %m");
1889 r = table_print_with_pager(details_table, json_format_flags, pager_flags, /* show_header= */true);
1890 if (r < 0)
1891 return log_error_errno(r, "Failed to output table: %m");
1894 exposure = DIV_ROUND_UP(badness_sum * 100U, weight_sum);
1896 for (i = 0; i < ELEMENTSOF(badness_table); i++)
1897 if (exposure >= badness_table[i].exposure)
1898 break;
1900 assert(i < ELEMENTSOF(badness_table));
1902 if (details_table && (json_format_flags & JSON_FORMAT_OFF)) {
1903 _cleanup_free_ char *clickable = NULL;
1904 const char *name;
1906 /* If we shall output the details table, also print the brief summary underneath */
1908 if (info->fragment_path) {
1909 r = terminal_urlify_path(info->fragment_path, info->id, &clickable);
1910 if (r < 0)
1911 return log_oom();
1913 name = clickable;
1914 } else
1915 name = info->id;
1917 printf("\n%s %sOverall exposure level for %s%s: %s%" PRIu64 ".%" PRIu64 " %s%s %s\n",
1918 special_glyph(SPECIAL_GLYPH_ARROW_RIGHT),
1919 ansi_highlight(),
1920 name,
1921 ansi_normal(),
1922 colors_enabled() ? strempty(badness_table[i].color) : "",
1923 exposure / 10, exposure % 10,
1924 badness_table[i].name,
1925 ansi_normal(),
1926 special_glyph(badness_table[i].smiley));
1929 fflush(stdout);
1931 if (overview_table) {
1932 char buf[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1];
1933 _cleanup_free_ char *url = NULL;
1935 if (info->fragment_path) {
1936 r = file_url_from_path(info->fragment_path, &url);
1937 if (r < 0)
1938 return log_error_errno(r, "Failed to generate URL from path: %m");
1941 xsprintf(buf, "%" PRIu64 ".%" PRIu64, exposure / 10, exposure % 10);
1943 r = table_add_many(overview_table,
1944 TABLE_STRING, info->id,
1945 TABLE_SET_URL, url,
1946 TABLE_STRING, buf,
1947 TABLE_SET_ALIGN_PERCENT, 100,
1948 TABLE_STRING, badness_table[i].name,
1949 TABLE_SET_COLOR, strempty(badness_table[i].color),
1950 TABLE_STRING, special_glyph(badness_table[i].smiley));
1951 if (r < 0)
1952 return table_log_add_error(r);
1955 /* Return error when overall exposure level is over threshold */
1956 if (exposure > threshold)
1957 return -EINVAL;
1959 return 0;
1962 static int property_read_restrict_namespaces(
1963 sd_bus *bus,
1964 const char *member,
1965 sd_bus_message *m,
1966 sd_bus_error *error,
1967 void *userdata) {
1969 SecurityInfo *info = ASSERT_PTR(userdata);
1970 int r;
1971 uint64_t namespaces;
1973 assert(bus);
1974 assert(member);
1975 assert(m);
1977 r = sd_bus_message_read(m, "t", &namespaces);
1978 if (r < 0)
1979 return r;
1981 info->restrict_namespaces = (unsigned long long) namespaces;
1983 return 0;
1986 static int property_read_umask(
1987 sd_bus *bus,
1988 const char *member,
1989 sd_bus_message *m,
1990 sd_bus_error *error,
1991 void *userdata) {
1993 SecurityInfo *info = ASSERT_PTR(userdata);
1994 int r;
1995 uint32_t umask;
1997 assert(bus);
1998 assert(member);
1999 assert(m);
2001 r = sd_bus_message_read(m, "u", &umask);
2002 if (r < 0)
2003 return r;
2005 info->_umask = (mode_t) umask;
2007 return 0;
2010 static int property_read_restrict_address_families(
2011 sd_bus *bus,
2012 const char *member,
2013 sd_bus_message *m,
2014 sd_bus_error *error,
2015 void *userdata) {
2017 SecurityInfo *info = userdata;
2018 int allow_list, r;
2020 assert(bus);
2021 assert(member);
2022 assert(m);
2024 r = sd_bus_message_enter_container(m, 'r', "bas");
2025 if (r < 0)
2026 return r;
2028 r = sd_bus_message_read(m, "b", &allow_list);
2029 if (r < 0)
2030 return r;
2032 info->restrict_address_family_inet =
2033 info->restrict_address_family_unix =
2034 info->restrict_address_family_netlink =
2035 info->restrict_address_family_packet =
2036 info->restrict_address_family_other = allow_list;
2038 r = sd_bus_message_enter_container(m, 'a', "s");
2039 if (r < 0)
2040 return r;
2042 for (;;) {
2043 const char *name;
2045 r = sd_bus_message_read(m, "s", &name);
2046 if (r < 0)
2047 return r;
2048 if (r == 0)
2049 break;
2051 if (STR_IN_SET(name, "AF_INET", "AF_INET6"))
2052 info->restrict_address_family_inet = !allow_list;
2053 else if (streq(name, "AF_UNIX"))
2054 info->restrict_address_family_unix = !allow_list;
2055 else if (streq(name, "AF_NETLINK"))
2056 info->restrict_address_family_netlink = !allow_list;
2057 else if (streq(name, "AF_PACKET"))
2058 info->restrict_address_family_packet = !allow_list;
2059 else
2060 info->restrict_address_family_other = !allow_list;
2063 r = sd_bus_message_exit_container(m);
2064 if (r < 0)
2065 return r;
2067 return sd_bus_message_exit_container(m);
2070 static int property_read_syscall_archs(
2071 sd_bus *bus,
2072 const char *member,
2073 sd_bus_message *m,
2074 sd_bus_error *error,
2075 void *userdata) {
2077 SecurityInfo *info = ASSERT_PTR(userdata);
2078 int r;
2080 assert(bus);
2081 assert(member);
2082 assert(m);
2084 r = sd_bus_message_enter_container(m, 'a', "s");
2085 if (r < 0)
2086 return r;
2088 for (;;) {
2089 const char *name;
2091 r = sd_bus_message_read(m, "s", &name);
2092 if (r < 0)
2093 return r;
2094 if (r == 0)
2095 break;
2097 r = set_put_strdup(&info->system_call_architectures, name);
2098 if (r < 0)
2099 return r;
2102 return sd_bus_message_exit_container(m);
2105 static int property_read_system_call_filter(
2106 sd_bus *bus,
2107 const char *member,
2108 sd_bus_message *m,
2109 sd_bus_error *error,
2110 void *userdata) {
2112 SecurityInfo *info = userdata;
2113 int allow_list, r;
2115 assert(bus);
2116 assert(member);
2117 assert(m);
2119 r = sd_bus_message_enter_container(m, 'r', "bas");
2120 if (r < 0)
2121 return r;
2123 r = sd_bus_message_read(m, "b", &allow_list);
2124 if (r < 0)
2125 return r;
2127 info->system_call_filter_allow_list = allow_list;
2129 r = sd_bus_message_enter_container(m, 'a', "s");
2130 if (r < 0)
2131 return r;
2133 for (;;) {
2134 const char *name;
2136 r = sd_bus_message_read(m, "s", &name);
2137 if (r < 0)
2138 return r;
2139 if (r == 0)
2140 break;
2142 /* ignore errno or action after colon */
2143 r = set_put_strndup(&info->system_call_filter, name, strchrnul(name, ':') - name);
2144 if (r < 0)
2145 return r;
2148 r = sd_bus_message_exit_container(m);
2149 if (r < 0)
2150 return r;
2152 return sd_bus_message_exit_container(m);
2155 static int property_read_ip_address_allow(
2156 sd_bus *bus,
2157 const char *member,
2158 sd_bus_message *m,
2159 sd_bus_error *error,
2160 void *userdata) {
2162 SecurityInfo *info = userdata;
2163 bool deny_ipv4 = false, deny_ipv6 = false;
2164 int r;
2166 assert(bus);
2167 assert(member);
2168 assert(m);
2170 r = sd_bus_message_enter_container(m, 'a', "(iayu)");
2171 if (r < 0)
2172 return r;
2174 for (;;) {
2175 const void *data;
2176 size_t size;
2177 int32_t family;
2178 uint32_t prefixlen;
2180 r = sd_bus_message_enter_container(m, 'r', "iayu");
2181 if (r < 0)
2182 return r;
2183 if (r == 0)
2184 break;
2186 r = sd_bus_message_read(m, "i", &family);
2187 if (r < 0)
2188 return r;
2190 r = sd_bus_message_read_array(m, 'y', &data, &size);
2191 if (r < 0)
2192 return r;
2194 r = sd_bus_message_read(m, "u", &prefixlen);
2195 if (r < 0)
2196 return r;
2198 r = sd_bus_message_exit_container(m);
2199 if (r < 0)
2200 return r;
2202 if (streq(member, "IPAddressAllow")) {
2203 union in_addr_union u;
2205 if (family == AF_INET && size == 4 && prefixlen == 8)
2206 memcpy(&u.in, data, size);
2207 else if (family == AF_INET6 && size == 16 && prefixlen == 128)
2208 memcpy(&u.in6, data, size);
2209 else {
2210 info->ip_address_allow_other = true;
2211 continue;
2214 if (in_addr_is_localhost(family, &u))
2215 info->ip_address_allow_localhost = true;
2216 else
2217 info->ip_address_allow_other = true;
2218 } else {
2219 assert(streq(member, "IPAddressDeny"));
2221 if (family == AF_INET && size == 4 && prefixlen == 0)
2222 deny_ipv4 = true;
2223 else if (family == AF_INET6 && size == 16 && prefixlen == 0)
2224 deny_ipv6 = true;
2228 info->ip_address_deny_all = deny_ipv4 && deny_ipv6;
2230 return sd_bus_message_exit_container(m);
2233 static int property_read_ip_filters(
2234 sd_bus *bus,
2235 const char *member,
2236 sd_bus_message *m,
2237 sd_bus_error *error,
2238 void *userdata) {
2240 SecurityInfo *info = userdata;
2241 _cleanup_strv_free_ char **l = NULL;
2242 int r;
2244 assert(bus);
2245 assert(member);
2246 assert(m);
2248 r = sd_bus_message_read_strv(m, &l);
2249 if (r < 0)
2250 return r;
2252 if (streq(member, "IPIngressFilterPath"))
2253 info->ip_filters_custom_ingress = !strv_isempty(l);
2254 else if (streq(member, "IPEgressFilterPath"))
2255 info->ip_filters_custom_egress = !strv_isempty(l);
2257 return 0;
2260 static int property_read_device_allow(
2261 sd_bus *bus,
2262 const char *member,
2263 sd_bus_message *m,
2264 sd_bus_error *error,
2265 void *userdata) {
2267 SecurityInfo *info = userdata;
2268 int r;
2270 assert(bus);
2271 assert(member);
2272 assert(m);
2274 r = sd_bus_message_enter_container(m, 'a', "(ss)");
2275 if (r < 0)
2276 return r;
2278 for (;;) {
2279 const char *name, *policy;
2281 r = sd_bus_message_read(m, "(ss)", &name, &policy);
2282 if (r < 0)
2283 return r;
2284 if (r == 0)
2285 break;
2287 r = strv_extendf(&info->device_allow, "%s:%s", name, policy);
2288 if (r < 0)
2289 return r;
2292 return sd_bus_message_exit_container(m);
2295 static int acquire_security_info(sd_bus *bus, const char *name, SecurityInfo *info, AnalyzeSecurityFlags flags) {
2297 static const struct bus_properties_map security_map[] = {
2298 { "AmbientCapabilities", "t", NULL, offsetof(SecurityInfo, ambient_capabilities) },
2299 { "CapabilityBoundingSet", "t", NULL, offsetof(SecurityInfo, capability_bounding_set) },
2300 { "DefaultDependencies", "b", NULL, offsetof(SecurityInfo, default_dependencies) },
2301 { "Delegate", "b", NULL, offsetof(SecurityInfo, delegate) },
2302 { "DeviceAllow", "a(ss)", property_read_device_allow, 0 },
2303 { "DevicePolicy", "s", NULL, offsetof(SecurityInfo, device_policy) },
2304 { "DynamicUser", "b", NULL, offsetof(SecurityInfo, dynamic_user) },
2305 { "FragmentPath", "s", NULL, offsetof(SecurityInfo, fragment_path) },
2306 { "IPAddressAllow", "a(iayu)", property_read_ip_address_allow, 0 },
2307 { "IPAddressDeny", "a(iayu)", property_read_ip_address_allow, 0 },
2308 { "IPIngressFilterPath", "as", property_read_ip_filters, 0 },
2309 { "IPEgressFilterPath", "as", property_read_ip_filters, 0 },
2310 { "Id", "s", NULL, offsetof(SecurityInfo, id) },
2311 { "KeyringMode", "s", NULL, offsetof(SecurityInfo, keyring_mode) },
2312 { "ProtectProc", "s", NULL, offsetof(SecurityInfo, protect_proc) },
2313 { "ProcSubset", "s", NULL, offsetof(SecurityInfo, proc_subset) },
2314 { "LoadState", "s", NULL, offsetof(SecurityInfo, load_state) },
2315 { "LockPersonality", "b", NULL, offsetof(SecurityInfo, lock_personality) },
2316 { "MemoryDenyWriteExecute", "b", NULL, offsetof(SecurityInfo, memory_deny_write_execute) },
2317 { "NoNewPrivileges", "b", NULL, offsetof(SecurityInfo, no_new_privileges) },
2318 { "NotifyAccess", "s", NULL, offsetof(SecurityInfo, notify_access) },
2319 { "PrivateDevices", "b", NULL, offsetof(SecurityInfo, private_devices) },
2320 { "PrivateMounts", "b", NULL, offsetof(SecurityInfo, private_mounts) },
2321 { "PrivateNetwork", "b", NULL, offsetof(SecurityInfo, private_network) },
2322 { "PrivateTmp", "b", NULL, offsetof(SecurityInfo, private_tmp) },
2323 { "PrivateUsers", "b", NULL, offsetof(SecurityInfo, private_users) },
2324 { "ProtectControlGroups", "b", NULL, offsetof(SecurityInfo, protect_control_groups) },
2325 { "ProtectHome", "s", NULL, offsetof(SecurityInfo, protect_home) },
2326 { "ProtectHostname", "b", NULL, offsetof(SecurityInfo, protect_hostname) },
2327 { "ProtectKernelModules", "b", NULL, offsetof(SecurityInfo, protect_kernel_modules) },
2328 { "ProtectKernelTunables", "b", NULL, offsetof(SecurityInfo, protect_kernel_tunables) },
2329 { "ProtectKernelLogs", "b", NULL, offsetof(SecurityInfo, protect_kernel_logs) },
2330 { "ProtectClock", "b", NULL, offsetof(SecurityInfo, protect_clock) },
2331 { "ProtectSystem", "s", NULL, offsetof(SecurityInfo, protect_system) },
2332 { "RemoveIPC", "b", NULL, offsetof(SecurityInfo, remove_ipc) },
2333 { "RestrictAddressFamilies", "(bas)", property_read_restrict_address_families, 0 },
2334 { "RestrictNamespaces", "t", property_read_restrict_namespaces, 0 },
2335 { "RestrictRealtime", "b", NULL, offsetof(SecurityInfo, restrict_realtime) },
2336 { "RestrictSUIDSGID", "b", NULL, offsetof(SecurityInfo, restrict_suid_sgid) },
2337 { "RootDirectory", "s", NULL, offsetof(SecurityInfo, root_directory) },
2338 { "RootImage", "s", NULL, offsetof(SecurityInfo, root_image) },
2339 { "SupplementaryGroups", "as", NULL, offsetof(SecurityInfo, supplementary_groups) },
2340 { "SystemCallArchitectures", "as", property_read_syscall_archs, 0 },
2341 { "SystemCallFilter", "(as)", property_read_system_call_filter, 0 },
2342 { "Type", "s", NULL, offsetof(SecurityInfo, type) },
2343 { "UMask", "u", property_read_umask, 0 },
2344 { "User", "s", NULL, offsetof(SecurityInfo, user) },
2348 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2349 _cleanup_free_ char *path = NULL;
2350 int r;
2352 /* Note: this mangles *info on failure! */
2354 assert(bus);
2355 assert(name);
2356 assert(info);
2358 path = unit_dbus_path_from_name(name);
2359 if (!path)
2360 return log_oom();
2362 r = bus_map_all_properties(
2363 bus,
2364 "org.freedesktop.systemd1",
2365 path,
2366 security_map,
2367 BUS_MAP_STRDUP | BUS_MAP_BOOLEAN_AS_BOOL,
2368 &error,
2369 NULL,
2370 info);
2371 if (r < 0)
2372 return log_error_errno(r, "Failed to get unit properties: %s", bus_error_message(&error, r));
2374 if (!streq_ptr(info->load_state, "loaded")) {
2376 if (FLAGS_SET(flags, ANALYZE_SECURITY_ONLY_LOADED))
2377 return -EMEDIUMTYPE;
2379 if (streq_ptr(info->load_state, "not-found"))
2380 log_error("Unit %s not found, cannot analyze.", name);
2381 else if (streq_ptr(info->load_state, "masked"))
2382 log_error("Unit %s is masked, cannot analyze.", name);
2383 else
2384 log_error("Unit %s not loaded properly, cannot analyze.", name);
2386 return -EINVAL;
2389 if (FLAGS_SET(flags, ANALYZE_SECURITY_ONLY_LONG_RUNNING) && streq_ptr(info->type, "oneshot"))
2390 return -EMEDIUMTYPE;
2392 if (info->private_devices ||
2393 info->private_tmp ||
2394 info->protect_control_groups ||
2395 info->protect_kernel_tunables ||
2396 info->protect_kernel_modules ||
2397 !streq_ptr(info->protect_home, "no") ||
2398 !streq_ptr(info->protect_system, "no") ||
2399 info->root_image)
2400 info->private_mounts = true;
2402 if (info->protect_kernel_modules)
2403 info->capability_bounding_set &= ~(UINT64_C(1) << CAP_SYS_MODULE);
2405 if (info->protect_kernel_logs)
2406 info->capability_bounding_set &= ~(UINT64_C(1) << CAP_SYSLOG);
2408 if (info->protect_clock)
2409 info->capability_bounding_set &= ~((UINT64_C(1) << CAP_SYS_TIME) |
2410 (UINT64_C(1) << CAP_WAKE_ALARM));
2412 if (info->private_devices)
2413 info->capability_bounding_set &= ~((UINT64_C(1) << CAP_MKNOD) |
2414 (UINT64_C(1) << CAP_SYS_RAWIO));
2416 return 0;
2419 static int analyze_security_one(sd_bus *bus,
2420 const char *name,
2421 Table *overview_table,
2422 AnalyzeSecurityFlags flags,
2423 unsigned threshold,
2424 JsonVariant *policy,
2425 PagerFlags pager_flags,
2426 JsonFormatFlags json_format_flags) {
2428 _cleanup_(security_info_freep) SecurityInfo *info = security_info_new();
2429 if (!info)
2430 return log_oom();
2432 int r;
2434 assert(bus);
2435 assert(name);
2437 r = acquire_security_info(bus, name, info, flags);
2438 if (r == -EMEDIUMTYPE) /* Ignore this one because not loaded or Type is oneshot */
2439 return 0;
2440 if (r < 0)
2441 return r;
2443 r = assess(info, overview_table, flags, threshold, policy, pager_flags, json_format_flags);
2444 if (r < 0)
2445 return r;
2447 return 0;
2450 /* Refactoring SecurityInfo so that it can make use of existing struct variables instead of reading from dbus */
2451 static int get_security_info(Unit *u, ExecContext *c, CGroupContext *g, SecurityInfo **ret_info) {
2452 assert(ret_info);
2454 _cleanup_(security_info_freep) SecurityInfo *info = security_info_new();
2455 if (!info)
2456 return log_oom();
2458 if (u) {
2459 if (u->id) {
2460 info->id = strdup(u->id);
2461 if (!info->id)
2462 return log_oom();
2464 if (unit_type_to_string(u->type)) {
2465 info->type = strdup(unit_type_to_string(u->type));
2466 if (!info->type)
2467 return log_oom();
2469 if (unit_load_state_to_string(u->load_state)) {
2470 info->load_state = strdup(unit_load_state_to_string(u->load_state));
2471 if (!info->load_state)
2472 return log_oom();
2474 if (u->fragment_path) {
2475 info->fragment_path = strdup(u->fragment_path);
2476 if (!info->fragment_path)
2477 return log_oom();
2479 info->default_dependencies = u->default_dependencies;
2480 if (u->type == UNIT_SERVICE && notify_access_to_string(SERVICE(u)->notify_access)) {
2481 info->notify_access = strdup(notify_access_to_string(SERVICE(u)->notify_access));
2482 if (!info->notify_access)
2483 return log_oom();
2487 if (c) {
2488 info->ambient_capabilities = c->capability_ambient_set;
2489 info->capability_bounding_set = c->capability_bounding_set;
2490 if (c->user) {
2491 info->user = strdup(c->user);
2492 if (!info->user)
2493 return log_oom();
2495 if (c->supplementary_groups) {
2496 info->supplementary_groups = strv_copy(c->supplementary_groups);
2497 if (!info->supplementary_groups)
2498 return log_oom();
2500 info->dynamic_user = c->dynamic_user;
2501 if (exec_keyring_mode_to_string(c->keyring_mode)) {
2502 info->keyring_mode = strdup(exec_keyring_mode_to_string(c->keyring_mode));
2503 if (!info->keyring_mode)
2504 return log_oom();
2506 if (protect_proc_to_string(c->protect_proc)) {
2507 info->protect_proc = strdup(protect_proc_to_string(c->protect_proc));
2508 if (!info->protect_proc)
2509 return log_oom();
2511 if (proc_subset_to_string(c->proc_subset)) {
2512 info->proc_subset = strdup(proc_subset_to_string(c->proc_subset));
2513 if (!info->proc_subset)
2514 return log_oom();
2516 info->lock_personality = c->lock_personality;
2517 info->memory_deny_write_execute = c->memory_deny_write_execute;
2518 info->no_new_privileges = c->no_new_privileges;
2519 info->protect_hostname = c->protect_hostname;
2520 info->private_devices = c->private_devices;
2521 info->private_mounts = c->private_mounts;
2522 info->private_network = c->private_network;
2523 info->private_tmp = c->private_tmp;
2524 info->private_users = c->private_users;
2525 info->protect_control_groups = c->protect_control_groups;
2526 info->protect_kernel_modules = c->protect_kernel_modules;
2527 info->protect_kernel_tunables = c->protect_kernel_tunables;
2528 info->protect_kernel_logs = c->protect_kernel_logs;
2529 info->protect_clock = c->protect_clock;
2530 if (protect_home_to_string(c->protect_home)) {
2531 info->protect_home = strdup(protect_home_to_string(c->protect_home));
2532 if (!info->protect_home)
2533 return log_oom();
2535 if (protect_system_to_string(c->protect_system)) {
2536 info->protect_system = strdup(protect_system_to_string(c->protect_system));
2537 if (!info->protect_system)
2538 return log_oom();
2540 info->remove_ipc = c->remove_ipc;
2541 info->restrict_address_family_inet =
2542 info->restrict_address_family_unix =
2543 info->restrict_address_family_netlink =
2544 info->restrict_address_family_packet =
2545 info->restrict_address_family_other =
2546 c->address_families_allow_list;
2548 void *key;
2549 SET_FOREACH(key, c->address_families) {
2550 int family = PTR_TO_INT(key);
2551 if (family == 0)
2552 continue;
2553 if (IN_SET(family, AF_INET, AF_INET6))
2554 info->restrict_address_family_inet = !c->address_families_allow_list;
2555 else if (family == AF_UNIX)
2556 info->restrict_address_family_unix = !c->address_families_allow_list;
2557 else if (family == AF_NETLINK)
2558 info->restrict_address_family_netlink = !c->address_families_allow_list;
2559 else if (family == AF_PACKET)
2560 info->restrict_address_family_packet = !c->address_families_allow_list;
2561 else
2562 info->restrict_address_family_other = !c->address_families_allow_list;
2565 info->restrict_namespaces = c->restrict_namespaces;
2566 info->restrict_realtime = c->restrict_realtime;
2567 info->restrict_suid_sgid = c->restrict_suid_sgid;
2568 if (c->root_directory) {
2569 info->root_directory = strdup(c->root_directory);
2570 if (!info->root_directory)
2571 return log_oom();
2573 if (c->root_image) {
2574 info->root_image = strdup(c->root_image);
2575 if (!info->root_image)
2576 return log_oom();
2578 info->_umask = c->umask;
2580 #if HAVE_SECCOMP
2581 SET_FOREACH(key, c->syscall_archs) {
2582 const char *name;
2584 name = seccomp_arch_to_string(PTR_TO_UINT32(key) - 1);
2585 if (!name)
2586 continue;
2588 if (set_put_strdup(&info->system_call_architectures, name) < 0)
2589 return log_oom();
2592 info->system_call_filter_allow_list = c->syscall_allow_list;
2594 void *id, *num;
2595 HASHMAP_FOREACH_KEY(num, id, c->syscall_filter) {
2596 _cleanup_free_ char *name = NULL;
2598 if (info->system_call_filter_allow_list && PTR_TO_INT(num) >= 0)
2599 continue;
2601 name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
2602 if (!name)
2603 continue;
2605 if (set_ensure_consume(&info->system_call_filter, &string_hash_ops_free, TAKE_PTR(name)) < 0)
2606 return log_oom();
2608 #endif
2611 if (g) {
2612 info->delegate = g->delegate;
2613 if (cgroup_device_policy_to_string(g->device_policy)) {
2614 info->device_policy = strdup(cgroup_device_policy_to_string(g->device_policy));
2615 if (!info->device_policy)
2616 return log_oom();
2619 struct in_addr_prefix *i;
2620 bool deny_ipv4 = false, deny_ipv6 = false;
2622 SET_FOREACH(i, g->ip_address_deny) {
2623 if (i->family == AF_INET && i->prefixlen == 0)
2624 deny_ipv4 = true;
2625 else if (i->family == AF_INET6 && i->prefixlen == 0)
2626 deny_ipv6 = true;
2628 info->ip_address_deny_all = deny_ipv4 && deny_ipv6;
2630 info->ip_address_allow_localhost = info->ip_address_allow_other = false;
2631 SET_FOREACH(i, g->ip_address_allow) {
2632 if (in_addr_is_localhost(i->family, &i->address))
2633 info->ip_address_allow_localhost = true;
2634 else
2635 info->ip_address_allow_other = true;
2638 info->ip_filters_custom_ingress = !strv_isempty(g->ip_filters_ingress);
2639 info->ip_filters_custom_egress = !strv_isempty(g->ip_filters_egress);
2641 LIST_FOREACH(device_allow, a, g->device_allow)
2642 if (strv_extendf(&info->device_allow,
2643 "%s:%s",
2644 a->path,
2645 cgroup_device_permissions_to_string(a->permissions)) < 0)
2646 return log_oom();
2649 *ret_info = TAKE_PTR(info);
2651 return 0;
2654 static int offline_security_check(Unit *u,
2655 unsigned threshold,
2656 JsonVariant *policy,
2657 PagerFlags pager_flags,
2658 JsonFormatFlags json_format_flags) {
2660 _cleanup_(table_unrefp) Table *overview_table = NULL;
2661 AnalyzeSecurityFlags flags = 0;
2662 _cleanup_(security_info_freep) SecurityInfo *info = NULL;
2663 int r;
2665 assert(u);
2667 if (DEBUG_LOGGING)
2668 unit_dump(u, stdout, "\t");
2670 r = get_security_info(u, unit_get_exec_context(u), unit_get_cgroup_context(u), &info);
2671 if (r < 0)
2672 return r;
2674 return assess(info, overview_table, flags, threshold, policy, pager_flags, json_format_flags);
2677 static int offline_security_checks(
2678 char **filenames,
2679 JsonVariant *policy,
2680 RuntimeScope scope,
2681 bool check_man,
2682 bool run_generators,
2683 unsigned threshold,
2684 const char *root,
2685 const char *profile,
2686 PagerFlags pager_flags,
2687 JsonFormatFlags json_format_flags) {
2689 const ManagerTestRunFlags flags =
2690 MANAGER_TEST_RUN_MINIMAL |
2691 MANAGER_TEST_RUN_ENV_GENERATORS |
2692 MANAGER_TEST_RUN_IGNORE_DEPENDENCIES |
2693 MANAGER_TEST_DONT_OPEN_EXECUTOR |
2694 run_generators * MANAGER_TEST_RUN_GENERATORS;
2696 _cleanup_(manager_freep) Manager *m = NULL;
2697 Unit *units[strv_length(filenames)];
2698 int r, k;
2699 size_t count = 0;
2701 if (strv_isempty(filenames))
2702 return 0;
2704 r = verify_set_unit_path(filenames);
2705 if (r < 0)
2706 return log_error_errno(r, "Failed to set unit load path: %m");
2708 r = manager_new(scope, flags, &m);
2709 if (r < 0)
2710 return log_error_errno(r, "Failed to initialize manager: %m");
2712 log_debug("Starting manager...");
2714 r = manager_startup(m, /* serialization= */ NULL, /* fds= */ NULL, root);
2715 if (r < 0)
2716 return r;
2718 if (profile) {
2719 /* Ensure the temporary directory is in the search path, so that we can add drop-ins. */
2720 r = strv_extend(&m->lookup_paths.search_path, m->lookup_paths.temporary_dir);
2721 if (r < 0)
2722 return log_oom();
2725 log_debug("Loading remaining units from the command line...");
2727 STRV_FOREACH(filename, filenames) {
2728 _cleanup_free_ char *prepared = NULL;
2730 log_debug("Handling %s...", *filename);
2732 k = verify_prepare_filename(*filename, &prepared);
2733 if (k < 0) {
2734 log_warning_errno(k, "Failed to prepare filename %s: %m", *filename);
2735 RET_GATHER(r, k);
2736 continue;
2739 /* When a portable image is analyzed, the profile is what provides a good chunk of
2740 * the security-related settings, but they are obviously not shipped with the image.
2741 * This allows to take them in consideration. */
2742 if (profile) {
2743 _cleanup_free_ char *unit_name = NULL, *dropin = NULL, *profile_path = NULL;
2745 r = path_extract_filename(prepared, &unit_name);
2746 if (r < 0)
2747 return log_oom();
2749 dropin = strjoin(m->lookup_paths.temporary_dir, "/", unit_name, ".d/profile.conf");
2750 if (!dropin)
2751 return log_oom();
2752 (void) mkdir_parents(dropin, 0755);
2754 if (!is_path(profile)) {
2755 r = find_portable_profile(profile, unit_name, &profile_path);
2756 if (r < 0)
2757 return log_error_errno(r, "Failed to find portable profile %s: %m", profile);
2758 profile = profile_path;
2761 r = copy_file(profile, dropin, 0, 0644, 0);
2762 if (r < 0)
2763 return log_error_errno(r, "Failed to copy: %m");
2766 k = manager_load_startable_unit_or_warn(m, NULL, prepared, &units[count]);
2767 if (k < 0) {
2768 RET_GATHER(r, k);
2769 continue;
2772 count++;
2775 for (size_t i = 0; i < count; i++)
2776 RET_GATHER(r, offline_security_check(units[i], threshold, policy, pager_flags, json_format_flags));
2778 return r;
2781 static int analyze_security(sd_bus *bus,
2782 char **units,
2783 JsonVariant *policy,
2784 RuntimeScope scope,
2785 bool check_man,
2786 bool run_generators,
2787 bool offline,
2788 unsigned threshold,
2789 const char *root,
2790 const char *profile,
2791 JsonFormatFlags json_format_flags,
2792 PagerFlags pager_flags,
2793 AnalyzeSecurityFlags flags) {
2795 _cleanup_(table_unrefp) Table *overview_table = NULL;
2796 int ret = 0, r;
2798 assert(!!bus != offline);
2800 if (offline)
2801 return offline_security_checks(units, policy, scope, check_man, run_generators, threshold, root, profile, pager_flags, json_format_flags);
2803 if (strv_length(units) != 1) {
2804 overview_table = table_new("unit", "exposure", "predicate", "happy");
2805 if (!overview_table)
2806 return log_oom();
2809 if (strv_isempty(units)) {
2810 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2811 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
2812 _cleanup_strv_free_ char **list = NULL;
2813 size_t n = 0;
2815 r = bus_call_method(
2816 bus,
2817 bus_systemd_mgr,
2818 "ListUnits",
2819 &error,
2820 &reply,
2821 NULL);
2822 if (r < 0)
2823 return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r));
2825 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
2826 if (r < 0)
2827 return bus_log_parse_error(r);
2829 for (;;) {
2830 UnitInfo info;
2831 char *copy = NULL;
2833 r = bus_parse_unit_info(reply, &info);
2834 if (r < 0)
2835 return bus_log_parse_error(r);
2836 if (r == 0)
2837 break;
2839 if (!endswith(info.id, ".service"))
2840 continue;
2842 if (!GREEDY_REALLOC(list, n + 2))
2843 return log_oom();
2845 copy = strdup(info.id);
2846 if (!copy)
2847 return log_oom();
2849 list[n++] = copy;
2850 list[n] = NULL;
2853 strv_sort(list);
2855 flags |= ANALYZE_SECURITY_SHORT|ANALYZE_SECURITY_ONLY_LOADED|ANALYZE_SECURITY_ONLY_LONG_RUNNING;
2857 STRV_FOREACH(i, list) {
2858 r = analyze_security_one(bus, *i, overview_table, flags, threshold, policy, pager_flags, json_format_flags);
2859 if (r < 0 && ret >= 0)
2860 ret = r;
2863 } else
2864 STRV_FOREACH(i, units) {
2865 _cleanup_free_ char *mangled = NULL, *instance = NULL;
2866 const char *name;
2868 if (!FLAGS_SET(flags, ANALYZE_SECURITY_SHORT) && i != units) {
2869 putc('\n', stdout);
2870 fflush(stdout);
2873 r = unit_name_mangle(*i, 0, &mangled);
2874 if (r < 0)
2875 return log_error_errno(r, "Failed to mangle unit name '%s': %m", *i);
2877 if (!endswith(mangled, ".service"))
2878 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2879 "Unit %s is not a service unit, refusing.",
2880 *i);
2882 if (unit_name_is_valid(mangled, UNIT_NAME_TEMPLATE)) {
2883 r = unit_name_replace_instance(mangled, "test-instance", &instance);
2884 if (r < 0)
2885 return log_oom();
2887 name = instance;
2888 } else
2889 name = mangled;
2891 r = analyze_security_one(bus, name, overview_table, flags, threshold, policy, pager_flags, json_format_flags);
2892 if (r < 0 && ret >= 0)
2893 ret = r;
2896 if (overview_table) {
2897 if (!FLAGS_SET(flags, ANALYZE_SECURITY_SHORT)) {
2898 putc('\n', stdout);
2899 fflush(stdout);
2902 r = table_print_with_pager(overview_table, json_format_flags, pager_flags, /* show_header= */true);
2903 if (r < 0)
2904 return log_error_errno(r, "Failed to output table: %m");
2906 return ret;
2909 int verb_security(int argc, char *argv[], void *userdata) {
2910 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2911 _cleanup_(json_variant_unrefp) JsonVariant *policy = NULL;
2912 int r;
2913 unsigned line, column;
2915 if (!arg_offline) {
2916 r = acquire_bus(&bus, NULL);
2917 if (r < 0)
2918 return bus_log_connect_error(r, arg_transport);
2921 pager_open(arg_pager_flags);
2923 if (arg_security_policy) {
2924 r = json_parse_file(/*f=*/ NULL, arg_security_policy, /*flags=*/ 0, &policy, &line, &column);
2925 if (r < 0)
2926 return log_error_errno(r, "Failed to parse '%s' at %u:%u: %m", arg_security_policy, line, column);
2927 } else {
2928 _cleanup_fclose_ FILE *f = NULL;
2929 _cleanup_free_ char *pp = NULL;
2931 r = search_and_fopen_nulstr("systemd-analyze-security.policy", "re", /*root=*/ NULL, CONF_PATHS_NULSTR("systemd"), &f, &pp);
2932 if (r < 0 && r != -ENOENT)
2933 return r;
2935 if (f) {
2936 r = json_parse_file(f, pp, /*flags=*/ 0, &policy, &line, &column);
2937 if (r < 0)
2938 return log_error_errno(r, "[%s:%u:%u] Failed to parse JSON policy: %m", pp, line, column);
2942 return analyze_security(
2943 bus,
2944 strv_skip(argv, 1),
2945 policy,
2946 arg_runtime_scope,
2947 arg_man,
2948 arg_generators,
2949 arg_offline,
2950 arg_threshold,
2951 arg_root,
2952 arg_profile,
2953 arg_json_format_flags,
2954 arg_pager_flags,
2955 /*flags=*/ 0);