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