1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include <sys/utsname.h>
5 #include "analyze-security.h"
7 #include "bus-unit-util.h"
10 #include "format-table.h"
11 #include "in-addr-util.h"
12 #include "locale-util.h"
14 #include "missing_capability.h"
15 #include "missing_sched.h"
16 #include "nulstr-util.h"
17 #include "parse-util.h"
18 #include "path-util.h"
19 #include "pretty-print.h"
21 # include "seccomp-util.h"
24 #include "stdio-util.h"
26 #include "terminal-util.h"
28 #include "unit-name.h"
30 struct security_info
{
35 bool default_dependencies
;
37 uint64_t ambient_capabilities
;
38 uint64_t capability_bounding_set
;
41 char **supplementary_groups
;
44 bool ip_address_deny_all
;
45 bool ip_address_allow_localhost
;
46 bool ip_address_allow_other
;
48 bool ip_filters_custom_ingress
;
49 bool ip_filters_custom_egress
;
52 bool lock_personality
;
53 bool memory_deny_write_execute
;
54 bool no_new_privileges
;
56 bool protect_hostname
;
64 bool protect_control_groups
;
65 bool protect_kernel_modules
;
66 bool protect_kernel_tunables
;
67 bool protect_kernel_logs
;
74 bool restrict_address_family_inet
;
75 bool restrict_address_family_unix
;
76 bool restrict_address_family_netlink
;
77 bool restrict_address_family_packet
;
78 bool restrict_address_family_other
;
80 uint64_t restrict_namespaces
;
81 bool restrict_realtime
;
82 bool restrict_suid_sgid
;
89 bool device_allow_non_empty
;
91 char **system_call_architectures
;
93 bool system_call_filter_whitelist
;
94 Set
*system_call_filter
;
99 struct security_assessor
{
101 const char *description_good
;
102 const char *description_bad
;
103 const char *description_na
;
108 const struct security_assessor
*a
,
109 const struct security_info
*info
,
111 uint64_t *ret_badness
,
112 char **ret_description
);
115 bool default_dependencies_only
;
118 static void security_info_free(struct security_info
*i
) {
125 free(i
->fragment_path
);
129 free(i
->protect_home
);
130 free(i
->protect_system
);
132 free(i
->root_directory
);
135 free(i
->keyring_mode
);
136 free(i
->notify_access
);
138 free(i
->device_policy
);
140 strv_free(i
->supplementary_groups
);
141 strv_free(i
->system_call_architectures
);
143 set_free_free(i
->system_call_filter
);
146 static bool security_info_runs_privileged(const struct security_info
*i
) {
149 if (STRPTR_IN_SET(i
->user
, "0", "root"))
155 return isempty(i
->user
);
158 static int assess_bool(
159 const struct security_assessor
*a
,
160 const struct security_info
*info
,
162 uint64_t *ret_badness
,
163 char **ret_description
) {
165 const bool *b
= data
;
169 assert(ret_description
);
171 *ret_badness
= a
->parameter
? *b
: !*b
;
172 *ret_description
= NULL
;
177 static int assess_user(
178 const struct security_assessor
*a
,
179 const struct security_info
*info
,
181 uint64_t *ret_badness
,
182 char **ret_description
) {
184 _cleanup_free_
char *d
= NULL
;
188 assert(ret_description
);
190 if (streq_ptr(info
->user
, NOBODY_USER_NAME
)) {
191 d
= strdup("Service runs under as '" NOBODY_USER_NAME
"' user, which should not be used for services");
193 } else if (info
->dynamic_user
&& !STR_IN_SET(info
->user
, "0", "root")) {
194 d
= strdup("Service runs under a transient non-root user identity");
196 } else if (info
->user
&& !STR_IN_SET(info
->user
, "0", "root", "")) {
197 d
= strdup("Service runs under a static non-root user identity");
201 *ret_description
= NULL
;
209 *ret_description
= TAKE_PTR(d
);
214 static int assess_protect_home(
215 const struct security_assessor
*a
,
216 const struct security_info
*info
,
218 uint64_t *ret_badness
,
219 char **ret_description
) {
221 const char *description
;
227 assert(ret_description
);
230 description
= "Service has full access to home directories";
232 r
= parse_boolean(info
->protect_home
);
234 if (streq_ptr(info
->protect_home
, "read-only")) {
236 description
= "Service has read-only access to home directories";
237 } else if (streq_ptr(info
->protect_home
, "tmpfs")) {
239 description
= "Service has access to fake empty home directories";
243 description
= "Service has no access to home directories";
246 copy
= strdup(description
);
250 *ret_badness
= badness
;
251 *ret_description
= copy
;
256 static int assess_protect_system(
257 const struct security_assessor
*a
,
258 const struct security_info
*info
,
260 uint64_t *ret_badness
,
261 char **ret_description
) {
263 const char *description
;
269 assert(ret_description
);
272 description
= "Service has full access to the OS file hierarchy";
274 r
= parse_boolean(info
->protect_system
);
276 if (streq_ptr(info
->protect_system
, "full")) {
278 description
= "Service has very limited write access to the OS file hierarchy";
279 } else if (streq_ptr(info
->protect_system
, "strict")) {
281 description
= "Service has strict read-only access to the OS file hierarchy";
285 description
= "Service has limited write access to the OS file hierarchy";
288 copy
= strdup(description
);
292 *ret_badness
= badness
;
293 *ret_description
= copy
;
298 static int assess_root_directory(
299 const struct security_assessor
*a
,
300 const struct security_info
*info
,
302 uint64_t *ret_badness
,
303 char **ret_description
) {
306 assert(ret_description
);
309 empty_or_root(info
->root_directory
) ||
310 empty_or_root(info
->root_image
);
311 *ret_description
= NULL
;
316 static int assess_capability_bounding_set(
317 const struct security_assessor
*a
,
318 const struct security_info
*info
,
320 uint64_t *ret_badness
,
321 char **ret_description
) {
324 assert(ret_description
);
326 *ret_badness
= !!(info
->capability_bounding_set
& a
->parameter
);
327 *ret_description
= NULL
;
332 static int assess_umask(
333 const struct security_assessor
*a
,
334 const struct security_info
*info
,
336 uint64_t *ret_badness
,
337 char **ret_description
) {
344 assert(ret_description
);
346 if (!FLAGS_SET(info
->_umask
, 0002)) {
347 d
= "Files created by service are world-writable by default";
349 } else if (!FLAGS_SET(info
->_umask
, 0004)) {
350 d
= "Files created by service are world-readable by default";
352 } else if (!FLAGS_SET(info
->_umask
, 0020)) {
353 d
= "Files created by service are group-writable by default";
355 } else if (!FLAGS_SET(info
->_umask
, 0040)) {
356 d
= "Files created by service are group-readable by default";
359 d
= "Files created by service are accessible only by service's own user by default";
368 *ret_description
= copy
;
373 static int assess_keyring_mode(
374 const struct security_assessor
*a
,
375 const struct security_info
*info
,
377 uint64_t *ret_badness
,
378 char **ret_description
) {
381 assert(ret_description
);
383 *ret_badness
= !streq_ptr(info
->keyring_mode
, "private");
384 *ret_description
= NULL
;
389 static int assess_notify_access(
390 const struct security_assessor
*a
,
391 const struct security_info
*info
,
393 uint64_t *ret_badness
,
394 char **ret_description
) {
397 assert(ret_description
);
399 *ret_badness
= streq_ptr(info
->notify_access
, "all");
400 *ret_description
= NULL
;
405 static int assess_remove_ipc(
406 const struct security_assessor
*a
,
407 const struct security_info
*info
,
409 uint64_t *ret_badness
,
410 char **ret_description
) {
413 assert(ret_description
);
415 if (security_info_runs_privileged(info
))
416 *ret_badness
= UINT64_MAX
;
418 *ret_badness
= !info
->remove_ipc
;
420 *ret_description
= NULL
;
424 static int assess_supplementary_groups(
425 const struct security_assessor
*a
,
426 const struct security_info
*info
,
428 uint64_t *ret_badness
,
429 char **ret_description
) {
432 assert(ret_description
);
434 if (security_info_runs_privileged(info
))
435 *ret_badness
= UINT64_MAX
;
437 *ret_badness
= !strv_isempty(info
->supplementary_groups
);
439 *ret_description
= NULL
;
443 static int assess_restrict_namespaces(
444 const struct security_assessor
*a
,
445 const struct security_info
*info
,
447 uint64_t *ret_badness
,
448 char **ret_description
) {
451 assert(ret_description
);
453 *ret_badness
= !!(info
->restrict_namespaces
& a
->parameter
);
454 *ret_description
= NULL
;
459 static int assess_system_call_architectures(
460 const struct security_assessor
*a
,
461 const struct security_info
*info
,
463 uint64_t *ret_badness
,
464 char **ret_description
) {
470 assert(ret_description
);
472 if (strv_isempty(info
->system_call_architectures
)) {
474 d
= strdup("Service may execute system calls with all ABIs");
475 } else if (strv_equal(info
->system_call_architectures
, STRV_MAKE("native"))) {
477 d
= strdup("Service may execute system calls only with native ABI");
480 d
= strdup("Service may execute system calls with multiple ABIs");
487 *ret_description
= d
;
494 static bool syscall_names_in_filter(Set
*s
, bool whitelist
, const SyscallFilterSet
*f
) {
497 NULSTR_FOREACH(syscall
, f
->value
) {
500 if (syscall
[0] == '@') {
501 const SyscallFilterSet
*g
;
503 assert_se(g
= syscall_filter_set_find(syscall
));
504 if (syscall_names_in_filter(s
, whitelist
, g
))
505 return true; /* bad! */
510 /* Let's see if the system call actually exists on this platform, before complaining */
511 id
= seccomp_syscall_resolve_name(syscall
);
515 if (set_contains(s
, syscall
) == whitelist
) {
516 log_debug("Offending syscall filter item: %s", syscall
);
517 return true; /* bad! */
524 static int assess_system_call_filter(
525 const struct security_assessor
*a
,
526 const struct security_info
*info
,
528 uint64_t *ret_badness
,
529 char **ret_description
) {
531 const SyscallFilterSet
*f
;
538 assert(ret_description
);
540 assert(a
->parameter
< _SYSCALL_FILTER_SET_MAX
);
541 f
= syscall_filter_sets
+ a
->parameter
;
543 if (!info
->system_call_filter_whitelist
&& set_isempty(info
->system_call_filter
)) {
544 d
= strdup("Service does not filter system calls");
549 log_debug("Analyzing system call filter, checking against: %s", f
->name
);
550 bad
= syscall_names_in_filter(info
->system_call_filter
, info
->system_call_filter_whitelist
, f
);
551 log_debug("Result: %s", bad
? "bad" : "good");
553 if (info
->system_call_filter_whitelist
) {
555 (void) asprintf(&d
, "System call whitelist defined for service, and %s is included", f
->name
);
558 (void) asprintf(&d
, "System call whitelist defined for service, and %s is not included", f
->name
);
563 (void) asprintf(&d
, "System call blacklist defined for service, and %s is not included", f
->name
);
566 (void) asprintf(&d
, "System call blacklist defined for service, and %s is included", f
->name
);
576 *ret_description
= d
;
583 static int assess_ip_address_allow(
584 const struct security_assessor
*a
,
585 const struct security_info
*info
,
587 uint64_t *ret_badness
,
588 char **ret_description
) {
595 assert(ret_description
);
597 if (info
->ip_filters_custom_ingress
|| info
->ip_filters_custom_egress
) {
598 d
= strdup("Service defines custom ingress/egress IP filters with BPF programs");
600 } else if (!info
->ip_address_deny_all
) {
601 d
= strdup("Service does not define an IP address whitelist");
603 } else if (info
->ip_address_allow_other
) {
604 d
= strdup("Service defines IP address whitelist with non-localhost entries");
606 } else if (info
->ip_address_allow_localhost
) {
607 d
= strdup("Service defines IP address whitelist with only localhost entries");
610 d
= strdup("Service blocks all IP address ranges");
618 *ret_description
= d
;
623 static int assess_device_allow(
624 const struct security_assessor
*a
,
625 const struct security_info
*info
,
627 uint64_t *ret_badness
,
628 char **ret_description
) {
635 assert(ret_description
);
637 if (STRPTR_IN_SET(info
->device_policy
, "strict", "closed")) {
639 if (info
->device_allow_non_empty
) {
640 d
= strdup("Service has a device ACL with some special devices");
643 d
= strdup("Service has a minimal device ACL");
647 d
= strdup("Service has no device ACL");
655 *ret_description
= d
;
660 static int assess_ambient_capabilities(
661 const struct security_assessor
*a
,
662 const struct security_info
*info
,
664 uint64_t *ret_badness
,
665 char **ret_description
) {
668 assert(ret_description
);
670 *ret_badness
= info
->ambient_capabilities
!= 0;
671 *ret_description
= NULL
;
676 static const struct security_assessor security_assessor_table
[] = {
678 .id
= "User=/DynamicUser=",
679 .description_bad
= "Service runs as root user",
680 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#User=",
683 .assess
= assess_user
,
686 .id
= "SupplementaryGroups=",
687 .description_good
= "Service has no supplementary groups",
688 .description_bad
= "Service runs with supplementary groups",
689 .description_na
= "Service runs as root, option does not matter",
690 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SupplementaryGroups=",
693 .assess
= assess_supplementary_groups
,
696 .id
= "PrivateDevices=",
697 .description_good
= "Service has no access to hardware devices",
698 .description_bad
= "Service potentially has access to hardware devices",
699 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateDevices=",
702 .assess
= assess_bool
,
703 .offset
= offsetof(struct security_info
, private_devices
),
706 .id
= "PrivateMounts=",
707 .description_good
= "Service cannot install system mounts",
708 .description_bad
= "Service may install system mounts",
709 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateMounts=",
712 .assess
= assess_bool
,
713 .offset
= offsetof(struct security_info
, private_mounts
),
716 .id
= "PrivateNetwork=",
717 .description_good
= "Service has no access to the host's network",
718 .description_bad
= "Service has access to the host's network",
719 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateNetwork=",
722 .assess
= assess_bool
,
723 .offset
= offsetof(struct security_info
, private_network
),
727 .description_good
= "Service has no access to other software's temporary files",
728 .description_bad
= "Service has access to other software's temporary files",
729 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateTmp=",
732 .assess
= assess_bool
,
733 .offset
= offsetof(struct security_info
, private_tmp
),
734 .default_dependencies_only
= true,
737 .id
= "PrivateUsers=",
738 .description_good
= "Service does not have access to other users",
739 .description_bad
= "Service has access to other users",
740 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateUsers=",
743 .assess
= assess_bool
,
744 .offset
= offsetof(struct security_info
, private_users
),
747 .id
= "ProtectControlGroups=",
748 .description_good
= "Service cannot modify the control group file system",
749 .description_bad
= "Service may modify to the control group file system",
750 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectControlGroups=",
753 .assess
= assess_bool
,
754 .offset
= offsetof(struct security_info
, protect_control_groups
),
757 .id
= "ProtectKernelModules=",
758 .description_good
= "Service cannot load or read kernel modules",
759 .description_bad
= "Service may load or read kernel modules",
760 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelModules=",
763 .assess
= assess_bool
,
764 .offset
= offsetof(struct security_info
, protect_kernel_modules
),
767 .id
= "ProtectKernelTunables=",
768 .description_good
= "Service cannot alter kernel tunables (/proc/sys, …)",
769 .description_bad
= "Service may alter kernel tunables",
770 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelTunables=",
773 .assess
= assess_bool
,
774 .offset
= offsetof(struct security_info
, protect_kernel_tunables
),
777 .id
= "ProtectKernelLogs=",
778 .description_good
= "Service cannot read from or write to the kernel log ring buffer",
779 .description_bad
= "Service may read from or write to the kernel log ring buffer",
780 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelLogs=",
783 .assess
= assess_bool
,
784 .offset
= offsetof(struct security_info
, protect_kernel_logs
),
787 .id
= "ProtectHome=",
788 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHome=",
791 .assess
= assess_protect_home
,
792 .default_dependencies_only
= true,
795 .id
= "ProtectHostname=",
796 .description_good
= "Service cannot change system host/domainname",
797 .description_bad
= "Service may change system host/domainname",
798 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHostname=",
801 .assess
= assess_bool
,
802 .offset
= offsetof(struct security_info
, protect_hostname
),
805 .id
= "ProtectSystem=",
806 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectSystem=",
809 .assess
= assess_protect_system
,
810 .default_dependencies_only
= true,
813 .id
= "RootDirectory=/RootImage=",
814 .description_good
= "Service has its own root directory/image",
815 .description_bad
= "Service runs within the host's root directory",
816 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RootDirectory=",
819 .assess
= assess_root_directory
,
820 .default_dependencies_only
= true,
823 .id
= "LockPersonality=",
824 .description_good
= "Service cannot change ABI personality",
825 .description_bad
= "Service may change ABI personality",
826 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#LockPersonality=",
829 .assess
= assess_bool
,
830 .offset
= offsetof(struct security_info
, lock_personality
),
833 .id
= "MemoryDenyWriteExecute=",
834 .description_good
= "Service cannot create writable executable memory mappings",
835 .description_bad
= "Service may create writable executable memory mappings",
836 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#MemoryDenyWriteExecute=",
839 .assess
= assess_bool
,
840 .offset
= offsetof(struct security_info
, memory_deny_write_execute
),
843 .id
= "NoNewPrivileges=",
844 .description_good
= "Service processes cannot acquire new privileges",
845 .description_bad
= "Service processes may acquire new privileges",
846 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NoNewPrivileges=",
849 .assess
= assess_bool
,
850 .offset
= offsetof(struct security_info
, no_new_privileges
),
853 .id
= "CapabilityBoundingSet=~CAP_SYS_ADMIN",
854 .description_good
= "Service has no administrator privileges",
855 .description_bad
= "Service has administrator privileges",
856 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
859 .assess
= assess_capability_bounding_set
,
860 .parameter
= UINT64_C(1) << CAP_SYS_ADMIN
,
863 .id
= "CapabilityBoundingSet=~CAP_SET(UID|GID|PCAP)",
864 .description_good
= "Service cannot change UID/GID identities/capabilities",
865 .description_bad
= "Service may change UID/GID identities/capabilities",
866 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
869 .assess
= assess_capability_bounding_set
,
870 .parameter
= (UINT64_C(1) << CAP_SETUID
)|
871 (UINT64_C(1) << CAP_SETGID
)|
872 (UINT64_C(1) << CAP_SETPCAP
),
875 .id
= "CapabilityBoundingSet=~CAP_SYS_PTRACE",
876 .description_good
= "Service has no ptrace() debugging abilities",
877 .description_bad
= "Service has ptrace() debugging abilities",
878 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
881 .assess
= assess_capability_bounding_set
,
882 .parameter
= (UINT64_C(1) << CAP_SYS_PTRACE
),
885 .id
= "CapabilityBoundingSet=~CAP_SYS_TIME",
886 .description_good
= "Service processes cannot change the system clock",
887 .description_bad
= "Service processes may change the system clock",
888 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
891 .assess
= assess_capability_bounding_set
,
892 .parameter
= UINT64_C(1) << CAP_SYS_TIME
,
895 .id
= "CapabilityBoundingSet=~CAP_NET_ADMIN",
896 .description_good
= "Service has no network configuration privileges",
897 .description_bad
= "Service has network configuration privileges",
898 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
901 .assess
= assess_capability_bounding_set
,
902 .parameter
= (UINT64_C(1) << CAP_NET_ADMIN
),
905 .id
= "CapabilityBoundingSet=~CAP_RAWIO",
906 .description_good
= "Service has no raw I/O access",
907 .description_bad
= "Service has raw I/O access",
908 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
911 .assess
= assess_capability_bounding_set
,
912 .parameter
= (UINT64_C(1) << CAP_SYS_RAWIO
),
915 .id
= "CapabilityBoundingSet=~CAP_SYS_MODULE",
916 .description_good
= "Service cannot load kernel modules",
917 .description_bad
= "Service may load kernel modules",
918 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
921 .assess
= assess_capability_bounding_set
,
922 .parameter
= (UINT64_C(1) << CAP_SYS_MODULE
),
925 .id
= "CapabilityBoundingSet=~CAP_AUDIT_*",
926 .description_good
= "Service has no audit subsystem access",
927 .description_bad
= "Service has audit subsystem access",
928 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
931 .assess
= assess_capability_bounding_set
,
932 .parameter
= (UINT64_C(1) << CAP_AUDIT_CONTROL
) |
933 (UINT64_C(1) << CAP_AUDIT_READ
) |
934 (UINT64_C(1) << CAP_AUDIT_WRITE
),
937 .id
= "CapabilityBoundingSet=~CAP_SYSLOG",
938 .description_good
= "Service has no access to kernel logging",
939 .description_bad
= "Service has access to kernel logging",
940 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
943 .assess
= assess_capability_bounding_set
,
944 .parameter
= (UINT64_C(1) << CAP_SYSLOG
),
947 .id
= "CapabilityBoundingSet=~CAP_SYS_(NICE|RESOURCE)",
948 .description_good
= "Service has no privileges to change resource use parameters",
949 .description_bad
= "Service has privileges to change resource use parameters",
950 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
953 .assess
= assess_capability_bounding_set
,
954 .parameter
= (UINT64_C(1) << CAP_SYS_NICE
) |
955 (UINT64_C(1) << CAP_SYS_RESOURCE
),
958 .id
= "CapabilityBoundingSet=~CAP_MKNOD",
959 .description_good
= "Service cannot create device nodes",
960 .description_bad
= "Service may create device nodes",
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_MKNOD
),
968 .id
= "CapabilityBoundingSet=~CAP_(CHOWN|FSETID|SETFCAP)",
969 .description_good
= "Service cannot change file ownership/access mode/capabilities",
970 .description_bad
= "Service may change file ownership/access mode/capabilities unrestricted",
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_CHOWN
) |
976 (UINT64_C(1) << CAP_FSETID
) |
977 (UINT64_C(1) << CAP_SETFCAP
),
980 .id
= "CapabilityBoundingSet=~CAP_(DAC_*|FOWNER|IPC_OWNER)",
981 .description_good
= "Service cannot override UNIX file/IPC permission checks",
982 .description_bad
= "Service may override UNIX file/IPC permission checks",
983 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
986 .assess
= assess_capability_bounding_set
,
987 .parameter
= (UINT64_C(1) << CAP_DAC_OVERRIDE
) |
988 (UINT64_C(1) << CAP_DAC_READ_SEARCH
) |
989 (UINT64_C(1) << CAP_FOWNER
) |
990 (UINT64_C(1) << CAP_IPC_OWNER
),
993 .id
= "CapabilityBoundingSet=~CAP_KILL",
994 .description_good
= "Service cannot send UNIX signals to arbitrary processes",
995 .description_bad
= "Service may send UNIX signals to arbitrary processes",
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_KILL
),
1003 .id
= "CapabilityBoundingSet=~CAP_NET_(BIND_SERVICE|BROADCAST|RAW)",
1004 .description_good
= "Service has no elevated networking privileges",
1005 .description_bad
= "Service has elevated networking privileges",
1006 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1009 .assess
= assess_capability_bounding_set
,
1010 .parameter
= (UINT64_C(1) << CAP_NET_BIND_SERVICE
) |
1011 (UINT64_C(1) << CAP_NET_BROADCAST
) |
1012 (UINT64_C(1) << CAP_NET_RAW
),
1015 .id
= "CapabilityBoundingSet=~CAP_SYS_BOOT",
1016 .description_good
= "Service cannot issue reboot()",
1017 .description_bad
= "Service may issue reboot()",
1018 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1021 .assess
= assess_capability_bounding_set
,
1022 .parameter
= (UINT64_C(1) << CAP_SYS_BOOT
),
1025 .id
= "CapabilityBoundingSet=~CAP_MAC_*",
1026 .description_good
= "Service cannot adjust SMACK MAC",
1027 .description_bad
= "Service may adjust SMACK MAC",
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_MAC_ADMIN
)|
1033 (UINT64_C(1) << CAP_MAC_OVERRIDE
),
1036 .id
= "CapabilityBoundingSet=~CAP_LINUX_IMMUTABLE",
1037 .description_good
= "Service cannot mark files immutable",
1038 .description_bad
= "Service may mark files immutable",
1039 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1042 .assess
= assess_capability_bounding_set
,
1043 .parameter
= (UINT64_C(1) << CAP_LINUX_IMMUTABLE
),
1046 .id
= "CapabilityBoundingSet=~CAP_IPC_LOCK",
1047 .description_good
= "Service cannot lock memory into RAM",
1048 .description_bad
= "Service may lock memory into RAM",
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_IPC_LOCK
),
1056 .id
= "CapabilityBoundingSet=~CAP_SYS_CHROOT",
1057 .description_good
= "Service cannot issue chroot()",
1058 .description_bad
= "Service may issue chroot()",
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_SYS_CHROOT
),
1066 .id
= "CapabilityBoundingSet=~CAP_BLOCK_SUSPEND",
1067 .description_good
= "Service cannot establish wake locks",
1068 .description_bad
= "Service may establish wake locks",
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_BLOCK_SUSPEND
),
1076 .id
= "CapabilityBoundingSet=~CAP_WAKE_ALARM",
1077 .description_good
= "Service cannot program timers that wake up the system",
1078 .description_bad
= "Service may program timers that wake up the system",
1079 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1082 .assess
= assess_capability_bounding_set
,
1083 .parameter
= (UINT64_C(1) << CAP_WAKE_ALARM
),
1086 .id
= "CapabilityBoundingSet=~CAP_LEASE",
1087 .description_good
= "Service cannot create file leases",
1088 .description_bad
= "Service may create file leases",
1089 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1092 .assess
= assess_capability_bounding_set
,
1093 .parameter
= (UINT64_C(1) << CAP_LEASE
),
1096 .id
= "CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG",
1097 .description_good
= "Service cannot issue vhangup()",
1098 .description_bad
= "Service may issue vhangup()",
1099 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1102 .assess
= assess_capability_bounding_set
,
1103 .parameter
= (UINT64_C(1) << CAP_SYS_TTY_CONFIG
),
1106 .id
= "CapabilityBoundingSet=~CAP_SYS_PACCT",
1107 .description_good
= "Service cannot use acct()",
1108 .description_bad
= "Service may use acct()",
1109 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1112 .assess
= assess_capability_bounding_set
,
1113 .parameter
= (UINT64_C(1) << CAP_SYS_PACCT
),
1117 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#UMask=",
1120 .assess
= assess_umask
,
1123 .id
= "KeyringMode=",
1124 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#KeyringMode=",
1125 .description_good
= "Service doesn't share key material with other services",
1126 .description_bad
= "Service shares key material with other service",
1129 .assess
= assess_keyring_mode
,
1132 .id
= "NotifyAccess=",
1133 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NotifyAccess=",
1134 .description_good
= "Service child processes cannot alter service state",
1135 .description_bad
= "Service child processes may alter service state",
1138 .assess
= assess_notify_access
,
1142 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RemoveIPC=",
1143 .description_good
= "Service user cannot leave SysV IPC objects around",
1144 .description_bad
= "Service user may leave SysV IPC objects around",
1145 .description_na
= "Service runs as root, option does not apply",
1148 .assess
= assess_remove_ipc
,
1149 .offset
= offsetof(struct security_info
, remove_ipc
),
1153 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Delegate=",
1154 .description_good
= "Service does not maintain its own delegated control group subtree",
1155 .description_bad
= "Service maintains its own delegated control group subtree",
1158 .assess
= assess_bool
,
1159 .offset
= offsetof(struct security_info
, delegate
),
1160 .parameter
= true, /* invert! */
1163 .id
= "RestrictRealtime=",
1164 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictRealtime=",
1165 .description_good
= "Service realtime scheduling access is restricted",
1166 .description_bad
= "Service may acquire realtime scheduling",
1169 .assess
= assess_bool
,
1170 .offset
= offsetof(struct security_info
, restrict_realtime
),
1173 .id
= "RestrictSUIDSGID=",
1174 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictSUIDSGID=",
1175 .description_good
= "SUID/SGID file creation by service is restricted",
1176 .description_bad
= "Service may create SUID/SGID files",
1179 .assess
= assess_bool
,
1180 .offset
= offsetof(struct security_info
, restrict_suid_sgid
),
1183 .id
= "RestrictNamespaces=~CLONE_NEWUSER",
1184 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1185 .description_good
= "Service cannot create user namespaces",
1186 .description_bad
= "Service may create user namespaces",
1189 .assess
= assess_restrict_namespaces
,
1190 .parameter
= CLONE_NEWUSER
,
1193 .id
= "RestrictNamespaces=~CLONE_NEWNS",
1194 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1195 .description_good
= "Service cannot create file system namespaces",
1196 .description_bad
= "Service may create file system namespaces",
1199 .assess
= assess_restrict_namespaces
,
1200 .parameter
= CLONE_NEWNS
,
1203 .id
= "RestrictNamespaces=~CLONE_NEWIPC",
1204 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1205 .description_good
= "Service cannot create IPC namespaces",
1206 .description_bad
= "Service may create IPC namespaces",
1209 .assess
= assess_restrict_namespaces
,
1210 .parameter
= CLONE_NEWIPC
,
1213 .id
= "RestrictNamespaces=~CLONE_NEWPID",
1214 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1215 .description_good
= "Service cannot create process namespaces",
1216 .description_bad
= "Service may create process namespaces",
1219 .assess
= assess_restrict_namespaces
,
1220 .parameter
= CLONE_NEWPID
,
1223 .id
= "RestrictNamespaces=~CLONE_NEWCGROUP",
1224 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1225 .description_good
= "Service cannot create cgroup namespaces",
1226 .description_bad
= "Service may create cgroup namespaces",
1229 .assess
= assess_restrict_namespaces
,
1230 .parameter
= CLONE_NEWCGROUP
,
1233 .id
= "RestrictNamespaces=~CLONE_NEWNET",
1234 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1235 .description_good
= "Service cannot create network namespaces",
1236 .description_bad
= "Service may create network namespaces",
1239 .assess
= assess_restrict_namespaces
,
1240 .parameter
= CLONE_NEWNET
,
1243 .id
= "RestrictNamespaces=~CLONE_NEWUTS",
1244 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1245 .description_good
= "Service cannot create hostname namespaces",
1246 .description_bad
= "Service may create hostname namespaces",
1249 .assess
= assess_restrict_namespaces
,
1250 .parameter
= CLONE_NEWUTS
,
1253 .id
= "RestrictAddressFamilies=~AF_(INET|INET6)",
1254 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1255 .description_good
= "Service cannot allocate Internet sockets",
1256 .description_bad
= "Service may allocate Internet sockets",
1259 .assess
= assess_bool
,
1260 .offset
= offsetof(struct security_info
, restrict_address_family_inet
),
1263 .id
= "RestrictAddressFamilies=~AF_UNIX",
1264 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1265 .description_good
= "Service cannot allocate local sockets",
1266 .description_bad
= "Service may allocate local sockets",
1269 .assess
= assess_bool
,
1270 .offset
= offsetof(struct security_info
, restrict_address_family_unix
),
1273 .id
= "RestrictAddressFamilies=~AF_NETLINK",
1274 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1275 .description_good
= "Service cannot allocate netlink sockets",
1276 .description_bad
= "Service may allocate netlink sockets",
1279 .assess
= assess_bool
,
1280 .offset
= offsetof(struct security_info
, restrict_address_family_netlink
),
1283 .id
= "RestrictAddressFamilies=~AF_PACKET",
1284 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1285 .description_good
= "Service cannot allocate packet sockets",
1286 .description_bad
= "Service may allocate packet sockets",
1289 .assess
= assess_bool
,
1290 .offset
= offsetof(struct security_info
, restrict_address_family_packet
),
1293 .id
= "RestrictAddressFamilies=~…",
1294 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1295 .description_good
= "Service cannot allocate exotic sockets",
1296 .description_bad
= "Service may allocate exotic sockets",
1299 .assess
= assess_bool
,
1300 .offset
= offsetof(struct security_info
, restrict_address_family_other
),
1303 .id
= "SystemCallArchitectures=",
1304 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallArchitectures=",
1307 .assess
= assess_system_call_architectures
,
1311 .id
= "SystemCallFilter=~@swap",
1312 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1315 .assess
= assess_system_call_filter
,
1316 .parameter
= SYSCALL_FILTER_SET_SWAP
,
1319 .id
= "SystemCallFilter=~@obsolete",
1320 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1323 .assess
= assess_system_call_filter
,
1324 .parameter
= SYSCALL_FILTER_SET_OBSOLETE
,
1327 .id
= "SystemCallFilter=~@clock",
1328 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1331 .assess
= assess_system_call_filter
,
1332 .parameter
= SYSCALL_FILTER_SET_CLOCK
,
1335 .id
= "SystemCallFilter=~@cpu-emulation",
1336 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1339 .assess
= assess_system_call_filter
,
1340 .parameter
= SYSCALL_FILTER_SET_CPU_EMULATION
,
1343 .id
= "SystemCallFilter=~@debug",
1344 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1347 .assess
= assess_system_call_filter
,
1348 .parameter
= SYSCALL_FILTER_SET_DEBUG
,
1351 .id
= "SystemCallFilter=~@mount",
1352 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1355 .assess
= assess_system_call_filter
,
1356 .parameter
= SYSCALL_FILTER_SET_MOUNT
,
1359 .id
= "SystemCallFilter=~@module",
1360 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1363 .assess
= assess_system_call_filter
,
1364 .parameter
= SYSCALL_FILTER_SET_MODULE
,
1367 .id
= "SystemCallFilter=~@raw-io",
1368 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1371 .assess
= assess_system_call_filter
,
1372 .parameter
= SYSCALL_FILTER_SET_RAW_IO
,
1375 .id
= "SystemCallFilter=~@reboot",
1376 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1379 .assess
= assess_system_call_filter
,
1380 .parameter
= SYSCALL_FILTER_SET_REBOOT
,
1383 .id
= "SystemCallFilter=~@privileged",
1384 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1387 .assess
= assess_system_call_filter
,
1388 .parameter
= SYSCALL_FILTER_SET_PRIVILEGED
,
1391 .id
= "SystemCallFilter=~@resources",
1392 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1395 .assess
= assess_system_call_filter
,
1396 .parameter
= SYSCALL_FILTER_SET_RESOURCES
,
1400 .id
= "IPAddressDeny=",
1401 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#IPAddressDeny=",
1404 .assess
= assess_ip_address_allow
,
1407 .id
= "DeviceAllow=",
1408 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#DeviceAllow=",
1411 .assess
= assess_device_allow
,
1414 .id
= "AmbientCapabilities=",
1415 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#AmbientCapabilities=",
1416 .description_good
= "Service process does not receive ambient capabilities",
1417 .description_bad
= "Service process receives ambient capabilities",
1420 .assess
= assess_ambient_capabilities
,
1424 static int assess(const struct security_info
*info
, Table
*overview_table
, AnalyzeSecurityFlags flags
) {
1425 static const struct {
1429 SpecialGlyph smiley
;
1430 } badness_table
[] = {
1431 { 100, "DANGEROUS", ANSI_HIGHLIGHT_RED
, SPECIAL_GLYPH_DEPRESSED_SMILEY
},
1432 { 90, "UNSAFE", ANSI_HIGHLIGHT_RED
, SPECIAL_GLYPH_UNHAPPY_SMILEY
},
1433 { 75, "EXPOSED", ANSI_HIGHLIGHT_YELLOW
, SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY
},
1434 { 50, "MEDIUM", NULL
, SPECIAL_GLYPH_NEUTRAL_SMILEY
},
1435 { 10, "OK", ANSI_HIGHLIGHT_GREEN
, SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY
},
1436 { 1, "SAFE", ANSI_HIGHLIGHT_GREEN
, SPECIAL_GLYPH_HAPPY_SMILEY
},
1437 { 0, "PERFECT", ANSI_HIGHLIGHT_GREEN
, SPECIAL_GLYPH_ECSTATIC_SMILEY
},
1440 uint64_t badness_sum
= 0, weight_sum
= 0, exposure
;
1441 _cleanup_(table_unrefp
) Table
*details_table
= NULL
;
1445 if (!FLAGS_SET(flags
, ANALYZE_SECURITY_SHORT
)) {
1446 details_table
= table_new(" ", "name", "description", "weight", "badness", "range", "exposure");
1450 (void) table_set_sort(details_table
, 3, 1, (size_t) -1);
1451 (void) table_set_reverse(details_table
, 3, true);
1453 if (getenv_bool("SYSTEMD_ANALYZE_DEBUG") <= 0)
1454 (void) table_set_display(details_table
, 0, 1, 2, 6, (size_t) -1);
1457 for (i
= 0; i
< ELEMENTSOF(security_assessor_table
); i
++) {
1458 const struct security_assessor
*a
= security_assessor_table
+ i
;
1459 _cleanup_free_
char *d
= NULL
;
1463 data
= (uint8_t *) info
+ a
->offset
;
1465 if (a
->default_dependencies_only
&& !info
->default_dependencies
) {
1466 badness
= UINT64_MAX
;
1467 d
= strdup("Service runs in special boot phase, option does not apply");
1471 r
= a
->assess(a
, info
, data
, &badness
, &d
);
1476 assert(a
->range
> 0);
1478 if (badness
!= UINT64_MAX
) {
1479 assert(badness
<= a
->range
);
1481 badness_sum
+= DIV_ROUND_UP(badness
* a
->weight
, a
->range
);
1482 weight_sum
+= a
->weight
;
1485 if (details_table
) {
1486 const char *checkmark
, *description
, *color
= NULL
;
1489 if (badness
== UINT64_MAX
) {
1491 description
= a
->description_na
;
1493 } else if (badness
== a
->range
) {
1494 checkmark
= special_glyph(SPECIAL_GLYPH_CROSS_MARK
);
1495 description
= a
->description_bad
;
1496 color
= ansi_highlight_red();
1497 } else if (badness
== 0) {
1498 checkmark
= special_glyph(SPECIAL_GLYPH_CHECK_MARK
);
1499 description
= a
->description_good
;
1500 color
= ansi_highlight_green();
1502 checkmark
= special_glyph(SPECIAL_GLYPH_CROSS_MARK
);
1504 color
= ansi_highlight_red();
1510 r
= table_add_cell_full(details_table
, &cell
, TABLE_STRING
, checkmark
, 1, 1, 0, 0, 0);
1512 return log_error_errno(r
, "Failed to add cell to table: %m");
1514 (void) table_set_color(details_table
, cell
, color
);
1516 r
= table_add_many(details_table
,
1517 TABLE_STRING
, a
->id
, TABLE_SET_URL
, a
->url
,
1518 TABLE_STRING
, description
,
1519 TABLE_UINT64
, a
->weight
, TABLE_SET_ALIGN_PERCENT
, 100,
1520 TABLE_UINT64
, badness
, TABLE_SET_ALIGN_PERCENT
, 100,
1521 TABLE_UINT64
, a
->range
, TABLE_SET_ALIGN_PERCENT
, 100,
1522 TABLE_EMPTY
, TABLE_SET_ALIGN_PERCENT
, 100);
1524 return log_error_errno(r
, "Failed to add cells to table: %m");
1528 assert(weight_sum
> 0);
1530 if (details_table
) {
1533 for (row
= 1; row
< table_get_rows(details_table
); row
++) {
1534 char buf
[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1];
1535 const uint64_t *weight
, *badness
, *range
;
1539 assert_se(weight
= table_get_at(details_table
, row
, 3));
1540 assert_se(badness
= table_get_at(details_table
, row
, 4));
1541 assert_se(range
= table_get_at(details_table
, row
, 5));
1543 if (*badness
== UINT64_MAX
|| *badness
== 0)
1546 assert_se(cell
= table_get_cell(details_table
, row
, 6));
1548 x
= DIV_ROUND_UP(DIV_ROUND_UP(*badness
* *weight
* 100U, *range
), weight_sum
);
1549 xsprintf(buf
, "%" PRIu64
".%" PRIu64
, x
/ 10, x
% 10);
1551 r
= table_update(details_table
, cell
, TABLE_STRING
, buf
);
1553 return log_error_errno(r
, "Failed to update cell in table: %m");
1556 r
= table_print(details_table
, stdout
);
1558 return log_error_errno(r
, "Failed to output table: %m");
1561 exposure
= DIV_ROUND_UP(badness_sum
* 100U, weight_sum
);
1563 for (i
= 0; i
< ELEMENTSOF(badness_table
); i
++)
1564 if (exposure
>= badness_table
[i
].exposure
)
1567 assert(i
< ELEMENTSOF(badness_table
));
1569 if (details_table
) {
1570 _cleanup_free_
char *clickable
= NULL
;
1573 /* If we shall output the details table, also print the brief summary underneath */
1575 if (info
->fragment_path
) {
1576 r
= terminal_urlify_path(info
->fragment_path
, info
->id
, &clickable
);
1584 printf("\n%s %sOverall exposure level for %s%s: %s%" PRIu64
".%" PRIu64
" %s%s %s\n",
1585 special_glyph(SPECIAL_GLYPH_ARROW
),
1589 colors_enabled() ? strempty(badness_table
[i
].color
) : "",
1590 exposure
/ 10, exposure
% 10,
1591 badness_table
[i
].name
,
1593 special_glyph(badness_table
[i
].smiley
));
1598 if (overview_table
) {
1599 char buf
[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1];
1602 r
= table_add_cell(overview_table
, &cell
, TABLE_STRING
, info
->id
);
1604 return log_error_errno(r
, "Failed to add cell to table: %m");
1605 if (info
->fragment_path
) {
1606 _cleanup_free_
char *url
= NULL
;
1608 r
= file_url_from_path(info
->fragment_path
, &url
);
1610 return log_error_errno(r
, "Failed to generate URL from path: %m");
1612 (void) table_set_url(overview_table
, cell
, url
);
1615 xsprintf(buf
, "%" PRIu64
".%" PRIu64
, exposure
/ 10, exposure
% 10);
1616 r
= table_add_cell(overview_table
, &cell
, TABLE_STRING
, buf
);
1618 return log_error_errno(r
, "Failed to add cell to table: %m");
1619 (void) table_set_align_percent(overview_table
, cell
, 100);
1621 r
= table_add_cell(overview_table
, &cell
, TABLE_STRING
, badness_table
[i
].name
);
1623 return log_error_errno(r
, "Failed to add cell to table: %m");
1624 (void) table_set_color(overview_table
, cell
, strempty(badness_table
[i
].color
));
1626 r
= table_add_cell(overview_table
, NULL
, TABLE_STRING
, special_glyph(badness_table
[i
].smiley
));
1628 return log_error_errno(r
, "Failed to add cell to table: %m");
1634 static int property_read_restrict_address_families(
1638 sd_bus_error
*error
,
1641 struct security_info
*info
= userdata
;
1648 r
= sd_bus_message_enter_container(m
, 'r', "bas");
1652 r
= sd_bus_message_read(m
, "b", &whitelist
);
1656 info
->restrict_address_family_inet
=
1657 info
->restrict_address_family_unix
=
1658 info
->restrict_address_family_netlink
=
1659 info
->restrict_address_family_packet
=
1660 info
->restrict_address_family_other
= whitelist
;
1662 r
= sd_bus_message_enter_container(m
, 'a', "s");
1669 r
= sd_bus_message_read(m
, "s", &name
);
1675 if (STR_IN_SET(name
, "AF_INET", "AF_INET6"))
1676 info
->restrict_address_family_inet
= !whitelist
;
1677 else if (streq(name
, "AF_UNIX"))
1678 info
->restrict_address_family_unix
= !whitelist
;
1679 else if (streq(name
, "AF_NETLINK"))
1680 info
->restrict_address_family_netlink
= !whitelist
;
1681 else if (streq(name
, "AF_PACKET"))
1682 info
->restrict_address_family_packet
= !whitelist
;
1684 info
->restrict_address_family_other
= !whitelist
;
1687 r
= sd_bus_message_exit_container(m
);
1691 return sd_bus_message_exit_container(m
);
1694 static int property_read_system_call_filter(
1698 sd_bus_error
*error
,
1701 struct security_info
*info
= userdata
;
1708 r
= sd_bus_message_enter_container(m
, 'r', "bas");
1712 r
= sd_bus_message_read(m
, "b", &whitelist
);
1716 info
->system_call_filter_whitelist
= whitelist
;
1718 r
= sd_bus_message_enter_container(m
, 'a', "s");
1725 r
= sd_bus_message_read(m
, "s", &name
);
1731 r
= set_ensure_allocated(&info
->system_call_filter
, &string_hash_ops
);
1735 r
= set_put_strdup(info
->system_call_filter
, name
);
1740 r
= sd_bus_message_exit_container(m
);
1744 return sd_bus_message_exit_container(m
);
1747 static int property_read_ip_address_allow(
1751 sd_bus_error
*error
,
1754 struct security_info
*info
= userdata
;
1755 bool deny_ipv4
= false, deny_ipv6
= false;
1762 r
= sd_bus_message_enter_container(m
, 'a', "(iayu)");
1772 r
= sd_bus_message_enter_container(m
, 'r', "iayu");
1778 r
= sd_bus_message_read(m
, "i", &family
);
1782 r
= sd_bus_message_read_array(m
, 'y', &data
, &size
);
1786 r
= sd_bus_message_read(m
, "u", &prefixlen
);
1790 r
= sd_bus_message_exit_container(m
);
1794 if (streq(member
, "IPAddressAllow")) {
1795 union in_addr_union u
;
1797 if (family
== AF_INET
&& size
== 4 && prefixlen
== 8)
1798 memcpy(&u
.in
, data
, size
);
1799 else if (family
== AF_INET6
&& size
== 16 && prefixlen
== 128)
1800 memcpy(&u
.in6
, data
, size
);
1802 info
->ip_address_allow_other
= true;
1806 if (in_addr_is_localhost(family
, &u
))
1807 info
->ip_address_allow_localhost
= true;
1809 info
->ip_address_allow_other
= true;
1811 assert(streq(member
, "IPAddressDeny"));
1813 if (family
== AF_INET
&& size
== 4 && prefixlen
== 0)
1815 else if (family
== AF_INET6
&& size
== 16 && prefixlen
== 0)
1820 info
->ip_address_deny_all
= deny_ipv4
&& deny_ipv6
;
1822 return sd_bus_message_exit_container(m
);
1825 static int property_read_ip_filters(
1829 sd_bus_error
*error
,
1832 struct security_info
*info
= userdata
;
1833 _cleanup_(strv_freep
) char **l
= NULL
;
1840 r
= sd_bus_message_read_strv(m
, &l
);
1844 if (streq(member
, "IPIngressFilterPath"))
1845 info
->ip_filters_custom_ingress
= !strv_isempty(l
);
1846 else if (streq(member
, "IPEgressFilterPath"))
1847 info
->ip_filters_custom_ingress
= !strv_isempty(l
);
1852 static int property_read_device_allow(
1856 sd_bus_error
*error
,
1859 struct security_info
*info
= userdata
;
1867 r
= sd_bus_message_enter_container(m
, 'a', "(ss)");
1872 const char *name
, *policy
;
1874 r
= sd_bus_message_read(m
, "(ss)", &name
, &policy
);
1883 info
->device_allow_non_empty
= n
> 0;
1885 return sd_bus_message_exit_container(m
);
1888 static int acquire_security_info(sd_bus
*bus
, const char *name
, struct security_info
*info
, AnalyzeSecurityFlags flags
) {
1890 static const struct bus_properties_map security_map
[] = {
1891 { "AmbientCapabilities", "t", NULL
, offsetof(struct security_info
, ambient_capabilities
) },
1892 { "CapabilityBoundingSet", "t", NULL
, offsetof(struct security_info
, capability_bounding_set
) },
1893 { "DefaultDependencies", "b", NULL
, offsetof(struct security_info
, default_dependencies
) },
1894 { "Delegate", "b", NULL
, offsetof(struct security_info
, delegate
) },
1895 { "DeviceAllow", "a(ss)", property_read_device_allow
, 0 },
1896 { "DevicePolicy", "s", NULL
, offsetof(struct security_info
, device_policy
) },
1897 { "DynamicUser", "b", NULL
, offsetof(struct security_info
, dynamic_user
) },
1898 { "FragmentPath", "s", NULL
, offsetof(struct security_info
, fragment_path
) },
1899 { "IPAddressAllow", "a(iayu)", property_read_ip_address_allow
, 0 },
1900 { "IPAddressDeny", "a(iayu)", property_read_ip_address_allow
, 0 },
1901 { "IPIngressFilterPath", "as", property_read_ip_filters
, 0 },
1902 { "IPEgressFilterPath", "as", property_read_ip_filters
, 0 },
1903 { "Id", "s", NULL
, offsetof(struct security_info
, id
) },
1904 { "KeyringMode", "s", NULL
, offsetof(struct security_info
, keyring_mode
) },
1905 { "LoadState", "s", NULL
, offsetof(struct security_info
, load_state
) },
1906 { "LockPersonality", "b", NULL
, offsetof(struct security_info
, lock_personality
) },
1907 { "MemoryDenyWriteExecute", "b", NULL
, offsetof(struct security_info
, memory_deny_write_execute
) },
1908 { "NoNewPrivileges", "b", NULL
, offsetof(struct security_info
, no_new_privileges
) },
1909 { "NotifyAccess", "s", NULL
, offsetof(struct security_info
, notify_access
) },
1910 { "PrivateDevices", "b", NULL
, offsetof(struct security_info
, private_devices
) },
1911 { "PrivateMounts", "b", NULL
, offsetof(struct security_info
, private_mounts
) },
1912 { "PrivateNetwork", "b", NULL
, offsetof(struct security_info
, private_network
) },
1913 { "PrivateTmp", "b", NULL
, offsetof(struct security_info
, private_tmp
) },
1914 { "PrivateUsers", "b", NULL
, offsetof(struct security_info
, private_users
) },
1915 { "ProtectControlGroups", "b", NULL
, offsetof(struct security_info
, protect_control_groups
) },
1916 { "ProtectHome", "s", NULL
, offsetof(struct security_info
, protect_home
) },
1917 { "ProtectHostname", "b", NULL
, offsetof(struct security_info
, protect_hostname
) },
1918 { "ProtectKernelModules", "b", NULL
, offsetof(struct security_info
, protect_kernel_modules
) },
1919 { "ProtectKernelTunables", "b", NULL
, offsetof(struct security_info
, protect_kernel_tunables
) },
1920 { "ProtectKernelLogs", "b", NULL
, offsetof(struct security_info
, protect_kernel_logs
) },
1921 { "ProtectSystem", "s", NULL
, offsetof(struct security_info
, protect_system
) },
1922 { "RemoveIPC", "b", NULL
, offsetof(struct security_info
, remove_ipc
) },
1923 { "RestrictAddressFamilies", "(bas)", property_read_restrict_address_families
, 0 },
1924 { "RestrictNamespaces", "t", NULL
, offsetof(struct security_info
, restrict_namespaces
) },
1925 { "RestrictRealtime", "b", NULL
, offsetof(struct security_info
, restrict_realtime
) },
1926 { "RestrictSUIDSGID", "b", NULL
, offsetof(struct security_info
, restrict_suid_sgid
) },
1927 { "RootDirectory", "s", NULL
, offsetof(struct security_info
, root_directory
) },
1928 { "RootImage", "s", NULL
, offsetof(struct security_info
, root_image
) },
1929 { "SupplementaryGroups", "as", NULL
, offsetof(struct security_info
, supplementary_groups
) },
1930 { "SystemCallArchitectures", "as", NULL
, offsetof(struct security_info
, system_call_architectures
) },
1931 { "SystemCallFilter", "(as)", property_read_system_call_filter
, 0 },
1932 { "Type", "s", NULL
, offsetof(struct security_info
, type
) },
1933 { "UMask", "u", NULL
, offsetof(struct security_info
, _umask
) },
1934 { "User", "s", NULL
, offsetof(struct security_info
, user
) },
1938 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1939 _cleanup_free_
char *path
= NULL
;
1942 /* Note: this mangles *info on failure! */
1948 path
= unit_dbus_path_from_name(name
);
1952 r
= bus_map_all_properties(
1954 "org.freedesktop.systemd1",
1957 BUS_MAP_STRDUP
| BUS_MAP_BOOLEAN_AS_BOOL
,
1962 return log_error_errno(r
, "Failed to get unit properties: %s", bus_error_message(&error
, r
));
1964 if (!streq_ptr(info
->load_state
, "loaded")) {
1966 if (FLAGS_SET(flags
, ANALYZE_SECURITY_ONLY_LOADED
))
1967 return -EMEDIUMTYPE
;
1969 if (streq_ptr(info
->load_state
, "not-found"))
1970 log_error("Unit %s not found, cannot analyze.", name
);
1971 else if (streq_ptr(info
->load_state
, "masked"))
1972 log_error("Unit %s is masked, cannot analyze.", name
);
1974 log_error("Unit %s not loaded properly, cannot analyze.", name
);
1979 if (FLAGS_SET(flags
, ANALYZE_SECURITY_ONLY_LONG_RUNNING
) && streq_ptr(info
->type
, "oneshot"))
1980 return -EMEDIUMTYPE
;
1982 if (info
->private_devices
||
1983 info
->private_tmp
||
1984 info
->protect_control_groups
||
1985 info
->protect_kernel_tunables
||
1986 info
->protect_kernel_modules
||
1987 !streq_ptr(info
->protect_home
, "no") ||
1988 !streq_ptr(info
->protect_system
, "no") ||
1990 info
->private_mounts
= true;
1992 if (info
->protect_kernel_modules
)
1993 info
->capability_bounding_set
&= ~(UINT64_C(1) << CAP_SYS_MODULE
);
1995 if (info
->protect_kernel_logs
)
1996 info
->capability_bounding_set
&= ~(UINT64_C(1) << CAP_SYSLOG
);
1998 if (info
->private_devices
)
1999 info
->capability_bounding_set
&= ~((UINT64_C(1) << CAP_MKNOD
) |
2000 (UINT64_C(1) << CAP_SYS_RAWIO
));
2005 static int analyze_security_one(sd_bus
*bus
, const char *name
, Table
*overview_table
, AnalyzeSecurityFlags flags
) {
2006 _cleanup_(security_info_free
) struct security_info info
= {
2007 .default_dependencies
= true,
2008 .capability_bounding_set
= UINT64_MAX
,
2009 .restrict_namespaces
= UINT64_MAX
,
2017 r
= acquire_security_info(bus
, name
, &info
, flags
);
2018 if (r
== -EMEDIUMTYPE
) /* Ignore this one because not loaded or Type is oneshot */
2023 r
= assess(&info
, overview_table
, flags
);
2030 int analyze_security(sd_bus
*bus
, char **units
, AnalyzeSecurityFlags flags
) {
2031 _cleanup_(table_unrefp
) Table
*overview_table
= NULL
;
2036 if (strv_length(units
) != 1) {
2037 overview_table
= table_new("unit", "exposure", "predicate", "happy");
2038 if (!overview_table
)
2042 if (strv_isempty(units
)) {
2043 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2044 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2045 _cleanup_strv_free_
char **list
= NULL
;
2046 size_t allocated
= 0, n
= 0;
2049 r
= sd_bus_call_method(
2051 "org.freedesktop.systemd1",
2052 "/org/freedesktop/systemd1",
2053 "org.freedesktop.systemd1.Manager",
2059 return log_error_errno(r
, "Failed to list units: %s", bus_error_message(&error
, r
));
2061 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
2063 return bus_log_parse_error(r
);
2069 r
= bus_parse_unit_info(reply
, &info
);
2071 return bus_log_parse_error(r
);
2075 if (!endswith(info
.id
, ".service"))
2078 if (!GREEDY_REALLOC(list
, allocated
, n
+ 2))
2081 copy
= strdup(info
.id
);
2091 flags
|= ANALYZE_SECURITY_SHORT
|ANALYZE_SECURITY_ONLY_LOADED
|ANALYZE_SECURITY_ONLY_LONG_RUNNING
;
2093 STRV_FOREACH(i
, list
) {
2094 r
= analyze_security_one(bus
, *i
, overview_table
, flags
);
2095 if (r
< 0 && ret
>= 0)
2102 STRV_FOREACH(i
, units
) {
2103 _cleanup_free_
char *mangled
= NULL
, *instance
= NULL
;
2106 if (!FLAGS_SET(flags
, ANALYZE_SECURITY_SHORT
) && i
!= units
) {
2111 r
= unit_name_mangle(*i
, 0, &mangled
);
2113 return log_error_errno(r
, "Failed to mangle unit name '%s': %m", *i
);
2115 if (!endswith(mangled
, ".service")) {
2116 log_error("Unit %s is not a service unit, refusing.", *i
);
2120 if (unit_name_is_valid(mangled
, UNIT_NAME_TEMPLATE
)) {
2121 r
= unit_name_replace_instance(mangled
, "test-instance", &instance
);
2129 r
= analyze_security_one(bus
, name
, overview_table
, flags
);
2130 if (r
< 0 && ret
>= 0)
2135 if (overview_table
) {
2136 if (!FLAGS_SET(flags
, ANALYZE_SECURITY_SHORT
)) {
2141 r
= table_print(overview_table
, stdout
);
2143 return log_error_errno(r
, "Failed to output table: %m");