1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <sys/utsname.h>
6 #include "analyze-security.h"
7 #include "analyze-verify.h"
9 #include "bus-map-properties.h"
10 #include "bus-unit-util.h"
13 #include "format-table.h"
14 #include "in-addr-util.h"
15 #include "locale-util.h"
18 #include "missing_capability.h"
19 #include "missing_sched.h"
20 #include "nulstr-util.h"
21 #include "parse-util.h"
22 #include "path-util.h"
23 #include "pretty-print.h"
25 # include "seccomp-util.h"
29 #include "stdio-util.h"
31 #include "terminal-util.h"
33 #include "unit-name.h"
34 #include "unit-serialize.h"
36 typedef struct SecurityInfo
{
41 bool default_dependencies
;
43 uint64_t ambient_capabilities
;
44 uint64_t capability_bounding_set
;
47 char **supplementary_groups
;
50 bool ip_address_deny_all
;
51 bool ip_address_allow_localhost
;
52 bool ip_address_allow_other
;
54 bool ip_filters_custom_ingress
;
55 bool ip_filters_custom_egress
;
60 bool lock_personality
;
61 bool memory_deny_write_execute
;
62 bool no_new_privileges
;
64 bool protect_hostname
;
72 bool protect_control_groups
;
73 bool protect_kernel_modules
;
74 bool protect_kernel_tunables
;
75 bool protect_kernel_logs
;
83 bool restrict_address_family_inet
;
84 bool restrict_address_family_unix
;
85 bool restrict_address_family_netlink
;
86 bool restrict_address_family_packet
;
87 bool restrict_address_family_other
;
89 unsigned long long restrict_namespaces
;
90 bool restrict_realtime
;
91 bool restrict_suid_sgid
;
98 bool device_allow_non_empty
;
100 Set
*system_call_architectures
;
102 bool system_call_filter_allow_list
;
103 Hashmap
*system_call_filter
;
108 struct security_assessor
{
110 const char *description_good
;
111 const char *description_bad
;
112 const char *description_na
;
117 const struct security_assessor
*a
,
118 const SecurityInfo
*info
,
120 uint64_t *ret_badness
,
121 char **ret_description
);
124 bool default_dependencies_only
;
127 static SecurityInfo
*security_info_new(void) {
128 SecurityInfo
*info
= new(SecurityInfo
, 1);
132 *info
= (SecurityInfo
) {
133 .default_dependencies
= true,
134 .capability_bounding_set
= UINT64_MAX
,
135 .restrict_namespaces
= UINT64_MAX
,
142 static SecurityInfo
*security_info_free(SecurityInfo
*i
) {
149 free(i
->fragment_path
);
153 free(i
->protect_home
);
154 free(i
->protect_system
);
156 free(i
->root_directory
);
159 free(i
->keyring_mode
);
160 free(i
->protect_proc
);
161 free(i
->proc_subset
);
162 free(i
->notify_access
);
164 free(i
->device_policy
);
166 strv_free(i
->supplementary_groups
);
167 set_free(i
->system_call_architectures
);
169 hashmap_free(i
->system_call_filter
);
174 DEFINE_TRIVIAL_CLEANUP_FUNC(SecurityInfo
*, security_info_free
);
176 static bool security_info_runs_privileged(const SecurityInfo
*i
) {
179 if (STRPTR_IN_SET(i
->user
, "0", "root"))
185 return isempty(i
->user
);
188 static int assess_bool(
189 const struct security_assessor
*a
,
190 const SecurityInfo
*info
,
192 uint64_t *ret_badness
,
193 char **ret_description
) {
195 const bool *b
= data
;
199 assert(ret_description
);
201 *ret_badness
= a
->parameter
? *b
: !*b
;
202 *ret_description
= NULL
;
207 static int assess_user(
208 const struct security_assessor
*a
,
209 const SecurityInfo
*info
,
211 uint64_t *ret_badness
,
212 char **ret_description
) {
214 _cleanup_free_
char *d
= NULL
;
218 assert(ret_description
);
220 if (streq_ptr(info
->user
, NOBODY_USER_NAME
)) {
221 d
= strdup("Service runs under as '" NOBODY_USER_NAME
"' user, which should not be used for services");
223 } else if (info
->dynamic_user
&& !STR_IN_SET(info
->user
, "0", "root")) {
224 d
= strdup("Service runs under a transient non-root user identity");
226 } else if (info
->user
&& !STR_IN_SET(info
->user
, "0", "root", "")) {
227 d
= strdup("Service runs under a static non-root user identity");
231 *ret_description
= NULL
;
239 *ret_description
= TAKE_PTR(d
);
244 static int assess_protect_home(
245 const struct security_assessor
*a
,
246 const SecurityInfo
*info
,
248 uint64_t *ret_badness
,
249 char **ret_description
) {
251 const char *description
;
257 assert(ret_description
);
260 description
= "Service has full access to home directories";
262 r
= parse_boolean(info
->protect_home
);
264 if (streq_ptr(info
->protect_home
, "read-only")) {
266 description
= "Service has read-only access to home directories";
267 } else if (streq_ptr(info
->protect_home
, "tmpfs")) {
269 description
= "Service has access to fake empty home directories";
273 description
= "Service has no access to home directories";
276 copy
= strdup(description
);
280 *ret_badness
= badness
;
281 *ret_description
= copy
;
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
;
299 assert(ret_description
);
302 description
= "Service has full access to the OS file hierarchy";
304 r
= parse_boolean(info
->protect_system
);
306 if (streq_ptr(info
->protect_system
, "full")) {
308 description
= "Service has very limited write access to the OS file hierarchy";
309 } else if (streq_ptr(info
->protect_system
, "strict")) {
311 description
= "Service has strict read-only access to the OS file hierarchy";
315 description
= "Service has limited write access to the OS file hierarchy";
318 copy
= strdup(description
);
322 *ret_badness
= badness
;
323 *ret_description
= copy
;
328 static int assess_root_directory(
329 const struct security_assessor
*a
,
330 const SecurityInfo
*info
,
332 uint64_t *ret_badness
,
333 char **ret_description
) {
336 assert(ret_description
);
339 empty_or_root(info
->root_directory
) &&
340 empty_or_root(info
->root_image
);
341 *ret_description
= NULL
;
346 static int assess_capability_bounding_set(
347 const struct security_assessor
*a
,
348 const SecurityInfo
*info
,
350 uint64_t *ret_badness
,
351 char **ret_description
) {
354 assert(ret_description
);
356 *ret_badness
= !!(info
->capability_bounding_set
& a
->parameter
);
357 *ret_description
= NULL
;
362 static int assess_umask(
363 const struct security_assessor
*a
,
364 const SecurityInfo
*info
,
366 uint64_t *ret_badness
,
367 char **ret_description
) {
374 assert(ret_description
);
376 if (!FLAGS_SET(info
->_umask
, 0002)) {
377 d
= "Files created by service are world-writable by default";
379 } else if (!FLAGS_SET(info
->_umask
, 0004)) {
380 d
= "Files created by service are world-readable by default";
382 } else if (!FLAGS_SET(info
->_umask
, 0020)) {
383 d
= "Files created by service are group-writable by default";
385 } else if (!FLAGS_SET(info
->_umask
, 0040)) {
386 d
= "Files created by service are group-readable by default";
389 d
= "Files created by service are accessible only by service's own user by default";
398 *ret_description
= copy
;
403 static int assess_keyring_mode(
404 const struct security_assessor
*a
,
405 const SecurityInfo
*info
,
407 uint64_t *ret_badness
,
408 char **ret_description
) {
411 assert(ret_description
);
413 *ret_badness
= !streq_ptr(info
->keyring_mode
, "private");
414 *ret_description
= NULL
;
419 static int assess_protect_proc(
420 const struct security_assessor
*a
,
421 const SecurityInfo
*info
,
423 uint64_t *ret_badness
,
424 char **ret_description
) {
427 assert(ret_description
);
429 if (streq_ptr(info
->protect_proc
, "noaccess"))
431 else if (STRPTR_IN_SET(info
->protect_proc
, "invisible", "ptraceable"))
436 *ret_description
= NULL
;
441 static int assess_proc_subset(
442 const struct security_assessor
*a
,
443 const SecurityInfo
*info
,
445 uint64_t *ret_badness
,
446 char **ret_description
) {
449 assert(ret_description
);
451 *ret_badness
= !streq_ptr(info
->proc_subset
, "pid");
452 *ret_description
= NULL
;
457 static int assess_notify_access(
458 const struct security_assessor
*a
,
459 const SecurityInfo
*info
,
461 uint64_t *ret_badness
,
462 char **ret_description
) {
465 assert(ret_description
);
467 *ret_badness
= streq_ptr(info
->notify_access
, "all");
468 *ret_description
= NULL
;
473 static int assess_remove_ipc(
474 const struct security_assessor
*a
,
475 const SecurityInfo
*info
,
477 uint64_t *ret_badness
,
478 char **ret_description
) {
481 assert(ret_description
);
483 if (security_info_runs_privileged(info
))
484 *ret_badness
= UINT64_MAX
;
486 *ret_badness
= !info
->remove_ipc
;
488 *ret_description
= NULL
;
492 static int assess_supplementary_groups(
493 const struct security_assessor
*a
,
494 const SecurityInfo
*info
,
496 uint64_t *ret_badness
,
497 char **ret_description
) {
500 assert(ret_description
);
502 if (security_info_runs_privileged(info
))
503 *ret_badness
= UINT64_MAX
;
505 *ret_badness
= !strv_isempty(info
->supplementary_groups
);
507 *ret_description
= NULL
;
511 static int assess_restrict_namespaces(
512 const struct security_assessor
*a
,
513 const SecurityInfo
*info
,
515 uint64_t *ret_badness
,
516 char **ret_description
) {
519 assert(ret_description
);
521 *ret_badness
= !!(info
->restrict_namespaces
& a
->parameter
);
522 *ret_description
= NULL
;
527 static int assess_system_call_architectures(
528 const struct security_assessor
*a
,
529 const SecurityInfo
*info
,
531 uint64_t *ret_badness
,
532 char **ret_description
) {
538 assert(ret_description
);
540 if (set_isempty(info
->system_call_architectures
)) {
542 d
= strdup("Service may execute system calls with all ABIs");
543 } else if (set_contains(info
->system_call_architectures
, "native") &&
544 set_size(info
->system_call_architectures
) == 1) {
546 d
= strdup("Service may execute system calls only with native ABI");
549 d
= strdup("Service may execute system calls with multiple ABIs");
556 *ret_description
= d
;
563 static bool syscall_names_in_filter(Hashmap
*s
, bool allow_list
, const SyscallFilterSet
*f
, const char **ret_offending_syscall
) {
566 NULSTR_FOREACH(syscall
, f
->value
) {
569 if (syscall
[0] == '@') {
570 const SyscallFilterSet
*g
;
572 assert_se(g
= syscall_filter_set_find(syscall
));
573 if (syscall_names_in_filter(s
, allow_list
, g
, ret_offending_syscall
))
574 return true; /* bad! */
579 /* Let's see if the system call actually exists on this platform, before complaining */
580 id
= seccomp_syscall_resolve_name(syscall
);
584 if (hashmap_contains(s
, syscall
) == allow_list
) {
585 log_debug("Offending syscall filter item: %s", syscall
);
586 if (ret_offending_syscall
)
587 *ret_offending_syscall
= syscall
;
588 return true; /* bad! */
592 *ret_offending_syscall
= NULL
;
596 static int assess_system_call_filter(
597 const struct security_assessor
*a
,
598 const SecurityInfo
*info
,
600 uint64_t *ret_badness
,
601 char **ret_description
) {
606 assert(ret_description
);
608 assert(a
->parameter
< _SYSCALL_FILTER_SET_MAX
);
609 const SyscallFilterSet
*f
= syscall_filter_sets
+ a
->parameter
;
611 _cleanup_free_
char *d
= NULL
;
615 if (!info
->system_call_filter_allow_list
&& hashmap_isempty(info
->system_call_filter
)) {
616 r
= free_and_strdup(&d
, "Service does not filter system calls");
620 const char *offender
= NULL
;
622 log_debug("Analyzing system call filter, checking against: %s", f
->name
);
623 bad
= syscall_names_in_filter(info
->system_call_filter
, info
->system_call_filter_allow_list
, f
, &offender
);
624 log_debug("Result: %s", bad
? "bad" : "good");
626 if (info
->system_call_filter_allow_list
) {
628 r
= asprintf(&d
, "System call allow list defined for service, and %s is included "
629 "(e.g. %s is allowed)",
633 r
= asprintf(&d
, "System call allow list defined for service, and %s is not included",
639 r
= asprintf(&d
, "System call deny list defined for service, and %s is not included "
640 "(e.g. %s is allowed)",
644 r
= asprintf(&d
, "System call deny list defined for service, and %s is included",
654 *ret_description
= TAKE_PTR(d
);
661 static int assess_ip_address_allow(
662 const struct security_assessor
*a
,
663 const SecurityInfo
*info
,
665 uint64_t *ret_badness
,
666 char **ret_description
) {
673 assert(ret_description
);
675 if (info
->ip_filters_custom_ingress
|| info
->ip_filters_custom_egress
) {
676 d
= strdup("Service defines custom ingress/egress IP filters with BPF programs");
678 } else if (!info
->ip_address_deny_all
) {
679 d
= strdup("Service does not define an IP address allow list");
681 } else if (info
->ip_address_allow_other
) {
682 d
= strdup("Service defines IP address allow list with non-localhost entries");
684 } else if (info
->ip_address_allow_localhost
) {
685 d
= strdup("Service defines IP address allow list with only localhost entries");
688 d
= strdup("Service blocks all IP address ranges");
696 *ret_description
= d
;
701 static int assess_device_allow(
702 const struct security_assessor
*a
,
703 const SecurityInfo
*info
,
705 uint64_t *ret_badness
,
706 char **ret_description
) {
713 assert(ret_description
);
715 if (STRPTR_IN_SET(info
->device_policy
, "strict", "closed")) {
717 if (info
->device_allow_non_empty
) {
718 d
= strdup("Service has a device ACL with some special devices");
721 d
= strdup("Service has a minimal device ACL");
725 d
= strdup("Service has no device ACL");
733 *ret_description
= d
;
738 static int assess_ambient_capabilities(
739 const struct security_assessor
*a
,
740 const SecurityInfo
*info
,
742 uint64_t *ret_badness
,
743 char **ret_description
) {
746 assert(ret_description
);
748 *ret_badness
= info
->ambient_capabilities
!= 0;
749 *ret_description
= NULL
;
754 static const struct security_assessor security_assessor_table
[] = {
756 .id
= "User=/DynamicUser=",
757 .description_bad
= "Service runs as root user",
758 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#User=",
761 .assess
= assess_user
,
764 .id
= "SupplementaryGroups=",
765 .description_good
= "Service has no supplementary groups",
766 .description_bad
= "Service runs with supplementary groups",
767 .description_na
= "Service runs as root, option does not matter",
768 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SupplementaryGroups=",
771 .assess
= assess_supplementary_groups
,
774 .id
= "PrivateDevices=",
775 .description_good
= "Service has no access to hardware devices",
776 .description_bad
= "Service potentially has access to hardware devices",
777 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateDevices=",
780 .assess
= assess_bool
,
781 .offset
= offsetof(SecurityInfo
, private_devices
),
784 .id
= "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 .description_good
= "Service has no access to the host's network",
796 .description_bad
= "Service has access to the host's network",
797 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateNetwork=",
800 .assess
= assess_bool
,
801 .offset
= offsetof(SecurityInfo
, private_network
),
805 .description_good
= "Service has no access to other software's temporary files",
806 .description_bad
= "Service has access to other software's temporary files",
807 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateTmp=",
810 .assess
= assess_bool
,
811 .offset
= offsetof(SecurityInfo
, private_tmp
),
812 .default_dependencies_only
= true,
815 .id
= "PrivateUsers=",
816 .description_good
= "Service does not have access to other users",
817 .description_bad
= "Service has access to other users",
818 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateUsers=",
821 .assess
= assess_bool
,
822 .offset
= offsetof(SecurityInfo
, private_users
),
825 .id
= "ProtectControlGroups=",
826 .description_good
= "Service cannot modify the control group file system",
827 .description_bad
= "Service may modify the control group file system",
828 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectControlGroups=",
831 .assess
= assess_bool
,
832 .offset
= offsetof(SecurityInfo
, protect_control_groups
),
835 .id
= "ProtectKernelModules=",
836 .description_good
= "Service cannot load or read kernel modules",
837 .description_bad
= "Service may load or read kernel modules",
838 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelModules=",
841 .assess
= assess_bool
,
842 .offset
= offsetof(SecurityInfo
, protect_kernel_modules
),
845 .id
= "ProtectKernelTunables=",
846 .description_good
= "Service cannot alter kernel tunables (/proc/sys, …)",
847 .description_bad
= "Service may alter kernel tunables",
848 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelTunables=",
851 .assess
= assess_bool
,
852 .offset
= offsetof(SecurityInfo
, protect_kernel_tunables
),
855 .id
= "ProtectKernelLogs=",
856 .description_good
= "Service cannot read from or write to the kernel log ring buffer",
857 .description_bad
= "Service may read from or write to the kernel log ring buffer",
858 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelLogs=",
861 .assess
= assess_bool
,
862 .offset
= offsetof(SecurityInfo
, protect_kernel_logs
),
865 .id
= "ProtectClock=",
866 .description_good
= "Service cannot write to the hardware clock or system clock",
867 .description_bad
= "Service may write to the hardware clock or system clock",
868 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectClock=",
871 .assess
= assess_bool
,
872 .offset
= offsetof(SecurityInfo
, protect_clock
),
875 .id
= "ProtectHome=",
876 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHome=",
879 .assess
= assess_protect_home
,
880 .default_dependencies_only
= true,
883 .id
= "ProtectHostname=",
884 .description_good
= "Service cannot change system host/domainname",
885 .description_bad
= "Service may change system host/domainname",
886 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHostname=",
889 .assess
= assess_bool
,
890 .offset
= offsetof(SecurityInfo
, protect_hostname
),
893 .id
= "ProtectSystem=",
894 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectSystem=",
897 .assess
= assess_protect_system
,
898 .default_dependencies_only
= true,
901 .id
= "RootDirectory=/RootImage=",
902 .description_good
= "Service has its own root directory/image",
903 .description_bad
= "Service runs within the host's root directory",
904 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RootDirectory=",
907 .assess
= assess_root_directory
,
908 .default_dependencies_only
= true,
911 .id
= "LockPersonality=",
912 .description_good
= "Service cannot change ABI personality",
913 .description_bad
= "Service may change ABI personality",
914 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#LockPersonality=",
917 .assess
= assess_bool
,
918 .offset
= offsetof(SecurityInfo
, lock_personality
),
921 .id
= "MemoryDenyWriteExecute=",
922 .description_good
= "Service cannot create writable executable memory mappings",
923 .description_bad
= "Service may create writable executable memory mappings",
924 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#MemoryDenyWriteExecute=",
927 .assess
= assess_bool
,
928 .offset
= offsetof(SecurityInfo
, memory_deny_write_execute
),
931 .id
= "NoNewPrivileges=",
932 .description_good
= "Service processes cannot acquire new privileges",
933 .description_bad
= "Service processes may acquire new privileges",
934 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NoNewPrivileges=",
937 .assess
= assess_bool
,
938 .offset
= offsetof(SecurityInfo
, no_new_privileges
),
941 .id
= "CapabilityBoundingSet=~CAP_SYS_ADMIN",
942 .description_good
= "Service has no administrator privileges",
943 .description_bad
= "Service has administrator privileges",
944 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
947 .assess
= assess_capability_bounding_set
,
948 .parameter
= UINT64_C(1) << CAP_SYS_ADMIN
,
951 .id
= "CapabilityBoundingSet=~CAP_SET(UID|GID|PCAP)",
952 .description_good
= "Service cannot change UID/GID identities/capabilities",
953 .description_bad
= "Service may change UID/GID identities/capabilities",
954 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
957 .assess
= assess_capability_bounding_set
,
958 .parameter
= (UINT64_C(1) << CAP_SETUID
)|
959 (UINT64_C(1) << CAP_SETGID
)|
960 (UINT64_C(1) << CAP_SETPCAP
),
963 .id
= "CapabilityBoundingSet=~CAP_SYS_PTRACE",
964 .description_good
= "Service has no ptrace() debugging abilities",
965 .description_bad
= "Service has ptrace() debugging abilities",
966 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
969 .assess
= assess_capability_bounding_set
,
970 .parameter
= (UINT64_C(1) << CAP_SYS_PTRACE
),
973 .id
= "CapabilityBoundingSet=~CAP_SYS_TIME",
974 .description_good
= "Service processes cannot change the system clock",
975 .description_bad
= "Service processes may change the system clock",
976 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
979 .assess
= assess_capability_bounding_set
,
980 .parameter
= UINT64_C(1) << CAP_SYS_TIME
,
983 .id
= "CapabilityBoundingSet=~CAP_NET_ADMIN",
984 .description_good
= "Service has no network configuration privileges",
985 .description_bad
= "Service has network configuration privileges",
986 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
989 .assess
= assess_capability_bounding_set
,
990 .parameter
= (UINT64_C(1) << CAP_NET_ADMIN
),
993 .id
= "CapabilityBoundingSet=~CAP_SYS_RAWIO",
994 .description_good
= "Service has no raw I/O access",
995 .description_bad
= "Service has raw I/O access",
996 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
999 .assess
= assess_capability_bounding_set
,
1000 .parameter
= (UINT64_C(1) << CAP_SYS_RAWIO
),
1003 .id
= "CapabilityBoundingSet=~CAP_SYS_MODULE",
1004 .description_good
= "Service cannot load kernel modules",
1005 .description_bad
= "Service may load kernel modules",
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_SYS_MODULE
),
1013 .id
= "CapabilityBoundingSet=~CAP_AUDIT_*",
1014 .description_good
= "Service has no audit subsystem access",
1015 .description_bad
= "Service has audit subsystem access",
1016 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1019 .assess
= assess_capability_bounding_set
,
1020 .parameter
= (UINT64_C(1) << CAP_AUDIT_CONTROL
) |
1021 (UINT64_C(1) << CAP_AUDIT_READ
) |
1022 (UINT64_C(1) << CAP_AUDIT_WRITE
),
1025 .id
= "CapabilityBoundingSet=~CAP_SYSLOG",
1026 .description_good
= "Service has no access to kernel logging",
1027 .description_bad
= "Service has access to kernel logging",
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_SYSLOG
),
1035 .id
= "CapabilityBoundingSet=~CAP_SYS_(NICE|RESOURCE)",
1036 .description_good
= "Service has no privileges to change resource use parameters",
1037 .description_bad
= "Service has privileges to change resource use parameters",
1038 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1041 .assess
= assess_capability_bounding_set
,
1042 .parameter
= (UINT64_C(1) << CAP_SYS_NICE
) |
1043 (UINT64_C(1) << CAP_SYS_RESOURCE
),
1046 .id
= "CapabilityBoundingSet=~CAP_MKNOD",
1047 .description_good
= "Service cannot create device nodes",
1048 .description_bad
= "Service may create device nodes",
1049 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1052 .assess
= assess_capability_bounding_set
,
1053 .parameter
= (UINT64_C(1) << CAP_MKNOD
),
1056 .id
= "CapabilityBoundingSet=~CAP_(CHOWN|FSETID|SETFCAP)",
1057 .description_good
= "Service cannot change file ownership/access mode/capabilities",
1058 .description_bad
= "Service may change file ownership/access mode/capabilities unrestricted",
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_CHOWN
) |
1064 (UINT64_C(1) << CAP_FSETID
) |
1065 (UINT64_C(1) << CAP_SETFCAP
),
1068 .id
= "CapabilityBoundingSet=~CAP_(DAC_*|FOWNER|IPC_OWNER)",
1069 .description_good
= "Service cannot override UNIX file/IPC permission checks",
1070 .description_bad
= "Service may override UNIX file/IPC permission checks",
1071 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1074 .assess
= assess_capability_bounding_set
,
1075 .parameter
= (UINT64_C(1) << CAP_DAC_OVERRIDE
) |
1076 (UINT64_C(1) << CAP_DAC_READ_SEARCH
) |
1077 (UINT64_C(1) << CAP_FOWNER
) |
1078 (UINT64_C(1) << CAP_IPC_OWNER
),
1081 .id
= "CapabilityBoundingSet=~CAP_KILL",
1082 .description_good
= "Service cannot send UNIX signals to arbitrary processes",
1083 .description_bad
= "Service may send UNIX signals to arbitrary processes",
1084 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1087 .assess
= assess_capability_bounding_set
,
1088 .parameter
= (UINT64_C(1) << CAP_KILL
),
1091 .id
= "CapabilityBoundingSet=~CAP_NET_(BIND_SERVICE|BROADCAST|RAW)",
1092 .description_good
= "Service has no elevated networking privileges",
1093 .description_bad
= "Service has elevated networking privileges",
1094 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1097 .assess
= assess_capability_bounding_set
,
1098 .parameter
= (UINT64_C(1) << CAP_NET_BIND_SERVICE
) |
1099 (UINT64_C(1) << CAP_NET_BROADCAST
) |
1100 (UINT64_C(1) << CAP_NET_RAW
),
1103 .id
= "CapabilityBoundingSet=~CAP_SYS_BOOT",
1104 .description_good
= "Service cannot issue reboot()",
1105 .description_bad
= "Service may issue reboot()",
1106 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1109 .assess
= assess_capability_bounding_set
,
1110 .parameter
= (UINT64_C(1) << CAP_SYS_BOOT
),
1113 .id
= "CapabilityBoundingSet=~CAP_MAC_*",
1114 .description_good
= "Service cannot adjust SMACK MAC",
1115 .description_bad
= "Service may adjust SMACK MAC",
1116 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1119 .assess
= assess_capability_bounding_set
,
1120 .parameter
= (UINT64_C(1) << CAP_MAC_ADMIN
)|
1121 (UINT64_C(1) << CAP_MAC_OVERRIDE
),
1124 .id
= "CapabilityBoundingSet=~CAP_LINUX_IMMUTABLE",
1125 .description_good
= "Service cannot mark files immutable",
1126 .description_bad
= "Service may mark files immutable",
1127 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1130 .assess
= assess_capability_bounding_set
,
1131 .parameter
= (UINT64_C(1) << CAP_LINUX_IMMUTABLE
),
1134 .id
= "CapabilityBoundingSet=~CAP_IPC_LOCK",
1135 .description_good
= "Service cannot lock memory into RAM",
1136 .description_bad
= "Service may lock memory into RAM",
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_IPC_LOCK
),
1144 .id
= "CapabilityBoundingSet=~CAP_SYS_CHROOT",
1145 .description_good
= "Service cannot issue chroot()",
1146 .description_bad
= "Service may issue chroot()",
1147 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1150 .assess
= assess_capability_bounding_set
,
1151 .parameter
= (UINT64_C(1) << CAP_SYS_CHROOT
),
1154 .id
= "CapabilityBoundingSet=~CAP_BLOCK_SUSPEND",
1155 .description_good
= "Service cannot establish wake locks",
1156 .description_bad
= "Service may establish wake locks",
1157 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1160 .assess
= assess_capability_bounding_set
,
1161 .parameter
= (UINT64_C(1) << CAP_BLOCK_SUSPEND
),
1164 .id
= "CapabilityBoundingSet=~CAP_WAKE_ALARM",
1165 .description_good
= "Service cannot program timers that wake up the system",
1166 .description_bad
= "Service may program timers that wake up the system",
1167 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1170 .assess
= assess_capability_bounding_set
,
1171 .parameter
= (UINT64_C(1) << CAP_WAKE_ALARM
),
1174 .id
= "CapabilityBoundingSet=~CAP_LEASE",
1175 .description_good
= "Service cannot create file leases",
1176 .description_bad
= "Service may create file leases",
1177 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1180 .assess
= assess_capability_bounding_set
,
1181 .parameter
= (UINT64_C(1) << CAP_LEASE
),
1184 .id
= "CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG",
1185 .description_good
= "Service cannot issue vhangup()",
1186 .description_bad
= "Service may issue vhangup()",
1187 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1190 .assess
= assess_capability_bounding_set
,
1191 .parameter
= (UINT64_C(1) << CAP_SYS_TTY_CONFIG
),
1194 .id
= "CapabilityBoundingSet=~CAP_SYS_PACCT",
1195 .description_good
= "Service cannot use acct()",
1196 .description_bad
= "Service may use acct()",
1197 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1200 .assess
= assess_capability_bounding_set
,
1201 .parameter
= (UINT64_C(1) << CAP_SYS_PACCT
),
1205 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#UMask=",
1208 .assess
= assess_umask
,
1211 .id
= "KeyringMode=",
1212 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#KeyringMode=",
1213 .description_good
= "Service doesn't share key material with other services",
1214 .description_bad
= "Service shares key material with other service",
1217 .assess
= assess_keyring_mode
,
1220 .id
= "ProtectProc=",
1221 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectProc=",
1222 .description_good
= "Service has restricted access to process tree (/proc hidepid=)",
1223 .description_bad
= "Service has full access to process tree (/proc hidepid=)",
1226 .assess
= assess_protect_proc
,
1229 .id
= "ProcSubset=",
1230 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProcSubset=",
1231 .description_good
= "Service has no access to non-process /proc files (/proc subset=)",
1232 .description_bad
= "Service has full access to non-process /proc files (/proc subset=)",
1235 .assess
= assess_proc_subset
,
1238 .id
= "NotifyAccess=",
1239 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NotifyAccess=",
1240 .description_good
= "Service child processes cannot alter service state",
1241 .description_bad
= "Service child processes may alter service state",
1244 .assess
= assess_notify_access
,
1248 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RemoveIPC=",
1249 .description_good
= "Service user cannot leave SysV IPC objects around",
1250 .description_bad
= "Service user may leave SysV IPC objects around",
1251 .description_na
= "Service runs as root, option does not apply",
1254 .assess
= assess_remove_ipc
,
1255 .offset
= offsetof(SecurityInfo
, remove_ipc
),
1259 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Delegate=",
1260 .description_good
= "Service does not maintain its own delegated control group subtree",
1261 .description_bad
= "Service maintains its own delegated control group subtree",
1264 .assess
= assess_bool
,
1265 .offset
= offsetof(SecurityInfo
, delegate
),
1266 .parameter
= true, /* invert! */
1269 .id
= "RestrictRealtime=",
1270 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictRealtime=",
1271 .description_good
= "Service realtime scheduling access is restricted",
1272 .description_bad
= "Service may acquire realtime scheduling",
1275 .assess
= assess_bool
,
1276 .offset
= offsetof(SecurityInfo
, restrict_realtime
),
1279 .id
= "RestrictSUIDSGID=",
1280 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictSUIDSGID=",
1281 .description_good
= "SUID/SGID file creation by service is restricted",
1282 .description_bad
= "Service may create SUID/SGID files",
1285 .assess
= assess_bool
,
1286 .offset
= offsetof(SecurityInfo
, restrict_suid_sgid
),
1289 .id
= "RestrictNamespaces=~CLONE_NEWUSER",
1290 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1291 .description_good
= "Service cannot create user namespaces",
1292 .description_bad
= "Service may create user namespaces",
1295 .assess
= assess_restrict_namespaces
,
1296 .parameter
= CLONE_NEWUSER
,
1299 .id
= "RestrictNamespaces=~CLONE_NEWNS",
1300 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1301 .description_good
= "Service cannot create file system namespaces",
1302 .description_bad
= "Service may create file system namespaces",
1305 .assess
= assess_restrict_namespaces
,
1306 .parameter
= CLONE_NEWNS
,
1309 .id
= "RestrictNamespaces=~CLONE_NEWIPC",
1310 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1311 .description_good
= "Service cannot create IPC namespaces",
1312 .description_bad
= "Service may create IPC namespaces",
1315 .assess
= assess_restrict_namespaces
,
1316 .parameter
= CLONE_NEWIPC
,
1319 .id
= "RestrictNamespaces=~CLONE_NEWPID",
1320 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1321 .description_good
= "Service cannot create process namespaces",
1322 .description_bad
= "Service may create process namespaces",
1325 .assess
= assess_restrict_namespaces
,
1326 .parameter
= CLONE_NEWPID
,
1329 .id
= "RestrictNamespaces=~CLONE_NEWCGROUP",
1330 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1331 .description_good
= "Service cannot create cgroup namespaces",
1332 .description_bad
= "Service may create cgroup namespaces",
1335 .assess
= assess_restrict_namespaces
,
1336 .parameter
= CLONE_NEWCGROUP
,
1339 .id
= "RestrictNamespaces=~CLONE_NEWNET",
1340 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1341 .description_good
= "Service cannot create network namespaces",
1342 .description_bad
= "Service may create network namespaces",
1345 .assess
= assess_restrict_namespaces
,
1346 .parameter
= CLONE_NEWNET
,
1349 .id
= "RestrictNamespaces=~CLONE_NEWUTS",
1350 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1351 .description_good
= "Service cannot create hostname namespaces",
1352 .description_bad
= "Service may create hostname namespaces",
1355 .assess
= assess_restrict_namespaces
,
1356 .parameter
= CLONE_NEWUTS
,
1359 .id
= "RestrictAddressFamilies=~AF_(INET|INET6)",
1360 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1361 .description_good
= "Service cannot allocate Internet sockets",
1362 .description_bad
= "Service may allocate Internet sockets",
1365 .assess
= assess_bool
,
1366 .offset
= offsetof(SecurityInfo
, restrict_address_family_inet
),
1369 .id
= "RestrictAddressFamilies=~AF_UNIX",
1370 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1371 .description_good
= "Service cannot allocate local sockets",
1372 .description_bad
= "Service may allocate local sockets",
1375 .assess
= assess_bool
,
1376 .offset
= offsetof(SecurityInfo
, restrict_address_family_unix
),
1379 .id
= "RestrictAddressFamilies=~AF_NETLINK",
1380 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1381 .description_good
= "Service cannot allocate netlink sockets",
1382 .description_bad
= "Service may allocate netlink sockets",
1385 .assess
= assess_bool
,
1386 .offset
= offsetof(SecurityInfo
, restrict_address_family_netlink
),
1389 .id
= "RestrictAddressFamilies=~AF_PACKET",
1390 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1391 .description_good
= "Service cannot allocate packet sockets",
1392 .description_bad
= "Service may allocate packet sockets",
1395 .assess
= assess_bool
,
1396 .offset
= offsetof(SecurityInfo
, restrict_address_family_packet
),
1399 .id
= "RestrictAddressFamilies=~…",
1400 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1401 .description_good
= "Service cannot allocate exotic sockets",
1402 .description_bad
= "Service may allocate exotic sockets",
1405 .assess
= assess_bool
,
1406 .offset
= offsetof(SecurityInfo
, restrict_address_family_other
),
1409 .id
= "SystemCallArchitectures=",
1410 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallArchitectures=",
1413 .assess
= assess_system_call_architectures
,
1417 .id
= "SystemCallFilter=~@swap",
1418 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1421 .assess
= assess_system_call_filter
,
1422 .parameter
= SYSCALL_FILTER_SET_SWAP
,
1425 .id
= "SystemCallFilter=~@obsolete",
1426 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1429 .assess
= assess_system_call_filter
,
1430 .parameter
= SYSCALL_FILTER_SET_OBSOLETE
,
1433 .id
= "SystemCallFilter=~@clock",
1434 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1437 .assess
= assess_system_call_filter
,
1438 .parameter
= SYSCALL_FILTER_SET_CLOCK
,
1441 .id
= "SystemCallFilter=~@cpu-emulation",
1442 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1445 .assess
= assess_system_call_filter
,
1446 .parameter
= SYSCALL_FILTER_SET_CPU_EMULATION
,
1449 .id
= "SystemCallFilter=~@debug",
1450 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1453 .assess
= assess_system_call_filter
,
1454 .parameter
= SYSCALL_FILTER_SET_DEBUG
,
1457 .id
= "SystemCallFilter=~@mount",
1458 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1461 .assess
= assess_system_call_filter
,
1462 .parameter
= SYSCALL_FILTER_SET_MOUNT
,
1465 .id
= "SystemCallFilter=~@module",
1466 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1469 .assess
= assess_system_call_filter
,
1470 .parameter
= SYSCALL_FILTER_SET_MODULE
,
1473 .id
= "SystemCallFilter=~@raw-io",
1474 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1477 .assess
= assess_system_call_filter
,
1478 .parameter
= SYSCALL_FILTER_SET_RAW_IO
,
1481 .id
= "SystemCallFilter=~@reboot",
1482 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1485 .assess
= assess_system_call_filter
,
1486 .parameter
= SYSCALL_FILTER_SET_REBOOT
,
1489 .id
= "SystemCallFilter=~@privileged",
1490 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1493 .assess
= assess_system_call_filter
,
1494 .parameter
= SYSCALL_FILTER_SET_PRIVILEGED
,
1497 .id
= "SystemCallFilter=~@resources",
1498 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1501 .assess
= assess_system_call_filter
,
1502 .parameter
= SYSCALL_FILTER_SET_RESOURCES
,
1506 .id
= "IPAddressDeny=",
1507 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#IPAddressDeny=",
1510 .assess
= assess_ip_address_allow
,
1513 .id
= "DeviceAllow=",
1514 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#DeviceAllow=",
1517 .assess
= assess_device_allow
,
1520 .id
= "AmbientCapabilities=",
1521 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#AmbientCapabilities=",
1522 .description_good
= "Service process does not receive ambient capabilities",
1523 .description_bad
= "Service process receives ambient capabilities",
1526 .assess
= assess_ambient_capabilities
,
1530 static int assess(const SecurityInfo
*info
, Table
*overview_table
, AnalyzeSecurityFlags flags
, unsigned threshold
) {
1531 static const struct {
1535 SpecialGlyph smiley
;
1536 } badness_table
[] = {
1537 { 100, "DANGEROUS", ANSI_HIGHLIGHT_RED
, SPECIAL_GLYPH_DEPRESSED_SMILEY
},
1538 { 90, "UNSAFE", ANSI_HIGHLIGHT_RED
, SPECIAL_GLYPH_UNHAPPY_SMILEY
},
1539 { 75, "EXPOSED", ANSI_HIGHLIGHT_YELLOW
, SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY
},
1540 { 50, "MEDIUM", NULL
, SPECIAL_GLYPH_NEUTRAL_SMILEY
},
1541 { 10, "OK", ANSI_HIGHLIGHT_GREEN
, SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY
},
1542 { 1, "SAFE", ANSI_HIGHLIGHT_GREEN
, SPECIAL_GLYPH_HAPPY_SMILEY
},
1543 { 0, "PERFECT", ANSI_HIGHLIGHT_GREEN
, SPECIAL_GLYPH_ECSTATIC_SMILEY
},
1546 uint64_t badness_sum
= 0, weight_sum
= 0, exposure
;
1547 _cleanup_(table_unrefp
) Table
*details_table
= NULL
;
1551 if (!FLAGS_SET(flags
, ANALYZE_SECURITY_SHORT
)) {
1552 details_table
= table_new(" ", "name", "description", "weight", "badness", "range", "exposure");
1556 (void) table_set_sort(details_table
, (size_t) 3, (size_t) 1);
1557 (void) table_set_reverse(details_table
, 3, true);
1559 if (getenv_bool("SYSTEMD_ANALYZE_DEBUG") <= 0)
1560 (void) table_set_display(details_table
, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 6);
1563 for (i
= 0; i
< ELEMENTSOF(security_assessor_table
); i
++) {
1564 const struct security_assessor
*a
= security_assessor_table
+ i
;
1565 _cleanup_free_
char *d
= NULL
;
1569 data
= (uint8_t *) info
+ a
->offset
;
1571 if (a
->default_dependencies_only
&& !info
->default_dependencies
) {
1572 badness
= UINT64_MAX
;
1573 d
= strdup("Service runs in special boot phase, option is not appropriate");
1577 r
= a
->assess(a
, info
, data
, &badness
, &d
);
1582 assert(a
->range
> 0);
1584 if (badness
!= UINT64_MAX
) {
1585 assert(badness
<= a
->range
);
1587 badness_sum
+= DIV_ROUND_UP(badness
* a
->weight
, a
->range
);
1588 weight_sum
+= a
->weight
;
1591 if (details_table
) {
1592 const char *checkmark
, *description
, *color
= NULL
;
1594 if (badness
== UINT64_MAX
) {
1596 description
= a
->description_na
;
1598 } else if (badness
== a
->range
) {
1599 checkmark
= special_glyph(SPECIAL_GLYPH_CROSS_MARK
);
1600 description
= a
->description_bad
;
1601 color
= ansi_highlight_red();
1602 } else if (badness
== 0) {
1603 checkmark
= special_glyph(SPECIAL_GLYPH_CHECK_MARK
);
1604 description
= a
->description_good
;
1605 color
= ansi_highlight_green();
1607 checkmark
= special_glyph(SPECIAL_GLYPH_CROSS_MARK
);
1609 color
= ansi_highlight_red();
1615 r
= table_add_many(details_table
,
1616 TABLE_STRING
, checkmark
,
1617 TABLE_SET_MINIMUM_WIDTH
, 1,
1618 TABLE_SET_MAXIMUM_WIDTH
, 1,
1619 TABLE_SET_ELLIPSIZE_PERCENT
, 0,
1620 TABLE_SET_COLOR
, color
,
1621 TABLE_STRING
, a
->id
, TABLE_SET_URL
, a
->url
,
1622 TABLE_STRING
, description
,
1623 TABLE_UINT64
, a
->weight
, TABLE_SET_ALIGN_PERCENT
, 100,
1624 TABLE_UINT64
, badness
, TABLE_SET_ALIGN_PERCENT
, 100,
1625 TABLE_UINT64
, a
->range
, TABLE_SET_ALIGN_PERCENT
, 100,
1626 TABLE_EMPTY
, TABLE_SET_ALIGN_PERCENT
, 100);
1628 return table_log_add_error(r
);
1632 assert(weight_sum
> 0);
1634 if (details_table
) {
1637 for (row
= 1; row
< table_get_rows(details_table
); row
++) {
1638 char buf
[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1];
1639 const uint64_t *weight
, *badness
, *range
;
1643 assert_se(weight
= table_get_at(details_table
, row
, 3));
1644 assert_se(badness
= table_get_at(details_table
, row
, 4));
1645 assert_se(range
= table_get_at(details_table
, row
, 5));
1647 if (*badness
== UINT64_MAX
|| *badness
== 0)
1650 assert_se(cell
= table_get_cell(details_table
, row
, 6));
1652 x
= DIV_ROUND_UP(DIV_ROUND_UP(*badness
* *weight
* 100U, *range
), weight_sum
);
1653 xsprintf(buf
, "%" PRIu64
".%" PRIu64
, x
/ 10, x
% 10);
1655 r
= table_update(details_table
, cell
, TABLE_STRING
, buf
);
1657 return log_error_errno(r
, "Failed to update cell in table: %m");
1660 r
= table_print(details_table
, stdout
);
1662 return log_error_errno(r
, "Failed to output table: %m");
1665 exposure
= DIV_ROUND_UP(badness_sum
* 100U, weight_sum
);
1667 for (i
= 0; i
< ELEMENTSOF(badness_table
); i
++)
1668 if (exposure
>= badness_table
[i
].exposure
)
1671 assert(i
< ELEMENTSOF(badness_table
));
1673 if (details_table
) {
1674 _cleanup_free_
char *clickable
= NULL
;
1677 /* If we shall output the details table, also print the brief summary underneath */
1679 if (info
->fragment_path
) {
1680 r
= terminal_urlify_path(info
->fragment_path
, info
->id
, &clickable
);
1688 printf("\n%s %sOverall exposure level for %s%s: %s%" PRIu64
".%" PRIu64
" %s%s %s\n",
1689 special_glyph(SPECIAL_GLYPH_ARROW
),
1693 colors_enabled() ? strempty(badness_table
[i
].color
) : "",
1694 exposure
/ 10, exposure
% 10,
1695 badness_table
[i
].name
,
1697 special_glyph(badness_table
[i
].smiley
));
1702 if (overview_table
) {
1703 char buf
[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1];
1704 _cleanup_free_
char *url
= NULL
;
1706 if (info
->fragment_path
) {
1707 r
= file_url_from_path(info
->fragment_path
, &url
);
1709 return log_error_errno(r
, "Failed to generate URL from path: %m");
1712 xsprintf(buf
, "%" PRIu64
".%" PRIu64
, exposure
/ 10, exposure
% 10);
1714 r
= table_add_many(overview_table
,
1715 TABLE_STRING
, info
->id
,
1718 TABLE_SET_ALIGN_PERCENT
, 100,
1719 TABLE_STRING
, badness_table
[i
].name
,
1720 TABLE_SET_COLOR
, strempty(badness_table
[i
].color
),
1721 TABLE_STRING
, special_glyph(badness_table
[i
].smiley
));
1723 return table_log_add_error(r
);
1726 /* Return error when overall exposure level is over threshold */
1727 if (exposure
> threshold
)
1733 static int property_read_restrict_namespaces(
1737 sd_bus_error
*error
,
1740 SecurityInfo
*info
= userdata
;
1742 uint64_t namespaces
;
1749 r
= sd_bus_message_read(m
, "t", &namespaces
);
1753 info
->restrict_namespaces
= (unsigned long long) namespaces
;
1758 static int property_read_umask(
1762 sd_bus_error
*error
,
1765 SecurityInfo
*info
= userdata
;
1774 r
= sd_bus_message_read(m
, "u", &umask
);
1778 info
->_umask
= (mode_t
) umask
;
1783 static int property_read_restrict_address_families(
1787 sd_bus_error
*error
,
1790 SecurityInfo
*info
= userdata
;
1797 r
= sd_bus_message_enter_container(m
, 'r', "bas");
1801 r
= sd_bus_message_read(m
, "b", &allow_list
);
1805 info
->restrict_address_family_inet
=
1806 info
->restrict_address_family_unix
=
1807 info
->restrict_address_family_netlink
=
1808 info
->restrict_address_family_packet
=
1809 info
->restrict_address_family_other
= allow_list
;
1811 r
= sd_bus_message_enter_container(m
, 'a', "s");
1818 r
= sd_bus_message_read(m
, "s", &name
);
1824 if (STR_IN_SET(name
, "AF_INET", "AF_INET6"))
1825 info
->restrict_address_family_inet
= !allow_list
;
1826 else if (streq(name
, "AF_UNIX"))
1827 info
->restrict_address_family_unix
= !allow_list
;
1828 else if (streq(name
, "AF_NETLINK"))
1829 info
->restrict_address_family_netlink
= !allow_list
;
1830 else if (streq(name
, "AF_PACKET"))
1831 info
->restrict_address_family_packet
= !allow_list
;
1833 info
->restrict_address_family_other
= !allow_list
;
1836 r
= sd_bus_message_exit_container(m
);
1840 return sd_bus_message_exit_container(m
);
1843 static int property_read_syscall_archs(
1847 sd_bus_error
*error
,
1850 SecurityInfo
*info
= userdata
;
1858 r
= sd_bus_message_enter_container(m
, 'a', "s");
1865 r
= sd_bus_message_read(m
, "s", &name
);
1871 r
= set_put_strdup(&info
->system_call_architectures
, name
);
1876 return sd_bus_message_exit_container(m
);
1879 static int property_read_system_call_filter(
1883 sd_bus_error
*error
,
1886 SecurityInfo
*info
= userdata
;
1893 r
= sd_bus_message_enter_container(m
, 'r', "bas");
1897 r
= sd_bus_message_read(m
, "b", &allow_list
);
1901 info
->system_call_filter_allow_list
= allow_list
;
1903 r
= sd_bus_message_enter_container(m
, 'a', "s");
1910 r
= sd_bus_message_read(m
, "s", &name
);
1916 /* The actual ExecContext stores the system call id as the map value, which we don't
1917 * need. So we assign NULL to all values here. */
1918 r
= hashmap_put_strdup(&info
->system_call_filter
, name
, NULL
);
1923 r
= sd_bus_message_exit_container(m
);
1927 return sd_bus_message_exit_container(m
);
1930 static int property_read_ip_address_allow(
1934 sd_bus_error
*error
,
1937 SecurityInfo
*info
= userdata
;
1938 bool deny_ipv4
= false, deny_ipv6
= false;
1945 r
= sd_bus_message_enter_container(m
, 'a', "(iayu)");
1955 r
= sd_bus_message_enter_container(m
, 'r', "iayu");
1961 r
= sd_bus_message_read(m
, "i", &family
);
1965 r
= sd_bus_message_read_array(m
, 'y', &data
, &size
);
1969 r
= sd_bus_message_read(m
, "u", &prefixlen
);
1973 r
= sd_bus_message_exit_container(m
);
1977 if (streq(member
, "IPAddressAllow")) {
1978 union in_addr_union u
;
1980 if (family
== AF_INET
&& size
== 4 && prefixlen
== 8)
1981 memcpy(&u
.in
, data
, size
);
1982 else if (family
== AF_INET6
&& size
== 16 && prefixlen
== 128)
1983 memcpy(&u
.in6
, data
, size
);
1985 info
->ip_address_allow_other
= true;
1989 if (in_addr_is_localhost(family
, &u
))
1990 info
->ip_address_allow_localhost
= true;
1992 info
->ip_address_allow_other
= true;
1994 assert(streq(member
, "IPAddressDeny"));
1996 if (family
== AF_INET
&& size
== 4 && prefixlen
== 0)
1998 else if (family
== AF_INET6
&& size
== 16 && prefixlen
== 0)
2003 info
->ip_address_deny_all
= deny_ipv4
&& deny_ipv6
;
2005 return sd_bus_message_exit_container(m
);
2008 static int property_read_ip_filters(
2012 sd_bus_error
*error
,
2015 SecurityInfo
*info
= userdata
;
2016 _cleanup_(strv_freep
) char **l
= NULL
;
2023 r
= sd_bus_message_read_strv(m
, &l
);
2027 if (streq(member
, "IPIngressFilterPath"))
2028 info
->ip_filters_custom_ingress
= !strv_isempty(l
);
2029 else if (streq(member
, "IPEgressFilterPath"))
2030 info
->ip_filters_custom_egress
= !strv_isempty(l
);
2035 static int property_read_device_allow(
2039 sd_bus_error
*error
,
2042 SecurityInfo
*info
= userdata
;
2050 r
= sd_bus_message_enter_container(m
, 'a', "(ss)");
2055 const char *name
, *policy
;
2057 r
= sd_bus_message_read(m
, "(ss)", &name
, &policy
);
2066 info
->device_allow_non_empty
= n
> 0;
2068 return sd_bus_message_exit_container(m
);
2071 static int acquire_security_info(sd_bus
*bus
, const char *name
, SecurityInfo
*info
, AnalyzeSecurityFlags flags
) {
2073 static const struct bus_properties_map security_map
[] = {
2074 { "AmbientCapabilities", "t", NULL
, offsetof(SecurityInfo
, ambient_capabilities
) },
2075 { "CapabilityBoundingSet", "t", NULL
, offsetof(SecurityInfo
, capability_bounding_set
) },
2076 { "DefaultDependencies", "b", NULL
, offsetof(SecurityInfo
, default_dependencies
) },
2077 { "Delegate", "b", NULL
, offsetof(SecurityInfo
, delegate
) },
2078 { "DeviceAllow", "a(ss)", property_read_device_allow
, 0 },
2079 { "DevicePolicy", "s", NULL
, offsetof(SecurityInfo
, device_policy
) },
2080 { "DynamicUser", "b", NULL
, offsetof(SecurityInfo
, dynamic_user
) },
2081 { "FragmentPath", "s", NULL
, offsetof(SecurityInfo
, fragment_path
) },
2082 { "IPAddressAllow", "a(iayu)", property_read_ip_address_allow
, 0 },
2083 { "IPAddressDeny", "a(iayu)", property_read_ip_address_allow
, 0 },
2084 { "IPIngressFilterPath", "as", property_read_ip_filters
, 0 },
2085 { "IPEgressFilterPath", "as", property_read_ip_filters
, 0 },
2086 { "Id", "s", NULL
, offsetof(SecurityInfo
, id
) },
2087 { "KeyringMode", "s", NULL
, offsetof(SecurityInfo
, keyring_mode
) },
2088 { "ProtectProc", "s", NULL
, offsetof(SecurityInfo
, protect_proc
) },
2089 { "ProcSubset", "s", NULL
, offsetof(SecurityInfo
, proc_subset
) },
2090 { "LoadState", "s", NULL
, offsetof(SecurityInfo
, load_state
) },
2091 { "LockPersonality", "b", NULL
, offsetof(SecurityInfo
, lock_personality
) },
2092 { "MemoryDenyWriteExecute", "b", NULL
, offsetof(SecurityInfo
, memory_deny_write_execute
) },
2093 { "NoNewPrivileges", "b", NULL
, offsetof(SecurityInfo
, no_new_privileges
) },
2094 { "NotifyAccess", "s", NULL
, offsetof(SecurityInfo
, notify_access
) },
2095 { "PrivateDevices", "b", NULL
, offsetof(SecurityInfo
, private_devices
) },
2096 { "PrivateMounts", "b", NULL
, offsetof(SecurityInfo
, private_mounts
) },
2097 { "PrivateNetwork", "b", NULL
, offsetof(SecurityInfo
, private_network
) },
2098 { "PrivateTmp", "b", NULL
, offsetof(SecurityInfo
, private_tmp
) },
2099 { "PrivateUsers", "b", NULL
, offsetof(SecurityInfo
, private_users
) },
2100 { "ProtectControlGroups", "b", NULL
, offsetof(SecurityInfo
, protect_control_groups
) },
2101 { "ProtectHome", "s", NULL
, offsetof(SecurityInfo
, protect_home
) },
2102 { "ProtectHostname", "b", NULL
, offsetof(SecurityInfo
, protect_hostname
) },
2103 { "ProtectKernelModules", "b", NULL
, offsetof(SecurityInfo
, protect_kernel_modules
) },
2104 { "ProtectKernelTunables", "b", NULL
, offsetof(SecurityInfo
, protect_kernel_tunables
) },
2105 { "ProtectKernelLogs", "b", NULL
, offsetof(SecurityInfo
, protect_kernel_logs
) },
2106 { "ProtectClock", "b", NULL
, offsetof(SecurityInfo
, protect_clock
) },
2107 { "ProtectSystem", "s", NULL
, offsetof(SecurityInfo
, protect_system
) },
2108 { "RemoveIPC", "b", NULL
, offsetof(SecurityInfo
, remove_ipc
) },
2109 { "RestrictAddressFamilies", "(bas)", property_read_restrict_address_families
, 0 },
2110 { "RestrictNamespaces", "t", property_read_restrict_namespaces
, 0 },
2111 { "RestrictRealtime", "b", NULL
, offsetof(SecurityInfo
, restrict_realtime
) },
2112 { "RestrictSUIDSGID", "b", NULL
, offsetof(SecurityInfo
, restrict_suid_sgid
) },
2113 { "RootDirectory", "s", NULL
, offsetof(SecurityInfo
, root_directory
) },
2114 { "RootImage", "s", NULL
, offsetof(SecurityInfo
, root_image
) },
2115 { "SupplementaryGroups", "as", NULL
, offsetof(SecurityInfo
, supplementary_groups
) },
2116 { "SystemCallArchitectures", "as", property_read_syscall_archs
, 0 },
2117 { "SystemCallFilter", "(as)", property_read_system_call_filter
, 0 },
2118 { "Type", "s", NULL
, offsetof(SecurityInfo
, type
) },
2119 { "UMask", "u", property_read_umask
, 0 },
2120 { "User", "s", NULL
, offsetof(SecurityInfo
, user
) },
2124 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2125 _cleanup_free_
char *path
= NULL
;
2128 /* Note: this mangles *info on failure! */
2134 path
= unit_dbus_path_from_name(name
);
2138 r
= bus_map_all_properties(
2140 "org.freedesktop.systemd1",
2143 BUS_MAP_STRDUP
| BUS_MAP_BOOLEAN_AS_BOOL
,
2148 return log_error_errno(r
, "Failed to get unit properties: %s", bus_error_message(&error
, r
));
2150 if (!streq_ptr(info
->load_state
, "loaded")) {
2152 if (FLAGS_SET(flags
, ANALYZE_SECURITY_ONLY_LOADED
))
2153 return -EMEDIUMTYPE
;
2155 if (streq_ptr(info
->load_state
, "not-found"))
2156 log_error("Unit %s not found, cannot analyze.", name
);
2157 else if (streq_ptr(info
->load_state
, "masked"))
2158 log_error("Unit %s is masked, cannot analyze.", name
);
2160 log_error("Unit %s not loaded properly, cannot analyze.", name
);
2165 if (FLAGS_SET(flags
, ANALYZE_SECURITY_ONLY_LONG_RUNNING
) && streq_ptr(info
->type
, "oneshot"))
2166 return -EMEDIUMTYPE
;
2168 if (info
->private_devices
||
2169 info
->private_tmp
||
2170 info
->protect_control_groups
||
2171 info
->protect_kernel_tunables
||
2172 info
->protect_kernel_modules
||
2173 !streq_ptr(info
->protect_home
, "no") ||
2174 !streq_ptr(info
->protect_system
, "no") ||
2176 info
->private_mounts
= true;
2178 if (info
->protect_kernel_modules
)
2179 info
->capability_bounding_set
&= ~(UINT64_C(1) << CAP_SYS_MODULE
);
2181 if (info
->protect_kernel_logs
)
2182 info
->capability_bounding_set
&= ~(UINT64_C(1) << CAP_SYSLOG
);
2184 if (info
->protect_clock
)
2185 info
->capability_bounding_set
&= ~((UINT64_C(1) << CAP_SYS_TIME
) |
2186 (UINT64_C(1) << CAP_WAKE_ALARM
));
2188 if (info
->private_devices
)
2189 info
->capability_bounding_set
&= ~((UINT64_C(1) << CAP_MKNOD
) |
2190 (UINT64_C(1) << CAP_SYS_RAWIO
));
2195 static int analyze_security_one(sd_bus
*bus
, const char *name
, Table
*overview_table
,
2196 AnalyzeSecurityFlags flags
, unsigned threshold
) {
2198 _cleanup_(security_info_freep
) SecurityInfo
*info
= security_info_new();
2207 r
= acquire_security_info(bus
, name
, info
, flags
);
2208 if (r
== -EMEDIUMTYPE
) /* Ignore this one because not loaded or Type is oneshot */
2213 r
= assess(info
, overview_table
, flags
, threshold
);
2220 /* Refactoring SecurityInfo so that it can make use of existing struct variables instead of reading from dbus */
2221 static int get_security_info(Unit
*u
, ExecContext
*c
, CGroupContext
*g
, SecurityInfo
**ret_info
) {
2224 _cleanup_(security_info_freep
) SecurityInfo
*info
= security_info_new();
2230 info
->id
= strdup(u
->id
);
2234 if (unit_type_to_string(u
->type
)) {
2235 info
->type
= strdup(unit_type_to_string(u
->type
));
2239 if (unit_load_state_to_string(u
->load_state
)) {
2240 info
->load_state
= strdup(unit_load_state_to_string(u
->load_state
));
2241 if (!info
->load_state
)
2244 if (u
->fragment_path
) {
2245 info
->fragment_path
= strdup(u
->fragment_path
);
2246 if (!info
->fragment_path
)
2249 info
->default_dependencies
= u
->default_dependencies
;
2250 if (u
->type
== UNIT_SERVICE
&& notify_access_to_string(SERVICE(u
)->notify_access
)) {
2251 info
->notify_access
= strdup(notify_access_to_string(SERVICE(u
)->notify_access
));
2252 if (!info
->notify_access
)
2258 info
->ambient_capabilities
= c
->capability_ambient_set
;
2259 info
->capability_bounding_set
= c
->capability_bounding_set
;
2261 info
->user
= strdup(c
->user
);
2265 if (c
->supplementary_groups
) {
2266 info
->supplementary_groups
= strv_copy(c
->supplementary_groups
);
2267 if (!info
->supplementary_groups
)
2270 info
->dynamic_user
= c
->dynamic_user
;
2271 if (exec_keyring_mode_to_string(c
->keyring_mode
)) {
2272 info
->keyring_mode
= strdup(exec_keyring_mode_to_string(c
->keyring_mode
));
2273 if (!info
->keyring_mode
)
2276 if (protect_proc_to_string(c
->protect_proc
)) {
2277 info
->protect_proc
= strdup(protect_proc_to_string(c
->protect_proc
));
2278 if (!info
->protect_proc
)
2281 if (proc_subset_to_string(c
->proc_subset
)) {
2282 info
->proc_subset
= strdup(proc_subset_to_string(c
->proc_subset
));
2283 if (!info
->proc_subset
)
2286 info
->lock_personality
= c
->lock_personality
;
2287 info
->memory_deny_write_execute
= c
->memory_deny_write_execute
;
2288 info
->no_new_privileges
= c
->no_new_privileges
;
2289 info
->protect_hostname
= c
->protect_hostname
;
2290 info
->private_devices
= c
->private_devices
;
2291 info
->private_mounts
= c
->private_mounts
;
2292 info
->private_network
= c
->private_network
;
2293 info
->private_tmp
= c
->private_tmp
;
2294 info
->private_users
= c
->private_users
;
2295 info
->protect_control_groups
= c
->protect_control_groups
;
2296 info
->protect_kernel_modules
= c
->protect_kernel_modules
;
2297 info
->protect_kernel_tunables
= c
->protect_kernel_tunables
;
2298 info
->protect_kernel_logs
= c
->protect_kernel_logs
;
2299 info
->protect_clock
= c
->protect_clock
;
2300 if (protect_home_to_string(c
->protect_home
)) {
2301 info
->protect_home
= strdup(protect_home_to_string(c
->protect_home
));
2302 if (!info
->protect_home
)
2305 if (protect_system_to_string(c
->protect_system
)) {
2306 info
->protect_system
= strdup(protect_system_to_string(c
->protect_system
));
2307 if (!info
->protect_system
)
2310 info
->remove_ipc
= c
->remove_ipc
;
2311 info
->restrict_address_family_inet
=
2312 info
->restrict_address_family_unix
=
2313 info
->restrict_address_family_netlink
=
2314 info
->restrict_address_family_packet
=
2315 info
->restrict_address_family_other
=
2316 c
->address_families_allow_list
;
2319 SET_FOREACH(key
, c
->address_families
) {
2320 int family
= PTR_TO_INT(key
);
2323 if (IN_SET(family
, AF_INET
, AF_INET6
))
2324 info
->restrict_address_family_inet
= !c
->address_families_allow_list
;
2325 else if (family
== AF_UNIX
)
2326 info
->restrict_address_family_unix
= !c
->address_families_allow_list
;
2327 else if (family
== AF_NETLINK
)
2328 info
->restrict_address_family_netlink
= !c
->address_families_allow_list
;
2329 else if (family
== AF_PACKET
)
2330 info
->restrict_address_family_packet
= !c
->address_families_allow_list
;
2332 info
->restrict_address_family_other
= !c
->address_families_allow_list
;
2335 info
->restrict_namespaces
= c
->restrict_namespaces
;
2336 info
->restrict_realtime
= c
->restrict_realtime
;
2337 info
->restrict_suid_sgid
= c
->restrict_suid_sgid
;
2338 if (c
->root_directory
) {
2339 info
->root_directory
= strdup(c
->root_directory
);
2340 if (!info
->root_directory
)
2343 if (c
->root_image
) {
2344 info
->root_image
= strdup(c
->root_image
);
2345 if (!info
->root_image
)
2348 info
->_umask
= c
->umask
;
2349 if (c
->syscall_archs
) {
2350 info
->system_call_architectures
= set_copy(c
->syscall_archs
);
2351 if (!info
->system_call_architectures
)
2354 info
->system_call_filter_allow_list
= c
->syscall_allow_list
;
2355 if (c
->syscall_filter
) {
2356 info
->system_call_filter
= hashmap_copy(c
->syscall_filter
);
2357 if (!info
->system_call_filter
)
2363 info
->delegate
= g
->delegate
;
2364 if (cgroup_device_policy_to_string(g
->device_policy
)) {
2365 info
->device_policy
= strdup(cgroup_device_policy_to_string(g
->device_policy
));
2366 if (!info
->device_policy
)
2370 IPAddressAccessItem
*i
;
2371 bool deny_ipv4
= false, deny_ipv6
= false;
2373 LIST_FOREACH(items
, i
, g
->ip_address_deny
) {
2374 if (i
->family
== AF_INET
&& i
->prefixlen
== 0)
2376 else if (i
->family
== AF_INET6
&& i
->prefixlen
== 0)
2379 info
->ip_address_deny_all
= deny_ipv4
&& deny_ipv6
;
2381 info
->ip_address_allow_localhost
= info
->ip_address_allow_other
= false;
2382 LIST_FOREACH(items
, i
, g
->ip_address_allow
) {
2383 if (in_addr_is_localhost(i
->family
, &i
->address
))
2384 info
->ip_address_allow_localhost
= true;
2386 info
->ip_address_allow_other
= true;
2389 info
->ip_filters_custom_ingress
= !strv_isempty(g
->ip_filters_ingress
);
2390 info
->ip_filters_custom_egress
= !strv_isempty(g
->ip_filters_egress
);
2391 info
->device_allow_non_empty
= !LIST_IS_EMPTY(g
->device_allow
);
2394 *ret_info
= TAKE_PTR(info
);
2399 static int offline_security_check(Unit
*u
, unsigned threshold
) {
2400 _cleanup_(table_unrefp
) Table
*overview_table
= NULL
;
2401 AnalyzeSecurityFlags flags
= 0;
2402 _cleanup_(security_info_freep
) SecurityInfo
*info
= NULL
;
2408 unit_dump(u
, stdout
, "\t");
2410 r
= get_security_info(u
, unit_get_exec_context(u
), unit_get_cgroup_context(u
), &info
);
2414 return assess(info
, overview_table
, flags
, threshold
);
2417 static int offline_security_checks(char **filenames
, UnitFileScope scope
, bool check_man
, bool run_generators
, unsigned threshold
, const char *root
) {
2418 const ManagerTestRunFlags flags
=
2419 MANAGER_TEST_RUN_MINIMAL
|
2420 MANAGER_TEST_RUN_ENV_GENERATORS
|
2421 run_generators
* MANAGER_TEST_RUN_GENERATORS
;
2423 _cleanup_(manager_freep
) Manager
*m
= NULL
;
2424 Unit
*units
[strv_length(filenames
)];
2425 _cleanup_free_
char *var
= NULL
;
2430 if (strv_isempty(filenames
))
2434 r
= verify_generate_path(&var
, filenames
);
2436 return log_error_errno(r
, "Failed to generate unit load path: %m");
2438 assert_se(set_unit_path(var
) >= 0);
2440 r
= manager_new(scope
, flags
, &m
);
2442 return log_error_errno(r
, "Failed to initialize manager: %m");
2444 log_debug("Starting manager...");
2446 r
= manager_startup(m
, /* serialization= */ NULL
, /* fds= */ NULL
, root
);
2450 log_debug("Loading remaining units from the command line...");
2452 STRV_FOREACH(filename
, filenames
) {
2453 _cleanup_free_
char *prepared
= NULL
;
2455 log_debug("Handling %s...", *filename
);
2457 k
= verify_prepare_filename(*filename
, &prepared
);
2459 log_warning_errno(k
, "Failed to prepare filename %s: %m", *filename
);
2465 k
= manager_load_startable_unit_or_warn(m
, NULL
, prepared
, &units
[count
]);
2475 for (size_t i
= 0; i
< count
; i
++) {
2476 k
= offline_security_check(units
[i
], threshold
);
2477 if (k
< 0 && r
== 0)
2484 int analyze_security(sd_bus
*bus
, char **units
, UnitFileScope scope
, bool check_man
, bool run_generators
,
2485 bool offline
, unsigned threshold
, const char *root
, AnalyzeSecurityFlags flags
) {
2487 _cleanup_(table_unrefp
) Table
*overview_table
= NULL
;
2493 return offline_security_checks(units
, scope
, check_man
, run_generators
, threshold
, root
);
2495 if (strv_length(units
) != 1) {
2496 overview_table
= table_new("unit", "exposure", "predicate", "happy");
2497 if (!overview_table
)
2501 if (strv_isempty(units
)) {
2502 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2503 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2504 _cleanup_strv_free_
char **list
= NULL
;
2508 r
= sd_bus_call_method(
2510 "org.freedesktop.systemd1",
2511 "/org/freedesktop/systemd1",
2512 "org.freedesktop.systemd1.Manager",
2518 return log_error_errno(r
, "Failed to list units: %s", bus_error_message(&error
, r
));
2520 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
2522 return bus_log_parse_error(r
);
2528 r
= bus_parse_unit_info(reply
, &info
);
2530 return bus_log_parse_error(r
);
2534 if (!endswith(info
.id
, ".service"))
2537 if (!GREEDY_REALLOC(list
, n
+ 2))
2540 copy
= strdup(info
.id
);
2550 flags
|= ANALYZE_SECURITY_SHORT
|ANALYZE_SECURITY_ONLY_LOADED
|ANALYZE_SECURITY_ONLY_LONG_RUNNING
;
2552 STRV_FOREACH(i
, list
) {
2553 r
= analyze_security_one(bus
, *i
, overview_table
, flags
, threshold
);
2554 if (r
< 0 && ret
>= 0)
2561 STRV_FOREACH(i
, units
) {
2562 _cleanup_free_
char *mangled
= NULL
, *instance
= NULL
;
2565 if (!FLAGS_SET(flags
, ANALYZE_SECURITY_SHORT
) && i
!= units
) {
2570 r
= unit_name_mangle(*i
, 0, &mangled
);
2572 return log_error_errno(r
, "Failed to mangle unit name '%s': %m", *i
);
2574 if (!endswith(mangled
, ".service"))
2575 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
2576 "Unit %s is not a service unit, refusing.",
2579 if (unit_name_is_valid(mangled
, UNIT_NAME_TEMPLATE
)) {
2580 r
= unit_name_replace_instance(mangled
, "test-instance", &instance
);
2588 r
= analyze_security_one(bus
, name
, overview_table
, flags
, threshold
);
2589 if (r
< 0 && ret
>= 0)
2594 if (overview_table
) {
2595 if (!FLAGS_SET(flags
, ANALYZE_SECURITY_SHORT
)) {
2600 r
= table_print(overview_table
, stdout
);
2602 return log_error_errno(r
, "Failed to output table: %m");