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