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 "parse-util.h"
17 #include "path-util.h"
18 #include "pretty-print.h"
20 # include "seccomp-util.h"
23 #include "stdio-util.h"
25 #include "terminal-util.h"
27 #include "unit-name.h"
29 struct security_info
{
34 bool default_dependencies
;
36 uint64_t ambient_capabilities
;
37 uint64_t capability_bounding_set
;
40 char **supplementary_groups
;
43 bool ip_address_deny_all
;
44 bool ip_address_allow_localhost
;
45 bool ip_address_allow_other
;
48 bool lock_personality
;
49 bool memory_deny_write_execute
;
50 bool no_new_privileges
;
59 bool protect_control_groups
;
60 bool protect_kernel_modules
;
61 bool protect_kernel_tunables
;
68 bool restrict_address_family_inet
;
69 bool restrict_address_family_unix
;
70 bool restrict_address_family_netlink
;
71 bool restrict_address_family_packet
;
72 bool restrict_address_family_other
;
74 uint64_t restrict_namespaces
;
75 bool restrict_realtime
;
82 bool device_allow_non_empty
;
84 char **system_call_architectures
;
86 bool system_call_filter_whitelist
;
87 Set
*system_call_filter
;
92 struct security_assessor
{
94 const char *description_good
;
95 const char *description_bad
;
96 const char *description_na
;
100 int (*assess
)(const struct security_assessor
*a
, const struct security_info
*info
, const void *data
, uint64_t *ret_badness
, char **ret_description
);
103 bool default_dependencies_only
;
106 static void security_info_free(struct security_info
*i
) {
113 free(i
->fragment_path
);
117 free(i
->protect_home
);
118 free(i
->protect_system
);
120 free(i
->root_directory
);
123 free(i
->keyring_mode
);
124 free(i
->notify_access
);
126 free(i
->device_policy
);
128 strv_free(i
->supplementary_groups
);
129 strv_free(i
->system_call_architectures
);
131 set_free_free(i
->system_call_filter
);
134 static bool security_info_runs_privileged(const struct security_info
*i
) {
137 if (STRPTR_IN_SET(i
->user
, "0", "root"))
143 return isempty(i
->user
);
146 static int assess_bool(
147 const struct security_assessor
*a
,
148 const struct security_info
*info
,
150 uint64_t *ret_badness
,
151 char **ret_description
) {
153 const bool *b
= data
;
157 assert(ret_description
);
159 *ret_badness
= a
->parameter
? *b
: !*b
;
160 *ret_description
= NULL
;
165 static int assess_user(
166 const struct security_assessor
*a
,
167 const struct security_info
*info
,
169 uint64_t *ret_badness
,
170 char **ret_description
) {
172 _cleanup_free_
char *d
= NULL
;
176 assert(ret_description
);
178 if (streq_ptr(info
->user
, NOBODY_USER_NAME
)) {
179 d
= strdup("Service runs under as '" NOBODY_USER_NAME
"' user, which should not be used for services");
181 } else if (info
->dynamic_user
&& !STR_IN_SET(info
->user
, "0", "root")) {
182 d
= strdup("Service runs under a transient non-root user identity");
184 } else if (info
->user
&& !STR_IN_SET(info
->user
, "0", "root", "")) {
185 d
= strdup("Service runs under a static non-root user identity");
189 *ret_description
= NULL
;
197 *ret_description
= TAKE_PTR(d
);
202 static int assess_protect_home(
203 const struct security_assessor
*a
,
204 const struct security_info
*info
,
206 uint64_t *ret_badness
,
207 char **ret_description
) {
209 const char *description
;
215 assert(ret_description
);
218 description
= "Service has full access to home directories";
220 r
= parse_boolean(info
->protect_home
);
222 if (streq_ptr(info
->protect_home
, "read-only")) {
224 description
= "Service has read-only access to home directories";
225 } else if (streq_ptr(info
->protect_home
, "tmpfs")) {
227 description
= "Service has access to fake empty home directories";
231 description
= "Service has no access to home directories";
234 copy
= strdup(description
);
238 *ret_badness
= badness
;
239 *ret_description
= copy
;
244 static int assess_protect_system(
245 const struct security_assessor
*a
,
246 const struct security_info
*info
,
248 uint64_t *ret_badness
,
249 char **ret_description
) {
251 const char *description
;
257 assert(ret_description
);
260 description
= "Service has full access to the OS file hierarchy";
262 r
= parse_boolean(info
->protect_system
);
264 if (streq_ptr(info
->protect_system
, "full")) {
266 description
= "Service has very limited write access to the OS file hierarchy";
267 } else if (streq_ptr(info
->protect_system
, "strict")) {
269 description
= "Service has strict read-only access to the OS file hierarchy";
273 description
= "Service has limited write access to the OS file hierarchy";
276 copy
= strdup(description
);
280 *ret_badness
= badness
;
281 *ret_description
= copy
;
286 static int assess_root_directory(
287 const struct security_assessor
*a
,
288 const struct security_info
*info
,
290 uint64_t *ret_badness
,
291 char **ret_description
) {
294 assert(ret_description
);
297 (isempty(info
->root_directory
) ||
298 path_equal(info
->root_directory
, "/")) &&
299 (isempty(info
->root_image
) ||
300 path_equal(info
->root_image
, "/"));
301 *ret_description
= NULL
;
306 static int assess_capability_bounding_set(
307 const struct security_assessor
*a
,
308 const struct security_info
*info
,
310 uint64_t *ret_badness
,
311 char **ret_description
) {
314 assert(ret_description
);
316 *ret_badness
= !!(info
->capability_bounding_set
& a
->parameter
);
317 *ret_description
= NULL
;
322 static int assess_umask(
323 const struct security_assessor
*a
,
324 const struct security_info
*info
,
326 uint64_t *ret_badness
,
327 char **ret_description
) {
334 assert(ret_description
);
336 if (!FLAGS_SET(info
->_umask
, 0002)) {
337 d
= "Files created by service are world-writable by default";
339 } else if (!FLAGS_SET(info
->_umask
, 0004)) {
340 d
= "Files created by service are world-readable by default";
342 } else if (!FLAGS_SET(info
->_umask
, 0020)) {
343 d
= "Files created by service are group-writable by default";
345 } else if (!FLAGS_SET(info
->_umask
, 0040)) {
346 d
= "Files created by service are group-readable by default";
349 d
= "Files created by service are accessible only by service's own user by default";
358 *ret_description
= copy
;
363 static int assess_keyring_mode(
364 const struct security_assessor
*a
,
365 const struct security_info
*info
,
367 uint64_t *ret_badness
,
368 char **ret_description
) {
371 assert(ret_description
);
373 *ret_badness
= !streq_ptr(info
->keyring_mode
, "private");
374 *ret_description
= NULL
;
379 static int assess_notify_access(
380 const struct security_assessor
*a
,
381 const struct security_info
*info
,
383 uint64_t *ret_badness
,
384 char **ret_description
) {
387 assert(ret_description
);
389 *ret_badness
= streq_ptr(info
->notify_access
, "all");
390 *ret_description
= NULL
;
395 static int assess_remove_ipc(
396 const struct security_assessor
*a
,
397 const struct security_info
*info
,
399 uint64_t *ret_badness
,
400 char **ret_description
) {
403 assert(ret_description
);
405 if (security_info_runs_privileged(info
))
406 *ret_badness
= UINT64_MAX
;
408 *ret_badness
= !info
->remove_ipc
;
410 *ret_description
= NULL
;
414 static int assess_supplementary_groups(
415 const struct security_assessor
*a
,
416 const struct security_info
*info
,
418 uint64_t *ret_badness
,
419 char **ret_description
) {
422 assert(ret_description
);
424 if (security_info_runs_privileged(info
))
425 *ret_badness
= UINT64_MAX
;
427 *ret_badness
= !strv_isempty(info
->supplementary_groups
);
429 *ret_description
= NULL
;
433 static int assess_restrict_namespaces(
434 const struct security_assessor
*a
,
435 const struct security_info
*info
,
437 uint64_t *ret_badness
,
438 char **ret_description
) {
441 assert(ret_description
);
443 *ret_badness
= !!(info
->restrict_namespaces
& a
->parameter
);
444 *ret_description
= NULL
;
449 static int assess_system_call_architectures(
450 const struct security_assessor
*a
,
451 const struct security_info
*info
,
453 uint64_t *ret_badness
,
454 char **ret_description
) {
460 assert(ret_description
);
462 if (strv_isempty(info
->system_call_architectures
)) {
464 d
= strdup("Service may execute system calls with all ABIs");
465 } else if (strv_equal(info
->system_call_architectures
, STRV_MAKE("native"))) {
467 d
= strdup("Service may execute system calls only with native ABI");
470 d
= strdup("Service may execute system calls with multiple ABIs");
477 *ret_description
= d
;
484 static bool syscall_names_in_filter(Set
*s
, bool whitelist
, const SyscallFilterSet
*f
) {
487 NULSTR_FOREACH(syscall
, f
->value
) {
490 if (syscall
[0] == '@') {
491 const SyscallFilterSet
*g
;
492 assert_se(g
= syscall_filter_set_find(syscall
));
493 b
= syscall_names_in_filter(s
, whitelist
, g
);
497 /* Let's see if the system call actually exists on this platform, before complaining */
498 id
= seccomp_syscall_resolve_name(syscall
);
502 b
= set_contains(s
, syscall
);
505 if (whitelist
== b
) {
506 log_debug("Offending syscall filter item: %s", syscall
);
507 return true; /* bad! */
514 static int assess_system_call_filter(
515 const struct security_assessor
*a
,
516 const struct security_info
*info
,
518 uint64_t *ret_badness
,
519 char **ret_description
) {
521 const SyscallFilterSet
*f
;
528 assert(ret_description
);
530 assert(a
->parameter
< _SYSCALL_FILTER_SET_MAX
);
531 f
= syscall_filter_sets
+ a
->parameter
;
533 if (!info
->system_call_filter_whitelist
&& set_isempty(info
->system_call_filter
)) {
534 d
= strdup("Service does not filter system calls");
539 log_debug("Analyzing system call filter, checking against: %s", f
->name
);
540 bad
= syscall_names_in_filter(info
->system_call_filter
, info
->system_call_filter_whitelist
, f
);
541 log_debug("Result: %s", bad
? "bad" : "good");
543 if (info
->system_call_filter_whitelist
) {
545 (void) asprintf(&d
, "System call whitelist defined for service, and %s is included", f
->name
);
548 (void) asprintf(&d
, "System call whitelist defined for service, and %s is not included", f
->name
);
553 (void) asprintf(&d
, "System call blacklist defined for service, and %s is not included", f
->name
);
556 (void) asprintf(&d
, "System call blacklist defined for service, and %s is included", f
->name
);
566 *ret_description
= d
;
573 static int assess_ip_address_allow(
574 const struct security_assessor
*a
,
575 const struct security_info
*info
,
577 uint64_t *ret_badness
,
578 char **ret_description
) {
585 assert(ret_description
);
587 if (!info
->ip_address_deny_all
) {
588 d
= strdup("Service does not define an IP address whitelist");
590 } else if (info
->ip_address_allow_other
) {
591 d
= strdup("Service defines IP address whitelist with non-localhost entries");
593 } else if (info
->ip_address_allow_localhost
) {
594 d
= strdup("Service defines IP address whitelits with only localhost entries");
597 d
= strdup("Service blocks all IP address ranges");
605 *ret_description
= d
;
610 static int assess_device_allow(
611 const struct security_assessor
*a
,
612 const struct security_info
*info
,
614 uint64_t *ret_badness
,
615 char **ret_description
) {
622 assert(ret_description
);
624 if (STRPTR_IN_SET(info
->device_policy
, "strict", "closed")) {
626 if (info
->device_allow_non_empty
) {
627 d
= strdup("Service has a device ACL with some special devices");
630 d
= strdup("Service has a minimal device ACL");
634 d
= strdup("Service has no device ACL");
642 *ret_description
= d
;
647 static int assess_ambient_capabilities(
648 const struct security_assessor
*a
,
649 const struct security_info
*info
,
651 uint64_t *ret_badness
,
652 char **ret_description
) {
655 assert(ret_description
);
657 *ret_badness
= info
->ambient_capabilities
!= 0;
658 *ret_description
= NULL
;
663 static const struct security_assessor security_assessor_table
[] = {
665 .id
= "User=/DynamicUser=",
666 .description_bad
= "Service runs as root user",
667 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#User=",
670 .assess
= assess_user
,
673 .id
= "SupplementaryGroups=",
674 .description_good
= "Service has no supplementary groups",
675 .description_bad
= "Service runs with supplementary groups",
676 .description_na
= "Service runs as root, option does not matter",
677 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SupplementaryGroups=",
680 .assess
= assess_supplementary_groups
,
683 .id
= "PrivateDevices=",
684 .description_good
= "Service has no access to hardware devices",
685 .description_bad
= "Service potentially has access to hardware devices",
686 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateDevices=",
689 .assess
= assess_bool
,
690 .offset
= offsetof(struct security_info
, private_devices
),
693 .id
= "PrivateMounts=",
694 .description_good
= "Service cannot install system mounts",
695 .description_bad
= "Service may install system mounts",
696 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateMounts=",
699 .assess
= assess_bool
,
700 .offset
= offsetof(struct security_info
, private_mounts
),
703 .id
= "PrivateNetwork=",
704 .description_good
= "Service has no access to the host's network",
705 .description_bad
= "Service has access to the host's network",
706 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateNetwork=",
709 .assess
= assess_bool
,
710 .offset
= offsetof(struct security_info
, private_network
),
714 .description_good
= "Service has no access to other software's temporary files",
715 .description_bad
= "Service has access to other software's temporary files",
716 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateTmp=",
719 .assess
= assess_bool
,
720 .offset
= offsetof(struct security_info
, private_tmp
),
721 .default_dependencies_only
= true,
724 .id
= "PrivateUsers=",
725 .description_good
= "Service does not have access to other users",
726 .description_bad
= "Service has access to other users",
727 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateUsers=",
730 .assess
= assess_bool
,
731 .offset
= offsetof(struct security_info
, private_users
),
734 .id
= "ProtectControlGroups=",
735 .description_good
= "Service cannot modify the control group file system",
736 .description_bad
= "Service may modify to the control group file system",
737 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectControlGroups=",
740 .assess
= assess_bool
,
741 .offset
= offsetof(struct security_info
, protect_control_groups
),
744 .id
= "ProtectKernelModules=",
745 .description_good
= "Service cannot load or read kernel modules",
746 .description_bad
= "Service may load or read kernel modules",
747 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelModules=",
750 .assess
= assess_bool
,
751 .offset
= offsetof(struct security_info
, protect_kernel_modules
),
754 .id
= "ProtectKernelTunables=",
755 .description_good
= "Service cannot alter kernel tunables (/proc/sys, …)",
756 .description_bad
= "Service may alter kernel tunables",
757 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelTunables=",
760 .assess
= assess_bool
,
761 .offset
= offsetof(struct security_info
, protect_kernel_tunables
),
764 .id
= "ProtectHome=",
765 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHome=",
768 .assess
= assess_protect_home
,
769 .default_dependencies_only
= true,
772 .id
= "ProtectSystem=",
773 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectSystem=",
776 .assess
= assess_protect_system
,
777 .default_dependencies_only
= true,
780 .id
= "RootDirectory=/RootImage=",
781 .description_good
= "Service has its own root directory/image",
782 .description_bad
= "Service runs within the host's root directory",
783 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RootDirectory=",
786 .assess
= assess_root_directory
,
787 .default_dependencies_only
= true,
790 .id
= "LockPersonality=",
791 .description_good
= "Service cannot change ABI personality",
792 .description_bad
= "Service may change ABI personality",
793 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#LockPersonality=",
796 .assess
= assess_bool
,
797 .offset
= offsetof(struct security_info
, lock_personality
),
800 .id
= "MemoryDenyWriteExecute=",
801 .description_good
= "Service cannot create writable executable memory mappings",
802 .description_bad
= "Service may create writable executable memory mappings",
803 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#MemoryDenyWriteExecute=",
806 .assess
= assess_bool
,
807 .offset
= offsetof(struct security_info
, memory_deny_write_execute
),
810 .id
= "NoNewPrivileges=",
811 .description_good
= "Service processes cannot acquire new privileges",
812 .description_bad
= "Service processes may acquire new privileges",
813 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NoNewPrivileges=",
816 .assess
= assess_bool
,
817 .offset
= offsetof(struct security_info
, no_new_privileges
),
820 .id
= "CapabilityBoundingSet=~CAP_SYS_ADMIN",
821 .description_good
= "Service has no administrator privileges",
822 .description_bad
= "Service has administrator privileges",
823 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
826 .assess
= assess_capability_bounding_set
,
827 .parameter
= UINT64_C(1) << CAP_SYS_ADMIN
,
830 .id
= "CapabilityBoundingSet=~CAP_SET(UID|GID|PCAP)",
831 .description_good
= "Service cannot change UID/GID identities/capabilities",
832 .description_bad
= "Service may change UID/GID identities/capabilities",
833 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
836 .assess
= assess_capability_bounding_set
,
837 .parameter
= (UINT64_C(1) << CAP_SETUID
)|
838 (UINT64_C(1) << CAP_SETGID
)|
839 (UINT64_C(1) << CAP_SETPCAP
),
842 .id
= "CapabilityBoundingSet=~CAP_SYS_PTRACE",
843 .description_good
= "Service has no ptrace() debugging abilities",
844 .description_bad
= "Service has ptrace() debugging abilities",
845 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
848 .assess
= assess_capability_bounding_set
,
849 .parameter
= (UINT64_C(1) << CAP_SYS_PTRACE
),
852 .id
= "CapabilityBoundingSet=~CAP_SYS_TIME",
853 .description_good
= "Service processes cannot change the system clock",
854 .description_bad
= "Service processes may change the system clock",
855 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
858 .assess
= assess_capability_bounding_set
,
859 .parameter
= UINT64_C(1) << CAP_SYS_TIME
,
862 .id
= "CapabilityBoundingSet=~CAP_NET_ADMIN",
863 .description_good
= "Service has no network configuration privileges",
864 .description_bad
= "Service has network configuration privileges",
865 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
868 .assess
= assess_capability_bounding_set
,
869 .parameter
= (UINT64_C(1) << CAP_NET_ADMIN
),
872 .id
= "CapabilityBoundingSet=~CAP_RAWIO",
873 .description_good
= "Service has no raw I/O access",
874 .description_bad
= "Service has raw I/O access",
875 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
878 .assess
= assess_capability_bounding_set
,
879 .parameter
= (UINT64_C(1) << CAP_SYS_RAWIO
),
882 .id
= "CapabilityBoundingSet=~CAP_SYS_MODULE",
883 .description_good
= "Service cannot load kernel modules",
884 .description_bad
= "Service may load kernel modules",
885 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
888 .assess
= assess_capability_bounding_set
,
889 .parameter
= (UINT64_C(1) << CAP_SYS_MODULE
),
892 .id
= "CapabilityBoundingSet=~CAP_AUDIT_*",
893 .description_good
= "Service has no audit subsystem access",
894 .description_bad
= "Service has audit subsystem access",
895 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
898 .assess
= assess_capability_bounding_set
,
899 .parameter
= (UINT64_C(1) << CAP_AUDIT_CONTROL
) |
900 (UINT64_C(1) << CAP_AUDIT_READ
) |
901 (UINT64_C(1) << CAP_AUDIT_WRITE
),
904 .id
= "CapabilityBoundingSet=~CAP_SYSLOG",
905 .description_good
= "Service has no access to kernel logging",
906 .description_bad
= "Service has access to kernel logging",
907 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
910 .assess
= assess_capability_bounding_set
,
911 .parameter
= (UINT64_C(1) << CAP_SYSLOG
),
914 .id
= "CapabilityBoundingSet=~CAP_SYS_(NICE|RESOURCE)",
915 .description_good
= "Service has no privileges to change resource use parameters",
916 .description_bad
= "Service has privileges to change resource use parameters",
917 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
920 .assess
= assess_capability_bounding_set
,
921 .parameter
= (UINT64_C(1) << CAP_SYS_NICE
) |
922 (UINT64_C(1) << CAP_SYS_RESOURCE
),
925 .id
= "CapabilityBoundingSet=~CAP_MKNOD",
926 .description_good
= "Service cannot create device nodes",
927 .description_bad
= "Service may create device nodes",
928 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
931 .assess
= assess_capability_bounding_set
,
932 .parameter
= (UINT64_C(1) << CAP_MKNOD
),
935 .id
= "CapabilityBoundingSet=~CAP_(CHOWN|FSETID|SETFCAP)",
936 .description_good
= "Service cannot change file ownership/access mode/capabilities",
937 .description_bad
= "Service may change file ownership/access mode/capabilities unrestricted",
938 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
941 .assess
= assess_capability_bounding_set
,
942 .parameter
= (UINT64_C(1) << CAP_CHOWN
) |
943 (UINT64_C(1) << CAP_FSETID
) |
944 (UINT64_C(1) << CAP_SETFCAP
),
947 .id
= "CapabilityBoundingSet=~CAP_(DAC_*|FOWNER|IPC_OWNER)",
948 .description_good
= "Service cannot override UNIX file/IPC permission checks",
949 .description_bad
= "Service may override UNIX file/IPC permission checks",
950 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
953 .assess
= assess_capability_bounding_set
,
954 .parameter
= (UINT64_C(1) << CAP_DAC_OVERRIDE
) |
955 (UINT64_C(1) << CAP_DAC_READ_SEARCH
) |
956 (UINT64_C(1) << CAP_FOWNER
) |
957 (UINT64_C(1) << CAP_IPC_OWNER
),
960 .id
= "CapabilityBoundingSet=~CAP_KILL",
961 .description_good
= "Service cannot send UNIX signals to arbitrary processes",
962 .description_bad
= "Service may send UNIX signals to arbitrary processes",
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_KILL
),
970 .id
= "CapabilityBoundingSet=~CAP_NET_(BIND_SERVICE|BROADCAST|RAW)",
971 .description_good
= "Service has no elevated networking privileges",
972 .description_bad
= "Service has elevated networking privileges",
973 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
976 .assess
= assess_capability_bounding_set
,
977 .parameter
= (UINT64_C(1) << CAP_NET_BIND_SERVICE
) |
978 (UINT64_C(1) << CAP_NET_BROADCAST
) |
979 (UINT64_C(1) << CAP_NET_RAW
),
982 .id
= "CapabilityBoundingSet=~CAP_SYS_BOOT",
983 .description_good
= "Service cannot issue reboot()",
984 .description_bad
= "Service may issue reboot()",
985 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
988 .assess
= assess_capability_bounding_set
,
989 .parameter
= (UINT64_C(1) << CAP_SYS_BOOT
),
992 .id
= "CapabilityBoundingSet=~CAP_MAC_*",
993 .description_good
= "Service cannot adjust SMACK MAC",
994 .description_bad
= "Service may adjust SMACK MAC",
995 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
998 .assess
= assess_capability_bounding_set
,
999 .parameter
= (UINT64_C(1) << CAP_MAC_ADMIN
)|
1000 (UINT64_C(1) << CAP_MAC_OVERRIDE
),
1003 .id
= "CapabilityBoundingSet=~CAP_LINUX_IMMUTABLE",
1004 .description_good
= "Service cannot mark files immutable",
1005 .description_bad
= "Service may mark files immutable",
1006 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1009 .assess
= assess_capability_bounding_set
,
1010 .parameter
= (UINT64_C(1) << CAP_LINUX_IMMUTABLE
),
1013 .id
= "CapabilityBoundingSet=~CAP_IPC_LOCK",
1014 .description_good
= "Service cannot lock memory into RAM",
1015 .description_bad
= "Service may lock memory into RAM",
1016 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1019 .assess
= assess_capability_bounding_set
,
1020 .parameter
= (UINT64_C(1) << CAP_IPC_LOCK
),
1023 .id
= "CapabilityBoundingSet=~CAP_SYS_CHROOT",
1024 .description_good
= "Service cannot issue chroot()",
1025 .description_bad
= "Service may issue chroot()",
1026 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1029 .assess
= assess_capability_bounding_set
,
1030 .parameter
= (UINT64_C(1) << CAP_SYS_CHROOT
),
1033 .id
= "CapabilityBoundingSet=~CAP_BLOCK_SUSPEND",
1034 .description_good
= "Service cannot establish wake locks",
1035 .description_bad
= "Service may establish wake locks",
1036 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1039 .assess
= assess_capability_bounding_set
,
1040 .parameter
= (UINT64_C(1) << CAP_BLOCK_SUSPEND
),
1043 .id
= "CapabilityBoundingSet=~CAP_WAKE_ALARM",
1044 .description_good
= "Service cannot program timers that wake up the system",
1045 .description_bad
= "Service may program timers that wake up the system",
1046 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1049 .assess
= assess_capability_bounding_set
,
1050 .parameter
= (UINT64_C(1) << CAP_WAKE_ALARM
),
1053 .id
= "CapabilityBoundingSet=~CAP_LEASE",
1054 .description_good
= "Service cannot create file leases",
1055 .description_bad
= "Service may create file leases",
1056 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1059 .assess
= assess_capability_bounding_set
,
1060 .parameter
= (UINT64_C(1) << CAP_LEASE
),
1063 .id
= "CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG",
1064 .description_good
= "Service cannot issue vhangup()",
1065 .description_bad
= "Service may issue vhangup()",
1066 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1069 .assess
= assess_capability_bounding_set
,
1070 .parameter
= (UINT64_C(1) << CAP_SYS_TTY_CONFIG
),
1073 .id
= "CapabilityBoundingSet=~CAP_SYS_PACCT",
1074 .description_good
= "Service cannot use acct()",
1075 .description_bad
= "Service may use acct()",
1076 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1079 .assess
= assess_capability_bounding_set
,
1080 .parameter
= (UINT64_C(1) << CAP_SYS_PACCT
),
1084 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#UMask=",
1087 .assess
= assess_umask
,
1090 .id
= "KeyringMode=",
1091 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#KeyringMode=",
1092 .description_good
= "Service doesn't share key material with other services",
1093 .description_bad
= "Service shares key material with other service",
1096 .assess
= assess_keyring_mode
,
1099 .id
= "NotifyAccess=",
1100 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NotifyAccess=",
1101 .description_good
= "Service child processes cannot alter service state",
1102 .description_bad
= "Service child processes may alter service state",
1105 .assess
= assess_notify_access
,
1109 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RemoveIPC=",
1110 .description_good
= "Service user cannot leave SysV IPC objects around",
1111 .description_bad
= "Service user may leave SysV IPC objects around",
1112 .description_na
= "Service runs as root, option does not apply",
1115 .assess
= assess_remove_ipc
,
1116 .offset
= offsetof(struct security_info
, remove_ipc
),
1120 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Delegate=",
1121 .description_good
= "Service does not maintain its own delegated control group subtree",
1122 .description_bad
= "Service maintains its own delegated control group subtree",
1125 .assess
= assess_bool
,
1126 .offset
= offsetof(struct security_info
, delegate
),
1127 .parameter
= true, /* invert! */
1130 .id
= "RestrictRealtime=",
1131 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictRealtime=",
1132 .description_good
= "Service realtime scheduling access is restricted",
1133 .description_bad
= "Service may acquire realtime scheduling",
1136 .assess
= assess_bool
,
1137 .offset
= offsetof(struct security_info
, restrict_realtime
),
1140 .id
= "RestrictNamespaces=~CLONE_NEWUSER",
1141 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1142 .description_good
= "Service cannot create user namespaces",
1143 .description_bad
= "Service may create user namespaces",
1146 .assess
= assess_restrict_namespaces
,
1147 .parameter
= CLONE_NEWUSER
,
1150 .id
= "RestrictNamespaces=~CLONE_NEWNS",
1151 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1152 .description_good
= "Service cannot create file system namespaces",
1153 .description_bad
= "Service may create file system namespaces",
1156 .assess
= assess_restrict_namespaces
,
1157 .parameter
= CLONE_NEWNS
,
1160 .id
= "RestrictNamespaces=~CLONE_NEWIPC",
1161 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1162 .description_good
= "Service cannot create IPC namespaces",
1163 .description_bad
= "Service may create IPC namespaces",
1166 .assess
= assess_restrict_namespaces
,
1167 .parameter
= CLONE_NEWIPC
,
1170 .id
= "RestrictNamespaces=~CLONE_NEWPID",
1171 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1172 .description_good
= "Service cannot create process namespaces",
1173 .description_bad
= "Service may create process namespaces",
1176 .assess
= assess_restrict_namespaces
,
1177 .parameter
= CLONE_NEWPID
,
1180 .id
= "RestrictNamespaces=~CLONE_NEWCGROUP",
1181 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1182 .description_good
= "Service cannot create cgroup namespaces",
1183 .description_bad
= "Service may create cgroup namespaces",
1186 .assess
= assess_restrict_namespaces
,
1187 .parameter
= CLONE_NEWCGROUP
,
1190 .id
= "RestrictNamespaces=~CLONE_NEWNET",
1191 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1192 .description_good
= "Service cannot create network namespaces",
1193 .description_bad
= "Service may create network namespaces",
1196 .assess
= assess_restrict_namespaces
,
1197 .parameter
= CLONE_NEWNET
,
1200 .id
= "RestrictNamespaces=~CLONE_NEWUTS",
1201 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1202 .description_good
= "Service cannot create hostname namespaces",
1203 .description_bad
= "Service may create hostname namespaces",
1206 .assess
= assess_restrict_namespaces
,
1207 .parameter
= CLONE_NEWUTS
,
1210 .id
= "RestrictAddressFamilies=~AF_(INET|INET6)",
1211 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1212 .description_good
= "Service cannot allocate Internet sockets",
1213 .description_bad
= "Service may allocate Internet sockets",
1216 .assess
= assess_bool
,
1217 .offset
= offsetof(struct security_info
, restrict_address_family_inet
),
1220 .id
= "RestrictAddressFamilies=~AF_UNIX",
1221 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1222 .description_good
= "Service cannot allocate local sockets",
1223 .description_bad
= "Service may allocate local sockets",
1226 .assess
= assess_bool
,
1227 .offset
= offsetof(struct security_info
, restrict_address_family_unix
),
1230 .id
= "RestrictAddressFamilies=~AF_NETLINK",
1231 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1232 .description_good
= "Service cannot allocate netlink sockets",
1233 .description_bad
= "Service may allocate netlink sockets",
1236 .assess
= assess_bool
,
1237 .offset
= offsetof(struct security_info
, restrict_address_family_netlink
),
1240 .id
= "RestrictAddressFamilies=~AF_PACKET",
1241 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1242 .description_good
= "Service cannot allocate packet sockets",
1243 .description_bad
= "Service may allocate packet sockets",
1246 .assess
= assess_bool
,
1247 .offset
= offsetof(struct security_info
, restrict_address_family_packet
),
1250 .id
= "RestrictAddressFamilies=~…",
1251 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1252 .description_good
= "Service cannot allocate exotic sockets",
1253 .description_bad
= "Service may allocate exotic sockets",
1256 .assess
= assess_bool
,
1257 .offset
= offsetof(struct security_info
, restrict_address_family_other
),
1260 .id
= "SystemCallArchitectures=",
1261 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallArchitectures=",
1264 .assess
= assess_system_call_architectures
,
1268 .id
= "SystemCallFilter=~@swap",
1269 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1272 .assess
= assess_system_call_filter
,
1273 .parameter
= SYSCALL_FILTER_SET_SWAP
,
1276 .id
= "SystemCallFilter=~@obsolete",
1277 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1280 .assess
= assess_system_call_filter
,
1281 .parameter
= SYSCALL_FILTER_SET_OBSOLETE
,
1284 .id
= "SystemCallFilter=~@clock",
1285 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1288 .assess
= assess_system_call_filter
,
1289 .parameter
= SYSCALL_FILTER_SET_CLOCK
,
1292 .id
= "SystemCallFilter=~@cpu-emulation",
1293 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1296 .assess
= assess_system_call_filter
,
1297 .parameter
= SYSCALL_FILTER_SET_CPU_EMULATION
,
1300 .id
= "SystemCallFilter=~@debug",
1301 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1304 .assess
= assess_system_call_filter
,
1305 .parameter
= SYSCALL_FILTER_SET_DEBUG
,
1308 .id
= "SystemCallFilter=~@mount",
1309 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1312 .assess
= assess_system_call_filter
,
1313 .parameter
= SYSCALL_FILTER_SET_MOUNT
,
1316 .id
= "SystemCallFilter=~@module",
1317 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1320 .assess
= assess_system_call_filter
,
1321 .parameter
= SYSCALL_FILTER_SET_MODULE
,
1324 .id
= "SystemCallFilter=~@raw-io",
1325 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1328 .assess
= assess_system_call_filter
,
1329 .parameter
= SYSCALL_FILTER_SET_RAW_IO
,
1332 .id
= "SystemCallFilter=~@reboot",
1333 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1336 .assess
= assess_system_call_filter
,
1337 .parameter
= SYSCALL_FILTER_SET_REBOOT
,
1340 .id
= "SystemCallFilter=~@privileged",
1341 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1344 .assess
= assess_system_call_filter
,
1345 .parameter
= SYSCALL_FILTER_SET_PRIVILEGED
,
1348 .id
= "SystemCallFilter=~@resources",
1349 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1352 .assess
= assess_system_call_filter
,
1353 .parameter
= SYSCALL_FILTER_SET_RESOURCES
,
1357 .id
= "IPAddressDeny=",
1358 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#IPAddressDeny=",
1361 .assess
= assess_ip_address_allow
,
1364 .id
= "DeviceAllow=",
1365 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#DeviceAllow=",
1368 .assess
= assess_device_allow
,
1371 .id
= "AmbientCapabilities=",
1372 .url
= "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#AmbientCapabilities=",
1373 .description_good
= "Service process does not receive ambient capabilities",
1374 .description_bad
= "Service process receives ambient capabilities",
1377 .assess
= assess_ambient_capabilities
,
1381 static int assess(const struct security_info
*info
, Table
*overview_table
, AnalyzeSecurityFlags flags
) {
1382 static const struct {
1386 SpecialGlyph smiley
;
1387 } badness_table
[] = {
1388 { 100, "DANGEROUS", ANSI_HIGHLIGHT_RED
, SPECIAL_GLYPH_DEPRESSED_SMILEY
},
1389 { 90, "UNSAFE", ANSI_HIGHLIGHT_RED
, SPECIAL_GLYPH_UNHAPPY_SMILEY
},
1390 { 75, "EXPOSED", ANSI_HIGHLIGHT_YELLOW
, SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY
},
1391 { 50, "MEDIUM", NULL
, SPECIAL_GLYPH_NEUTRAL_SMILEY
},
1392 { 10, "OK", ANSI_HIGHLIGHT_GREEN
, SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY
},
1393 { 1, "SAFE", ANSI_HIGHLIGHT_GREEN
, SPECIAL_GLYPH_HAPPY_SMILEY
},
1394 { 0, "PERFECT", ANSI_HIGHLIGHT_GREEN
, SPECIAL_GLYPH_ECSTATIC_SMILEY
},
1397 uint64_t badness_sum
= 0, weight_sum
= 0, exposure
;
1398 _cleanup_(table_unrefp
) Table
*details_table
= NULL
;
1402 if (!FLAGS_SET(flags
, ANALYZE_SECURITY_SHORT
)) {
1403 details_table
= table_new(" ", "name", "description", "weight", "badness", "range", "exposure");
1407 (void) table_set_sort(details_table
, 3, 1, (size_t) -1);
1408 (void) table_set_reverse(details_table
, 3, true);
1410 if (getenv_bool("SYSTEMD_ANALYZE_DEBUG") <= 0)
1411 (void) table_set_display(details_table
, 0, 1, 2, 6, (size_t) -1);
1414 for (i
= 0; i
< ELEMENTSOF(security_assessor_table
); i
++) {
1415 const struct security_assessor
*a
= security_assessor_table
+ i
;
1416 _cleanup_free_
char *d
= NULL
;
1420 data
= (uint8_t*) info
+ a
->offset
;
1422 if (a
->default_dependencies_only
&& !info
->default_dependencies
) {
1423 badness
= UINT64_MAX
;
1424 d
= strdup("Service runs in special boot phase, option does not apply");
1428 r
= a
->assess(a
, info
, data
, &badness
, &d
);
1433 assert(a
->range
> 0);
1435 if (badness
!= UINT64_MAX
) {
1436 assert(badness
<= a
->range
);
1438 badness_sum
+= DIV_ROUND_UP(badness
* a
->weight
, a
->range
);
1439 weight_sum
+= a
->weight
;
1442 if (details_table
) {
1443 const char *checkmark
, *description
, *color
= NULL
;
1446 if (badness
== UINT64_MAX
) {
1448 description
= a
->description_na
;
1450 } else if (badness
== a
->range
) {
1451 checkmark
= special_glyph(SPECIAL_GLYPH_CROSS_MARK
);
1452 description
= a
->description_bad
;
1453 color
= ansi_highlight_red();
1454 } else if (badness
== 0) {
1455 checkmark
= special_glyph(SPECIAL_GLYPH_CHECK_MARK
);
1456 description
= a
->description_good
;
1457 color
= ansi_highlight_green();
1459 checkmark
= special_glyph(SPECIAL_GLYPH_CROSS_MARK
);
1461 color
= ansi_highlight_red();
1467 r
= table_add_cell_full(details_table
, &cell
, TABLE_STRING
, checkmark
, 1, 1, 0, 0, 0);
1469 return log_error_errno(r
, "Failed to add cell to table: %m");
1471 (void) table_set_color(details_table
, cell
, color
);
1473 r
= table_add_cell(details_table
, &cell
, TABLE_STRING
, a
->id
);
1475 return log_error_errno(r
, "Failed to add cell to table: %m");
1477 (void) table_set_url(details_table
, cell
, a
->url
);
1479 r
= table_add_cell(details_table
, NULL
, TABLE_STRING
, description
);
1481 return log_error_errno(r
, "Failed to add cell to table: %m");
1483 r
= table_add_cell(details_table
, &cell
, TABLE_UINT64
, &a
->weight
);
1485 return log_error_errno(r
, "Failed to add cell to table: %m");
1486 (void) table_set_align_percent(details_table
, cell
, 100);
1488 r
= table_add_cell(details_table
, &cell
, TABLE_UINT64
, &badness
);
1490 return log_error_errno(r
, "Failed to add cell to table: %m");
1491 (void) table_set_align_percent(details_table
, cell
, 100);
1493 r
= table_add_cell(details_table
, &cell
, TABLE_UINT64
, &a
->range
);
1495 return log_error_errno(r
, "Failed to add cell to table: %m");
1496 (void) table_set_align_percent(details_table
, cell
, 100);
1498 r
= table_add_cell(details_table
, &cell
, TABLE_EMPTY
, NULL
);
1500 return log_error_errno(r
, "Failed to add cell to table: %m");
1501 (void) table_set_align_percent(details_table
, cell
, 100);
1505 if (details_table
) {
1508 for (row
= 1; row
< table_get_rows(details_table
); row
++) {
1509 char buf
[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1];
1510 const uint64_t *weight
, *badness
, *range
;
1514 assert_se(weight
= table_get_at(details_table
, row
, 3));
1515 assert_se(badness
= table_get_at(details_table
, row
, 4));
1516 assert_se(range
= table_get_at(details_table
, row
, 5));
1518 if (*badness
== UINT64_MAX
|| *badness
== 0)
1521 assert_se(cell
= table_get_cell(details_table
, row
, 6));
1523 x
= DIV_ROUND_UP(DIV_ROUND_UP(*badness
* *weight
* 100U, *range
), weight_sum
);
1524 xsprintf(buf
, "%" PRIu64
".%" PRIu64
, x
/ 10, x
% 10);
1526 r
= table_update(details_table
, cell
, TABLE_STRING
, buf
);
1528 return log_error_errno(r
, "Failed to update cell in table: %m");
1531 r
= table_print(details_table
, stdout
);
1533 return log_error_errno(r
, "Failed to output table: %m");
1536 assert(weight_sum
> 0);
1537 exposure
= DIV_ROUND_UP(badness_sum
* 100U, weight_sum
);
1539 for (i
= 0; i
< ELEMENTSOF(badness_table
); i
++)
1540 if (exposure
>= badness_table
[i
].exposure
)
1543 assert(i
< ELEMENTSOF(badness_table
));
1545 if (details_table
) {
1546 _cleanup_free_
char *clickable
= NULL
;
1549 /* If we shall output the details table, also print the brief summary underneath */
1551 if (info
->fragment_path
) {
1552 r
= terminal_urlify_path(info
->fragment_path
, info
->id
, &clickable
);
1560 printf("\n%s %sOverall exposure level for %s%s: %s%" PRIu64
".%" PRIu64
" %s%s %s\n",
1561 special_glyph(SPECIAL_GLYPH_ARROW
),
1565 colors_enabled() ? strempty(badness_table
[i
].color
) : "",
1566 exposure
/ 10, exposure
% 10,
1567 badness_table
[i
].name
,
1569 special_glyph(badness_table
[i
].smiley
));
1574 if (overview_table
) {
1575 char buf
[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1];
1578 r
= table_add_cell(overview_table
, &cell
, TABLE_STRING
, info
->id
);
1580 return log_error_errno(r
, "Failed to add cell to table: %m");
1581 if (info
->fragment_path
) {
1582 _cleanup_free_
char *url
= NULL
;
1584 r
= file_url_from_path(info
->fragment_path
, &url
);
1586 return log_error_errno(r
, "Failed to generate URL from path: %m");
1588 (void) table_set_url(overview_table
, cell
, url
);
1591 xsprintf(buf
, "%" PRIu64
".%" PRIu64
, exposure
/ 10, exposure
% 10);
1592 r
= table_add_cell(overview_table
, &cell
, TABLE_STRING
, buf
);
1594 return log_error_errno(r
, "Failed to add cell to table: %m");
1595 (void) table_set_align_percent(overview_table
, cell
, 100);
1597 r
= table_add_cell(overview_table
, &cell
, TABLE_STRING
, badness_table
[i
].name
);
1599 return log_error_errno(r
, "Failed to add cell to table: %m");
1600 (void) table_set_color(overview_table
, cell
, strempty(badness_table
[i
].color
));
1602 r
= table_add_cell(overview_table
, NULL
, TABLE_STRING
, special_glyph(badness_table
[i
].smiley
));
1604 return log_error_errno(r
, "Failed to add cell to table: %m");
1610 static int property_read_restrict_address_families(
1614 sd_bus_error
*error
,
1617 struct security_info
*info
= userdata
;
1624 r
= sd_bus_message_enter_container(m
, 'r', "bas");
1628 r
= sd_bus_message_read(m
, "b", &whitelist
);
1632 info
->restrict_address_family_inet
=
1633 info
->restrict_address_family_unix
=
1634 info
->restrict_address_family_netlink
=
1635 info
->restrict_address_family_packet
=
1636 info
->restrict_address_family_other
= whitelist
;
1638 r
= sd_bus_message_enter_container(m
, 'a', "s");
1645 r
= sd_bus_message_read(m
, "s", &name
);
1651 if (STR_IN_SET(name
, "AF_INET", "AF_INET6"))
1652 info
->restrict_address_family_inet
= !whitelist
;
1653 else if (streq(name
, "AF_UNIX"))
1654 info
->restrict_address_family_unix
= !whitelist
;
1655 else if (streq(name
, "AF_NETLINK"))
1656 info
->restrict_address_family_netlink
= !whitelist
;
1657 else if (streq(name
, "AF_PACKET"))
1658 info
->restrict_address_family_packet
= !whitelist
;
1660 info
->restrict_address_family_other
= !whitelist
;
1663 r
= sd_bus_message_exit_container(m
);
1667 return sd_bus_message_exit_container(m
);
1670 static int property_read_system_call_filter(
1674 sd_bus_error
*error
,
1677 struct security_info
*info
= userdata
;
1684 r
= sd_bus_message_enter_container(m
, 'r', "bas");
1688 r
= sd_bus_message_read(m
, "b", &whitelist
);
1692 info
->system_call_filter_whitelist
= whitelist
;
1694 r
= sd_bus_message_enter_container(m
, 'a', "s");
1701 r
= sd_bus_message_read(m
, "s", &name
);
1707 r
= set_ensure_allocated(&info
->system_call_filter
, &string_hash_ops
);
1711 r
= set_put_strdup(info
->system_call_filter
, name
);
1716 r
= sd_bus_message_exit_container(m
);
1720 return sd_bus_message_exit_container(m
);
1723 static int property_read_ip_address_allow(
1727 sd_bus_error
*error
,
1730 struct security_info
*info
= userdata
;
1731 bool deny_ipv4
= false, deny_ipv6
= false;
1738 r
= sd_bus_message_enter_container(m
, 'a', "(iayu)");
1748 r
= sd_bus_message_enter_container(m
, 'r', "iayu");
1754 r
= sd_bus_message_read(m
, "i", &family
);
1758 r
= sd_bus_message_read_array(m
, 'y', &data
, &size
);
1762 r
= sd_bus_message_read(m
, "u", &prefixlen
);
1766 r
= sd_bus_message_exit_container(m
);
1770 if (streq(member
, "IPAddressAllow")) {
1771 union in_addr_union u
;
1773 if (family
== AF_INET
&& size
== 4 && prefixlen
== 8)
1774 memcpy(&u
.in
, data
, size
);
1775 else if (family
== AF_INET6
&& size
== 16 && prefixlen
== 128)
1776 memcpy(&u
.in6
, data
, size
);
1778 info
->ip_address_allow_other
= true;
1782 if (in_addr_is_localhost(family
, &u
))
1783 info
->ip_address_allow_localhost
= true;
1785 info
->ip_address_allow_other
= true;
1787 assert(streq(member
, "IPAddressDeny"));
1789 if (family
== AF_INET
&& size
== 4 && prefixlen
== 0)
1791 else if (family
== AF_INET6
&& size
== 16 && prefixlen
== 0)
1796 info
->ip_address_deny_all
= deny_ipv4
&& deny_ipv6
;
1798 return sd_bus_message_exit_container(m
);
1801 static int property_read_device_allow(
1805 sd_bus_error
*error
,
1808 struct security_info
*info
= userdata
;
1816 r
= sd_bus_message_enter_container(m
, 'a', "(ss)");
1821 const char *name
, *policy
;
1823 r
= sd_bus_message_read(m
, "(ss)", &name
, &policy
);
1832 info
->device_allow_non_empty
= n
> 0;
1834 return sd_bus_message_exit_container(m
);
1837 static int acquire_security_info(sd_bus
*bus
, const char *name
, struct security_info
*info
, AnalyzeSecurityFlags flags
) {
1839 static const struct bus_properties_map security_map
[] = {
1840 { "AmbientCapabilities", "t", NULL
, offsetof(struct security_info
, ambient_capabilities
) },
1841 { "CapabilityBoundingSet", "t", NULL
, offsetof(struct security_info
, capability_bounding_set
) },
1842 { "DefaultDependencies", "b", NULL
, offsetof(struct security_info
, default_dependencies
) },
1843 { "Delegate", "b", NULL
, offsetof(struct security_info
, delegate
) },
1844 { "DeviceAllow", "a(ss)", property_read_device_allow
, 0 },
1845 { "DevicePolicy", "s", NULL
, offsetof(struct security_info
, device_policy
) },
1846 { "DynamicUser", "b", NULL
, offsetof(struct security_info
, dynamic_user
) },
1847 { "FragmentPath", "s", NULL
, offsetof(struct security_info
, fragment_path
) },
1848 { "IPAddressAllow", "a(iayu)", property_read_ip_address_allow
, 0 },
1849 { "IPAddressDeny", "a(iayu)", property_read_ip_address_allow
, 0 },
1850 { "Id", "s", NULL
, offsetof(struct security_info
, id
) },
1851 { "KeyringMode", "s", NULL
, offsetof(struct security_info
, keyring_mode
) },
1852 { "LoadState", "s", NULL
, offsetof(struct security_info
, load_state
) },
1853 { "LockPersonality", "b", NULL
, offsetof(struct security_info
, lock_personality
) },
1854 { "MemoryDenyWriteExecute", "b", NULL
, offsetof(struct security_info
, memory_deny_write_execute
) },
1855 { "NoNewPrivileges", "b", NULL
, offsetof(struct security_info
, no_new_privileges
) },
1856 { "NotifyAccess", "s", NULL
, offsetof(struct security_info
, notify_access
) },
1857 { "PrivateDevices", "b", NULL
, offsetof(struct security_info
, private_devices
) },
1858 { "PrivateMounts", "b", NULL
, offsetof(struct security_info
, private_mounts
) },
1859 { "PrivateNetwork", "b", NULL
, offsetof(struct security_info
, private_network
) },
1860 { "PrivateTmp", "b", NULL
, offsetof(struct security_info
, private_tmp
) },
1861 { "PrivateUsers", "b", NULL
, offsetof(struct security_info
, private_users
) },
1862 { "ProtectControlGroups", "b", NULL
, offsetof(struct security_info
, protect_control_groups
) },
1863 { "ProtectHome", "s", NULL
, offsetof(struct security_info
, protect_home
) },
1864 { "ProtectKernelModules", "b", NULL
, offsetof(struct security_info
, protect_kernel_modules
) },
1865 { "ProtectKernelTunables", "b", NULL
, offsetof(struct security_info
, protect_kernel_tunables
) },
1866 { "ProtectSystem", "s", NULL
, offsetof(struct security_info
, protect_system
) },
1867 { "RemoveIPC", "b", NULL
, offsetof(struct security_info
, remove_ipc
) },
1868 { "RestrictAddressFamilies", "(bas)", property_read_restrict_address_families
, 0 },
1869 { "RestrictNamespaces", "t", NULL
, offsetof(struct security_info
, restrict_namespaces
) },
1870 { "RestrictRealtime", "b", NULL
, offsetof(struct security_info
, restrict_realtime
) },
1871 { "RootDirectory", "s", NULL
, offsetof(struct security_info
, root_directory
) },
1872 { "RootImage", "s", NULL
, offsetof(struct security_info
, root_image
) },
1873 { "SupplementaryGroups", "as", NULL
, offsetof(struct security_info
, supplementary_groups
) },
1874 { "SystemCallArchitectures", "as", NULL
, offsetof(struct security_info
, system_call_architectures
) },
1875 { "SystemCallFilter", "(as)", property_read_system_call_filter
, 0 },
1876 { "Type", "s", NULL
, offsetof(struct security_info
, type
) },
1877 { "UMask", "u", NULL
, offsetof(struct security_info
, _umask
) },
1878 { "User", "s", NULL
, offsetof(struct security_info
, user
) },
1882 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1883 _cleanup_free_
char *path
= NULL
;
1886 /* Note: this mangles *info on failure! */
1892 path
= unit_dbus_path_from_name(name
);
1896 r
= bus_map_all_properties(bus
,
1897 "org.freedesktop.systemd1",
1900 BUS_MAP_STRDUP
|BUS_MAP_BOOLEAN_AS_BOOL
,
1905 return log_error_errno(r
, "Failed to get unit properties: %s", bus_error_message(&error
, r
));
1907 if (!streq_ptr(info
->load_state
, "loaded")) {
1909 if (FLAGS_SET(flags
, ANALYZE_SECURITY_ONLY_LOADED
))
1910 return -EMEDIUMTYPE
;
1912 if (streq_ptr(info
->load_state
, "not-found"))
1913 log_error("Unit %s not found, cannot analyze.", name
);
1914 else if (streq_ptr(info
->load_state
, "masked"))
1915 log_error("Unit %s is masked, cannot analyze.", name
);
1917 log_error("Unit %s not loaded properly, cannot analyze.", name
);
1922 if (FLAGS_SET(flags
, ANALYZE_SECURITY_ONLY_LONG_RUNNING
) && streq_ptr(info
->type
, "oneshot"))
1923 return -EMEDIUMTYPE
;
1925 if (info
->private_devices
||
1926 info
->private_tmp
||
1927 info
->protect_control_groups
||
1928 info
->protect_kernel_tunables
||
1929 info
->protect_kernel_modules
||
1930 !streq_ptr(info
->protect_home
, "no") ||
1931 !streq_ptr(info
->protect_system
, "no") ||
1933 info
->private_mounts
= true;
1935 if (info
->protect_kernel_modules
)
1936 info
->capability_bounding_set
&= ~(UINT64_C(1) << CAP_SYS_MODULE
);
1938 if (info
->private_devices
)
1939 info
->capability_bounding_set
&= ~((UINT64_C(1) << CAP_MKNOD
) |
1940 (UINT64_C(1) << CAP_SYS_RAWIO
));
1945 static int analyze_security_one(sd_bus
*bus
, const char *name
, Table
* overview_table
, AnalyzeSecurityFlags flags
) {
1946 _cleanup_(security_info_free
) struct security_info info
= {
1947 .default_dependencies
= true,
1948 .capability_bounding_set
= UINT64_MAX
,
1949 .restrict_namespaces
= UINT64_MAX
,
1957 r
= acquire_security_info(bus
, name
, &info
, flags
);
1958 if (r
== -EMEDIUMTYPE
) /* Ignore this one because not loaded or Type is oneshot */
1963 r
= assess(&info
, overview_table
, flags
);
1970 int analyze_security(sd_bus
*bus
, char **units
, AnalyzeSecurityFlags flags
) {
1971 _cleanup_(table_unrefp
) Table
*overview_table
= NULL
;
1976 if (strv_length(units
) != 1) {
1977 overview_table
= table_new("unit", "exposure", "predicate", "happy");
1978 if (!overview_table
)
1982 if (strv_isempty(units
)) {
1983 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1984 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1985 _cleanup_strv_free_
char **list
= NULL
;
1986 size_t allocated
= 0, n
= 0;
1989 r
= sd_bus_call_method(
1991 "org.freedesktop.systemd1",
1992 "/org/freedesktop/systemd1",
1993 "org.freedesktop.systemd1.Manager",
1998 return log_error_errno(r
, "Failed to list units: %s", bus_error_message(&error
, r
));
2000 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
2002 return bus_log_parse_error(r
);
2008 r
= bus_parse_unit_info(reply
, &info
);
2010 return bus_log_parse_error(r
);
2014 if (!endswith(info
.id
, ".service"))
2017 if (!GREEDY_REALLOC(list
, allocated
, n
+2))
2020 copy
= strdup(info
.id
);
2030 flags
|= ANALYZE_SECURITY_SHORT
|ANALYZE_SECURITY_ONLY_LOADED
|ANALYZE_SECURITY_ONLY_LONG_RUNNING
;
2032 STRV_FOREACH(i
, list
) {
2033 r
= analyze_security_one(bus
, *i
, overview_table
, flags
);
2034 if (r
< 0 && ret
>= 0)
2041 STRV_FOREACH(i
, units
) {
2042 _cleanup_free_
char *mangled
= NULL
, *instance
= NULL
;
2045 if (!FLAGS_SET(flags
, ANALYZE_SECURITY_SHORT
) && i
!= units
) {
2050 r
= unit_name_mangle_with_suffix(*i
, 0, ".service", &mangled
);
2052 return log_error_errno(r
, "Failed to mangle unit name '%s': %m", *i
);
2054 if (!endswith(mangled
, ".service")) {
2055 log_error("Unit %s is not a service unit, refusing.", *i
);
2059 if (unit_name_is_valid(mangled
, UNIT_NAME_TEMPLATE
)) {
2060 r
= unit_name_replace_instance(mangled
, "test-instance", &instance
);
2068 r
= analyze_security_one(bus
, name
, overview_table
, flags
);
2069 if (r
< 0 && ret
>= 0)
2074 if (overview_table
) {
2075 if (!FLAGS_SET(flags
, ANALYZE_SECURITY_SHORT
)) {
2080 r
= table_print(overview_table
, stdout
);
2082 return log_error_errno(r
, "Failed to output table: %m");