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