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