1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <sys/utsname.h>
5 #include "analyze-security.h"
7 #include "bus-map-properties.h"
8 #include "bus-unit-util.h"
11 #include "format-table.h"
12 #include "in-addr-util.h"
13 #include "locale-util.h"
15 #include "missing_capability.h"
16 #include "missing_sched.h"
17 #include "nulstr-util.h"
18 #include "parse-util.h"
19 #include "path-util.h"
20 #include "pretty-print.h"
22 # include "seccomp-util.h"
25 #include "stdio-util.h"
27 #include "terminal-util.h"
29 #include "unit-name.h"
31 struct security_info
{
36 bool default_dependencies
;
38 uint64_t ambient_capabilities
;
39 uint64_t capability_bounding_set
;
42 char **supplementary_groups
;
45 bool ip_address_deny_all
;
46 bool ip_address_allow_localhost
;
47 bool ip_address_allow_other
;
49 bool ip_filters_custom_ingress
;
50 bool ip_filters_custom_egress
;
55 bool lock_personality
;
56 bool memory_deny_write_execute
;
57 bool no_new_privileges
;
59 bool protect_hostname
;
67 bool protect_control_groups
;
68 bool protect_kernel_modules
;
69 bool protect_kernel_tunables
;
70 bool protect_kernel_logs
;
78 bool restrict_address_family_inet
;
79 bool restrict_address_family_unix
;
80 bool restrict_address_family_netlink
;
81 bool restrict_address_family_packet
;
82 bool restrict_address_family_other
;
84 uint64_t restrict_namespaces
;
85 bool restrict_realtime
;
86 bool restrict_suid_sgid
;
93 bool device_allow_non_empty
;
95 char **system_call_architectures
;
97 bool system_call_filter_allow_list
;
98 Set
*system_call_filter
;
103 struct security_assessor
{
105 const char *description_good
;
106 const char *description_bad
;
107 const char *description_na
;
112 const struct security_assessor
*a
,
113 const struct security_info
*info
,
115 uint64_t *ret_badness
,
116 char **ret_description
);
119 bool default_dependencies_only
;
122 static void security_info_free(struct security_info
*i
) {
129 free(i
->fragment_path
);
133 free(i
->protect_home
);
134 free(i
->protect_system
);
136 free(i
->root_directory
);
139 free(i
->keyring_mode
);
140 free(i
->protect_proc
);
141 free(i
->proc_subset
);
142 free(i
->notify_access
);
144 free(i
->device_policy
);
146 strv_free(i
->supplementary_groups
);
147 strv_free(i
->system_call_architectures
);
149 set_free(i
->system_call_filter
);
152 static bool security_info_runs_privileged(const struct security_info
*i
) {
155 if (STRPTR_IN_SET(i
->user
, "0", "root"))
161 return isempty(i
->user
);
164 static int assess_bool(
165 const struct security_assessor
*a
,
166 const struct security_info
*info
,
168 uint64_t *ret_badness
,
169 char **ret_description
) {
171 const bool *b
= data
;
175 assert(ret_description
);
177 *ret_badness
= a
->parameter
? *b
: !*b
;
178 *ret_description
= NULL
;
183 static int assess_user(
184 const struct security_assessor
*a
,
185 const struct security_info
*info
,
187 uint64_t *ret_badness
,
188 char **ret_description
) {
190 _cleanup_free_
char *d
= NULL
;
194 assert(ret_description
);
196 if (streq_ptr(info
->user
, NOBODY_USER_NAME
)) {
197 d
= strdup("Service runs under as '" NOBODY_USER_NAME
"' user, which should not be used for services");
199 } else if (info
->dynamic_user
&& !STR_IN_SET(info
->user
, "0", "root")) {
200 d
= strdup("Service runs under a transient non-root user identity");
202 } else if (info
->user
&& !STR_IN_SET(info
->user
, "0", "root", "")) {
203 d
= strdup("Service runs under a static non-root user identity");
207 *ret_description
= NULL
;
215 *ret_description
= TAKE_PTR(d
);
220 static int assess_protect_home(
221 const struct security_assessor
*a
,
222 const struct security_info
*info
,
224 uint64_t *ret_badness
,
225 char **ret_description
) {
227 const char *description
;
233 assert(ret_description
);
236 description
= "Service has full access to home directories";
238 r
= parse_boolean(info
->protect_home
);
240 if (streq_ptr(info
->protect_home
, "read-only")) {
242 description
= "Service has read-only access to home directories";
243 } else if (streq_ptr(info
->protect_home
, "tmpfs")) {
245 description
= "Service has access to fake empty home directories";
249 description
= "Service has no access to home directories";
252 copy
= strdup(description
);
256 *ret_badness
= badness
;
257 *ret_description
= copy
;
262 static int assess_protect_system(
263 const struct security_assessor
*a
,
264 const struct security_info
*info
,
266 uint64_t *ret_badness
,
267 char **ret_description
) {
269 const char *description
;
275 assert(ret_description
);
278 description
= "Service has full access to the OS file hierarchy";
280 r
= parse_boolean(info
->protect_system
);
282 if (streq_ptr(info
->protect_system
, "full")) {
284 description
= "Service has very limited write access to the OS file hierarchy";
285 } else if (streq_ptr(info
->protect_system
, "strict")) {
287 description
= "Service has strict read-only access to the OS file hierarchy";
291 description
= "Service has limited write access to the OS file hierarchy";
294 copy
= strdup(description
);
298 *ret_badness
= badness
;
299 *ret_description
= copy
;
304 static int assess_root_directory(
305 const struct security_assessor
*a
,
306 const struct security_info
*info
,
308 uint64_t *ret_badness
,
309 char **ret_description
) {
312 assert(ret_description
);
315 empty_or_root(info
->root_directory
) &&
316 empty_or_root(info
->root_image
);
317 *ret_description
= NULL
;
322 static int assess_capability_bounding_set(
323 const struct security_assessor
*a
,
324 const struct security_info
*info
,
326 uint64_t *ret_badness
,
327 char **ret_description
) {
330 assert(ret_description
);
332 *ret_badness
= !!(info
->capability_bounding_set
& a
->parameter
);
333 *ret_description
= NULL
;
338 static int assess_umask(
339 const struct security_assessor
*a
,
340 const struct security_info
*info
,
342 uint64_t *ret_badness
,
343 char **ret_description
) {
350 assert(ret_description
);
352 if (!FLAGS_SET(info
->_umask
, 0002)) {
353 d
= "Files created by service are world-writable by default";
355 } else if (!FLAGS_SET(info
->_umask
, 0004)) {
356 d
= "Files created by service are world-readable by default";
358 } else if (!FLAGS_SET(info
->_umask
, 0020)) {
359 d
= "Files created by service are group-writable by default";
361 } else if (!FLAGS_SET(info
->_umask
, 0040)) {
362 d
= "Files created by service are group-readable by default";
365 d
= "Files created by service are accessible only by service's own user by default";
374 *ret_description
= copy
;
379 static int assess_keyring_mode(
380 const struct security_assessor
*a
,
381 const struct security_info
*info
,
383 uint64_t *ret_badness
,
384 char **ret_description
) {
387 assert(ret_description
);
389 *ret_badness
= !streq_ptr(info
->keyring_mode
, "private");
390 *ret_description
= NULL
;
395 static int assess_protect_proc(
396 const struct security_assessor
*a
,
397 const struct security_info
*info
,
399 uint64_t *ret_badness
,
400 char **ret_description
) {
403 assert(ret_description
);
405 if (streq_ptr(info
->protect_proc
, "noaccess"))
407 else if (STRPTR_IN_SET(info
->protect_proc
, "invisible", "ptraceable"))
412 *ret_description
= NULL
;
417 static int assess_proc_subset(
418 const struct security_assessor
*a
,
419 const struct security_info
*info
,
421 uint64_t *ret_badness
,
422 char **ret_description
) {
425 assert(ret_description
);
427 *ret_badness
= !streq_ptr(info
->proc_subset
, "pid");
428 *ret_description
= NULL
;
433 static int assess_notify_access(
434 const struct security_assessor
*a
,
435 const struct security_info
*info
,
437 uint64_t *ret_badness
,
438 char **ret_description
) {
441 assert(ret_description
);
443 *ret_badness
= streq_ptr(info
->notify_access
, "all");
444 *ret_description
= NULL
;
449 static int assess_remove_ipc(
450 const struct security_assessor
*a
,
451 const struct security_info
*info
,
453 uint64_t *ret_badness
,
454 char **ret_description
) {
457 assert(ret_description
);
459 if (security_info_runs_privileged(info
))
460 *ret_badness
= UINT64_MAX
;
462 *ret_badness
= !info
->remove_ipc
;
464 *ret_description
= NULL
;
468 static int assess_supplementary_groups(
469 const struct security_assessor
*a
,
470 const struct security_info
*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
= !strv_isempty(info
->supplementary_groups
);
483 *ret_description
= NULL
;
487 static int assess_restrict_namespaces(
488 const struct security_assessor
*a
,
489 const struct security_info
*info
,
491 uint64_t *ret_badness
,
492 char **ret_description
) {
495 assert(ret_description
);
497 *ret_badness
= !!(info
->restrict_namespaces
& a
->parameter
);
498 *ret_description
= NULL
;
503 static int assess_system_call_architectures(
504 const struct security_assessor
*a
,
505 const struct security_info
*info
,
507 uint64_t *ret_badness
,
508 char **ret_description
) {
514 assert(ret_description
);
516 if (strv_isempty(info
->system_call_architectures
)) {
518 d
= strdup("Service may execute system calls with all ABIs");
519 } else if (strv_equal(info
->system_call_architectures
, STRV_MAKE("native"))) {
521 d
= strdup("Service may execute system calls only with native ABI");
524 d
= strdup("Service may execute system calls with multiple ABIs");
531 *ret_description
= d
;
538 static bool syscall_names_in_filter(Set
*s
, bool allow_list
, const SyscallFilterSet
*f
, const char **ret_offending_syscall
) {
541 NULSTR_FOREACH(syscall
, f
->value
) {
544 if (syscall
[0] == '@') {
545 const SyscallFilterSet
*g
;
547 assert_se(g
= syscall_filter_set_find(syscall
));
548 if (syscall_names_in_filter(s
, allow_list
, g
, ret_offending_syscall
))
549 return true; /* bad! */
554 /* Let's see if the system call actually exists on this platform, before complaining */
555 id
= seccomp_syscall_resolve_name(syscall
);
559 if (set_contains(s
, syscall
) == allow_list
) {
560 log_debug("Offending syscall filter item: %s", syscall
);
561 if (ret_offending_syscall
)
562 *ret_offending_syscall
= syscall
;
563 return true; /* bad! */
567 *ret_offending_syscall
= NULL
;
571 static int assess_system_call_filter(
572 const struct security_assessor
*a
,
573 const struct security_info
*info
,
575 uint64_t *ret_badness
,
576 char **ret_description
) {
581 assert(ret_description
);
583 assert(a
->parameter
< _SYSCALL_FILTER_SET_MAX
);
584 const SyscallFilterSet
*f
= syscall_filter_sets
+ a
->parameter
;
586 _cleanup_free_
char *d
= NULL
;
590 if (!info
->system_call_filter_allow_list
&& set_isempty(info
->system_call_filter
)) {
591 r
= free_and_strdup(&d
, "Service does not filter system calls");
595 const char *offender
= NULL
;
597 log_debug("Analyzing system call filter, checking against: %s", f
->name
);
598 bad
= syscall_names_in_filter(info
->system_call_filter
, info
->system_call_filter_allow_list
, f
, &offender
);
599 log_debug("Result: %s", bad
? "bad" : "good");
601 if (info
->system_call_filter_allow_list
) {
603 r
= asprintf(&d
, "System call allow list defined for service, and %s is included "
604 "(e.g. %s is allowed)",
608 r
= asprintf(&d
, "System call allow list defined for service, and %s is not included",
614 r
= asprintf(&d
, "System call deny list defined for service, and %s is not included "
615 "(e.g. %s is allowed)",
619 r
= asprintf(&d
, "System call deny list defined for service, and %s is included",
629 *ret_description
= TAKE_PTR(d
);
636 static int assess_ip_address_allow(
637 const struct security_assessor
*a
,
638 const struct security_info
*info
,
640 uint64_t *ret_badness
,
641 char **ret_description
) {
648 assert(ret_description
);
650 if (info
->ip_filters_custom_ingress
|| info
->ip_filters_custom_egress
) {
651 d
= strdup("Service defines custom ingress/egress IP filters with BPF programs");
653 } else if (!info
->ip_address_deny_all
) {
654 d
= strdup("Service does not define an IP address allow list");
656 } else if (info
->ip_address_allow_other
) {
657 d
= strdup("Service defines IP address allow list with non-localhost entries");
659 } else if (info
->ip_address_allow_localhost
) {
660 d
= strdup("Service defines IP address allow list with only localhost entries");
663 d
= strdup("Service blocks all IP address ranges");
671 *ret_description
= d
;
676 static int assess_device_allow(
677 const struct security_assessor
*a
,
678 const struct security_info
*info
,
680 uint64_t *ret_badness
,
681 char **ret_description
) {
688 assert(ret_description
);
690 if (STRPTR_IN_SET(info
->device_policy
, "strict", "closed")) {
692 if (info
->device_allow_non_empty
) {
693 d
= strdup("Service has a device ACL with some special devices");
696 d
= strdup("Service has a minimal device ACL");
700 d
= strdup("Service has no device ACL");
708 *ret_description
= d
;
713 static int assess_ambient_capabilities(
714 const struct security_assessor
*a
,
715 const struct security_info
*info
,
717 uint64_t *ret_badness
,
718 char **ret_description
) {
721 assert(ret_description
);
723 *ret_badness
= info
->ambient_capabilities
!= 0;
724 *ret_description
= NULL
;
729 static const struct security_assessor security_assessor_table
[] = {
731 .id
= "User=/DynamicUser=",
732 .description_bad
= "Service runs as root user",
733 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#User=",
736 .assess
= assess_user
,
739 .id
= "SupplementaryGroups=",
740 .description_good
= "Service has no supplementary groups",
741 .description_bad
= "Service runs with supplementary groups",
742 .description_na
= "Service runs as root, option does not matter",
743 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SupplementaryGroups=",
746 .assess
= assess_supplementary_groups
,
749 .id
= "PrivateDevices=",
750 .description_good
= "Service has no access to hardware devices",
751 .description_bad
= "Service potentially has access to hardware devices",
752 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateDevices=",
755 .assess
= assess_bool
,
756 .offset
= offsetof(struct security_info
, private_devices
),
759 .id
= "PrivateMounts=",
760 .description_good
= "Service cannot install system mounts",
761 .description_bad
= "Service may install system mounts",
762 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateMounts=",
765 .assess
= assess_bool
,
766 .offset
= offsetof(struct security_info
, private_mounts
),
769 .id
= "PrivateNetwork=",
770 .description_good
= "Service has no access to the host's network",
771 .description_bad
= "Service has access to the host's network",
772 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateNetwork=",
775 .assess
= assess_bool
,
776 .offset
= offsetof(struct security_info
, private_network
),
780 .description_good
= "Service has no access to other software's temporary files",
781 .description_bad
= "Service has access to other software's temporary files",
782 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateTmp=",
785 .assess
= assess_bool
,
786 .offset
= offsetof(struct security_info
, private_tmp
),
787 .default_dependencies_only
= true,
790 .id
= "PrivateUsers=",
791 .description_good
= "Service does not have access to other users",
792 .description_bad
= "Service has access to other users",
793 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateUsers=",
796 .assess
= assess_bool
,
797 .offset
= offsetof(struct security_info
, private_users
),
800 .id
= "ProtectControlGroups=",
801 .description_good
= "Service cannot modify the control group file system",
802 .description_bad
= "Service may modify the control group file system",
803 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectControlGroups=",
806 .assess
= assess_bool
,
807 .offset
= offsetof(struct security_info
, protect_control_groups
),
810 .id
= "ProtectKernelModules=",
811 .description_good
= "Service cannot load or read kernel modules",
812 .description_bad
= "Service may load or read kernel modules",
813 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelModules=",
816 .assess
= assess_bool
,
817 .offset
= offsetof(struct security_info
, protect_kernel_modules
),
820 .id
= "ProtectKernelTunables=",
821 .description_good
= "Service cannot alter kernel tunables (/proc/sys, …)",
822 .description_bad
= "Service may alter kernel tunables",
823 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelTunables=",
826 .assess
= assess_bool
,
827 .offset
= offsetof(struct security_info
, protect_kernel_tunables
),
830 .id
= "ProtectKernelLogs=",
831 .description_good
= "Service cannot read from or write to the kernel log ring buffer",
832 .description_bad
= "Service may read from or write to the kernel log ring buffer",
833 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelLogs=",
836 .assess
= assess_bool
,
837 .offset
= offsetof(struct security_info
, protect_kernel_logs
),
840 .id
= "ProtectClock=",
841 .description_good
= "Service cannot write to the hardware clock or system clock",
842 .description_bad
= "Service may write to the hardware clock or system clock",
843 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectClock=",
846 .assess
= assess_bool
,
847 .offset
= offsetof(struct security_info
, protect_clock
),
850 .id
= "ProtectHome=",
851 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHome=",
854 .assess
= assess_protect_home
,
855 .default_dependencies_only
= true,
858 .id
= "ProtectHostname=",
859 .description_good
= "Service cannot change system host/domainname",
860 .description_bad
= "Service may change system host/domainname",
861 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHostname=",
864 .assess
= assess_bool
,
865 .offset
= offsetof(struct security_info
, protect_hostname
),
868 .id
= "ProtectSystem=",
869 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectSystem=",
872 .assess
= assess_protect_system
,
873 .default_dependencies_only
= true,
876 .id
= "RootDirectory=/RootImage=",
877 .description_good
= "Service has its own root directory/image",
878 .description_bad
= "Service runs within the host's root directory",
879 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RootDirectory=",
882 .assess
= assess_root_directory
,
883 .default_dependencies_only
= true,
886 .id
= "LockPersonality=",
887 .description_good
= "Service cannot change ABI personality",
888 .description_bad
= "Service may change ABI personality",
889 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#LockPersonality=",
892 .assess
= assess_bool
,
893 .offset
= offsetof(struct security_info
, lock_personality
),
896 .id
= "MemoryDenyWriteExecute=",
897 .description_good
= "Service cannot create writable executable memory mappings",
898 .description_bad
= "Service may create writable executable memory mappings",
899 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#MemoryDenyWriteExecute=",
902 .assess
= assess_bool
,
903 .offset
= offsetof(struct security_info
, memory_deny_write_execute
),
906 .id
= "NoNewPrivileges=",
907 .description_good
= "Service processes cannot acquire new privileges",
908 .description_bad
= "Service processes may acquire new privileges",
909 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NoNewPrivileges=",
912 .assess
= assess_bool
,
913 .offset
= offsetof(struct security_info
, no_new_privileges
),
916 .id
= "CapabilityBoundingSet=~CAP_SYS_ADMIN",
917 .description_good
= "Service has no administrator privileges",
918 .description_bad
= "Service has administrator privileges",
919 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
922 .assess
= assess_capability_bounding_set
,
923 .parameter
= UINT64_C(1) << CAP_SYS_ADMIN
,
926 .id
= "CapabilityBoundingSet=~CAP_SET(UID|GID|PCAP)",
927 .description_good
= "Service cannot change UID/GID identities/capabilities",
928 .description_bad
= "Service may change UID/GID identities/capabilities",
929 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
932 .assess
= assess_capability_bounding_set
,
933 .parameter
= (UINT64_C(1) << CAP_SETUID
)|
934 (UINT64_C(1) << CAP_SETGID
)|
935 (UINT64_C(1) << CAP_SETPCAP
),
938 .id
= "CapabilityBoundingSet=~CAP_SYS_PTRACE",
939 .description_good
= "Service has no ptrace() debugging abilities",
940 .description_bad
= "Service has ptrace() debugging abilities",
941 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
944 .assess
= assess_capability_bounding_set
,
945 .parameter
= (UINT64_C(1) << CAP_SYS_PTRACE
),
948 .id
= "CapabilityBoundingSet=~CAP_SYS_TIME",
949 .description_good
= "Service processes cannot change the system clock",
950 .description_bad
= "Service processes may change the system clock",
951 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
954 .assess
= assess_capability_bounding_set
,
955 .parameter
= UINT64_C(1) << CAP_SYS_TIME
,
958 .id
= "CapabilityBoundingSet=~CAP_NET_ADMIN",
959 .description_good
= "Service has no network configuration privileges",
960 .description_bad
= "Service has network configuration privileges",
961 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
964 .assess
= assess_capability_bounding_set
,
965 .parameter
= (UINT64_C(1) << CAP_NET_ADMIN
),
968 .id
= "CapabilityBoundingSet=~CAP_SYS_RAWIO",
969 .description_good
= "Service has no raw I/O access",
970 .description_bad
= "Service has raw I/O access",
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_SYS_RAWIO
),
978 .id
= "CapabilityBoundingSet=~CAP_SYS_MODULE",
979 .description_good
= "Service cannot load kernel modules",
980 .description_bad
= "Service may load kernel modules",
981 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
984 .assess
= assess_capability_bounding_set
,
985 .parameter
= (UINT64_C(1) << CAP_SYS_MODULE
),
988 .id
= "CapabilityBoundingSet=~CAP_AUDIT_*",
989 .description_good
= "Service has no audit subsystem access",
990 .description_bad
= "Service has audit subsystem access",
991 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
994 .assess
= assess_capability_bounding_set
,
995 .parameter
= (UINT64_C(1) << CAP_AUDIT_CONTROL
) |
996 (UINT64_C(1) << CAP_AUDIT_READ
) |
997 (UINT64_C(1) << CAP_AUDIT_WRITE
),
1000 .id
= "CapabilityBoundingSet=~CAP_SYSLOG",
1001 .description_good
= "Service has no access to kernel logging",
1002 .description_bad
= "Service has access to kernel logging",
1003 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1006 .assess
= assess_capability_bounding_set
,
1007 .parameter
= (UINT64_C(1) << CAP_SYSLOG
),
1010 .id
= "CapabilityBoundingSet=~CAP_SYS_(NICE|RESOURCE)",
1011 .description_good
= "Service has no privileges to change resource use parameters",
1012 .description_bad
= "Service has privileges to change resource use parameters",
1013 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1016 .assess
= assess_capability_bounding_set
,
1017 .parameter
= (UINT64_C(1) << CAP_SYS_NICE
) |
1018 (UINT64_C(1) << CAP_SYS_RESOURCE
),
1021 .id
= "CapabilityBoundingSet=~CAP_MKNOD",
1022 .description_good
= "Service cannot create device nodes",
1023 .description_bad
= "Service may create device nodes",
1024 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1027 .assess
= assess_capability_bounding_set
,
1028 .parameter
= (UINT64_C(1) << CAP_MKNOD
),
1031 .id
= "CapabilityBoundingSet=~CAP_(CHOWN|FSETID|SETFCAP)",
1032 .description_good
= "Service cannot change file ownership/access mode/capabilities",
1033 .description_bad
= "Service may change file ownership/access mode/capabilities unrestricted",
1034 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1037 .assess
= assess_capability_bounding_set
,
1038 .parameter
= (UINT64_C(1) << CAP_CHOWN
) |
1039 (UINT64_C(1) << CAP_FSETID
) |
1040 (UINT64_C(1) << CAP_SETFCAP
),
1043 .id
= "CapabilityBoundingSet=~CAP_(DAC_*|FOWNER|IPC_OWNER)",
1044 .description_good
= "Service cannot override UNIX file/IPC permission checks",
1045 .description_bad
= "Service may override UNIX file/IPC permission checks",
1046 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1049 .assess
= assess_capability_bounding_set
,
1050 .parameter
= (UINT64_C(1) << CAP_DAC_OVERRIDE
) |
1051 (UINT64_C(1) << CAP_DAC_READ_SEARCH
) |
1052 (UINT64_C(1) << CAP_FOWNER
) |
1053 (UINT64_C(1) << CAP_IPC_OWNER
),
1056 .id
= "CapabilityBoundingSet=~CAP_KILL",
1057 .description_good
= "Service cannot send UNIX signals to arbitrary processes",
1058 .description_bad
= "Service may send UNIX signals to arbitrary processes",
1059 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1062 .assess
= assess_capability_bounding_set
,
1063 .parameter
= (UINT64_C(1) << CAP_KILL
),
1066 .id
= "CapabilityBoundingSet=~CAP_NET_(BIND_SERVICE|BROADCAST|RAW)",
1067 .description_good
= "Service has no elevated networking privileges",
1068 .description_bad
= "Service has elevated networking privileges",
1069 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1072 .assess
= assess_capability_bounding_set
,
1073 .parameter
= (UINT64_C(1) << CAP_NET_BIND_SERVICE
) |
1074 (UINT64_C(1) << CAP_NET_BROADCAST
) |
1075 (UINT64_C(1) << CAP_NET_RAW
),
1078 .id
= "CapabilityBoundingSet=~CAP_SYS_BOOT",
1079 .description_good
= "Service cannot issue reboot()",
1080 .description_bad
= "Service may issue reboot()",
1081 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1084 .assess
= assess_capability_bounding_set
,
1085 .parameter
= (UINT64_C(1) << CAP_SYS_BOOT
),
1088 .id
= "CapabilityBoundingSet=~CAP_MAC_*",
1089 .description_good
= "Service cannot adjust SMACK MAC",
1090 .description_bad
= "Service may adjust SMACK MAC",
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_MAC_ADMIN
)|
1096 (UINT64_C(1) << CAP_MAC_OVERRIDE
),
1099 .id
= "CapabilityBoundingSet=~CAP_LINUX_IMMUTABLE",
1100 .description_good
= "Service cannot mark files immutable",
1101 .description_bad
= "Service may mark files immutable",
1102 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1105 .assess
= assess_capability_bounding_set
,
1106 .parameter
= (UINT64_C(1) << CAP_LINUX_IMMUTABLE
),
1109 .id
= "CapabilityBoundingSet=~CAP_IPC_LOCK",
1110 .description_good
= "Service cannot lock memory into RAM",
1111 .description_bad
= "Service may lock memory into RAM",
1112 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1115 .assess
= assess_capability_bounding_set
,
1116 .parameter
= (UINT64_C(1) << CAP_IPC_LOCK
),
1119 .id
= "CapabilityBoundingSet=~CAP_SYS_CHROOT",
1120 .description_good
= "Service cannot issue chroot()",
1121 .description_bad
= "Service may issue chroot()",
1122 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1125 .assess
= assess_capability_bounding_set
,
1126 .parameter
= (UINT64_C(1) << CAP_SYS_CHROOT
),
1129 .id
= "CapabilityBoundingSet=~CAP_BLOCK_SUSPEND",
1130 .description_good
= "Service cannot establish wake locks",
1131 .description_bad
= "Service may establish wake locks",
1132 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1135 .assess
= assess_capability_bounding_set
,
1136 .parameter
= (UINT64_C(1) << CAP_BLOCK_SUSPEND
),
1139 .id
= "CapabilityBoundingSet=~CAP_WAKE_ALARM",
1140 .description_good
= "Service cannot program timers that wake up the system",
1141 .description_bad
= "Service may program timers that wake up the system",
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_WAKE_ALARM
),
1149 .id
= "CapabilityBoundingSet=~CAP_LEASE",
1150 .description_good
= "Service cannot create file leases",
1151 .description_bad
= "Service may create file leases",
1152 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1155 .assess
= assess_capability_bounding_set
,
1156 .parameter
= (UINT64_C(1) << CAP_LEASE
),
1159 .id
= "CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG",
1160 .description_good
= "Service cannot issue vhangup()",
1161 .description_bad
= "Service may issue vhangup()",
1162 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1165 .assess
= assess_capability_bounding_set
,
1166 .parameter
= (UINT64_C(1) << CAP_SYS_TTY_CONFIG
),
1169 .id
= "CapabilityBoundingSet=~CAP_SYS_PACCT",
1170 .description_good
= "Service cannot use acct()",
1171 .description_bad
= "Service may use acct()",
1172 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1175 .assess
= assess_capability_bounding_set
,
1176 .parameter
= (UINT64_C(1) << CAP_SYS_PACCT
),
1180 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#UMask=",
1183 .assess
= assess_umask
,
1186 .id
= "KeyringMode=",
1187 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#KeyringMode=",
1188 .description_good
= "Service doesn't share key material with other services",
1189 .description_bad
= "Service shares key material with other service",
1192 .assess
= assess_keyring_mode
,
1195 .id
= "ProtectProc=",
1196 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectProc=",
1197 .description_good
= "Service has restricted access to process tree (/proc hidepid=)",
1198 .description_bad
= "Service has full access to process tree (/proc hidepid=)",
1201 .assess
= assess_protect_proc
,
1204 .id
= "ProcSubset=",
1205 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProcSubset=",
1206 .description_good
= "Service has no access to non-process /proc files (/proc subset=)",
1207 .description_bad
= "Service has full access to non-process /proc files (/proc subset=)",
1210 .assess
= assess_proc_subset
,
1213 .id
= "NotifyAccess=",
1214 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NotifyAccess=",
1215 .description_good
= "Service child processes cannot alter service state",
1216 .description_bad
= "Service child processes may alter service state",
1219 .assess
= assess_notify_access
,
1223 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RemoveIPC=",
1224 .description_good
= "Service user cannot leave SysV IPC objects around",
1225 .description_bad
= "Service user may leave SysV IPC objects around",
1226 .description_na
= "Service runs as root, option does not apply",
1229 .assess
= assess_remove_ipc
,
1230 .offset
= offsetof(struct security_info
, remove_ipc
),
1234 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Delegate=",
1235 .description_good
= "Service does not maintain its own delegated control group subtree",
1236 .description_bad
= "Service maintains its own delegated control group subtree",
1239 .assess
= assess_bool
,
1240 .offset
= offsetof(struct security_info
, delegate
),
1241 .parameter
= true, /* invert! */
1244 .id
= "RestrictRealtime=",
1245 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictRealtime=",
1246 .description_good
= "Service realtime scheduling access is restricted",
1247 .description_bad
= "Service may acquire realtime scheduling",
1250 .assess
= assess_bool
,
1251 .offset
= offsetof(struct security_info
, restrict_realtime
),
1254 .id
= "RestrictSUIDSGID=",
1255 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictSUIDSGID=",
1256 .description_good
= "SUID/SGID file creation by service is restricted",
1257 .description_bad
= "Service may create SUID/SGID files",
1260 .assess
= assess_bool
,
1261 .offset
= offsetof(struct security_info
, restrict_suid_sgid
),
1264 .id
= "RestrictNamespaces=~CLONE_NEWUSER",
1265 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1266 .description_good
= "Service cannot create user namespaces",
1267 .description_bad
= "Service may create user namespaces",
1270 .assess
= assess_restrict_namespaces
,
1271 .parameter
= CLONE_NEWUSER
,
1274 .id
= "RestrictNamespaces=~CLONE_NEWNS",
1275 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1276 .description_good
= "Service cannot create file system namespaces",
1277 .description_bad
= "Service may create file system namespaces",
1280 .assess
= assess_restrict_namespaces
,
1281 .parameter
= CLONE_NEWNS
,
1284 .id
= "RestrictNamespaces=~CLONE_NEWIPC",
1285 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1286 .description_good
= "Service cannot create IPC namespaces",
1287 .description_bad
= "Service may create IPC namespaces",
1290 .assess
= assess_restrict_namespaces
,
1291 .parameter
= CLONE_NEWIPC
,
1294 .id
= "RestrictNamespaces=~CLONE_NEWPID",
1295 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1296 .description_good
= "Service cannot create process namespaces",
1297 .description_bad
= "Service may create process namespaces",
1300 .assess
= assess_restrict_namespaces
,
1301 .parameter
= CLONE_NEWPID
,
1304 .id
= "RestrictNamespaces=~CLONE_NEWCGROUP",
1305 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1306 .description_good
= "Service cannot create cgroup namespaces",
1307 .description_bad
= "Service may create cgroup namespaces",
1310 .assess
= assess_restrict_namespaces
,
1311 .parameter
= CLONE_NEWCGROUP
,
1314 .id
= "RestrictNamespaces=~CLONE_NEWNET",
1315 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1316 .description_good
= "Service cannot create network namespaces",
1317 .description_bad
= "Service may create network namespaces",
1320 .assess
= assess_restrict_namespaces
,
1321 .parameter
= CLONE_NEWNET
,
1324 .id
= "RestrictNamespaces=~CLONE_NEWUTS",
1325 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1326 .description_good
= "Service cannot create hostname namespaces",
1327 .description_bad
= "Service may create hostname namespaces",
1330 .assess
= assess_restrict_namespaces
,
1331 .parameter
= CLONE_NEWUTS
,
1334 .id
= "RestrictAddressFamilies=~AF_(INET|INET6)",
1335 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1336 .description_good
= "Service cannot allocate Internet sockets",
1337 .description_bad
= "Service may allocate Internet sockets",
1340 .assess
= assess_bool
,
1341 .offset
= offsetof(struct security_info
, restrict_address_family_inet
),
1344 .id
= "RestrictAddressFamilies=~AF_UNIX",
1345 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1346 .description_good
= "Service cannot allocate local sockets",
1347 .description_bad
= "Service may allocate local sockets",
1350 .assess
= assess_bool
,
1351 .offset
= offsetof(struct security_info
, restrict_address_family_unix
),
1354 .id
= "RestrictAddressFamilies=~AF_NETLINK",
1355 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1356 .description_good
= "Service cannot allocate netlink sockets",
1357 .description_bad
= "Service may allocate netlink sockets",
1360 .assess
= assess_bool
,
1361 .offset
= offsetof(struct security_info
, restrict_address_family_netlink
),
1364 .id
= "RestrictAddressFamilies=~AF_PACKET",
1365 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1366 .description_good
= "Service cannot allocate packet sockets",
1367 .description_bad
= "Service may allocate packet sockets",
1370 .assess
= assess_bool
,
1371 .offset
= offsetof(struct security_info
, restrict_address_family_packet
),
1374 .id
= "RestrictAddressFamilies=~…",
1375 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1376 .description_good
= "Service cannot allocate exotic sockets",
1377 .description_bad
= "Service may allocate exotic sockets",
1380 .assess
= assess_bool
,
1381 .offset
= offsetof(struct security_info
, restrict_address_family_other
),
1384 .id
= "SystemCallArchitectures=",
1385 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallArchitectures=",
1388 .assess
= assess_system_call_architectures
,
1392 .id
= "SystemCallFilter=~@swap",
1393 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1396 .assess
= assess_system_call_filter
,
1397 .parameter
= SYSCALL_FILTER_SET_SWAP
,
1400 .id
= "SystemCallFilter=~@obsolete",
1401 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1404 .assess
= assess_system_call_filter
,
1405 .parameter
= SYSCALL_FILTER_SET_OBSOLETE
,
1408 .id
= "SystemCallFilter=~@clock",
1409 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1412 .assess
= assess_system_call_filter
,
1413 .parameter
= SYSCALL_FILTER_SET_CLOCK
,
1416 .id
= "SystemCallFilter=~@cpu-emulation",
1417 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1420 .assess
= assess_system_call_filter
,
1421 .parameter
= SYSCALL_FILTER_SET_CPU_EMULATION
,
1424 .id
= "SystemCallFilter=~@debug",
1425 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1428 .assess
= assess_system_call_filter
,
1429 .parameter
= SYSCALL_FILTER_SET_DEBUG
,
1432 .id
= "SystemCallFilter=~@mount",
1433 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1436 .assess
= assess_system_call_filter
,
1437 .parameter
= SYSCALL_FILTER_SET_MOUNT
,
1440 .id
= "SystemCallFilter=~@module",
1441 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1444 .assess
= assess_system_call_filter
,
1445 .parameter
= SYSCALL_FILTER_SET_MODULE
,
1448 .id
= "SystemCallFilter=~@raw-io",
1449 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1452 .assess
= assess_system_call_filter
,
1453 .parameter
= SYSCALL_FILTER_SET_RAW_IO
,
1456 .id
= "SystemCallFilter=~@reboot",
1457 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1460 .assess
= assess_system_call_filter
,
1461 .parameter
= SYSCALL_FILTER_SET_REBOOT
,
1464 .id
= "SystemCallFilter=~@privileged",
1465 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1468 .assess
= assess_system_call_filter
,
1469 .parameter
= SYSCALL_FILTER_SET_PRIVILEGED
,
1472 .id
= "SystemCallFilter=~@resources",
1473 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1476 .assess
= assess_system_call_filter
,
1477 .parameter
= SYSCALL_FILTER_SET_RESOURCES
,
1481 .id
= "IPAddressDeny=",
1482 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#IPAddressDeny=",
1485 .assess
= assess_ip_address_allow
,
1488 .id
= "DeviceAllow=",
1489 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#DeviceAllow=",
1492 .assess
= assess_device_allow
,
1495 .id
= "AmbientCapabilities=",
1496 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#AmbientCapabilities=",
1497 .description_good
= "Service process does not receive ambient capabilities",
1498 .description_bad
= "Service process receives ambient capabilities",
1501 .assess
= assess_ambient_capabilities
,
1505 static int assess(const struct security_info
*info
, Table
*overview_table
, AnalyzeSecurityFlags flags
) {
1506 static const struct {
1510 SpecialGlyph smiley
;
1511 } badness_table
[] = {
1512 { 100, "DANGEROUS", ANSI_HIGHLIGHT_RED
, SPECIAL_GLYPH_DEPRESSED_SMILEY
},
1513 { 90, "UNSAFE", ANSI_HIGHLIGHT_RED
, SPECIAL_GLYPH_UNHAPPY_SMILEY
},
1514 { 75, "EXPOSED", ANSI_HIGHLIGHT_YELLOW
, SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY
},
1515 { 50, "MEDIUM", NULL
, SPECIAL_GLYPH_NEUTRAL_SMILEY
},
1516 { 10, "OK", ANSI_HIGHLIGHT_GREEN
, SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY
},
1517 { 1, "SAFE", ANSI_HIGHLIGHT_GREEN
, SPECIAL_GLYPH_HAPPY_SMILEY
},
1518 { 0, "PERFECT", ANSI_HIGHLIGHT_GREEN
, SPECIAL_GLYPH_ECSTATIC_SMILEY
},
1521 uint64_t badness_sum
= 0, weight_sum
= 0, exposure
;
1522 _cleanup_(table_unrefp
) Table
*details_table
= NULL
;
1526 if (!FLAGS_SET(flags
, ANALYZE_SECURITY_SHORT
)) {
1527 details_table
= table_new(" ", "name", "description", "weight", "badness", "range", "exposure");
1531 (void) table_set_sort(details_table
, (size_t) 3, (size_t) 1);
1532 (void) table_set_reverse(details_table
, 3, true);
1534 if (getenv_bool("SYSTEMD_ANALYZE_DEBUG") <= 0)
1535 (void) table_set_display(details_table
, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 6);
1538 for (i
= 0; i
< ELEMENTSOF(security_assessor_table
); i
++) {
1539 const struct security_assessor
*a
= security_assessor_table
+ i
;
1540 _cleanup_free_
char *d
= NULL
;
1544 data
= (uint8_t *) info
+ a
->offset
;
1546 if (a
->default_dependencies_only
&& !info
->default_dependencies
) {
1547 badness
= UINT64_MAX
;
1548 d
= strdup("Service runs in special boot phase, option is not appropriate");
1552 r
= a
->assess(a
, info
, data
, &badness
, &d
);
1557 assert(a
->range
> 0);
1559 if (badness
!= UINT64_MAX
) {
1560 assert(badness
<= a
->range
);
1562 badness_sum
+= DIV_ROUND_UP(badness
* a
->weight
, a
->range
);
1563 weight_sum
+= a
->weight
;
1566 if (details_table
) {
1567 const char *checkmark
, *description
, *color
= NULL
;
1569 if (badness
== UINT64_MAX
) {
1571 description
= a
->description_na
;
1573 } else if (badness
== a
->range
) {
1574 checkmark
= special_glyph(SPECIAL_GLYPH_CROSS_MARK
);
1575 description
= a
->description_bad
;
1576 color
= ansi_highlight_red();
1577 } else if (badness
== 0) {
1578 checkmark
= special_glyph(SPECIAL_GLYPH_CHECK_MARK
);
1579 description
= a
->description_good
;
1580 color
= ansi_highlight_green();
1582 checkmark
= special_glyph(SPECIAL_GLYPH_CROSS_MARK
);
1584 color
= ansi_highlight_red();
1590 r
= table_add_many(details_table
,
1591 TABLE_STRING
, checkmark
,
1592 TABLE_SET_MINIMUM_WIDTH
, 1,
1593 TABLE_SET_MAXIMUM_WIDTH
, 1,
1594 TABLE_SET_ELLIPSIZE_PERCENT
, 0,
1595 TABLE_SET_COLOR
, color
,
1596 TABLE_STRING
, a
->id
, TABLE_SET_URL
, a
->url
,
1597 TABLE_STRING
, description
,
1598 TABLE_UINT64
, a
->weight
, TABLE_SET_ALIGN_PERCENT
, 100,
1599 TABLE_UINT64
, badness
, TABLE_SET_ALIGN_PERCENT
, 100,
1600 TABLE_UINT64
, a
->range
, TABLE_SET_ALIGN_PERCENT
, 100,
1601 TABLE_EMPTY
, TABLE_SET_ALIGN_PERCENT
, 100);
1603 return table_log_add_error(r
);
1607 assert(weight_sum
> 0);
1609 if (details_table
) {
1612 for (row
= 1; row
< table_get_rows(details_table
); row
++) {
1613 char buf
[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1];
1614 const uint64_t *weight
, *badness
, *range
;
1618 assert_se(weight
= table_get_at(details_table
, row
, 3));
1619 assert_se(badness
= table_get_at(details_table
, row
, 4));
1620 assert_se(range
= table_get_at(details_table
, row
, 5));
1622 if (*badness
== UINT64_MAX
|| *badness
== 0)
1625 assert_se(cell
= table_get_cell(details_table
, row
, 6));
1627 x
= DIV_ROUND_UP(DIV_ROUND_UP(*badness
* *weight
* 100U, *range
), weight_sum
);
1628 xsprintf(buf
, "%" PRIu64
".%" PRIu64
, x
/ 10, x
% 10);
1630 r
= table_update(details_table
, cell
, TABLE_STRING
, buf
);
1632 return log_error_errno(r
, "Failed to update cell in table: %m");
1635 r
= table_print(details_table
, stdout
);
1637 return log_error_errno(r
, "Failed to output table: %m");
1640 exposure
= DIV_ROUND_UP(badness_sum
* 100U, weight_sum
);
1642 for (i
= 0; i
< ELEMENTSOF(badness_table
); i
++)
1643 if (exposure
>= badness_table
[i
].exposure
)
1646 assert(i
< ELEMENTSOF(badness_table
));
1648 if (details_table
) {
1649 _cleanup_free_
char *clickable
= NULL
;
1652 /* If we shall output the details table, also print the brief summary underneath */
1654 if (info
->fragment_path
) {
1655 r
= terminal_urlify_path(info
->fragment_path
, info
->id
, &clickable
);
1663 printf("\n%s %sOverall exposure level for %s%s: %s%" PRIu64
".%" PRIu64
" %s%s %s\n",
1664 special_glyph(SPECIAL_GLYPH_ARROW
),
1668 colors_enabled() ? strempty(badness_table
[i
].color
) : "",
1669 exposure
/ 10, exposure
% 10,
1670 badness_table
[i
].name
,
1672 special_glyph(badness_table
[i
].smiley
));
1677 if (overview_table
) {
1678 char buf
[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1];
1679 _cleanup_free_
char *url
= NULL
;
1681 if (info
->fragment_path
) {
1682 r
= file_url_from_path(info
->fragment_path
, &url
);
1684 return log_error_errno(r
, "Failed to generate URL from path: %m");
1687 xsprintf(buf
, "%" PRIu64
".%" PRIu64
, exposure
/ 10, exposure
% 10);
1689 r
= table_add_many(overview_table
,
1690 TABLE_STRING
, info
->id
,
1693 TABLE_SET_ALIGN_PERCENT
, 100,
1694 TABLE_STRING
, badness_table
[i
].name
,
1695 TABLE_SET_COLOR
, strempty(badness_table
[i
].color
),
1696 TABLE_STRING
, special_glyph(badness_table
[i
].smiley
));
1698 return table_log_add_error(r
);
1704 static int property_read_restrict_address_families(
1708 sd_bus_error
*error
,
1711 struct security_info
*info
= userdata
;
1718 r
= sd_bus_message_enter_container(m
, 'r', "bas");
1722 r
= sd_bus_message_read(m
, "b", &allow_list
);
1726 info
->restrict_address_family_inet
=
1727 info
->restrict_address_family_unix
=
1728 info
->restrict_address_family_netlink
=
1729 info
->restrict_address_family_packet
=
1730 info
->restrict_address_family_other
= allow_list
;
1732 r
= sd_bus_message_enter_container(m
, 'a', "s");
1739 r
= sd_bus_message_read(m
, "s", &name
);
1745 if (STR_IN_SET(name
, "AF_INET", "AF_INET6"))
1746 info
->restrict_address_family_inet
= !allow_list
;
1747 else if (streq(name
, "AF_UNIX"))
1748 info
->restrict_address_family_unix
= !allow_list
;
1749 else if (streq(name
, "AF_NETLINK"))
1750 info
->restrict_address_family_netlink
= !allow_list
;
1751 else if (streq(name
, "AF_PACKET"))
1752 info
->restrict_address_family_packet
= !allow_list
;
1754 info
->restrict_address_family_other
= !allow_list
;
1757 r
= sd_bus_message_exit_container(m
);
1761 return sd_bus_message_exit_container(m
);
1764 static int property_read_system_call_filter(
1768 sd_bus_error
*error
,
1771 struct security_info
*info
= userdata
;
1778 r
= sd_bus_message_enter_container(m
, 'r', "bas");
1782 r
= sd_bus_message_read(m
, "b", &allow_list
);
1786 info
->system_call_filter_allow_list
= allow_list
;
1788 r
= sd_bus_message_enter_container(m
, 'a', "s");
1795 r
= sd_bus_message_read(m
, "s", &name
);
1801 r
= set_put_strdup(&info
->system_call_filter
, name
);
1806 r
= sd_bus_message_exit_container(m
);
1810 return sd_bus_message_exit_container(m
);
1813 static int property_read_ip_address_allow(
1817 sd_bus_error
*error
,
1820 struct security_info
*info
= userdata
;
1821 bool deny_ipv4
= false, deny_ipv6
= false;
1828 r
= sd_bus_message_enter_container(m
, 'a', "(iayu)");
1838 r
= sd_bus_message_enter_container(m
, 'r', "iayu");
1844 r
= sd_bus_message_read(m
, "i", &family
);
1848 r
= sd_bus_message_read_array(m
, 'y', &data
, &size
);
1852 r
= sd_bus_message_read(m
, "u", &prefixlen
);
1856 r
= sd_bus_message_exit_container(m
);
1860 if (streq(member
, "IPAddressAllow")) {
1861 union in_addr_union u
;
1863 if (family
== AF_INET
&& size
== 4 && prefixlen
== 8)
1864 memcpy(&u
.in
, data
, size
);
1865 else if (family
== AF_INET6
&& size
== 16 && prefixlen
== 128)
1866 memcpy(&u
.in6
, data
, size
);
1868 info
->ip_address_allow_other
= true;
1872 if (in_addr_is_localhost(family
, &u
))
1873 info
->ip_address_allow_localhost
= true;
1875 info
->ip_address_allow_other
= true;
1877 assert(streq(member
, "IPAddressDeny"));
1879 if (family
== AF_INET
&& size
== 4 && prefixlen
== 0)
1881 else if (family
== AF_INET6
&& size
== 16 && prefixlen
== 0)
1886 info
->ip_address_deny_all
= deny_ipv4
&& deny_ipv6
;
1888 return sd_bus_message_exit_container(m
);
1891 static int property_read_ip_filters(
1895 sd_bus_error
*error
,
1898 struct security_info
*info
= userdata
;
1899 _cleanup_(strv_freep
) char **l
= NULL
;
1906 r
= sd_bus_message_read_strv(m
, &l
);
1910 if (streq(member
, "IPIngressFilterPath"))
1911 info
->ip_filters_custom_ingress
= !strv_isempty(l
);
1912 else if (streq(member
, "IPEgressFilterPath"))
1913 info
->ip_filters_custom_ingress
= !strv_isempty(l
);
1918 static int property_read_device_allow(
1922 sd_bus_error
*error
,
1925 struct security_info
*info
= userdata
;
1933 r
= sd_bus_message_enter_container(m
, 'a', "(ss)");
1938 const char *name
, *policy
;
1940 r
= sd_bus_message_read(m
, "(ss)", &name
, &policy
);
1949 info
->device_allow_non_empty
= n
> 0;
1951 return sd_bus_message_exit_container(m
);
1954 static int acquire_security_info(sd_bus
*bus
, const char *name
, struct security_info
*info
, AnalyzeSecurityFlags flags
) {
1956 static const struct bus_properties_map security_map
[] = {
1957 { "AmbientCapabilities", "t", NULL
, offsetof(struct security_info
, ambient_capabilities
) },
1958 { "CapabilityBoundingSet", "t", NULL
, offsetof(struct security_info
, capability_bounding_set
) },
1959 { "DefaultDependencies", "b", NULL
, offsetof(struct security_info
, default_dependencies
) },
1960 { "Delegate", "b", NULL
, offsetof(struct security_info
, delegate
) },
1961 { "DeviceAllow", "a(ss)", property_read_device_allow
, 0 },
1962 { "DevicePolicy", "s", NULL
, offsetof(struct security_info
, device_policy
) },
1963 { "DynamicUser", "b", NULL
, offsetof(struct security_info
, dynamic_user
) },
1964 { "FragmentPath", "s", NULL
, offsetof(struct security_info
, fragment_path
) },
1965 { "IPAddressAllow", "a(iayu)", property_read_ip_address_allow
, 0 },
1966 { "IPAddressDeny", "a(iayu)", property_read_ip_address_allow
, 0 },
1967 { "IPIngressFilterPath", "as", property_read_ip_filters
, 0 },
1968 { "IPEgressFilterPath", "as", property_read_ip_filters
, 0 },
1969 { "Id", "s", NULL
, offsetof(struct security_info
, id
) },
1970 { "KeyringMode", "s", NULL
, offsetof(struct security_info
, keyring_mode
) },
1971 { "ProtectProc", "s", NULL
, offsetof(struct security_info
, protect_proc
) },
1972 { "ProcSubset", "s", NULL
, offsetof(struct security_info
, proc_subset
) },
1973 { "LoadState", "s", NULL
, offsetof(struct security_info
, load_state
) },
1974 { "LockPersonality", "b", NULL
, offsetof(struct security_info
, lock_personality
) },
1975 { "MemoryDenyWriteExecute", "b", NULL
, offsetof(struct security_info
, memory_deny_write_execute
) },
1976 { "NoNewPrivileges", "b", NULL
, offsetof(struct security_info
, no_new_privileges
) },
1977 { "NotifyAccess", "s", NULL
, offsetof(struct security_info
, notify_access
) },
1978 { "PrivateDevices", "b", NULL
, offsetof(struct security_info
, private_devices
) },
1979 { "PrivateMounts", "b", NULL
, offsetof(struct security_info
, private_mounts
) },
1980 { "PrivateNetwork", "b", NULL
, offsetof(struct security_info
, private_network
) },
1981 { "PrivateTmp", "b", NULL
, offsetof(struct security_info
, private_tmp
) },
1982 { "PrivateUsers", "b", NULL
, offsetof(struct security_info
, private_users
) },
1983 { "ProtectControlGroups", "b", NULL
, offsetof(struct security_info
, protect_control_groups
) },
1984 { "ProtectHome", "s", NULL
, offsetof(struct security_info
, protect_home
) },
1985 { "ProtectHostname", "b", NULL
, offsetof(struct security_info
, protect_hostname
) },
1986 { "ProtectKernelModules", "b", NULL
, offsetof(struct security_info
, protect_kernel_modules
) },
1987 { "ProtectKernelTunables", "b", NULL
, offsetof(struct security_info
, protect_kernel_tunables
) },
1988 { "ProtectKernelLogs", "b", NULL
, offsetof(struct security_info
, protect_kernel_logs
) },
1989 { "ProtectClock", "b", NULL
, offsetof(struct security_info
, protect_clock
) },
1990 { "ProtectSystem", "s", NULL
, offsetof(struct security_info
, protect_system
) },
1991 { "RemoveIPC", "b", NULL
, offsetof(struct security_info
, remove_ipc
) },
1992 { "RestrictAddressFamilies", "(bas)", property_read_restrict_address_families
, 0 },
1993 { "RestrictNamespaces", "t", NULL
, offsetof(struct security_info
, restrict_namespaces
) },
1994 { "RestrictRealtime", "b", NULL
, offsetof(struct security_info
, restrict_realtime
) },
1995 { "RestrictSUIDSGID", "b", NULL
, offsetof(struct security_info
, restrict_suid_sgid
) },
1996 { "RootDirectory", "s", NULL
, offsetof(struct security_info
, root_directory
) },
1997 { "RootImage", "s", NULL
, offsetof(struct security_info
, root_image
) },
1998 { "SupplementaryGroups", "as", NULL
, offsetof(struct security_info
, supplementary_groups
) },
1999 { "SystemCallArchitectures", "as", NULL
, offsetof(struct security_info
, system_call_architectures
) },
2000 { "SystemCallFilter", "(as)", property_read_system_call_filter
, 0 },
2001 { "Type", "s", NULL
, offsetof(struct security_info
, type
) },
2002 { "UMask", "u", NULL
, offsetof(struct security_info
, _umask
) },
2003 { "User", "s", NULL
, offsetof(struct security_info
, user
) },
2007 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2008 _cleanup_free_
char *path
= NULL
;
2011 /* Note: this mangles *info on failure! */
2017 path
= unit_dbus_path_from_name(name
);
2021 r
= bus_map_all_properties(
2023 "org.freedesktop.systemd1",
2026 BUS_MAP_STRDUP
| BUS_MAP_BOOLEAN_AS_BOOL
,
2031 return log_error_errno(r
, "Failed to get unit properties: %s", bus_error_message(&error
, r
));
2033 if (!streq_ptr(info
->load_state
, "loaded")) {
2035 if (FLAGS_SET(flags
, ANALYZE_SECURITY_ONLY_LOADED
))
2036 return -EMEDIUMTYPE
;
2038 if (streq_ptr(info
->load_state
, "not-found"))
2039 log_error("Unit %s not found, cannot analyze.", name
);
2040 else if (streq_ptr(info
->load_state
, "masked"))
2041 log_error("Unit %s is masked, cannot analyze.", name
);
2043 log_error("Unit %s not loaded properly, cannot analyze.", name
);
2048 if (FLAGS_SET(flags
, ANALYZE_SECURITY_ONLY_LONG_RUNNING
) && streq_ptr(info
->type
, "oneshot"))
2049 return -EMEDIUMTYPE
;
2051 if (info
->private_devices
||
2052 info
->private_tmp
||
2053 info
->protect_control_groups
||
2054 info
->protect_kernel_tunables
||
2055 info
->protect_kernel_modules
||
2056 !streq_ptr(info
->protect_home
, "no") ||
2057 !streq_ptr(info
->protect_system
, "no") ||
2059 info
->private_mounts
= true;
2061 if (info
->protect_kernel_modules
)
2062 info
->capability_bounding_set
&= ~(UINT64_C(1) << CAP_SYS_MODULE
);
2064 if (info
->protect_kernel_logs
)
2065 info
->capability_bounding_set
&= ~(UINT64_C(1) << CAP_SYSLOG
);
2067 if (info
->protect_clock
)
2068 info
->capability_bounding_set
&= ~((UINT64_C(1) << CAP_SYS_TIME
) |
2069 (UINT64_C(1) << CAP_WAKE_ALARM
));
2071 if (info
->private_devices
)
2072 info
->capability_bounding_set
&= ~((UINT64_C(1) << CAP_MKNOD
) |
2073 (UINT64_C(1) << CAP_SYS_RAWIO
));
2078 static int analyze_security_one(sd_bus
*bus
, const char *name
, Table
*overview_table
, AnalyzeSecurityFlags flags
) {
2079 _cleanup_(security_info_free
) struct security_info info
= {
2080 .default_dependencies
= true,
2081 .capability_bounding_set
= UINT64_MAX
,
2082 .restrict_namespaces
= UINT64_MAX
,
2090 r
= acquire_security_info(bus
, name
, &info
, flags
);
2091 if (r
== -EMEDIUMTYPE
) /* Ignore this one because not loaded or Type is oneshot */
2096 r
= assess(&info
, overview_table
, flags
);
2103 int analyze_security(sd_bus
*bus
, char **units
, AnalyzeSecurityFlags flags
) {
2104 _cleanup_(table_unrefp
) Table
*overview_table
= NULL
;
2109 if (strv_length(units
) != 1) {
2110 overview_table
= table_new("unit", "exposure", "predicate", "happy");
2111 if (!overview_table
)
2115 if (strv_isempty(units
)) {
2116 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2117 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2118 _cleanup_strv_free_
char **list
= NULL
;
2122 r
= sd_bus_call_method(
2124 "org.freedesktop.systemd1",
2125 "/org/freedesktop/systemd1",
2126 "org.freedesktop.systemd1.Manager",
2132 return log_error_errno(r
, "Failed to list units: %s", bus_error_message(&error
, r
));
2134 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
2136 return bus_log_parse_error(r
);
2142 r
= bus_parse_unit_info(reply
, &info
);
2144 return bus_log_parse_error(r
);
2148 if (!endswith(info
.id
, ".service"))
2151 if (!GREEDY_REALLOC(list
, n
+ 2))
2154 copy
= strdup(info
.id
);
2164 flags
|= ANALYZE_SECURITY_SHORT
|ANALYZE_SECURITY_ONLY_LOADED
|ANALYZE_SECURITY_ONLY_LONG_RUNNING
;
2166 STRV_FOREACH(i
, list
) {
2167 r
= analyze_security_one(bus
, *i
, overview_table
, flags
);
2168 if (r
< 0 && ret
>= 0)
2175 STRV_FOREACH(i
, units
) {
2176 _cleanup_free_
char *mangled
= NULL
, *instance
= NULL
;
2179 if (!FLAGS_SET(flags
, ANALYZE_SECURITY_SHORT
) && i
!= units
) {
2184 r
= unit_name_mangle(*i
, 0, &mangled
);
2186 return log_error_errno(r
, "Failed to mangle unit name '%s': %m", *i
);
2188 if (!endswith(mangled
, ".service"))
2189 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
2190 "Unit %s is not a service unit, refusing.",
2193 if (unit_name_is_valid(mangled
, UNIT_NAME_TEMPLATE
)) {
2194 r
= unit_name_replace_instance(mangled
, "test-instance", &instance
);
2202 r
= analyze_security_one(bus
, name
, overview_table
, flags
);
2203 if (r
< 0 && ret
>= 0)
2208 if (overview_table
) {
2209 if (!FLAGS_SET(flags
, ANALYZE_SECURITY_SHORT
)) {
2214 r
= table_print(overview_table
, stdout
);
2216 return log_error_errno(r
, "Failed to output table: %m");