1 /* SPDX-License-Identifier: LGPL-2.1+ */
4 #include <sys/utsname.h>
6 #include "analyze-security.h"
8 #include "bus-unit-util.h"
11 #include "format-table.h"
12 #include "in-addr-util.h"
13 #include "locale-util.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
;
49 bool lock_personality
;
50 bool memory_deny_write_execute
;
51 bool no_new_privileges
;
53 bool protect_hostname
;
61 bool protect_control_groups
;
62 bool protect_kernel_modules
;
63 bool protect_kernel_tunables
;
70 bool restrict_address_family_inet
;
71 bool restrict_address_family_unix
;
72 bool restrict_address_family_netlink
;
73 bool restrict_address_family_packet
;
74 bool restrict_address_family_other
;
76 uint64_t restrict_namespaces
;
77 bool restrict_realtime
;
78 bool restrict_suid_sgid
;
85 bool device_allow_non_empty
;
87 char **system_call_architectures
;
89 bool system_call_filter_whitelist
;
90 Set
*system_call_filter
;
95 struct security_assessor
{
97 const char *description_good
;
98 const char *description_bad
;
99 const char *description_na
;
103 int (*assess
)(const struct security_assessor
*a
, const struct security_info
*info
, const void *data
, uint64_t *ret_badness
, char **ret_description
);
106 bool default_dependencies_only
;
109 static void security_info_free(struct security_info
*i
) {
116 free(i
->fragment_path
);
120 free(i
->protect_home
);
121 free(i
->protect_system
);
123 free(i
->root_directory
);
126 free(i
->keyring_mode
);
127 free(i
->notify_access
);
129 free(i
->device_policy
);
131 strv_free(i
->supplementary_groups
);
132 strv_free(i
->system_call_architectures
);
134 set_free_free(i
->system_call_filter
);
137 static bool security_info_runs_privileged(const struct security_info
*i
) {
140 if (STRPTR_IN_SET(i
->user
, "0", "root"))
146 return isempty(i
->user
);
149 static int assess_bool(
150 const struct security_assessor
*a
,
151 const struct security_info
*info
,
153 uint64_t *ret_badness
,
154 char **ret_description
) {
156 const bool *b
= data
;
160 assert(ret_description
);
162 *ret_badness
= a
->parameter
? *b
: !*b
;
163 *ret_description
= NULL
;
168 static int assess_user(
169 const struct security_assessor
*a
,
170 const struct security_info
*info
,
172 uint64_t *ret_badness
,
173 char **ret_description
) {
175 _cleanup_free_
char *d
= NULL
;
179 assert(ret_description
);
181 if (streq_ptr(info
->user
, NOBODY_USER_NAME
)) {
182 d
= strdup("Service runs under as '" NOBODY_USER_NAME
"' user, which should not be used for services");
184 } else if (info
->dynamic_user
&& !STR_IN_SET(info
->user
, "0", "root")) {
185 d
= strdup("Service runs under a transient non-root user identity");
187 } else if (info
->user
&& !STR_IN_SET(info
->user
, "0", "root", "")) {
188 d
= strdup("Service runs under a static non-root user identity");
192 *ret_description
= NULL
;
200 *ret_description
= TAKE_PTR(d
);
205 static int assess_protect_home(
206 const struct security_assessor
*a
,
207 const struct security_info
*info
,
209 uint64_t *ret_badness
,
210 char **ret_description
) {
212 const char *description
;
218 assert(ret_description
);
221 description
= "Service has full access to home directories";
223 r
= parse_boolean(info
->protect_home
);
225 if (streq_ptr(info
->protect_home
, "read-only")) {
227 description
= "Service has read-only access to home directories";
228 } else if (streq_ptr(info
->protect_home
, "tmpfs")) {
230 description
= "Service has access to fake empty home directories";
234 description
= "Service has no access to home directories";
237 copy
= strdup(description
);
241 *ret_badness
= badness
;
242 *ret_description
= copy
;
247 static int assess_protect_system(
248 const struct security_assessor
*a
,
249 const struct security_info
*info
,
251 uint64_t *ret_badness
,
252 char **ret_description
) {
254 const char *description
;
260 assert(ret_description
);
263 description
= "Service has full access to the OS file hierarchy";
265 r
= parse_boolean(info
->protect_system
);
267 if (streq_ptr(info
->protect_system
, "full")) {
269 description
= "Service has very limited write access to the OS file hierarchy";
270 } else if (streq_ptr(info
->protect_system
, "strict")) {
272 description
= "Service has strict read-only access to the OS file hierarchy";
276 description
= "Service has limited write access to the OS file hierarchy";
279 copy
= strdup(description
);
283 *ret_badness
= badness
;
284 *ret_description
= copy
;
289 static int assess_root_directory(
290 const struct security_assessor
*a
,
291 const struct security_info
*info
,
293 uint64_t *ret_badness
,
294 char **ret_description
) {
297 assert(ret_description
);
300 (isempty(info
->root_directory
) ||
301 path_equal(info
->root_directory
, "/")) &&
302 (isempty(info
->root_image
) ||
303 path_equal(info
->root_image
, "/"));
304 *ret_description
= NULL
;
309 static int assess_capability_bounding_set(
310 const struct security_assessor
*a
,
311 const struct security_info
*info
,
313 uint64_t *ret_badness
,
314 char **ret_description
) {
317 assert(ret_description
);
319 *ret_badness
= !!(info
->capability_bounding_set
& a
->parameter
);
320 *ret_description
= NULL
;
325 static int assess_umask(
326 const struct security_assessor
*a
,
327 const struct security_info
*info
,
329 uint64_t *ret_badness
,
330 char **ret_description
) {
337 assert(ret_description
);
339 if (!FLAGS_SET(info
->_umask
, 0002)) {
340 d
= "Files created by service are world-writable by default";
342 } else if (!FLAGS_SET(info
->_umask
, 0004)) {
343 d
= "Files created by service are world-readable by default";
345 } else if (!FLAGS_SET(info
->_umask
, 0020)) {
346 d
= "Files created by service are group-writable by default";
348 } else if (!FLAGS_SET(info
->_umask
, 0040)) {
349 d
= "Files created by service are group-readable by default";
352 d
= "Files created by service are accessible only by service's own user by default";
361 *ret_description
= copy
;
366 static int assess_keyring_mode(
367 const struct security_assessor
*a
,
368 const struct security_info
*info
,
370 uint64_t *ret_badness
,
371 char **ret_description
) {
374 assert(ret_description
);
376 *ret_badness
= !streq_ptr(info
->keyring_mode
, "private");
377 *ret_description
= NULL
;
382 static int assess_notify_access(
383 const struct security_assessor
*a
,
384 const struct security_info
*info
,
386 uint64_t *ret_badness
,
387 char **ret_description
) {
390 assert(ret_description
);
392 *ret_badness
= streq_ptr(info
->notify_access
, "all");
393 *ret_description
= NULL
;
398 static int assess_remove_ipc(
399 const struct security_assessor
*a
,
400 const struct security_info
*info
,
402 uint64_t *ret_badness
,
403 char **ret_description
) {
406 assert(ret_description
);
408 if (security_info_runs_privileged(info
))
409 *ret_badness
= UINT64_MAX
;
411 *ret_badness
= !info
->remove_ipc
;
413 *ret_description
= NULL
;
417 static int assess_supplementary_groups(
418 const struct security_assessor
*a
,
419 const struct security_info
*info
,
421 uint64_t *ret_badness
,
422 char **ret_description
) {
425 assert(ret_description
);
427 if (security_info_runs_privileged(info
))
428 *ret_badness
= UINT64_MAX
;
430 *ret_badness
= !strv_isempty(info
->supplementary_groups
);
432 *ret_description
= NULL
;
436 static int assess_restrict_namespaces(
437 const struct security_assessor
*a
,
438 const struct security_info
*info
,
440 uint64_t *ret_badness
,
441 char **ret_description
) {
444 assert(ret_description
);
446 *ret_badness
= !!(info
->restrict_namespaces
& a
->parameter
);
447 *ret_description
= NULL
;
452 static int assess_system_call_architectures(
453 const struct security_assessor
*a
,
454 const struct security_info
*info
,
456 uint64_t *ret_badness
,
457 char **ret_description
) {
463 assert(ret_description
);
465 if (strv_isempty(info
->system_call_architectures
)) {
467 d
= strdup("Service may execute system calls with all ABIs");
468 } else if (strv_equal(info
->system_call_architectures
, STRV_MAKE("native"))) {
470 d
= strdup("Service may execute system calls only with native ABI");
473 d
= strdup("Service may execute system calls with multiple ABIs");
480 *ret_description
= d
;
487 static bool syscall_names_in_filter(Set
*s
, bool whitelist
, const SyscallFilterSet
*f
) {
490 NULSTR_FOREACH(syscall
, f
->value
) {
493 if (syscall
[0] == '@') {
494 const SyscallFilterSet
*g
;
496 assert_se(g
= syscall_filter_set_find(syscall
));
497 if (syscall_names_in_filter(s
, whitelist
, g
))
498 return true; /* bad! */
503 /* Let's see if the system call actually exists on this platform, before complaining */
504 id
= seccomp_syscall_resolve_name(syscall
);
508 if (set_contains(s
, syscall
) == whitelist
) {
509 log_debug("Offending syscall filter item: %s", syscall
);
510 return true; /* bad! */
517 static int assess_system_call_filter(
518 const struct security_assessor
*a
,
519 const struct security_info
*info
,
521 uint64_t *ret_badness
,
522 char **ret_description
) {
524 const SyscallFilterSet
*f
;
531 assert(ret_description
);
533 assert(a
->parameter
< _SYSCALL_FILTER_SET_MAX
);
534 f
= syscall_filter_sets
+ a
->parameter
;
536 if (!info
->system_call_filter_whitelist
&& set_isempty(info
->system_call_filter
)) {
537 d
= strdup("Service does not filter system calls");
542 log_debug("Analyzing system call filter, checking against: %s", f
->name
);
543 bad
= syscall_names_in_filter(info
->system_call_filter
, info
->system_call_filter_whitelist
, f
);
544 log_debug("Result: %s", bad
? "bad" : "good");
546 if (info
->system_call_filter_whitelist
) {
548 (void) asprintf(&d
, "System call whitelist defined for service, and %s is included", f
->name
);
551 (void) asprintf(&d
, "System call whitelist defined for service, and %s is not included", f
->name
);
556 (void) asprintf(&d
, "System call blacklist defined for service, and %s is not included", f
->name
);
559 (void) asprintf(&d
, "System call blacklist defined for service, and %s is included", f
->name
);
569 *ret_description
= d
;
576 static int assess_ip_address_allow(
577 const struct security_assessor
*a
,
578 const struct security_info
*info
,
580 uint64_t *ret_badness
,
581 char **ret_description
) {
588 assert(ret_description
);
590 if (!info
->ip_address_deny_all
) {
591 d
= strdup("Service does not define an IP address whitelist");
593 } else if (info
->ip_address_allow_other
) {
594 d
= strdup("Service defines IP address whitelist with non-localhost entries");
596 } else if (info
->ip_address_allow_localhost
) {
597 d
= strdup("Service defines IP address whitelits with only localhost entries");
600 d
= strdup("Service blocks all IP address ranges");
608 *ret_description
= d
;
613 static int assess_device_allow(
614 const struct security_assessor
*a
,
615 const struct security_info
*info
,
617 uint64_t *ret_badness
,
618 char **ret_description
) {
625 assert(ret_description
);
627 if (STRPTR_IN_SET(info
->device_policy
, "strict", "closed")) {
629 if (info
->device_allow_non_empty
) {
630 d
= strdup("Service has a device ACL with some special devices");
633 d
= strdup("Service has a minimal device ACL");
637 d
= strdup("Service has no device ACL");
645 *ret_description
= d
;
650 static int assess_ambient_capabilities(
651 const struct security_assessor
*a
,
652 const struct security_info
*info
,
654 uint64_t *ret_badness
,
655 char **ret_description
) {
658 assert(ret_description
);
660 *ret_badness
= info
->ambient_capabilities
!= 0;
661 *ret_description
= NULL
;
666 static const struct security_assessor security_assessor_table
[] = {
668 .id
= "User=/DynamicUser=",
669 .description_bad
= "Service runs as root user",
670 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#User=",
673 .assess
= assess_user
,
676 .id
= "SupplementaryGroups=",
677 .description_good
= "Service has no supplementary groups",
678 .description_bad
= "Service runs with supplementary groups",
679 .description_na
= "Service runs as root, option does not matter",
680 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SupplementaryGroups=",
683 .assess
= assess_supplementary_groups
,
686 .id
= "PrivateDevices=",
687 .description_good
= "Service has no access to hardware devices",
688 .description_bad
= "Service potentially has access to hardware devices",
689 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateDevices=",
692 .assess
= assess_bool
,
693 .offset
= offsetof(struct security_info
, private_devices
),
696 .id
= "PrivateMounts=",
697 .description_good
= "Service cannot install system mounts",
698 .description_bad
= "Service may install system mounts",
699 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateMounts=",
702 .assess
= assess_bool
,
703 .offset
= offsetof(struct security_info
, private_mounts
),
706 .id
= "PrivateNetwork=",
707 .description_good
= "Service has no access to the host's network",
708 .description_bad
= "Service has access to the host's network",
709 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateNetwork=",
712 .assess
= assess_bool
,
713 .offset
= offsetof(struct security_info
, private_network
),
717 .description_good
= "Service has no access to other software's temporary files",
718 .description_bad
= "Service has access to other software's temporary files",
719 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateTmp=",
722 .assess
= assess_bool
,
723 .offset
= offsetof(struct security_info
, private_tmp
),
724 .default_dependencies_only
= true,
727 .id
= "PrivateUsers=",
728 .description_good
= "Service does not have access to other users",
729 .description_bad
= "Service has access to other users",
730 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateUsers=",
733 .assess
= assess_bool
,
734 .offset
= offsetof(struct security_info
, private_users
),
737 .id
= "ProtectControlGroups=",
738 .description_good
= "Service cannot modify the control group file system",
739 .description_bad
= "Service may modify to the control group file system",
740 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectControlGroups=",
743 .assess
= assess_bool
,
744 .offset
= offsetof(struct security_info
, protect_control_groups
),
747 .id
= "ProtectKernelModules=",
748 .description_good
= "Service cannot load or read kernel modules",
749 .description_bad
= "Service may load or read kernel modules",
750 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelModules=",
753 .assess
= assess_bool
,
754 .offset
= offsetof(struct security_info
, protect_kernel_modules
),
757 .id
= "ProtectKernelTunables=",
758 .description_good
= "Service cannot alter kernel tunables (/proc/sys, …)",
759 .description_bad
= "Service may alter kernel tunables",
760 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelTunables=",
763 .assess
= assess_bool
,
764 .offset
= offsetof(struct security_info
, protect_kernel_tunables
),
767 .id
= "ProtectHome=",
768 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHome=",
771 .assess
= assess_protect_home
,
772 .default_dependencies_only
= true,
775 .id
= "ProtectHostname=",
776 .description_good
= "Service cannot change system host/domainname",
777 .description_bad
= "Service may change system host/domainname",
778 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHostname=",
781 .assess
= assess_bool
,
782 .offset
= offsetof(struct security_info
, protect_hostname
),
785 .id
= "ProtectSystem=",
786 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectSystem=",
789 .assess
= assess_protect_system
,
790 .default_dependencies_only
= true,
793 .id
= "RootDirectory=/RootImage=",
794 .description_good
= "Service has its own root directory/image",
795 .description_bad
= "Service runs within the host's root directory",
796 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RootDirectory=",
799 .assess
= assess_root_directory
,
800 .default_dependencies_only
= true,
803 .id
= "LockPersonality=",
804 .description_good
= "Service cannot change ABI personality",
805 .description_bad
= "Service may change ABI personality",
806 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#LockPersonality=",
809 .assess
= assess_bool
,
810 .offset
= offsetof(struct security_info
, lock_personality
),
813 .id
= "MemoryDenyWriteExecute=",
814 .description_good
= "Service cannot create writable executable memory mappings",
815 .description_bad
= "Service may create writable executable memory mappings",
816 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#MemoryDenyWriteExecute=",
819 .assess
= assess_bool
,
820 .offset
= offsetof(struct security_info
, memory_deny_write_execute
),
823 .id
= "NoNewPrivileges=",
824 .description_good
= "Service processes cannot acquire new privileges",
825 .description_bad
= "Service processes may acquire new privileges",
826 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NoNewPrivileges=",
829 .assess
= assess_bool
,
830 .offset
= offsetof(struct security_info
, no_new_privileges
),
833 .id
= "CapabilityBoundingSet=~CAP_SYS_ADMIN",
834 .description_good
= "Service has no administrator privileges",
835 .description_bad
= "Service has administrator privileges",
836 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
839 .assess
= assess_capability_bounding_set
,
840 .parameter
= UINT64_C(1) << CAP_SYS_ADMIN
,
843 .id
= "CapabilityBoundingSet=~CAP_SET(UID|GID|PCAP)",
844 .description_good
= "Service cannot change UID/GID identities/capabilities",
845 .description_bad
= "Service may change UID/GID identities/capabilities",
846 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
849 .assess
= assess_capability_bounding_set
,
850 .parameter
= (UINT64_C(1) << CAP_SETUID
)|
851 (UINT64_C(1) << CAP_SETGID
)|
852 (UINT64_C(1) << CAP_SETPCAP
),
855 .id
= "CapabilityBoundingSet=~CAP_SYS_PTRACE",
856 .description_good
= "Service has no ptrace() debugging abilities",
857 .description_bad
= "Service has ptrace() debugging abilities",
858 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
861 .assess
= assess_capability_bounding_set
,
862 .parameter
= (UINT64_C(1) << CAP_SYS_PTRACE
),
865 .id
= "CapabilityBoundingSet=~CAP_SYS_TIME",
866 .description_good
= "Service processes cannot change the system clock",
867 .description_bad
= "Service processes may change the system clock",
868 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
871 .assess
= assess_capability_bounding_set
,
872 .parameter
= UINT64_C(1) << CAP_SYS_TIME
,
875 .id
= "CapabilityBoundingSet=~CAP_NET_ADMIN",
876 .description_good
= "Service has no network configuration privileges",
877 .description_bad
= "Service has network configuration privileges",
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_NET_ADMIN
),
885 .id
= "CapabilityBoundingSet=~CAP_RAWIO",
886 .description_good
= "Service has no raw I/O access",
887 .description_bad
= "Service has raw I/O access",
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_RAWIO
),
895 .id
= "CapabilityBoundingSet=~CAP_SYS_MODULE",
896 .description_good
= "Service cannot load kernel modules",
897 .description_bad
= "Service may load kernel modules",
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_SYS_MODULE
),
905 .id
= "CapabilityBoundingSet=~CAP_AUDIT_*",
906 .description_good
= "Service has no audit subsystem access",
907 .description_bad
= "Service has audit subsystem 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_AUDIT_CONTROL
) |
913 (UINT64_C(1) << CAP_AUDIT_READ
) |
914 (UINT64_C(1) << CAP_AUDIT_WRITE
),
917 .id
= "CapabilityBoundingSet=~CAP_SYSLOG",
918 .description_good
= "Service has no access to kernel logging",
919 .description_bad
= "Service has access to kernel logging",
920 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
923 .assess
= assess_capability_bounding_set
,
924 .parameter
= (UINT64_C(1) << CAP_SYSLOG
),
927 .id
= "CapabilityBoundingSet=~CAP_SYS_(NICE|RESOURCE)",
928 .description_good
= "Service has no privileges to change resource use parameters",
929 .description_bad
= "Service has privileges to change resource use parameters",
930 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
933 .assess
= assess_capability_bounding_set
,
934 .parameter
= (UINT64_C(1) << CAP_SYS_NICE
) |
935 (UINT64_C(1) << CAP_SYS_RESOURCE
),
938 .id
= "CapabilityBoundingSet=~CAP_MKNOD",
939 .description_good
= "Service cannot create device nodes",
940 .description_bad
= "Service may create device nodes",
941 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
944 .assess
= assess_capability_bounding_set
,
945 .parameter
= (UINT64_C(1) << CAP_MKNOD
),
948 .id
= "CapabilityBoundingSet=~CAP_(CHOWN|FSETID|SETFCAP)",
949 .description_good
= "Service cannot change file ownership/access mode/capabilities",
950 .description_bad
= "Service may change file ownership/access mode/capabilities unrestricted",
951 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
954 .assess
= assess_capability_bounding_set
,
955 .parameter
= (UINT64_C(1) << CAP_CHOWN
) |
956 (UINT64_C(1) << CAP_FSETID
) |
957 (UINT64_C(1) << CAP_SETFCAP
),
960 .id
= "CapabilityBoundingSet=~CAP_(DAC_*|FOWNER|IPC_OWNER)",
961 .description_good
= "Service cannot override UNIX file/IPC permission checks",
962 .description_bad
= "Service may override UNIX file/IPC permission checks",
963 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
966 .assess
= assess_capability_bounding_set
,
967 .parameter
= (UINT64_C(1) << CAP_DAC_OVERRIDE
) |
968 (UINT64_C(1) << CAP_DAC_READ_SEARCH
) |
969 (UINT64_C(1) << CAP_FOWNER
) |
970 (UINT64_C(1) << CAP_IPC_OWNER
),
973 .id
= "CapabilityBoundingSet=~CAP_KILL",
974 .description_good
= "Service cannot send UNIX signals to arbitrary processes",
975 .description_bad
= "Service may send UNIX signals to arbitrary processes",
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_KILL
),
983 .id
= "CapabilityBoundingSet=~CAP_NET_(BIND_SERVICE|BROADCAST|RAW)",
984 .description_good
= "Service has no elevated networking privileges",
985 .description_bad
= "Service has elevated networking 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_BIND_SERVICE
) |
991 (UINT64_C(1) << CAP_NET_BROADCAST
) |
992 (UINT64_C(1) << CAP_NET_RAW
),
995 .id
= "CapabilityBoundingSet=~CAP_SYS_BOOT",
996 .description_good
= "Service cannot issue reboot()",
997 .description_bad
= "Service may issue reboot()",
998 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1001 .assess
= assess_capability_bounding_set
,
1002 .parameter
= (UINT64_C(1) << CAP_SYS_BOOT
),
1005 .id
= "CapabilityBoundingSet=~CAP_MAC_*",
1006 .description_good
= "Service cannot adjust SMACK MAC",
1007 .description_bad
= "Service may adjust SMACK MAC",
1008 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1011 .assess
= assess_capability_bounding_set
,
1012 .parameter
= (UINT64_C(1) << CAP_MAC_ADMIN
)|
1013 (UINT64_C(1) << CAP_MAC_OVERRIDE
),
1016 .id
= "CapabilityBoundingSet=~CAP_LINUX_IMMUTABLE",
1017 .description_good
= "Service cannot mark files immutable",
1018 .description_bad
= "Service may mark files immutable",
1019 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1022 .assess
= assess_capability_bounding_set
,
1023 .parameter
= (UINT64_C(1) << CAP_LINUX_IMMUTABLE
),
1026 .id
= "CapabilityBoundingSet=~CAP_IPC_LOCK",
1027 .description_good
= "Service cannot lock memory into RAM",
1028 .description_bad
= "Service may lock memory into RAM",
1029 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1032 .assess
= assess_capability_bounding_set
,
1033 .parameter
= (UINT64_C(1) << CAP_IPC_LOCK
),
1036 .id
= "CapabilityBoundingSet=~CAP_SYS_CHROOT",
1037 .description_good
= "Service cannot issue chroot()",
1038 .description_bad
= "Service may issue chroot()",
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_SYS_CHROOT
),
1046 .id
= "CapabilityBoundingSet=~CAP_BLOCK_SUSPEND",
1047 .description_good
= "Service cannot establish wake locks",
1048 .description_bad
= "Service may establish wake locks",
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_BLOCK_SUSPEND
),
1056 .id
= "CapabilityBoundingSet=~CAP_WAKE_ALARM",
1057 .description_good
= "Service cannot program timers that wake up the system",
1058 .description_bad
= "Service may program timers that wake up the system",
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_WAKE_ALARM
),
1066 .id
= "CapabilityBoundingSet=~CAP_LEASE",
1067 .description_good
= "Service cannot create file leases",
1068 .description_bad
= "Service may create file leases",
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_LEASE
),
1076 .id
= "CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG",
1077 .description_good
= "Service cannot issue vhangup()",
1078 .description_bad
= "Service may issue vhangup()",
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_SYS_TTY_CONFIG
),
1086 .id
= "CapabilityBoundingSet=~CAP_SYS_PACCT",
1087 .description_good
= "Service cannot use acct()",
1088 .description_bad
= "Service may use acct()",
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_SYS_PACCT
),
1097 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#UMask=",
1100 .assess
= assess_umask
,
1103 .id
= "KeyringMode=",
1104 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#KeyringMode=",
1105 .description_good
= "Service doesn't share key material with other services",
1106 .description_bad
= "Service shares key material with other service",
1109 .assess
= assess_keyring_mode
,
1112 .id
= "NotifyAccess=",
1113 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NotifyAccess=",
1114 .description_good
= "Service child processes cannot alter service state",
1115 .description_bad
= "Service child processes may alter service state",
1118 .assess
= assess_notify_access
,
1122 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RemoveIPC=",
1123 .description_good
= "Service user cannot leave SysV IPC objects around",
1124 .description_bad
= "Service user may leave SysV IPC objects around",
1125 .description_na
= "Service runs as root, option does not apply",
1128 .assess
= assess_remove_ipc
,
1129 .offset
= offsetof(struct security_info
, remove_ipc
),
1133 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Delegate=",
1134 .description_good
= "Service does not maintain its own delegated control group subtree",
1135 .description_bad
= "Service maintains its own delegated control group subtree",
1138 .assess
= assess_bool
,
1139 .offset
= offsetof(struct security_info
, delegate
),
1140 .parameter
= true, /* invert! */
1143 .id
= "RestrictRealtime=",
1144 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictRealtime=",
1145 .description_good
= "Service realtime scheduling access is restricted",
1146 .description_bad
= "Service may acquire realtime scheduling",
1149 .assess
= assess_bool
,
1150 .offset
= offsetof(struct security_info
, restrict_realtime
),
1153 .id
= "RestrictSUIDSGID=",
1154 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictSUIDSGID=",
1155 .description_good
= "SUID/SGID file creation by service is restricted",
1156 .description_bad
= "Service may create SUID/SGID files",
1159 .assess
= assess_bool
,
1160 .offset
= offsetof(struct security_info
, restrict_suid_sgid
),
1163 .id
= "RestrictNamespaces=~CLONE_NEWUSER",
1164 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1165 .description_good
= "Service cannot create user namespaces",
1166 .description_bad
= "Service may create user namespaces",
1169 .assess
= assess_restrict_namespaces
,
1170 .parameter
= CLONE_NEWUSER
,
1173 .id
= "RestrictNamespaces=~CLONE_NEWNS",
1174 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1175 .description_good
= "Service cannot create file system namespaces",
1176 .description_bad
= "Service may create file system namespaces",
1179 .assess
= assess_restrict_namespaces
,
1180 .parameter
= CLONE_NEWNS
,
1183 .id
= "RestrictNamespaces=~CLONE_NEWIPC",
1184 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1185 .description_good
= "Service cannot create IPC namespaces",
1186 .description_bad
= "Service may create IPC namespaces",
1189 .assess
= assess_restrict_namespaces
,
1190 .parameter
= CLONE_NEWIPC
,
1193 .id
= "RestrictNamespaces=~CLONE_NEWPID",
1194 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1195 .description_good
= "Service cannot create process namespaces",
1196 .description_bad
= "Service may create process namespaces",
1199 .assess
= assess_restrict_namespaces
,
1200 .parameter
= CLONE_NEWPID
,
1203 .id
= "RestrictNamespaces=~CLONE_NEWCGROUP",
1204 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1205 .description_good
= "Service cannot create cgroup namespaces",
1206 .description_bad
= "Service may create cgroup namespaces",
1209 .assess
= assess_restrict_namespaces
,
1210 .parameter
= CLONE_NEWCGROUP
,
1213 .id
= "RestrictNamespaces=~CLONE_NEWNET",
1214 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1215 .description_good
= "Service cannot create network namespaces",
1216 .description_bad
= "Service may create network namespaces",
1219 .assess
= assess_restrict_namespaces
,
1220 .parameter
= CLONE_NEWNET
,
1223 .id
= "RestrictNamespaces=~CLONE_NEWUTS",
1224 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1225 .description_good
= "Service cannot create hostname namespaces",
1226 .description_bad
= "Service may create hostname namespaces",
1229 .assess
= assess_restrict_namespaces
,
1230 .parameter
= CLONE_NEWUTS
,
1233 .id
= "RestrictAddressFamilies=~AF_(INET|INET6)",
1234 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1235 .description_good
= "Service cannot allocate Internet sockets",
1236 .description_bad
= "Service may allocate Internet sockets",
1239 .assess
= assess_bool
,
1240 .offset
= offsetof(struct security_info
, restrict_address_family_inet
),
1243 .id
= "RestrictAddressFamilies=~AF_UNIX",
1244 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1245 .description_good
= "Service cannot allocate local sockets",
1246 .description_bad
= "Service may allocate local sockets",
1249 .assess
= assess_bool
,
1250 .offset
= offsetof(struct security_info
, restrict_address_family_unix
),
1253 .id
= "RestrictAddressFamilies=~AF_NETLINK",
1254 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1255 .description_good
= "Service cannot allocate netlink sockets",
1256 .description_bad
= "Service may allocate netlink sockets",
1259 .assess
= assess_bool
,
1260 .offset
= offsetof(struct security_info
, restrict_address_family_netlink
),
1263 .id
= "RestrictAddressFamilies=~AF_PACKET",
1264 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1265 .description_good
= "Service cannot allocate packet sockets",
1266 .description_bad
= "Service may allocate packet sockets",
1269 .assess
= assess_bool
,
1270 .offset
= offsetof(struct security_info
, restrict_address_family_packet
),
1273 .id
= "RestrictAddressFamilies=~…",
1274 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1275 .description_good
= "Service cannot allocate exotic sockets",
1276 .description_bad
= "Service may allocate exotic sockets",
1279 .assess
= assess_bool
,
1280 .offset
= offsetof(struct security_info
, restrict_address_family_other
),
1283 .id
= "SystemCallArchitectures=",
1284 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallArchitectures=",
1287 .assess
= assess_system_call_architectures
,
1291 .id
= "SystemCallFilter=~@swap",
1292 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1295 .assess
= assess_system_call_filter
,
1296 .parameter
= SYSCALL_FILTER_SET_SWAP
,
1299 .id
= "SystemCallFilter=~@obsolete",
1300 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1303 .assess
= assess_system_call_filter
,
1304 .parameter
= SYSCALL_FILTER_SET_OBSOLETE
,
1307 .id
= "SystemCallFilter=~@clock",
1308 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1311 .assess
= assess_system_call_filter
,
1312 .parameter
= SYSCALL_FILTER_SET_CLOCK
,
1315 .id
= "SystemCallFilter=~@cpu-emulation",
1316 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1319 .assess
= assess_system_call_filter
,
1320 .parameter
= SYSCALL_FILTER_SET_CPU_EMULATION
,
1323 .id
= "SystemCallFilter=~@debug",
1324 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1327 .assess
= assess_system_call_filter
,
1328 .parameter
= SYSCALL_FILTER_SET_DEBUG
,
1331 .id
= "SystemCallFilter=~@mount",
1332 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1335 .assess
= assess_system_call_filter
,
1336 .parameter
= SYSCALL_FILTER_SET_MOUNT
,
1339 .id
= "SystemCallFilter=~@module",
1340 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1343 .assess
= assess_system_call_filter
,
1344 .parameter
= SYSCALL_FILTER_SET_MODULE
,
1347 .id
= "SystemCallFilter=~@raw-io",
1348 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1351 .assess
= assess_system_call_filter
,
1352 .parameter
= SYSCALL_FILTER_SET_RAW_IO
,
1355 .id
= "SystemCallFilter=~@reboot",
1356 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1359 .assess
= assess_system_call_filter
,
1360 .parameter
= SYSCALL_FILTER_SET_REBOOT
,
1363 .id
= "SystemCallFilter=~@privileged",
1364 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1367 .assess
= assess_system_call_filter
,
1368 .parameter
= SYSCALL_FILTER_SET_PRIVILEGED
,
1371 .id
= "SystemCallFilter=~@resources",
1372 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1375 .assess
= assess_system_call_filter
,
1376 .parameter
= SYSCALL_FILTER_SET_RESOURCES
,
1380 .id
= "IPAddressDeny=",
1381 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#IPAddressDeny=",
1384 .assess
= assess_ip_address_allow
,
1387 .id
= "DeviceAllow=",
1388 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#DeviceAllow=",
1391 .assess
= assess_device_allow
,
1394 .id
= "AmbientCapabilities=",
1395 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#AmbientCapabilities=",
1396 .description_good
= "Service process does not receive ambient capabilities",
1397 .description_bad
= "Service process receives ambient capabilities",
1400 .assess
= assess_ambient_capabilities
,
1404 static int assess(const struct security_info
*info
, Table
*overview_table
, AnalyzeSecurityFlags flags
) {
1405 static const struct {
1409 SpecialGlyph smiley
;
1410 } badness_table
[] = {
1411 { 100, "DANGEROUS", ANSI_HIGHLIGHT_RED
, SPECIAL_GLYPH_DEPRESSED_SMILEY
},
1412 { 90, "UNSAFE", ANSI_HIGHLIGHT_RED
, SPECIAL_GLYPH_UNHAPPY_SMILEY
},
1413 { 75, "EXPOSED", ANSI_HIGHLIGHT_YELLOW
, SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY
},
1414 { 50, "MEDIUM", NULL
, SPECIAL_GLYPH_NEUTRAL_SMILEY
},
1415 { 10, "OK", ANSI_HIGHLIGHT_GREEN
, SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY
},
1416 { 1, "SAFE", ANSI_HIGHLIGHT_GREEN
, SPECIAL_GLYPH_HAPPY_SMILEY
},
1417 { 0, "PERFECT", ANSI_HIGHLIGHT_GREEN
, SPECIAL_GLYPH_ECSTATIC_SMILEY
},
1420 uint64_t badness_sum
= 0, weight_sum
= 0, exposure
;
1421 _cleanup_(table_unrefp
) Table
*details_table
= NULL
;
1425 if (!FLAGS_SET(flags
, ANALYZE_SECURITY_SHORT
)) {
1426 details_table
= table_new(" ", "name", "description", "weight", "badness", "range", "exposure");
1430 (void) table_set_sort(details_table
, 3, 1, (size_t) -1);
1431 (void) table_set_reverse(details_table
, 3, true);
1433 if (getenv_bool("SYSTEMD_ANALYZE_DEBUG") <= 0)
1434 (void) table_set_display(details_table
, 0, 1, 2, 6, (size_t) -1);
1437 for (i
= 0; i
< ELEMENTSOF(security_assessor_table
); i
++) {
1438 const struct security_assessor
*a
= security_assessor_table
+ i
;
1439 _cleanup_free_
char *d
= NULL
;
1443 data
= (uint8_t*) info
+ a
->offset
;
1445 if (a
->default_dependencies_only
&& !info
->default_dependencies
) {
1446 badness
= UINT64_MAX
;
1447 d
= strdup("Service runs in special boot phase, option does not apply");
1451 r
= a
->assess(a
, info
, data
, &badness
, &d
);
1456 assert(a
->range
> 0);
1458 if (badness
!= UINT64_MAX
) {
1459 assert(badness
<= a
->range
);
1461 badness_sum
+= DIV_ROUND_UP(badness
* a
->weight
, a
->range
);
1462 weight_sum
+= a
->weight
;
1465 if (details_table
) {
1466 const char *checkmark
, *description
, *color
= NULL
;
1469 if (badness
== UINT64_MAX
) {
1471 description
= a
->description_na
;
1473 } else if (badness
== a
->range
) {
1474 checkmark
= special_glyph(SPECIAL_GLYPH_CROSS_MARK
);
1475 description
= a
->description_bad
;
1476 color
= ansi_highlight_red();
1477 } else if (badness
== 0) {
1478 checkmark
= special_glyph(SPECIAL_GLYPH_CHECK_MARK
);
1479 description
= a
->description_good
;
1480 color
= ansi_highlight_green();
1482 checkmark
= special_glyph(SPECIAL_GLYPH_CROSS_MARK
);
1484 color
= ansi_highlight_red();
1490 r
= table_add_cell_full(details_table
, &cell
, TABLE_STRING
, checkmark
, 1, 1, 0, 0, 0);
1492 return log_error_errno(r
, "Failed to add cell to table: %m");
1494 (void) table_set_color(details_table
, cell
, color
);
1496 r
= table_add_cell(details_table
, &cell
, TABLE_STRING
, a
->id
);
1498 return log_error_errno(r
, "Failed to add cell to table: %m");
1500 (void) table_set_url(details_table
, cell
, a
->url
);
1502 r
= table_add_cell(details_table
, NULL
, TABLE_STRING
, description
);
1504 return log_error_errno(r
, "Failed to add cell to table: %m");
1506 r
= table_add_cell(details_table
, &cell
, TABLE_UINT64
, &a
->weight
);
1508 return log_error_errno(r
, "Failed to add cell to table: %m");
1509 (void) table_set_align_percent(details_table
, cell
, 100);
1511 r
= table_add_cell(details_table
, &cell
, TABLE_UINT64
, &badness
);
1513 return log_error_errno(r
, "Failed to add cell to table: %m");
1514 (void) table_set_align_percent(details_table
, cell
, 100);
1516 r
= table_add_cell(details_table
, &cell
, TABLE_UINT64
, &a
->range
);
1518 return log_error_errno(r
, "Failed to add cell to table: %m");
1519 (void) table_set_align_percent(details_table
, cell
, 100);
1521 r
= table_add_cell(details_table
, &cell
, TABLE_EMPTY
, NULL
);
1523 return log_error_errno(r
, "Failed to add cell to table: %m");
1524 (void) table_set_align_percent(details_table
, cell
, 100);
1528 if (details_table
) {
1531 for (row
= 1; row
< table_get_rows(details_table
); row
++) {
1532 char buf
[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1];
1533 const uint64_t *weight
, *badness
, *range
;
1537 assert_se(weight
= table_get_at(details_table
, row
, 3));
1538 assert_se(badness
= table_get_at(details_table
, row
, 4));
1539 assert_se(range
= table_get_at(details_table
, row
, 5));
1541 if (*badness
== UINT64_MAX
|| *badness
== 0)
1544 assert_se(cell
= table_get_cell(details_table
, row
, 6));
1546 x
= DIV_ROUND_UP(DIV_ROUND_UP(*badness
* *weight
* 100U, *range
), weight_sum
);
1547 xsprintf(buf
, "%" PRIu64
".%" PRIu64
, x
/ 10, x
% 10);
1549 r
= table_update(details_table
, cell
, TABLE_STRING
, buf
);
1551 return log_error_errno(r
, "Failed to update cell in table: %m");
1554 r
= table_print(details_table
, stdout
);
1556 return log_error_errno(r
, "Failed to output table: %m");
1559 assert(weight_sum
> 0);
1560 exposure
= DIV_ROUND_UP(badness_sum
* 100U, weight_sum
);
1562 for (i
= 0; i
< ELEMENTSOF(badness_table
); i
++)
1563 if (exposure
>= badness_table
[i
].exposure
)
1566 assert(i
< ELEMENTSOF(badness_table
));
1568 if (details_table
) {
1569 _cleanup_free_
char *clickable
= NULL
;
1572 /* If we shall output the details table, also print the brief summary underneath */
1574 if (info
->fragment_path
) {
1575 r
= terminal_urlify_path(info
->fragment_path
, info
->id
, &clickable
);
1583 printf("\n%s %sOverall exposure level for %s%s: %s%" PRIu64
".%" PRIu64
" %s%s %s\n",
1584 special_glyph(SPECIAL_GLYPH_ARROW
),
1588 colors_enabled() ? strempty(badness_table
[i
].color
) : "",
1589 exposure
/ 10, exposure
% 10,
1590 badness_table
[i
].name
,
1592 special_glyph(badness_table
[i
].smiley
));
1597 if (overview_table
) {
1598 char buf
[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1];
1601 r
= table_add_cell(overview_table
, &cell
, TABLE_STRING
, info
->id
);
1603 return log_error_errno(r
, "Failed to add cell to table: %m");
1604 if (info
->fragment_path
) {
1605 _cleanup_free_
char *url
= NULL
;
1607 r
= file_url_from_path(info
->fragment_path
, &url
);
1609 return log_error_errno(r
, "Failed to generate URL from path: %m");
1611 (void) table_set_url(overview_table
, cell
, url
);
1614 xsprintf(buf
, "%" PRIu64
".%" PRIu64
, exposure
/ 10, exposure
% 10);
1615 r
= table_add_cell(overview_table
, &cell
, TABLE_STRING
, buf
);
1617 return log_error_errno(r
, "Failed to add cell to table: %m");
1618 (void) table_set_align_percent(overview_table
, cell
, 100);
1620 r
= table_add_cell(overview_table
, &cell
, TABLE_STRING
, badness_table
[i
].name
);
1622 return log_error_errno(r
, "Failed to add cell to table: %m");
1623 (void) table_set_color(overview_table
, cell
, strempty(badness_table
[i
].color
));
1625 r
= table_add_cell(overview_table
, NULL
, TABLE_STRING
, special_glyph(badness_table
[i
].smiley
));
1627 return log_error_errno(r
, "Failed to add cell to table: %m");
1633 static int property_read_restrict_address_families(
1637 sd_bus_error
*error
,
1640 struct security_info
*info
= userdata
;
1647 r
= sd_bus_message_enter_container(m
, 'r', "bas");
1651 r
= sd_bus_message_read(m
, "b", &whitelist
);
1655 info
->restrict_address_family_inet
=
1656 info
->restrict_address_family_unix
=
1657 info
->restrict_address_family_netlink
=
1658 info
->restrict_address_family_packet
=
1659 info
->restrict_address_family_other
= whitelist
;
1661 r
= sd_bus_message_enter_container(m
, 'a', "s");
1668 r
= sd_bus_message_read(m
, "s", &name
);
1674 if (STR_IN_SET(name
, "AF_INET", "AF_INET6"))
1675 info
->restrict_address_family_inet
= !whitelist
;
1676 else if (streq(name
, "AF_UNIX"))
1677 info
->restrict_address_family_unix
= !whitelist
;
1678 else if (streq(name
, "AF_NETLINK"))
1679 info
->restrict_address_family_netlink
= !whitelist
;
1680 else if (streq(name
, "AF_PACKET"))
1681 info
->restrict_address_family_packet
= !whitelist
;
1683 info
->restrict_address_family_other
= !whitelist
;
1686 r
= sd_bus_message_exit_container(m
);
1690 return sd_bus_message_exit_container(m
);
1693 static int property_read_system_call_filter(
1697 sd_bus_error
*error
,
1700 struct security_info
*info
= userdata
;
1707 r
= sd_bus_message_enter_container(m
, 'r', "bas");
1711 r
= sd_bus_message_read(m
, "b", &whitelist
);
1715 info
->system_call_filter_whitelist
= whitelist
;
1717 r
= sd_bus_message_enter_container(m
, 'a', "s");
1724 r
= sd_bus_message_read(m
, "s", &name
);
1730 r
= set_ensure_allocated(&info
->system_call_filter
, &string_hash_ops
);
1734 r
= set_put_strdup(info
->system_call_filter
, name
);
1739 r
= sd_bus_message_exit_container(m
);
1743 return sd_bus_message_exit_container(m
);
1746 static int property_read_ip_address_allow(
1750 sd_bus_error
*error
,
1753 struct security_info
*info
= userdata
;
1754 bool deny_ipv4
= false, deny_ipv6
= false;
1761 r
= sd_bus_message_enter_container(m
, 'a', "(iayu)");
1771 r
= sd_bus_message_enter_container(m
, 'r', "iayu");
1777 r
= sd_bus_message_read(m
, "i", &family
);
1781 r
= sd_bus_message_read_array(m
, 'y', &data
, &size
);
1785 r
= sd_bus_message_read(m
, "u", &prefixlen
);
1789 r
= sd_bus_message_exit_container(m
);
1793 if (streq(member
, "IPAddressAllow")) {
1794 union in_addr_union u
;
1796 if (family
== AF_INET
&& size
== 4 && prefixlen
== 8)
1797 memcpy(&u
.in
, data
, size
);
1798 else if (family
== AF_INET6
&& size
== 16 && prefixlen
== 128)
1799 memcpy(&u
.in6
, data
, size
);
1801 info
->ip_address_allow_other
= true;
1805 if (in_addr_is_localhost(family
, &u
))
1806 info
->ip_address_allow_localhost
= true;
1808 info
->ip_address_allow_other
= true;
1810 assert(streq(member
, "IPAddressDeny"));
1812 if (family
== AF_INET
&& size
== 4 && prefixlen
== 0)
1814 else if (family
== AF_INET6
&& size
== 16 && prefixlen
== 0)
1819 info
->ip_address_deny_all
= deny_ipv4
&& deny_ipv6
;
1821 return sd_bus_message_exit_container(m
);
1824 static int property_read_device_allow(
1828 sd_bus_error
*error
,
1831 struct security_info
*info
= userdata
;
1839 r
= sd_bus_message_enter_container(m
, 'a', "(ss)");
1844 const char *name
, *policy
;
1846 r
= sd_bus_message_read(m
, "(ss)", &name
, &policy
);
1855 info
->device_allow_non_empty
= n
> 0;
1857 return sd_bus_message_exit_container(m
);
1860 static int acquire_security_info(sd_bus
*bus
, const char *name
, struct security_info
*info
, AnalyzeSecurityFlags flags
) {
1862 static const struct bus_properties_map security_map
[] = {
1863 { "AmbientCapabilities", "t", NULL
, offsetof(struct security_info
, ambient_capabilities
) },
1864 { "CapabilityBoundingSet", "t", NULL
, offsetof(struct security_info
, capability_bounding_set
) },
1865 { "DefaultDependencies", "b", NULL
, offsetof(struct security_info
, default_dependencies
) },
1866 { "Delegate", "b", NULL
, offsetof(struct security_info
, delegate
) },
1867 { "DeviceAllow", "a(ss)", property_read_device_allow
, 0 },
1868 { "DevicePolicy", "s", NULL
, offsetof(struct security_info
, device_policy
) },
1869 { "DynamicUser", "b", NULL
, offsetof(struct security_info
, dynamic_user
) },
1870 { "FragmentPath", "s", NULL
, offsetof(struct security_info
, fragment_path
) },
1871 { "IPAddressAllow", "a(iayu)", property_read_ip_address_allow
, 0 },
1872 { "IPAddressDeny", "a(iayu)", property_read_ip_address_allow
, 0 },
1873 { "Id", "s", NULL
, offsetof(struct security_info
, id
) },
1874 { "KeyringMode", "s", NULL
, offsetof(struct security_info
, keyring_mode
) },
1875 { "LoadState", "s", NULL
, offsetof(struct security_info
, load_state
) },
1876 { "LockPersonality", "b", NULL
, offsetof(struct security_info
, lock_personality
) },
1877 { "MemoryDenyWriteExecute", "b", NULL
, offsetof(struct security_info
, memory_deny_write_execute
) },
1878 { "NoNewPrivileges", "b", NULL
, offsetof(struct security_info
, no_new_privileges
) },
1879 { "NotifyAccess", "s", NULL
, offsetof(struct security_info
, notify_access
) },
1880 { "PrivateDevices", "b", NULL
, offsetof(struct security_info
, private_devices
) },
1881 { "PrivateMounts", "b", NULL
, offsetof(struct security_info
, private_mounts
) },
1882 { "PrivateNetwork", "b", NULL
, offsetof(struct security_info
, private_network
) },
1883 { "PrivateTmp", "b", NULL
, offsetof(struct security_info
, private_tmp
) },
1884 { "PrivateUsers", "b", NULL
, offsetof(struct security_info
, private_users
) },
1885 { "ProtectControlGroups", "b", NULL
, offsetof(struct security_info
, protect_control_groups
) },
1886 { "ProtectHome", "s", NULL
, offsetof(struct security_info
, protect_home
) },
1887 { "ProtectHostname", "b", NULL
, offsetof(struct security_info
, protect_hostname
) },
1888 { "ProtectKernelModules", "b", NULL
, offsetof(struct security_info
, protect_kernel_modules
) },
1889 { "ProtectKernelTunables", "b", NULL
, offsetof(struct security_info
, protect_kernel_tunables
) },
1890 { "ProtectSystem", "s", NULL
, offsetof(struct security_info
, protect_system
) },
1891 { "RemoveIPC", "b", NULL
, offsetof(struct security_info
, remove_ipc
) },
1892 { "RestrictAddressFamilies", "(bas)", property_read_restrict_address_families
, 0 },
1893 { "RestrictNamespaces", "t", NULL
, offsetof(struct security_info
, restrict_namespaces
) },
1894 { "RestrictRealtime", "b", NULL
, offsetof(struct security_info
, restrict_realtime
) },
1895 { "RestrictSUIDSGID", "b", NULL
, offsetof(struct security_info
, restrict_suid_sgid
) },
1896 { "RootDirectory", "s", NULL
, offsetof(struct security_info
, root_directory
) },
1897 { "RootImage", "s", NULL
, offsetof(struct security_info
, root_image
) },
1898 { "SupplementaryGroups", "as", NULL
, offsetof(struct security_info
, supplementary_groups
) },
1899 { "SystemCallArchitectures", "as", NULL
, offsetof(struct security_info
, system_call_architectures
) },
1900 { "SystemCallFilter", "(as)", property_read_system_call_filter
, 0 },
1901 { "Type", "s", NULL
, offsetof(struct security_info
, type
) },
1902 { "UMask", "u", NULL
, offsetof(struct security_info
, _umask
) },
1903 { "User", "s", NULL
, offsetof(struct security_info
, user
) },
1907 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1908 _cleanup_free_
char *path
= NULL
;
1911 /* Note: this mangles *info on failure! */
1917 path
= unit_dbus_path_from_name(name
);
1921 r
= bus_map_all_properties(bus
,
1922 "org.freedesktop.systemd1",
1925 BUS_MAP_STRDUP
|BUS_MAP_BOOLEAN_AS_BOOL
,
1930 return log_error_errno(r
, "Failed to get unit properties: %s", bus_error_message(&error
, r
));
1932 if (!streq_ptr(info
->load_state
, "loaded")) {
1934 if (FLAGS_SET(flags
, ANALYZE_SECURITY_ONLY_LOADED
))
1935 return -EMEDIUMTYPE
;
1937 if (streq_ptr(info
->load_state
, "not-found"))
1938 log_error("Unit %s not found, cannot analyze.", name
);
1939 else if (streq_ptr(info
->load_state
, "masked"))
1940 log_error("Unit %s is masked, cannot analyze.", name
);
1942 log_error("Unit %s not loaded properly, cannot analyze.", name
);
1947 if (FLAGS_SET(flags
, ANALYZE_SECURITY_ONLY_LONG_RUNNING
) && streq_ptr(info
->type
, "oneshot"))
1948 return -EMEDIUMTYPE
;
1950 if (info
->private_devices
||
1951 info
->private_tmp
||
1952 info
->protect_control_groups
||
1953 info
->protect_kernel_tunables
||
1954 info
->protect_kernel_modules
||
1955 !streq_ptr(info
->protect_home
, "no") ||
1956 !streq_ptr(info
->protect_system
, "no") ||
1958 info
->private_mounts
= true;
1960 if (info
->protect_kernel_modules
)
1961 info
->capability_bounding_set
&= ~(UINT64_C(1) << CAP_SYS_MODULE
);
1963 if (info
->private_devices
)
1964 info
->capability_bounding_set
&= ~((UINT64_C(1) << CAP_MKNOD
) |
1965 (UINT64_C(1) << CAP_SYS_RAWIO
));
1970 static int analyze_security_one(sd_bus
*bus
, const char *name
, Table
* overview_table
, AnalyzeSecurityFlags flags
) {
1971 _cleanup_(security_info_free
) struct security_info info
= {
1972 .default_dependencies
= true,
1973 .capability_bounding_set
= UINT64_MAX
,
1974 .restrict_namespaces
= UINT64_MAX
,
1982 r
= acquire_security_info(bus
, name
, &info
, flags
);
1983 if (r
== -EMEDIUMTYPE
) /* Ignore this one because not loaded or Type is oneshot */
1988 r
= assess(&info
, overview_table
, flags
);
1995 int analyze_security(sd_bus
*bus
, char **units
, AnalyzeSecurityFlags flags
) {
1996 _cleanup_(table_unrefp
) Table
*overview_table
= NULL
;
2001 if (strv_length(units
) != 1) {
2002 overview_table
= table_new("unit", "exposure", "predicate", "happy");
2003 if (!overview_table
)
2007 if (strv_isempty(units
)) {
2008 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2009 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2010 _cleanup_strv_free_
char **list
= NULL
;
2011 size_t allocated
= 0, n
= 0;
2014 r
= sd_bus_call_method(
2016 "org.freedesktop.systemd1",
2017 "/org/freedesktop/systemd1",
2018 "org.freedesktop.systemd1.Manager",
2023 return log_error_errno(r
, "Failed to list units: %s", bus_error_message(&error
, r
));
2025 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
2027 return bus_log_parse_error(r
);
2033 r
= bus_parse_unit_info(reply
, &info
);
2035 return bus_log_parse_error(r
);
2039 if (!endswith(info
.id
, ".service"))
2042 if (!GREEDY_REALLOC(list
, allocated
, n
+2))
2045 copy
= strdup(info
.id
);
2055 flags
|= ANALYZE_SECURITY_SHORT
|ANALYZE_SECURITY_ONLY_LOADED
|ANALYZE_SECURITY_ONLY_LONG_RUNNING
;
2057 STRV_FOREACH(i
, list
) {
2058 r
= analyze_security_one(bus
, *i
, overview_table
, flags
);
2059 if (r
< 0 && ret
>= 0)
2066 STRV_FOREACH(i
, units
) {
2067 _cleanup_free_
char *mangled
= NULL
, *instance
= NULL
;
2070 if (!FLAGS_SET(flags
, ANALYZE_SECURITY_SHORT
) && i
!= units
) {
2075 r
= unit_name_mangle_with_suffix(*i
, 0, ".service", &mangled
);
2077 return log_error_errno(r
, "Failed to mangle unit name '%s': %m", *i
);
2079 if (!endswith(mangled
, ".service")) {
2080 log_error("Unit %s is not a service unit, refusing.", *i
);
2084 if (unit_name_is_valid(mangled
, UNIT_NAME_TEMPLATE
)) {
2085 r
= unit_name_replace_instance(mangled
, "test-instance", &instance
);
2093 r
= analyze_security_one(bus
, name
, overview_table
, flags
);
2094 if (r
< 0 && ret
>= 0)
2099 if (overview_table
) {
2100 if (!FLAGS_SET(flags
, ANALYZE_SECURITY_SHORT
)) {
2105 r
= table_print(overview_table
, stdout
);
2107 return log_error_errno(r
, "Failed to output table: %m");