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