1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <linux/capability.h>
7 #include "alloc-util.h"
8 #include "analyze-verify-util.h"
10 #include "analyze-security.h"
11 #include "bus-error.h"
12 #include "bus-locator.h"
13 #include "bus-map-properties.h"
14 #include "bus-unit-util.h"
20 #include "format-table.h"
21 #include "in-addr-prefix-util.h"
24 #include "nulstr-util.h"
25 #include "parse-util.h"
26 #include "path-util.h"
27 #include "portable-util.h"
28 #include "pretty-print.h"
29 #include "seccomp-util.h"
32 #include "stdio-util.h"
33 #include "string-util.h"
36 #include "unit-name.h"
37 #include "unit-serialize.h"
39 typedef struct SecurityInfo
{
44 bool default_dependencies
;
46 uint64_t ambient_capabilities
;
47 uint64_t capability_bounding_set
;
50 char **supplementary_groups
;
53 bool ip_address_deny_all
;
54 bool ip_address_allow_localhost
;
55 bool ip_address_allow_other
;
57 bool ip_filters_custom_ingress
;
58 bool ip_filters_custom_egress
;
63 bool lock_personality
;
64 bool memory_deny_write_execute
;
65 bool no_new_privileges
;
67 bool protect_hostname
;
75 bool protect_control_groups
;
76 bool protect_kernel_modules
;
77 bool protect_kernel_tunables
;
78 bool protect_kernel_logs
;
86 bool restrict_address_family_inet
;
87 bool restrict_address_family_unix
;
88 bool restrict_address_family_netlink
;
89 bool restrict_address_family_packet
;
90 bool restrict_address_family_other
;
92 unsigned long long restrict_namespaces
;
93 bool restrict_realtime
;
94 bool restrict_suid_sgid
;
103 Set
*system_call_architectures
;
105 bool system_call_filter_allow_list
;
106 Set
*system_call_filter
;
111 struct security_assessor
{
113 const char *json_field
;
114 const char *description_good
;
115 const char *description_bad
;
116 const char *description_na
;
121 const struct security_assessor
*a
,
122 const SecurityInfo
*info
,
124 uint64_t *ret_badness
,
125 char **ret_description
);
128 bool default_dependencies_only
;
131 static SecurityInfo
*security_info_new(void) {
132 SecurityInfo
*info
= new(SecurityInfo
, 1);
136 *info
= (SecurityInfo
) {
137 .default_dependencies
= true,
138 .capability_bounding_set
= UINT64_MAX
,
139 .restrict_namespaces
= UINT64_MAX
,
146 static SecurityInfo
*security_info_free(SecurityInfo
*i
) {
153 free(i
->fragment_path
);
157 free(i
->protect_home
);
158 free(i
->protect_system
);
160 free(i
->root_directory
);
163 free(i
->keyring_mode
);
164 free(i
->protect_proc
);
165 free(i
->proc_subset
);
166 free(i
->notify_access
);
168 free(i
->device_policy
);
169 strv_free(i
->device_allow
);
171 strv_free(i
->supplementary_groups
);
172 set_free(i
->system_call_architectures
);
173 set_free(i
->system_call_filter
);
178 DEFINE_TRIVIAL_CLEANUP_FUNC(SecurityInfo
*, security_info_free
);
180 static bool security_info_runs_privileged(const SecurityInfo
*i
) {
183 if (STRPTR_IN_SET(i
->user
, "0", "root"))
189 return isempty(i
->user
);
192 static int assess_bool(
193 const struct security_assessor
*a
,
194 const SecurityInfo
*info
,
196 uint64_t *ret_badness
,
197 char **ret_description
) {
199 const bool *b
= ASSERT_PTR(data
);
202 assert(ret_description
);
204 *ret_badness
= a
->parameter
? *b
: !*b
;
205 *ret_description
= NULL
;
210 static int assess_user(
211 const struct security_assessor
*a
,
212 const SecurityInfo
*info
,
214 uint64_t *ret_badness
,
215 char **ret_description
) {
222 assert(ret_description
);
224 if (streq_ptr(info
->user
, NOBODY_USER_NAME
)) {
225 d
= "Service runs under as '" NOBODY_USER_NAME
"' user, which should not be used for services";
227 } else if (info
->dynamic_user
&& !STR_IN_SET(info
->user
, "0", "root")) {
228 d
= "Service runs under a transient non-root user identity";
230 } else if (info
->user
&& !STR_IN_SET(info
->user
, "0", "root", "")) {
231 d
= "Service runs under a static non-root user identity";
235 *ret_description
= NULL
;
239 r
= strdup_to(ret_description
, d
);
247 static int assess_protect_home(
248 const struct security_assessor
*a
,
249 const SecurityInfo
*info
,
251 uint64_t *ret_badness
,
252 char **ret_description
) {
254 const char *description
;
259 assert(ret_description
);
262 description
= "Service has full access to home directories";
264 r
= parse_boolean(info
->protect_home
);
266 if (streq_ptr(info
->protect_home
, "read-only")) {
268 description
= "Service has read-only access to home directories";
269 } else if (streq_ptr(info
->protect_home
, "tmpfs")) {
271 description
= "Service has access to fake empty home directories";
275 description
= "Service has no access to home directories";
278 r
= strdup_to(ret_description
, description
);
282 *ret_badness
= badness
;
286 static int assess_protect_system(
287 const struct security_assessor
*a
,
288 const SecurityInfo
*info
,
290 uint64_t *ret_badness
,
291 char **ret_description
) {
293 const char *description
;
298 assert(ret_description
);
301 description
= "Service has full access to the OS file hierarchy";
303 r
= parse_boolean(info
->protect_system
);
305 if (streq_ptr(info
->protect_system
, "full")) {
307 description
= "Service has very limited write access to the OS file hierarchy";
308 } else if (streq_ptr(info
->protect_system
, "strict")) {
310 description
= "Service has strict read-only access to the OS file hierarchy";
314 description
= "Service has limited write access to the OS file hierarchy";
317 r
= strdup_to(ret_description
, description
);
321 *ret_badness
= badness
;
325 static int assess_root_directory(
326 const struct security_assessor
*a
,
327 const SecurityInfo
*info
,
329 uint64_t *ret_badness
,
330 char **ret_description
) {
333 assert(ret_description
);
336 empty_or_root(info
->root_directory
) &&
337 empty_or_root(info
->root_image
);
338 *ret_description
= NULL
;
343 static int assess_capability_bounding_set(
344 const struct security_assessor
*a
,
345 const SecurityInfo
*info
,
347 uint64_t *ret_badness
,
348 char **ret_description
) {
351 assert(ret_description
);
353 *ret_badness
= !!(info
->capability_bounding_set
& a
->parameter
);
354 *ret_description
= NULL
;
359 static int assess_umask(
360 const struct security_assessor
*a
,
361 const SecurityInfo
*info
,
363 uint64_t *ret_badness
,
364 char **ret_description
) {
371 assert(ret_description
);
373 if (!FLAGS_SET(info
->_umask
, 0002)) {
374 d
= "Files created by service are world-writable by default";
376 } else if (!FLAGS_SET(info
->_umask
, 0004)) {
377 d
= "Files created by service are world-readable by default";
379 } else if (!FLAGS_SET(info
->_umask
, 0020)) {
380 d
= "Files created by service are group-writable by default";
382 } else if (!FLAGS_SET(info
->_umask
, 0040)) {
383 d
= "Files created by service are group-readable by default";
386 d
= "Files created by service are accessible only by service's own user by default";
390 r
= strdup_to(ret_description
, d
);
398 static int assess_keyring_mode(
399 const struct security_assessor
*a
,
400 const SecurityInfo
*info
,
402 uint64_t *ret_badness
,
403 char **ret_description
) {
406 assert(ret_description
);
408 *ret_badness
= !streq_ptr(info
->keyring_mode
, "private");
409 *ret_description
= NULL
;
414 static int assess_protect_proc(
415 const struct security_assessor
*a
,
416 const SecurityInfo
*info
,
418 uint64_t *ret_badness
,
419 char **ret_description
) {
422 assert(ret_description
);
424 if (streq_ptr(info
->protect_proc
, "noaccess"))
426 else if (STRPTR_IN_SET(info
->protect_proc
, "invisible", "ptraceable"))
431 *ret_description
= NULL
;
436 static int assess_proc_subset(
437 const struct security_assessor
*a
,
438 const SecurityInfo
*info
,
440 uint64_t *ret_badness
,
441 char **ret_description
) {
444 assert(ret_description
);
446 *ret_badness
= !streq_ptr(info
->proc_subset
, "pid");
447 *ret_description
= NULL
;
452 static int assess_notify_access(
453 const struct security_assessor
*a
,
454 const SecurityInfo
*info
,
456 uint64_t *ret_badness
,
457 char **ret_description
) {
460 assert(ret_description
);
462 *ret_badness
= streq_ptr(info
->notify_access
, "all");
463 *ret_description
= NULL
;
468 static int assess_remove_ipc(
469 const struct security_assessor
*a
,
470 const SecurityInfo
*info
,
472 uint64_t *ret_badness
,
473 char **ret_description
) {
476 assert(ret_description
);
478 if (security_info_runs_privileged(info
))
479 *ret_badness
= UINT64_MAX
;
481 *ret_badness
= !info
->remove_ipc
;
483 *ret_description
= NULL
;
487 static int assess_supplementary_groups(
488 const struct security_assessor
*a
,
489 const SecurityInfo
*info
,
491 uint64_t *ret_badness
,
492 char **ret_description
) {
495 assert(ret_description
);
497 if (security_info_runs_privileged(info
))
498 *ret_badness
= UINT64_MAX
;
500 *ret_badness
= !strv_isempty(info
->supplementary_groups
);
502 *ret_description
= NULL
;
506 static int assess_restrict_namespaces(
507 const struct security_assessor
*a
,
508 const SecurityInfo
*info
,
510 uint64_t *ret_badness
,
511 char **ret_description
) {
514 assert(ret_description
);
516 *ret_badness
= !!(info
->restrict_namespaces
& a
->parameter
);
517 *ret_description
= NULL
;
524 static int assess_system_call_architectures(
525 const struct security_assessor
*a
,
526 const SecurityInfo
*info
,
528 uint64_t *ret_badness
,
529 char **ret_description
) {
536 assert(ret_description
);
538 if (set_isempty(info
->system_call_architectures
)) {
540 d
= "Service may execute system calls with all ABIs";
541 } else if (set_contains(info
->system_call_architectures
, "native") &&
542 set_size(info
->system_call_architectures
) == 1) {
544 d
= "Service may execute system calls only with native ABI";
547 d
= "Service may execute system calls with multiple ABIs";
550 r
= strdup_to(ret_description
, d
);
558 static bool syscall_names_in_filter(Set
*s
, bool allow_list
, const SyscallFilterSet
*f
, const char **ret_offending_syscall
) {
559 NULSTR_FOREACH(syscall
, f
->value
) {
560 if (syscall
[0] == '@') {
561 const SyscallFilterSet
*g
;
563 assert_se(g
= syscall_filter_set_find(syscall
));
564 if (syscall_names_in_filter(s
, allow_list
, g
, ret_offending_syscall
))
565 return true; /* bad! */
570 /* Let's see if the system call actually exists on this platform, before complaining */
571 if (seccomp_syscall_resolve_name(syscall
) < 0)
574 if (set_contains(s
, syscall
) == allow_list
) {
575 log_debug("Offending syscall filter item: %s", syscall
);
576 if (ret_offending_syscall
)
577 *ret_offending_syscall
= syscall
;
578 return true; /* bad! */
582 *ret_offending_syscall
= NULL
;
586 static int assess_system_call_filter(
587 const struct security_assessor
*a
,
588 const SecurityInfo
*info
,
590 uint64_t *ret_badness
,
591 char **ret_description
) {
596 assert(ret_description
);
598 assert(a
->parameter
< _SYSCALL_FILTER_SET_MAX
);
599 const SyscallFilterSet
*f
= syscall_filter_sets
+ a
->parameter
;
605 if (!info
->system_call_filter_allow_list
&& set_isempty(info
->system_call_filter
)) {
606 r
= strdup_to(&d
, "Service does not filter system calls");
610 const char *offender
= NULL
;
612 log_debug("Analyzing system call filter, checking against: %s", f
->name
);
613 bad
= syscall_names_in_filter(info
->system_call_filter
, info
->system_call_filter_allow_list
, f
, &offender
);
614 log_debug("Result: %s", bad
? "bad" : "good");
616 if (info
->system_call_filter_allow_list
) {
618 r
= asprintf(&d
, "System call allow list defined for service, and %s is included "
619 "(e.g. %s is allowed)",
623 r
= asprintf(&d
, "System call allow list defined for service, and %s is not included",
629 r
= asprintf(&d
, "System call deny list defined for service, and %s is not included "
630 "(e.g. %s is allowed)",
634 r
= asprintf(&d
, "System call deny list defined for service, and %s is included",
643 *ret_description
= d
;
651 static int assess_ip_address_allow(
652 const struct security_assessor
*a
,
653 const SecurityInfo
*info
,
655 uint64_t *ret_badness
,
656 char **ret_description
) {
664 assert(ret_description
);
666 if (info
->ip_filters_custom_ingress
|| info
->ip_filters_custom_egress
) {
667 d
= "Service defines custom ingress/egress IP filters with BPF programs";
669 } else if (!info
->ip_address_deny_all
) {
670 d
= "Service does not define an IP address allow list";
672 } else if (info
->ip_address_allow_other
) {
673 d
= "Service defines IP address allow list with non-localhost entries";
675 } else if (info
->ip_address_allow_localhost
) {
676 d
= "Service defines IP address allow list with only localhost entries";
679 d
= "Service blocks all IP address ranges";
683 r
= strdup_to(ret_description
, d
);
691 static int assess_device_allow(
692 const struct security_assessor
*a
,
693 const SecurityInfo
*info
,
695 uint64_t *ret_badness
,
696 char **ret_description
) {
703 assert(ret_description
);
705 if (STRPTR_IN_SET(info
->device_policy
, "strict", "closed")) {
707 if (!strv_isempty(info
->device_allow
)) {
708 _cleanup_free_
char *join
= NULL
;
710 join
= strv_join(info
->device_allow
, " ");
714 d
= strjoin("Service has a device ACL with some special devices: ", join
);
717 d
= strdup("Service has a minimal device ACL");
721 d
= strdup("Service has no device ACL");
729 *ret_description
= d
;
734 static int assess_ambient_capabilities(
735 const struct security_assessor
*a
,
736 const SecurityInfo
*info
,
738 uint64_t *ret_badness
,
739 char **ret_description
) {
742 assert(ret_description
);
744 *ret_badness
= info
->ambient_capabilities
!= 0;
745 *ret_description
= NULL
;
750 static const struct security_assessor security_assessor_table
[] = {
752 .id
= "User=/DynamicUser=",
753 .json_field
= "UserOrDynamicUser",
754 .description_bad
= "Service runs as root user",
755 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#User=",
758 .assess
= assess_user
,
761 .id
= "SupplementaryGroups=",
762 .json_field
= "SupplementaryGroups",
763 .description_good
= "Service has no supplementary groups",
764 .description_bad
= "Service runs with supplementary groups",
765 .description_na
= "Service runs as root, option does not matter",
766 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SupplementaryGroups=",
769 .assess
= assess_supplementary_groups
,
772 .id
= "PrivateDevices=",
773 .json_field
= "PrivateDevices",
774 .description_good
= "Service has no access to hardware devices",
775 .description_bad
= "Service potentially has access to hardware devices",
776 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateDevices=",
779 .assess
= assess_bool
,
780 .offset
= offsetof(SecurityInfo
, private_devices
),
783 .id
= "PrivateMounts=",
784 .json_field
= "PrivateMounts",
785 .description_good
= "Service cannot install system mounts",
786 .description_bad
= "Service may install system mounts",
787 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateMounts=",
790 .assess
= assess_bool
,
791 .offset
= offsetof(SecurityInfo
, private_mounts
),
794 .id
= "PrivateNetwork=",
795 .json_field
= "PrivateNetwork",
796 .description_good
= "Service has no access to the host's network",
797 .description_bad
= "Service has access to the host's network",
798 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateNetwork=",
801 .assess
= assess_bool
,
802 .offset
= offsetof(SecurityInfo
, private_network
),
806 .json_field
= "PrivateTmp",
807 .description_good
= "Service has no access to other software's temporary files",
808 .description_bad
= "Service has access to other software's temporary files",
809 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateTmp=",
812 .assess
= assess_bool
,
813 .offset
= offsetof(SecurityInfo
, private_tmp
),
814 .default_dependencies_only
= true,
817 .id
= "PrivateUsers=",
818 .json_field
= "PrivateUsers",
819 .description_good
= "Service does not have access to other users",
820 .description_bad
= "Service has access to other users",
821 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateUsers=",
824 .assess
= assess_bool
,
825 .offset
= offsetof(SecurityInfo
, private_users
),
828 .id
= "ProtectControlGroups=",
829 .json_field
= "ProtectControlGroups",
830 .description_good
= "Service cannot modify the control group file system",
831 .description_bad
= "Service may modify the control group file system",
832 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectControlGroups=",
835 .assess
= assess_bool
,
836 .offset
= offsetof(SecurityInfo
, protect_control_groups
),
839 .id
= "ProtectKernelModules=",
840 .json_field
= "ProtectKernelModules",
841 .description_good
= "Service cannot load or read kernel modules",
842 .description_bad
= "Service may load or read kernel modules",
843 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelModules=",
846 .assess
= assess_bool
,
847 .offset
= offsetof(SecurityInfo
, protect_kernel_modules
),
850 .id
= "ProtectKernelTunables=",
851 .json_field
= "ProtectKernelTunables",
852 .description_good
= "Service cannot alter kernel tunables (/proc/sys, …)",
853 .description_bad
= "Service may alter kernel tunables",
854 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelTunables=",
857 .assess
= assess_bool
,
858 .offset
= offsetof(SecurityInfo
, protect_kernel_tunables
),
861 .id
= "ProtectKernelLogs=",
862 .json_field
= "ProtectKernelLogs",
863 .description_good
= "Service cannot read from or write to the kernel log ring buffer",
864 .description_bad
= "Service may read from or write to the kernel log ring buffer",
865 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelLogs=",
868 .assess
= assess_bool
,
869 .offset
= offsetof(SecurityInfo
, protect_kernel_logs
),
872 .id
= "ProtectClock=",
873 .json_field
= "ProtectClock",
874 .description_good
= "Service cannot write to the hardware clock or system clock",
875 .description_bad
= "Service may write to the hardware clock or system clock",
876 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectClock=",
879 .assess
= assess_bool
,
880 .offset
= offsetof(SecurityInfo
, protect_clock
),
883 .id
= "ProtectHome=",
884 .json_field
= "ProtectHome",
885 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHome=",
888 .assess
= assess_protect_home
,
889 .default_dependencies_only
= true,
892 .id
= "ProtectHostname=",
893 .json_field
= "ProtectHostname",
894 .description_good
= "Service cannot change system host/domainname",
895 .description_bad
= "Service may change system host/domainname",
896 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHostname=",
899 .assess
= assess_bool
,
900 .offset
= offsetof(SecurityInfo
, protect_hostname
),
903 .id
= "ProtectSystem=",
904 .json_field
= "ProtectSystem",
905 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectSystem=",
908 .assess
= assess_protect_system
,
909 .default_dependencies_only
= true,
912 .id
= "RootDirectory=/RootImage=",
913 .json_field
= "RootDirectoryOrRootImage",
914 .description_good
= "Service has its own root directory/image",
915 .description_bad
= "Service runs within the host's root directory",
916 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RootDirectory=",
919 .assess
= assess_root_directory
,
920 .default_dependencies_only
= true,
923 .id
= "LockPersonality=",
924 .json_field
= "LockPersonality",
925 .description_good
= "Service cannot change ABI personality",
926 .description_bad
= "Service may change ABI personality",
927 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#LockPersonality=",
930 .assess
= assess_bool
,
931 .offset
= offsetof(SecurityInfo
, lock_personality
),
934 .id
= "MemoryDenyWriteExecute=",
935 .json_field
= "MemoryDenyWriteExecute",
936 .description_good
= "Service cannot create writable executable memory mappings",
937 .description_bad
= "Service may create writable executable memory mappings",
938 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#MemoryDenyWriteExecute=",
941 .assess
= assess_bool
,
942 .offset
= offsetof(SecurityInfo
, memory_deny_write_execute
),
945 .id
= "NoNewPrivileges=",
946 .json_field
= "NoNewPrivileges",
947 .description_good
= "Service processes cannot acquire new privileges",
948 .description_bad
= "Service processes may acquire new privileges",
949 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NoNewPrivileges=",
952 .assess
= assess_bool
,
953 .offset
= offsetof(SecurityInfo
, no_new_privileges
),
956 .id
= "CapabilityBoundingSet=~CAP_SYS_ADMIN",
957 .json_field
= "CapabilityBoundingSet_CAP_SYS_ADMIN",
958 .description_good
= "Service has no administrator privileges",
959 .description_bad
= "Service has administrator privileges",
960 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
963 .assess
= assess_capability_bounding_set
,
964 .parameter
= UINT64_C(1) << CAP_SYS_ADMIN
,
967 .id
= "CapabilityBoundingSet=~CAP_SET(UID|GID|PCAP)",
968 .json_field
= "CapabilityBoundingSet_CAP_SET_UID_GID_PCAP",
969 .description_good
= "Service cannot change UID/GID identities/capabilities",
970 .description_bad
= "Service may change UID/GID identities/capabilities",
971 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
974 .assess
= assess_capability_bounding_set
,
975 .parameter
= (UINT64_C(1) << CAP_SETUID
)|
976 (UINT64_C(1) << CAP_SETGID
)|
977 (UINT64_C(1) << CAP_SETPCAP
),
980 .id
= "CapabilityBoundingSet=~CAP_SYS_PTRACE",
981 .json_field
= "CapabilityBoundingSet_CAP_SYS_PTRACE",
982 .description_good
= "Service has no ptrace() debugging abilities",
983 .description_bad
= "Service has ptrace() debugging abilities",
984 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
987 .assess
= assess_capability_bounding_set
,
988 .parameter
= (UINT64_C(1) << CAP_SYS_PTRACE
),
991 .id
= "CapabilityBoundingSet=~CAP_SYS_TIME",
992 .json_field
= "CapabilityBoundingSet_CAP_SYS_TIME",
993 .description_good
= "Service processes cannot change the system clock",
994 .description_bad
= "Service processes may change the system clock",
995 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
998 .assess
= assess_capability_bounding_set
,
999 .parameter
= UINT64_C(1) << CAP_SYS_TIME
,
1002 .id
= "CapabilityBoundingSet=~CAP_NET_ADMIN",
1003 .json_field
= "CapabilityBoundingSet_CAP_NET_ADMIN",
1004 .description_good
= "Service has no network configuration privileges",
1005 .description_bad
= "Service has network configuration privileges",
1006 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1009 .assess
= assess_capability_bounding_set
,
1010 .parameter
= (UINT64_C(1) << CAP_NET_ADMIN
),
1013 .id
= "CapabilityBoundingSet=~CAP_SYS_RAWIO",
1014 .json_field
= "CapabilityBoundingSet_CAP_SYS_RAWIO",
1015 .description_good
= "Service has no raw I/O access",
1016 .description_bad
= "Service has raw I/O access",
1017 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1020 .assess
= assess_capability_bounding_set
,
1021 .parameter
= (UINT64_C(1) << CAP_SYS_RAWIO
),
1024 .id
= "CapabilityBoundingSet=~CAP_SYS_MODULE",
1025 .json_field
= "CapabilityBoundingSet_CAP_SYS_MODULE",
1026 .description_good
= "Service cannot load kernel modules",
1027 .description_bad
= "Service may load kernel modules",
1028 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1031 .assess
= assess_capability_bounding_set
,
1032 .parameter
= (UINT64_C(1) << CAP_SYS_MODULE
),
1035 .id
= "CapabilityBoundingSet=~CAP_AUDIT_*",
1036 .json_field
= "CapabilityBoundingSet_CAP_AUDIT",
1037 .description_good
= "Service has no audit subsystem access",
1038 .description_bad
= "Service has audit subsystem access",
1039 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1042 .assess
= assess_capability_bounding_set
,
1043 .parameter
= (UINT64_C(1) << CAP_AUDIT_CONTROL
) |
1044 (UINT64_C(1) << CAP_AUDIT_READ
) |
1045 (UINT64_C(1) << CAP_AUDIT_WRITE
),
1048 .id
= "CapabilityBoundingSet=~CAP_SYSLOG",
1049 .json_field
= "CapabilityBoundingSet_CAP_SYSLOG",
1050 .description_good
= "Service has no access to kernel logging",
1051 .description_bad
= "Service has access to kernel logging",
1052 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1055 .assess
= assess_capability_bounding_set
,
1056 .parameter
= (UINT64_C(1) << CAP_SYSLOG
),
1059 .id
= "CapabilityBoundingSet=~CAP_SYS_(NICE|RESOURCE)",
1060 .json_field
= "CapabilityBoundingSet_CAP_SYS_NICE_RESOURCE",
1061 .description_good
= "Service has no privileges to change resource use parameters",
1062 .description_bad
= "Service has privileges to change resource use parameters",
1063 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1066 .assess
= assess_capability_bounding_set
,
1067 .parameter
= (UINT64_C(1) << CAP_SYS_NICE
) |
1068 (UINT64_C(1) << CAP_SYS_RESOURCE
),
1071 .id
= "CapabilityBoundingSet=~CAP_MKNOD",
1072 .json_field
= "CapabilityBoundingSet_CAP_MKNOD",
1073 .description_good
= "Service cannot create device nodes",
1074 .description_bad
= "Service may create device nodes",
1075 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1078 .assess
= assess_capability_bounding_set
,
1079 .parameter
= (UINT64_C(1) << CAP_MKNOD
),
1082 .id
= "CapabilityBoundingSet=~CAP_(CHOWN|FSETID|SETFCAP)",
1083 .json_field
= "CapabilityBoundingSet_CAP_CHOWN_FSETID_SETFCAP",
1084 .description_good
= "Service cannot change file ownership/access mode/capabilities",
1085 .description_bad
= "Service may change file ownership/access mode/capabilities unrestricted",
1086 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1089 .assess
= assess_capability_bounding_set
,
1090 .parameter
= (UINT64_C(1) << CAP_CHOWN
) |
1091 (UINT64_C(1) << CAP_FSETID
) |
1092 (UINT64_C(1) << CAP_SETFCAP
),
1095 .id
= "CapabilityBoundingSet=~CAP_(DAC_*|FOWNER|IPC_OWNER)",
1096 .json_field
= "CapabilityBoundingSet_CAP_DAC_FOWNER_IPC_OWNER",
1097 .description_good
= "Service cannot override UNIX file/IPC permission checks",
1098 .description_bad
= "Service may override UNIX file/IPC permission checks",
1099 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1102 .assess
= assess_capability_bounding_set
,
1103 .parameter
= (UINT64_C(1) << CAP_DAC_OVERRIDE
) |
1104 (UINT64_C(1) << CAP_DAC_READ_SEARCH
) |
1105 (UINT64_C(1) << CAP_FOWNER
) |
1106 (UINT64_C(1) << CAP_IPC_OWNER
),
1109 .id
= "CapabilityBoundingSet=~CAP_KILL",
1110 .json_field
= "CapabilityBoundingSet_CAP_KILL",
1111 .description_good
= "Service cannot send UNIX signals to arbitrary processes",
1112 .description_bad
= "Service may send UNIX signals to arbitrary processes",
1113 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1116 .assess
= assess_capability_bounding_set
,
1117 .parameter
= (UINT64_C(1) << CAP_KILL
),
1120 .id
= "CapabilityBoundingSet=~CAP_NET_(BIND_SERVICE|BROADCAST|RAW)",
1121 .json_field
= "CapabilityBoundingSet_CAP_NET_BIND_SERVICE_BROADCAST_RAW)",
1122 .description_good
= "Service has no elevated networking privileges",
1123 .description_bad
= "Service has elevated networking privileges",
1124 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1127 .assess
= assess_capability_bounding_set
,
1128 .parameter
= (UINT64_C(1) << CAP_NET_BIND_SERVICE
) |
1129 (UINT64_C(1) << CAP_NET_BROADCAST
) |
1130 (UINT64_C(1) << CAP_NET_RAW
),
1133 .id
= "CapabilityBoundingSet=~CAP_SYS_BOOT",
1134 .json_field
= "CapabilityBoundingSet_CAP_SYS_BOOT",
1135 .description_good
= "Service cannot issue reboot()",
1136 .description_bad
= "Service may issue reboot()",
1137 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1140 .assess
= assess_capability_bounding_set
,
1141 .parameter
= (UINT64_C(1) << CAP_SYS_BOOT
),
1144 .id
= "CapabilityBoundingSet=~CAP_MAC_*",
1145 .json_field
= "CapabilityBoundingSet_CAP_MAC",
1146 .description_good
= "Service cannot adjust SMACK MAC",
1147 .description_bad
= "Service may adjust SMACK MAC",
1148 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1151 .assess
= assess_capability_bounding_set
,
1152 .parameter
= (UINT64_C(1) << CAP_MAC_ADMIN
)|
1153 (UINT64_C(1) << CAP_MAC_OVERRIDE
),
1156 .id
= "CapabilityBoundingSet=~CAP_LINUX_IMMUTABLE",
1157 .json_field
= "CapabilityBoundingSet_CAP_LINUX_IMMUTABLE",
1158 .description_good
= "Service cannot mark files immutable",
1159 .description_bad
= "Service may mark files immutable",
1160 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1163 .assess
= assess_capability_bounding_set
,
1164 .parameter
= (UINT64_C(1) << CAP_LINUX_IMMUTABLE
),
1167 .id
= "CapabilityBoundingSet=~CAP_IPC_LOCK",
1168 .json_field
= "CapabilityBoundingSet_CAP_IPC_LOCK",
1169 .description_good
= "Service cannot lock memory into RAM",
1170 .description_bad
= "Service may lock memory into RAM",
1171 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1174 .assess
= assess_capability_bounding_set
,
1175 .parameter
= (UINT64_C(1) << CAP_IPC_LOCK
),
1178 .id
= "CapabilityBoundingSet=~CAP_SYS_CHROOT",
1179 .json_field
= "CapabilityBoundingSet_CAP_SYS_CHROOT",
1180 .description_good
= "Service cannot issue chroot()",
1181 .description_bad
= "Service may issue chroot()",
1182 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1185 .assess
= assess_capability_bounding_set
,
1186 .parameter
= (UINT64_C(1) << CAP_SYS_CHROOT
),
1189 .id
= "CapabilityBoundingSet=~CAP_BLOCK_SUSPEND",
1190 .json_field
= "CapabilityBoundingSet_CAP_BLOCK_SUSPEND",
1191 .description_good
= "Service cannot establish wake locks",
1192 .description_bad
= "Service may establish wake locks",
1193 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1196 .assess
= assess_capability_bounding_set
,
1197 .parameter
= (UINT64_C(1) << CAP_BLOCK_SUSPEND
),
1200 .id
= "CapabilityBoundingSet=~CAP_WAKE_ALARM",
1201 .json_field
= "CapabilityBoundingSet_CAP_WAKE_ALARM",
1202 .description_good
= "Service cannot program timers that wake up the system",
1203 .description_bad
= "Service may program timers that wake up the system",
1204 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1207 .assess
= assess_capability_bounding_set
,
1208 .parameter
= (UINT64_C(1) << CAP_WAKE_ALARM
),
1211 .id
= "CapabilityBoundingSet=~CAP_LEASE",
1212 .json_field
= "CapabilityBoundingSet_CAP_LEASE",
1213 .description_good
= "Service cannot create file leases",
1214 .description_bad
= "Service may create file leases",
1215 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1218 .assess
= assess_capability_bounding_set
,
1219 .parameter
= (UINT64_C(1) << CAP_LEASE
),
1222 .id
= "CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG",
1223 .json_field
= "CapabilityBoundingSet_CAP_SYS_TTY_CONFIG",
1224 .description_good
= "Service cannot issue vhangup()",
1225 .description_bad
= "Service may issue vhangup()",
1226 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1229 .assess
= assess_capability_bounding_set
,
1230 .parameter
= (UINT64_C(1) << CAP_SYS_TTY_CONFIG
),
1233 .id
= "CapabilityBoundingSet=~CAP_SYS_PACCT",
1234 .json_field
= "CapabilityBoundingSet_CAP_SYS_PACCT",
1235 .description_good
= "Service cannot use acct()",
1236 .description_bad
= "Service may use acct()",
1237 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1240 .assess
= assess_capability_bounding_set
,
1241 .parameter
= (UINT64_C(1) << CAP_SYS_PACCT
),
1244 .id
= "CapabilityBoundingSet=~CAP_BPF",
1245 .json_field
= "CapabilityBoundingSet_CAP_BPF",
1246 .description_good
= "Service may not load BPF programs",
1247 .description_bad
= "Service may load BPF programs",
1248 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1251 .assess
= assess_capability_bounding_set
,
1252 .parameter
= (UINT64_C(1) << CAP_BPF
),
1256 .json_field
= "UMask",
1257 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#UMask=",
1260 .assess
= assess_umask
,
1263 .id
= "KeyringMode=",
1264 .json_field
= "KeyringMode",
1265 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#KeyringMode=",
1266 .description_good
= "Service doesn't share key material with other services",
1267 .description_bad
= "Service shares key material with other service",
1270 .assess
= assess_keyring_mode
,
1273 .id
= "ProtectProc=",
1274 .json_field
= "ProtectProc",
1275 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectProc=",
1276 .description_good
= "Service has restricted access to process tree (/proc hidepid=)",
1277 .description_bad
= "Service has full access to process tree (/proc hidepid=)",
1280 .assess
= assess_protect_proc
,
1283 .id
= "ProcSubset=",
1284 .json_field
= "ProcSubset",
1285 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProcSubset=",
1286 .description_good
= "Service has no access to non-process /proc files (/proc subset=)",
1287 .description_bad
= "Service has full access to non-process /proc files (/proc subset=)",
1290 .assess
= assess_proc_subset
,
1293 .id
= "NotifyAccess=",
1294 .json_field
= "NotifyAccess",
1295 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NotifyAccess=",
1296 .description_good
= "Service child processes cannot alter service state",
1297 .description_bad
= "Service child processes may alter service state",
1300 .assess
= assess_notify_access
,
1304 .json_field
= "RemoveIPC",
1305 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RemoveIPC=",
1306 .description_good
= "Service user cannot leave SysV IPC objects around",
1307 .description_bad
= "Service user may leave SysV IPC objects around",
1308 .description_na
= "Service runs as root, option does not apply",
1311 .assess
= assess_remove_ipc
,
1312 .offset
= offsetof(SecurityInfo
, remove_ipc
),
1316 .json_field
= "Delegate",
1317 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Delegate=",
1318 .description_good
= "Service does not maintain its own delegated control group subtree",
1319 .description_bad
= "Service maintains its own delegated control group subtree",
1322 .assess
= assess_bool
,
1323 .offset
= offsetof(SecurityInfo
, delegate
),
1324 .parameter
= true, /* invert! */
1327 .id
= "RestrictRealtime=",
1328 .json_field
= "RestrictRealtime",
1329 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictRealtime=",
1330 .description_good
= "Service realtime scheduling access is restricted",
1331 .description_bad
= "Service may acquire realtime scheduling",
1334 .assess
= assess_bool
,
1335 .offset
= offsetof(SecurityInfo
, restrict_realtime
),
1338 .id
= "RestrictSUIDSGID=",
1339 .json_field
= "RestrictSUIDSGID",
1340 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictSUIDSGID=",
1341 .description_good
= "SUID/SGID file creation by service is restricted",
1342 .description_bad
= "Service may create SUID/SGID files",
1345 .assess
= assess_bool
,
1346 .offset
= offsetof(SecurityInfo
, restrict_suid_sgid
),
1349 .id
= "RestrictNamespaces=~user",
1350 .json_field
= "RestrictNamespaces_user",
1351 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1352 .description_good
= "Service cannot create user namespaces",
1353 .description_bad
= "Service may create user namespaces",
1356 .assess
= assess_restrict_namespaces
,
1357 .parameter
= CLONE_NEWUSER
,
1360 .id
= "RestrictNamespaces=~mnt",
1361 .json_field
= "RestrictNamespaces_mnt",
1362 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1363 .description_good
= "Service cannot create file system namespaces",
1364 .description_bad
= "Service may create file system namespaces",
1367 .assess
= assess_restrict_namespaces
,
1368 .parameter
= CLONE_NEWNS
,
1371 .id
= "RestrictNamespaces=~ipc",
1372 .json_field
= "RestrictNamespaces_ipc",
1373 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1374 .description_good
= "Service cannot create IPC namespaces",
1375 .description_bad
= "Service may create IPC namespaces",
1378 .assess
= assess_restrict_namespaces
,
1379 .parameter
= CLONE_NEWIPC
,
1382 .id
= "RestrictNamespaces=~pid",
1383 .json_field
= "RestrictNamespaces_pid",
1384 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1385 .description_good
= "Service cannot create process namespaces",
1386 .description_bad
= "Service may create process namespaces",
1389 .assess
= assess_restrict_namespaces
,
1390 .parameter
= CLONE_NEWPID
,
1393 .id
= "RestrictNamespaces=~cgroup",
1394 .json_field
= "RestrictNamespaces_cgroup",
1395 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1396 .description_good
= "Service cannot create cgroup namespaces",
1397 .description_bad
= "Service may create cgroup namespaces",
1400 .assess
= assess_restrict_namespaces
,
1401 .parameter
= CLONE_NEWCGROUP
,
1404 .id
= "RestrictNamespaces=~net",
1405 .json_field
= "RestrictNamespaces_net",
1406 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1407 .description_good
= "Service cannot create network namespaces",
1408 .description_bad
= "Service may create network namespaces",
1411 .assess
= assess_restrict_namespaces
,
1412 .parameter
= CLONE_NEWNET
,
1415 .id
= "RestrictNamespaces=~uts",
1416 .json_field
= "RestrictNamespaces_uts",
1417 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1418 .description_good
= "Service cannot create hostname namespaces",
1419 .description_bad
= "Service may create hostname namespaces",
1422 .assess
= assess_restrict_namespaces
,
1423 .parameter
= CLONE_NEWUTS
,
1426 .id
= "RestrictAddressFamilies=~AF_(INET|INET6)",
1427 .json_field
= "RestrictAddressFamilies_AF_INET_INET6",
1428 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1429 .description_good
= "Service cannot allocate Internet sockets",
1430 .description_bad
= "Service may allocate Internet sockets",
1433 .assess
= assess_bool
,
1434 .offset
= offsetof(SecurityInfo
, restrict_address_family_inet
),
1437 .id
= "RestrictAddressFamilies=~AF_UNIX",
1438 .json_field
= "RestrictAddressFamilies_AF_UNIX",
1439 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1440 .description_good
= "Service cannot allocate local sockets",
1441 .description_bad
= "Service may allocate local sockets",
1444 .assess
= assess_bool
,
1445 .offset
= offsetof(SecurityInfo
, restrict_address_family_unix
),
1448 .id
= "RestrictAddressFamilies=~AF_NETLINK",
1449 .json_field
= "RestrictAddressFamilies_AF_NETLINK",
1450 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1451 .description_good
= "Service cannot allocate netlink sockets",
1452 .description_bad
= "Service may allocate netlink sockets",
1455 .assess
= assess_bool
,
1456 .offset
= offsetof(SecurityInfo
, restrict_address_family_netlink
),
1459 .id
= "RestrictAddressFamilies=~AF_PACKET",
1460 .json_field
= "RestrictAddressFamilies_AF_PACKET",
1461 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1462 .description_good
= "Service cannot allocate packet sockets",
1463 .description_bad
= "Service may allocate packet sockets",
1466 .assess
= assess_bool
,
1467 .offset
= offsetof(SecurityInfo
, restrict_address_family_packet
),
1470 .id
= "RestrictAddressFamilies=~…",
1471 .json_field
= "RestrictAddressFamilies_OTHER",
1472 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1473 .description_good
= "Service cannot allocate exotic sockets",
1474 .description_bad
= "Service may allocate exotic sockets",
1477 .assess
= assess_bool
,
1478 .offset
= offsetof(SecurityInfo
, restrict_address_family_other
),
1482 .id
= "SystemCallArchitectures=",
1483 .json_field
= "SystemCallArchitectures",
1484 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallArchitectures=",
1487 .assess
= assess_system_call_architectures
,
1490 .id
= "SystemCallFilter=~@swap",
1491 .json_field
= "SystemCallFilter_swap",
1492 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1495 .assess
= assess_system_call_filter
,
1496 .parameter
= SYSCALL_FILTER_SET_SWAP
,
1499 .id
= "SystemCallFilter=~@obsolete",
1500 .json_field
= "SystemCallFilter_obsolete",
1501 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1504 .assess
= assess_system_call_filter
,
1505 .parameter
= SYSCALL_FILTER_SET_OBSOLETE
,
1508 .id
= "SystemCallFilter=~@clock",
1509 .json_field
= "SystemCallFilter_clock",
1510 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1513 .assess
= assess_system_call_filter
,
1514 .parameter
= SYSCALL_FILTER_SET_CLOCK
,
1517 .id
= "SystemCallFilter=~@cpu-emulation",
1518 .json_field
= "SystemCallFilter_cpu_emulation",
1519 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1522 .assess
= assess_system_call_filter
,
1523 .parameter
= SYSCALL_FILTER_SET_CPU_EMULATION
,
1526 .id
= "SystemCallFilter=~@debug",
1527 .json_field
= "SystemCallFilter_debug",
1528 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1531 .assess
= assess_system_call_filter
,
1532 .parameter
= SYSCALL_FILTER_SET_DEBUG
,
1535 .id
= "SystemCallFilter=~@mount",
1536 .json_field
= "SystemCallFilter_mount",
1537 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1540 .assess
= assess_system_call_filter
,
1541 .parameter
= SYSCALL_FILTER_SET_MOUNT
,
1544 .id
= "SystemCallFilter=~@module",
1545 .json_field
= "SystemCallFilter_module",
1546 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1549 .assess
= assess_system_call_filter
,
1550 .parameter
= SYSCALL_FILTER_SET_MODULE
,
1553 .id
= "SystemCallFilter=~@raw-io",
1554 .json_field
= "SystemCallFilter_raw_io",
1555 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1558 .assess
= assess_system_call_filter
,
1559 .parameter
= SYSCALL_FILTER_SET_RAW_IO
,
1562 .id
= "SystemCallFilter=~@reboot",
1563 .json_field
= "SystemCallFilter_reboot",
1564 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1567 .assess
= assess_system_call_filter
,
1568 .parameter
= SYSCALL_FILTER_SET_REBOOT
,
1571 .id
= "SystemCallFilter=~@privileged",
1572 .json_field
= "SystemCallFilter_privileged",
1573 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1576 .assess
= assess_system_call_filter
,
1577 .parameter
= SYSCALL_FILTER_SET_PRIVILEGED
,
1580 .id
= "SystemCallFilter=~@resources",
1581 .json_field
= "SystemCallFilter_resources",
1582 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1585 .assess
= assess_system_call_filter
,
1586 .parameter
= SYSCALL_FILTER_SET_RESOURCES
,
1590 .id
= "IPAddressDeny=",
1591 .json_field
= "IPAddressDeny",
1592 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#IPAddressAllow=ADDRESS%5B/PREFIXLENGTH%5D…",
1595 .assess
= assess_ip_address_allow
,
1598 .id
= "DeviceAllow=",
1599 .json_field
= "DeviceAllow",
1600 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#DeviceAllow=",
1603 .assess
= assess_device_allow
,
1606 .id
= "AmbientCapabilities=",
1607 .json_field
= "AmbientCapabilities",
1608 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#AmbientCapabilities=",
1609 .description_good
= "Service process does not receive ambient capabilities",
1610 .description_bad
= "Service process receives ambient capabilities",
1613 .assess
= assess_ambient_capabilities
,
1617 static sd_json_variant
* security_assessor_find_in_policy(const struct security_assessor
*a
, sd_json_variant
*policy
, const char *name
) {
1618 sd_json_variant
*item
;
1623 if (!sd_json_variant_is_object(policy
)) {
1624 log_debug("Specified policy is not a JSON object, ignoring.");
1628 item
= sd_json_variant_by_key(policy
, a
->json_field
);
1631 if (!sd_json_variant_is_object(item
)) {
1632 log_debug("Item for '%s' in policy JSON object is not an object, ignoring.", a
->id
);
1636 return name
? sd_json_variant_by_key(item
, name
) : item
;
1639 static uint64_t access_weight(const struct security_assessor
*a
, sd_json_variant
*policy
) {
1640 sd_json_variant
*val
;
1644 val
= security_assessor_find_in_policy(a
, policy
, "weight");
1646 if (sd_json_variant_is_unsigned(val
))
1647 return sd_json_variant_unsigned(val
);
1648 log_debug("JSON field 'weight' of policy for %s is not an unsigned integer, ignoring.", a
->id
);
1654 static uint64_t access_range(const struct security_assessor
*a
, sd_json_variant
*policy
) {
1655 sd_json_variant
*val
;
1659 val
= security_assessor_find_in_policy(a
, policy
, "range");
1661 if (sd_json_variant_is_unsigned(val
))
1662 return sd_json_variant_unsigned(val
);
1663 log_debug("JSON field 'range' of policy for %s is not an unsigned integer, ignoring.", a
->id
);
1669 static const char *access_description_na(const struct security_assessor
*a
, sd_json_variant
*policy
) {
1670 sd_json_variant
*val
;
1674 val
= security_assessor_find_in_policy(a
, policy
, "description_na");
1676 if (sd_json_variant_is_string(val
))
1677 return sd_json_variant_string(val
);
1678 log_debug("JSON field 'description_na' of policy for %s is not a string, ignoring.", a
->id
);
1681 return a
->description_na
;
1684 static const char *access_description_good(const struct security_assessor
*a
, sd_json_variant
*policy
) {
1685 sd_json_variant
*val
;
1689 val
= security_assessor_find_in_policy(a
, policy
, "description_good");
1691 if (sd_json_variant_is_string(val
))
1692 return sd_json_variant_string(val
);
1693 log_debug("JSON field 'description_good' of policy for %s is not a string, ignoring.", a
->id
);
1696 return a
->description_good
;
1699 static const char *access_description_bad(const struct security_assessor
*a
, sd_json_variant
*policy
) {
1700 sd_json_variant
*val
;
1704 val
= security_assessor_find_in_policy(a
, policy
, "description_bad");
1706 if (sd_json_variant_is_string(val
))
1707 return sd_json_variant_string(val
);
1708 log_debug("JSON field 'description_bad' of policy for %s is not a string, ignoring.", a
->id
);
1711 return a
->description_bad
;
1714 static int assess(const SecurityInfo
*info
,
1715 Table
*overview_table
,
1716 AnalyzeSecurityFlags flags
,
1718 sd_json_variant
*policy
,
1719 PagerFlags pager_flags
,
1720 sd_json_format_flags_t json_format_flags
) {
1722 static const struct {
1725 const char* (*color
)(void);
1727 } badness_table
[] = {
1728 { 100, "DANGEROUS", ansi_highlight_red
, GLYPH_DEPRESSED_SMILEY
},
1729 { 90, "UNSAFE", ansi_highlight_red
, GLYPH_UNHAPPY_SMILEY
},
1730 { 75, "EXPOSED", ansi_highlight_yellow
, GLYPH_SLIGHTLY_UNHAPPY_SMILEY
},
1731 { 50, "MEDIUM", NULL
, GLYPH_NEUTRAL_SMILEY
},
1732 { 10, "OK", ansi_highlight_green
, GLYPH_SLIGHTLY_HAPPY_SMILEY
},
1733 { 1, "SAFE", ansi_highlight_green
, GLYPH_HAPPY_SMILEY
},
1734 { 0, "PERFECT", ansi_highlight_green
, GLYPH_ECSTATIC_SMILEY
},
1737 uint64_t badness_sum
= 0, weight_sum
= 0, exposure
;
1738 _cleanup_(table_unrefp
) Table
*details_table
= NULL
;
1742 if (!FLAGS_SET(flags
, ANALYZE_SECURITY_SHORT
)) {
1743 details_table
= table_new(" ", "name", "json_field", "description", "weight", "badness", "range", "exposure");
1747 r
= table_set_json_field_name(details_table
, 0, "set");
1749 return log_error_errno(r
, "Failed to set JSON field name of column 0: %m");
1751 (void) table_set_sort(details_table
, (size_t) 3, (size_t) 1);
1752 (void) table_set_reverse(details_table
, 3, true);
1754 if (getenv_bool("SYSTEMD_ANALYZE_DEBUG") <= 0)
1755 (void) table_set_display(details_table
, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 7);
1758 FOREACH_ELEMENT(a
, security_assessor_table
) {
1759 _cleanup_free_
char *d
= NULL
;
1762 uint64_t weight
= access_weight(a
, policy
);
1763 uint64_t range
= access_range(a
, policy
);
1765 data
= (uint8_t*) info
+ a
->offset
;
1767 if (a
->default_dependencies_only
&& !info
->default_dependencies
) {
1768 badness
= UINT64_MAX
;
1769 d
= strdup("Service runs in special boot phase, option is not appropriate");
1772 } else if (weight
== 0) {
1773 badness
= UINT64_MAX
;
1774 d
= strdup("Option excluded by policy, skipping");
1778 r
= a
->assess(a
, info
, data
, &badness
, &d
);
1785 if (badness
!= UINT64_MAX
) {
1786 assert(badness
<= range
);
1788 badness_sum
+= DIV_ROUND_UP(badness
* weight
, range
);
1789 weight_sum
+= weight
;
1792 if (details_table
) {
1793 const char *description
, *color
= NULL
;
1796 if (badness
== UINT64_MAX
) {
1798 description
= access_description_na(a
, policy
);
1800 } else if (badness
== a
->range
) {
1802 description
= access_description_bad(a
, policy
);
1803 color
= ansi_highlight_red();
1804 } else if (badness
== 0) {
1806 description
= access_description_good(a
, policy
);
1807 color
= ansi_highlight_green();
1811 color
= ansi_highlight_red();
1817 if (checkmark
< 0) {
1818 r
= table_add_many(details_table
, TABLE_EMPTY
);
1820 return table_log_add_error(r
);
1822 r
= table_add_many(details_table
,
1823 TABLE_BOOLEAN_CHECKMARK
, checkmark
> 0,
1824 TABLE_SET_MINIMUM_WIDTH
, 1,
1825 TABLE_SET_MAXIMUM_WIDTH
, 1,
1826 TABLE_SET_ELLIPSIZE_PERCENT
, 0,
1827 TABLE_SET_COLOR
, color
);
1829 return table_log_add_error(r
);
1832 r
= table_add_many(details_table
,
1833 TABLE_STRING
, a
->id
, TABLE_SET_URL
, a
->url
,
1834 TABLE_STRING
, a
->json_field
,
1835 TABLE_STRING
, description
,
1836 TABLE_UINT64
, weight
, TABLE_SET_ALIGN_PERCENT
, 100,
1837 TABLE_UINT64
, badness
, TABLE_SET_ALIGN_PERCENT
, 100,
1838 TABLE_UINT64
, range
, TABLE_SET_ALIGN_PERCENT
, 100,
1839 TABLE_EMPTY
, TABLE_SET_ALIGN_PERCENT
, 100);
1841 return table_log_add_error(r
);
1845 assert(weight_sum
> 0);
1847 if (details_table
) {
1850 for (row
= 1; row
< table_get_rows(details_table
); row
++) {
1851 char buf
[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1];
1852 const uint64_t *weight
, *badness
, *range
;
1856 assert_se(weight
= table_get_at(details_table
, row
, 4));
1857 assert_se(badness
= table_get_at(details_table
, row
, 5));
1858 assert_se(range
= table_get_at(details_table
, row
, 6));
1860 if (*badness
== UINT64_MAX
|| *badness
== 0)
1863 assert_se(cell
= table_get_cell(details_table
, row
, 7));
1865 x
= DIV_ROUND_UP(DIV_ROUND_UP(*badness
* *weight
* 100U, *range
), weight_sum
);
1866 xsprintf(buf
, "%" PRIu64
".%" PRIu64
, x
/ 10, x
% 10);
1868 r
= table_update(details_table
, cell
, TABLE_STRING
, buf
);
1870 return log_error_errno(r
, "Failed to update cell in table: %m");
1873 if (!sd_json_format_enabled(json_format_flags
)) {
1874 r
= table_hide_column_from_display(details_table
, (size_t) 2);
1876 return log_error_errno(r
, "Failed to set columns to display: %m");
1879 r
= table_print_with_pager(details_table
, json_format_flags
, pager_flags
, /* show_header= */true);
1881 return log_error_errno(r
, "Failed to output table: %m");
1884 exposure
= DIV_ROUND_UP(badness_sum
* 100U, weight_sum
);
1886 for (i
= 0; i
< ELEMENTSOF(badness_table
); i
++)
1887 if (exposure
>= badness_table
[i
].exposure
)
1890 assert(i
< ELEMENTSOF(badness_table
));
1892 if (details_table
&& !sd_json_format_enabled(json_format_flags
)) {
1893 _cleanup_free_
char *clickable
= NULL
;
1896 /* If we shall output the details table, also print the brief summary underneath */
1898 if (info
->fragment_path
) {
1899 r
= terminal_urlify_path(info
->fragment_path
, info
->id
, &clickable
);
1907 printf("\n%s %sOverall exposure level for %s%s: %s%" PRIu64
".%" PRIu64
" %s%s %s\n",
1908 glyph(GLYPH_ARROW_RIGHT
),
1912 badness_table
[i
].color
? badness_table
[i
].color() : "",
1913 exposure
/ 10, exposure
% 10,
1914 badness_table
[i
].name
,
1916 glyph(badness_table
[i
].smiley
));
1921 if (overview_table
) {
1922 char buf
[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1];
1923 _cleanup_free_
char *url
= NULL
;
1925 if (info
->fragment_path
) {
1926 r
= file_url_from_path(info
->fragment_path
, &url
);
1928 return log_error_errno(r
, "Failed to generate URL from path: %m");
1931 xsprintf(buf
, "%" PRIu64
".%" PRIu64
, exposure
/ 10, exposure
% 10);
1933 r
= table_add_many(overview_table
,
1934 TABLE_STRING
, info
->id
,
1937 TABLE_SET_ALIGN_PERCENT
, 100,
1938 TABLE_STRING
, badness_table
[i
].name
,
1939 TABLE_SET_COLOR
, badness_table
[i
].color
? badness_table
[i
].color() : "",
1940 TABLE_STRING
, glyph(badness_table
[i
].smiley
));
1942 return table_log_add_error(r
);
1945 /* Return error when overall exposure level is over threshold */
1946 if (exposure
> threshold
)
1952 static int property_read_restrict_namespaces(
1956 sd_bus_error
*error
,
1959 SecurityInfo
*info
= ASSERT_PTR(userdata
);
1961 uint64_t namespaces
;
1967 r
= sd_bus_message_read(m
, "t", &namespaces
);
1971 info
->restrict_namespaces
= (unsigned long long) namespaces
;
1976 static int property_read_umask(
1980 sd_bus_error
*error
,
1983 SecurityInfo
*info
= ASSERT_PTR(userdata
);
1991 r
= sd_bus_message_read(m
, "u", &umask
);
1995 info
->_umask
= (mode_t
) umask
;
2000 static int property_read_restrict_address_families(
2004 sd_bus_error
*error
,
2007 SecurityInfo
*info
= userdata
;
2014 r
= sd_bus_message_enter_container(m
, 'r', "bas");
2018 r
= sd_bus_message_read(m
, "b", &allow_list
);
2022 info
->restrict_address_family_inet
=
2023 info
->restrict_address_family_unix
=
2024 info
->restrict_address_family_netlink
=
2025 info
->restrict_address_family_packet
=
2026 info
->restrict_address_family_other
= allow_list
;
2028 r
= sd_bus_message_enter_container(m
, 'a', "s");
2035 r
= sd_bus_message_read(m
, "s", &name
);
2041 if (STR_IN_SET(name
, "AF_INET", "AF_INET6"))
2042 info
->restrict_address_family_inet
= !allow_list
;
2043 else if (streq(name
, "AF_UNIX"))
2044 info
->restrict_address_family_unix
= !allow_list
;
2045 else if (streq(name
, "AF_NETLINK"))
2046 info
->restrict_address_family_netlink
= !allow_list
;
2047 else if (streq(name
, "AF_PACKET"))
2048 info
->restrict_address_family_packet
= !allow_list
;
2050 info
->restrict_address_family_other
= !allow_list
;
2053 r
= sd_bus_message_exit_container(m
);
2057 return sd_bus_message_exit_container(m
);
2060 static int property_read_syscall_archs(
2064 sd_bus_error
*error
,
2067 SecurityInfo
*info
= ASSERT_PTR(userdata
);
2074 r
= sd_bus_message_enter_container(m
, 'a', "s");
2081 r
= sd_bus_message_read(m
, "s", &name
);
2087 r
= set_put_strdup(&info
->system_call_architectures
, name
);
2092 return sd_bus_message_exit_container(m
);
2095 static int property_read_system_call_filter(
2099 sd_bus_error
*error
,
2102 SecurityInfo
*info
= userdata
;
2109 r
= sd_bus_message_enter_container(m
, 'r', "bas");
2113 r
= sd_bus_message_read(m
, "b", &allow_list
);
2117 info
->system_call_filter_allow_list
= allow_list
;
2119 r
= sd_bus_message_enter_container(m
, 'a', "s");
2126 r
= sd_bus_message_read(m
, "s", &name
);
2132 /* ignore errno or action after colon */
2133 r
= set_put_strndup(&info
->system_call_filter
, name
, strchrnul(name
, ':') - name
);
2138 r
= sd_bus_message_exit_container(m
);
2142 return sd_bus_message_exit_container(m
);
2145 static int property_read_ip_address_allow(
2149 sd_bus_error
*error
,
2152 SecurityInfo
*info
= userdata
;
2153 bool deny_ipv4
= false, deny_ipv6
= false;
2160 r
= sd_bus_message_enter_container(m
, 'a', "(iayu)");
2170 r
= sd_bus_message_enter_container(m
, 'r', "iayu");
2176 r
= sd_bus_message_read(m
, "i", &family
);
2180 r
= sd_bus_message_read_array(m
, 'y', &data
, &size
);
2184 r
= sd_bus_message_read(m
, "u", &prefixlen
);
2188 r
= sd_bus_message_exit_container(m
);
2192 if (streq(member
, "IPAddressAllow")) {
2193 union in_addr_union u
;
2195 if (family
== AF_INET
&& size
== 4 && prefixlen
== 8)
2196 memcpy(&u
.in
, data
, size
);
2197 else if (family
== AF_INET6
&& size
== 16 && prefixlen
== 128)
2198 memcpy(&u
.in6
, data
, size
);
2200 info
->ip_address_allow_other
= true;
2204 if (in_addr_is_localhost(family
, &u
))
2205 info
->ip_address_allow_localhost
= true;
2207 info
->ip_address_allow_other
= true;
2209 assert(streq(member
, "IPAddressDeny"));
2211 if (family
== AF_INET
&& size
== 4 && prefixlen
== 0)
2213 else if (family
== AF_INET6
&& size
== 16 && prefixlen
== 0)
2218 info
->ip_address_deny_all
= deny_ipv4
&& deny_ipv6
;
2220 return sd_bus_message_exit_container(m
);
2223 static int property_read_ip_filters(
2227 sd_bus_error
*error
,
2230 SecurityInfo
*info
= userdata
;
2231 _cleanup_strv_free_
char **l
= NULL
;
2238 r
= sd_bus_message_read_strv(m
, &l
);
2242 if (streq(member
, "IPIngressFilterPath"))
2243 info
->ip_filters_custom_ingress
= !strv_isempty(l
);
2244 else if (streq(member
, "IPEgressFilterPath"))
2245 info
->ip_filters_custom_egress
= !strv_isempty(l
);
2250 static int property_read_device_allow(
2254 sd_bus_error
*error
,
2257 SecurityInfo
*info
= userdata
;
2264 r
= sd_bus_message_enter_container(m
, 'a', "(ss)");
2269 const char *name
, *policy
;
2271 r
= sd_bus_message_read(m
, "(ss)", &name
, &policy
);
2277 r
= strv_extendf(&info
->device_allow
, "%s:%s", name
, policy
);
2282 return sd_bus_message_exit_container(m
);
2285 static int acquire_security_info(sd_bus
*bus
, const char *name
, SecurityInfo
*info
, AnalyzeSecurityFlags flags
) {
2287 static const struct bus_properties_map security_map
[] = {
2288 { "AmbientCapabilities", "t", NULL
, offsetof(SecurityInfo
, ambient_capabilities
) },
2289 { "CapabilityBoundingSet", "t", NULL
, offsetof(SecurityInfo
, capability_bounding_set
) },
2290 { "DefaultDependencies", "b", NULL
, offsetof(SecurityInfo
, default_dependencies
) },
2291 { "Delegate", "b", NULL
, offsetof(SecurityInfo
, delegate
) },
2292 { "DeviceAllow", "a(ss)", property_read_device_allow
, 0 },
2293 { "DevicePolicy", "s", NULL
, offsetof(SecurityInfo
, device_policy
) },
2294 { "DynamicUser", "b", NULL
, offsetof(SecurityInfo
, dynamic_user
) },
2295 { "FragmentPath", "s", NULL
, offsetof(SecurityInfo
, fragment_path
) },
2296 { "IPAddressAllow", "a(iayu)", property_read_ip_address_allow
, 0 },
2297 { "IPAddressDeny", "a(iayu)", property_read_ip_address_allow
, 0 },
2298 { "IPIngressFilterPath", "as", property_read_ip_filters
, 0 },
2299 { "IPEgressFilterPath", "as", property_read_ip_filters
, 0 },
2300 { "Id", "s", NULL
, offsetof(SecurityInfo
, id
) },
2301 { "KeyringMode", "s", NULL
, offsetof(SecurityInfo
, keyring_mode
) },
2302 { "ProtectProc", "s", NULL
, offsetof(SecurityInfo
, protect_proc
) },
2303 { "ProcSubset", "s", NULL
, offsetof(SecurityInfo
, proc_subset
) },
2304 { "LoadState", "s", NULL
, offsetof(SecurityInfo
, load_state
) },
2305 { "LockPersonality", "b", NULL
, offsetof(SecurityInfo
, lock_personality
) },
2306 { "MemoryDenyWriteExecute", "b", NULL
, offsetof(SecurityInfo
, memory_deny_write_execute
) },
2307 { "NoNewPrivileges", "b", NULL
, offsetof(SecurityInfo
, no_new_privileges
) },
2308 { "NotifyAccess", "s", NULL
, offsetof(SecurityInfo
, notify_access
) },
2309 { "PrivateDevices", "b", NULL
, offsetof(SecurityInfo
, private_devices
) },
2310 { "PrivateMounts", "b", NULL
, offsetof(SecurityInfo
, private_mounts
) },
2311 { "PrivateNetwork", "b", NULL
, offsetof(SecurityInfo
, private_network
) },
2312 { "PrivateTmp", "b", NULL
, offsetof(SecurityInfo
, private_tmp
) },
2313 { "PrivateUsers", "b", NULL
, offsetof(SecurityInfo
, private_users
) },
2314 { "ProtectControlGroups", "b", NULL
, offsetof(SecurityInfo
, protect_control_groups
) },
2315 { "ProtectHome", "s", NULL
, offsetof(SecurityInfo
, protect_home
) },
2316 { "ProtectHostname", "b", NULL
, offsetof(SecurityInfo
, protect_hostname
) },
2317 { "ProtectKernelModules", "b", NULL
, offsetof(SecurityInfo
, protect_kernel_modules
) },
2318 { "ProtectKernelTunables", "b", NULL
, offsetof(SecurityInfo
, protect_kernel_tunables
) },
2319 { "ProtectKernelLogs", "b", NULL
, offsetof(SecurityInfo
, protect_kernel_logs
) },
2320 { "ProtectClock", "b", NULL
, offsetof(SecurityInfo
, protect_clock
) },
2321 { "ProtectSystem", "s", NULL
, offsetof(SecurityInfo
, protect_system
) },
2322 { "RemoveIPC", "b", NULL
, offsetof(SecurityInfo
, remove_ipc
) },
2323 { "RestrictAddressFamilies", "(bas)", property_read_restrict_address_families
, 0 },
2324 { "RestrictNamespaces", "t", property_read_restrict_namespaces
, 0 },
2325 { "RestrictRealtime", "b", NULL
, offsetof(SecurityInfo
, restrict_realtime
) },
2326 { "RestrictSUIDSGID", "b", NULL
, offsetof(SecurityInfo
, restrict_suid_sgid
) },
2327 { "RootDirectory", "s", NULL
, offsetof(SecurityInfo
, root_directory
) },
2328 { "RootImage", "s", NULL
, offsetof(SecurityInfo
, root_image
) },
2329 { "SupplementaryGroups", "as", NULL
, offsetof(SecurityInfo
, supplementary_groups
) },
2330 { "SystemCallArchitectures", "as", property_read_syscall_archs
, 0 },
2331 { "SystemCallFilter", "(as)", property_read_system_call_filter
, 0 },
2332 { "Type", "s", NULL
, offsetof(SecurityInfo
, type
) },
2333 { "UMask", "u", property_read_umask
, 0 },
2334 { "User", "s", NULL
, offsetof(SecurityInfo
, user
) },
2338 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2339 _cleanup_free_
char *path
= NULL
;
2342 /* Note: this mangles *info on failure! */
2348 path
= unit_dbus_path_from_name(name
);
2352 r
= bus_map_all_properties(
2354 "org.freedesktop.systemd1",
2357 BUS_MAP_STRDUP
| BUS_MAP_BOOLEAN_AS_BOOL
,
2362 return log_error_errno(r
, "Failed to get unit properties: %s", bus_error_message(&error
, r
));
2364 if (!streq_ptr(info
->load_state
, "loaded")) {
2366 if (FLAGS_SET(flags
, ANALYZE_SECURITY_ONLY_LOADED
))
2367 return -EMEDIUMTYPE
;
2369 if (streq_ptr(info
->load_state
, "not-found"))
2370 log_error("Unit %s not found, cannot analyze.", name
);
2371 else if (streq_ptr(info
->load_state
, "masked"))
2372 log_error("Unit %s is masked, cannot analyze.", name
);
2374 log_error("Unit %s not loaded properly, cannot analyze.", name
);
2379 if (FLAGS_SET(flags
, ANALYZE_SECURITY_ONLY_LONG_RUNNING
) && streq_ptr(info
->type
, "oneshot"))
2380 return -EMEDIUMTYPE
;
2382 if (info
->private_devices
||
2383 info
->private_tmp
||
2384 info
->protect_control_groups
||
2385 info
->protect_kernel_tunables
||
2386 info
->protect_kernel_modules
||
2387 !streq_ptr(info
->protect_home
, "no") ||
2388 !streq_ptr(info
->protect_system
, "no") ||
2390 info
->private_mounts
= true;
2392 if (info
->protect_kernel_modules
)
2393 info
->capability_bounding_set
&= ~(UINT64_C(1) << CAP_SYS_MODULE
);
2395 if (info
->protect_kernel_logs
)
2396 info
->capability_bounding_set
&= ~(UINT64_C(1) << CAP_SYSLOG
);
2398 if (info
->protect_clock
)
2399 info
->capability_bounding_set
&= ~((UINT64_C(1) << CAP_SYS_TIME
) |
2400 (UINT64_C(1) << CAP_WAKE_ALARM
));
2402 if (info
->private_devices
)
2403 info
->capability_bounding_set
&= ~((UINT64_C(1) << CAP_MKNOD
) |
2404 (UINT64_C(1) << CAP_SYS_RAWIO
));
2409 static int analyze_security_one(sd_bus
*bus
,
2411 Table
*overview_table
,
2412 AnalyzeSecurityFlags flags
,
2414 sd_json_variant
*policy
,
2415 PagerFlags pager_flags
,
2416 sd_json_format_flags_t json_format_flags
) {
2418 _cleanup_(security_info_freep
) SecurityInfo
*info
= security_info_new();
2427 r
= acquire_security_info(bus
, name
, info
, flags
);
2428 if (r
== -EMEDIUMTYPE
) /* Ignore this one because not loaded or Type is oneshot */
2433 r
= assess(info
, overview_table
, flags
, threshold
, policy
, pager_flags
, json_format_flags
);
2440 /* Refactoring SecurityInfo so that it can make use of existing struct variables instead of reading from dbus */
2441 static int get_security_info(Unit
*u
, ExecContext
*c
, CGroupContext
*g
, SecurityInfo
**ret_info
) {
2444 _cleanup_(security_info_freep
) SecurityInfo
*info
= security_info_new();
2450 info
->id
= strdup(u
->id
);
2454 if (unit_type_to_string(u
->type
)) {
2455 info
->type
= strdup(unit_type_to_string(u
->type
));
2459 if (unit_load_state_to_string(u
->load_state
)) {
2460 info
->load_state
= strdup(unit_load_state_to_string(u
->load_state
));
2461 if (!info
->load_state
)
2464 if (u
->fragment_path
) {
2465 info
->fragment_path
= strdup(u
->fragment_path
);
2466 if (!info
->fragment_path
)
2469 info
->default_dependencies
= u
->default_dependencies
;
2470 if (u
->type
== UNIT_SERVICE
&& notify_access_to_string(SERVICE(u
)->notify_access
)) {
2471 info
->notify_access
= strdup(notify_access_to_string(SERVICE(u
)->notify_access
));
2472 if (!info
->notify_access
)
2478 info
->ambient_capabilities
= c
->capability_ambient_set
;
2479 info
->capability_bounding_set
= c
->capability_bounding_set
;
2481 info
->user
= strdup(c
->user
);
2485 if (c
->supplementary_groups
) {
2486 info
->supplementary_groups
= strv_copy(c
->supplementary_groups
);
2487 if (!info
->supplementary_groups
)
2490 info
->dynamic_user
= c
->dynamic_user
;
2491 if (exec_keyring_mode_to_string(c
->keyring_mode
)) {
2492 info
->keyring_mode
= strdup(exec_keyring_mode_to_string(c
->keyring_mode
));
2493 if (!info
->keyring_mode
)
2496 if (protect_proc_to_string(c
->protect_proc
)) {
2497 info
->protect_proc
= strdup(protect_proc_to_string(c
->protect_proc
));
2498 if (!info
->protect_proc
)
2501 if (proc_subset_to_string(c
->proc_subset
)) {
2502 info
->proc_subset
= strdup(proc_subset_to_string(c
->proc_subset
));
2503 if (!info
->proc_subset
)
2506 info
->lock_personality
= c
->lock_personality
;
2507 info
->memory_deny_write_execute
= c
->memory_deny_write_execute
;
2508 info
->no_new_privileges
= c
->no_new_privileges
;
2509 info
->protect_hostname
= c
->protect_hostname
;
2510 info
->private_devices
= c
->private_devices
;
2511 info
->private_mounts
= c
->private_mounts
;
2512 info
->private_network
= c
->private_network
;
2513 info
->private_tmp
= c
->private_tmp
;
2514 info
->private_users
= c
->private_users
;
2515 info
->protect_control_groups
= c
->protect_control_groups
;
2516 info
->protect_kernel_modules
= c
->protect_kernel_modules
;
2517 info
->protect_kernel_tunables
= c
->protect_kernel_tunables
;
2518 info
->protect_kernel_logs
= c
->protect_kernel_logs
;
2519 info
->protect_clock
= c
->protect_clock
;
2520 if (protect_home_to_string(c
->protect_home
)) {
2521 info
->protect_home
= strdup(protect_home_to_string(c
->protect_home
));
2522 if (!info
->protect_home
)
2525 if (protect_system_to_string(c
->protect_system
)) {
2526 info
->protect_system
= strdup(protect_system_to_string(c
->protect_system
));
2527 if (!info
->protect_system
)
2530 info
->remove_ipc
= c
->remove_ipc
;
2531 info
->restrict_address_family_inet
=
2532 info
->restrict_address_family_unix
=
2533 info
->restrict_address_family_netlink
=
2534 info
->restrict_address_family_packet
=
2535 info
->restrict_address_family_other
=
2536 c
->address_families_allow_list
;
2539 SET_FOREACH(key
, c
->address_families
) {
2540 int family
= PTR_TO_INT(key
);
2543 if (IN_SET(family
, AF_INET
, AF_INET6
))
2544 info
->restrict_address_family_inet
= !c
->address_families_allow_list
;
2545 else if (family
== AF_UNIX
)
2546 info
->restrict_address_family_unix
= !c
->address_families_allow_list
;
2547 else if (family
== AF_NETLINK
)
2548 info
->restrict_address_family_netlink
= !c
->address_families_allow_list
;
2549 else if (family
== AF_PACKET
)
2550 info
->restrict_address_family_packet
= !c
->address_families_allow_list
;
2552 info
->restrict_address_family_other
= !c
->address_families_allow_list
;
2555 info
->restrict_namespaces
= c
->restrict_namespaces
;
2556 info
->restrict_realtime
= c
->restrict_realtime
;
2557 info
->restrict_suid_sgid
= c
->restrict_suid_sgid
;
2558 if (c
->root_directory
) {
2559 info
->root_directory
= strdup(c
->root_directory
);
2560 if (!info
->root_directory
)
2563 if (c
->root_image
) {
2564 info
->root_image
= strdup(c
->root_image
);
2565 if (!info
->root_image
)
2568 info
->_umask
= c
->umask
;
2571 SET_FOREACH(key
, c
->syscall_archs
) {
2574 name
= seccomp_arch_to_string(PTR_TO_UINT32(key
) - 1);
2578 if (set_put_strdup(&info
->system_call_architectures
, name
) < 0)
2582 info
->system_call_filter_allow_list
= c
->syscall_allow_list
;
2585 HASHMAP_FOREACH_KEY(num
, id
, c
->syscall_filter
) {
2586 _cleanup_free_
char *name
= NULL
;
2588 if (info
->system_call_filter_allow_list
&& PTR_TO_INT(num
) >= 0)
2591 name
= seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE
, PTR_TO_INT(id
) - 1);
2595 if (set_ensure_consume(&info
->system_call_filter
, &string_hash_ops_free
, TAKE_PTR(name
)) < 0)
2602 info
->delegate
= g
->delegate
;
2603 if (cgroup_device_policy_to_string(g
->device_policy
)) {
2604 info
->device_policy
= strdup(cgroup_device_policy_to_string(g
->device_policy
));
2605 if (!info
->device_policy
)
2609 struct in_addr_prefix
*i
;
2610 bool deny_ipv4
= false, deny_ipv6
= false;
2612 SET_FOREACH(i
, g
->ip_address_deny
) {
2613 if (i
->family
== AF_INET
&& i
->prefixlen
== 0)
2615 else if (i
->family
== AF_INET6
&& i
->prefixlen
== 0)
2618 info
->ip_address_deny_all
= deny_ipv4
&& deny_ipv6
;
2620 info
->ip_address_allow_localhost
= info
->ip_address_allow_other
= false;
2621 SET_FOREACH(i
, g
->ip_address_allow
) {
2622 if (in_addr_is_localhost(i
->family
, &i
->address
))
2623 info
->ip_address_allow_localhost
= true;
2625 info
->ip_address_allow_other
= true;
2628 info
->ip_filters_custom_ingress
= !strv_isempty(g
->ip_filters_ingress
);
2629 info
->ip_filters_custom_egress
= !strv_isempty(g
->ip_filters_egress
);
2631 LIST_FOREACH(device_allow
, a
, g
->device_allow
)
2632 if (strv_extendf(&info
->device_allow
,
2635 cgroup_device_permissions_to_string(a
->permissions
)) < 0)
2639 *ret_info
= TAKE_PTR(info
);
2644 static int offline_security_check(Unit
*u
,
2646 sd_json_variant
*policy
,
2647 PagerFlags pager_flags
,
2648 sd_json_format_flags_t json_format_flags
) {
2650 _cleanup_(table_unrefp
) Table
*overview_table
= NULL
;
2651 AnalyzeSecurityFlags flags
= 0;
2652 _cleanup_(security_info_freep
) SecurityInfo
*info
= NULL
;
2658 unit_dump(u
, stdout
, "\t");
2660 r
= get_security_info(u
, unit_get_exec_context(u
), unit_get_cgroup_context(u
), &info
);
2664 return assess(info
, overview_table
, flags
, threshold
, policy
, pager_flags
, json_format_flags
);
2667 static int offline_security_checks(
2669 sd_json_variant
*policy
,
2672 bool run_generators
,
2675 const char *profile
,
2676 PagerFlags pager_flags
,
2677 sd_json_format_flags_t json_format_flags
) {
2679 const ManagerTestRunFlags flags
=
2680 MANAGER_TEST_RUN_MINIMAL
|
2681 MANAGER_TEST_RUN_ENV_GENERATORS
|
2682 MANAGER_TEST_RUN_IGNORE_DEPENDENCIES
|
2683 MANAGER_TEST_DONT_OPEN_EXECUTOR
|
2684 run_generators
* MANAGER_TEST_RUN_GENERATORS
;
2686 _cleanup_(manager_freep
) Manager
*m
= NULL
;
2687 Unit
*units
[strv_length(filenames
)];
2691 if (strv_isempty(filenames
))
2694 r
= verify_set_unit_path(filenames
);
2696 return log_error_errno(r
, "Failed to set unit load path: %m");
2698 r
= manager_new(scope
, flags
, &m
);
2700 return log_error_errno(r
, "Failed to initialize manager: %m");
2702 log_debug("Starting manager...");
2704 r
= manager_startup(m
, /* serialization= */ NULL
, /* fds= */ NULL
, root
);
2709 /* Ensure the temporary directory is in the search path, so that we can add drop-ins. */
2710 r
= strv_extend(&m
->lookup_paths
.search_path
, m
->lookup_paths
.temporary_dir
);
2715 log_debug("Loading remaining units from the command line...");
2717 STRV_FOREACH(filename
, filenames
) {
2718 _cleanup_free_
char *prepared
= NULL
;
2720 log_debug("Handling %s...", *filename
);
2722 k
= verify_prepare_filename(*filename
, &prepared
);
2724 log_warning_errno(k
, "Failed to prepare filename %s: %m", *filename
);
2729 /* When a portable image is analyzed, the profile is what provides a good chunk of
2730 * the security-related settings, but they are obviously not shipped with the image.
2731 * This allows them to be taken into consideration. */
2733 _cleanup_free_
char *unit_name
= NULL
, *dropin
= NULL
, *profile_path
= NULL
;
2735 r
= path_extract_filename(prepared
, &unit_name
);
2739 dropin
= strjoin(m
->lookup_paths
.temporary_dir
, "/", unit_name
, ".d/profile.conf");
2742 (void) mkdir_parents(dropin
, 0755);
2744 if (!is_path(profile
)) {
2745 r
= find_portable_profile(profile
, unit_name
, &profile_path
);
2747 return log_error_errno(r
, "Failed to find portable profile %s: %m", profile
);
2748 profile
= profile_path
;
2751 r
= copy_file(profile
, dropin
, 0, 0644, 0);
2753 return log_error_errno(r
, "Failed to copy: %m");
2756 k
= manager_load_startable_unit_or_warn(m
, NULL
, prepared
, &units
[count
]);
2765 for (size_t i
= 0; i
< count
; i
++)
2766 RET_GATHER(r
, offline_security_check(units
[i
], threshold
, policy
, pager_flags
, json_format_flags
));
2771 static int analyze_security(sd_bus
*bus
,
2773 sd_json_variant
*policy
,
2776 bool run_generators
,
2780 const char *profile
,
2781 sd_json_format_flags_t json_format_flags
,
2782 PagerFlags pager_flags
,
2783 AnalyzeSecurityFlags flags
) {
2785 _cleanup_(table_unrefp
) Table
*overview_table
= NULL
;
2788 assert(!!bus
!= offline
);
2791 return offline_security_checks(units
, policy
, scope
, check_man
, run_generators
, threshold
, root
, profile
, pager_flags
, json_format_flags
);
2793 if (strv_length(units
) != 1) {
2794 overview_table
= table_new("unit", "exposure", "predicate", "happy");
2795 if (!overview_table
)
2799 if (strv_isempty(units
)) {
2800 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2801 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2802 _cleanup_strv_free_
char **list
= NULL
;
2805 r
= bus_call_method(
2813 return log_error_errno(r
, "Failed to list units: %s", bus_error_message(&error
, r
));
2815 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
2817 return bus_log_parse_error(r
);
2822 r
= bus_parse_unit_info(reply
, &info
);
2824 return bus_log_parse_error(r
);
2828 if (!endswith(info
.id
, ".service"))
2831 if (!GREEDY_REALLOC(list
, n
+ 2))
2834 r
= strdup_to(&list
[n
], info
.id
);
2843 flags
|= ANALYZE_SECURITY_SHORT
|ANALYZE_SECURITY_ONLY_LOADED
|ANALYZE_SECURITY_ONLY_LONG_RUNNING
;
2845 STRV_FOREACH(i
, list
) {
2846 r
= analyze_security_one(bus
, *i
, overview_table
, flags
, threshold
, policy
, pager_flags
, json_format_flags
);
2847 if (r
< 0 && ret
>= 0)
2852 STRV_FOREACH(i
, units
) {
2853 _cleanup_free_
char *mangled
= NULL
, *instance
= NULL
;
2856 if (!FLAGS_SET(flags
, ANALYZE_SECURITY_SHORT
) && i
!= units
) {
2861 r
= unit_name_mangle(*i
, 0, &mangled
);
2863 return log_error_errno(r
, "Failed to mangle unit name '%s': %m", *i
);
2865 if (!endswith(mangled
, ".service"))
2866 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
2867 "Unit %s is not a service unit, refusing.",
2870 if (unit_name_is_valid(mangled
, UNIT_NAME_TEMPLATE
)) {
2871 r
= unit_name_replace_instance(mangled
, arg_instance
, &instance
);
2879 r
= analyze_security_one(bus
, name
, overview_table
, flags
, threshold
, policy
, pager_flags
, json_format_flags
);
2880 if (r
< 0 && ret
>= 0)
2884 if (overview_table
) {
2885 if (!FLAGS_SET(flags
, ANALYZE_SECURITY_SHORT
)) {
2890 r
= table_print_with_pager(overview_table
, json_format_flags
, pager_flags
, /* show_header= */true);
2892 return log_error_errno(r
, "Failed to output table: %m");
2897 int verb_security(int argc
, char *argv
[], void *userdata
) {
2898 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2899 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*policy
= NULL
;
2903 r
= acquire_bus(&bus
, NULL
);
2905 return bus_log_connect_error(r
, arg_transport
, arg_runtime_scope
);
2908 pager_open(arg_pager_flags
);
2910 unsigned line
= 0, column
= 0;
2911 if (arg_security_policy
) {
2912 r
= sd_json_parse_file(/*f=*/ NULL
, arg_security_policy
, /*flags=*/ 0, &policy
, &line
, &column
);
2914 return log_error_errno(r
, "Failed to parse '%s' at %u:%u: %m", arg_security_policy
, line
, column
);
2916 _cleanup_fclose_
FILE *f
= NULL
;
2917 _cleanup_free_
char *pp
= NULL
;
2919 r
= search_and_fopen_nulstr("systemd-analyze-security.policy", "re", /*root=*/ NULL
, CONF_PATHS_NULSTR("systemd"), &f
, &pp
);
2920 if (r
< 0 && r
!= -ENOENT
)
2924 r
= sd_json_parse_file(f
, pp
, /*flags=*/ 0, &policy
, &line
, &column
);
2926 return log_error_errno(r
, "[%s:%u:%u] Failed to parse JSON policy: %m", pp
, line
, column
);
2930 return analyze_security(
2941 arg_json_format_flags
,