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