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