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"
15 #include "parse-util.h"
16 #include "path-util.h"
17 #include "pretty-print.h"
18 #include "seccomp-util.h"
20 #include "stdio-util.h"
22 #include "terminal-util.h"
24 #include "unit-name.h"
26 struct security_info
{
31 bool default_dependencies
;
33 uint64_t ambient_capabilities
;
34 uint64_t capability_bounding_set
;
37 char **supplementary_groups
;
40 bool ip_address_deny_all
;
41 bool ip_address_allow_localhost
;
42 bool ip_address_allow_other
;
45 bool lock_personality
;
46 bool memory_deny_write_execute
;
47 bool no_new_privileges
;
56 bool protect_control_groups
;
57 bool protect_kernel_modules
;
58 bool protect_kernel_tunables
;
65 bool restrict_address_family_inet
;
66 bool restrict_address_family_unix
;
67 bool restrict_address_family_netlink
;
68 bool restrict_address_family_packet
;
69 bool restrict_address_family_other
;
71 uint64_t restrict_namespaces
;
72 bool restrict_realtime
;
79 bool device_allow_non_empty
;
81 char **system_call_architectures
;
83 bool system_call_filter_whitelist
;
84 Set
*system_call_filter
;
89 struct security_assessor
{
91 const char *description_good
;
92 const char *description_bad
;
93 const char *description_na
;
97 int (*assess
)(const struct security_assessor
*a
, const struct security_info
*info
, const void *data
, uint64_t *ret_badness
, char **ret_description
);
100 bool default_dependencies_only
;
103 static void security_info_free(struct security_info
*i
) {
110 free(i
->fragment_path
);
114 free(i
->protect_home
);
115 free(i
->protect_system
);
117 free(i
->root_directory
);
120 free(i
->keyring_mode
);
121 free(i
->notify_access
);
123 free(i
->device_policy
);
125 strv_free(i
->supplementary_groups
);
126 strv_free(i
->system_call_architectures
);
128 set_free_free(i
->system_call_filter
);
131 static bool security_info_runs_privileged(const struct security_info
*i
) {
134 if (STRPTR_IN_SET(i
->user
, "0", "root"))
140 return isempty(i
->user
);
143 static int assess_bool(
144 const struct security_assessor
*a
,
145 const struct security_info
*info
,
147 uint64_t *ret_badness
,
148 char **ret_description
) {
150 const bool *b
= data
;
154 assert(ret_description
);
156 *ret_badness
= a
->parameter
? *b
: !*b
;
157 *ret_description
= NULL
;
162 static int assess_user(
163 const struct security_assessor
*a
,
164 const struct security_info
*info
,
166 uint64_t *ret_badness
,
167 char **ret_description
) {
169 _cleanup_free_
char *d
= NULL
;
173 assert(ret_description
);
175 if (streq_ptr(info
->user
, NOBODY_USER_NAME
)) {
176 d
= strdup("Service runs under as '" NOBODY_USER_NAME
"' user, which should not be used for services");
178 } else if (info
->dynamic_user
&& !STR_IN_SET(info
->user
, "0", "root")) {
179 d
= strdup("Service runs under a transient non-root user identity");
181 } else if (info
->user
&& !STR_IN_SET(info
->user
, "0", "root", "")) {
182 d
= strdup("Service runs under a static non-root user identity");
186 *ret_description
= NULL
;
194 *ret_description
= TAKE_PTR(d
);
199 static int assess_protect_home(
200 const struct security_assessor
*a
,
201 const struct security_info
*info
,
203 uint64_t *ret_badness
,
204 char **ret_description
) {
206 const char *description
;
212 assert(ret_description
);
215 description
= "Service has full access to home directories";
217 r
= parse_boolean(info
->protect_home
);
219 if (streq_ptr(info
->protect_home
, "read-only")) {
221 description
= "Service has read-only access to home directories";
222 } else if (streq_ptr(info
->protect_home
, "tmpfs")) {
224 description
= "Service has access to fake empty home directories";
228 description
= "Service has no access to home directories";
231 copy
= strdup(description
);
235 *ret_badness
= badness
;
236 *ret_description
= copy
;
241 static int assess_protect_system(
242 const struct security_assessor
*a
,
243 const struct security_info
*info
,
245 uint64_t *ret_badness
,
246 char **ret_description
) {
248 const char *description
;
254 assert(ret_description
);
257 description
= "Service has full access the OS file hierarchy";
259 r
= parse_boolean(info
->protect_system
);
261 if (streq_ptr(info
->protect_system
, "full")) {
263 description
= "Service has very limited write access to OS file hierarchy";
264 } else if (streq_ptr(info
->protect_system
, "strict")) {
266 description
= "Service has strict read-only access to the OS file hierarchy";
270 description
= "Service has limited write access to the OS file hierarchy";
273 copy
= strdup(description
);
277 *ret_badness
= badness
;
278 *ret_description
= copy
;
283 static int assess_root_directory(
284 const struct security_assessor
*a
,
285 const struct security_info
*info
,
287 uint64_t *ret_badness
,
288 char **ret_description
) {
291 assert(ret_description
);
294 (isempty(info
->root_directory
) ||
295 path_equal(info
->root_directory
, "/")) &&
296 (isempty(info
->root_image
) ||
297 path_equal(info
->root_image
, "/"));
298 *ret_description
= NULL
;
303 static int assess_capability_bounding_set(
304 const struct security_assessor
*a
,
305 const struct security_info
*info
,
307 uint64_t *ret_badness
,
308 char **ret_description
) {
311 assert(ret_description
);
313 *ret_badness
= !!(info
->capability_bounding_set
& a
->parameter
);
314 *ret_description
= NULL
;
319 static int assess_umask(
320 const struct security_assessor
*a
,
321 const struct security_info
*info
,
323 uint64_t *ret_badness
,
324 char **ret_description
) {
331 assert(ret_description
);
333 if (!FLAGS_SET(info
->_umask
, 0002)) {
334 d
= "Files created by service are world-writable by default";
336 } else if (!FLAGS_SET(info
->_umask
, 0004)) {
337 d
= "Files created by service are world-readable by default";
339 } else if (!FLAGS_SET(info
->_umask
, 0020)) {
340 d
= "Files created by service are group-writable by default";
342 } else if (!FLAGS_SET(info
->_umask
, 0040)) {
343 d
= "Files created by service are group-readable by default";
346 d
= "Files created by service are accessible only by service's own user by default";
355 *ret_description
= copy
;
360 static int assess_keyring_mode(
361 const struct security_assessor
*a
,
362 const struct security_info
*info
,
364 uint64_t *ret_badness
,
365 char **ret_description
) {
368 assert(ret_description
);
370 *ret_badness
= !streq_ptr(info
->keyring_mode
, "private");
371 *ret_description
= NULL
;
376 static int assess_notify_access(
377 const struct security_assessor
*a
,
378 const struct security_info
*info
,
380 uint64_t *ret_badness
,
381 char **ret_description
) {
384 assert(ret_description
);
386 *ret_badness
= streq_ptr(info
->notify_access
, "all");
387 *ret_description
= NULL
;
392 static int assess_remove_ipc(
393 const struct security_assessor
*a
,
394 const struct security_info
*info
,
396 uint64_t *ret_badness
,
397 char **ret_description
) {
400 assert(ret_description
);
402 if (security_info_runs_privileged(info
))
403 *ret_badness
= UINT64_MAX
;
405 *ret_badness
= !info
->remove_ipc
;
407 *ret_description
= NULL
;
411 static int assess_supplementary_groups(
412 const struct security_assessor
*a
,
413 const struct security_info
*info
,
415 uint64_t *ret_badness
,
416 char **ret_description
) {
419 assert(ret_description
);
421 if (security_info_runs_privileged(info
))
422 *ret_badness
= UINT64_MAX
;
424 *ret_badness
= !strv_isempty(info
->supplementary_groups
);
426 *ret_description
= NULL
;
430 static int assess_restrict_namespaces(
431 const struct security_assessor
*a
,
432 const struct security_info
*info
,
434 uint64_t *ret_badness
,
435 char **ret_description
) {
438 assert(ret_description
);
440 *ret_badness
= !!(info
->restrict_namespaces
& a
->parameter
);
441 *ret_description
= NULL
;
446 static int assess_system_call_architectures(
447 const struct security_assessor
*a
,
448 const struct security_info
*info
,
450 uint64_t *ret_badness
,
451 char **ret_description
) {
457 assert(ret_description
);
459 if (strv_isempty(info
->system_call_architectures
)) {
461 d
= strdup("Service may execute system calls with all ABIs");
462 } else if (strv_equal(info
->system_call_architectures
, STRV_MAKE("native"))) {
464 d
= strdup("Service may execute system calls only with native ABI");
467 d
= strdup("Service may execute system calls with multiple ABIs");
474 *ret_description
= d
;
479 static bool syscall_names_in_filter(Set
*s
, bool whitelist
, const SyscallFilterSet
*f
) {
482 NULSTR_FOREACH(syscall
, f
->value
) {
485 if (syscall
[0] == '@') {
486 const SyscallFilterSet
*g
;
487 assert_se(g
= syscall_filter_set_find(syscall
));
488 b
= syscall_names_in_filter(s
, whitelist
, g
);
493 /* Let's see if the system call actually exists on this platform, before complaining */
494 id
= seccomp_syscall_resolve_name(syscall
);
499 b
= set_contains(s
, syscall
);
502 if (whitelist
== b
) {
503 log_debug("Offending syscall filter item: %s", syscall
);
504 return true; /* bad! */
511 static int assess_system_call_filter(
512 const struct security_assessor
*a
,
513 const struct security_info
*info
,
515 uint64_t *ret_badness
,
516 char **ret_description
) {
518 const SyscallFilterSet
*f
;
525 assert(ret_description
);
527 assert(a
->parameter
< _SYSCALL_FILTER_SET_MAX
);
528 f
= syscall_filter_sets
+ a
->parameter
;
530 if (!info
->system_call_filter_whitelist
&& set_isempty(info
->system_call_filter
)) {
531 d
= strdup("Service does not filter system calls");
536 log_debug("Analyzing system call filter, checking against: %s", f
->name
);
537 bad
= syscall_names_in_filter(info
->system_call_filter
, info
->system_call_filter_whitelist
, f
);
538 log_debug("Result: %s", bad
? "bad" : "good");
540 if (info
->system_call_filter_whitelist
) {
542 (void) asprintf(&d
, "System call whitelist defined for service, and %s is included", f
->name
);
545 (void) asprintf(&d
, "System call whitelist defined for service, and %s is not included", f
->name
);
550 (void) asprintf(&d
, "System call blacklist defined for service, and %s is not included", f
->name
);
553 (void) asprintf(&d
, "System call blacklist defined for service, and %s is included", f
->name
);
563 *ret_description
= d
;
568 static int assess_ip_address_allow(
569 const struct security_assessor
*a
,
570 const struct security_info
*info
,
572 uint64_t *ret_badness
,
573 char **ret_description
) {
580 assert(ret_description
);
582 if (!info
->ip_address_deny_all
) {
583 d
= strdup("Service does not define an IP address whitelist");
585 } else if (info
->ip_address_allow_other
) {
586 d
= strdup("Service defines IP address whitelist with non-localhost entries");
588 } else if (info
->ip_address_allow_localhost
) {
589 d
= strdup("Service defines IP address whitelits with only localhost entries");
592 d
= strdup("Service blocks all IP address ranges");
600 *ret_description
= d
;
605 static int assess_device_allow(
606 const struct security_assessor
*a
,
607 const struct security_info
*info
,
609 uint64_t *ret_badness
,
610 char **ret_description
) {
617 assert(ret_description
);
619 if (STRPTR_IN_SET(info
->device_policy
, "strict", "closed")) {
621 if (info
->device_allow_non_empty
) {
622 d
= strdup("Service has a device ACL with some special devices");
625 d
= strdup("Service has a minimal device ACL");
629 d
= strdup("Service has no device ACL");
637 *ret_description
= d
;
642 static int assess_ambient_capabilities(
643 const struct security_assessor
*a
,
644 const struct security_info
*info
,
646 uint64_t *ret_badness
,
647 char **ret_description
) {
650 assert(ret_description
);
652 *ret_badness
= info
->ambient_capabilities
!= 0;
653 *ret_description
= NULL
;
658 static const struct security_assessor security_assessor_table
[] = {
660 .id
= "User=/DynamicUser=",
661 .description_bad
= "Service runs as root user",
662 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#User=",
665 .assess
= assess_user
,
668 .id
= "SupplementaryGroups=",
669 .description_good
= "Service has no supplementary groups",
670 .description_bad
= "Service runs with supplementary groups",
671 .description_na
= "Service runs as root, option does not matter",
672 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SupplementaryGroups=",
675 .assess
= assess_supplementary_groups
,
678 .id
= "PrivateDevices=",
679 .description_good
= "Service has no access to hardware devices",
680 .description_bad
= "Service potentially has access to hardware devices",
681 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateDevices=",
684 .assess
= assess_bool
,
685 .offset
= offsetof(struct security_info
, private_devices
),
688 .id
= "PrivateMounts=",
689 .description_good
= "Service cannot install system mounts",
690 .description_bad
= "Service may install system mounts",
691 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateMounts=",
694 .assess
= assess_bool
,
695 .offset
= offsetof(struct security_info
, private_mounts
),
698 .id
= "PrivateNetwork=",
699 .description_good
= "Service has no access to the host's network",
700 .description_bad
= "Service has access to the host's network",
701 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateNetwork=",
704 .assess
= assess_bool
,
705 .offset
= offsetof(struct security_info
, private_network
),
709 .description_good
= "Service has no access to other software's temporary files",
710 .description_bad
= "Service has access to other software's temporary files",
711 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateTmp=",
714 .assess
= assess_bool
,
715 .offset
= offsetof(struct security_info
, private_tmp
),
716 .default_dependencies_only
= true,
719 .id
= "PrivateUsers=",
720 .description_good
= "Service does not have access to other users",
721 .description_bad
= "Service has access to other users",
722 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateUsers=",
725 .assess
= assess_bool
,
726 .offset
= offsetof(struct security_info
, private_users
),
729 .id
= "ProtectControlGroups=",
730 .description_good
= "Service cannot modify the control group file system",
731 .description_bad
= "Service may modify to the control group file system",
732 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectControlGroups=",
735 .assess
= assess_bool
,
736 .offset
= offsetof(struct security_info
, protect_control_groups
),
739 .id
= "ProtectKernelModules=",
740 .description_good
= "Service cannot load or read kernel modules",
741 .description_bad
= "Service may load or read kernel modules",
742 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelModules=",
745 .assess
= assess_bool
,
746 .offset
= offsetof(struct security_info
, protect_kernel_modules
),
749 .id
= "ProtectKernelTunables=",
750 .description_good
= "Service cannot alter kernel tunables (/proc/sys, …)",
751 .description_bad
= "Service may alter kernel tunables",
752 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelTunables=",
755 .assess
= assess_bool
,
756 .offset
= offsetof(struct security_info
, protect_kernel_tunables
),
759 .id
= "ProtectHome=",
760 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHome=",
763 .assess
= assess_protect_home
,
764 .default_dependencies_only
= true,
767 .id
= "ProtectSystem=",
768 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectSystem=",
771 .assess
= assess_protect_system
,
772 .default_dependencies_only
= true,
775 .id
= "RootDirectory=/RootImage=",
776 .description_good
= "Service has its own root directory/image",
777 .description_bad
= "Service runs within the host's root directory",
778 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RootDirectory=",
781 .assess
= assess_root_directory
,
782 .default_dependencies_only
= true,
785 .id
= "LockPersonality=",
786 .description_good
= "Service cannot change ABI personality",
787 .description_bad
= "Service may change ABI personality",
788 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#LockPersonality=",
791 .assess
= assess_bool
,
792 .offset
= offsetof(struct security_info
, lock_personality
),
795 .id
= "MemoryDenyWriteExecute=",
796 .description_good
= "Service cannot create writable executable memory mappings",
797 .description_bad
= "Service may create writable executable memory mappings",
798 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#MemoryDenyWriteExecute=",
801 .assess
= assess_bool
,
802 .offset
= offsetof(struct security_info
, memory_deny_write_execute
),
805 .id
= "NoNewPrivileges=",
806 .description_good
= "Service processes cannot acquire new privileges",
807 .description_bad
= "Service processes may acquire new privileges",
808 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NoNewPrivileges=",
811 .assess
= assess_bool
,
812 .offset
= offsetof(struct security_info
, no_new_privileges
),
815 .id
= "CapabilityBoundingSet=~CAP_SYS_ADMIN",
816 .description_good
= "Service has no administrator privileges",
817 .description_bad
= "Service has administrator privileges",
818 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
821 .assess
= assess_capability_bounding_set
,
822 .parameter
= UINT64_C(1) << CAP_SYS_ADMIN
,
825 .id
= "CapabilityBoundingSet=~CAP_SET(UID|GID|PCAP)",
826 .description_good
= "Service cannot change UID/GID identities/capabilities",
827 .description_bad
= "Service may change UID/GID identities/capabilities",
828 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
831 .assess
= assess_capability_bounding_set
,
832 .parameter
= (UINT64_C(1) << CAP_SETUID
)|
833 (UINT64_C(1) << CAP_SETGID
)|
834 (UINT64_C(1) << CAP_SETPCAP
),
837 .id
= "CapabilityBoundingSet=~CAP_SYS_PTRACE",
838 .description_good
= "Service has no ptrace() debugging abilities",
839 .description_bad
= "Service has ptrace() debugging abilities",
840 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
843 .assess
= assess_capability_bounding_set
,
844 .parameter
= (UINT64_C(1) << CAP_SYS_PTRACE
),
847 .id
= "CapabilityBoundingSet=~CAP_SYS_TIME",
848 .description_good
= "Service processes cannot change the system clock",
849 .description_bad
= "Service processes may change the system clock",
850 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
853 .assess
= assess_capability_bounding_set
,
854 .parameter
= UINT64_C(1) << CAP_SYS_TIME
,
857 .id
= "CapabilityBoundingSet=~CAP_NET_ADMIN",
858 .description_good
= "Service has no network configuration privileges",
859 .description_bad
= "Service has network configuration privileges",
860 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
863 .assess
= assess_capability_bounding_set
,
864 .parameter
= (UINT64_C(1) << CAP_NET_ADMIN
),
867 .id
= "CapabilityBoundingSet=~CAP_RAWIO",
868 .description_good
= "Service has no raw I/O access",
869 .description_bad
= "Service has raw I/O access",
870 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
873 .assess
= assess_capability_bounding_set
,
874 .parameter
= (UINT64_C(1) << CAP_SYS_RAWIO
),
877 .id
= "CapabilityBoundingSet=~CAP_SYS_MODULE",
878 .description_good
= "Service cannot load kernel modules",
879 .description_bad
= "Service may load kernel modules",
880 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
883 .assess
= assess_capability_bounding_set
,
884 .parameter
= (UINT64_C(1) << CAP_SYS_MODULE
),
887 .id
= "CapabilityBoundingSet=~CAP_AUDIT_*",
888 .description_good
= "Service has no audit subsystem access",
889 .description_bad
= "Service has audit subsystem access",
890 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
893 .assess
= assess_capability_bounding_set
,
894 .parameter
= (UINT64_C(1) << CAP_AUDIT_CONTROL
) |
895 (UINT64_C(1) << CAP_AUDIT_READ
) |
896 (UINT64_C(1) << CAP_AUDIT_WRITE
),
899 .id
= "CapabilityBoundingSet=~CAP_SYSLOG",
900 .description_good
= "Service has no access to kernel logging",
901 .description_bad
= "Service has access to kernel logging",
902 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
905 .assess
= assess_capability_bounding_set
,
906 .parameter
= (UINT64_C(1) << CAP_SYSLOG
),
909 .id
= "CapabilityBoundingSet=~CAP_SYS_(NICE|RESOURCE)",
910 .description_good
= "Service has no privileges to change resource use parameters",
911 .description_bad
= "Service has privileges to change resource use parameters",
912 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
915 .assess
= assess_capability_bounding_set
,
916 .parameter
= (UINT64_C(1) << CAP_SYS_NICE
) |
917 (UINT64_C(1) << CAP_SYS_RESOURCE
),
920 .id
= "CapabilityBoundingSet=~CAP_MKNOD",
921 .description_good
= "Service cannot create device nodes",
922 .description_bad
= "Service may create device nodes",
923 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
926 .assess
= assess_capability_bounding_set
,
927 .parameter
= (UINT64_C(1) << CAP_MKNOD
),
930 .id
= "CapabilityBoundingSet=~CAP_(CHOWN|FSETID|SETFCAP)",
931 .description_good
= "Service cannot change file ownership/access mode/capabilities",
932 .description_bad
= "Service may change file ownership/access mode/capabilities unrestricted",
933 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
936 .assess
= assess_capability_bounding_set
,
937 .parameter
= (UINT64_C(1) << CAP_CHOWN
) |
938 (UINT64_C(1) << CAP_FSETID
) |
939 (UINT64_C(1) << CAP_SETFCAP
),
942 .id
= "CapabilityBoundingSet=~CAP_(DAC_*|FOWNER|IPC_OWNER)",
943 .description_good
= "Service cannot override UNIX file/IPC permission checks",
944 .description_bad
= "Service may override UNIX file/IPC permission checks",
945 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
948 .assess
= assess_capability_bounding_set
,
949 .parameter
= (UINT64_C(1) << CAP_DAC_OVERRIDE
) |
950 (UINT64_C(1) << CAP_DAC_READ_SEARCH
) |
951 (UINT64_C(1) << CAP_FOWNER
) |
952 (UINT64_C(1) << CAP_IPC_OWNER
),
955 .id
= "CapabilityBoundingSet=~CAP_KILL",
956 .description_good
= "Service cannot send UNIX signals to arbitrary processes",
957 .description_bad
= "Service may send UNIX signals to arbitrary processes",
958 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
961 .assess
= assess_capability_bounding_set
,
962 .parameter
= (UINT64_C(1) << CAP_KILL
),
965 .id
= "CapabilityBoundingSet=~CAP_NET_(BIND_SERVICE|BROADCAST|RAW)",
966 .description_good
= "Service has no elevated networking privileges",
967 .description_bad
= "Service has elevated networking privileges",
968 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
971 .assess
= assess_capability_bounding_set
,
972 .parameter
= (UINT64_C(1) << CAP_NET_BIND_SERVICE
) |
973 (UINT64_C(1) << CAP_NET_BROADCAST
) |
974 (UINT64_C(1) << CAP_NET_RAW
),
977 .id
= "CapabilityBoundingSet=~CAP_SYS_BOOT",
978 .description_good
= "Service cannot issue reboot()",
979 .description_bad
= "Service may issue reboot()",
980 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
983 .assess
= assess_capability_bounding_set
,
984 .parameter
= (UINT64_C(1) << CAP_SYS_BOOT
),
987 .id
= "CapabilityBoundingSet=~CAP_MAC_*",
988 .description_good
= "Service cannot adjust SMACK MAC",
989 .description_bad
= "Service may adjust SMACK MAC",
990 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
993 .assess
= assess_capability_bounding_set
,
994 .parameter
= (UINT64_C(1) << CAP_MAC_ADMIN
)|
995 (UINT64_C(1) << CAP_MAC_OVERRIDE
),
998 .id
= "CapabilityBoundingSet=~CAP_LINUX_IMMUTABLE",
999 .description_good
= "Service cannot mark files immutable",
1000 .description_bad
= "Service may mark files immutable",
1001 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1004 .assess
= assess_capability_bounding_set
,
1005 .parameter
= (UINT64_C(1) << CAP_LINUX_IMMUTABLE
),
1008 .id
= "CapabilityBoundingSet=~CAP_IPC_LOCK",
1009 .description_good
= "Service cannot lock memory into RAM",
1010 .description_bad
= "Service may lock memory into RAM",
1011 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1014 .assess
= assess_capability_bounding_set
,
1015 .parameter
= (UINT64_C(1) << CAP_IPC_LOCK
),
1018 .id
= "CapabilityBoundingSet=~CAP_SYS_CHROOT",
1019 .description_good
= "Service cannot issue chroot()",
1020 .description_bad
= "Service may issue chroot()",
1021 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1024 .assess
= assess_capability_bounding_set
,
1025 .parameter
= (UINT64_C(1) << CAP_SYS_CHROOT
),
1028 .id
= "CapabilityBoundingSet=~CAP_BLOCK_SUSPEND",
1029 .description_good
= "Service cannot establish wake locks",
1030 .description_bad
= "Service may establish wake locks",
1031 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1034 .assess
= assess_capability_bounding_set
,
1035 .parameter
= (UINT64_C(1) << CAP_BLOCK_SUSPEND
),
1038 .id
= "CapabilityBoundingSet=~CAP_WAKE_ALARM",
1039 .description_good
= "Service cannot program timers that wake up the system",
1040 .description_bad
= "Service may program timers that wake up the system",
1041 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1044 .assess
= assess_capability_bounding_set
,
1045 .parameter
= (UINT64_C(1) << CAP_WAKE_ALARM
),
1048 .id
= "CapabilityBoundingSet=~CAP_LEASE",
1049 .description_good
= "Service cannot create file leases",
1050 .description_bad
= "Service may create file leases",
1051 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1054 .assess
= assess_capability_bounding_set
,
1055 .parameter
= (UINT64_C(1) << CAP_LEASE
),
1058 .id
= "CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG",
1059 .description_good
= "Service cannot issue vhangup()",
1060 .description_bad
= "Service may issue vhangup()",
1061 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1064 .assess
= assess_capability_bounding_set
,
1065 .parameter
= (UINT64_C(1) << CAP_SYS_TTY_CONFIG
),
1068 .id
= "CapabilityBoundingSet=~CAP_SYS_PACCT",
1069 .description_good
= "Service cannot use acct()",
1070 .description_bad
= "Service may use acct()",
1071 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1074 .assess
= assess_capability_bounding_set
,
1075 .parameter
= (UINT64_C(1) << CAP_SYS_PACCT
),
1079 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#UMask=",
1082 .assess
= assess_umask
,
1085 .id
= "KeyringMode=",
1086 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#KeyringMode=",
1087 .description_good
= "Service doesn't share key material with other services",
1088 .description_bad
= "Service shares key material with other service",
1091 .assess
= assess_keyring_mode
,
1094 .id
= "NotifyAccess=",
1095 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NotifyAccess=",
1096 .description_good
= "Service child processes cannot alter service state",
1097 .description_bad
= "Service child processes may alter service state",
1100 .assess
= assess_notify_access
,
1104 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RemoveIPC=",
1105 .description_good
= "Service user cannot leave SysV IPC objects around",
1106 .description_bad
= "Service user may leave SysV IPC objects around",
1107 .description_na
= "Service runs as root, option does not apply",
1110 .assess
= assess_remove_ipc
,
1111 .offset
= offsetof(struct security_info
, remove_ipc
),
1115 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Delegate=",
1116 .description_good
= "Service does not maintain its own delegated control group subtree",
1117 .description_bad
= "Service maintains its own delegated control group subtree",
1120 .assess
= assess_bool
,
1121 .offset
= offsetof(struct security_info
, delegate
),
1122 .parameter
= true, /* invert! */
1125 .id
= "RestrictRealtime=",
1126 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictRealtime=",
1127 .description_good
= "Service realtime scheduling access is restricted",
1128 .description_bad
= "Service may acquire realtime scheduling",
1131 .assess
= assess_bool
,
1132 .offset
= offsetof(struct security_info
, restrict_realtime
),
1135 .id
= "RestrictNamespaces=~CLONE_NEWUSER",
1136 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1137 .description_good
= "Service cannot create user namespaces",
1138 .description_bad
= "Service may create user namespaces",
1141 .assess
= assess_restrict_namespaces
,
1142 .parameter
= CLONE_NEWUSER
,
1145 .id
= "RestrictNamespaces=~CLONE_NEWNS",
1146 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1147 .description_good
= "Service cannot create file system namespaces",
1148 .description_bad
= "Service may create file system namespaces",
1151 .assess
= assess_restrict_namespaces
,
1152 .parameter
= CLONE_NEWNS
,
1155 .id
= "RestrictNamespaces=~CLONE_NEWIPC",
1156 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1157 .description_good
= "Service cannot create IPC namespaces",
1158 .description_bad
= "Service may create IPC namespaces",
1161 .assess
= assess_restrict_namespaces
,
1162 .parameter
= CLONE_NEWIPC
,
1165 .id
= "RestrictNamespaces=~CLONE_NEWPID",
1166 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1167 .description_good
= "Service cannot create process namespaces",
1168 .description_bad
= "Service may create process namespaces",
1171 .assess
= assess_restrict_namespaces
,
1172 .parameter
= CLONE_NEWPID
,
1175 .id
= "RestrictNamespaces=~CLONE_NEWCGROUP",
1176 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1177 .description_good
= "Service cannot create cgroup namespaces",
1178 .description_bad
= "Service may create cgroup namespaces",
1181 .assess
= assess_restrict_namespaces
,
1182 .parameter
= CLONE_NEWCGROUP
,
1185 .id
= "RestrictNamespaces=~CLONE_NEWNET",
1186 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1187 .description_good
= "Service cannot create network namespaces",
1188 .description_bad
= "Service may create network namespaces",
1191 .assess
= assess_restrict_namespaces
,
1192 .parameter
= CLONE_NEWNET
,
1195 .id
= "RestrictNamespaces=~CLONE_NEWUTS",
1196 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1197 .description_good
= "Service cannot create hostname namespaces",
1198 .description_bad
= "Service may create hostname namespaces",
1201 .assess
= assess_restrict_namespaces
,
1202 .parameter
= CLONE_NEWUTS
,
1205 .id
= "RestrictAddressFamilies=~AF_(INET|INET6)",
1206 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1207 .description_good
= "Service cannot allocate Internet sockets",
1208 .description_bad
= "Service may allocate Internet sockets",
1211 .assess
= assess_bool
,
1212 .offset
= offsetof(struct security_info
, restrict_address_family_inet
),
1215 .id
= "RestrictAddressFamilies=~AF_UNIX",
1216 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1217 .description_good
= "Service cannot allocate local sockets",
1218 .description_bad
= "Service may allocate local sockets",
1221 .assess
= assess_bool
,
1222 .offset
= offsetof(struct security_info
, restrict_address_family_unix
),
1225 .id
= "RestrictAddressFamilies=~AF_NETLINK",
1226 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1227 .description_good
= "Service cannot allocate netlink sockets",
1228 .description_bad
= "Service may allocate netlink sockets",
1231 .assess
= assess_bool
,
1232 .offset
= offsetof(struct security_info
, restrict_address_family_netlink
),
1235 .id
= "RestrictAddressFamilies=~AF_PACKET",
1236 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1237 .description_good
= "Service cannot allocate packet sockets",
1238 .description_bad
= "Service may allocate packet sockets",
1241 .assess
= assess_bool
,
1242 .offset
= offsetof(struct security_info
, restrict_address_family_packet
),
1245 .id
= "RestrictAddressFamilies=~…",
1246 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1247 .description_good
= "Service cannot allocate exotic sockets",
1248 .description_bad
= "Service may allocate exotic sockets",
1251 .assess
= assess_bool
,
1252 .offset
= offsetof(struct security_info
, restrict_address_family_other
),
1255 .id
= "SystemCallArchitectures=",
1256 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallArchitectures=",
1259 .assess
= assess_system_call_architectures
,
1262 .id
= "SystemCallFilter=~@swap",
1263 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1266 .assess
= assess_system_call_filter
,
1267 .parameter
= SYSCALL_FILTER_SET_SWAP
,
1270 .id
= "SystemCallFilter=~@obsolete",
1271 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1274 .assess
= assess_system_call_filter
,
1275 .parameter
= SYSCALL_FILTER_SET_OBSOLETE
,
1278 .id
= "SystemCallFilter=~@clock",
1279 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1282 .assess
= assess_system_call_filter
,
1283 .parameter
= SYSCALL_FILTER_SET_CLOCK
,
1286 .id
= "SystemCallFilter=~@cpu-emulation",
1287 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1290 .assess
= assess_system_call_filter
,
1291 .parameter
= SYSCALL_FILTER_SET_CPU_EMULATION
,
1294 .id
= "SystemCallFilter=~@debug",
1295 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1298 .assess
= assess_system_call_filter
,
1299 .parameter
= SYSCALL_FILTER_SET_DEBUG
,
1302 .id
= "SystemCallFilter=~@mount",
1303 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1306 .assess
= assess_system_call_filter
,
1307 .parameter
= SYSCALL_FILTER_SET_MOUNT
,
1310 .id
= "SystemCallFilter=~@module",
1311 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1314 .assess
= assess_system_call_filter
,
1315 .parameter
= SYSCALL_FILTER_SET_MODULE
,
1318 .id
= "SystemCallFilter=~@raw-io",
1319 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1322 .assess
= assess_system_call_filter
,
1323 .parameter
= SYSCALL_FILTER_SET_RAW_IO
,
1326 .id
= "SystemCallFilter=~@reboot",
1327 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1330 .assess
= assess_system_call_filter
,
1331 .parameter
= SYSCALL_FILTER_SET_REBOOT
,
1334 .id
= "SystemCallFilter=~@privileged",
1335 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1338 .assess
= assess_system_call_filter
,
1339 .parameter
= SYSCALL_FILTER_SET_PRIVILEGED
,
1342 .id
= "SystemCallFilter=~@resources",
1343 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1346 .assess
= assess_system_call_filter
,
1347 .parameter
= SYSCALL_FILTER_SET_RESOURCES
,
1350 .id
= "IPAddressDeny=",
1351 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#IPAddressDeny=",
1354 .assess
= assess_ip_address_allow
,
1357 .id
= "DeviceAllow=",
1358 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#DeviceAllow=",
1361 .assess
= assess_device_allow
,
1364 .id
= "AmbientCapabilities=",
1365 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#AmbientCapabilities=",
1366 .description_good
= "Service process does not receive ambient capabilities",
1367 .description_bad
= "Service process receives ambient capabilities",
1370 .assess
= assess_ambient_capabilities
,
1374 static int assess(const struct security_info
*info
, Table
*overview_table
, AnalyzeSecurityFlags flags
) {
1375 static const struct {
1379 SpecialGlyph smiley
;
1380 } badness_table
[] = {
1381 { 100, "DANGEROUS", ANSI_HIGHLIGHT_RED
, DEPRESSED_SMILEY
},
1382 { 90, "UNSAFE", ANSI_HIGHLIGHT_RED
, UNHAPPY_SMILEY
},
1383 { 75, "EXPOSED", ANSI_HIGHLIGHT_YELLOW
, SLIGHTLY_UNHAPPY_SMILEY
},
1384 { 50, "MEDIUM", NULL
, NEUTRAL_SMILEY
},
1385 { 10, "OK", ANSI_HIGHLIGHT_GREEN
, SLIGHTLY_HAPPY_SMILEY
},
1386 { 1, "SAFE", ANSI_HIGHLIGHT_GREEN
, HAPPY_SMILEY
},
1387 { 0, "PERFECT", ANSI_HIGHLIGHT_GREEN
, ECSTATIC_SMILEY
},
1390 uint64_t badness_sum
= 0, weight_sum
= 0, exposure
;
1391 _cleanup_(table_unrefp
) Table
*details_table
= NULL
;
1395 if (!FLAGS_SET(flags
, ANALYZE_SECURITY_SHORT
)) {
1396 details_table
= table_new("", "NAME", "DESCRIPTION", "WEIGHT", "BADNESS", "RANGE", "EXPOSURE");
1400 (void) table_set_sort(details_table
, 3, 1, (size_t) -1);
1401 (void) table_set_reverse(details_table
, 3, true);
1403 if (getenv_bool("SYSTEMD_ANALYZE_DEBUG") <= 0)
1404 (void) table_set_display(details_table
, 0, 1, 2, 6, (size_t) -1);
1407 for (i
= 0; i
< ELEMENTSOF(security_assessor_table
); i
++) {
1408 const struct security_assessor
*a
= security_assessor_table
+ i
;
1409 _cleanup_free_
char *d
= NULL
;
1413 data
= (uint8_t*) info
+ a
->offset
;
1415 if (a
->default_dependencies_only
&& !info
->default_dependencies
) {
1416 badness
= UINT64_MAX
;
1417 d
= strdup("Service runs in special boot phase, option does not apply");
1421 r
= a
->assess(a
, info
, data
, &badness
, &d
);
1426 assert(a
->range
> 0);
1428 if (badness
!= UINT64_MAX
) {
1429 assert(badness
<= a
->range
);
1431 badness_sum
+= DIV_ROUND_UP(badness
* a
->weight
, a
->range
);
1432 weight_sum
+= a
->weight
;
1435 if (details_table
) {
1436 const char *checkmark
, *description
, *color
= NULL
;
1439 if (badness
== UINT64_MAX
) {
1441 description
= a
->description_na
;
1443 } else if (badness
== a
->range
) {
1444 checkmark
= special_glyph(CROSS_MARK
);
1445 description
= a
->description_bad
;
1446 color
= ansi_highlight_red();
1447 } else if (badness
== 0) {
1448 checkmark
= special_glyph(CHECK_MARK
);
1449 description
= a
->description_good
;
1450 color
= ansi_highlight_green();
1452 checkmark
= special_glyph(CROSS_MARK
);
1454 color
= ansi_highlight_red();
1460 r
= table_add_cell_full(details_table
, &cell
, TABLE_STRING
, checkmark
, 1, 1, 0, 0, 0);
1462 return log_error_errno(r
, "Failed to add cell to table: %m");
1464 (void) table_set_color(details_table
, cell
, color
);
1466 r
= table_add_cell(details_table
, &cell
, TABLE_STRING
, a
->id
);
1468 return log_error_errno(r
, "Failed to add cell to table: %m");
1470 (void) table_set_url(details_table
, cell
, a
->url
);
1472 r
= table_add_cell(details_table
, NULL
, TABLE_STRING
, description
);
1474 return log_error_errno(r
, "Failed to add cell to table: %m");
1476 r
= table_add_cell(details_table
, &cell
, TABLE_UINT64
, &a
->weight
);
1478 return log_error_errno(r
, "Failed to add cell to table: %m");
1479 (void) table_set_align_percent(details_table
, cell
, 100);
1481 r
= table_add_cell(details_table
, &cell
, TABLE_UINT64
, &badness
);
1483 return log_error_errno(r
, "Failed to add cell to table: %m");
1484 (void) table_set_align_percent(details_table
, cell
, 100);
1486 r
= table_add_cell(details_table
, &cell
, TABLE_UINT64
, &a
->range
);
1488 return log_error_errno(r
, "Failed to add cell to table: %m");
1489 (void) table_set_align_percent(details_table
, cell
, 100);
1491 r
= table_add_cell(details_table
, &cell
, TABLE_EMPTY
, NULL
);
1493 return log_error_errno(r
, "Failed to add cell to table: %m");
1494 (void) table_set_align_percent(details_table
, cell
, 100);
1498 if (details_table
) {
1501 for (row
= 1; row
< table_get_rows(details_table
); row
++) {
1502 char buf
[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1];
1503 const uint64_t *weight
, *badness
, *range
;
1507 assert_se(weight
= table_get_at(details_table
, row
, 3));
1508 assert_se(badness
= table_get_at(details_table
, row
, 4));
1509 assert_se(range
= table_get_at(details_table
, row
, 5));
1511 if (*badness
== UINT64_MAX
|| *badness
== 0)
1514 assert_se(cell
= table_get_cell(details_table
, row
, 6));
1516 x
= DIV_ROUND_UP(DIV_ROUND_UP(*badness
* *weight
* 100U, *range
), weight_sum
);
1517 xsprintf(buf
, "%" PRIu64
".%" PRIu64
, x
/ 10, x
% 10);
1519 r
= table_update(details_table
, cell
, TABLE_STRING
, buf
);
1521 return log_error_errno(r
, "Failed to update cell in table: %m");
1524 r
= table_print(details_table
, stdout
);
1526 return log_error_errno(r
, "Failed to output table: %m");
1529 exposure
= DIV_ROUND_UP(badness_sum
* 100U, weight_sum
);
1531 for (i
= 0; i
< ELEMENTSOF(badness_table
); i
++)
1532 if (exposure
>= badness_table
[i
].exposure
)
1535 assert(i
< ELEMENTSOF(badness_table
));
1537 if (details_table
) {
1538 _cleanup_free_
char *clickable
= NULL
;
1541 /* If we shall output the details table, also print the brief summary underneath */
1543 if (info
->fragment_path
) {
1544 r
= terminal_urlify_path(info
->fragment_path
, info
->id
, &clickable
);
1552 printf("\n%s %sOverall exposure level for %s%s: %s%" PRIu64
".%" PRIu64
" %s%s %s\n",
1553 special_glyph(ARROW
),
1557 colors_enabled() ? strempty(badness_table
[i
].color
) : "",
1558 exposure
/ 10, exposure
% 10,
1559 badness_table
[i
].name
,
1561 special_glyph(badness_table
[i
].smiley
));
1566 if (overview_table
) {
1567 char buf
[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1];
1570 r
= table_add_cell(overview_table
, &cell
, TABLE_STRING
, info
->id
);
1572 return log_error_errno(r
, "Failed to add cell to table: %m");
1573 if (info
->fragment_path
) {
1574 _cleanup_free_
char *url
= NULL
;
1576 r
= file_url_from_path(info
->fragment_path
, &url
);
1578 return log_error_errno(r
, "Failed to generate URL from path: %m");
1580 (void) table_set_url(overview_table
, cell
, url
);
1583 xsprintf(buf
, "%" PRIu64
".%" PRIu64
, exposure
/ 10, exposure
% 10);
1584 r
= table_add_cell(overview_table
, &cell
, TABLE_STRING
, buf
);
1586 return log_error_errno(r
, "Failed to add cell to table: %m");
1587 (void) table_set_align_percent(overview_table
, cell
, 100);
1589 r
= table_add_cell(overview_table
, &cell
, TABLE_STRING
, badness_table
[i
].name
);
1591 return log_error_errno(r
, "Failed to add cell to table: %m");
1592 (void) table_set_color(overview_table
, cell
, strempty(badness_table
[i
].color
));
1594 r
= table_add_cell(overview_table
, NULL
, TABLE_STRING
, special_glyph(badness_table
[i
].smiley
));
1596 return log_error_errno(r
, "Failed to add cell to table: %m");
1602 static int property_read_restrict_address_families(
1606 sd_bus_error
*error
,
1609 struct security_info
*info
= userdata
;
1616 r
= sd_bus_message_enter_container(m
, 'r', "bas");
1620 r
= sd_bus_message_read(m
, "b", &whitelist
);
1624 info
->restrict_address_family_inet
=
1625 info
->restrict_address_family_unix
=
1626 info
->restrict_address_family_netlink
=
1627 info
->restrict_address_family_packet
=
1628 info
->restrict_address_family_other
= whitelist
;
1630 r
= sd_bus_message_enter_container(m
, 'a', "s");
1637 r
= sd_bus_message_read(m
, "s", &name
);
1643 if (STR_IN_SET(name
, "AF_INET", "AF_INET6"))
1644 info
->restrict_address_family_inet
= !whitelist
;
1645 else if (streq(name
, "AF_UNIX"))
1646 info
->restrict_address_family_unix
= !whitelist
;
1647 else if (streq(name
, "AF_NETLINK"))
1648 info
->restrict_address_family_netlink
= !whitelist
;
1649 else if (streq(name
, "AF_PACKET"))
1650 info
->restrict_address_family_packet
= !whitelist
;
1652 info
->restrict_address_family_other
= !whitelist
;
1655 r
= sd_bus_message_exit_container(m
);
1659 return sd_bus_message_exit_container(m
);
1662 static int property_read_system_call_filter(
1666 sd_bus_error
*error
,
1669 struct security_info
*info
= userdata
;
1676 r
= sd_bus_message_enter_container(m
, 'r', "bas");
1680 r
= sd_bus_message_read(m
, "b", &whitelist
);
1684 info
->system_call_filter_whitelist
= whitelist
;
1686 r
= sd_bus_message_enter_container(m
, 'a', "s");
1693 r
= sd_bus_message_read(m
, "s", &name
);
1699 r
= set_ensure_allocated(&info
->system_call_filter
, &string_hash_ops
);
1703 r
= set_put_strdup(info
->system_call_filter
, name
);
1708 r
= sd_bus_message_exit_container(m
);
1712 return sd_bus_message_exit_container(m
);
1715 static int property_read_ip_address_allow(
1719 sd_bus_error
*error
,
1722 struct security_info
*info
= userdata
;
1723 bool deny_ipv4
= false, deny_ipv6
= false;
1730 r
= sd_bus_message_enter_container(m
, 'a', "(iayu)");
1740 r
= sd_bus_message_enter_container(m
, 'r', "iayu");
1746 r
= sd_bus_message_read(m
, "i", &family
);
1750 r
= sd_bus_message_read_array(m
, 'y', &data
, &size
);
1754 r
= sd_bus_message_read(m
, "u", &prefixlen
);
1758 r
= sd_bus_message_exit_container(m
);
1762 if (streq(member
, "IPAddressAllow")) {
1763 union in_addr_union u
;
1765 if (family
== AF_INET
&& size
== 4 && prefixlen
== 8)
1766 memcpy(&u
.in
, data
, size
);
1767 else if (family
== AF_INET6
&& size
== 16 && prefixlen
== 128)
1768 memcpy(&u
.in6
, data
, size
);
1770 info
->ip_address_allow_other
= true;
1774 if (in_addr_is_localhost(family
, &u
))
1775 info
->ip_address_allow_localhost
= true;
1777 info
->ip_address_allow_other
= true;
1779 assert(streq(member
, "IPAddressDeny"));
1781 if (family
== AF_INET
&& size
== 4 && prefixlen
== 0)
1783 else if (family
== AF_INET6
&& size
== 16 && prefixlen
== 0)
1788 info
->ip_address_deny_all
= deny_ipv4
&& deny_ipv6
;
1790 return sd_bus_message_exit_container(m
);
1793 static int property_read_device_allow(
1797 sd_bus_error
*error
,
1800 struct security_info
*info
= userdata
;
1808 r
= sd_bus_message_enter_container(m
, 'a', "(ss)");
1813 const char *name
, *policy
;
1815 r
= sd_bus_message_read(m
, "(ss)", &name
, &policy
);
1824 info
->device_allow_non_empty
= n
> 0;
1826 return sd_bus_message_exit_container(m
);
1829 static int acquire_security_info(sd_bus
*bus
, const char *name
, struct security_info
*info
, AnalyzeSecurityFlags flags
) {
1831 static const struct bus_properties_map security_map
[] = {
1832 { "AmbientCapabilities", "t", NULL
, offsetof(struct security_info
, ambient_capabilities
) },
1833 { "CapabilityBoundingSet", "t", NULL
, offsetof(struct security_info
, capability_bounding_set
) },
1834 { "DefaultDependencies", "b", NULL
, offsetof(struct security_info
, default_dependencies
) },
1835 { "Delegate", "b", NULL
, offsetof(struct security_info
, delegate
) },
1836 { "DeviceAllow", "a(ss)", property_read_device_allow
, 0 },
1837 { "DevicePolicy", "s", NULL
, offsetof(struct security_info
, device_policy
) },
1838 { "DynamicUser", "b", NULL
, offsetof(struct security_info
, dynamic_user
) },
1839 { "FragmentPath", "s", NULL
, offsetof(struct security_info
, fragment_path
) },
1840 { "IPAddressAllow", "a(iayu)", property_read_ip_address_allow
, 0 },
1841 { "IPAddressDeny", "a(iayu)", property_read_ip_address_allow
, 0 },
1842 { "Id", "s", NULL
, offsetof(struct security_info
, id
) },
1843 { "KeyringMode", "s", NULL
, offsetof(struct security_info
, keyring_mode
) },
1844 { "LoadState", "s", NULL
, offsetof(struct security_info
, load_state
) },
1845 { "LockPersonality", "b", NULL
, offsetof(struct security_info
, lock_personality
) },
1846 { "MemoryDenyWriteExecute", "b", NULL
, offsetof(struct security_info
, memory_deny_write_execute
) },
1847 { "NoNewPrivileges", "b", NULL
, offsetof(struct security_info
, no_new_privileges
) },
1848 { "NotifyAccess", "s", NULL
, offsetof(struct security_info
, notify_access
) },
1849 { "PrivateDevices", "b", NULL
, offsetof(struct security_info
, private_devices
) },
1850 { "PrivateMounts", "b", NULL
, offsetof(struct security_info
, private_mounts
) },
1851 { "PrivateNetwork", "b", NULL
, offsetof(struct security_info
, private_network
) },
1852 { "PrivateTmp", "b", NULL
, offsetof(struct security_info
, private_tmp
) },
1853 { "PrivateUsers", "b", NULL
, offsetof(struct security_info
, private_users
) },
1854 { "PrivateUsers", "b", NULL
, offsetof(struct security_info
, private_users
) },
1855 { "ProtectControlGroups", "b", NULL
, offsetof(struct security_info
, protect_control_groups
) },
1856 { "ProtectHome", "s", NULL
, offsetof(struct security_info
, protect_home
) },
1857 { "ProtectKernelModules", "b", NULL
, offsetof(struct security_info
, protect_kernel_modules
) },
1858 { "ProtectKernelTunables", "b", NULL
, offsetof(struct security_info
, protect_kernel_tunables
) },
1859 { "ProtectSystem", "s", NULL
, offsetof(struct security_info
, protect_system
) },
1860 { "RemoveIPC", "b", NULL
, offsetof(struct security_info
, remove_ipc
) },
1861 { "RestrictAddressFamilies", "(bas)", property_read_restrict_address_families
, 0 },
1862 { "RestrictNamespaces", "t", NULL
, offsetof(struct security_info
, restrict_namespaces
) },
1863 { "RestrictRealtime", "b", NULL
, offsetof(struct security_info
, restrict_realtime
) },
1864 { "RootDirectory", "s", NULL
, offsetof(struct security_info
, root_directory
) },
1865 { "RootImage", "s", NULL
, offsetof(struct security_info
, root_image
) },
1866 { "SupplementaryGroups", "as", NULL
, offsetof(struct security_info
, supplementary_groups
) },
1867 { "SystemCallArchitectures", "as", NULL
, offsetof(struct security_info
, system_call_architectures
) },
1868 { "SystemCallFilter", "(as)", property_read_system_call_filter
, 0 },
1869 { "Type", "s", NULL
, offsetof(struct security_info
, type
) },
1870 { "UMask", "u", NULL
, offsetof(struct security_info
, _umask
) },
1871 { "User", "s", NULL
, offsetof(struct security_info
, user
) },
1875 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1876 _cleanup_free_
char *path
= NULL
;
1879 /* Note: this mangles *info on failure! */
1885 path
= unit_dbus_path_from_name(name
);
1889 r
= bus_map_all_properties(bus
,
1890 "org.freedesktop.systemd1",
1893 BUS_MAP_STRDUP
|BUS_MAP_BOOLEAN_AS_BOOL
,
1898 return log_error_errno(r
, "Failed to get unit properties: %s", bus_error_message(&error
, r
));
1900 if (!streq_ptr(info
->load_state
, "loaded")) {
1902 if (FLAGS_SET(flags
, ANALYZE_SECURITY_ONLY_LOADED
))
1903 return -EMEDIUMTYPE
;
1905 if (streq_ptr(info
->load_state
, "not-found"))
1906 log_error("Unit %s not found, cannot analyze.", name
);
1907 else if (streq_ptr(info
->load_state
, "masked"))
1908 log_error("Unit %s is masked, cannot analyze.", name
);
1910 log_error("Unit %s not loaded properly, cannot analyze.", name
);
1915 if (FLAGS_SET(flags
, ANALYZE_SECURITY_ONLY_LONG_RUNNING
) && streq_ptr(info
->type
, "oneshot"))
1916 return -EMEDIUMTYPE
;
1918 if (info
->private_devices
||
1919 info
->private_tmp
||
1920 info
->protect_control_groups
||
1921 info
->protect_kernel_tunables
||
1922 info
->protect_kernel_modules
||
1923 !streq_ptr(info
->protect_home
, "no") ||
1924 !streq_ptr(info
->protect_system
, "no") ||
1926 info
->private_mounts
= true;
1928 if (info
->protect_kernel_modules
)
1929 info
->capability_bounding_set
&= ~(UINT64_C(1) << CAP_SYS_MODULE
);
1931 if (info
->private_devices
)
1932 info
->capability_bounding_set
&= ~((UINT64_C(1) << CAP_MKNOD
) |
1933 (UINT64_C(1) << CAP_SYS_RAWIO
));
1938 static int analyze_security_one(sd_bus
*bus
, const char *name
, Table
* overview_table
, AnalyzeSecurityFlags flags
) {
1939 _cleanup_(security_info_free
) struct security_info info
= {
1940 .default_dependencies
= true,
1941 .capability_bounding_set
= UINT64_MAX
,
1942 .restrict_namespaces
= UINT64_MAX
,
1950 r
= acquire_security_info(bus
, name
, &info
, flags
);
1951 if (r
== -EMEDIUMTYPE
) /* Ignore this one because not loaded or Type is oneshot */
1956 r
= assess(&info
, overview_table
, flags
);
1963 int analyze_security(sd_bus
*bus
, char **units
, AnalyzeSecurityFlags flags
) {
1964 _cleanup_(table_unrefp
) Table
*overview_table
= NULL
;
1969 if (strv_length(units
) != 1) {
1970 overview_table
= table_new("UNIT", "EXPOSURE", "PREDICATE", "HAPPY");
1971 if (!overview_table
)
1975 if (strv_isempty(units
)) {
1976 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1977 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1978 _cleanup_strv_free_
char **list
= NULL
;
1979 size_t allocated
= 0, n
= 0;
1982 r
= sd_bus_call_method(
1984 "org.freedesktop.systemd1",
1985 "/org/freedesktop/systemd1",
1986 "org.freedesktop.systemd1.Manager",
1991 return log_error_errno(r
, "Failed to list units: %s", bus_error_message(&error
, r
));
1993 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
1995 return bus_log_parse_error(r
);
2001 r
= bus_parse_unit_info(reply
, &info
);
2003 return bus_log_parse_error(r
);
2007 if (!endswith(info
.id
, ".service"))
2010 if (!GREEDY_REALLOC(list
, allocated
, n
+2))
2013 copy
= strdup(info
.id
);
2023 flags
|= ANALYZE_SECURITY_SHORT
|ANALYZE_SECURITY_ONLY_LOADED
|ANALYZE_SECURITY_ONLY_LONG_RUNNING
;
2025 STRV_FOREACH(i
, list
) {
2026 r
= analyze_security_one(bus
, *i
, overview_table
, flags
);
2027 if (r
< 0 && ret
>= 0)
2034 STRV_FOREACH(i
, units
) {
2035 _cleanup_free_
char *mangled
= NULL
, *instance
= NULL
;
2038 if (!FLAGS_SET(flags
, ANALYZE_SECURITY_SHORT
) && i
!= units
) {
2043 r
= unit_name_mangle_with_suffix(*i
, 0, ".service", &mangled
);
2045 return log_error_errno(r
, "Failed to mangle unit name '%s': %m", *i
);
2047 if (!endswith(mangled
, ".service")) {
2048 log_error("Unit %s is not a service unit, refusing.", *i
);
2052 if (unit_name_is_valid(mangled
, UNIT_NAME_TEMPLATE
)) {
2053 r
= unit_name_replace_instance(mangled
, "test-instance", &instance
);
2061 r
= analyze_security_one(bus
, name
, overview_table
, flags
);
2062 if (r
< 0 && ret
>= 0)
2067 if (overview_table
) {
2068 if (!FLAGS_SET(flags
, ANALYZE_SECURITY_SHORT
)) {
2073 r
= table_print(overview_table
, stdout
);
2075 return log_error_errno(r
, "Failed to output table: %m");