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