1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <sys/utsname.h>
6 #include "analyze-security.h"
7 #include "analyze-verify.h"
9 #include "bus-map-properties.h"
10 #include "bus-unit-util.h"
13 #include "format-table.h"
14 #include "in-addr-util.h"
15 #include "locale-util.h"
18 #include "missing_capability.h"
19 #include "missing_sched.h"
20 #include "nulstr-util.h"
21 #include "parse-util.h"
22 #include "path-util.h"
23 #include "pretty-print.h"
25 # include "seccomp-util.h"
29 #include "stdio-util.h"
31 #include "terminal-util.h"
33 #include "unit-name.h"
34 #include "unit-serialize.h"
36 typedef struct SecurityInfo
{
41 bool default_dependencies
;
43 uint64_t ambient_capabilities
;
44 uint64_t capability_bounding_set
;
47 char **supplementary_groups
;
50 bool ip_address_deny_all
;
51 bool ip_address_allow_localhost
;
52 bool ip_address_allow_other
;
54 bool ip_filters_custom_ingress
;
55 bool ip_filters_custom_egress
;
60 bool lock_personality
;
61 bool memory_deny_write_execute
;
62 bool no_new_privileges
;
64 bool protect_hostname
;
72 bool protect_control_groups
;
73 bool protect_kernel_modules
;
74 bool protect_kernel_tunables
;
75 bool protect_kernel_logs
;
83 bool restrict_address_family_inet
;
84 bool restrict_address_family_unix
;
85 bool restrict_address_family_netlink
;
86 bool restrict_address_family_packet
;
87 bool restrict_address_family_other
;
89 unsigned long long restrict_namespaces
;
90 bool restrict_realtime
;
91 bool restrict_suid_sgid
;
98 bool device_allow_non_empty
;
100 Set
*system_call_architectures
;
102 bool system_call_filter_allow_list
;
103 Hashmap
*system_call_filter
;
108 struct security_assessor
{
110 const char *json_field
;
111 const char *description_good
;
112 const char *description_bad
;
113 const char *description_na
;
118 const struct security_assessor
*a
,
119 const SecurityInfo
*info
,
121 uint64_t *ret_badness
,
122 char **ret_description
);
125 bool default_dependencies_only
;
128 static SecurityInfo
*security_info_new(void) {
129 SecurityInfo
*info
= new(SecurityInfo
, 1);
133 *info
= (SecurityInfo
) {
134 .default_dependencies
= true,
135 .capability_bounding_set
= UINT64_MAX
,
136 .restrict_namespaces
= UINT64_MAX
,
143 static SecurityInfo
*security_info_free(SecurityInfo
*i
) {
150 free(i
->fragment_path
);
154 free(i
->protect_home
);
155 free(i
->protect_system
);
157 free(i
->root_directory
);
160 free(i
->keyring_mode
);
161 free(i
->protect_proc
);
162 free(i
->proc_subset
);
163 free(i
->notify_access
);
165 free(i
->device_policy
);
167 strv_free(i
->supplementary_groups
);
168 set_free(i
->system_call_architectures
);
170 hashmap_free(i
->system_call_filter
);
175 DEFINE_TRIVIAL_CLEANUP_FUNC(SecurityInfo
*, security_info_free
);
177 static bool security_info_runs_privileged(const SecurityInfo
*i
) {
180 if (STRPTR_IN_SET(i
->user
, "0", "root"))
186 return isempty(i
->user
);
189 static int assess_bool(
190 const struct security_assessor
*a
,
191 const SecurityInfo
*info
,
193 uint64_t *ret_badness
,
194 char **ret_description
) {
196 const bool *b
= data
;
200 assert(ret_description
);
202 *ret_badness
= a
->parameter
? *b
: !*b
;
203 *ret_description
= NULL
;
208 static int assess_user(
209 const struct security_assessor
*a
,
210 const SecurityInfo
*info
,
212 uint64_t *ret_badness
,
213 char **ret_description
) {
215 _cleanup_free_
char *d
= NULL
;
219 assert(ret_description
);
221 if (streq_ptr(info
->user
, NOBODY_USER_NAME
)) {
222 d
= strdup("Service runs under as '" NOBODY_USER_NAME
"' user, which should not be used for services");
224 } else if (info
->dynamic_user
&& !STR_IN_SET(info
->user
, "0", "root")) {
225 d
= strdup("Service runs under a transient non-root user identity");
227 } else if (info
->user
&& !STR_IN_SET(info
->user
, "0", "root", "")) {
228 d
= strdup("Service runs under a static non-root user identity");
232 *ret_description
= NULL
;
240 *ret_description
= TAKE_PTR(d
);
245 static int assess_protect_home(
246 const struct security_assessor
*a
,
247 const SecurityInfo
*info
,
249 uint64_t *ret_badness
,
250 char **ret_description
) {
252 const char *description
;
258 assert(ret_description
);
261 description
= "Service has full access to home directories";
263 r
= parse_boolean(info
->protect_home
);
265 if (streq_ptr(info
->protect_home
, "read-only")) {
267 description
= "Service has read-only access to home directories";
268 } else if (streq_ptr(info
->protect_home
, "tmpfs")) {
270 description
= "Service has access to fake empty home directories";
274 description
= "Service has no access to home directories";
277 copy
= strdup(description
);
281 *ret_badness
= badness
;
282 *ret_description
= copy
;
287 static int assess_protect_system(
288 const struct security_assessor
*a
,
289 const SecurityInfo
*info
,
291 uint64_t *ret_badness
,
292 char **ret_description
) {
294 const char *description
;
300 assert(ret_description
);
303 description
= "Service has full access to the OS file hierarchy";
305 r
= parse_boolean(info
->protect_system
);
307 if (streq_ptr(info
->protect_system
, "full")) {
309 description
= "Service has very limited write access to the OS file hierarchy";
310 } else if (streq_ptr(info
->protect_system
, "strict")) {
312 description
= "Service has strict read-only access to the OS file hierarchy";
316 description
= "Service has limited write access to the OS file hierarchy";
319 copy
= strdup(description
);
323 *ret_badness
= badness
;
324 *ret_description
= copy
;
329 static int assess_root_directory(
330 const struct security_assessor
*a
,
331 const SecurityInfo
*info
,
333 uint64_t *ret_badness
,
334 char **ret_description
) {
337 assert(ret_description
);
340 empty_or_root(info
->root_directory
) &&
341 empty_or_root(info
->root_image
);
342 *ret_description
= NULL
;
347 static int assess_capability_bounding_set(
348 const struct security_assessor
*a
,
349 const SecurityInfo
*info
,
351 uint64_t *ret_badness
,
352 char **ret_description
) {
355 assert(ret_description
);
357 *ret_badness
= !!(info
->capability_bounding_set
& a
->parameter
);
358 *ret_description
= NULL
;
363 static int assess_umask(
364 const struct security_assessor
*a
,
365 const SecurityInfo
*info
,
367 uint64_t *ret_badness
,
368 char **ret_description
) {
375 assert(ret_description
);
377 if (!FLAGS_SET(info
->_umask
, 0002)) {
378 d
= "Files created by service are world-writable by default";
380 } else if (!FLAGS_SET(info
->_umask
, 0004)) {
381 d
= "Files created by service are world-readable by default";
383 } else if (!FLAGS_SET(info
->_umask
, 0020)) {
384 d
= "Files created by service are group-writable by default";
386 } else if (!FLAGS_SET(info
->_umask
, 0040)) {
387 d
= "Files created by service are group-readable by default";
390 d
= "Files created by service are accessible only by service's own user by default";
399 *ret_description
= copy
;
404 static int assess_keyring_mode(
405 const struct security_assessor
*a
,
406 const SecurityInfo
*info
,
408 uint64_t *ret_badness
,
409 char **ret_description
) {
412 assert(ret_description
);
414 *ret_badness
= !streq_ptr(info
->keyring_mode
, "private");
415 *ret_description
= NULL
;
420 static int assess_protect_proc(
421 const struct security_assessor
*a
,
422 const SecurityInfo
*info
,
424 uint64_t *ret_badness
,
425 char **ret_description
) {
428 assert(ret_description
);
430 if (streq_ptr(info
->protect_proc
, "noaccess"))
432 else if (STRPTR_IN_SET(info
->protect_proc
, "invisible", "ptraceable"))
437 *ret_description
= NULL
;
442 static int assess_proc_subset(
443 const struct security_assessor
*a
,
444 const SecurityInfo
*info
,
446 uint64_t *ret_badness
,
447 char **ret_description
) {
450 assert(ret_description
);
452 *ret_badness
= !streq_ptr(info
->proc_subset
, "pid");
453 *ret_description
= NULL
;
458 static int assess_notify_access(
459 const struct security_assessor
*a
,
460 const SecurityInfo
*info
,
462 uint64_t *ret_badness
,
463 char **ret_description
) {
466 assert(ret_description
);
468 *ret_badness
= streq_ptr(info
->notify_access
, "all");
469 *ret_description
= NULL
;
474 static int assess_remove_ipc(
475 const struct security_assessor
*a
,
476 const SecurityInfo
*info
,
478 uint64_t *ret_badness
,
479 char **ret_description
) {
482 assert(ret_description
);
484 if (security_info_runs_privileged(info
))
485 *ret_badness
= UINT64_MAX
;
487 *ret_badness
= !info
->remove_ipc
;
489 *ret_description
= NULL
;
493 static int assess_supplementary_groups(
494 const struct security_assessor
*a
,
495 const SecurityInfo
*info
,
497 uint64_t *ret_badness
,
498 char **ret_description
) {
501 assert(ret_description
);
503 if (security_info_runs_privileged(info
))
504 *ret_badness
= UINT64_MAX
;
506 *ret_badness
= !strv_isempty(info
->supplementary_groups
);
508 *ret_description
= NULL
;
512 static int assess_restrict_namespaces(
513 const struct security_assessor
*a
,
514 const SecurityInfo
*info
,
516 uint64_t *ret_badness
,
517 char **ret_description
) {
520 assert(ret_description
);
522 *ret_badness
= !!(info
->restrict_namespaces
& a
->parameter
);
523 *ret_description
= NULL
;
528 static int assess_system_call_architectures(
529 const struct security_assessor
*a
,
530 const SecurityInfo
*info
,
532 uint64_t *ret_badness
,
533 char **ret_description
) {
539 assert(ret_description
);
541 if (set_isempty(info
->system_call_architectures
)) {
543 d
= strdup("Service may execute system calls with all ABIs");
544 } else if (set_contains(info
->system_call_architectures
, "native") &&
545 set_size(info
->system_call_architectures
) == 1) {
547 d
= strdup("Service may execute system calls only with native ABI");
550 d
= strdup("Service may execute system calls with multiple ABIs");
557 *ret_description
= d
;
564 static bool syscall_names_in_filter(Hashmap
*s
, bool allow_list
, const SyscallFilterSet
*f
, const char **ret_offending_syscall
) {
567 NULSTR_FOREACH(syscall
, f
->value
) {
570 if (syscall
[0] == '@') {
571 const SyscallFilterSet
*g
;
573 assert_se(g
= syscall_filter_set_find(syscall
));
574 if (syscall_names_in_filter(s
, allow_list
, g
, ret_offending_syscall
))
575 return true; /* bad! */
580 /* Let's see if the system call actually exists on this platform, before complaining */
581 id
= seccomp_syscall_resolve_name(syscall
);
585 if (hashmap_contains(s
, syscall
) == allow_list
) {
586 log_debug("Offending syscall filter item: %s", syscall
);
587 if (ret_offending_syscall
)
588 *ret_offending_syscall
= syscall
;
589 return true; /* bad! */
593 *ret_offending_syscall
= NULL
;
597 static int assess_system_call_filter(
598 const struct security_assessor
*a
,
599 const SecurityInfo
*info
,
601 uint64_t *ret_badness
,
602 char **ret_description
) {
607 assert(ret_description
);
609 assert(a
->parameter
< _SYSCALL_FILTER_SET_MAX
);
610 const SyscallFilterSet
*f
= syscall_filter_sets
+ a
->parameter
;
612 _cleanup_free_
char *d
= NULL
;
616 if (!info
->system_call_filter_allow_list
&& hashmap_isempty(info
->system_call_filter
)) {
617 r
= free_and_strdup(&d
, "Service does not filter system calls");
621 const char *offender
= NULL
;
623 log_debug("Analyzing system call filter, checking against: %s", f
->name
);
624 bad
= syscall_names_in_filter(info
->system_call_filter
, info
->system_call_filter_allow_list
, f
, &offender
);
625 log_debug("Result: %s", bad
? "bad" : "good");
627 if (info
->system_call_filter_allow_list
) {
629 r
= asprintf(&d
, "System call allow list defined for service, and %s is included "
630 "(e.g. %s is allowed)",
634 r
= asprintf(&d
, "System call allow list defined for service, and %s is not included",
640 r
= asprintf(&d
, "System call deny list defined for service, and %s is not included "
641 "(e.g. %s is allowed)",
645 r
= asprintf(&d
, "System call deny list defined for service, and %s is included",
655 *ret_description
= TAKE_PTR(d
);
662 static int assess_ip_address_allow(
663 const struct security_assessor
*a
,
664 const SecurityInfo
*info
,
666 uint64_t *ret_badness
,
667 char **ret_description
) {
674 assert(ret_description
);
676 if (info
->ip_filters_custom_ingress
|| info
->ip_filters_custom_egress
) {
677 d
= strdup("Service defines custom ingress/egress IP filters with BPF programs");
679 } else if (!info
->ip_address_deny_all
) {
680 d
= strdup("Service does not define an IP address allow list");
682 } else if (info
->ip_address_allow_other
) {
683 d
= strdup("Service defines IP address allow list with non-localhost entries");
685 } else if (info
->ip_address_allow_localhost
) {
686 d
= strdup("Service defines IP address allow list with only localhost entries");
689 d
= strdup("Service blocks all IP address ranges");
697 *ret_description
= d
;
702 static int assess_device_allow(
703 const struct security_assessor
*a
,
704 const SecurityInfo
*info
,
706 uint64_t *ret_badness
,
707 char **ret_description
) {
714 assert(ret_description
);
716 if (STRPTR_IN_SET(info
->device_policy
, "strict", "closed")) {
718 if (info
->device_allow_non_empty
) {
719 d
= strdup("Service has a device ACL with some special devices");
722 d
= strdup("Service has a minimal device ACL");
726 d
= strdup("Service has no device ACL");
734 *ret_description
= d
;
739 static int assess_ambient_capabilities(
740 const struct security_assessor
*a
,
741 const SecurityInfo
*info
,
743 uint64_t *ret_badness
,
744 char **ret_description
) {
747 assert(ret_description
);
749 *ret_badness
= info
->ambient_capabilities
!= 0;
750 *ret_description
= NULL
;
755 static const struct security_assessor security_assessor_table
[] = {
757 .id
= "User=/DynamicUser=",
758 .json_field
= "UserOrDynamicUser",
759 .description_bad
= "Service runs as root user",
760 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#User=",
763 .assess
= assess_user
,
766 .id
= "SupplementaryGroups=",
767 .json_field
= "SupplementaryGroups",
768 .description_good
= "Service has no supplementary groups",
769 .description_bad
= "Service runs with supplementary groups",
770 .description_na
= "Service runs as root, option does not matter",
771 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SupplementaryGroups=",
774 .assess
= assess_supplementary_groups
,
777 .id
= "PrivateDevices=",
778 .json_field
= "PrivateDevices",
779 .description_good
= "Service has no access to hardware devices",
780 .description_bad
= "Service potentially has access to hardware devices",
781 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateDevices=",
784 .assess
= assess_bool
,
785 .offset
= offsetof(SecurityInfo
, private_devices
),
788 .id
= "PrivateMounts=",
789 .json_field
= "PrivateMounts",
790 .description_good
= "Service cannot install system mounts",
791 .description_bad
= "Service may install system mounts",
792 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateMounts=",
795 .assess
= assess_bool
,
796 .offset
= offsetof(SecurityInfo
, private_mounts
),
799 .id
= "PrivateNetwork=",
800 .json_field
= "PrivateNetwork",
801 .description_good
= "Service has no access to the host's network",
802 .description_bad
= "Service has access to the host's network",
803 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateNetwork=",
806 .assess
= assess_bool
,
807 .offset
= offsetof(SecurityInfo
, private_network
),
811 .json_field
= "PrivateTmp",
812 .description_good
= "Service has no access to other software's temporary files",
813 .description_bad
= "Service has access to other software's temporary files",
814 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateTmp=",
817 .assess
= assess_bool
,
818 .offset
= offsetof(SecurityInfo
, private_tmp
),
819 .default_dependencies_only
= true,
822 .id
= "PrivateUsers=",
823 .json_field
= "PrivateUsers",
824 .description_good
= "Service does not have access to other users",
825 .description_bad
= "Service has access to other users",
826 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateUsers=",
829 .assess
= assess_bool
,
830 .offset
= offsetof(SecurityInfo
, private_users
),
833 .id
= "ProtectControlGroups=",
834 .json_field
= "ProtectControlGroups",
835 .description_good
= "Service cannot modify the control group file system",
836 .description_bad
= "Service may modify the control group file system",
837 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectControlGroups=",
840 .assess
= assess_bool
,
841 .offset
= offsetof(SecurityInfo
, protect_control_groups
),
844 .id
= "ProtectKernelModules=",
845 .json_field
= "ProtectKernelModules",
846 .description_good
= "Service cannot load or read kernel modules",
847 .description_bad
= "Service may load or read kernel modules",
848 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelModules=",
851 .assess
= assess_bool
,
852 .offset
= offsetof(SecurityInfo
, protect_kernel_modules
),
855 .id
= "ProtectKernelTunables=",
856 .json_field
= "ProtectKernelTunables",
857 .description_good
= "Service cannot alter kernel tunables (/proc/sys, …)",
858 .description_bad
= "Service may alter kernel tunables",
859 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelTunables=",
862 .assess
= assess_bool
,
863 .offset
= offsetof(SecurityInfo
, protect_kernel_tunables
),
866 .id
= "ProtectKernelLogs=",
867 .json_field
= "ProtectKernelLogs",
868 .description_good
= "Service cannot read from or write to the kernel log ring buffer",
869 .description_bad
= "Service may read from or write to the kernel log ring buffer",
870 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelLogs=",
873 .assess
= assess_bool
,
874 .offset
= offsetof(SecurityInfo
, protect_kernel_logs
),
877 .id
= "ProtectClock=",
878 .json_field
= "ProtectClock",
879 .description_good
= "Service cannot write to the hardware clock or system clock",
880 .description_bad
= "Service may write to the hardware clock or system clock",
881 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectClock=",
884 .assess
= assess_bool
,
885 .offset
= offsetof(SecurityInfo
, protect_clock
),
888 .id
= "ProtectHome=",
889 .json_field
= "ProtectHome",
890 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHome=",
893 .assess
= assess_protect_home
,
894 .default_dependencies_only
= true,
897 .id
= "ProtectHostname=",
898 .json_field
= "ProtectHostname",
899 .description_good
= "Service cannot change system host/domainname",
900 .description_bad
= "Service may change system host/domainname",
901 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHostname=",
904 .assess
= assess_bool
,
905 .offset
= offsetof(SecurityInfo
, protect_hostname
),
908 .id
= "ProtectSystem=",
909 .json_field
= "ProtectSystem",
910 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectSystem=",
913 .assess
= assess_protect_system
,
914 .default_dependencies_only
= true,
917 .id
= "RootDirectory=/RootImage=",
918 .json_field
= "RootDirectoryOrRootImage",
919 .description_good
= "Service has its own root directory/image",
920 .description_bad
= "Service runs within the host's root directory",
921 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RootDirectory=",
924 .assess
= assess_root_directory
,
925 .default_dependencies_only
= true,
928 .id
= "LockPersonality=",
929 .json_field
= "LockPersonality",
930 .description_good
= "Service cannot change ABI personality",
931 .description_bad
= "Service may change ABI personality",
932 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#LockPersonality=",
935 .assess
= assess_bool
,
936 .offset
= offsetof(SecurityInfo
, lock_personality
),
939 .id
= "MemoryDenyWriteExecute=",
940 .json_field
= "MemoryDenyWriteExecute",
941 .description_good
= "Service cannot create writable executable memory mappings",
942 .description_bad
= "Service may create writable executable memory mappings",
943 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#MemoryDenyWriteExecute=",
946 .assess
= assess_bool
,
947 .offset
= offsetof(SecurityInfo
, memory_deny_write_execute
),
950 .id
= "NoNewPrivileges=",
951 .json_field
= "NoNewPrivileges",
952 .description_good
= "Service processes cannot acquire new privileges",
953 .description_bad
= "Service processes may acquire new privileges",
954 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NoNewPrivileges=",
957 .assess
= assess_bool
,
958 .offset
= offsetof(SecurityInfo
, no_new_privileges
),
961 .id
= "CapabilityBoundingSet=~CAP_SYS_ADMIN",
962 .json_field
= "CapabilityBoundingSet_CAP_SYS_ADMIN",
963 .description_good
= "Service has no administrator privileges",
964 .description_bad
= "Service has administrator privileges",
965 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
968 .assess
= assess_capability_bounding_set
,
969 .parameter
= UINT64_C(1) << CAP_SYS_ADMIN
,
972 .id
= "CapabilityBoundingSet=~CAP_SET(UID|GID|PCAP)",
973 .json_field
= "CapabilityBoundingSet_CAP_SET_UID_GID_PCAP",
974 .description_good
= "Service cannot change UID/GID identities/capabilities",
975 .description_bad
= "Service may change UID/GID identities/capabilities",
976 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
979 .assess
= assess_capability_bounding_set
,
980 .parameter
= (UINT64_C(1) << CAP_SETUID
)|
981 (UINT64_C(1) << CAP_SETGID
)|
982 (UINT64_C(1) << CAP_SETPCAP
),
985 .id
= "CapabilityBoundingSet=~CAP_SYS_PTRACE",
986 .json_field
= "CapabilityBoundingSet_CAP_SYS_PTRACE",
987 .description_good
= "Service has no ptrace() debugging abilities",
988 .description_bad
= "Service has ptrace() debugging abilities",
989 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
992 .assess
= assess_capability_bounding_set
,
993 .parameter
= (UINT64_C(1) << CAP_SYS_PTRACE
),
996 .id
= "CapabilityBoundingSet=~CAP_SYS_TIME",
997 .json_field
= "CapabilityBoundingSet_CAP_SYS_TIME",
998 .description_good
= "Service processes cannot change the system clock",
999 .description_bad
= "Service processes may change the system clock",
1000 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1003 .assess
= assess_capability_bounding_set
,
1004 .parameter
= UINT64_C(1) << CAP_SYS_TIME
,
1007 .id
= "CapabilityBoundingSet=~CAP_NET_ADMIN",
1008 .json_field
= "CapabilityBoundingSet_CAP_NET_ADMIN",
1009 .description_good
= "Service has no network configuration privileges",
1010 .description_bad
= "Service has network configuration privileges",
1011 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1014 .assess
= assess_capability_bounding_set
,
1015 .parameter
= (UINT64_C(1) << CAP_NET_ADMIN
),
1018 .id
= "CapabilityBoundingSet=~CAP_SYS_RAWIO",
1019 .json_field
= "CapabilityBoundingSet_CAP_SYS_RAWIO",
1020 .description_good
= "Service has no raw I/O access",
1021 .description_bad
= "Service has raw I/O access",
1022 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1025 .assess
= assess_capability_bounding_set
,
1026 .parameter
= (UINT64_C(1) << CAP_SYS_RAWIO
),
1029 .id
= "CapabilityBoundingSet=~CAP_SYS_MODULE",
1030 .json_field
= "CapabilityBoundingSet_CAP_SYS_MODULE",
1031 .description_good
= "Service cannot load kernel modules",
1032 .description_bad
= "Service may load kernel modules",
1033 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1036 .assess
= assess_capability_bounding_set
,
1037 .parameter
= (UINT64_C(1) << CAP_SYS_MODULE
),
1040 .id
= "CapabilityBoundingSet=~CAP_AUDIT_*",
1041 .json_field
= "CapabilityBoundingSet_CAP_AUDIT",
1042 .description_good
= "Service has no audit subsystem access",
1043 .description_bad
= "Service has audit subsystem access",
1044 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1047 .assess
= assess_capability_bounding_set
,
1048 .parameter
= (UINT64_C(1) << CAP_AUDIT_CONTROL
) |
1049 (UINT64_C(1) << CAP_AUDIT_READ
) |
1050 (UINT64_C(1) << CAP_AUDIT_WRITE
),
1053 .id
= "CapabilityBoundingSet=~CAP_SYSLOG",
1054 .json_field
= "CapabilityBoundingSet_CAP_SYSLOG",
1055 .description_good
= "Service has no access to kernel logging",
1056 .description_bad
= "Service has access to kernel logging",
1057 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1060 .assess
= assess_capability_bounding_set
,
1061 .parameter
= (UINT64_C(1) << CAP_SYSLOG
),
1064 .id
= "CapabilityBoundingSet=~CAP_SYS_(NICE|RESOURCE)",
1065 .json_field
= "CapabilityBoundingSet_CAP_SYS_NICE_RESOURCE",
1066 .description_good
= "Service has no privileges to change resource use parameters",
1067 .description_bad
= "Service has privileges to change resource use parameters",
1068 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1071 .assess
= assess_capability_bounding_set
,
1072 .parameter
= (UINT64_C(1) << CAP_SYS_NICE
) |
1073 (UINT64_C(1) << CAP_SYS_RESOURCE
),
1076 .id
= "CapabilityBoundingSet=~CAP_MKNOD",
1077 .json_field
= "CapabilityBoundingSet_CAP_MKNOD",
1078 .description_good
= "Service cannot create device nodes",
1079 .description_bad
= "Service may create device nodes",
1080 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1083 .assess
= assess_capability_bounding_set
,
1084 .parameter
= (UINT64_C(1) << CAP_MKNOD
),
1087 .id
= "CapabilityBoundingSet=~CAP_(CHOWN|FSETID|SETFCAP)",
1088 .json_field
= "CapabilityBoundingSet_CAP_CHOWN_FSETID_SETFCAP",
1089 .description_good
= "Service cannot change file ownership/access mode/capabilities",
1090 .description_bad
= "Service may change file ownership/access mode/capabilities unrestricted",
1091 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1094 .assess
= assess_capability_bounding_set
,
1095 .parameter
= (UINT64_C(1) << CAP_CHOWN
) |
1096 (UINT64_C(1) << CAP_FSETID
) |
1097 (UINT64_C(1) << CAP_SETFCAP
),
1100 .id
= "CapabilityBoundingSet=~CAP_(DAC_*|FOWNER|IPC_OWNER)",
1101 .json_field
= "CapabilityBoundingSet_CAP_DAC_FOWNER_IPC_OWNER",
1102 .description_good
= "Service cannot override UNIX file/IPC permission checks",
1103 .description_bad
= "Service may override UNIX file/IPC permission checks",
1104 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1107 .assess
= assess_capability_bounding_set
,
1108 .parameter
= (UINT64_C(1) << CAP_DAC_OVERRIDE
) |
1109 (UINT64_C(1) << CAP_DAC_READ_SEARCH
) |
1110 (UINT64_C(1) << CAP_FOWNER
) |
1111 (UINT64_C(1) << CAP_IPC_OWNER
),
1114 .id
= "CapabilityBoundingSet=~CAP_KILL",
1115 .json_field
= "CapabilityBoundingSet_CAP_KILL",
1116 .description_good
= "Service cannot send UNIX signals to arbitrary processes",
1117 .description_bad
= "Service may send UNIX signals to arbitrary processes",
1118 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1121 .assess
= assess_capability_bounding_set
,
1122 .parameter
= (UINT64_C(1) << CAP_KILL
),
1125 .id
= "CapabilityBoundingSet=~CAP_NET_(BIND_SERVICE|BROADCAST|RAW)",
1126 .json_field
= "CapabilityBoundingSet_CAP_NET_BIND_SERVICE_BROADCAST_RAW)",
1127 .description_good
= "Service has no elevated networking privileges",
1128 .description_bad
= "Service has elevated networking privileges",
1129 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1132 .assess
= assess_capability_bounding_set
,
1133 .parameter
= (UINT64_C(1) << CAP_NET_BIND_SERVICE
) |
1134 (UINT64_C(1) << CAP_NET_BROADCAST
) |
1135 (UINT64_C(1) << CAP_NET_RAW
),
1138 .id
= "CapabilityBoundingSet=~CAP_SYS_BOOT",
1139 .json_field
= "CapabilityBoundingSet_CAP_SYS_BOOT",
1140 .description_good
= "Service cannot issue reboot()",
1141 .description_bad
= "Service may issue reboot()",
1142 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1145 .assess
= assess_capability_bounding_set
,
1146 .parameter
= (UINT64_C(1) << CAP_SYS_BOOT
),
1149 .id
= "CapabilityBoundingSet=~CAP_MAC_*",
1150 .json_field
= "CapabilityBoundingSet_CAP_MAC",
1151 .description_good
= "Service cannot adjust SMACK MAC",
1152 .description_bad
= "Service may adjust SMACK MAC",
1153 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1156 .assess
= assess_capability_bounding_set
,
1157 .parameter
= (UINT64_C(1) << CAP_MAC_ADMIN
)|
1158 (UINT64_C(1) << CAP_MAC_OVERRIDE
),
1161 .id
= "CapabilityBoundingSet=~CAP_LINUX_IMMUTABLE",
1162 .json_field
= "CapabilityBoundingSet_CAP_LINUX_IMMUTABLE",
1163 .description_good
= "Service cannot mark files immutable",
1164 .description_bad
= "Service may mark files immutable",
1165 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1168 .assess
= assess_capability_bounding_set
,
1169 .parameter
= (UINT64_C(1) << CAP_LINUX_IMMUTABLE
),
1172 .id
= "CapabilityBoundingSet=~CAP_IPC_LOCK",
1173 .json_field
= "CapabilityBoundingSet_CAP_IPC_LOCK",
1174 .description_good
= "Service cannot lock memory into RAM",
1175 .description_bad
= "Service may lock memory into RAM",
1176 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1179 .assess
= assess_capability_bounding_set
,
1180 .parameter
= (UINT64_C(1) << CAP_IPC_LOCK
),
1183 .id
= "CapabilityBoundingSet=~CAP_SYS_CHROOT",
1184 .json_field
= "CapabilityBoundingSet_CAP_SYS_CHROOT",
1185 .description_good
= "Service cannot issue chroot()",
1186 .description_bad
= "Service may issue chroot()",
1187 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1190 .assess
= assess_capability_bounding_set
,
1191 .parameter
= (UINT64_C(1) << CAP_SYS_CHROOT
),
1194 .id
= "CapabilityBoundingSet=~CAP_BLOCK_SUSPEND",
1195 .json_field
= "CapabilityBoundingSet_CAP_BLOCK_SUSPEND",
1196 .description_good
= "Service cannot establish wake locks",
1197 .description_bad
= "Service may establish wake locks",
1198 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1201 .assess
= assess_capability_bounding_set
,
1202 .parameter
= (UINT64_C(1) << CAP_BLOCK_SUSPEND
),
1205 .id
= "CapabilityBoundingSet=~CAP_WAKE_ALARM",
1206 .json_field
= "CapabilityBoundingSet_CAP_WAKE_ALARM",
1207 .description_good
= "Service cannot program timers that wake up the system",
1208 .description_bad
= "Service may program timers that wake up the system",
1209 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1212 .assess
= assess_capability_bounding_set
,
1213 .parameter
= (UINT64_C(1) << CAP_WAKE_ALARM
),
1216 .id
= "CapabilityBoundingSet=~CAP_LEASE",
1217 .json_field
= "CapabilityBoundingSet_CAP_LEASE",
1218 .description_good
= "Service cannot create file leases",
1219 .description_bad
= "Service may create file leases",
1220 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1223 .assess
= assess_capability_bounding_set
,
1224 .parameter
= (UINT64_C(1) << CAP_LEASE
),
1227 .id
= "CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG",
1228 .json_field
= "CapabilityBoundingSet_CAP_SYS_TTY_CONFIG",
1229 .description_good
= "Service cannot issue vhangup()",
1230 .description_bad
= "Service may issue vhangup()",
1231 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1234 .assess
= assess_capability_bounding_set
,
1235 .parameter
= (UINT64_C(1) << CAP_SYS_TTY_CONFIG
),
1238 .id
= "CapabilityBoundingSet=~CAP_SYS_PACCT",
1239 .json_field
= "CapabilityBoundingSet_CAP_SYS_PACCT",
1240 .description_good
= "Service cannot use acct()",
1241 .description_bad
= "Service may use acct()",
1242 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1245 .assess
= assess_capability_bounding_set
,
1246 .parameter
= (UINT64_C(1) << CAP_SYS_PACCT
),
1250 .json_field
= "UMask",
1251 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#UMask=",
1254 .assess
= assess_umask
,
1257 .id
= "KeyringMode=",
1258 .json_field
= "KeyringMode",
1259 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#KeyringMode=",
1260 .description_good
= "Service doesn't share key material with other services",
1261 .description_bad
= "Service shares key material with other service",
1264 .assess
= assess_keyring_mode
,
1267 .id
= "ProtectProc=",
1268 .json_field
= "ProtectProc",
1269 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectProc=",
1270 .description_good
= "Service has restricted access to process tree (/proc hidepid=)",
1271 .description_bad
= "Service has full access to process tree (/proc hidepid=)",
1274 .assess
= assess_protect_proc
,
1277 .id
= "ProcSubset=",
1278 .json_field
= "ProcSubset",
1279 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProcSubset=",
1280 .description_good
= "Service has no access to non-process /proc files (/proc subset=)",
1281 .description_bad
= "Service has full access to non-process /proc files (/proc subset=)",
1284 .assess
= assess_proc_subset
,
1287 .id
= "NotifyAccess=",
1288 .json_field
= "NotifyAccess",
1289 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NotifyAccess=",
1290 .description_good
= "Service child processes cannot alter service state",
1291 .description_bad
= "Service child processes may alter service state",
1294 .assess
= assess_notify_access
,
1298 .json_field
= "RemoveIPC",
1299 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RemoveIPC=",
1300 .description_good
= "Service user cannot leave SysV IPC objects around",
1301 .description_bad
= "Service user may leave SysV IPC objects around",
1302 .description_na
= "Service runs as root, option does not apply",
1305 .assess
= assess_remove_ipc
,
1306 .offset
= offsetof(SecurityInfo
, remove_ipc
),
1310 .json_field
= "Delegate",
1311 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Delegate=",
1312 .description_good
= "Service does not maintain its own delegated control group subtree",
1313 .description_bad
= "Service maintains its own delegated control group subtree",
1316 .assess
= assess_bool
,
1317 .offset
= offsetof(SecurityInfo
, delegate
),
1318 .parameter
= true, /* invert! */
1321 .id
= "RestrictRealtime=",
1322 .json_field
= "RestrictRealtime",
1323 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictRealtime=",
1324 .description_good
= "Service realtime scheduling access is restricted",
1325 .description_bad
= "Service may acquire realtime scheduling",
1328 .assess
= assess_bool
,
1329 .offset
= offsetof(SecurityInfo
, restrict_realtime
),
1332 .id
= "RestrictSUIDSGID=",
1333 .json_field
= "RestrictSUIDSGID",
1334 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictSUIDSGID=",
1335 .description_good
= "SUID/SGID file creation by service is restricted",
1336 .description_bad
= "Service may create SUID/SGID files",
1339 .assess
= assess_bool
,
1340 .offset
= offsetof(SecurityInfo
, restrict_suid_sgid
),
1343 .id
= "RestrictNamespaces=~user",
1344 .json_field
= "RestrictNamespaces_user",
1345 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1346 .description_good
= "Service cannot create user namespaces",
1347 .description_bad
= "Service may create user namespaces",
1350 .assess
= assess_restrict_namespaces
,
1351 .parameter
= CLONE_NEWUSER
,
1354 .id
= "RestrictNamespaces=~mnt",
1355 .json_field
= "RestrictNamespaces_mnt",
1356 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1357 .description_good
= "Service cannot create file system namespaces",
1358 .description_bad
= "Service may create file system namespaces",
1361 .assess
= assess_restrict_namespaces
,
1362 .parameter
= CLONE_NEWNS
,
1365 .id
= "RestrictNamespaces=~ipc",
1366 .json_field
= "RestrictNamespaces_ipc",
1367 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1368 .description_good
= "Service cannot create IPC namespaces",
1369 .description_bad
= "Service may create IPC namespaces",
1372 .assess
= assess_restrict_namespaces
,
1373 .parameter
= CLONE_NEWIPC
,
1376 .id
= "RestrictNamespaces=~pid",
1377 .json_field
= "RestrictNamespaces_pid",
1378 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1379 .description_good
= "Service cannot create process namespaces",
1380 .description_bad
= "Service may create process namespaces",
1383 .assess
= assess_restrict_namespaces
,
1384 .parameter
= CLONE_NEWPID
,
1387 .id
= "RestrictNamespaces=~cgroup",
1388 .json_field
= "RestrictNamespaces_cgroup",
1389 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1390 .description_good
= "Service cannot create cgroup namespaces",
1391 .description_bad
= "Service may create cgroup namespaces",
1394 .assess
= assess_restrict_namespaces
,
1395 .parameter
= CLONE_NEWCGROUP
,
1398 .id
= "RestrictNamespaces=~net",
1399 .json_field
= "RestrictNamespaces_net",
1400 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1401 .description_good
= "Service cannot create network namespaces",
1402 .description_bad
= "Service may create network namespaces",
1405 .assess
= assess_restrict_namespaces
,
1406 .parameter
= CLONE_NEWNET
,
1409 .id
= "RestrictNamespaces=~uts",
1410 .json_field
= "RestrictNamespaces_uts",
1411 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1412 .description_good
= "Service cannot create hostname namespaces",
1413 .description_bad
= "Service may create hostname namespaces",
1416 .assess
= assess_restrict_namespaces
,
1417 .parameter
= CLONE_NEWUTS
,
1420 .id
= "RestrictAddressFamilies=~AF_(INET|INET6)",
1421 .json_field
= "RestrictAddressFamilies_AF_INET_INET6",
1422 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1423 .description_good
= "Service cannot allocate Internet sockets",
1424 .description_bad
= "Service may allocate Internet sockets",
1427 .assess
= assess_bool
,
1428 .offset
= offsetof(SecurityInfo
, restrict_address_family_inet
),
1431 .id
= "RestrictAddressFamilies=~AF_UNIX",
1432 .json_field
= "RestrictAddressFamilies_AF_UNIX",
1433 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1434 .description_good
= "Service cannot allocate local sockets",
1435 .description_bad
= "Service may allocate local sockets",
1438 .assess
= assess_bool
,
1439 .offset
= offsetof(SecurityInfo
, restrict_address_family_unix
),
1442 .id
= "RestrictAddressFamilies=~AF_NETLINK",
1443 .json_field
= "RestrictAddressFamilies_AF_NETLINK",
1444 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1445 .description_good
= "Service cannot allocate netlink sockets",
1446 .description_bad
= "Service may allocate netlink sockets",
1449 .assess
= assess_bool
,
1450 .offset
= offsetof(SecurityInfo
, restrict_address_family_netlink
),
1453 .id
= "RestrictAddressFamilies=~AF_PACKET",
1454 .json_field
= "RestrictAddressFamilies_AF_PACKET",
1455 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1456 .description_good
= "Service cannot allocate packet sockets",
1457 .description_bad
= "Service may allocate packet sockets",
1460 .assess
= assess_bool
,
1461 .offset
= offsetof(SecurityInfo
, restrict_address_family_packet
),
1464 .id
= "RestrictAddressFamilies=~…",
1465 .json_field
= "RestrictAddressFamilies_OTHER",
1466 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1467 .description_good
= "Service cannot allocate exotic sockets",
1468 .description_bad
= "Service may allocate exotic sockets",
1471 .assess
= assess_bool
,
1472 .offset
= offsetof(SecurityInfo
, restrict_address_family_other
),
1475 .id
= "SystemCallArchitectures=",
1476 .json_field
= "SystemCallArchitectures",
1477 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallArchitectures=",
1480 .assess
= assess_system_call_architectures
,
1484 .id
= "SystemCallFilter=~@swap",
1485 .json_field
= "SystemCallFilter_swap",
1486 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1489 .assess
= assess_system_call_filter
,
1490 .parameter
= SYSCALL_FILTER_SET_SWAP
,
1493 .id
= "SystemCallFilter=~@obsolete",
1494 .json_field
= "SystemCallFilter_obsolete",
1495 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1498 .assess
= assess_system_call_filter
,
1499 .parameter
= SYSCALL_FILTER_SET_OBSOLETE
,
1502 .id
= "SystemCallFilter=~@clock",
1503 .json_field
= "SystemCallFilter_clock",
1504 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1507 .assess
= assess_system_call_filter
,
1508 .parameter
= SYSCALL_FILTER_SET_CLOCK
,
1511 .id
= "SystemCallFilter=~@cpu-emulation",
1512 .json_field
= "SystemCallFilter_cpu_emulation",
1513 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1516 .assess
= assess_system_call_filter
,
1517 .parameter
= SYSCALL_FILTER_SET_CPU_EMULATION
,
1520 .id
= "SystemCallFilter=~@debug",
1521 .json_field
= "SystemCallFilter_debug",
1522 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1525 .assess
= assess_system_call_filter
,
1526 .parameter
= SYSCALL_FILTER_SET_DEBUG
,
1529 .id
= "SystemCallFilter=~@mount",
1530 .json_field
= "SystemCallFilter_mount",
1531 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1534 .assess
= assess_system_call_filter
,
1535 .parameter
= SYSCALL_FILTER_SET_MOUNT
,
1538 .id
= "SystemCallFilter=~@module",
1539 .json_field
= "SystemCallFilter_module",
1540 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1543 .assess
= assess_system_call_filter
,
1544 .parameter
= SYSCALL_FILTER_SET_MODULE
,
1547 .id
= "SystemCallFilter=~@raw-io",
1548 .json_field
= "SystemCallFilter_raw_io",
1549 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1552 .assess
= assess_system_call_filter
,
1553 .parameter
= SYSCALL_FILTER_SET_RAW_IO
,
1556 .id
= "SystemCallFilter=~@reboot",
1557 .json_field
= "SystemCallFilter_reboot",
1558 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1561 .assess
= assess_system_call_filter
,
1562 .parameter
= SYSCALL_FILTER_SET_REBOOT
,
1565 .id
= "SystemCallFilter=~@privileged",
1566 .json_field
= "SystemCallFilter_privileged",
1567 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1570 .assess
= assess_system_call_filter
,
1571 .parameter
= SYSCALL_FILTER_SET_PRIVILEGED
,
1574 .id
= "SystemCallFilter=~@resources",
1575 .json_field
= "SystemCallFilter_resources",
1576 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1579 .assess
= assess_system_call_filter
,
1580 .parameter
= SYSCALL_FILTER_SET_RESOURCES
,
1584 .id
= "IPAddressDeny=",
1585 .json_field
= "IPAddressDeny",
1586 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#IPAddressDeny=",
1589 .assess
= assess_ip_address_allow
,
1592 .id
= "DeviceAllow=",
1593 .json_field
= "DeviceAllow",
1594 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#DeviceAllow=",
1597 .assess
= assess_device_allow
,
1600 .id
= "AmbientCapabilities=",
1601 .json_field
= "AmbientCapabilities",
1602 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#AmbientCapabilities=",
1603 .description_good
= "Service process does not receive ambient capabilities",
1604 .description_bad
= "Service process receives ambient capabilities",
1607 .assess
= assess_ambient_capabilities
,
1611 static JsonVariant
* security_assessor_find_in_policy(const struct security_assessor
*a
, JsonVariant
*policy
, const char *name
) {
1617 if (!json_variant_is_object(policy
)) {
1618 log_debug("Specified policy is not a JSON object, ignoring.");
1622 item
= json_variant_by_key(policy
, a
->json_field
);
1625 if (!json_variant_is_object(item
)) {
1626 log_debug("Item for '%s' in policy JSON object is not an object, ignoring.", a
->id
);
1630 return name
? json_variant_by_key(item
, name
) : item
;
1633 static uint64_t access_weight(const struct security_assessor
*a
, JsonVariant
*policy
) {
1638 val
= security_assessor_find_in_policy(a
, policy
, "weight");
1640 if (json_variant_is_unsigned(val
))
1641 return json_variant_unsigned(val
);
1642 log_debug("JSON field 'weight' of policy for %s is not an unsigned integer, ignoring.", a
->id
);
1648 static uint64_t access_range(const struct security_assessor
*a
, JsonVariant
*policy
) {
1653 val
= security_assessor_find_in_policy(a
, policy
, "range");
1655 if (json_variant_is_unsigned(val
))
1656 return json_variant_unsigned(val
);
1657 log_debug("JSON field 'range' of policy for %s is not an unsigned integer, ignoring.", a
->id
);
1663 static const char *access_description_na(const struct security_assessor
*a
, JsonVariant
*policy
) {
1668 val
= security_assessor_find_in_policy(a
, policy
, "description_na");
1670 if (json_variant_is_string(val
))
1671 return json_variant_string(val
);
1672 log_debug("JSON field 'description_na' of policy for %s is not a string, ignoring.", a
->id
);
1675 return a
->description_na
;
1678 static const char *access_description_good(const struct security_assessor
*a
, JsonVariant
*policy
) {
1683 val
= security_assessor_find_in_policy(a
, policy
, "description_good");
1685 if (json_variant_is_string(val
))
1686 return json_variant_string(val
);
1687 log_debug("JSON field 'description_good' of policy for %s is not a string, ignoring.", a
->id
);
1690 return a
->description_good
;
1693 static const char *access_description_bad(const struct security_assessor
*a
, JsonVariant
*policy
) {
1698 val
= security_assessor_find_in_policy(a
, policy
, "description_bad");
1700 if (json_variant_is_string(val
))
1701 return json_variant_string(val
);
1702 log_debug("JSON field 'description_bad' of policy for %s is not a string, ignoring.", a
->id
);
1705 return a
->description_bad
;
1708 static int assess(const SecurityInfo
*info
,
1709 Table
*overview_table
,
1710 AnalyzeSecurityFlags flags
,
1712 JsonVariant
*policy
,
1713 PagerFlags pager_flags
,
1714 JsonFormatFlags json_format_flags
) {
1716 static const struct {
1720 SpecialGlyph smiley
;
1721 } badness_table
[] = {
1722 { 100, "DANGEROUS", ANSI_HIGHLIGHT_RED
, SPECIAL_GLYPH_DEPRESSED_SMILEY
},
1723 { 90, "UNSAFE", ANSI_HIGHLIGHT_RED
, SPECIAL_GLYPH_UNHAPPY_SMILEY
},
1724 { 75, "EXPOSED", ANSI_HIGHLIGHT_YELLOW
, SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY
},
1725 { 50, "MEDIUM", NULL
, SPECIAL_GLYPH_NEUTRAL_SMILEY
},
1726 { 10, "OK", ANSI_HIGHLIGHT_GREEN
, SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY
},
1727 { 1, "SAFE", ANSI_HIGHLIGHT_GREEN
, SPECIAL_GLYPH_HAPPY_SMILEY
},
1728 { 0, "PERFECT", ANSI_HIGHLIGHT_GREEN
, SPECIAL_GLYPH_ECSTATIC_SMILEY
},
1731 uint64_t badness_sum
= 0, weight_sum
= 0, exposure
;
1732 _cleanup_(table_unrefp
) Table
*details_table
= NULL
;
1736 if (!FLAGS_SET(flags
, ANALYZE_SECURITY_SHORT
)) {
1737 details_table
= table_new(" ", "name", "json_field", "description", "weight", "badness", "range", "exposure");
1741 r
= table_set_json_field_name(details_table
, 0, "set");
1743 return log_error_errno(r
, "Failed to set JSON field name of column 0: %m");
1745 (void) table_set_sort(details_table
, (size_t) 3, (size_t) 1);
1746 (void) table_set_reverse(details_table
, 3, true);
1748 if (getenv_bool("SYSTEMD_ANALYZE_DEBUG") <= 0)
1749 (void) table_set_display(details_table
, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 7);
1752 for (i
= 0; i
< ELEMENTSOF(security_assessor_table
); i
++) {
1753 const struct security_assessor
*a
= security_assessor_table
+ i
;
1754 _cleanup_free_
char *d
= NULL
;
1757 uint64_t weight
= access_weight(a
, policy
);
1758 uint64_t range
= access_range(a
, policy
);
1760 data
= (uint8_t *) info
+ a
->offset
;
1762 if (a
->default_dependencies_only
&& !info
->default_dependencies
) {
1763 badness
= UINT64_MAX
;
1764 d
= strdup("Service runs in special boot phase, option is not appropriate");
1768 r
= a
->assess(a
, info
, data
, &badness
, &d
);
1775 if (badness
!= UINT64_MAX
) {
1776 assert(badness
<= range
);
1778 badness_sum
+= DIV_ROUND_UP(badness
* weight
, range
);
1779 weight_sum
+= weight
;
1782 if (details_table
) {
1783 const char *description
, *color
= NULL
;
1786 if (badness
== UINT64_MAX
) {
1788 description
= access_description_na(a
, policy
);
1790 } else if (badness
== a
->range
) {
1792 description
= access_description_bad(a
, policy
);
1793 color
= ansi_highlight_red();
1794 } else if (badness
== 0) {
1796 description
= access_description_good(a
, policy
);
1797 color
= ansi_highlight_green();
1801 color
= ansi_highlight_red();
1807 if (checkmark
< 0) {
1808 r
= table_add_many(details_table
, TABLE_EMPTY
);
1810 return table_log_add_error(r
);
1812 r
= table_add_many(details_table
,
1813 TABLE_BOOLEAN_CHECKMARK
, checkmark
> 0,
1814 TABLE_SET_MINIMUM_WIDTH
, 1,
1815 TABLE_SET_MAXIMUM_WIDTH
, 1,
1816 TABLE_SET_ELLIPSIZE_PERCENT
, 0,
1817 TABLE_SET_COLOR
, color
);
1819 return table_log_add_error(r
);
1822 r
= table_add_many(details_table
,
1823 TABLE_STRING
, a
->id
, TABLE_SET_URL
, a
->url
,
1824 TABLE_STRING
, a
->json_field
,
1825 TABLE_STRING
, description
,
1826 TABLE_UINT64
, weight
, TABLE_SET_ALIGN_PERCENT
, 100,
1827 TABLE_UINT64
, badness
, TABLE_SET_ALIGN_PERCENT
, 100,
1828 TABLE_UINT64
, range
, TABLE_SET_ALIGN_PERCENT
, 100,
1829 TABLE_EMPTY
, TABLE_SET_ALIGN_PERCENT
, 100);
1831 return table_log_add_error(r
);
1835 assert(weight_sum
> 0);
1837 if (details_table
) {
1840 for (row
= 1; row
< table_get_rows(details_table
); row
++) {
1841 char buf
[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1];
1842 const uint64_t *weight
, *badness
, *range
;
1846 assert_se(weight
= table_get_at(details_table
, row
, 4));
1847 assert_se(badness
= table_get_at(details_table
, row
, 5));
1848 assert_se(range
= table_get_at(details_table
, row
, 6));
1850 if (*badness
== UINT64_MAX
|| *badness
== 0)
1853 assert_se(cell
= table_get_cell(details_table
, row
, 7));
1855 x
= DIV_ROUND_UP(DIV_ROUND_UP(*badness
* *weight
* 100U, *range
), weight_sum
);
1856 xsprintf(buf
, "%" PRIu64
".%" PRIu64
, x
/ 10, x
% 10);
1858 r
= table_update(details_table
, cell
, TABLE_STRING
, buf
);
1860 return log_error_errno(r
, "Failed to update cell in table: %m");
1863 if (json_format_flags
& JSON_FORMAT_OFF
) {
1864 r
= table_hide_column_from_display(details_table
, (size_t) 2);
1866 return log_error_errno(r
, "Failed to set columns to display: %m");
1869 r
= table_print_with_pager(details_table
, json_format_flags
, pager_flags
, /* show_header= */true);
1871 return log_error_errno(r
, "Failed to output table: %m");
1874 exposure
= DIV_ROUND_UP(badness_sum
* 100U, weight_sum
);
1876 for (i
= 0; i
< ELEMENTSOF(badness_table
); i
++)
1877 if (exposure
>= badness_table
[i
].exposure
)
1880 assert(i
< ELEMENTSOF(badness_table
));
1882 if (details_table
&& (json_format_flags
& JSON_FORMAT_OFF
)) {
1883 _cleanup_free_
char *clickable
= NULL
;
1886 /* If we shall output the details table, also print the brief summary underneath */
1888 if (info
->fragment_path
) {
1889 r
= terminal_urlify_path(info
->fragment_path
, info
->id
, &clickable
);
1897 printf("\n%s %sOverall exposure level for %s%s: %s%" PRIu64
".%" PRIu64
" %s%s %s\n",
1898 special_glyph(SPECIAL_GLYPH_ARROW
),
1902 colors_enabled() ? strempty(badness_table
[i
].color
) : "",
1903 exposure
/ 10, exposure
% 10,
1904 badness_table
[i
].name
,
1906 special_glyph(badness_table
[i
].smiley
));
1911 if (overview_table
) {
1912 char buf
[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1];
1913 _cleanup_free_
char *url
= NULL
;
1915 if (info
->fragment_path
) {
1916 r
= file_url_from_path(info
->fragment_path
, &url
);
1918 return log_error_errno(r
, "Failed to generate URL from path: %m");
1921 xsprintf(buf
, "%" PRIu64
".%" PRIu64
, exposure
/ 10, exposure
% 10);
1923 r
= table_add_many(overview_table
,
1924 TABLE_STRING
, info
->id
,
1927 TABLE_SET_ALIGN_PERCENT
, 100,
1928 TABLE_STRING
, badness_table
[i
].name
,
1929 TABLE_SET_COLOR
, strempty(badness_table
[i
].color
),
1930 TABLE_STRING
, special_glyph(badness_table
[i
].smiley
));
1932 return table_log_add_error(r
);
1935 /* Return error when overall exposure level is over threshold */
1936 if (exposure
> threshold
)
1942 static int property_read_restrict_namespaces(
1946 sd_bus_error
*error
,
1949 SecurityInfo
*info
= userdata
;
1951 uint64_t namespaces
;
1958 r
= sd_bus_message_read(m
, "t", &namespaces
);
1962 info
->restrict_namespaces
= (unsigned long long) namespaces
;
1967 static int property_read_umask(
1971 sd_bus_error
*error
,
1974 SecurityInfo
*info
= userdata
;
1983 r
= sd_bus_message_read(m
, "u", &umask
);
1987 info
->_umask
= (mode_t
) umask
;
1992 static int property_read_restrict_address_families(
1996 sd_bus_error
*error
,
1999 SecurityInfo
*info
= userdata
;
2006 r
= sd_bus_message_enter_container(m
, 'r', "bas");
2010 r
= sd_bus_message_read(m
, "b", &allow_list
);
2014 info
->restrict_address_family_inet
=
2015 info
->restrict_address_family_unix
=
2016 info
->restrict_address_family_netlink
=
2017 info
->restrict_address_family_packet
=
2018 info
->restrict_address_family_other
= allow_list
;
2020 r
= sd_bus_message_enter_container(m
, 'a', "s");
2027 r
= sd_bus_message_read(m
, "s", &name
);
2033 if (STR_IN_SET(name
, "AF_INET", "AF_INET6"))
2034 info
->restrict_address_family_inet
= !allow_list
;
2035 else if (streq(name
, "AF_UNIX"))
2036 info
->restrict_address_family_unix
= !allow_list
;
2037 else if (streq(name
, "AF_NETLINK"))
2038 info
->restrict_address_family_netlink
= !allow_list
;
2039 else if (streq(name
, "AF_PACKET"))
2040 info
->restrict_address_family_packet
= !allow_list
;
2042 info
->restrict_address_family_other
= !allow_list
;
2045 r
= sd_bus_message_exit_container(m
);
2049 return sd_bus_message_exit_container(m
);
2052 static int property_read_syscall_archs(
2056 sd_bus_error
*error
,
2059 SecurityInfo
*info
= userdata
;
2067 r
= sd_bus_message_enter_container(m
, 'a', "s");
2074 r
= sd_bus_message_read(m
, "s", &name
);
2080 r
= set_put_strdup(&info
->system_call_architectures
, name
);
2085 return sd_bus_message_exit_container(m
);
2088 static int property_read_system_call_filter(
2092 sd_bus_error
*error
,
2095 SecurityInfo
*info
= userdata
;
2102 r
= sd_bus_message_enter_container(m
, 'r', "bas");
2106 r
= sd_bus_message_read(m
, "b", &allow_list
);
2110 info
->system_call_filter_allow_list
= allow_list
;
2112 r
= sd_bus_message_enter_container(m
, 'a', "s");
2119 r
= sd_bus_message_read(m
, "s", &name
);
2125 /* The actual ExecContext stores the system call id as the map value, which we don't
2126 * need. So we assign NULL to all values here. */
2127 r
= hashmap_put_strdup(&info
->system_call_filter
, name
, NULL
);
2132 r
= sd_bus_message_exit_container(m
);
2136 return sd_bus_message_exit_container(m
);
2139 static int property_read_ip_address_allow(
2143 sd_bus_error
*error
,
2146 SecurityInfo
*info
= userdata
;
2147 bool deny_ipv4
= false, deny_ipv6
= false;
2154 r
= sd_bus_message_enter_container(m
, 'a', "(iayu)");
2164 r
= sd_bus_message_enter_container(m
, 'r', "iayu");
2170 r
= sd_bus_message_read(m
, "i", &family
);
2174 r
= sd_bus_message_read_array(m
, 'y', &data
, &size
);
2178 r
= sd_bus_message_read(m
, "u", &prefixlen
);
2182 r
= sd_bus_message_exit_container(m
);
2186 if (streq(member
, "IPAddressAllow")) {
2187 union in_addr_union u
;
2189 if (family
== AF_INET
&& size
== 4 && prefixlen
== 8)
2190 memcpy(&u
.in
, data
, size
);
2191 else if (family
== AF_INET6
&& size
== 16 && prefixlen
== 128)
2192 memcpy(&u
.in6
, data
, size
);
2194 info
->ip_address_allow_other
= true;
2198 if (in_addr_is_localhost(family
, &u
))
2199 info
->ip_address_allow_localhost
= true;
2201 info
->ip_address_allow_other
= true;
2203 assert(streq(member
, "IPAddressDeny"));
2205 if (family
== AF_INET
&& size
== 4 && prefixlen
== 0)
2207 else if (family
== AF_INET6
&& size
== 16 && prefixlen
== 0)
2212 info
->ip_address_deny_all
= deny_ipv4
&& deny_ipv6
;
2214 return sd_bus_message_exit_container(m
);
2217 static int property_read_ip_filters(
2221 sd_bus_error
*error
,
2224 SecurityInfo
*info
= userdata
;
2225 _cleanup_(strv_freep
) char **l
= NULL
;
2232 r
= sd_bus_message_read_strv(m
, &l
);
2236 if (streq(member
, "IPIngressFilterPath"))
2237 info
->ip_filters_custom_ingress
= !strv_isempty(l
);
2238 else if (streq(member
, "IPEgressFilterPath"))
2239 info
->ip_filters_custom_egress
= !strv_isempty(l
);
2244 static int property_read_device_allow(
2248 sd_bus_error
*error
,
2251 SecurityInfo
*info
= userdata
;
2259 r
= sd_bus_message_enter_container(m
, 'a', "(ss)");
2264 const char *name
, *policy
;
2266 r
= sd_bus_message_read(m
, "(ss)", &name
, &policy
);
2275 info
->device_allow_non_empty
= n
> 0;
2277 return sd_bus_message_exit_container(m
);
2280 static int acquire_security_info(sd_bus
*bus
, const char *name
, SecurityInfo
*info
, AnalyzeSecurityFlags flags
) {
2282 static const struct bus_properties_map security_map
[] = {
2283 { "AmbientCapabilities", "t", NULL
, offsetof(SecurityInfo
, ambient_capabilities
) },
2284 { "CapabilityBoundingSet", "t", NULL
, offsetof(SecurityInfo
, capability_bounding_set
) },
2285 { "DefaultDependencies", "b", NULL
, offsetof(SecurityInfo
, default_dependencies
) },
2286 { "Delegate", "b", NULL
, offsetof(SecurityInfo
, delegate
) },
2287 { "DeviceAllow", "a(ss)", property_read_device_allow
, 0 },
2288 { "DevicePolicy", "s", NULL
, offsetof(SecurityInfo
, device_policy
) },
2289 { "DynamicUser", "b", NULL
, offsetof(SecurityInfo
, dynamic_user
) },
2290 { "FragmentPath", "s", NULL
, offsetof(SecurityInfo
, fragment_path
) },
2291 { "IPAddressAllow", "a(iayu)", property_read_ip_address_allow
, 0 },
2292 { "IPAddressDeny", "a(iayu)", property_read_ip_address_allow
, 0 },
2293 { "IPIngressFilterPath", "as", property_read_ip_filters
, 0 },
2294 { "IPEgressFilterPath", "as", property_read_ip_filters
, 0 },
2295 { "Id", "s", NULL
, offsetof(SecurityInfo
, id
) },
2296 { "KeyringMode", "s", NULL
, offsetof(SecurityInfo
, keyring_mode
) },
2297 { "ProtectProc", "s", NULL
, offsetof(SecurityInfo
, protect_proc
) },
2298 { "ProcSubset", "s", NULL
, offsetof(SecurityInfo
, proc_subset
) },
2299 { "LoadState", "s", NULL
, offsetof(SecurityInfo
, load_state
) },
2300 { "LockPersonality", "b", NULL
, offsetof(SecurityInfo
, lock_personality
) },
2301 { "MemoryDenyWriteExecute", "b", NULL
, offsetof(SecurityInfo
, memory_deny_write_execute
) },
2302 { "NoNewPrivileges", "b", NULL
, offsetof(SecurityInfo
, no_new_privileges
) },
2303 { "NotifyAccess", "s", NULL
, offsetof(SecurityInfo
, notify_access
) },
2304 { "PrivateDevices", "b", NULL
, offsetof(SecurityInfo
, private_devices
) },
2305 { "PrivateMounts", "b", NULL
, offsetof(SecurityInfo
, private_mounts
) },
2306 { "PrivateNetwork", "b", NULL
, offsetof(SecurityInfo
, private_network
) },
2307 { "PrivateTmp", "b", NULL
, offsetof(SecurityInfo
, private_tmp
) },
2308 { "PrivateUsers", "b", NULL
, offsetof(SecurityInfo
, private_users
) },
2309 { "ProtectControlGroups", "b", NULL
, offsetof(SecurityInfo
, protect_control_groups
) },
2310 { "ProtectHome", "s", NULL
, offsetof(SecurityInfo
, protect_home
) },
2311 { "ProtectHostname", "b", NULL
, offsetof(SecurityInfo
, protect_hostname
) },
2312 { "ProtectKernelModules", "b", NULL
, offsetof(SecurityInfo
, protect_kernel_modules
) },
2313 { "ProtectKernelTunables", "b", NULL
, offsetof(SecurityInfo
, protect_kernel_tunables
) },
2314 { "ProtectKernelLogs", "b", NULL
, offsetof(SecurityInfo
, protect_kernel_logs
) },
2315 { "ProtectClock", "b", NULL
, offsetof(SecurityInfo
, protect_clock
) },
2316 { "ProtectSystem", "s", NULL
, offsetof(SecurityInfo
, protect_system
) },
2317 { "RemoveIPC", "b", NULL
, offsetof(SecurityInfo
, remove_ipc
) },
2318 { "RestrictAddressFamilies", "(bas)", property_read_restrict_address_families
, 0 },
2319 { "RestrictNamespaces", "t", property_read_restrict_namespaces
, 0 },
2320 { "RestrictRealtime", "b", NULL
, offsetof(SecurityInfo
, restrict_realtime
) },
2321 { "RestrictSUIDSGID", "b", NULL
, offsetof(SecurityInfo
, restrict_suid_sgid
) },
2322 { "RootDirectory", "s", NULL
, offsetof(SecurityInfo
, root_directory
) },
2323 { "RootImage", "s", NULL
, offsetof(SecurityInfo
, root_image
) },
2324 { "SupplementaryGroups", "as", NULL
, offsetof(SecurityInfo
, supplementary_groups
) },
2325 { "SystemCallArchitectures", "as", property_read_syscall_archs
, 0 },
2326 { "SystemCallFilter", "(as)", property_read_system_call_filter
, 0 },
2327 { "Type", "s", NULL
, offsetof(SecurityInfo
, type
) },
2328 { "UMask", "u", property_read_umask
, 0 },
2329 { "User", "s", NULL
, offsetof(SecurityInfo
, user
) },
2333 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2334 _cleanup_free_
char *path
= NULL
;
2337 /* Note: this mangles *info on failure! */
2343 path
= unit_dbus_path_from_name(name
);
2347 r
= bus_map_all_properties(
2349 "org.freedesktop.systemd1",
2352 BUS_MAP_STRDUP
| BUS_MAP_BOOLEAN_AS_BOOL
,
2357 return log_error_errno(r
, "Failed to get unit properties: %s", bus_error_message(&error
, r
));
2359 if (!streq_ptr(info
->load_state
, "loaded")) {
2361 if (FLAGS_SET(flags
, ANALYZE_SECURITY_ONLY_LOADED
))
2362 return -EMEDIUMTYPE
;
2364 if (streq_ptr(info
->load_state
, "not-found"))
2365 log_error("Unit %s not found, cannot analyze.", name
);
2366 else if (streq_ptr(info
->load_state
, "masked"))
2367 log_error("Unit %s is masked, cannot analyze.", name
);
2369 log_error("Unit %s not loaded properly, cannot analyze.", name
);
2374 if (FLAGS_SET(flags
, ANALYZE_SECURITY_ONLY_LONG_RUNNING
) && streq_ptr(info
->type
, "oneshot"))
2375 return -EMEDIUMTYPE
;
2377 if (info
->private_devices
||
2378 info
->private_tmp
||
2379 info
->protect_control_groups
||
2380 info
->protect_kernel_tunables
||
2381 info
->protect_kernel_modules
||
2382 !streq_ptr(info
->protect_home
, "no") ||
2383 !streq_ptr(info
->protect_system
, "no") ||
2385 info
->private_mounts
= true;
2387 if (info
->protect_kernel_modules
)
2388 info
->capability_bounding_set
&= ~(UINT64_C(1) << CAP_SYS_MODULE
);
2390 if (info
->protect_kernel_logs
)
2391 info
->capability_bounding_set
&= ~(UINT64_C(1) << CAP_SYSLOG
);
2393 if (info
->protect_clock
)
2394 info
->capability_bounding_set
&= ~((UINT64_C(1) << CAP_SYS_TIME
) |
2395 (UINT64_C(1) << CAP_WAKE_ALARM
));
2397 if (info
->private_devices
)
2398 info
->capability_bounding_set
&= ~((UINT64_C(1) << CAP_MKNOD
) |
2399 (UINT64_C(1) << CAP_SYS_RAWIO
));
2404 static int analyze_security_one(sd_bus
*bus
,
2406 Table
*overview_table
,
2407 AnalyzeSecurityFlags flags
,
2409 JsonVariant
*policy
,
2410 PagerFlags pager_flags
,
2411 JsonFormatFlags json_format_flags
) {
2413 _cleanup_(security_info_freep
) SecurityInfo
*info
= security_info_new();
2422 r
= acquire_security_info(bus
, name
, info
, flags
);
2423 if (r
== -EMEDIUMTYPE
) /* Ignore this one because not loaded or Type is oneshot */
2428 r
= assess(info
, overview_table
, flags
, threshold
, policy
, pager_flags
, json_format_flags
);
2435 /* Refactoring SecurityInfo so that it can make use of existing struct variables instead of reading from dbus */
2436 static int get_security_info(Unit
*u
, ExecContext
*c
, CGroupContext
*g
, SecurityInfo
**ret_info
) {
2439 _cleanup_(security_info_freep
) SecurityInfo
*info
= security_info_new();
2445 info
->id
= strdup(u
->id
);
2449 if (unit_type_to_string(u
->type
)) {
2450 info
->type
= strdup(unit_type_to_string(u
->type
));
2454 if (unit_load_state_to_string(u
->load_state
)) {
2455 info
->load_state
= strdup(unit_load_state_to_string(u
->load_state
));
2456 if (!info
->load_state
)
2459 if (u
->fragment_path
) {
2460 info
->fragment_path
= strdup(u
->fragment_path
);
2461 if (!info
->fragment_path
)
2464 info
->default_dependencies
= u
->default_dependencies
;
2465 if (u
->type
== UNIT_SERVICE
&& notify_access_to_string(SERVICE(u
)->notify_access
)) {
2466 info
->notify_access
= strdup(notify_access_to_string(SERVICE(u
)->notify_access
));
2467 if (!info
->notify_access
)
2473 info
->ambient_capabilities
= c
->capability_ambient_set
;
2474 info
->capability_bounding_set
= c
->capability_bounding_set
;
2476 info
->user
= strdup(c
->user
);
2480 if (c
->supplementary_groups
) {
2481 info
->supplementary_groups
= strv_copy(c
->supplementary_groups
);
2482 if (!info
->supplementary_groups
)
2485 info
->dynamic_user
= c
->dynamic_user
;
2486 if (exec_keyring_mode_to_string(c
->keyring_mode
)) {
2487 info
->keyring_mode
= strdup(exec_keyring_mode_to_string(c
->keyring_mode
));
2488 if (!info
->keyring_mode
)
2491 if (protect_proc_to_string(c
->protect_proc
)) {
2492 info
->protect_proc
= strdup(protect_proc_to_string(c
->protect_proc
));
2493 if (!info
->protect_proc
)
2496 if (proc_subset_to_string(c
->proc_subset
)) {
2497 info
->proc_subset
= strdup(proc_subset_to_string(c
->proc_subset
));
2498 if (!info
->proc_subset
)
2501 info
->lock_personality
= c
->lock_personality
;
2502 info
->memory_deny_write_execute
= c
->memory_deny_write_execute
;
2503 info
->no_new_privileges
= c
->no_new_privileges
;
2504 info
->protect_hostname
= c
->protect_hostname
;
2505 info
->private_devices
= c
->private_devices
;
2506 info
->private_mounts
= c
->private_mounts
;
2507 info
->private_network
= c
->private_network
;
2508 info
->private_tmp
= c
->private_tmp
;
2509 info
->private_users
= c
->private_users
;
2510 info
->protect_control_groups
= c
->protect_control_groups
;
2511 info
->protect_kernel_modules
= c
->protect_kernel_modules
;
2512 info
->protect_kernel_tunables
= c
->protect_kernel_tunables
;
2513 info
->protect_kernel_logs
= c
->protect_kernel_logs
;
2514 info
->protect_clock
= c
->protect_clock
;
2515 if (protect_home_to_string(c
->protect_home
)) {
2516 info
->protect_home
= strdup(protect_home_to_string(c
->protect_home
));
2517 if (!info
->protect_home
)
2520 if (protect_system_to_string(c
->protect_system
)) {
2521 info
->protect_system
= strdup(protect_system_to_string(c
->protect_system
));
2522 if (!info
->protect_system
)
2525 info
->remove_ipc
= c
->remove_ipc
;
2526 info
->restrict_address_family_inet
=
2527 info
->restrict_address_family_unix
=
2528 info
->restrict_address_family_netlink
=
2529 info
->restrict_address_family_packet
=
2530 info
->restrict_address_family_other
=
2531 c
->address_families_allow_list
;
2534 SET_FOREACH(key
, c
->address_families
) {
2535 int family
= PTR_TO_INT(key
);
2538 if (IN_SET(family
, AF_INET
, AF_INET6
))
2539 info
->restrict_address_family_inet
= !c
->address_families_allow_list
;
2540 else if (family
== AF_UNIX
)
2541 info
->restrict_address_family_unix
= !c
->address_families_allow_list
;
2542 else if (family
== AF_NETLINK
)
2543 info
->restrict_address_family_netlink
= !c
->address_families_allow_list
;
2544 else if (family
== AF_PACKET
)
2545 info
->restrict_address_family_packet
= !c
->address_families_allow_list
;
2547 info
->restrict_address_family_other
= !c
->address_families_allow_list
;
2550 info
->restrict_namespaces
= c
->restrict_namespaces
;
2551 info
->restrict_realtime
= c
->restrict_realtime
;
2552 info
->restrict_suid_sgid
= c
->restrict_suid_sgid
;
2553 if (c
->root_directory
) {
2554 info
->root_directory
= strdup(c
->root_directory
);
2555 if (!info
->root_directory
)
2558 if (c
->root_image
) {
2559 info
->root_image
= strdup(c
->root_image
);
2560 if (!info
->root_image
)
2563 info
->_umask
= c
->umask
;
2564 if (c
->syscall_archs
) {
2565 info
->system_call_architectures
= set_copy(c
->syscall_archs
);
2566 if (!info
->system_call_architectures
)
2569 info
->system_call_filter_allow_list
= c
->syscall_allow_list
;
2570 if (c
->syscall_filter
) {
2571 info
->system_call_filter
= hashmap_copy(c
->syscall_filter
);
2572 if (!info
->system_call_filter
)
2578 info
->delegate
= g
->delegate
;
2579 if (cgroup_device_policy_to_string(g
->device_policy
)) {
2580 info
->device_policy
= strdup(cgroup_device_policy_to_string(g
->device_policy
));
2581 if (!info
->device_policy
)
2585 IPAddressAccessItem
*i
;
2586 bool deny_ipv4
= false, deny_ipv6
= false;
2588 LIST_FOREACH(items
, i
, g
->ip_address_deny
) {
2589 if (i
->family
== AF_INET
&& i
->prefixlen
== 0)
2591 else if (i
->family
== AF_INET6
&& i
->prefixlen
== 0)
2594 info
->ip_address_deny_all
= deny_ipv4
&& deny_ipv6
;
2596 info
->ip_address_allow_localhost
= info
->ip_address_allow_other
= false;
2597 LIST_FOREACH(items
, i
, g
->ip_address_allow
) {
2598 if (in_addr_is_localhost(i
->family
, &i
->address
))
2599 info
->ip_address_allow_localhost
= true;
2601 info
->ip_address_allow_other
= true;
2604 info
->ip_filters_custom_ingress
= !strv_isempty(g
->ip_filters_ingress
);
2605 info
->ip_filters_custom_egress
= !strv_isempty(g
->ip_filters_egress
);
2606 info
->device_allow_non_empty
= !LIST_IS_EMPTY(g
->device_allow
);
2609 *ret_info
= TAKE_PTR(info
);
2614 static int offline_security_check(Unit
*u
,
2616 JsonVariant
*policy
,
2617 PagerFlags pager_flags
,
2618 JsonFormatFlags json_format_flags
) {
2620 _cleanup_(table_unrefp
) Table
*overview_table
= NULL
;
2621 AnalyzeSecurityFlags flags
= 0;
2622 _cleanup_(security_info_freep
) SecurityInfo
*info
= NULL
;
2628 unit_dump(u
, stdout
, "\t");
2630 r
= get_security_info(u
, unit_get_exec_context(u
), unit_get_cgroup_context(u
), &info
);
2634 return assess(info
, overview_table
, flags
, threshold
, policy
, pager_flags
, json_format_flags
);
2637 static int offline_security_checks(char **filenames
,
2638 JsonVariant
*policy
,
2639 UnitFileScope scope
,
2641 bool run_generators
,
2644 PagerFlags pager_flags
,
2645 JsonFormatFlags json_format_flags
) {
2647 const ManagerTestRunFlags flags
=
2648 MANAGER_TEST_RUN_MINIMAL
|
2649 MANAGER_TEST_RUN_ENV_GENERATORS
|
2650 MANAGER_TEST_RUN_IGNORE_DEPENDENCIES
|
2651 run_generators
* MANAGER_TEST_RUN_GENERATORS
;
2653 _cleanup_(manager_freep
) Manager
*m
= NULL
;
2654 Unit
*units
[strv_length(filenames
)];
2655 _cleanup_free_
char *var
= NULL
;
2660 if (strv_isempty(filenames
))
2664 r
= verify_generate_path(&var
, filenames
);
2666 return log_error_errno(r
, "Failed to generate unit load path: %m");
2668 assert_se(set_unit_path(var
) >= 0);
2670 r
= manager_new(scope
, flags
, &m
);
2672 return log_error_errno(r
, "Failed to initialize manager: %m");
2674 log_debug("Starting manager...");
2676 r
= manager_startup(m
, /* serialization= */ NULL
, /* fds= */ NULL
, root
);
2680 log_debug("Loading remaining units from the command line...");
2682 STRV_FOREACH(filename
, filenames
) {
2683 _cleanup_free_
char *prepared
= NULL
;
2685 log_debug("Handling %s...", *filename
);
2687 k
= verify_prepare_filename(*filename
, &prepared
);
2689 log_warning_errno(k
, "Failed to prepare filename %s: %m", *filename
);
2695 k
= manager_load_startable_unit_or_warn(m
, NULL
, prepared
, &units
[count
]);
2705 for (size_t i
= 0; i
< count
; i
++) {
2706 k
= offline_security_check(units
[i
], threshold
, policy
, pager_flags
, json_format_flags
);
2707 if (k
< 0 && r
== 0)
2714 int analyze_security(sd_bus
*bus
,
2716 JsonVariant
*policy
,
2717 UnitFileScope scope
,
2719 bool run_generators
,
2723 JsonFormatFlags json_format_flags
,
2724 PagerFlags pager_flags
,
2725 AnalyzeSecurityFlags flags
) {
2727 _cleanup_(table_unrefp
) Table
*overview_table
= NULL
;
2733 return offline_security_checks(units
, policy
, scope
, check_man
, run_generators
, threshold
, root
, pager_flags
, json_format_flags
);
2735 if (strv_length(units
) != 1) {
2736 overview_table
= table_new("unit", "exposure", "predicate", "happy");
2737 if (!overview_table
)
2741 if (strv_isempty(units
)) {
2742 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2743 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2744 _cleanup_strv_free_
char **list
= NULL
;
2748 r
= sd_bus_call_method(
2750 "org.freedesktop.systemd1",
2751 "/org/freedesktop/systemd1",
2752 "org.freedesktop.systemd1.Manager",
2758 return log_error_errno(r
, "Failed to list units: %s", bus_error_message(&error
, r
));
2760 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
2762 return bus_log_parse_error(r
);
2768 r
= bus_parse_unit_info(reply
, &info
);
2770 return bus_log_parse_error(r
);
2774 if (!endswith(info
.id
, ".service"))
2777 if (!GREEDY_REALLOC(list
, n
+ 2))
2780 copy
= strdup(info
.id
);
2790 flags
|= ANALYZE_SECURITY_SHORT
|ANALYZE_SECURITY_ONLY_LOADED
|ANALYZE_SECURITY_ONLY_LONG_RUNNING
;
2792 STRV_FOREACH(i
, list
) {
2793 r
= analyze_security_one(bus
, *i
, overview_table
, flags
, threshold
, policy
, pager_flags
, json_format_flags
);
2794 if (r
< 0 && ret
>= 0)
2801 STRV_FOREACH(i
, units
) {
2802 _cleanup_free_
char *mangled
= NULL
, *instance
= NULL
;
2805 if (!FLAGS_SET(flags
, ANALYZE_SECURITY_SHORT
) && i
!= units
) {
2810 r
= unit_name_mangle(*i
, 0, &mangled
);
2812 return log_error_errno(r
, "Failed to mangle unit name '%s': %m", *i
);
2814 if (!endswith(mangled
, ".service"))
2815 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
2816 "Unit %s is not a service unit, refusing.",
2819 if (unit_name_is_valid(mangled
, UNIT_NAME_TEMPLATE
)) {
2820 r
= unit_name_replace_instance(mangled
, "test-instance", &instance
);
2828 r
= analyze_security_one(bus
, name
, overview_table
, flags
, threshold
, policy
, pager_flags
, json_format_flags
);
2829 if (r
< 0 && ret
>= 0)
2834 if (overview_table
) {
2835 if (!FLAGS_SET(flags
, ANALYZE_SECURITY_SHORT
)) {
2840 r
= table_print_with_pager(overview_table
, json_format_flags
, pager_flags
, /* show_header= */true);
2842 return log_error_errno(r
, "Failed to output table: %m");