]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/analyze/analyze-security.c
tree-wide: do not use (void) asprintf
[thirdparty/systemd.git] / src / analyze / analyze-security.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <sys/utsname.h>
4
5 #include "analyze-security.h"
6 #include "bus-error.h"
7 #include "bus-map-properties.h"
8 #include "bus-unit-util.h"
9 #include "bus-util.h"
10 #include "env-util.h"
11 #include "format-table.h"
12 #include "in-addr-util.h"
13 #include "locale-util.h"
14 #include "macro.h"
15 #include "missing_capability.h"
16 #include "missing_sched.h"
17 #include "nulstr-util.h"
18 #include "parse-util.h"
19 #include "path-util.h"
20 #include "pretty-print.h"
21 #if HAVE_SECCOMP
22 # include "seccomp-util.h"
23 #endif
24 #include "set.h"
25 #include "stdio-util.h"
26 #include "strv.h"
27 #include "terminal-util.h"
28 #include "unit-def.h"
29 #include "unit-name.h"
30
31 struct security_info {
32 char *id;
33 char *type;
34 char *load_state;
35 char *fragment_path;
36 bool default_dependencies;
37
38 uint64_t ambient_capabilities;
39 uint64_t capability_bounding_set;
40
41 char *user;
42 char **supplementary_groups;
43 bool dynamic_user;
44
45 bool ip_address_deny_all;
46 bool ip_address_allow_localhost;
47 bool ip_address_allow_other;
48
49 bool ip_filters_custom_ingress;
50 bool ip_filters_custom_egress;
51
52 char *keyring_mode;
53 char *protect_proc;
54 char *proc_subset;
55 bool lock_personality;
56 bool memory_deny_write_execute;
57 bool no_new_privileges;
58 char *notify_access;
59 bool protect_hostname;
60
61 bool private_devices;
62 bool private_mounts;
63 bool private_network;
64 bool private_tmp;
65 bool private_users;
66
67 bool protect_control_groups;
68 bool protect_kernel_modules;
69 bool protect_kernel_tunables;
70 bool protect_kernel_logs;
71 bool protect_clock;
72
73 char *protect_home;
74 char *protect_system;
75
76 bool remove_ipc;
77
78 bool restrict_address_family_inet;
79 bool restrict_address_family_unix;
80 bool restrict_address_family_netlink;
81 bool restrict_address_family_packet;
82 bool restrict_address_family_other;
83
84 uint64_t restrict_namespaces;
85 bool restrict_realtime;
86 bool restrict_suid_sgid;
87
88 char *root_directory;
89 char *root_image;
90
91 bool delegate;
92 char *device_policy;
93 bool device_allow_non_empty;
94
95 char **system_call_architectures;
96
97 bool system_call_filter_allow_list;
98 Set *system_call_filter;
99
100 uint32_t _umask;
101 };
102
103 struct security_assessor {
104 const char *id;
105 const char *description_good;
106 const char *description_bad;
107 const char *description_na;
108 const char *url;
109 uint64_t weight;
110 uint64_t range;
111 int (*assess)(
112 const struct security_assessor *a,
113 const struct security_info *info,
114 const void *data,
115 uint64_t *ret_badness,
116 char **ret_description);
117 size_t offset;
118 uint64_t parameter;
119 bool default_dependencies_only;
120 };
121
122 static void security_info_free(struct security_info *i) {
123 if (!i)
124 return;
125
126 free(i->id);
127 free(i->type);
128 free(i->load_state);
129 free(i->fragment_path);
130
131 free(i->user);
132
133 free(i->protect_home);
134 free(i->protect_system);
135
136 free(i->root_directory);
137 free(i->root_image);
138
139 free(i->keyring_mode);
140 free(i->protect_proc);
141 free(i->proc_subset);
142 free(i->notify_access);
143
144 free(i->device_policy);
145
146 strv_free(i->supplementary_groups);
147 strv_free(i->system_call_architectures);
148
149 set_free(i->system_call_filter);
150 }
151
152 static bool security_info_runs_privileged(const struct security_info *i) {
153 assert(i);
154
155 if (STRPTR_IN_SET(i->user, "0", "root"))
156 return true;
157
158 if (i->dynamic_user)
159 return false;
160
161 return isempty(i->user);
162 }
163
164 static int assess_bool(
165 const struct security_assessor *a,
166 const struct security_info *info,
167 const void *data,
168 uint64_t *ret_badness,
169 char **ret_description) {
170
171 const bool *b = data;
172
173 assert(b);
174 assert(ret_badness);
175 assert(ret_description);
176
177 *ret_badness = a->parameter ? *b : !*b;
178 *ret_description = NULL;
179
180 return 0;
181 }
182
183 static int assess_user(
184 const struct security_assessor *a,
185 const struct security_info *info,
186 const void *data,
187 uint64_t *ret_badness,
188 char **ret_description) {
189
190 _cleanup_free_ char *d = NULL;
191 uint64_t b;
192
193 assert(ret_badness);
194 assert(ret_description);
195
196 if (streq_ptr(info->user, NOBODY_USER_NAME)) {
197 d = strdup("Service runs under as '" NOBODY_USER_NAME "' user, which should not be used for services");
198 b = 9;
199 } else if (info->dynamic_user && !STR_IN_SET(info->user, "0", "root")) {
200 d = strdup("Service runs under a transient non-root user identity");
201 b = 0;
202 } else if (info->user && !STR_IN_SET(info->user, "0", "root", "")) {
203 d = strdup("Service runs under a static non-root user identity");
204 b = 0;
205 } else {
206 *ret_badness = 10;
207 *ret_description = NULL;
208 return 0;
209 }
210
211 if (!d)
212 return log_oom();
213
214 *ret_badness = b;
215 *ret_description = TAKE_PTR(d);
216
217 return 0;
218 }
219
220 static int assess_protect_home(
221 const struct security_assessor *a,
222 const struct security_info *info,
223 const void *data,
224 uint64_t *ret_badness,
225 char **ret_description) {
226
227 const char *description;
228 uint64_t badness;
229 char *copy;
230 int r;
231
232 assert(ret_badness);
233 assert(ret_description);
234
235 badness = 10;
236 description = "Service has full access to home directories";
237
238 r = parse_boolean(info->protect_home);
239 if (r < 0) {
240 if (streq_ptr(info->protect_home, "read-only")) {
241 badness = 5;
242 description = "Service has read-only access to home directories";
243 } else if (streq_ptr(info->protect_home, "tmpfs")) {
244 badness = 1;
245 description = "Service has access to fake empty home directories";
246 }
247 } else if (r > 0) {
248 badness = 0;
249 description = "Service has no access to home directories";
250 }
251
252 copy = strdup(description);
253 if (!copy)
254 return log_oom();
255
256 *ret_badness = badness;
257 *ret_description = copy;
258
259 return 0;
260 }
261
262 static int assess_protect_system(
263 const struct security_assessor *a,
264 const struct security_info *info,
265 const void *data,
266 uint64_t *ret_badness,
267 char **ret_description) {
268
269 const char *description;
270 uint64_t badness;
271 char *copy;
272 int r;
273
274 assert(ret_badness);
275 assert(ret_description);
276
277 badness = 10;
278 description = "Service has full access to the OS file hierarchy";
279
280 r = parse_boolean(info->protect_system);
281 if (r < 0) {
282 if (streq_ptr(info->protect_system, "full")) {
283 badness = 3;
284 description = "Service has very limited write access to the OS file hierarchy";
285 } else if (streq_ptr(info->protect_system, "strict")) {
286 badness = 0;
287 description = "Service has strict read-only access to the OS file hierarchy";
288 }
289 } else if (r > 0) {
290 badness = 5;
291 description = "Service has limited write access to the OS file hierarchy";
292 }
293
294 copy = strdup(description);
295 if (!copy)
296 return log_oom();
297
298 *ret_badness = badness;
299 *ret_description = copy;
300
301 return 0;
302 }
303
304 static int assess_root_directory(
305 const struct security_assessor *a,
306 const struct security_info *info,
307 const void *data,
308 uint64_t *ret_badness,
309 char **ret_description) {
310
311 assert(ret_badness);
312 assert(ret_description);
313
314 *ret_badness =
315 empty_or_root(info->root_directory) &&
316 empty_or_root(info->root_image);
317 *ret_description = NULL;
318
319 return 0;
320 }
321
322 static int assess_capability_bounding_set(
323 const struct security_assessor *a,
324 const struct security_info *info,
325 const void *data,
326 uint64_t *ret_badness,
327 char **ret_description) {
328
329 assert(ret_badness);
330 assert(ret_description);
331
332 *ret_badness = !!(info->capability_bounding_set & a->parameter);
333 *ret_description = NULL;
334
335 return 0;
336 }
337
338 static int assess_umask(
339 const struct security_assessor *a,
340 const struct security_info *info,
341 const void *data,
342 uint64_t *ret_badness,
343 char **ret_description) {
344
345 char *copy = NULL;
346 const char *d;
347 uint64_t b;
348
349 assert(ret_badness);
350 assert(ret_description);
351
352 if (!FLAGS_SET(info->_umask, 0002)) {
353 d = "Files created by service are world-writable by default";
354 b = 10;
355 } else if (!FLAGS_SET(info->_umask, 0004)) {
356 d = "Files created by service are world-readable by default";
357 b = 5;
358 } else if (!FLAGS_SET(info->_umask, 0020)) {
359 d = "Files created by service are group-writable by default";
360 b = 2;
361 } else if (!FLAGS_SET(info->_umask, 0040)) {
362 d = "Files created by service are group-readable by default";
363 b = 1;
364 } else {
365 d = "Files created by service are accessible only by service's own user by default";
366 b = 0;
367 }
368
369 copy = strdup(d);
370 if (!copy)
371 return log_oom();
372
373 *ret_badness = b;
374 *ret_description = copy;
375
376 return 0;
377 }
378
379 static int assess_keyring_mode(
380 const struct security_assessor *a,
381 const struct security_info *info,
382 const void *data,
383 uint64_t *ret_badness,
384 char **ret_description) {
385
386 assert(ret_badness);
387 assert(ret_description);
388
389 *ret_badness = !streq_ptr(info->keyring_mode, "private");
390 *ret_description = NULL;
391
392 return 0;
393 }
394
395 static int assess_protect_proc(
396 const struct security_assessor *a,
397 const struct security_info *info,
398 const void *data,
399 uint64_t *ret_badness,
400 char **ret_description) {
401
402 assert(ret_badness);
403 assert(ret_description);
404
405 if (streq_ptr(info->protect_proc, "noaccess"))
406 *ret_badness = 1;
407 else if (STRPTR_IN_SET(info->protect_proc, "invisible", "ptraceable"))
408 *ret_badness = 0;
409 else
410 *ret_badness = 3;
411
412 *ret_description = NULL;
413
414 return 0;
415 }
416
417 static int assess_proc_subset(
418 const struct security_assessor *a,
419 const struct security_info *info,
420 const void *data,
421 uint64_t *ret_badness,
422 char **ret_description) {
423
424 assert(ret_badness);
425 assert(ret_description);
426
427 *ret_badness = !streq_ptr(info->proc_subset, "pid");
428 *ret_description = NULL;
429
430 return 0;
431 }
432
433 static int assess_notify_access(
434 const struct security_assessor *a,
435 const struct security_info *info,
436 const void *data,
437 uint64_t *ret_badness,
438 char **ret_description) {
439
440 assert(ret_badness);
441 assert(ret_description);
442
443 *ret_badness = streq_ptr(info->notify_access, "all");
444 *ret_description = NULL;
445
446 return 0;
447 }
448
449 static int assess_remove_ipc(
450 const struct security_assessor *a,
451 const struct security_info *info,
452 const void *data,
453 uint64_t *ret_badness,
454 char **ret_description) {
455
456 assert(ret_badness);
457 assert(ret_description);
458
459 if (security_info_runs_privileged(info))
460 *ret_badness = UINT64_MAX;
461 else
462 *ret_badness = !info->remove_ipc;
463
464 *ret_description = NULL;
465 return 0;
466 }
467
468 static int assess_supplementary_groups(
469 const struct security_assessor *a,
470 const struct security_info *info,
471 const void *data,
472 uint64_t *ret_badness,
473 char **ret_description) {
474
475 assert(ret_badness);
476 assert(ret_description);
477
478 if (security_info_runs_privileged(info))
479 *ret_badness = UINT64_MAX;
480 else
481 *ret_badness = !strv_isempty(info->supplementary_groups);
482
483 *ret_description = NULL;
484 return 0;
485 }
486
487 static int assess_restrict_namespaces(
488 const struct security_assessor *a,
489 const struct security_info *info,
490 const void *data,
491 uint64_t *ret_badness,
492 char **ret_description) {
493
494 assert(ret_badness);
495 assert(ret_description);
496
497 *ret_badness = !!(info->restrict_namespaces & a->parameter);
498 *ret_description = NULL;
499
500 return 0;
501 }
502
503 static int assess_system_call_architectures(
504 const struct security_assessor *a,
505 const struct security_info *info,
506 const void *data,
507 uint64_t *ret_badness,
508 char **ret_description) {
509
510 char *d;
511 uint64_t b;
512
513 assert(ret_badness);
514 assert(ret_description);
515
516 if (strv_isempty(info->system_call_architectures)) {
517 b = 10;
518 d = strdup("Service may execute system calls with all ABIs");
519 } else if (strv_equal(info->system_call_architectures, STRV_MAKE("native"))) {
520 b = 0;
521 d = strdup("Service may execute system calls only with native ABI");
522 } else {
523 b = 8;
524 d = strdup("Service may execute system calls with multiple ABIs");
525 }
526
527 if (!d)
528 return log_oom();
529
530 *ret_badness = b;
531 *ret_description = d;
532
533 return 0;
534 }
535
536 #if HAVE_SECCOMP
537
538 static bool syscall_names_in_filter(Set *s, bool allow_list, const SyscallFilterSet *f, const char **ret_offending_syscall) {
539 const char *syscall;
540
541 NULSTR_FOREACH(syscall, f->value) {
542 int id;
543
544 if (syscall[0] == '@') {
545 const SyscallFilterSet *g;
546
547 assert_se(g = syscall_filter_set_find(syscall));
548 if (syscall_names_in_filter(s, allow_list, g, ret_offending_syscall))
549 return true; /* bad! */
550
551 continue;
552 }
553
554 /* Let's see if the system call actually exists on this platform, before complaining */
555 id = seccomp_syscall_resolve_name(syscall);
556 if (id < 0)
557 continue;
558
559 if (set_contains(s, syscall) == allow_list) {
560 log_debug("Offending syscall filter item: %s", syscall);
561 if (ret_offending_syscall)
562 *ret_offending_syscall = syscall;
563 return true; /* bad! */
564 }
565 }
566
567 *ret_offending_syscall = NULL;
568 return false;
569 }
570
571 static int assess_system_call_filter(
572 const struct security_assessor *a,
573 const struct security_info *info,
574 const void *data,
575 uint64_t *ret_badness,
576 char **ret_description) {
577
578 assert(a);
579 assert(info);
580 assert(ret_badness);
581 assert(ret_description);
582
583 assert(a->parameter < _SYSCALL_FILTER_SET_MAX);
584 const SyscallFilterSet *f = syscall_filter_sets + a->parameter;
585
586 _cleanup_free_ char *d = NULL;
587 uint64_t b;
588 int r;
589
590 if (!info->system_call_filter_allow_list && set_isempty(info->system_call_filter)) {
591 r = free_and_strdup(&d, "Service does not filter system calls");
592 b = 10;
593 } else {
594 bool bad;
595 const char *offender = NULL;
596
597 log_debug("Analyzing system call filter, checking against: %s", f->name);
598 bad = syscall_names_in_filter(info->system_call_filter, info->system_call_filter_allow_list, f, &offender);
599 log_debug("Result: %s", bad ? "bad" : "good");
600
601 if (info->system_call_filter_allow_list) {
602 if (bad) {
603 r = asprintf(&d, "System call allow list defined for service, and %s is included "
604 "(e.g. %s is allowed)",
605 f->name, offender);
606 b = 9;
607 } else {
608 r = asprintf(&d, "System call allow list defined for service, and %s is not included",
609 f->name);
610 b = 0;
611 }
612 } else {
613 if (bad) {
614 r = asprintf(&d, "System call deny list defined for service, and %s is not included "
615 "(e.g. %s is allowed)",
616 f->name, offender);
617 b = 10;
618 } else {
619 r = asprintf(&d, "System call deny list defined for service, and %s is included",
620 f->name);
621 b = 0;
622 }
623 }
624 }
625 if (r < 0)
626 return log_oom();
627
628 *ret_badness = b;
629 *ret_description = TAKE_PTR(d);
630
631 return 0;
632 }
633
634 #endif
635
636 static int assess_ip_address_allow(
637 const struct security_assessor *a,
638 const struct security_info *info,
639 const void *data,
640 uint64_t *ret_badness,
641 char **ret_description) {
642
643 char *d = NULL;
644 uint64_t b;
645
646 assert(info);
647 assert(ret_badness);
648 assert(ret_description);
649
650 if (info->ip_filters_custom_ingress || info->ip_filters_custom_egress) {
651 d = strdup("Service defines custom ingress/egress IP filters with BPF programs");
652 b = 0;
653 } else if (!info->ip_address_deny_all) {
654 d = strdup("Service does not define an IP address allow list");
655 b = 10;
656 } else if (info->ip_address_allow_other) {
657 d = strdup("Service defines IP address allow list with non-localhost entries");
658 b = 5;
659 } else if (info->ip_address_allow_localhost) {
660 d = strdup("Service defines IP address allow list with only localhost entries");
661 b = 2;
662 } else {
663 d = strdup("Service blocks all IP address ranges");
664 b = 0;
665 }
666
667 if (!d)
668 return log_oom();
669
670 *ret_badness = b;
671 *ret_description = d;
672
673 return 0;
674 }
675
676 static int assess_device_allow(
677 const struct security_assessor *a,
678 const struct security_info *info,
679 const void *data,
680 uint64_t *ret_badness,
681 char **ret_description) {
682
683 char *d = NULL;
684 uint64_t b;
685
686 assert(info);
687 assert(ret_badness);
688 assert(ret_description);
689
690 if (STRPTR_IN_SET(info->device_policy, "strict", "closed")) {
691
692 if (info->device_allow_non_empty) {
693 d = strdup("Service has a device ACL with some special devices");
694 b = 5;
695 } else {
696 d = strdup("Service has a minimal device ACL");
697 b = 0;
698 }
699 } else {
700 d = strdup("Service has no device ACL");
701 b = 10;
702 }
703
704 if (!d)
705 return log_oom();
706
707 *ret_badness = b;
708 *ret_description = d;
709
710 return 0;
711 }
712
713 static int assess_ambient_capabilities(
714 const struct security_assessor *a,
715 const struct security_info *info,
716 const void *data,
717 uint64_t *ret_badness,
718 char **ret_description) {
719
720 assert(ret_badness);
721 assert(ret_description);
722
723 *ret_badness = info->ambient_capabilities != 0;
724 *ret_description = NULL;
725
726 return 0;
727 }
728
729 static const struct security_assessor security_assessor_table[] = {
730 {
731 .id = "User=/DynamicUser=",
732 .description_bad = "Service runs as root user",
733 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#User=",
734 .weight = 2000,
735 .range = 10,
736 .assess = assess_user,
737 },
738 {
739 .id = "SupplementaryGroups=",
740 .description_good = "Service has no supplementary groups",
741 .description_bad = "Service runs with supplementary groups",
742 .description_na = "Service runs as root, option does not matter",
743 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SupplementaryGroups=",
744 .weight = 200,
745 .range = 1,
746 .assess = assess_supplementary_groups,
747 },
748 {
749 .id = "PrivateDevices=",
750 .description_good = "Service has no access to hardware devices",
751 .description_bad = "Service potentially has access to hardware devices",
752 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateDevices=",
753 .weight = 1000,
754 .range = 1,
755 .assess = assess_bool,
756 .offset = offsetof(struct security_info, private_devices),
757 },
758 {
759 .id = "PrivateMounts=",
760 .description_good = "Service cannot install system mounts",
761 .description_bad = "Service may install system mounts",
762 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateMounts=",
763 .weight = 1000,
764 .range = 1,
765 .assess = assess_bool,
766 .offset = offsetof(struct security_info, private_mounts),
767 },
768 {
769 .id = "PrivateNetwork=",
770 .description_good = "Service has no access to the host's network",
771 .description_bad = "Service has access to the host's network",
772 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateNetwork=",
773 .weight = 2500,
774 .range = 1,
775 .assess = assess_bool,
776 .offset = offsetof(struct security_info, private_network),
777 },
778 {
779 .id = "PrivateTmp=",
780 .description_good = "Service has no access to other software's temporary files",
781 .description_bad = "Service has access to other software's temporary files",
782 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateTmp=",
783 .weight = 1000,
784 .range = 1,
785 .assess = assess_bool,
786 .offset = offsetof(struct security_info, private_tmp),
787 .default_dependencies_only = true,
788 },
789 {
790 .id = "PrivateUsers=",
791 .description_good = "Service does not have access to other users",
792 .description_bad = "Service has access to other users",
793 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateUsers=",
794 .weight = 1000,
795 .range = 1,
796 .assess = assess_bool,
797 .offset = offsetof(struct security_info, private_users),
798 },
799 {
800 .id = "ProtectControlGroups=",
801 .description_good = "Service cannot modify the control group file system",
802 .description_bad = "Service may modify the control group file system",
803 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectControlGroups=",
804 .weight = 1000,
805 .range = 1,
806 .assess = assess_bool,
807 .offset = offsetof(struct security_info, protect_control_groups),
808 },
809 {
810 .id = "ProtectKernelModules=",
811 .description_good = "Service cannot load or read kernel modules",
812 .description_bad = "Service may load or read kernel modules",
813 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelModules=",
814 .weight = 1000,
815 .range = 1,
816 .assess = assess_bool,
817 .offset = offsetof(struct security_info, protect_kernel_modules),
818 },
819 {
820 .id = "ProtectKernelTunables=",
821 .description_good = "Service cannot alter kernel tunables (/proc/sys, …)",
822 .description_bad = "Service may alter kernel tunables",
823 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelTunables=",
824 .weight = 1000,
825 .range = 1,
826 .assess = assess_bool,
827 .offset = offsetof(struct security_info, protect_kernel_tunables),
828 },
829 {
830 .id = "ProtectKernelLogs=",
831 .description_good = "Service cannot read from or write to the kernel log ring buffer",
832 .description_bad = "Service may read from or write to the kernel log ring buffer",
833 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelLogs=",
834 .weight = 1000,
835 .range = 1,
836 .assess = assess_bool,
837 .offset = offsetof(struct security_info, protect_kernel_logs),
838 },
839 {
840 .id = "ProtectClock=",
841 .description_good = "Service cannot write to the hardware clock or system clock",
842 .description_bad = "Service may write to the hardware clock or system clock",
843 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectClock=",
844 .weight = 1000,
845 .range = 1,
846 .assess = assess_bool,
847 .offset = offsetof(struct security_info, protect_clock),
848 },
849 {
850 .id = "ProtectHome=",
851 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHome=",
852 .weight = 1000,
853 .range = 10,
854 .assess = assess_protect_home,
855 .default_dependencies_only = true,
856 },
857 {
858 .id = "ProtectHostname=",
859 .description_good = "Service cannot change system host/domainname",
860 .description_bad = "Service may change system host/domainname",
861 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHostname=",
862 .weight = 50,
863 .range = 1,
864 .assess = assess_bool,
865 .offset = offsetof(struct security_info, protect_hostname),
866 },
867 {
868 .id = "ProtectSystem=",
869 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectSystem=",
870 .weight = 1000,
871 .range = 10,
872 .assess = assess_protect_system,
873 .default_dependencies_only = true,
874 },
875 {
876 .id = "RootDirectory=/RootImage=",
877 .description_good = "Service has its own root directory/image",
878 .description_bad = "Service runs within the host's root directory",
879 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RootDirectory=",
880 .weight = 200,
881 .range = 1,
882 .assess = assess_root_directory,
883 .default_dependencies_only = true,
884 },
885 {
886 .id = "LockPersonality=",
887 .description_good = "Service cannot change ABI personality",
888 .description_bad = "Service may change ABI personality",
889 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#LockPersonality=",
890 .weight = 100,
891 .range = 1,
892 .assess = assess_bool,
893 .offset = offsetof(struct security_info, lock_personality),
894 },
895 {
896 .id = "MemoryDenyWriteExecute=",
897 .description_good = "Service cannot create writable executable memory mappings",
898 .description_bad = "Service may create writable executable memory mappings",
899 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#MemoryDenyWriteExecute=",
900 .weight = 100,
901 .range = 1,
902 .assess = assess_bool,
903 .offset = offsetof(struct security_info, memory_deny_write_execute),
904 },
905 {
906 .id = "NoNewPrivileges=",
907 .description_good = "Service processes cannot acquire new privileges",
908 .description_bad = "Service processes may acquire new privileges",
909 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NoNewPrivileges=",
910 .weight = 1000,
911 .range = 1,
912 .assess = assess_bool,
913 .offset = offsetof(struct security_info, no_new_privileges),
914 },
915 {
916 .id = "CapabilityBoundingSet=~CAP_SYS_ADMIN",
917 .description_good = "Service has no administrator privileges",
918 .description_bad = "Service has administrator privileges",
919 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
920 .weight = 1500,
921 .range = 1,
922 .assess = assess_capability_bounding_set,
923 .parameter = UINT64_C(1) << CAP_SYS_ADMIN,
924 },
925 {
926 .id = "CapabilityBoundingSet=~CAP_SET(UID|GID|PCAP)",
927 .description_good = "Service cannot change UID/GID identities/capabilities",
928 .description_bad = "Service may change UID/GID identities/capabilities",
929 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
930 .weight = 1500,
931 .range = 1,
932 .assess = assess_capability_bounding_set,
933 .parameter = (UINT64_C(1) << CAP_SETUID)|
934 (UINT64_C(1) << CAP_SETGID)|
935 (UINT64_C(1) << CAP_SETPCAP),
936 },
937 {
938 .id = "CapabilityBoundingSet=~CAP_SYS_PTRACE",
939 .description_good = "Service has no ptrace() debugging abilities",
940 .description_bad = "Service has ptrace() debugging abilities",
941 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
942 .weight = 1500,
943 .range = 1,
944 .assess = assess_capability_bounding_set,
945 .parameter = (UINT64_C(1) << CAP_SYS_PTRACE),
946 },
947 {
948 .id = "CapabilityBoundingSet=~CAP_SYS_TIME",
949 .description_good = "Service processes cannot change the system clock",
950 .description_bad = "Service processes may change the system clock",
951 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
952 .weight = 1000,
953 .range = 1,
954 .assess = assess_capability_bounding_set,
955 .parameter = UINT64_C(1) << CAP_SYS_TIME,
956 },
957 {
958 .id = "CapabilityBoundingSet=~CAP_NET_ADMIN",
959 .description_good = "Service has no network configuration privileges",
960 .description_bad = "Service has network configuration privileges",
961 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
962 .weight = 1000,
963 .range = 1,
964 .assess = assess_capability_bounding_set,
965 .parameter = (UINT64_C(1) << CAP_NET_ADMIN),
966 },
967 {
968 .id = "CapabilityBoundingSet=~CAP_SYS_RAWIO",
969 .description_good = "Service has no raw I/O access",
970 .description_bad = "Service has raw I/O access",
971 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
972 .weight = 1000,
973 .range = 1,
974 .assess = assess_capability_bounding_set,
975 .parameter = (UINT64_C(1) << CAP_SYS_RAWIO),
976 },
977 {
978 .id = "CapabilityBoundingSet=~CAP_SYS_MODULE",
979 .description_good = "Service cannot load kernel modules",
980 .description_bad = "Service may load kernel modules",
981 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
982 .weight = 1000,
983 .range = 1,
984 .assess = assess_capability_bounding_set,
985 .parameter = (UINT64_C(1) << CAP_SYS_MODULE),
986 },
987 {
988 .id = "CapabilityBoundingSet=~CAP_AUDIT_*",
989 .description_good = "Service has no audit subsystem access",
990 .description_bad = "Service has audit subsystem access",
991 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
992 .weight = 500,
993 .range = 1,
994 .assess = assess_capability_bounding_set,
995 .parameter = (UINT64_C(1) << CAP_AUDIT_CONTROL) |
996 (UINT64_C(1) << CAP_AUDIT_READ) |
997 (UINT64_C(1) << CAP_AUDIT_WRITE),
998 },
999 {
1000 .id = "CapabilityBoundingSet=~CAP_SYSLOG",
1001 .description_good = "Service has no access to kernel logging",
1002 .description_bad = "Service has access to kernel logging",
1003 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1004 .weight = 500,
1005 .range = 1,
1006 .assess = assess_capability_bounding_set,
1007 .parameter = (UINT64_C(1) << CAP_SYSLOG),
1008 },
1009 {
1010 .id = "CapabilityBoundingSet=~CAP_SYS_(NICE|RESOURCE)",
1011 .description_good = "Service has no privileges to change resource use parameters",
1012 .description_bad = "Service has privileges to change resource use parameters",
1013 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1014 .weight = 500,
1015 .range = 1,
1016 .assess = assess_capability_bounding_set,
1017 .parameter = (UINT64_C(1) << CAP_SYS_NICE) |
1018 (UINT64_C(1) << CAP_SYS_RESOURCE),
1019 },
1020 {
1021 .id = "CapabilityBoundingSet=~CAP_MKNOD",
1022 .description_good = "Service cannot create device nodes",
1023 .description_bad = "Service may create device nodes",
1024 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1025 .weight = 500,
1026 .range = 1,
1027 .assess = assess_capability_bounding_set,
1028 .parameter = (UINT64_C(1) << CAP_MKNOD),
1029 },
1030 {
1031 .id = "CapabilityBoundingSet=~CAP_(CHOWN|FSETID|SETFCAP)",
1032 .description_good = "Service cannot change file ownership/access mode/capabilities",
1033 .description_bad = "Service may change file ownership/access mode/capabilities unrestricted",
1034 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1035 .weight = 1000,
1036 .range = 1,
1037 .assess = assess_capability_bounding_set,
1038 .parameter = (UINT64_C(1) << CAP_CHOWN) |
1039 (UINT64_C(1) << CAP_FSETID) |
1040 (UINT64_C(1) << CAP_SETFCAP),
1041 },
1042 {
1043 .id = "CapabilityBoundingSet=~CAP_(DAC_*|FOWNER|IPC_OWNER)",
1044 .description_good = "Service cannot override UNIX file/IPC permission checks",
1045 .description_bad = "Service may override UNIX file/IPC permission checks",
1046 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1047 .weight = 1000,
1048 .range = 1,
1049 .assess = assess_capability_bounding_set,
1050 .parameter = (UINT64_C(1) << CAP_DAC_OVERRIDE) |
1051 (UINT64_C(1) << CAP_DAC_READ_SEARCH) |
1052 (UINT64_C(1) << CAP_FOWNER) |
1053 (UINT64_C(1) << CAP_IPC_OWNER),
1054 },
1055 {
1056 .id = "CapabilityBoundingSet=~CAP_KILL",
1057 .description_good = "Service cannot send UNIX signals to arbitrary processes",
1058 .description_bad = "Service may send UNIX signals to arbitrary processes",
1059 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1060 .weight = 500,
1061 .range = 1,
1062 .assess = assess_capability_bounding_set,
1063 .parameter = (UINT64_C(1) << CAP_KILL),
1064 },
1065 {
1066 .id = "CapabilityBoundingSet=~CAP_NET_(BIND_SERVICE|BROADCAST|RAW)",
1067 .description_good = "Service has no elevated networking privileges",
1068 .description_bad = "Service has elevated networking privileges",
1069 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1070 .weight = 500,
1071 .range = 1,
1072 .assess = assess_capability_bounding_set,
1073 .parameter = (UINT64_C(1) << CAP_NET_BIND_SERVICE) |
1074 (UINT64_C(1) << CAP_NET_BROADCAST) |
1075 (UINT64_C(1) << CAP_NET_RAW),
1076 },
1077 {
1078 .id = "CapabilityBoundingSet=~CAP_SYS_BOOT",
1079 .description_good = "Service cannot issue reboot()",
1080 .description_bad = "Service may issue reboot()",
1081 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1082 .weight = 100,
1083 .range = 1,
1084 .assess = assess_capability_bounding_set,
1085 .parameter = (UINT64_C(1) << CAP_SYS_BOOT),
1086 },
1087 {
1088 .id = "CapabilityBoundingSet=~CAP_MAC_*",
1089 .description_good = "Service cannot adjust SMACK MAC",
1090 .description_bad = "Service may adjust SMACK MAC",
1091 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1092 .weight = 100,
1093 .range = 1,
1094 .assess = assess_capability_bounding_set,
1095 .parameter = (UINT64_C(1) << CAP_MAC_ADMIN)|
1096 (UINT64_C(1) << CAP_MAC_OVERRIDE),
1097 },
1098 {
1099 .id = "CapabilityBoundingSet=~CAP_LINUX_IMMUTABLE",
1100 .description_good = "Service cannot mark files immutable",
1101 .description_bad = "Service may mark files immutable",
1102 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1103 .weight = 75,
1104 .range = 1,
1105 .assess = assess_capability_bounding_set,
1106 .parameter = (UINT64_C(1) << CAP_LINUX_IMMUTABLE),
1107 },
1108 {
1109 .id = "CapabilityBoundingSet=~CAP_IPC_LOCK",
1110 .description_good = "Service cannot lock memory into RAM",
1111 .description_bad = "Service may lock memory into RAM",
1112 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1113 .weight = 50,
1114 .range = 1,
1115 .assess = assess_capability_bounding_set,
1116 .parameter = (UINT64_C(1) << CAP_IPC_LOCK),
1117 },
1118 {
1119 .id = "CapabilityBoundingSet=~CAP_SYS_CHROOT",
1120 .description_good = "Service cannot issue chroot()",
1121 .description_bad = "Service may issue chroot()",
1122 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1123 .weight = 50,
1124 .range = 1,
1125 .assess = assess_capability_bounding_set,
1126 .parameter = (UINT64_C(1) << CAP_SYS_CHROOT),
1127 },
1128 {
1129 .id = "CapabilityBoundingSet=~CAP_BLOCK_SUSPEND",
1130 .description_good = "Service cannot establish wake locks",
1131 .description_bad = "Service may establish wake locks",
1132 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1133 .weight = 25,
1134 .range = 1,
1135 .assess = assess_capability_bounding_set,
1136 .parameter = (UINT64_C(1) << CAP_BLOCK_SUSPEND),
1137 },
1138 {
1139 .id = "CapabilityBoundingSet=~CAP_WAKE_ALARM",
1140 .description_good = "Service cannot program timers that wake up the system",
1141 .description_bad = "Service may program timers that wake up the system",
1142 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1143 .weight = 25,
1144 .range = 1,
1145 .assess = assess_capability_bounding_set,
1146 .parameter = (UINT64_C(1) << CAP_WAKE_ALARM),
1147 },
1148 {
1149 .id = "CapabilityBoundingSet=~CAP_LEASE",
1150 .description_good = "Service cannot create file leases",
1151 .description_bad = "Service may create file leases",
1152 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1153 .weight = 25,
1154 .range = 1,
1155 .assess = assess_capability_bounding_set,
1156 .parameter = (UINT64_C(1) << CAP_LEASE),
1157 },
1158 {
1159 .id = "CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG",
1160 .description_good = "Service cannot issue vhangup()",
1161 .description_bad = "Service may issue vhangup()",
1162 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1163 .weight = 25,
1164 .range = 1,
1165 .assess = assess_capability_bounding_set,
1166 .parameter = (UINT64_C(1) << CAP_SYS_TTY_CONFIG),
1167 },
1168 {
1169 .id = "CapabilityBoundingSet=~CAP_SYS_PACCT",
1170 .description_good = "Service cannot use acct()",
1171 .description_bad = "Service may use acct()",
1172 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1173 .weight = 25,
1174 .range = 1,
1175 .assess = assess_capability_bounding_set,
1176 .parameter = (UINT64_C(1) << CAP_SYS_PACCT),
1177 },
1178 {
1179 .id = "UMask=",
1180 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#UMask=",
1181 .weight = 100,
1182 .range = 10,
1183 .assess = assess_umask,
1184 },
1185 {
1186 .id = "KeyringMode=",
1187 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#KeyringMode=",
1188 .description_good = "Service doesn't share key material with other services",
1189 .description_bad = "Service shares key material with other service",
1190 .weight = 1000,
1191 .range = 1,
1192 .assess = assess_keyring_mode,
1193 },
1194 {
1195 .id = "ProtectProc=",
1196 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectProc=",
1197 .description_good = "Service has restricted access to process tree (/proc hidepid=)",
1198 .description_bad = "Service has full access to process tree (/proc hidepid=)",
1199 .weight = 1000,
1200 .range = 3,
1201 .assess = assess_protect_proc,
1202 },
1203 {
1204 .id = "ProcSubset=",
1205 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProcSubset=",
1206 .description_good = "Service has no access to non-process /proc files (/proc subset=)",
1207 .description_bad = "Service has full access to non-process /proc files (/proc subset=)",
1208 .weight = 10,
1209 .range = 1,
1210 .assess = assess_proc_subset,
1211 },
1212 {
1213 .id = "NotifyAccess=",
1214 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NotifyAccess=",
1215 .description_good = "Service child processes cannot alter service state",
1216 .description_bad = "Service child processes may alter service state",
1217 .weight = 1000,
1218 .range = 1,
1219 .assess = assess_notify_access,
1220 },
1221 {
1222 .id = "RemoveIPC=",
1223 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RemoveIPC=",
1224 .description_good = "Service user cannot leave SysV IPC objects around",
1225 .description_bad = "Service user may leave SysV IPC objects around",
1226 .description_na = "Service runs as root, option does not apply",
1227 .weight = 100,
1228 .range = 1,
1229 .assess = assess_remove_ipc,
1230 .offset = offsetof(struct security_info, remove_ipc),
1231 },
1232 {
1233 .id = "Delegate=",
1234 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Delegate=",
1235 .description_good = "Service does not maintain its own delegated control group subtree",
1236 .description_bad = "Service maintains its own delegated control group subtree",
1237 .weight = 100,
1238 .range = 1,
1239 .assess = assess_bool,
1240 .offset = offsetof(struct security_info, delegate),
1241 .parameter = true, /* invert! */
1242 },
1243 {
1244 .id = "RestrictRealtime=",
1245 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictRealtime=",
1246 .description_good = "Service realtime scheduling access is restricted",
1247 .description_bad = "Service may acquire realtime scheduling",
1248 .weight = 500,
1249 .range = 1,
1250 .assess = assess_bool,
1251 .offset = offsetof(struct security_info, restrict_realtime),
1252 },
1253 {
1254 .id = "RestrictSUIDSGID=",
1255 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictSUIDSGID=",
1256 .description_good = "SUID/SGID file creation by service is restricted",
1257 .description_bad = "Service may create SUID/SGID files",
1258 .weight = 1000,
1259 .range = 1,
1260 .assess = assess_bool,
1261 .offset = offsetof(struct security_info, restrict_suid_sgid),
1262 },
1263 {
1264 .id = "RestrictNamespaces=~CLONE_NEWUSER",
1265 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1266 .description_good = "Service cannot create user namespaces",
1267 .description_bad = "Service may create user namespaces",
1268 .weight = 1500,
1269 .range = 1,
1270 .assess = assess_restrict_namespaces,
1271 .parameter = CLONE_NEWUSER,
1272 },
1273 {
1274 .id = "RestrictNamespaces=~CLONE_NEWNS",
1275 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1276 .description_good = "Service cannot create file system namespaces",
1277 .description_bad = "Service may create file system namespaces",
1278 .weight = 500,
1279 .range = 1,
1280 .assess = assess_restrict_namespaces,
1281 .parameter = CLONE_NEWNS,
1282 },
1283 {
1284 .id = "RestrictNamespaces=~CLONE_NEWIPC",
1285 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1286 .description_good = "Service cannot create IPC namespaces",
1287 .description_bad = "Service may create IPC namespaces",
1288 .weight = 500,
1289 .range = 1,
1290 .assess = assess_restrict_namespaces,
1291 .parameter = CLONE_NEWIPC,
1292 },
1293 {
1294 .id = "RestrictNamespaces=~CLONE_NEWPID",
1295 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1296 .description_good = "Service cannot create process namespaces",
1297 .description_bad = "Service may create process namespaces",
1298 .weight = 500,
1299 .range = 1,
1300 .assess = assess_restrict_namespaces,
1301 .parameter = CLONE_NEWPID,
1302 },
1303 {
1304 .id = "RestrictNamespaces=~CLONE_NEWCGROUP",
1305 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1306 .description_good = "Service cannot create cgroup namespaces",
1307 .description_bad = "Service may create cgroup namespaces",
1308 .weight = 500,
1309 .range = 1,
1310 .assess = assess_restrict_namespaces,
1311 .parameter = CLONE_NEWCGROUP,
1312 },
1313 {
1314 .id = "RestrictNamespaces=~CLONE_NEWNET",
1315 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1316 .description_good = "Service cannot create network namespaces",
1317 .description_bad = "Service may create network namespaces",
1318 .weight = 500,
1319 .range = 1,
1320 .assess = assess_restrict_namespaces,
1321 .parameter = CLONE_NEWNET,
1322 },
1323 {
1324 .id = "RestrictNamespaces=~CLONE_NEWUTS",
1325 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1326 .description_good = "Service cannot create hostname namespaces",
1327 .description_bad = "Service may create hostname namespaces",
1328 .weight = 100,
1329 .range = 1,
1330 .assess = assess_restrict_namespaces,
1331 .parameter = CLONE_NEWUTS,
1332 },
1333 {
1334 .id = "RestrictAddressFamilies=~AF_(INET|INET6)",
1335 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1336 .description_good = "Service cannot allocate Internet sockets",
1337 .description_bad = "Service may allocate Internet sockets",
1338 .weight = 1500,
1339 .range = 1,
1340 .assess = assess_bool,
1341 .offset = offsetof(struct security_info, restrict_address_family_inet),
1342 },
1343 {
1344 .id = "RestrictAddressFamilies=~AF_UNIX",
1345 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1346 .description_good = "Service cannot allocate local sockets",
1347 .description_bad = "Service may allocate local sockets",
1348 .weight = 25,
1349 .range = 1,
1350 .assess = assess_bool,
1351 .offset = offsetof(struct security_info, restrict_address_family_unix),
1352 },
1353 {
1354 .id = "RestrictAddressFamilies=~AF_NETLINK",
1355 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1356 .description_good = "Service cannot allocate netlink sockets",
1357 .description_bad = "Service may allocate netlink sockets",
1358 .weight = 200,
1359 .range = 1,
1360 .assess = assess_bool,
1361 .offset = offsetof(struct security_info, restrict_address_family_netlink),
1362 },
1363 {
1364 .id = "RestrictAddressFamilies=~AF_PACKET",
1365 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1366 .description_good = "Service cannot allocate packet sockets",
1367 .description_bad = "Service may allocate packet sockets",
1368 .weight = 1000,
1369 .range = 1,
1370 .assess = assess_bool,
1371 .offset = offsetof(struct security_info, restrict_address_family_packet),
1372 },
1373 {
1374 .id = "RestrictAddressFamilies=~…",
1375 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1376 .description_good = "Service cannot allocate exotic sockets",
1377 .description_bad = "Service may allocate exotic sockets",
1378 .weight = 1250,
1379 .range = 1,
1380 .assess = assess_bool,
1381 .offset = offsetof(struct security_info, restrict_address_family_other),
1382 },
1383 {
1384 .id = "SystemCallArchitectures=",
1385 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallArchitectures=",
1386 .weight = 1000,
1387 .range = 10,
1388 .assess = assess_system_call_architectures,
1389 },
1390 #if HAVE_SECCOMP
1391 {
1392 .id = "SystemCallFilter=~@swap",
1393 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1394 .weight = 1000,
1395 .range = 10,
1396 .assess = assess_system_call_filter,
1397 .parameter = SYSCALL_FILTER_SET_SWAP,
1398 },
1399 {
1400 .id = "SystemCallFilter=~@obsolete",
1401 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1402 .weight = 250,
1403 .range = 10,
1404 .assess = assess_system_call_filter,
1405 .parameter = SYSCALL_FILTER_SET_OBSOLETE,
1406 },
1407 {
1408 .id = "SystemCallFilter=~@clock",
1409 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1410 .weight = 1000,
1411 .range = 10,
1412 .assess = assess_system_call_filter,
1413 .parameter = SYSCALL_FILTER_SET_CLOCK,
1414 },
1415 {
1416 .id = "SystemCallFilter=~@cpu-emulation",
1417 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1418 .weight = 250,
1419 .range = 10,
1420 .assess = assess_system_call_filter,
1421 .parameter = SYSCALL_FILTER_SET_CPU_EMULATION,
1422 },
1423 {
1424 .id = "SystemCallFilter=~@debug",
1425 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1426 .weight = 1000,
1427 .range = 10,
1428 .assess = assess_system_call_filter,
1429 .parameter = SYSCALL_FILTER_SET_DEBUG,
1430 },
1431 {
1432 .id = "SystemCallFilter=~@mount",
1433 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1434 .weight = 1000,
1435 .range = 10,
1436 .assess = assess_system_call_filter,
1437 .parameter = SYSCALL_FILTER_SET_MOUNT,
1438 },
1439 {
1440 .id = "SystemCallFilter=~@module",
1441 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1442 .weight = 1000,
1443 .range = 10,
1444 .assess = assess_system_call_filter,
1445 .parameter = SYSCALL_FILTER_SET_MODULE,
1446 },
1447 {
1448 .id = "SystemCallFilter=~@raw-io",
1449 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1450 .weight = 1000,
1451 .range = 10,
1452 .assess = assess_system_call_filter,
1453 .parameter = SYSCALL_FILTER_SET_RAW_IO,
1454 },
1455 {
1456 .id = "SystemCallFilter=~@reboot",
1457 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1458 .weight = 1000,
1459 .range = 10,
1460 .assess = assess_system_call_filter,
1461 .parameter = SYSCALL_FILTER_SET_REBOOT,
1462 },
1463 {
1464 .id = "SystemCallFilter=~@privileged",
1465 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1466 .weight = 700,
1467 .range = 10,
1468 .assess = assess_system_call_filter,
1469 .parameter = SYSCALL_FILTER_SET_PRIVILEGED,
1470 },
1471 {
1472 .id = "SystemCallFilter=~@resources",
1473 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1474 .weight = 700,
1475 .range = 10,
1476 .assess = assess_system_call_filter,
1477 .parameter = SYSCALL_FILTER_SET_RESOURCES,
1478 },
1479 #endif
1480 {
1481 .id = "IPAddressDeny=",
1482 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#IPAddressDeny=",
1483 .weight = 1000,
1484 .range = 10,
1485 .assess = assess_ip_address_allow,
1486 },
1487 {
1488 .id = "DeviceAllow=",
1489 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#DeviceAllow=",
1490 .weight = 1000,
1491 .range = 10,
1492 .assess = assess_device_allow,
1493 },
1494 {
1495 .id = "AmbientCapabilities=",
1496 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#AmbientCapabilities=",
1497 .description_good = "Service process does not receive ambient capabilities",
1498 .description_bad = "Service process receives ambient capabilities",
1499 .weight = 500,
1500 .range = 1,
1501 .assess = assess_ambient_capabilities,
1502 },
1503 };
1504
1505 static int assess(const struct security_info *info, Table *overview_table, AnalyzeSecurityFlags flags) {
1506 static const struct {
1507 uint64_t exposure;
1508 const char *name;
1509 const char *color;
1510 SpecialGlyph smiley;
1511 } badness_table[] = {
1512 { 100, "DANGEROUS", ANSI_HIGHLIGHT_RED, SPECIAL_GLYPH_DEPRESSED_SMILEY },
1513 { 90, "UNSAFE", ANSI_HIGHLIGHT_RED, SPECIAL_GLYPH_UNHAPPY_SMILEY },
1514 { 75, "EXPOSED", ANSI_HIGHLIGHT_YELLOW, SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY },
1515 { 50, "MEDIUM", NULL, SPECIAL_GLYPH_NEUTRAL_SMILEY },
1516 { 10, "OK", ANSI_HIGHLIGHT_GREEN, SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY },
1517 { 1, "SAFE", ANSI_HIGHLIGHT_GREEN, SPECIAL_GLYPH_HAPPY_SMILEY },
1518 { 0, "PERFECT", ANSI_HIGHLIGHT_GREEN, SPECIAL_GLYPH_ECSTATIC_SMILEY },
1519 };
1520
1521 uint64_t badness_sum = 0, weight_sum = 0, exposure;
1522 _cleanup_(table_unrefp) Table *details_table = NULL;
1523 size_t i;
1524 int r;
1525
1526 if (!FLAGS_SET(flags, ANALYZE_SECURITY_SHORT)) {
1527 details_table = table_new(" ", "name", "description", "weight", "badness", "range", "exposure");
1528 if (!details_table)
1529 return log_oom();
1530
1531 (void) table_set_sort(details_table, (size_t) 3, (size_t) 1);
1532 (void) table_set_reverse(details_table, 3, true);
1533
1534 if (getenv_bool("SYSTEMD_ANALYZE_DEBUG") <= 0)
1535 (void) table_set_display(details_table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 6);
1536 }
1537
1538 for (i = 0; i < ELEMENTSOF(security_assessor_table); i++) {
1539 const struct security_assessor *a = security_assessor_table + i;
1540 _cleanup_free_ char *d = NULL;
1541 uint64_t badness;
1542 void *data;
1543
1544 data = (uint8_t *) info + a->offset;
1545
1546 if (a->default_dependencies_only && !info->default_dependencies) {
1547 badness = UINT64_MAX;
1548 d = strdup("Service runs in special boot phase, option is not appropriate");
1549 if (!d)
1550 return log_oom();
1551 } else {
1552 r = a->assess(a, info, data, &badness, &d);
1553 if (r < 0)
1554 return r;
1555 }
1556
1557 assert(a->range > 0);
1558
1559 if (badness != UINT64_MAX) {
1560 assert(badness <= a->range);
1561
1562 badness_sum += DIV_ROUND_UP(badness * a->weight, a->range);
1563 weight_sum += a->weight;
1564 }
1565
1566 if (details_table) {
1567 const char *checkmark, *description, *color = NULL;
1568
1569 if (badness == UINT64_MAX) {
1570 checkmark = " ";
1571 description = a->description_na;
1572 color = NULL;
1573 } else if (badness == a->range) {
1574 checkmark = special_glyph(SPECIAL_GLYPH_CROSS_MARK);
1575 description = a->description_bad;
1576 color = ansi_highlight_red();
1577 } else if (badness == 0) {
1578 checkmark = special_glyph(SPECIAL_GLYPH_CHECK_MARK);
1579 description = a->description_good;
1580 color = ansi_highlight_green();
1581 } else {
1582 checkmark = special_glyph(SPECIAL_GLYPH_CROSS_MARK);
1583 description = NULL;
1584 color = ansi_highlight_red();
1585 }
1586
1587 if (d)
1588 description = d;
1589
1590 r = table_add_many(details_table,
1591 TABLE_STRING, checkmark,
1592 TABLE_SET_MINIMUM_WIDTH, 1,
1593 TABLE_SET_MAXIMUM_WIDTH, 1,
1594 TABLE_SET_ELLIPSIZE_PERCENT, 0,
1595 TABLE_SET_COLOR, color,
1596 TABLE_STRING, a->id, TABLE_SET_URL, a->url,
1597 TABLE_STRING, description,
1598 TABLE_UINT64, a->weight, TABLE_SET_ALIGN_PERCENT, 100,
1599 TABLE_UINT64, badness, TABLE_SET_ALIGN_PERCENT, 100,
1600 TABLE_UINT64, a->range, TABLE_SET_ALIGN_PERCENT, 100,
1601 TABLE_EMPTY, TABLE_SET_ALIGN_PERCENT, 100);
1602 if (r < 0)
1603 return table_log_add_error(r);
1604 }
1605 }
1606
1607 assert(weight_sum > 0);
1608
1609 if (details_table) {
1610 size_t row;
1611
1612 for (row = 1; row < table_get_rows(details_table); row++) {
1613 char buf[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1];
1614 const uint64_t *weight, *badness, *range;
1615 TableCell *cell;
1616 uint64_t x;
1617
1618 assert_se(weight = table_get_at(details_table, row, 3));
1619 assert_se(badness = table_get_at(details_table, row, 4));
1620 assert_se(range = table_get_at(details_table, row, 5));
1621
1622 if (*badness == UINT64_MAX || *badness == 0)
1623 continue;
1624
1625 assert_se(cell = table_get_cell(details_table, row, 6));
1626
1627 x = DIV_ROUND_UP(DIV_ROUND_UP(*badness * *weight * 100U, *range), weight_sum);
1628 xsprintf(buf, "%" PRIu64 ".%" PRIu64, x / 10, x % 10);
1629
1630 r = table_update(details_table, cell, TABLE_STRING, buf);
1631 if (r < 0)
1632 return log_error_errno(r, "Failed to update cell in table: %m");
1633 }
1634
1635 r = table_print(details_table, stdout);
1636 if (r < 0)
1637 return log_error_errno(r, "Failed to output table: %m");
1638 }
1639
1640 exposure = DIV_ROUND_UP(badness_sum * 100U, weight_sum);
1641
1642 for (i = 0; i < ELEMENTSOF(badness_table); i++)
1643 if (exposure >= badness_table[i].exposure)
1644 break;
1645
1646 assert(i < ELEMENTSOF(badness_table));
1647
1648 if (details_table) {
1649 _cleanup_free_ char *clickable = NULL;
1650 const char *name;
1651
1652 /* If we shall output the details table, also print the brief summary underneath */
1653
1654 if (info->fragment_path) {
1655 r = terminal_urlify_path(info->fragment_path, info->id, &clickable);
1656 if (r < 0)
1657 return log_oom();
1658
1659 name = clickable;
1660 } else
1661 name = info->id;
1662
1663 printf("\n%s %sOverall exposure level for %s%s: %s%" PRIu64 ".%" PRIu64 " %s%s %s\n",
1664 special_glyph(SPECIAL_GLYPH_ARROW),
1665 ansi_highlight(),
1666 name,
1667 ansi_normal(),
1668 colors_enabled() ? strempty(badness_table[i].color) : "",
1669 exposure / 10, exposure % 10,
1670 badness_table[i].name,
1671 ansi_normal(),
1672 special_glyph(badness_table[i].smiley));
1673 }
1674
1675 fflush(stdout);
1676
1677 if (overview_table) {
1678 char buf[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1];
1679 _cleanup_free_ char *url = NULL;
1680
1681 if (info->fragment_path) {
1682 r = file_url_from_path(info->fragment_path, &url);
1683 if (r < 0)
1684 return log_error_errno(r, "Failed to generate URL from path: %m");
1685 }
1686
1687 xsprintf(buf, "%" PRIu64 ".%" PRIu64, exposure / 10, exposure % 10);
1688
1689 r = table_add_many(overview_table,
1690 TABLE_STRING, info->id,
1691 TABLE_SET_URL, url,
1692 TABLE_STRING, buf,
1693 TABLE_SET_ALIGN_PERCENT, 100,
1694 TABLE_STRING, badness_table[i].name,
1695 TABLE_SET_COLOR, strempty(badness_table[i].color),
1696 TABLE_STRING, special_glyph(badness_table[i].smiley));
1697 if (r < 0)
1698 return table_log_add_error(r);
1699 }
1700
1701 return 0;
1702 }
1703
1704 static int property_read_restrict_address_families(
1705 sd_bus *bus,
1706 const char *member,
1707 sd_bus_message *m,
1708 sd_bus_error *error,
1709 void *userdata) {
1710
1711 struct security_info *info = userdata;
1712 int allow_list, r;
1713
1714 assert(bus);
1715 assert(member);
1716 assert(m);
1717
1718 r = sd_bus_message_enter_container(m, 'r', "bas");
1719 if (r < 0)
1720 return r;
1721
1722 r = sd_bus_message_read(m, "b", &allow_list);
1723 if (r < 0)
1724 return r;
1725
1726 info->restrict_address_family_inet =
1727 info->restrict_address_family_unix =
1728 info->restrict_address_family_netlink =
1729 info->restrict_address_family_packet =
1730 info->restrict_address_family_other = allow_list;
1731
1732 r = sd_bus_message_enter_container(m, 'a', "s");
1733 if (r < 0)
1734 return r;
1735
1736 for (;;) {
1737 const char *name;
1738
1739 r = sd_bus_message_read(m, "s", &name);
1740 if (r < 0)
1741 return r;
1742 if (r == 0)
1743 break;
1744
1745 if (STR_IN_SET(name, "AF_INET", "AF_INET6"))
1746 info->restrict_address_family_inet = !allow_list;
1747 else if (streq(name, "AF_UNIX"))
1748 info->restrict_address_family_unix = !allow_list;
1749 else if (streq(name, "AF_NETLINK"))
1750 info->restrict_address_family_netlink = !allow_list;
1751 else if (streq(name, "AF_PACKET"))
1752 info->restrict_address_family_packet = !allow_list;
1753 else
1754 info->restrict_address_family_other = !allow_list;
1755 }
1756
1757 r = sd_bus_message_exit_container(m);
1758 if (r < 0)
1759 return r;
1760
1761 return sd_bus_message_exit_container(m);
1762 }
1763
1764 static int property_read_system_call_filter(
1765 sd_bus *bus,
1766 const char *member,
1767 sd_bus_message *m,
1768 sd_bus_error *error,
1769 void *userdata) {
1770
1771 struct security_info *info = userdata;
1772 int allow_list, r;
1773
1774 assert(bus);
1775 assert(member);
1776 assert(m);
1777
1778 r = sd_bus_message_enter_container(m, 'r', "bas");
1779 if (r < 0)
1780 return r;
1781
1782 r = sd_bus_message_read(m, "b", &allow_list);
1783 if (r < 0)
1784 return r;
1785
1786 info->system_call_filter_allow_list = allow_list;
1787
1788 r = sd_bus_message_enter_container(m, 'a', "s");
1789 if (r < 0)
1790 return r;
1791
1792 for (;;) {
1793 const char *name;
1794
1795 r = sd_bus_message_read(m, "s", &name);
1796 if (r < 0)
1797 return r;
1798 if (r == 0)
1799 break;
1800
1801 r = set_put_strdup(&info->system_call_filter, name);
1802 if (r < 0)
1803 return r;
1804 }
1805
1806 r = sd_bus_message_exit_container(m);
1807 if (r < 0)
1808 return r;
1809
1810 return sd_bus_message_exit_container(m);
1811 }
1812
1813 static int property_read_ip_address_allow(
1814 sd_bus *bus,
1815 const char *member,
1816 sd_bus_message *m,
1817 sd_bus_error *error,
1818 void *userdata) {
1819
1820 struct security_info *info = userdata;
1821 bool deny_ipv4 = false, deny_ipv6 = false;
1822 int r;
1823
1824 assert(bus);
1825 assert(member);
1826 assert(m);
1827
1828 r = sd_bus_message_enter_container(m, 'a', "(iayu)");
1829 if (r < 0)
1830 return r;
1831
1832 for (;;) {
1833 const void *data;
1834 size_t size;
1835 int32_t family;
1836 uint32_t prefixlen;
1837
1838 r = sd_bus_message_enter_container(m, 'r', "iayu");
1839 if (r < 0)
1840 return r;
1841 if (r == 0)
1842 break;
1843
1844 r = sd_bus_message_read(m, "i", &family);
1845 if (r < 0)
1846 return r;
1847
1848 r = sd_bus_message_read_array(m, 'y', &data, &size);
1849 if (r < 0)
1850 return r;
1851
1852 r = sd_bus_message_read(m, "u", &prefixlen);
1853 if (r < 0)
1854 return r;
1855
1856 r = sd_bus_message_exit_container(m);
1857 if (r < 0)
1858 return r;
1859
1860 if (streq(member, "IPAddressAllow")) {
1861 union in_addr_union u;
1862
1863 if (family == AF_INET && size == 4 && prefixlen == 8)
1864 memcpy(&u.in, data, size);
1865 else if (family == AF_INET6 && size == 16 && prefixlen == 128)
1866 memcpy(&u.in6, data, size);
1867 else {
1868 info->ip_address_allow_other = true;
1869 continue;
1870 }
1871
1872 if (in_addr_is_localhost(family, &u))
1873 info->ip_address_allow_localhost = true;
1874 else
1875 info->ip_address_allow_other = true;
1876 } else {
1877 assert(streq(member, "IPAddressDeny"));
1878
1879 if (family == AF_INET && size == 4 && prefixlen == 0)
1880 deny_ipv4 = true;
1881 else if (family == AF_INET6 && size == 16 && prefixlen == 0)
1882 deny_ipv6 = true;
1883 }
1884 }
1885
1886 info->ip_address_deny_all = deny_ipv4 && deny_ipv6;
1887
1888 return sd_bus_message_exit_container(m);
1889 }
1890
1891 static int property_read_ip_filters(
1892 sd_bus *bus,
1893 const char *member,
1894 sd_bus_message *m,
1895 sd_bus_error *error,
1896 void *userdata) {
1897
1898 struct security_info *info = userdata;
1899 _cleanup_(strv_freep) char **l = NULL;
1900 int r;
1901
1902 assert(bus);
1903 assert(member);
1904 assert(m);
1905
1906 r = sd_bus_message_read_strv(m, &l);
1907 if (r < 0)
1908 return r;
1909
1910 if (streq(member, "IPIngressFilterPath"))
1911 info->ip_filters_custom_ingress = !strv_isempty(l);
1912 else if (streq(member, "IPEgressFilterPath"))
1913 info->ip_filters_custom_ingress = !strv_isempty(l);
1914
1915 return 0;
1916 }
1917
1918 static int property_read_device_allow(
1919 sd_bus *bus,
1920 const char *member,
1921 sd_bus_message *m,
1922 sd_bus_error *error,
1923 void *userdata) {
1924
1925 struct security_info *info = userdata;
1926 size_t n = 0;
1927 int r;
1928
1929 assert(bus);
1930 assert(member);
1931 assert(m);
1932
1933 r = sd_bus_message_enter_container(m, 'a', "(ss)");
1934 if (r < 0)
1935 return r;
1936
1937 for (;;) {
1938 const char *name, *policy;
1939
1940 r = sd_bus_message_read(m, "(ss)", &name, &policy);
1941 if (r < 0)
1942 return r;
1943 if (r == 0)
1944 break;
1945
1946 n++;
1947 }
1948
1949 info->device_allow_non_empty = n > 0;
1950
1951 return sd_bus_message_exit_container(m);
1952 }
1953
1954 static int acquire_security_info(sd_bus *bus, const char *name, struct security_info *info, AnalyzeSecurityFlags flags) {
1955
1956 static const struct bus_properties_map security_map[] = {
1957 { "AmbientCapabilities", "t", NULL, offsetof(struct security_info, ambient_capabilities) },
1958 { "CapabilityBoundingSet", "t", NULL, offsetof(struct security_info, capability_bounding_set) },
1959 { "DefaultDependencies", "b", NULL, offsetof(struct security_info, default_dependencies) },
1960 { "Delegate", "b", NULL, offsetof(struct security_info, delegate) },
1961 { "DeviceAllow", "a(ss)", property_read_device_allow, 0 },
1962 { "DevicePolicy", "s", NULL, offsetof(struct security_info, device_policy) },
1963 { "DynamicUser", "b", NULL, offsetof(struct security_info, dynamic_user) },
1964 { "FragmentPath", "s", NULL, offsetof(struct security_info, fragment_path) },
1965 { "IPAddressAllow", "a(iayu)", property_read_ip_address_allow, 0 },
1966 { "IPAddressDeny", "a(iayu)", property_read_ip_address_allow, 0 },
1967 { "IPIngressFilterPath", "as", property_read_ip_filters, 0 },
1968 { "IPEgressFilterPath", "as", property_read_ip_filters, 0 },
1969 { "Id", "s", NULL, offsetof(struct security_info, id) },
1970 { "KeyringMode", "s", NULL, offsetof(struct security_info, keyring_mode) },
1971 { "ProtectProc", "s", NULL, offsetof(struct security_info, protect_proc) },
1972 { "ProcSubset", "s", NULL, offsetof(struct security_info, proc_subset) },
1973 { "LoadState", "s", NULL, offsetof(struct security_info, load_state) },
1974 { "LockPersonality", "b", NULL, offsetof(struct security_info, lock_personality) },
1975 { "MemoryDenyWriteExecute", "b", NULL, offsetof(struct security_info, memory_deny_write_execute) },
1976 { "NoNewPrivileges", "b", NULL, offsetof(struct security_info, no_new_privileges) },
1977 { "NotifyAccess", "s", NULL, offsetof(struct security_info, notify_access) },
1978 { "PrivateDevices", "b", NULL, offsetof(struct security_info, private_devices) },
1979 { "PrivateMounts", "b", NULL, offsetof(struct security_info, private_mounts) },
1980 { "PrivateNetwork", "b", NULL, offsetof(struct security_info, private_network) },
1981 { "PrivateTmp", "b", NULL, offsetof(struct security_info, private_tmp) },
1982 { "PrivateUsers", "b", NULL, offsetof(struct security_info, private_users) },
1983 { "ProtectControlGroups", "b", NULL, offsetof(struct security_info, protect_control_groups) },
1984 { "ProtectHome", "s", NULL, offsetof(struct security_info, protect_home) },
1985 { "ProtectHostname", "b", NULL, offsetof(struct security_info, protect_hostname) },
1986 { "ProtectKernelModules", "b", NULL, offsetof(struct security_info, protect_kernel_modules) },
1987 { "ProtectKernelTunables", "b", NULL, offsetof(struct security_info, protect_kernel_tunables) },
1988 { "ProtectKernelLogs", "b", NULL, offsetof(struct security_info, protect_kernel_logs) },
1989 { "ProtectClock", "b", NULL, offsetof(struct security_info, protect_clock) },
1990 { "ProtectSystem", "s", NULL, offsetof(struct security_info, protect_system) },
1991 { "RemoveIPC", "b", NULL, offsetof(struct security_info, remove_ipc) },
1992 { "RestrictAddressFamilies", "(bas)", property_read_restrict_address_families, 0 },
1993 { "RestrictNamespaces", "t", NULL, offsetof(struct security_info, restrict_namespaces) },
1994 { "RestrictRealtime", "b", NULL, offsetof(struct security_info, restrict_realtime) },
1995 { "RestrictSUIDSGID", "b", NULL, offsetof(struct security_info, restrict_suid_sgid) },
1996 { "RootDirectory", "s", NULL, offsetof(struct security_info, root_directory) },
1997 { "RootImage", "s", NULL, offsetof(struct security_info, root_image) },
1998 { "SupplementaryGroups", "as", NULL, offsetof(struct security_info, supplementary_groups) },
1999 { "SystemCallArchitectures", "as", NULL, offsetof(struct security_info, system_call_architectures) },
2000 { "SystemCallFilter", "(as)", property_read_system_call_filter, 0 },
2001 { "Type", "s", NULL, offsetof(struct security_info, type) },
2002 { "UMask", "u", NULL, offsetof(struct security_info, _umask) },
2003 { "User", "s", NULL, offsetof(struct security_info, user) },
2004 {}
2005 };
2006
2007 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2008 _cleanup_free_ char *path = NULL;
2009 int r;
2010
2011 /* Note: this mangles *info on failure! */
2012
2013 assert(bus);
2014 assert(name);
2015 assert(info);
2016
2017 path = unit_dbus_path_from_name(name);
2018 if (!path)
2019 return log_oom();
2020
2021 r = bus_map_all_properties(
2022 bus,
2023 "org.freedesktop.systemd1",
2024 path,
2025 security_map,
2026 BUS_MAP_STRDUP | BUS_MAP_BOOLEAN_AS_BOOL,
2027 &error,
2028 NULL,
2029 info);
2030 if (r < 0)
2031 return log_error_errno(r, "Failed to get unit properties: %s", bus_error_message(&error, r));
2032
2033 if (!streq_ptr(info->load_state, "loaded")) {
2034
2035 if (FLAGS_SET(flags, ANALYZE_SECURITY_ONLY_LOADED))
2036 return -EMEDIUMTYPE;
2037
2038 if (streq_ptr(info->load_state, "not-found"))
2039 log_error("Unit %s not found, cannot analyze.", name);
2040 else if (streq_ptr(info->load_state, "masked"))
2041 log_error("Unit %s is masked, cannot analyze.", name);
2042 else
2043 log_error("Unit %s not loaded properly, cannot analyze.", name);
2044
2045 return -EINVAL;
2046 }
2047
2048 if (FLAGS_SET(flags, ANALYZE_SECURITY_ONLY_LONG_RUNNING) && streq_ptr(info->type, "oneshot"))
2049 return -EMEDIUMTYPE;
2050
2051 if (info->private_devices ||
2052 info->private_tmp ||
2053 info->protect_control_groups ||
2054 info->protect_kernel_tunables ||
2055 info->protect_kernel_modules ||
2056 !streq_ptr(info->protect_home, "no") ||
2057 !streq_ptr(info->protect_system, "no") ||
2058 info->root_image)
2059 info->private_mounts = true;
2060
2061 if (info->protect_kernel_modules)
2062 info->capability_bounding_set &= ~(UINT64_C(1) << CAP_SYS_MODULE);
2063
2064 if (info->protect_kernel_logs)
2065 info->capability_bounding_set &= ~(UINT64_C(1) << CAP_SYSLOG);
2066
2067 if (info->protect_clock)
2068 info->capability_bounding_set &= ~((UINT64_C(1) << CAP_SYS_TIME) |
2069 (UINT64_C(1) << CAP_WAKE_ALARM));
2070
2071 if (info->private_devices)
2072 info->capability_bounding_set &= ~((UINT64_C(1) << CAP_MKNOD) |
2073 (UINT64_C(1) << CAP_SYS_RAWIO));
2074
2075 return 0;
2076 }
2077
2078 static int analyze_security_one(sd_bus *bus, const char *name, Table *overview_table, AnalyzeSecurityFlags flags) {
2079 _cleanup_(security_info_free) struct security_info info = {
2080 .default_dependencies = true,
2081 .capability_bounding_set = UINT64_MAX,
2082 .restrict_namespaces = UINT64_MAX,
2083 ._umask = 0002,
2084 };
2085 int r;
2086
2087 assert(bus);
2088 assert(name);
2089
2090 r = acquire_security_info(bus, name, &info, flags);
2091 if (r == -EMEDIUMTYPE) /* Ignore this one because not loaded or Type is oneshot */
2092 return 0;
2093 if (r < 0)
2094 return r;
2095
2096 r = assess(&info, overview_table, flags);
2097 if (r < 0)
2098 return r;
2099
2100 return 0;
2101 }
2102
2103 int analyze_security(sd_bus *bus, char **units, AnalyzeSecurityFlags flags) {
2104 _cleanup_(table_unrefp) Table *overview_table = NULL;
2105 int ret = 0, r;
2106
2107 assert(bus);
2108
2109 if (strv_length(units) != 1) {
2110 overview_table = table_new("unit", "exposure", "predicate", "happy");
2111 if (!overview_table)
2112 return log_oom();
2113 }
2114
2115 if (strv_isempty(units)) {
2116 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2117 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
2118 _cleanup_strv_free_ char **list = NULL;
2119 size_t n = 0;
2120 char **i;
2121
2122 r = sd_bus_call_method(
2123 bus,
2124 "org.freedesktop.systemd1",
2125 "/org/freedesktop/systemd1",
2126 "org.freedesktop.systemd1.Manager",
2127 "ListUnits",
2128 &error,
2129 &reply,
2130 NULL);
2131 if (r < 0)
2132 return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r));
2133
2134 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
2135 if (r < 0)
2136 return bus_log_parse_error(r);
2137
2138 for (;;) {
2139 UnitInfo info;
2140 char *copy = NULL;
2141
2142 r = bus_parse_unit_info(reply, &info);
2143 if (r < 0)
2144 return bus_log_parse_error(r);
2145 if (r == 0)
2146 break;
2147
2148 if (!endswith(info.id, ".service"))
2149 continue;
2150
2151 if (!GREEDY_REALLOC(list, n + 2))
2152 return log_oom();
2153
2154 copy = strdup(info.id);
2155 if (!copy)
2156 return log_oom();
2157
2158 list[n++] = copy;
2159 list[n] = NULL;
2160 }
2161
2162 strv_sort(list);
2163
2164 flags |= ANALYZE_SECURITY_SHORT|ANALYZE_SECURITY_ONLY_LOADED|ANALYZE_SECURITY_ONLY_LONG_RUNNING;
2165
2166 STRV_FOREACH(i, list) {
2167 r = analyze_security_one(bus, *i, overview_table, flags);
2168 if (r < 0 && ret >= 0)
2169 ret = r;
2170 }
2171
2172 } else {
2173 char **i;
2174
2175 STRV_FOREACH(i, units) {
2176 _cleanup_free_ char *mangled = NULL, *instance = NULL;
2177 const char *name;
2178
2179 if (!FLAGS_SET(flags, ANALYZE_SECURITY_SHORT) && i != units) {
2180 putc('\n', stdout);
2181 fflush(stdout);
2182 }
2183
2184 r = unit_name_mangle(*i, 0, &mangled);
2185 if (r < 0)
2186 return log_error_errno(r, "Failed to mangle unit name '%s': %m", *i);
2187
2188 if (!endswith(mangled, ".service"))
2189 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2190 "Unit %s is not a service unit, refusing.",
2191 *i);
2192
2193 if (unit_name_is_valid(mangled, UNIT_NAME_TEMPLATE)) {
2194 r = unit_name_replace_instance(mangled, "test-instance", &instance);
2195 if (r < 0)
2196 return log_oom();
2197
2198 name = instance;
2199 } else
2200 name = mangled;
2201
2202 r = analyze_security_one(bus, name, overview_table, flags);
2203 if (r < 0 && ret >= 0)
2204 ret = r;
2205 }
2206 }
2207
2208 if (overview_table) {
2209 if (!FLAGS_SET(flags, ANALYZE_SECURITY_SHORT)) {
2210 putc('\n', stdout);
2211 fflush(stdout);
2212 }
2213
2214 r = table_print(overview_table, stdout);
2215 if (r < 0)
2216 return log_error_errno(r, "Failed to output table: %m");
2217 }
2218
2219 return ret;
2220 }