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