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