1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <sys/utsname.h>
4 
5 #include "af-list.h"
6 #include "analyze.h"
7 #include "analyze-security.h"
8 #include "analyze-verify.h"
9 #include "bus-error.h"
10 #include "bus-map-properties.h"
11 #include "bus-unit-util.h"
12 #include "bus-util.h"
13 #include "copy.h"
14 #include "env-util.h"
15 #include "fd-util.h"
16 #include "fileio.h"
17 #include "format-table.h"
18 #include "in-addr-prefix-util.h"
19 #include "locale-util.h"
20 #include "macro.h"
21 #include "manager.h"
22 #include "missing_capability.h"
23 #include "missing_sched.h"
24 #include "mkdir.h"
25 #include "nulstr-util.h"
26 #include "parse-util.h"
27 #include "path-util.h"
28 #include "pretty-print.h"
29 #if HAVE_SECCOMP
30 #  include "seccomp-util.h"
31 #endif
32 #include "service.h"
33 #include "set.h"
34 #include "stdio-util.h"
35 #include "strv.h"
36 #include "terminal-util.h"
37 #include "unit-def.h"
38 #include "unit-name.h"
39 #include "unit-serialize.h"
40 
41 typedef struct SecurityInfo {
42         char *id;
43         char *type;
44         char *load_state;
45         char *fragment_path;
46         bool default_dependencies;
47 
48         uint64_t ambient_capabilities;
49         uint64_t capability_bounding_set;
50 
51         char *user;
52         char **supplementary_groups;
53         bool dynamic_user;
54 
55         bool ip_address_deny_all;
56         bool ip_address_allow_localhost;
57         bool ip_address_allow_other;
58 
59         bool ip_filters_custom_ingress;
60         bool ip_filters_custom_egress;
61 
62         char *keyring_mode;
63         char *protect_proc;
64         char *proc_subset;
65         bool lock_personality;
66         bool memory_deny_write_execute;
67         bool no_new_privileges;
68         char *notify_access;
69         bool protect_hostname;
70 
71         bool private_devices;
72         bool private_mounts;
73         bool private_network;
74         bool private_tmp;
75         bool private_users;
76 
77         bool protect_control_groups;
78         bool protect_kernel_modules;
79         bool protect_kernel_tunables;
80         bool protect_kernel_logs;
81         bool protect_clock;
82 
83         char *protect_home;
84         char *protect_system;
85 
86         bool remove_ipc;
87 
88         bool restrict_address_family_inet;
89         bool restrict_address_family_unix;
90         bool restrict_address_family_netlink;
91         bool restrict_address_family_packet;
92         bool restrict_address_family_other;
93 
94         unsigned long long restrict_namespaces;
95         bool restrict_realtime;
96         bool restrict_suid_sgid;
97 
98         char *root_directory;
99         char *root_image;
100 
101         bool delegate;
102         char *device_policy;
103         char **device_allow;
104 
105         Set *system_call_architectures;
106 
107         bool system_call_filter_allow_list;
108         Hashmap *system_call_filter;
109 
110         mode_t _umask;
111 } SecurityInfo;
112 
113 struct security_assessor {
114         const char *id;
115         const char *json_field;
116         const char *description_good;
117         const char *description_bad;
118         const char *description_na;
119         const char *url;
120         uint64_t weight;
121         uint64_t range;
122         int (*assess)(
123                 const struct security_assessor *a,
124                 const SecurityInfo *info,
125                 const void *data,
126                 uint64_t *ret_badness,
127                 char **ret_description);
128         size_t offset;
129         uint64_t parameter;
130         bool default_dependencies_only;
131 };
132 
security_info_new(void)133 static SecurityInfo *security_info_new(void) {
134         SecurityInfo *info = new(SecurityInfo, 1);
135         if (!info)
136                 return NULL;
137 
138         *info = (SecurityInfo) {
139                 .default_dependencies = true,
140                 .capability_bounding_set = UINT64_MAX,
141                 .restrict_namespaces = UINT64_MAX,
142                 ._umask = 0002,
143         };
144 
145         return info;
146 }
147 
security_info_free(SecurityInfo * i)148 static SecurityInfo *security_info_free(SecurityInfo *i) {
149         if (!i)
150                 return NULL;
151 
152         free(i->id);
153         free(i->type);
154         free(i->load_state);
155         free(i->fragment_path);
156 
157         free(i->user);
158 
159         free(i->protect_home);
160         free(i->protect_system);
161 
162         free(i->root_directory);
163         free(i->root_image);
164 
165         free(i->keyring_mode);
166         free(i->protect_proc);
167         free(i->proc_subset);
168         free(i->notify_access);
169 
170         free(i->device_policy);
171         strv_free(i->device_allow);
172 
173         strv_free(i->supplementary_groups);
174         set_free(i->system_call_architectures);
175 
176         hashmap_free(i->system_call_filter);
177 
178         return mfree(i);
179 }
180 
181 DEFINE_TRIVIAL_CLEANUP_FUNC(SecurityInfo*, security_info_free);
182 
security_info_runs_privileged(const SecurityInfo * i)183 static bool security_info_runs_privileged(const SecurityInfo *i)  {
184         assert(i);
185 
186         if (STRPTR_IN_SET(i->user, "0", "root"))
187                 return true;
188 
189         if (i->dynamic_user)
190                 return false;
191 
192         return isempty(i->user);
193 }
194 
assess_bool(const struct security_assessor * a,const SecurityInfo * info,const void * data,uint64_t * ret_badness,char ** ret_description)195 static int assess_bool(
196                 const struct security_assessor *a,
197                 const SecurityInfo *info,
198                 const void *data,
199                 uint64_t *ret_badness,
200                 char **ret_description) {
201 
202         const bool *b = data;
203 
204         assert(b);
205         assert(ret_badness);
206         assert(ret_description);
207 
208         *ret_badness = a->parameter ? *b : !*b;
209         *ret_description = NULL;
210 
211         return 0;
212 }
213 
assess_user(const struct security_assessor * a,const SecurityInfo * info,const void * data,uint64_t * ret_badness,char ** ret_description)214 static int assess_user(
215                 const struct security_assessor *a,
216                 const SecurityInfo *info,
217                 const void *data,
218                 uint64_t *ret_badness,
219                 char **ret_description) {
220 
221         _cleanup_free_ char *d = NULL;
222         uint64_t b;
223 
224         assert(ret_badness);
225         assert(ret_description);
226 
227         if (streq_ptr(info->user, NOBODY_USER_NAME)) {
228                 d = strdup("Service runs under as '" NOBODY_USER_NAME "' user, which should not be used for services");
229                 b = 9;
230         } else if (info->dynamic_user && !STR_IN_SET(info->user, "0", "root")) {
231                 d = strdup("Service runs under a transient non-root user identity");
232                 b = 0;
233         } else if (info->user && !STR_IN_SET(info->user, "0", "root", "")) {
234                 d = strdup("Service runs under a static non-root user identity");
235                 b = 0;
236         } else {
237                 *ret_badness = 10;
238                 *ret_description = NULL;
239                 return 0;
240         }
241 
242         if (!d)
243                 return log_oom();
244 
245         *ret_badness = b;
246         *ret_description = TAKE_PTR(d);
247 
248         return 0;
249 }
250 
assess_protect_home(const struct security_assessor * a,const SecurityInfo * info,const void * data,uint64_t * ret_badness,char ** ret_description)251 static int assess_protect_home(
252                 const struct security_assessor *a,
253                 const SecurityInfo *info,
254                 const void *data,
255                 uint64_t *ret_badness,
256                 char **ret_description) {
257 
258         const char *description;
259         uint64_t badness;
260         char *copy;
261         int r;
262 
263         assert(ret_badness);
264         assert(ret_description);
265 
266         badness = 10;
267         description = "Service has full access to home directories";
268 
269         r = parse_boolean(info->protect_home);
270         if (r < 0) {
271                 if (streq_ptr(info->protect_home, "read-only")) {
272                         badness = 5;
273                         description = "Service has read-only access to home directories";
274                 } else if (streq_ptr(info->protect_home, "tmpfs")) {
275                         badness = 1;
276                         description = "Service has access to fake empty home directories";
277                 }
278         } else if (r > 0) {
279                 badness = 0;
280                 description = "Service has no access to home directories";
281         }
282 
283         copy = strdup(description);
284         if (!copy)
285                 return log_oom();
286 
287         *ret_badness = badness;
288         *ret_description = copy;
289 
290         return 0;
291 }
292 
assess_protect_system(const struct security_assessor * a,const SecurityInfo * info,const void * data,uint64_t * ret_badness,char ** ret_description)293 static int assess_protect_system(
294                 const struct security_assessor *a,
295                 const SecurityInfo *info,
296                 const void *data,
297                 uint64_t *ret_badness,
298                 char **ret_description) {
299 
300         const char *description;
301         uint64_t badness;
302         char *copy;
303         int r;
304 
305         assert(ret_badness);
306         assert(ret_description);
307 
308         badness = 10;
309         description = "Service has full access to the OS file hierarchy";
310 
311         r = parse_boolean(info->protect_system);
312         if (r < 0) {
313                 if (streq_ptr(info->protect_system, "full")) {
314                         badness = 3;
315                         description = "Service has very limited write access to the OS file hierarchy";
316                 } else if (streq_ptr(info->protect_system, "strict")) {
317                         badness = 0;
318                         description = "Service has strict read-only access to the OS file hierarchy";
319                 }
320         } else if (r > 0) {
321                 badness = 5;
322                 description = "Service has limited write access to the OS file hierarchy";
323         }
324 
325         copy = strdup(description);
326         if (!copy)
327                 return log_oom();
328 
329         *ret_badness = badness;
330         *ret_description = copy;
331 
332         return 0;
333 }
334 
assess_root_directory(const struct security_assessor * a,const SecurityInfo * info,const void * data,uint64_t * ret_badness,char ** ret_description)335 static int assess_root_directory(
336                 const struct security_assessor *a,
337                 const SecurityInfo *info,
338                 const void *data,
339                 uint64_t *ret_badness,
340                 char **ret_description) {
341 
342         assert(ret_badness);
343         assert(ret_description);
344 
345         *ret_badness =
346                 empty_or_root(info->root_directory) &&
347                 empty_or_root(info->root_image);
348         *ret_description = NULL;
349 
350         return 0;
351 }
352 
assess_capability_bounding_set(const struct security_assessor * a,const SecurityInfo * info,const void * data,uint64_t * ret_badness,char ** ret_description)353 static int assess_capability_bounding_set(
354                 const struct security_assessor *a,
355                 const SecurityInfo *info,
356                 const void *data,
357                 uint64_t *ret_badness,
358                 char **ret_description) {
359 
360         assert(ret_badness);
361         assert(ret_description);
362 
363         *ret_badness = !!(info->capability_bounding_set & a->parameter);
364         *ret_description = NULL;
365 
366         return 0;
367 }
368 
assess_umask(const struct security_assessor * a,const SecurityInfo * info,const void * data,uint64_t * ret_badness,char ** ret_description)369 static int assess_umask(
370                 const struct security_assessor *a,
371                 const SecurityInfo *info,
372                 const void *data,
373                 uint64_t *ret_badness,
374                 char **ret_description) {
375 
376         char *copy = NULL;
377         const char *d;
378         uint64_t b;
379 
380         assert(ret_badness);
381         assert(ret_description);
382 
383         if (!FLAGS_SET(info->_umask, 0002)) {
384                 d = "Files created by service are world-writable by default";
385                 b = 10;
386         } else if (!FLAGS_SET(info->_umask, 0004)) {
387                 d = "Files created by service are world-readable by default";
388                 b = 5;
389         } else if (!FLAGS_SET(info->_umask, 0020)) {
390                 d = "Files created by service are group-writable by default";
391                 b = 2;
392         } else if (!FLAGS_SET(info->_umask, 0040)) {
393                 d = "Files created by service are group-readable by default";
394                 b = 1;
395         } else {
396                 d = "Files created by service are accessible only by service's own user by default";
397                 b = 0;
398         }
399 
400         copy = strdup(d);
401         if (!copy)
402                 return log_oom();
403 
404         *ret_badness = b;
405         *ret_description = copy;
406 
407         return 0;
408 }
409 
assess_keyring_mode(const struct security_assessor * a,const SecurityInfo * info,const void * data,uint64_t * ret_badness,char ** ret_description)410 static int assess_keyring_mode(
411                 const struct security_assessor *a,
412                 const SecurityInfo *info,
413                 const void *data,
414                 uint64_t *ret_badness,
415                 char **ret_description) {
416 
417         assert(ret_badness);
418         assert(ret_description);
419 
420         *ret_badness = !streq_ptr(info->keyring_mode, "private");
421         *ret_description = NULL;
422 
423         return 0;
424 }
425 
assess_protect_proc(const struct security_assessor * a,const SecurityInfo * info,const void * data,uint64_t * ret_badness,char ** ret_description)426 static int assess_protect_proc(
427                 const struct security_assessor *a,
428                 const SecurityInfo *info,
429                 const void *data,
430                 uint64_t *ret_badness,
431                 char **ret_description) {
432 
433         assert(ret_badness);
434         assert(ret_description);
435 
436         if (streq_ptr(info->protect_proc, "noaccess"))
437                 *ret_badness = 1;
438         else if (STRPTR_IN_SET(info->protect_proc, "invisible", "ptraceable"))
439                 *ret_badness = 0;
440         else
441                 *ret_badness = 3;
442 
443         *ret_description = NULL;
444 
445         return 0;
446 }
447 
assess_proc_subset(const struct security_assessor * a,const SecurityInfo * info,const void * data,uint64_t * ret_badness,char ** ret_description)448 static int assess_proc_subset(
449                 const struct security_assessor *a,
450                 const SecurityInfo *info,
451                 const void *data,
452                 uint64_t *ret_badness,
453                 char **ret_description) {
454 
455         assert(ret_badness);
456         assert(ret_description);
457 
458         *ret_badness = !streq_ptr(info->proc_subset, "pid");
459         *ret_description = NULL;
460 
461         return 0;
462 }
463 
assess_notify_access(const struct security_assessor * a,const SecurityInfo * info,const void * data,uint64_t * ret_badness,char ** ret_description)464 static int assess_notify_access(
465                 const struct security_assessor *a,
466                 const SecurityInfo *info,
467                 const void *data,
468                 uint64_t *ret_badness,
469                 char **ret_description) {
470 
471         assert(ret_badness);
472         assert(ret_description);
473 
474         *ret_badness = streq_ptr(info->notify_access, "all");
475         *ret_description = NULL;
476 
477         return 0;
478 }
479 
assess_remove_ipc(const struct security_assessor * a,const SecurityInfo * info,const void * data,uint64_t * ret_badness,char ** ret_description)480 static int assess_remove_ipc(
481                 const struct security_assessor *a,
482                 const SecurityInfo *info,
483                 const void *data,
484                 uint64_t *ret_badness,
485                 char **ret_description) {
486 
487         assert(ret_badness);
488         assert(ret_description);
489 
490         if (security_info_runs_privileged(info))
491                 *ret_badness = UINT64_MAX;
492         else
493                 *ret_badness = !info->remove_ipc;
494 
495         *ret_description = NULL;
496         return 0;
497 }
498 
assess_supplementary_groups(const struct security_assessor * a,const SecurityInfo * info,const void * data,uint64_t * ret_badness,char ** ret_description)499 static int assess_supplementary_groups(
500                 const struct security_assessor *a,
501                 const SecurityInfo *info,
502                 const void *data,
503                 uint64_t *ret_badness,
504                 char **ret_description) {
505 
506         assert(ret_badness);
507         assert(ret_description);
508 
509         if (security_info_runs_privileged(info))
510                 *ret_badness = UINT64_MAX;
511         else
512                 *ret_badness = !strv_isempty(info->supplementary_groups);
513 
514         *ret_description = NULL;
515         return 0;
516 }
517 
assess_restrict_namespaces(const struct security_assessor * a,const SecurityInfo * info,const void * data,uint64_t * ret_badness,char ** ret_description)518 static int assess_restrict_namespaces(
519                 const struct security_assessor *a,
520                 const SecurityInfo *info,
521                 const void *data,
522                 uint64_t *ret_badness,
523                 char **ret_description) {
524 
525         assert(ret_badness);
526         assert(ret_description);
527 
528         *ret_badness = !!(info->restrict_namespaces & a->parameter);
529         *ret_description = NULL;
530 
531         return 0;
532 }
533 
534 #if HAVE_SECCOMP
535 
assess_system_call_architectures(const struct security_assessor * a,const SecurityInfo * info,const void * data,uint64_t * ret_badness,char ** ret_description)536 static int assess_system_call_architectures(
537                 const struct security_assessor *a,
538                 const SecurityInfo *info,
539                 const void *data,
540                 uint64_t *ret_badness,
541                 char **ret_description) {
542 
543         char *d;
544         uint64_t b;
545 
546         assert(ret_badness);
547         assert(ret_description);
548 
549         if (set_isempty(info->system_call_architectures)) {
550                 b = 10;
551                 d = strdup("Service may execute system calls with all ABIs");
552         } else if (set_contains(info->system_call_architectures, "native") &&
553                    set_size(info->system_call_architectures) == 1) {
554                 b = 0;
555                 d = strdup("Service may execute system calls only with native ABI");
556         } else {
557                 b = 8;
558                 d = strdup("Service may execute system calls with multiple ABIs");
559         }
560 
561         if (!d)
562                 return log_oom();
563 
564         *ret_badness = b;
565         *ret_description = d;
566 
567         return 0;
568 }
569 
syscall_names_in_filter(Hashmap * s,bool allow_list,const SyscallFilterSet * f,const char ** ret_offending_syscall)570 static bool syscall_names_in_filter(Hashmap *s, bool allow_list, const SyscallFilterSet *f, const char **ret_offending_syscall) {
571         const char *syscall;
572 
573         NULSTR_FOREACH(syscall, f->value) {
574                 int id;
575 
576                 if (syscall[0] == '@') {
577                         const SyscallFilterSet *g;
578 
579                         assert_se(g = syscall_filter_set_find(syscall));
580                         if (syscall_names_in_filter(s, allow_list, g, ret_offending_syscall))
581                                 return true; /* bad! */
582 
583                         continue;
584                 }
585 
586                 /* Let's see if the system call actually exists on this platform, before complaining */
587                 id = seccomp_syscall_resolve_name(syscall);
588                 if (id < 0)
589                         continue;
590 
591                 if (hashmap_contains(s, syscall) != allow_list) {
592                         log_debug("Offending syscall filter item: %s", syscall);
593                         if (ret_offending_syscall)
594                                 *ret_offending_syscall = syscall;
595                         return true; /* bad! */
596                 }
597         }
598 
599         *ret_offending_syscall = NULL;
600         return false;
601 }
602 
assess_system_call_filter(const struct security_assessor * a,const SecurityInfo * info,const void * data,uint64_t * ret_badness,char ** ret_description)603 static int assess_system_call_filter(
604                 const struct security_assessor *a,
605                 const SecurityInfo *info,
606                 const void *data,
607                 uint64_t *ret_badness,
608                 char **ret_description) {
609 
610         assert(a);
611         assert(info);
612         assert(ret_badness);
613         assert(ret_description);
614 
615         assert(a->parameter < _SYSCALL_FILTER_SET_MAX);
616         const SyscallFilterSet *f = syscall_filter_sets + a->parameter;
617 
618         _cleanup_free_ char *d = NULL;
619         uint64_t b;
620         int r;
621 
622         if (!info->system_call_filter_allow_list && hashmap_isempty(info->system_call_filter)) {
623                 r = free_and_strdup(&d, "Service does not filter system calls");
624                 b = 10;
625         } else {
626                 bool bad;
627                 const char *offender = NULL;
628 
629                 log_debug("Analyzing system call filter, checking against: %s", f->name);
630                 bad = syscall_names_in_filter(info->system_call_filter, info->system_call_filter_allow_list, f, &offender);
631                 log_debug("Result: %s", bad ? "bad" : "good");
632 
633                 if (info->system_call_filter_allow_list) {
634                         if (bad) {
635                                 r = asprintf(&d, "System call allow list defined for service, and %s is included "
636                                              "(e.g. %s is allowed)",
637                                              f->name, offender);
638                                 b = 9;
639                         } else {
640                                 r = asprintf(&d, "System call allow list defined for service, and %s is not included",
641                                              f->name);
642                                 b = 0;
643                         }
644                 } else {
645                         if (bad) {
646                                 r = asprintf(&d, "System call deny list defined for service, and %s is not included "
647                                              "(e.g. %s is allowed)",
648                                              f->name, offender);
649                                 b = 10;
650                         } else {
651                                 r = asprintf(&d, "System call deny list defined for service, and %s is included",
652                                              f->name);
653                                 b = 0;
654                         }
655                 }
656         }
657         if (r < 0)
658                 return log_oom();
659 
660         *ret_badness = b;
661         *ret_description = TAKE_PTR(d);
662 
663         return 0;
664 }
665 
666 #endif
667 
assess_ip_address_allow(const struct security_assessor * a,const SecurityInfo * info,const void * data,uint64_t * ret_badness,char ** ret_description)668 static int assess_ip_address_allow(
669                 const struct security_assessor *a,
670                 const SecurityInfo *info,
671                 const void *data,
672                 uint64_t *ret_badness,
673                 char **ret_description) {
674 
675         char *d = NULL;
676         uint64_t b;
677 
678         assert(info);
679         assert(ret_badness);
680         assert(ret_description);
681 
682         if (info->ip_filters_custom_ingress || info->ip_filters_custom_egress) {
683                 d = strdup("Service defines custom ingress/egress IP filters with BPF programs");
684                 b = 0;
685         } else if (!info->ip_address_deny_all) {
686                 d = strdup("Service does not define an IP address allow list");
687                 b = 10;
688         } else if (info->ip_address_allow_other) {
689                 d = strdup("Service defines IP address allow list with non-localhost entries");
690                 b = 5;
691         } else if (info->ip_address_allow_localhost) {
692                 d = strdup("Service defines IP address allow list with only localhost entries");
693                 b = 2;
694         } else {
695                 d = strdup("Service blocks all IP address ranges");
696                 b = 0;
697         }
698 
699         if (!d)
700                 return log_oom();
701 
702         *ret_badness = b;
703         *ret_description = d;
704 
705         return 0;
706 }
707 
assess_device_allow(const struct security_assessor * a,const SecurityInfo * info,const void * data,uint64_t * ret_badness,char ** ret_description)708 static int assess_device_allow(
709                 const struct security_assessor *a,
710                 const SecurityInfo *info,
711                 const void *data,
712                 uint64_t *ret_badness,
713                 char **ret_description) {
714 
715         char *d = NULL;
716         uint64_t b;
717 
718         assert(info);
719         assert(ret_badness);
720         assert(ret_description);
721 
722         if (STRPTR_IN_SET(info->device_policy, "strict", "closed")) {
723 
724                 if (!strv_isempty(info->device_allow)) {
725                         _cleanup_free_ char *join = NULL;
726 
727                         join = strv_join(info->device_allow, " ");
728                         if (!join)
729                                 return log_oom();
730 
731                         d = strjoin("Service has a device ACL with some special devices: ", join);
732                         b = 5;
733                 } else {
734                         d = strdup("Service has a minimal device ACL");
735                         b = 0;
736                 }
737         } else {
738                 d = strdup("Service has no device ACL");
739                 b = 10;
740         }
741 
742         if (!d)
743                 return log_oom();
744 
745         *ret_badness = b;
746         *ret_description = d;
747 
748         return 0;
749 }
750 
assess_ambient_capabilities(const struct security_assessor * a,const SecurityInfo * info,const void * data,uint64_t * ret_badness,char ** ret_description)751 static int assess_ambient_capabilities(
752                 const struct security_assessor *a,
753                 const SecurityInfo *info,
754                 const void *data,
755                 uint64_t *ret_badness,
756                 char **ret_description) {
757 
758         assert(ret_badness);
759         assert(ret_description);
760 
761         *ret_badness = info->ambient_capabilities != 0;
762         *ret_description = NULL;
763 
764         return 0;
765 }
766 
767 static const struct security_assessor security_assessor_table[] = {
768         {
769                 .id = "User=/DynamicUser=",
770                 .json_field = "UserOrDynamicUser",
771                 .description_bad = "Service runs as root user",
772                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#User=",
773                 .weight = 2000,
774                 .range = 10,
775                 .assess = assess_user,
776         },
777         {
778                 .id = "SupplementaryGroups=",
779                 .json_field = "SupplementaryGroups",
780                 .description_good = "Service has no supplementary groups",
781                 .description_bad = "Service runs with supplementary groups",
782                 .description_na = "Service runs as root, option does not matter",
783                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SupplementaryGroups=",
784                 .weight = 200,
785                 .range = 1,
786                 .assess = assess_supplementary_groups,
787         },
788         {
789                 .id = "PrivateDevices=",
790                 .json_field = "PrivateDevices",
791                 .description_good = "Service has no access to hardware devices",
792                 .description_bad = "Service potentially has access to hardware devices",
793                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateDevices=",
794                 .weight = 1000,
795                 .range = 1,
796                 .assess = assess_bool,
797                 .offset = offsetof(SecurityInfo, private_devices),
798         },
799         {
800                 .id = "PrivateMounts=",
801                 .json_field = "PrivateMounts",
802                 .description_good = "Service cannot install system mounts",
803                 .description_bad = "Service may install system mounts",
804                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateMounts=",
805                 .weight = 1000,
806                 .range = 1,
807                 .assess = assess_bool,
808                 .offset = offsetof(SecurityInfo, private_mounts),
809         },
810         {
811                 .id = "PrivateNetwork=",
812                 .json_field = "PrivateNetwork",
813                 .description_good = "Service has no access to the host's network",
814                 .description_bad = "Service has access to the host's network",
815                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateNetwork=",
816                 .weight = 2500,
817                 .range = 1,
818                 .assess = assess_bool,
819                 .offset = offsetof(SecurityInfo, private_network),
820         },
821         {
822                 .id = "PrivateTmp=",
823                 .json_field = "PrivateTmp",
824                 .description_good = "Service has no access to other software's temporary files",
825                 .description_bad = "Service has access to other software's temporary files",
826                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateTmp=",
827                 .weight = 1000,
828                 .range = 1,
829                 .assess = assess_bool,
830                 .offset = offsetof(SecurityInfo, private_tmp),
831                 .default_dependencies_only = true,
832         },
833         {
834                 .id = "PrivateUsers=",
835                 .json_field = "PrivateUsers",
836                 .description_good = "Service does not have access to other users",
837                 .description_bad = "Service has access to other users",
838                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateUsers=",
839                 .weight = 1000,
840                 .range = 1,
841                 .assess = assess_bool,
842                 .offset = offsetof(SecurityInfo, private_users),
843         },
844         {
845                 .id = "ProtectControlGroups=",
846                 .json_field = "ProtectControlGroups",
847                 .description_good = "Service cannot modify the control group file system",
848                 .description_bad = "Service may modify the control group file system",
849                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectControlGroups=",
850                 .weight = 1000,
851                 .range = 1,
852                 .assess = assess_bool,
853                 .offset = offsetof(SecurityInfo, protect_control_groups),
854         },
855         {
856                 .id = "ProtectKernelModules=",
857                 .json_field = "ProtectKernelModules",
858                 .description_good = "Service cannot load or read kernel modules",
859                 .description_bad = "Service may load or read kernel modules",
860                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelModules=",
861                 .weight = 1000,
862                 .range = 1,
863                 .assess = assess_bool,
864                 .offset = offsetof(SecurityInfo, protect_kernel_modules),
865         },
866         {
867                 .id = "ProtectKernelTunables=",
868                 .json_field = "ProtectKernelTunables",
869                 .description_good = "Service cannot alter kernel tunables (/proc/sys, …)",
870                 .description_bad = "Service may alter kernel tunables",
871                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelTunables=",
872                 .weight = 1000,
873                 .range = 1,
874                 .assess = assess_bool,
875                 .offset = offsetof(SecurityInfo, protect_kernel_tunables),
876         },
877         {
878                 .id = "ProtectKernelLogs=",
879                 .json_field = "ProtectKernelLogs",
880                 .description_good = "Service cannot read from or write to the kernel log ring buffer",
881                 .description_bad = "Service may read from or write to the kernel log ring buffer",
882                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelLogs=",
883                 .weight = 1000,
884                 .range = 1,
885                 .assess = assess_bool,
886                 .offset = offsetof(SecurityInfo, protect_kernel_logs),
887         },
888         {
889                 .id = "ProtectClock=",
890                 .json_field = "ProtectClock",
891                 .description_good = "Service cannot write to the hardware clock or system clock",
892                 .description_bad = "Service may write to the hardware clock or system clock",
893                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectClock=",
894                 .weight = 1000,
895                 .range = 1,
896                 .assess = assess_bool,
897                 .offset = offsetof(SecurityInfo, protect_clock),
898         },
899         {
900                 .id = "ProtectHome=",
901                 .json_field = "ProtectHome",
902                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHome=",
903                 .weight = 1000,
904                 .range = 10,
905                 .assess = assess_protect_home,
906                 .default_dependencies_only = true,
907         },
908         {
909                 .id = "ProtectHostname=",
910                 .json_field = "ProtectHostname",
911                 .description_good = "Service cannot change system host/domainname",
912                 .description_bad = "Service may change system host/domainname",
913                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHostname=",
914                 .weight = 50,
915                 .range = 1,
916                 .assess = assess_bool,
917                 .offset = offsetof(SecurityInfo, protect_hostname),
918         },
919         {
920                 .id = "ProtectSystem=",
921                 .json_field = "ProtectSystem",
922                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectSystem=",
923                 .weight = 1000,
924                 .range = 10,
925                 .assess = assess_protect_system,
926                 .default_dependencies_only = true,
927         },
928         {
929                 .id = "RootDirectory=/RootImage=",
930                 .json_field = "RootDirectoryOrRootImage",
931                 .description_good = "Service has its own root directory/image",
932                 .description_bad = "Service runs within the host's root directory",
933                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RootDirectory=",
934                 .weight = 200,
935                 .range = 1,
936                 .assess = assess_root_directory,
937                 .default_dependencies_only = true,
938         },
939         {
940                 .id = "LockPersonality=",
941                 .json_field = "LockPersonality",
942                 .description_good = "Service cannot change ABI personality",
943                 .description_bad = "Service may change ABI personality",
944                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#LockPersonality=",
945                 .weight = 100,
946                 .range = 1,
947                 .assess = assess_bool,
948                 .offset = offsetof(SecurityInfo, lock_personality),
949         },
950         {
951                 .id = "MemoryDenyWriteExecute=",
952                 .json_field = "MemoryDenyWriteExecute",
953                 .description_good = "Service cannot create writable executable memory mappings",
954                 .description_bad = "Service may create writable executable memory mappings",
955                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#MemoryDenyWriteExecute=",
956                 .weight = 100,
957                 .range = 1,
958                 .assess = assess_bool,
959                 .offset = offsetof(SecurityInfo, memory_deny_write_execute),
960         },
961         {
962                 .id = "NoNewPrivileges=",
963                 .json_field = "NoNewPrivileges",
964                 .description_good = "Service processes cannot acquire new privileges",
965                 .description_bad = "Service processes may acquire new privileges",
966                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NoNewPrivileges=",
967                 .weight = 1000,
968                 .range = 1,
969                 .assess = assess_bool,
970                 .offset = offsetof(SecurityInfo, no_new_privileges),
971         },
972         {
973                 .id = "CapabilityBoundingSet=~CAP_SYS_ADMIN",
974                 .json_field = "CapabilityBoundingSet_CAP_SYS_ADMIN",
975                 .description_good = "Service has no administrator privileges",
976                 .description_bad = "Service has administrator privileges",
977                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
978                 .weight = 1500,
979                 .range = 1,
980                 .assess = assess_capability_bounding_set,
981                 .parameter = UINT64_C(1) << CAP_SYS_ADMIN,
982         },
983         {
984                 .id = "CapabilityBoundingSet=~CAP_SET(UID|GID|PCAP)",
985                 .json_field = "CapabilityBoundingSet_CAP_SET_UID_GID_PCAP",
986                 .description_good = "Service cannot change UID/GID identities/capabilities",
987                 .description_bad = "Service may change UID/GID identities/capabilities",
988                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
989                 .weight = 1500,
990                 .range = 1,
991                 .assess = assess_capability_bounding_set,
992                 .parameter = (UINT64_C(1) << CAP_SETUID)|
993                              (UINT64_C(1) << CAP_SETGID)|
994                              (UINT64_C(1) << CAP_SETPCAP),
995         },
996         {
997                 .id = "CapabilityBoundingSet=~CAP_SYS_PTRACE",
998                 .json_field = "CapabilityBoundingSet_CAP_SYS_PTRACE",
999                 .description_good = "Service has no ptrace() debugging abilities",
1000                 .description_bad = "Service has ptrace() debugging abilities",
1001                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1002                 .weight = 1500,
1003                 .range = 1,
1004                 .assess = assess_capability_bounding_set,
1005                 .parameter = (UINT64_C(1) << CAP_SYS_PTRACE),
1006         },
1007         {
1008                 .id = "CapabilityBoundingSet=~CAP_SYS_TIME",
1009                 .json_field = "CapabilityBoundingSet_CAP_SYS_TIME",
1010                 .description_good = "Service processes cannot change the system clock",
1011                 .description_bad = "Service processes may change the system clock",
1012                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1013                 .weight = 1000,
1014                 .range = 1,
1015                 .assess = assess_capability_bounding_set,
1016                 .parameter = UINT64_C(1) << CAP_SYS_TIME,
1017         },
1018         {
1019                 .id = "CapabilityBoundingSet=~CAP_NET_ADMIN",
1020                 .json_field = "CapabilityBoundingSet_CAP_NET_ADMIN",
1021                 .description_good = "Service has no network configuration privileges",
1022                 .description_bad = "Service has network configuration privileges",
1023                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1024                 .weight = 1000,
1025                 .range = 1,
1026                 .assess = assess_capability_bounding_set,
1027                 .parameter = (UINT64_C(1) << CAP_NET_ADMIN),
1028         },
1029         {
1030                 .id = "CapabilityBoundingSet=~CAP_SYS_RAWIO",
1031                 .json_field = "CapabilityBoundingSet_CAP_SYS_RAWIO",
1032                 .description_good = "Service has no raw I/O access",
1033                 .description_bad = "Service has raw I/O access",
1034                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1035                 .weight = 1000,
1036                 .range = 1,
1037                 .assess = assess_capability_bounding_set,
1038                 .parameter = (UINT64_C(1) << CAP_SYS_RAWIO),
1039         },
1040         {
1041                 .id = "CapabilityBoundingSet=~CAP_SYS_MODULE",
1042                 .json_field = "CapabilityBoundingSet_CAP_SYS_MODULE",
1043                 .description_good = "Service cannot load kernel modules",
1044                 .description_bad = "Service may load kernel modules",
1045                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1046                 .weight = 1000,
1047                 .range = 1,
1048                 .assess = assess_capability_bounding_set,
1049                 .parameter = (UINT64_C(1) << CAP_SYS_MODULE),
1050         },
1051         {
1052                 .id = "CapabilityBoundingSet=~CAP_AUDIT_*",
1053                 .json_field = "CapabilityBoundingSet_CAP_AUDIT",
1054                 .description_good = "Service has no audit subsystem access",
1055                 .description_bad = "Service has audit subsystem access",
1056                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1057                 .weight = 500,
1058                 .range = 1,
1059                 .assess = assess_capability_bounding_set,
1060                 .parameter = (UINT64_C(1) << CAP_AUDIT_CONTROL) |
1061                              (UINT64_C(1) << CAP_AUDIT_READ) |
1062                              (UINT64_C(1) << CAP_AUDIT_WRITE),
1063         },
1064         {
1065                 .id = "CapabilityBoundingSet=~CAP_SYSLOG",
1066                 .json_field = "CapabilityBoundingSet_CAP_SYSLOG",
1067                 .description_good = "Service has no access to kernel logging",
1068                 .description_bad = "Service has access to kernel logging",
1069                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1070                 .weight = 500,
1071                 .range = 1,
1072                 .assess = assess_capability_bounding_set,
1073                 .parameter = (UINT64_C(1) << CAP_SYSLOG),
1074         },
1075         {
1076                 .id = "CapabilityBoundingSet=~CAP_SYS_(NICE|RESOURCE)",
1077                 .json_field = "CapabilityBoundingSet_CAP_SYS_NICE_RESOURCE",
1078                 .description_good = "Service has no privileges to change resource use parameters",
1079                 .description_bad = "Service has privileges to change resource use parameters",
1080                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1081                 .weight = 500,
1082                 .range = 1,
1083                 .assess = assess_capability_bounding_set,
1084                 .parameter = (UINT64_C(1) << CAP_SYS_NICE) |
1085                              (UINT64_C(1) << CAP_SYS_RESOURCE),
1086         },
1087         {
1088                 .id = "CapabilityBoundingSet=~CAP_MKNOD",
1089                 .json_field = "CapabilityBoundingSet_CAP_MKNOD",
1090                 .description_good = "Service cannot create device nodes",
1091                 .description_bad = "Service may create device nodes",
1092                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1093                 .weight = 500,
1094                 .range = 1,
1095                 .assess = assess_capability_bounding_set,
1096                 .parameter = (UINT64_C(1) << CAP_MKNOD),
1097         },
1098         {
1099                 .id = "CapabilityBoundingSet=~CAP_(CHOWN|FSETID|SETFCAP)",
1100                 .json_field = "CapabilityBoundingSet_CAP_CHOWN_FSETID_SETFCAP",
1101                 .description_good = "Service cannot change file ownership/access mode/capabilities",
1102                 .description_bad = "Service may change file ownership/access mode/capabilities unrestricted",
1103                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1104                 .weight = 1000,
1105                 .range = 1,
1106                 .assess = assess_capability_bounding_set,
1107                 .parameter = (UINT64_C(1) << CAP_CHOWN) |
1108                              (UINT64_C(1) << CAP_FSETID) |
1109                              (UINT64_C(1) << CAP_SETFCAP),
1110         },
1111         {
1112                 .id = "CapabilityBoundingSet=~CAP_(DAC_*|FOWNER|IPC_OWNER)",
1113                 .json_field = "CapabilityBoundingSet_CAP_DAC_FOWNER_IPC_OWNER",
1114                 .description_good = "Service cannot override UNIX file/IPC permission checks",
1115                 .description_bad = "Service may override UNIX file/IPC permission checks",
1116                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1117                 .weight = 1000,
1118                 .range = 1,
1119                 .assess = assess_capability_bounding_set,
1120                 .parameter = (UINT64_C(1) << CAP_DAC_OVERRIDE) |
1121                              (UINT64_C(1) << CAP_DAC_READ_SEARCH) |
1122                              (UINT64_C(1) << CAP_FOWNER) |
1123                              (UINT64_C(1) << CAP_IPC_OWNER),
1124         },
1125         {
1126                 .id = "CapabilityBoundingSet=~CAP_KILL",
1127                 .json_field = "CapabilityBoundingSet_CAP_KILL",
1128                 .description_good = "Service cannot send UNIX signals to arbitrary processes",
1129                 .description_bad = "Service may send UNIX signals to arbitrary processes",
1130                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1131                 .weight = 500,
1132                 .range = 1,
1133                 .assess = assess_capability_bounding_set,
1134                 .parameter = (UINT64_C(1) << CAP_KILL),
1135         },
1136         {
1137                 .id = "CapabilityBoundingSet=~CAP_NET_(BIND_SERVICE|BROADCAST|RAW)",
1138                 .json_field = "CapabilityBoundingSet_CAP_NET_BIND_SERVICE_BROADCAST_RAW)",
1139                 .description_good = "Service has no elevated networking privileges",
1140                 .description_bad = "Service has elevated networking privileges",
1141                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1142                 .weight = 500,
1143                 .range = 1,
1144                 .assess = assess_capability_bounding_set,
1145                 .parameter = (UINT64_C(1) << CAP_NET_BIND_SERVICE) |
1146                              (UINT64_C(1) << CAP_NET_BROADCAST) |
1147                              (UINT64_C(1) << CAP_NET_RAW),
1148         },
1149         {
1150                 .id = "CapabilityBoundingSet=~CAP_SYS_BOOT",
1151                 .json_field = "CapabilityBoundingSet_CAP_SYS_BOOT",
1152                 .description_good = "Service cannot issue reboot()",
1153                 .description_bad = "Service may issue reboot()",
1154                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1155                 .weight = 100,
1156                 .range = 1,
1157                 .assess = assess_capability_bounding_set,
1158                 .parameter = (UINT64_C(1) << CAP_SYS_BOOT),
1159         },
1160         {
1161                 .id = "CapabilityBoundingSet=~CAP_MAC_*",
1162                 .json_field = "CapabilityBoundingSet_CAP_MAC",
1163                 .description_good = "Service cannot adjust SMACK MAC",
1164                 .description_bad = "Service may adjust SMACK MAC",
1165                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1166                 .weight = 100,
1167                 .range = 1,
1168                 .assess = assess_capability_bounding_set,
1169                 .parameter = (UINT64_C(1) << CAP_MAC_ADMIN)|
1170                              (UINT64_C(1) << CAP_MAC_OVERRIDE),
1171         },
1172         {
1173                 .id = "CapabilityBoundingSet=~CAP_LINUX_IMMUTABLE",
1174                 .json_field = "CapabilityBoundingSet_CAP_LINUX_IMMUTABLE",
1175                 .description_good = "Service cannot mark files immutable",
1176                 .description_bad = "Service may mark files immutable",
1177                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1178                 .weight = 75,
1179                 .range = 1,
1180                 .assess = assess_capability_bounding_set,
1181                 .parameter = (UINT64_C(1) << CAP_LINUX_IMMUTABLE),
1182         },
1183         {
1184                 .id = "CapabilityBoundingSet=~CAP_IPC_LOCK",
1185                 .json_field = "CapabilityBoundingSet_CAP_IPC_LOCK",
1186                 .description_good = "Service cannot lock memory into RAM",
1187                 .description_bad = "Service may lock memory into RAM",
1188                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1189                 .weight = 50,
1190                 .range = 1,
1191                 .assess = assess_capability_bounding_set,
1192                 .parameter = (UINT64_C(1) << CAP_IPC_LOCK),
1193         },
1194         {
1195                 .id = "CapabilityBoundingSet=~CAP_SYS_CHROOT",
1196                 .json_field = "CapabilityBoundingSet_CAP_SYS_CHROOT",
1197                 .description_good = "Service cannot issue chroot()",
1198                 .description_bad = "Service may issue chroot()",
1199                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1200                 .weight = 50,
1201                 .range = 1,
1202                 .assess = assess_capability_bounding_set,
1203                 .parameter = (UINT64_C(1) << CAP_SYS_CHROOT),
1204         },
1205         {
1206                 .id = "CapabilityBoundingSet=~CAP_BLOCK_SUSPEND",
1207                 .json_field = "CapabilityBoundingSet_CAP_BLOCK_SUSPEND",
1208                 .description_good = "Service cannot establish wake locks",
1209                 .description_bad = "Service may establish wake locks",
1210                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1211                 .weight = 25,
1212                 .range = 1,
1213                 .assess = assess_capability_bounding_set,
1214                 .parameter = (UINT64_C(1) << CAP_BLOCK_SUSPEND),
1215         },
1216         {
1217                 .id = "CapabilityBoundingSet=~CAP_WAKE_ALARM",
1218                 .json_field = "CapabilityBoundingSet_CAP_WAKE_ALARM",
1219                 .description_good = "Service cannot program timers that wake up the system",
1220                 .description_bad = "Service may program timers that wake up the system",
1221                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1222                 .weight = 25,
1223                 .range = 1,
1224                 .assess = assess_capability_bounding_set,
1225                 .parameter = (UINT64_C(1) << CAP_WAKE_ALARM),
1226         },
1227         {
1228                 .id = "CapabilityBoundingSet=~CAP_LEASE",
1229                 .json_field = "CapabilityBoundingSet_CAP_LEASE",
1230                 .description_good = "Service cannot create file leases",
1231                 .description_bad = "Service may create file leases",
1232                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1233                 .weight = 25,
1234                 .range = 1,
1235                 .assess = assess_capability_bounding_set,
1236                 .parameter = (UINT64_C(1) << CAP_LEASE),
1237         },
1238         {
1239                 .id = "CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG",
1240                 .json_field = "CapabilityBoundingSet_CAP_SYS_TTY_CONFIG",
1241                 .description_good = "Service cannot issue vhangup()",
1242                 .description_bad = "Service may issue vhangup()",
1243                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1244                 .weight = 25,
1245                 .range = 1,
1246                 .assess = assess_capability_bounding_set,
1247                 .parameter = (UINT64_C(1) << CAP_SYS_TTY_CONFIG),
1248         },
1249         {
1250                 .id = "CapabilityBoundingSet=~CAP_SYS_PACCT",
1251                 .json_field = "CapabilityBoundingSet_CAP_SYS_PACCT",
1252                 .description_good = "Service cannot use acct()",
1253                 .description_bad = "Service may use acct()",
1254                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
1255                 .weight = 25,
1256                 .range = 1,
1257                 .assess = assess_capability_bounding_set,
1258                 .parameter = (UINT64_C(1) << CAP_SYS_PACCT),
1259         },
1260         {
1261                 .id = "UMask=",
1262                 .json_field = "UMask",
1263                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#UMask=",
1264                 .weight = 100,
1265                 .range = 10,
1266                 .assess = assess_umask,
1267         },
1268         {
1269                 .id = "KeyringMode=",
1270                 .json_field = "KeyringMode",
1271                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#KeyringMode=",
1272                 .description_good = "Service doesn't share key material with other services",
1273                 .description_bad = "Service shares key material with other service",
1274                 .weight = 1000,
1275                 .range = 1,
1276                 .assess = assess_keyring_mode,
1277         },
1278         {
1279                 .id = "ProtectProc=",
1280                 .json_field = "ProtectProc",
1281                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectProc=",
1282                 .description_good = "Service has restricted access to process tree (/proc hidepid=)",
1283                 .description_bad = "Service has full access to process tree (/proc hidepid=)",
1284                 .weight = 1000,
1285                 .range = 3,
1286                 .assess = assess_protect_proc,
1287         },
1288         {
1289                 .id = "ProcSubset=",
1290                 .json_field = "ProcSubset",
1291                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProcSubset=",
1292                 .description_good = "Service has no access to non-process /proc files (/proc subset=)",
1293                 .description_bad = "Service has full access to non-process /proc files (/proc subset=)",
1294                 .weight = 10,
1295                 .range = 1,
1296                 .assess = assess_proc_subset,
1297         },
1298         {
1299                 .id = "NotifyAccess=",
1300                 .json_field = "NotifyAccess",
1301                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NotifyAccess=",
1302                 .description_good = "Service child processes cannot alter service state",
1303                 .description_bad = "Service child processes may alter service state",
1304                 .weight = 1000,
1305                 .range = 1,
1306                 .assess = assess_notify_access,
1307         },
1308         {
1309                 .id = "RemoveIPC=",
1310                 .json_field = "RemoveIPC",
1311                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RemoveIPC=",
1312                 .description_good = "Service user cannot leave SysV IPC objects around",
1313                 .description_bad = "Service user may leave SysV IPC objects around",
1314                 .description_na = "Service runs as root, option does not apply",
1315                 .weight = 100,
1316                 .range = 1,
1317                 .assess = assess_remove_ipc,
1318                 .offset = offsetof(SecurityInfo, remove_ipc),
1319         },
1320         {
1321                 .id = "Delegate=",
1322                 .json_field = "Delegate",
1323                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Delegate=",
1324                 .description_good = "Service does not maintain its own delegated control group subtree",
1325                 .description_bad = "Service maintains its own delegated control group subtree",
1326                 .weight = 100,
1327                 .range = 1,
1328                 .assess = assess_bool,
1329                 .offset = offsetof(SecurityInfo, delegate),
1330                 .parameter = true, /* invert! */
1331         },
1332         {
1333                 .id = "RestrictRealtime=",
1334                 .json_field = "RestrictRealtime",
1335                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictRealtime=",
1336                 .description_good = "Service realtime scheduling access is restricted",
1337                 .description_bad = "Service may acquire realtime scheduling",
1338                 .weight = 500,
1339                 .range = 1,
1340                 .assess = assess_bool,
1341                 .offset = offsetof(SecurityInfo, restrict_realtime),
1342         },
1343         {
1344                 .id = "RestrictSUIDSGID=",
1345                 .json_field = "RestrictSUIDSGID",
1346                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictSUIDSGID=",
1347                 .description_good = "SUID/SGID file creation by service is restricted",
1348                 .description_bad = "Service may create SUID/SGID files",
1349                 .weight = 1000,
1350                 .range = 1,
1351                 .assess = assess_bool,
1352                 .offset = offsetof(SecurityInfo, restrict_suid_sgid),
1353         },
1354         {
1355                 .id = "RestrictNamespaces=~user",
1356                 .json_field = "RestrictNamespaces_user",
1357                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1358                 .description_good = "Service cannot create user namespaces",
1359                 .description_bad = "Service may create user namespaces",
1360                 .weight = 1500,
1361                 .range = 1,
1362                 .assess = assess_restrict_namespaces,
1363                 .parameter = CLONE_NEWUSER,
1364         },
1365         {
1366                 .id = "RestrictNamespaces=~mnt",
1367                 .json_field = "RestrictNamespaces_mnt",
1368                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1369                 .description_good = "Service cannot create file system namespaces",
1370                 .description_bad = "Service may create file system namespaces",
1371                 .weight = 500,
1372                 .range = 1,
1373                 .assess = assess_restrict_namespaces,
1374                 .parameter = CLONE_NEWNS,
1375         },
1376         {
1377                 .id = "RestrictNamespaces=~ipc",
1378                 .json_field = "RestrictNamespaces_ipc",
1379                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1380                 .description_good = "Service cannot create IPC namespaces",
1381                 .description_bad = "Service may create IPC namespaces",
1382                 .weight = 500,
1383                 .range = 1,
1384                 .assess = assess_restrict_namespaces,
1385                 .parameter = CLONE_NEWIPC,
1386         },
1387         {
1388                 .id = "RestrictNamespaces=~pid",
1389                 .json_field = "RestrictNamespaces_pid",
1390                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1391                 .description_good = "Service cannot create process namespaces",
1392                 .description_bad = "Service may create process namespaces",
1393                 .weight = 500,
1394                 .range = 1,
1395                 .assess = assess_restrict_namespaces,
1396                 .parameter = CLONE_NEWPID,
1397         },
1398         {
1399                 .id = "RestrictNamespaces=~cgroup",
1400                 .json_field = "RestrictNamespaces_cgroup",
1401                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1402                 .description_good = "Service cannot create cgroup namespaces",
1403                 .description_bad = "Service may create cgroup namespaces",
1404                 .weight = 500,
1405                 .range = 1,
1406                 .assess = assess_restrict_namespaces,
1407                 .parameter = CLONE_NEWCGROUP,
1408         },
1409         {
1410                 .id = "RestrictNamespaces=~net",
1411                 .json_field = "RestrictNamespaces_net",
1412                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1413                 .description_good = "Service cannot create network namespaces",
1414                 .description_bad = "Service may create network namespaces",
1415                 .weight = 500,
1416                 .range = 1,
1417                 .assess = assess_restrict_namespaces,
1418                 .parameter = CLONE_NEWNET,
1419         },
1420         {
1421                 .id = "RestrictNamespaces=~uts",
1422                 .json_field = "RestrictNamespaces_uts",
1423                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
1424                 .description_good = "Service cannot create hostname namespaces",
1425                 .description_bad = "Service may create hostname namespaces",
1426                 .weight = 100,
1427                 .range = 1,
1428                 .assess = assess_restrict_namespaces,
1429                 .parameter = CLONE_NEWUTS,
1430         },
1431         {
1432                 .id = "RestrictAddressFamilies=~AF_(INET|INET6)",
1433                 .json_field = "RestrictAddressFamilies_AF_INET_INET6",
1434                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1435                 .description_good = "Service cannot allocate Internet sockets",
1436                 .description_bad = "Service may allocate Internet sockets",
1437                 .weight = 1500,
1438                 .range = 1,
1439                 .assess = assess_bool,
1440                 .offset = offsetof(SecurityInfo, restrict_address_family_inet),
1441         },
1442         {
1443                 .id = "RestrictAddressFamilies=~AF_UNIX",
1444                 .json_field = "RestrictAddressFamilies_AF_UNIX",
1445                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1446                 .description_good = "Service cannot allocate local sockets",
1447                 .description_bad = "Service may allocate local sockets",
1448                 .weight = 25,
1449                 .range = 1,
1450                 .assess = assess_bool,
1451                 .offset = offsetof(SecurityInfo, restrict_address_family_unix),
1452         },
1453         {
1454                 .id = "RestrictAddressFamilies=~AF_NETLINK",
1455                 .json_field = "RestrictAddressFamilies_AF_NETLINK",
1456                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1457                 .description_good = "Service cannot allocate netlink sockets",
1458                 .description_bad = "Service may allocate netlink sockets",
1459                 .weight = 200,
1460                 .range = 1,
1461                 .assess = assess_bool,
1462                 .offset = offsetof(SecurityInfo, restrict_address_family_netlink),
1463         },
1464         {
1465                 .id = "RestrictAddressFamilies=~AF_PACKET",
1466                 .json_field = "RestrictAddressFamilies_AF_PACKET",
1467                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1468                 .description_good = "Service cannot allocate packet sockets",
1469                 .description_bad = "Service may allocate packet sockets",
1470                 .weight = 1000,
1471                 .range = 1,
1472                 .assess = assess_bool,
1473                 .offset = offsetof(SecurityInfo, restrict_address_family_packet),
1474         },
1475         {
1476                 .id = "RestrictAddressFamilies=~…",
1477                 .json_field = "RestrictAddressFamilies_OTHER",
1478                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
1479                 .description_good = "Service cannot allocate exotic sockets",
1480                 .description_bad = "Service may allocate exotic sockets",
1481                 .weight = 1250,
1482                 .range = 1,
1483                 .assess = assess_bool,
1484                 .offset = offsetof(SecurityInfo, restrict_address_family_other),
1485         },
1486 #if HAVE_SECCOMP
1487         {
1488                 .id = "SystemCallArchitectures=",
1489                 .json_field = "SystemCallArchitectures",
1490                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallArchitectures=",
1491                 .weight = 1000,
1492                 .range = 10,
1493                 .assess = assess_system_call_architectures,
1494         },
1495         {
1496                 .id = "SystemCallFilter=~@swap",
1497                 .json_field = "SystemCallFilter_swap",
1498                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1499                 .weight = 1000,
1500                 .range = 10,
1501                 .assess = assess_system_call_filter,
1502                 .parameter = SYSCALL_FILTER_SET_SWAP,
1503         },
1504         {
1505                 .id = "SystemCallFilter=~@obsolete",
1506                 .json_field = "SystemCallFilter_obsolete",
1507                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1508                 .weight = 250,
1509                 .range = 10,
1510                 .assess = assess_system_call_filter,
1511                 .parameter = SYSCALL_FILTER_SET_OBSOLETE,
1512         },
1513         {
1514                 .id = "SystemCallFilter=~@clock",
1515                 .json_field = "SystemCallFilter_clock",
1516                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1517                 .weight = 1000,
1518                 .range = 10,
1519                 .assess = assess_system_call_filter,
1520                 .parameter = SYSCALL_FILTER_SET_CLOCK,
1521         },
1522         {
1523                 .id = "SystemCallFilter=~@cpu-emulation",
1524                 .json_field = "SystemCallFilter_cpu_emulation",
1525                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1526                 .weight = 250,
1527                 .range = 10,
1528                 .assess = assess_system_call_filter,
1529                 .parameter = SYSCALL_FILTER_SET_CPU_EMULATION,
1530         },
1531         {
1532                 .id = "SystemCallFilter=~@debug",
1533                 .json_field = "SystemCallFilter_debug",
1534                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1535                 .weight = 1000,
1536                 .range = 10,
1537                 .assess = assess_system_call_filter,
1538                 .parameter = SYSCALL_FILTER_SET_DEBUG,
1539         },
1540         {
1541                 .id = "SystemCallFilter=~@mount",
1542                 .json_field = "SystemCallFilter_mount",
1543                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1544                 .weight = 1000,
1545                 .range = 10,
1546                 .assess = assess_system_call_filter,
1547                 .parameter = SYSCALL_FILTER_SET_MOUNT,
1548         },
1549         {
1550                 .id = "SystemCallFilter=~@module",
1551                 .json_field = "SystemCallFilter_module",
1552                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1553                 .weight = 1000,
1554                 .range = 10,
1555                 .assess = assess_system_call_filter,
1556                 .parameter = SYSCALL_FILTER_SET_MODULE,
1557         },
1558         {
1559                 .id = "SystemCallFilter=~@raw-io",
1560                 .json_field = "SystemCallFilter_raw_io",
1561                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1562                 .weight = 1000,
1563                 .range = 10,
1564                 .assess = assess_system_call_filter,
1565                 .parameter = SYSCALL_FILTER_SET_RAW_IO,
1566         },
1567         {
1568                 .id = "SystemCallFilter=~@reboot",
1569                 .json_field = "SystemCallFilter_reboot",
1570                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1571                 .weight = 1000,
1572                 .range = 10,
1573                 .assess = assess_system_call_filter,
1574                 .parameter = SYSCALL_FILTER_SET_REBOOT,
1575         },
1576         {
1577                 .id = "SystemCallFilter=~@privileged",
1578                 .json_field = "SystemCallFilter_privileged",
1579                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1580                 .weight = 700,
1581                 .range = 10,
1582                 .assess = assess_system_call_filter,
1583                 .parameter = SYSCALL_FILTER_SET_PRIVILEGED,
1584         },
1585         {
1586                 .id = "SystemCallFilter=~@resources",
1587                 .json_field = "SystemCallFilter_resources",
1588                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
1589                 .weight = 700,
1590                 .range = 10,
1591                 .assess = assess_system_call_filter,
1592                 .parameter = SYSCALL_FILTER_SET_RESOURCES,
1593         },
1594 #endif
1595         {
1596                 .id = "IPAddressDeny=",
1597                 .json_field = "IPAddressDeny",
1598                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#IPAddressDeny=",
1599                 .weight = 1000,
1600                 .range = 10,
1601                 .assess = assess_ip_address_allow,
1602         },
1603         {
1604                 .id = "DeviceAllow=",
1605                 .json_field = "DeviceAllow",
1606                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#DeviceAllow=",
1607                 .weight = 1000,
1608                 .range = 10,
1609                 .assess = assess_device_allow,
1610         },
1611         {
1612                 .id = "AmbientCapabilities=",
1613                 .json_field = "AmbientCapabilities",
1614                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#AmbientCapabilities=",
1615                 .description_good = "Service process does not receive ambient capabilities",
1616                 .description_bad = "Service process receives ambient capabilities",
1617                 .weight = 500,
1618                 .range = 1,
1619                 .assess = assess_ambient_capabilities,
1620         },
1621 };
1622 
security_assessor_find_in_policy(const struct security_assessor * a,JsonVariant * policy,const char * name)1623 static JsonVariant* security_assessor_find_in_policy(const struct security_assessor *a, JsonVariant *policy, const char *name) {
1624         JsonVariant *item;
1625         assert(a);
1626 
1627         if (!policy)
1628                 return NULL;
1629         if (!json_variant_is_object(policy)) {
1630                 log_debug("Specified policy is not a JSON object, ignoring.");
1631                 return NULL;
1632         }
1633 
1634         item = json_variant_by_key(policy, a->json_field);
1635         if (!item)
1636                 return NULL;
1637         if (!json_variant_is_object(item)) {
1638                 log_debug("Item for '%s' in policy JSON object is not an object, ignoring.", a->id);
1639                 return NULL;
1640         }
1641 
1642         return name ? json_variant_by_key(item, name) : item;
1643 }
1644 
access_weight(const struct security_assessor * a,JsonVariant * policy)1645 static uint64_t access_weight(const struct security_assessor *a, JsonVariant *policy) {
1646         JsonVariant *val;
1647 
1648         assert(a);
1649 
1650         val = security_assessor_find_in_policy(a, policy, "weight");
1651         if  (val) {
1652                 if (json_variant_is_unsigned(val))
1653                         return json_variant_unsigned(val);
1654                 log_debug("JSON field 'weight' of policy for %s is not an unsigned integer, ignoring.", a->id);
1655         }
1656 
1657         return a->weight;
1658 }
1659 
access_range(const struct security_assessor * a,JsonVariant * policy)1660 static uint64_t access_range(const struct security_assessor *a, JsonVariant *policy) {
1661         JsonVariant *val;
1662 
1663         assert(a);
1664 
1665         val = security_assessor_find_in_policy(a, policy, "range");
1666         if  (val) {
1667                 if (json_variant_is_unsigned(val))
1668                         return json_variant_unsigned(val);
1669                 log_debug("JSON field 'range' of policy for %s is not an unsigned integer, ignoring.", a->id);
1670         }
1671 
1672         return a->range;
1673 }
1674 
access_description_na(const struct security_assessor * a,JsonVariant * policy)1675 static const char *access_description_na(const struct security_assessor *a, JsonVariant *policy) {
1676         JsonVariant *val;
1677 
1678         assert(a);
1679 
1680         val = security_assessor_find_in_policy(a, policy, "description_na");
1681         if  (val) {
1682                 if (json_variant_is_string(val))
1683                         return json_variant_string(val);
1684                 log_debug("JSON field 'description_na' of policy for %s is not a string, ignoring.", a->id);
1685         }
1686 
1687         return a->description_na;
1688 }
1689 
access_description_good(const struct security_assessor * a,JsonVariant * policy)1690 static const char *access_description_good(const struct security_assessor *a, JsonVariant *policy) {
1691         JsonVariant *val;
1692 
1693         assert(a);
1694 
1695         val = security_assessor_find_in_policy(a, policy, "description_good");
1696         if  (val) {
1697                 if (json_variant_is_string(val))
1698                         return json_variant_string(val);
1699                 log_debug("JSON field 'description_good' of policy for %s is not a string, ignoring.", a->id);
1700         }
1701 
1702         return a->description_good;
1703 }
1704 
access_description_bad(const struct security_assessor * a,JsonVariant * policy)1705 static const char *access_description_bad(const struct security_assessor *a, JsonVariant *policy) {
1706         JsonVariant *val;
1707 
1708         assert(a);
1709 
1710         val = security_assessor_find_in_policy(a, policy, "description_bad");
1711         if  (val) {
1712                 if (json_variant_is_string(val))
1713                         return json_variant_string(val);
1714                 log_debug("JSON field 'description_bad' of policy for %s is not a string, ignoring.", a->id);
1715         }
1716 
1717         return a->description_bad;
1718 }
1719 
assess(const SecurityInfo * info,Table * overview_table,AnalyzeSecurityFlags flags,unsigned threshold,JsonVariant * policy,PagerFlags pager_flags,JsonFormatFlags json_format_flags)1720 static int assess(const SecurityInfo *info,
1721                   Table *overview_table,
1722                   AnalyzeSecurityFlags flags,
1723                   unsigned threshold,
1724                   JsonVariant *policy,
1725                   PagerFlags pager_flags,
1726                   JsonFormatFlags json_format_flags) {
1727 
1728         static const struct {
1729                 uint64_t exposure;
1730                 const char *name;
1731                 const char *color;
1732                 SpecialGlyph smiley;
1733         } badness_table[] = {
1734                 { 100, "DANGEROUS", ANSI_HIGHLIGHT_RED,    SPECIAL_GLYPH_DEPRESSED_SMILEY        },
1735                 { 90,  "UNSAFE",    ANSI_HIGHLIGHT_RED,    SPECIAL_GLYPH_UNHAPPY_SMILEY          },
1736                 { 75,  "EXPOSED",   ANSI_HIGHLIGHT_YELLOW, SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY },
1737                 { 50,  "MEDIUM",    NULL,                  SPECIAL_GLYPH_NEUTRAL_SMILEY          },
1738                 { 10,  "OK",        ANSI_HIGHLIGHT_GREEN,  SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY   },
1739                 { 1,   "SAFE",      ANSI_HIGHLIGHT_GREEN,  SPECIAL_GLYPH_HAPPY_SMILEY            },
1740                 { 0,   "PERFECT",   ANSI_HIGHLIGHT_GREEN,  SPECIAL_GLYPH_ECSTATIC_SMILEY         },
1741         };
1742 
1743         uint64_t badness_sum = 0, weight_sum = 0, exposure;
1744         _cleanup_(table_unrefp) Table *details_table = NULL;
1745         size_t i;
1746         int r;
1747 
1748         if (!FLAGS_SET(flags, ANALYZE_SECURITY_SHORT)) {
1749                 details_table = table_new(" ", "name", "json_field", "description", "weight", "badness", "range", "exposure");
1750                 if (!details_table)
1751                         return log_oom();
1752 
1753                 r = table_set_json_field_name(details_table, 0, "set");
1754                 if (r < 0)
1755                         return log_error_errno(r, "Failed to set JSON field name of column 0: %m");
1756 
1757                 (void) table_set_sort(details_table, (size_t) 3, (size_t) 1);
1758                 (void) table_set_reverse(details_table, 3, true);
1759 
1760                 if (getenv_bool("SYSTEMD_ANALYZE_DEBUG") <= 0)
1761                         (void) table_set_display(details_table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 7);
1762         }
1763 
1764         for (i = 0; i < ELEMENTSOF(security_assessor_table); i++) {
1765                 const struct security_assessor *a = security_assessor_table + i;
1766                 _cleanup_free_ char *d = NULL;
1767                 uint64_t badness;
1768                 void *data;
1769                 uint64_t weight = access_weight(a, policy);
1770                 uint64_t range = access_range(a, policy);
1771 
1772                 data = (uint8_t *) info + a->offset;
1773 
1774                 if (a->default_dependencies_only && !info->default_dependencies) {
1775                         badness = UINT64_MAX;
1776                         d = strdup("Service runs in special boot phase, option is not appropriate");
1777                         if (!d)
1778                                 return log_oom();
1779                 } else if (weight == 0) {
1780                         badness = UINT64_MAX;
1781                         d = strdup("Option excluded by policy, skipping");
1782                         if (!d)
1783                                 return log_oom();
1784                 } else {
1785                         r = a->assess(a, info, data, &badness, &d);
1786                         if (r < 0)
1787                                 return r;
1788                 }
1789 
1790                 assert(range > 0);
1791 
1792                 if (badness != UINT64_MAX) {
1793                         assert(badness <= range);
1794 
1795                         badness_sum += DIV_ROUND_UP(badness * weight, range);
1796                         weight_sum += weight;
1797                 }
1798 
1799                 if (details_table) {
1800                         const char *description, *color = NULL;
1801                         int checkmark;
1802 
1803                         if (badness == UINT64_MAX) {
1804                                 checkmark = -1;
1805                                 description = access_description_na(a, policy);
1806                                 color = NULL;
1807                         } else if (badness == a->range) {
1808                                 checkmark = 0;
1809                                 description = access_description_bad(a, policy);
1810                                 color = ansi_highlight_red();
1811                         } else if (badness == 0) {
1812                                 checkmark = 1;
1813                                 description = access_description_good(a, policy);
1814                                 color = ansi_highlight_green();
1815                         } else {
1816                                 checkmark = 0;
1817                                 description = NULL;
1818                                 color = ansi_highlight_red();
1819                         }
1820 
1821                         if (d)
1822                                 description = d;
1823 
1824                         if (checkmark < 0) {
1825                                 r = table_add_many(details_table, TABLE_EMPTY);
1826                                 if (r < 0)
1827                                         return table_log_add_error(r);
1828                         } else {
1829                                 r = table_add_many(details_table,
1830                                                    TABLE_BOOLEAN_CHECKMARK, checkmark > 0,
1831                                                    TABLE_SET_MINIMUM_WIDTH, 1,
1832                                                    TABLE_SET_MAXIMUM_WIDTH, 1,
1833                                                    TABLE_SET_ELLIPSIZE_PERCENT, 0,
1834                                                    TABLE_SET_COLOR, color);
1835                                 if (r < 0)
1836                                         return table_log_add_error(r);
1837                         }
1838 
1839                         r = table_add_many(details_table,
1840                                            TABLE_STRING, a->id, TABLE_SET_URL, a->url,
1841                                            TABLE_STRING, a->json_field,
1842                                            TABLE_STRING, description,
1843                                            TABLE_UINT64, weight, TABLE_SET_ALIGN_PERCENT, 100,
1844                                            TABLE_UINT64, badness, TABLE_SET_ALIGN_PERCENT, 100,
1845                                            TABLE_UINT64, range, TABLE_SET_ALIGN_PERCENT, 100,
1846                                            TABLE_EMPTY, TABLE_SET_ALIGN_PERCENT, 100);
1847                         if (r < 0)
1848                                 return table_log_add_error(r);
1849                 }
1850         }
1851 
1852         assert(weight_sum > 0);
1853 
1854         if (details_table) {
1855                 size_t row;
1856 
1857                 for (row = 1; row < table_get_rows(details_table); row++) {
1858                         char buf[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1];
1859                         const uint64_t *weight, *badness, *range;
1860                         TableCell *cell;
1861                         uint64_t x;
1862 
1863                         assert_se(weight = table_get_at(details_table, row, 4));
1864                         assert_se(badness = table_get_at(details_table, row, 5));
1865                         assert_se(range = table_get_at(details_table, row, 6));
1866 
1867                         if (*badness == UINT64_MAX || *badness == 0)
1868                                 continue;
1869 
1870                         assert_se(cell = table_get_cell(details_table, row, 7));
1871 
1872                         x = DIV_ROUND_UP(DIV_ROUND_UP(*badness * *weight * 100U, *range), weight_sum);
1873                         xsprintf(buf, "%" PRIu64 ".%" PRIu64, x / 10, x % 10);
1874 
1875                         r = table_update(details_table, cell, TABLE_STRING, buf);
1876                         if (r < 0)
1877                                 return log_error_errno(r, "Failed to update cell in table: %m");
1878                 }
1879 
1880                 if (json_format_flags & JSON_FORMAT_OFF) {
1881                         r = table_hide_column_from_display(details_table, (size_t) 2);
1882                         if (r < 0)
1883                                 return log_error_errno(r, "Failed to set columns to display: %m");
1884                 }
1885 
1886                 r = table_print_with_pager(details_table, json_format_flags, pager_flags, /* show_header= */true);
1887                 if (r < 0)
1888                         return log_error_errno(r, "Failed to output table: %m");
1889         }
1890 
1891         exposure = DIV_ROUND_UP(badness_sum * 100U, weight_sum);
1892 
1893         for (i = 0; i < ELEMENTSOF(badness_table); i++)
1894                 if (exposure >= badness_table[i].exposure)
1895                         break;
1896 
1897         assert(i < ELEMENTSOF(badness_table));
1898 
1899         if (details_table && (json_format_flags & JSON_FORMAT_OFF)) {
1900                 _cleanup_free_ char *clickable = NULL;
1901                 const char *name;
1902 
1903                 /* If we shall output the details table, also print the brief summary underneath */
1904 
1905                 if (info->fragment_path) {
1906                         r = terminal_urlify_path(info->fragment_path, info->id, &clickable);
1907                         if (r < 0)
1908                                 return log_oom();
1909 
1910                         name = clickable;
1911                 } else
1912                         name = info->id;
1913 
1914                 printf("\n%s %sOverall exposure level for %s%s: %s%" PRIu64 ".%" PRIu64 " %s%s %s\n",
1915                        special_glyph(SPECIAL_GLYPH_ARROW_RIGHT),
1916                        ansi_highlight(),
1917                        name,
1918                        ansi_normal(),
1919                        colors_enabled() ? strempty(badness_table[i].color) : "",
1920                        exposure / 10, exposure % 10,
1921                        badness_table[i].name,
1922                        ansi_normal(),
1923                        special_glyph(badness_table[i].smiley));
1924         }
1925 
1926         fflush(stdout);
1927 
1928         if (overview_table) {
1929                 char buf[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1];
1930                 _cleanup_free_ char *url = NULL;
1931 
1932                 if (info->fragment_path) {
1933                         r = file_url_from_path(info->fragment_path, &url);
1934                         if (r < 0)
1935                                 return log_error_errno(r, "Failed to generate URL from path: %m");
1936                 }
1937 
1938                 xsprintf(buf, "%" PRIu64 ".%" PRIu64, exposure / 10, exposure % 10);
1939 
1940                 r = table_add_many(overview_table,
1941                                    TABLE_STRING, info->id,
1942                                    TABLE_SET_URL, url,
1943                                    TABLE_STRING, buf,
1944                                    TABLE_SET_ALIGN_PERCENT, 100,
1945                                    TABLE_STRING, badness_table[i].name,
1946                                    TABLE_SET_COLOR, strempty(badness_table[i].color),
1947                                    TABLE_STRING, special_glyph(badness_table[i].smiley));
1948                 if (r < 0)
1949                         return table_log_add_error(r);
1950         }
1951 
1952         /* Return error when overall exposure level is over threshold */
1953         if (exposure > threshold)
1954                 return -EINVAL;
1955 
1956         return 0;
1957 }
1958 
property_read_restrict_namespaces(sd_bus * bus,const char * member,sd_bus_message * m,sd_bus_error * error,void * userdata)1959 static int property_read_restrict_namespaces(
1960                 sd_bus *bus,
1961                 const char *member,
1962                 sd_bus_message *m,
1963                 sd_bus_error *error,
1964                 void *userdata) {
1965 
1966         SecurityInfo *info = userdata;
1967         int r;
1968         uint64_t namespaces;
1969 
1970         assert(bus);
1971         assert(member);
1972         assert(m);
1973         assert(info);
1974 
1975         r = sd_bus_message_read(m, "t", &namespaces);
1976         if (r < 0)
1977                 return r;
1978 
1979         info->restrict_namespaces = (unsigned long long) namespaces;
1980 
1981         return 0;
1982 }
1983 
property_read_umask(sd_bus * bus,const char * member,sd_bus_message * m,sd_bus_error * error,void * userdata)1984 static int property_read_umask(
1985                 sd_bus *bus,
1986                 const char *member,
1987                 sd_bus_message *m,
1988                 sd_bus_error *error,
1989                 void *userdata) {
1990 
1991         SecurityInfo *info = userdata;
1992         int r;
1993         uint32_t umask;
1994 
1995         assert(bus);
1996         assert(member);
1997         assert(m);
1998         assert(info);
1999 
2000         r = sd_bus_message_read(m, "u", &umask);
2001         if (r < 0)
2002                 return r;
2003 
2004         info->_umask = (mode_t) umask;
2005 
2006         return 0;
2007 }
2008 
property_read_restrict_address_families(sd_bus * bus,const char * member,sd_bus_message * m,sd_bus_error * error,void * userdata)2009 static int property_read_restrict_address_families(
2010                 sd_bus *bus,
2011                 const char *member,
2012                 sd_bus_message *m,
2013                 sd_bus_error *error,
2014                 void *userdata) {
2015 
2016         SecurityInfo *info = userdata;
2017         int allow_list, r;
2018 
2019         assert(bus);
2020         assert(member);
2021         assert(m);
2022 
2023         r = sd_bus_message_enter_container(m, 'r', "bas");
2024         if (r < 0)
2025                 return r;
2026 
2027         r = sd_bus_message_read(m, "b", &allow_list);
2028         if (r < 0)
2029                 return r;
2030 
2031         info->restrict_address_family_inet =
2032                 info->restrict_address_family_unix =
2033                 info->restrict_address_family_netlink =
2034                 info->restrict_address_family_packet =
2035                 info->restrict_address_family_other = allow_list;
2036 
2037         r = sd_bus_message_enter_container(m, 'a', "s");
2038         if (r < 0)
2039                 return r;
2040 
2041         for (;;) {
2042                 const char *name;
2043 
2044                 r = sd_bus_message_read(m, "s", &name);
2045                 if (r < 0)
2046                         return r;
2047                 if (r == 0)
2048                         break;
2049 
2050                 if (STR_IN_SET(name, "AF_INET", "AF_INET6"))
2051                         info->restrict_address_family_inet = !allow_list;
2052                 else if (streq(name, "AF_UNIX"))
2053                         info->restrict_address_family_unix = !allow_list;
2054                 else if (streq(name, "AF_NETLINK"))
2055                         info->restrict_address_family_netlink = !allow_list;
2056                 else if (streq(name, "AF_PACKET"))
2057                         info->restrict_address_family_packet = !allow_list;
2058                 else
2059                         info->restrict_address_family_other = !allow_list;
2060         }
2061 
2062         r = sd_bus_message_exit_container(m);
2063         if (r < 0)
2064                 return r;
2065 
2066         return sd_bus_message_exit_container(m);
2067 }
2068 
property_read_syscall_archs(sd_bus * bus,const char * member,sd_bus_message * m,sd_bus_error * error,void * userdata)2069 static int property_read_syscall_archs(
2070                 sd_bus *bus,
2071                 const char *member,
2072                 sd_bus_message *m,
2073                 sd_bus_error *error,
2074                 void *userdata) {
2075 
2076         SecurityInfo *info = userdata;
2077         int r;
2078 
2079         assert(bus);
2080         assert(member);
2081         assert(m);
2082         assert(info);
2083 
2084         r = sd_bus_message_enter_container(m, 'a', "s");
2085         if (r < 0)
2086                 return r;
2087 
2088         for (;;) {
2089                 const char *name;
2090 
2091                 r = sd_bus_message_read(m, "s", &name);
2092                 if (r < 0)
2093                         return r;
2094                 if (r == 0)
2095                         break;
2096 
2097                 r = set_put_strdup(&info->system_call_architectures, name);
2098                 if (r < 0)
2099                         return r;
2100         }
2101 
2102         return sd_bus_message_exit_container(m);
2103 }
2104 
property_read_system_call_filter(sd_bus * bus,const char * member,sd_bus_message * m,sd_bus_error * error,void * userdata)2105 static int property_read_system_call_filter(
2106                 sd_bus *bus,
2107                 const char *member,
2108                 sd_bus_message *m,
2109                 sd_bus_error *error,
2110                 void *userdata) {
2111 
2112         SecurityInfo *info = userdata;
2113         int allow_list, r;
2114 
2115         assert(bus);
2116         assert(member);
2117         assert(m);
2118 
2119         r = sd_bus_message_enter_container(m, 'r', "bas");
2120         if (r < 0)
2121                 return r;
2122 
2123         r = sd_bus_message_read(m, "b", &allow_list);
2124         if (r < 0)
2125                 return r;
2126 
2127         info->system_call_filter_allow_list = allow_list;
2128 
2129         r = sd_bus_message_enter_container(m, 'a', "s");
2130         if (r < 0)
2131                 return r;
2132 
2133         for (;;) {
2134                 const char *name;
2135 
2136                 r = sd_bus_message_read(m, "s", &name);
2137                 if (r < 0)
2138                         return r;
2139                 if (r == 0)
2140                         break;
2141 
2142                 /* The actual ExecContext stores the system call id as the map value, which we don't
2143                  * need. So we assign NULL to all values here. */
2144                 r = hashmap_put_strdup(&info->system_call_filter, name, NULL);
2145                 if (r < 0)
2146                         return r;
2147         }
2148 
2149         r = sd_bus_message_exit_container(m);
2150         if (r < 0)
2151                 return r;
2152 
2153         return sd_bus_message_exit_container(m);
2154 }
2155 
property_read_ip_address_allow(sd_bus * bus,const char * member,sd_bus_message * m,sd_bus_error * error,void * userdata)2156 static int property_read_ip_address_allow(
2157                 sd_bus *bus,
2158                 const char *member,
2159                 sd_bus_message *m,
2160                 sd_bus_error *error,
2161                 void *userdata) {
2162 
2163         SecurityInfo *info = userdata;
2164         bool deny_ipv4 = false, deny_ipv6 = false;
2165         int r;
2166 
2167         assert(bus);
2168         assert(member);
2169         assert(m);
2170 
2171         r = sd_bus_message_enter_container(m, 'a', "(iayu)");
2172         if (r < 0)
2173                 return r;
2174 
2175         for (;;) {
2176                 const void *data;
2177                 size_t size;
2178                 int32_t family;
2179                 uint32_t prefixlen;
2180 
2181                 r = sd_bus_message_enter_container(m, 'r', "iayu");
2182                 if (r < 0)
2183                         return r;
2184                 if (r == 0)
2185                         break;
2186 
2187                 r = sd_bus_message_read(m, "i", &family);
2188                 if (r < 0)
2189                         return r;
2190 
2191                 r = sd_bus_message_read_array(m, 'y', &data, &size);
2192                 if (r < 0)
2193                         return r;
2194 
2195                 r = sd_bus_message_read(m, "u", &prefixlen);
2196                 if (r < 0)
2197                         return r;
2198 
2199                 r = sd_bus_message_exit_container(m);
2200                 if (r < 0)
2201                         return r;
2202 
2203                 if (streq(member, "IPAddressAllow")) {
2204                         union in_addr_union u;
2205 
2206                         if (family == AF_INET && size == 4 && prefixlen == 8)
2207                                 memcpy(&u.in, data, size);
2208                         else if (family == AF_INET6 && size == 16 && prefixlen == 128)
2209                                 memcpy(&u.in6, data, size);
2210                         else {
2211                                 info->ip_address_allow_other = true;
2212                                 continue;
2213                         }
2214 
2215                         if (in_addr_is_localhost(family, &u))
2216                                 info->ip_address_allow_localhost = true;
2217                         else
2218                                 info->ip_address_allow_other = true;
2219                 } else {
2220                         assert(streq(member, "IPAddressDeny"));
2221 
2222                         if (family == AF_INET && size == 4 && prefixlen == 0)
2223                                 deny_ipv4 = true;
2224                         else if (family == AF_INET6 && size == 16 && prefixlen == 0)
2225                                 deny_ipv6 = true;
2226                 }
2227         }
2228 
2229         info->ip_address_deny_all = deny_ipv4 && deny_ipv6;
2230 
2231         return sd_bus_message_exit_container(m);
2232 }
2233 
property_read_ip_filters(sd_bus * bus,const char * member,sd_bus_message * m,sd_bus_error * error,void * userdata)2234 static int property_read_ip_filters(
2235                 sd_bus *bus,
2236                 const char *member,
2237                 sd_bus_message *m,
2238                 sd_bus_error *error,
2239                 void *userdata) {
2240 
2241         SecurityInfo *info = userdata;
2242         _cleanup_(strv_freep) char **l = NULL;
2243         int r;
2244 
2245         assert(bus);
2246         assert(member);
2247         assert(m);
2248 
2249         r = sd_bus_message_read_strv(m, &l);
2250         if (r < 0)
2251                 return r;
2252 
2253         if (streq(member, "IPIngressFilterPath"))
2254                 info->ip_filters_custom_ingress = !strv_isempty(l);
2255         else if (streq(member, "IPEgressFilterPath"))
2256                 info->ip_filters_custom_egress = !strv_isempty(l);
2257 
2258         return 0;
2259 }
2260 
property_read_device_allow(sd_bus * bus,const char * member,sd_bus_message * m,sd_bus_error * error,void * userdata)2261 static int property_read_device_allow(
2262                 sd_bus *bus,
2263                 const char *member,
2264                 sd_bus_message *m,
2265                 sd_bus_error *error,
2266                 void *userdata) {
2267 
2268         SecurityInfo *info = userdata;
2269         int r;
2270 
2271         assert(bus);
2272         assert(member);
2273         assert(m);
2274 
2275         r = sd_bus_message_enter_container(m, 'a', "(ss)");
2276         if (r < 0)
2277                 return r;
2278 
2279         for (;;) {
2280                 const char *name, *policy;
2281 
2282                 r = sd_bus_message_read(m, "(ss)", &name, &policy);
2283                 if (r < 0)
2284                         return r;
2285                 if (r == 0)
2286                         break;
2287 
2288                 r = strv_extendf(&info->device_allow, "%s:%s", name, policy);
2289                 if (r < 0)
2290                         return r;
2291         }
2292 
2293         return sd_bus_message_exit_container(m);
2294 }
2295 
acquire_security_info(sd_bus * bus,const char * name,SecurityInfo * info,AnalyzeSecurityFlags flags)2296 static int acquire_security_info(sd_bus *bus, const char *name, SecurityInfo *info, AnalyzeSecurityFlags flags) {
2297 
2298         static const struct bus_properties_map security_map[] = {
2299                 { "AmbientCapabilities",     "t",       NULL,                                    offsetof(SecurityInfo, ambient_capabilities)      },
2300                 { "CapabilityBoundingSet",   "t",       NULL,                                    offsetof(SecurityInfo, capability_bounding_set)   },
2301                 { "DefaultDependencies",     "b",       NULL,                                    offsetof(SecurityInfo, default_dependencies)      },
2302                 { "Delegate",                "b",       NULL,                                    offsetof(SecurityInfo, delegate)                  },
2303                 { "DeviceAllow",             "a(ss)",   property_read_device_allow,              0                                                 },
2304                 { "DevicePolicy",            "s",       NULL,                                    offsetof(SecurityInfo, device_policy)             },
2305                 { "DynamicUser",             "b",       NULL,                                    offsetof(SecurityInfo, dynamic_user)              },
2306                 { "FragmentPath",            "s",       NULL,                                    offsetof(SecurityInfo, fragment_path)             },
2307                 { "IPAddressAllow",          "a(iayu)", property_read_ip_address_allow,          0                                                 },
2308                 { "IPAddressDeny",           "a(iayu)", property_read_ip_address_allow,          0                                                 },
2309                 { "IPIngressFilterPath",     "as",      property_read_ip_filters,                0                                                 },
2310                 { "IPEgressFilterPath",      "as",      property_read_ip_filters,                0                                                 },
2311                 { "Id",                      "s",       NULL,                                    offsetof(SecurityInfo, id)                        },
2312                 { "KeyringMode",             "s",       NULL,                                    offsetof(SecurityInfo, keyring_mode)              },
2313                 { "ProtectProc",             "s",       NULL,                                    offsetof(SecurityInfo, protect_proc)              },
2314                 { "ProcSubset",              "s",       NULL,                                    offsetof(SecurityInfo, proc_subset)               },
2315                 { "LoadState",               "s",       NULL,                                    offsetof(SecurityInfo, load_state)                },
2316                 { "LockPersonality",         "b",       NULL,                                    offsetof(SecurityInfo, lock_personality)          },
2317                 { "MemoryDenyWriteExecute",  "b",       NULL,                                    offsetof(SecurityInfo, memory_deny_write_execute) },
2318                 { "NoNewPrivileges",         "b",       NULL,                                    offsetof(SecurityInfo, no_new_privileges)         },
2319                 { "NotifyAccess",            "s",       NULL,                                    offsetof(SecurityInfo, notify_access)             },
2320                 { "PrivateDevices",          "b",       NULL,                                    offsetof(SecurityInfo, private_devices)           },
2321                 { "PrivateMounts",           "b",       NULL,                                    offsetof(SecurityInfo, private_mounts)            },
2322                 { "PrivateNetwork",          "b",       NULL,                                    offsetof(SecurityInfo, private_network)           },
2323                 { "PrivateTmp",              "b",       NULL,                                    offsetof(SecurityInfo, private_tmp)               },
2324                 { "PrivateUsers",            "b",       NULL,                                    offsetof(SecurityInfo, private_users)             },
2325                 { "ProtectControlGroups",    "b",       NULL,                                    offsetof(SecurityInfo, protect_control_groups)    },
2326                 { "ProtectHome",             "s",       NULL,                                    offsetof(SecurityInfo, protect_home)              },
2327                 { "ProtectHostname",         "b",       NULL,                                    offsetof(SecurityInfo, protect_hostname)          },
2328                 { "ProtectKernelModules",    "b",       NULL,                                    offsetof(SecurityInfo, protect_kernel_modules)    },
2329                 { "ProtectKernelTunables",   "b",       NULL,                                    offsetof(SecurityInfo, protect_kernel_tunables)   },
2330                 { "ProtectKernelLogs",       "b",       NULL,                                    offsetof(SecurityInfo, protect_kernel_logs)       },
2331                 { "ProtectClock",            "b",       NULL,                                    offsetof(SecurityInfo, protect_clock)             },
2332                 { "ProtectSystem",           "s",       NULL,                                    offsetof(SecurityInfo, protect_system)            },
2333                 { "RemoveIPC",               "b",       NULL,                                    offsetof(SecurityInfo, remove_ipc)                },
2334                 { "RestrictAddressFamilies", "(bas)",   property_read_restrict_address_families, 0                                                 },
2335                 { "RestrictNamespaces",      "t",       property_read_restrict_namespaces,       0                                                 },
2336                 { "RestrictRealtime",        "b",       NULL,                                    offsetof(SecurityInfo, restrict_realtime)         },
2337                 { "RestrictSUIDSGID",        "b",       NULL,                                    offsetof(SecurityInfo, restrict_suid_sgid)        },
2338                 { "RootDirectory",           "s",       NULL,                                    offsetof(SecurityInfo, root_directory)            },
2339                 { "RootImage",               "s",       NULL,                                    offsetof(SecurityInfo, root_image)                },
2340                 { "SupplementaryGroups",     "as",      NULL,                                    offsetof(SecurityInfo, supplementary_groups)      },
2341                 { "SystemCallArchitectures", "as",      property_read_syscall_archs,             0                                                 },
2342                 { "SystemCallFilter",        "(as)",    property_read_system_call_filter,        0                                                 },
2343                 { "Type",                    "s",       NULL,                                    offsetof(SecurityInfo, type)                      },
2344                 { "UMask",                   "u",       property_read_umask,                     0                                                 },
2345                 { "User",                    "s",       NULL,                                    offsetof(SecurityInfo, user)                      },
2346                 {}
2347         };
2348 
2349         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2350         _cleanup_free_ char *path = NULL;
2351         int r;
2352 
2353         /* Note: this mangles *info on failure! */
2354 
2355         assert(bus);
2356         assert(name);
2357         assert(info);
2358 
2359         path = unit_dbus_path_from_name(name);
2360         if (!path)
2361                 return log_oom();
2362 
2363         r = bus_map_all_properties(
2364                         bus,
2365                         "org.freedesktop.systemd1",
2366                         path,
2367                         security_map,
2368                         BUS_MAP_STRDUP | BUS_MAP_BOOLEAN_AS_BOOL,
2369                         &error,
2370                         NULL,
2371                         info);
2372         if (r < 0)
2373                 return log_error_errno(r, "Failed to get unit properties: %s", bus_error_message(&error, r));
2374 
2375         if (!streq_ptr(info->load_state, "loaded")) {
2376 
2377                 if (FLAGS_SET(flags, ANALYZE_SECURITY_ONLY_LOADED))
2378                         return -EMEDIUMTYPE;
2379 
2380                 if (streq_ptr(info->load_state, "not-found"))
2381                         log_error("Unit %s not found, cannot analyze.", name);
2382                 else if (streq_ptr(info->load_state, "masked"))
2383                         log_error("Unit %s is masked, cannot analyze.", name);
2384                 else
2385                         log_error("Unit %s not loaded properly, cannot analyze.", name);
2386 
2387                 return -EINVAL;
2388         }
2389 
2390         if (FLAGS_SET(flags, ANALYZE_SECURITY_ONLY_LONG_RUNNING) && streq_ptr(info->type, "oneshot"))
2391                 return -EMEDIUMTYPE;
2392 
2393         if (info->private_devices ||
2394             info->private_tmp ||
2395             info->protect_control_groups ||
2396             info->protect_kernel_tunables ||
2397             info->protect_kernel_modules ||
2398             !streq_ptr(info->protect_home, "no") ||
2399             !streq_ptr(info->protect_system, "no") ||
2400             info->root_image)
2401                 info->private_mounts = true;
2402 
2403         if (info->protect_kernel_modules)
2404                 info->capability_bounding_set &= ~(UINT64_C(1) << CAP_SYS_MODULE);
2405 
2406         if (info->protect_kernel_logs)
2407                 info->capability_bounding_set &= ~(UINT64_C(1) << CAP_SYSLOG);
2408 
2409         if (info->protect_clock)
2410                 info->capability_bounding_set &= ~((UINT64_C(1) << CAP_SYS_TIME) |
2411                                                    (UINT64_C(1) << CAP_WAKE_ALARM));
2412 
2413         if (info->private_devices)
2414                 info->capability_bounding_set &= ~((UINT64_C(1) << CAP_MKNOD) |
2415                                                    (UINT64_C(1) << CAP_SYS_RAWIO));
2416 
2417         return 0;
2418 }
2419 
analyze_security_one(sd_bus * bus,const char * name,Table * overview_table,AnalyzeSecurityFlags flags,unsigned threshold,JsonVariant * policy,PagerFlags pager_flags,JsonFormatFlags json_format_flags)2420 static int analyze_security_one(sd_bus *bus,
2421                                 const char *name,
2422                                 Table *overview_table,
2423                                 AnalyzeSecurityFlags flags,
2424                                 unsigned threshold,
2425                                 JsonVariant *policy,
2426                                 PagerFlags pager_flags,
2427                                 JsonFormatFlags json_format_flags) {
2428 
2429         _cleanup_(security_info_freep) SecurityInfo *info = security_info_new();
2430         if (!info)
2431                 return log_oom();
2432 
2433         int r;
2434 
2435         assert(bus);
2436         assert(name);
2437 
2438         r = acquire_security_info(bus, name, info, flags);
2439         if (r == -EMEDIUMTYPE) /* Ignore this one because not loaded or Type is oneshot */
2440                 return 0;
2441         if (r < 0)
2442                 return r;
2443 
2444         r = assess(info, overview_table, flags, threshold, policy, pager_flags, json_format_flags);
2445         if (r < 0)
2446                 return r;
2447 
2448         return 0;
2449 }
2450 
2451 /* Refactoring SecurityInfo so that it can make use of existing struct variables instead of reading from dbus */
get_security_info(Unit * u,ExecContext * c,CGroupContext * g,SecurityInfo ** ret_info)2452 static int get_security_info(Unit *u, ExecContext *c, CGroupContext *g, SecurityInfo **ret_info) {
2453         assert(ret_info);
2454 
2455         _cleanup_(security_info_freep) SecurityInfo *info = security_info_new();
2456         if (!info)
2457                 return log_oom();
2458 
2459         if (u) {
2460                 if (u->id) {
2461                         info->id = strdup(u->id);
2462                         if (!info->id)
2463                                 return log_oom();
2464                 }
2465                 if (unit_type_to_string(u->type)) {
2466                         info->type = strdup(unit_type_to_string(u->type));
2467                         if (!info->type)
2468                                 return log_oom();
2469                 }
2470                 if (unit_load_state_to_string(u->load_state)) {
2471                         info->load_state = strdup(unit_load_state_to_string(u->load_state));
2472                         if (!info->load_state)
2473                                 return log_oom();
2474                 }
2475                 if (u->fragment_path) {
2476                         info->fragment_path = strdup(u->fragment_path);
2477                         if (!info->fragment_path)
2478                                 return log_oom();
2479                 }
2480                 info->default_dependencies = u->default_dependencies;
2481                 if (u->type == UNIT_SERVICE && notify_access_to_string(SERVICE(u)->notify_access)) {
2482                         info->notify_access = strdup(notify_access_to_string(SERVICE(u)->notify_access));
2483                         if (!info->notify_access)
2484                                 return log_oom();
2485                 }
2486         }
2487 
2488         if (c) {
2489                 info->ambient_capabilities = c->capability_ambient_set;
2490                 info->capability_bounding_set = c->capability_bounding_set;
2491                 if (c->user) {
2492                         info->user = strdup(c->user);
2493                         if (!info->user)
2494                                 return log_oom();
2495                 }
2496                 if (c->supplementary_groups) {
2497                         info->supplementary_groups = strv_copy(c->supplementary_groups);
2498                         if (!info->supplementary_groups)
2499                                 return log_oom();
2500                 }
2501                 info->dynamic_user = c->dynamic_user;
2502                 if (exec_keyring_mode_to_string(c->keyring_mode)) {
2503                         info->keyring_mode = strdup(exec_keyring_mode_to_string(c->keyring_mode));
2504                         if (!info->keyring_mode)
2505                                 return log_oom();
2506                 }
2507                 if (protect_proc_to_string(c->protect_proc)) {
2508                         info->protect_proc = strdup(protect_proc_to_string(c->protect_proc));
2509                         if (!info->protect_proc)
2510                                 return log_oom();
2511                 }
2512                 if (proc_subset_to_string(c->proc_subset)) {
2513                         info->proc_subset = strdup(proc_subset_to_string(c->proc_subset));
2514                         if (!info->proc_subset)
2515                                 return log_oom();
2516                 }
2517                 info->lock_personality = c->lock_personality;
2518                 info->memory_deny_write_execute = c->memory_deny_write_execute;
2519                 info->no_new_privileges = c->no_new_privileges;
2520                 info->protect_hostname = c->protect_hostname;
2521                 info->private_devices = c->private_devices;
2522                 info->private_mounts = c->private_mounts;
2523                 info->private_network = c->private_network;
2524                 info->private_tmp = c->private_tmp;
2525                 info->private_users = c->private_users;
2526                 info->protect_control_groups = c->protect_control_groups;
2527                 info->protect_kernel_modules = c->protect_kernel_modules;
2528                 info->protect_kernel_tunables = c->protect_kernel_tunables;
2529                 info->protect_kernel_logs = c->protect_kernel_logs;
2530                 info->protect_clock = c->protect_clock;
2531                 if (protect_home_to_string(c->protect_home)) {
2532                         info->protect_home = strdup(protect_home_to_string(c->protect_home));
2533                         if (!info->protect_home)
2534                                 return log_oom();
2535                 }
2536                 if (protect_system_to_string(c->protect_system)) {
2537                         info->protect_system = strdup(protect_system_to_string(c->protect_system));
2538                         if (!info->protect_system)
2539                                 return log_oom();
2540                 }
2541                 info->remove_ipc = c->remove_ipc;
2542                 info->restrict_address_family_inet =
2543                         info->restrict_address_family_unix =
2544                         info->restrict_address_family_netlink =
2545                         info->restrict_address_family_packet =
2546                         info->restrict_address_family_other =
2547                         c->address_families_allow_list;
2548 
2549                 void *key;
2550                 SET_FOREACH(key, c->address_families) {
2551                         int family = PTR_TO_INT(key);
2552                         if (family == 0)
2553                                 continue;
2554                         if (IN_SET(family, AF_INET, AF_INET6))
2555                                 info->restrict_address_family_inet = !c->address_families_allow_list;
2556                         else if (family == AF_UNIX)
2557                                 info->restrict_address_family_unix = !c->address_families_allow_list;
2558                         else if (family == AF_NETLINK)
2559                                 info->restrict_address_family_netlink = !c->address_families_allow_list;
2560                         else if (family == AF_PACKET)
2561                                 info->restrict_address_family_packet = !c->address_families_allow_list;
2562                         else
2563                                 info->restrict_address_family_other = !c->address_families_allow_list;
2564                 }
2565 
2566                 info->restrict_namespaces = c->restrict_namespaces;
2567                 info->restrict_realtime = c->restrict_realtime;
2568                 info->restrict_suid_sgid = c->restrict_suid_sgid;
2569                 if (c->root_directory) {
2570                         info->root_directory = strdup(c->root_directory);
2571                         if (!info->root_directory)
2572                                 return log_oom();
2573                 }
2574                 if (c->root_image) {
2575                         info->root_image = strdup(c->root_image);
2576                         if (!info->root_image)
2577                                 return log_oom();
2578                 }
2579                 info->_umask = c->umask;
2580 
2581 #if HAVE_SECCOMP
2582                 SET_FOREACH(key, c->syscall_archs) {
2583                         const char *name;
2584 
2585                         name = seccomp_arch_to_string(PTR_TO_UINT32(key) - 1);
2586                         if (!name)
2587                                 continue;
2588 
2589                         if (set_put_strdup(&info->system_call_architectures, name) < 0)
2590                                 return log_oom();
2591                 }
2592 #endif
2593 
2594                 info->system_call_filter_allow_list = c->syscall_allow_list;
2595                 if (c->syscall_filter) {
2596                         info->system_call_filter = hashmap_copy(c->syscall_filter);
2597                         if (!info->system_call_filter)
2598                                 return log_oom();
2599                 }
2600         }
2601 
2602         if (g) {
2603                 info->delegate = g->delegate;
2604                 if (cgroup_device_policy_to_string(g->device_policy)) {
2605                         info->device_policy = strdup(cgroup_device_policy_to_string(g->device_policy));
2606                         if (!info->device_policy)
2607                                 return log_oom();
2608                 }
2609 
2610                 struct in_addr_prefix *i;
2611                 bool deny_ipv4 = false, deny_ipv6 = false;
2612 
2613                 SET_FOREACH(i, g->ip_address_deny) {
2614                         if (i->family == AF_INET && i->prefixlen == 0)
2615                                 deny_ipv4 = true;
2616                         else if (i->family == AF_INET6 && i->prefixlen == 0)
2617                                 deny_ipv6 = true;
2618                 }
2619                 info->ip_address_deny_all = deny_ipv4 && deny_ipv6;
2620 
2621                 info->ip_address_allow_localhost = info->ip_address_allow_other = false;
2622                 SET_FOREACH(i, g->ip_address_allow) {
2623                         if (in_addr_is_localhost(i->family, &i->address))
2624                                 info->ip_address_allow_localhost = true;
2625                         else
2626                                 info->ip_address_allow_other = true;
2627                 }
2628 
2629                 info->ip_filters_custom_ingress = !strv_isempty(g->ip_filters_ingress);
2630                 info->ip_filters_custom_egress = !strv_isempty(g->ip_filters_egress);
2631 
2632                 LIST_FOREACH(device_allow, a, g->device_allow)
2633                         if (strv_extendf(&info->device_allow,
2634                                          "%s:%s%s%s",
2635                                          a->path,
2636                                          a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "") < 0)
2637                                 return log_oom();
2638         }
2639 
2640         *ret_info = TAKE_PTR(info);
2641 
2642         return 0;
2643 }
2644 
offline_security_check(Unit * u,unsigned threshold,JsonVariant * policy,PagerFlags pager_flags,JsonFormatFlags json_format_flags)2645 static int offline_security_check(Unit *u,
2646                                   unsigned threshold,
2647                                   JsonVariant *policy,
2648                                   PagerFlags pager_flags,
2649                                   JsonFormatFlags json_format_flags) {
2650 
2651         _cleanup_(table_unrefp) Table *overview_table = NULL;
2652         AnalyzeSecurityFlags flags = 0;
2653         _cleanup_(security_info_freep) SecurityInfo *info = NULL;
2654         int r;
2655 
2656         assert(u);
2657 
2658         if (DEBUG_LOGGING)
2659                 unit_dump(u, stdout, "\t");
2660 
2661         r = get_security_info(u, unit_get_exec_context(u), unit_get_cgroup_context(u), &info);
2662         if (r < 0)
2663               return r;
2664 
2665         return assess(info, overview_table, flags, threshold, policy, pager_flags, json_format_flags);
2666 }
2667 
offline_security_checks(char ** filenames,JsonVariant * policy,LookupScope scope,bool check_man,bool run_generators,unsigned threshold,const char * root,const char * profile,PagerFlags pager_flags,JsonFormatFlags json_format_flags)2668 static int offline_security_checks(char **filenames,
2669                                    JsonVariant *policy,
2670                                    LookupScope scope,
2671                                    bool check_man,
2672                                    bool run_generators,
2673                                    unsigned threshold,
2674                                    const char *root,
2675                                    const char *profile,
2676                                    PagerFlags pager_flags,
2677                                    JsonFormatFlags json_format_flags) {
2678 
2679         const ManagerTestRunFlags flags =
2680                 MANAGER_TEST_RUN_MINIMAL |
2681                 MANAGER_TEST_RUN_ENV_GENERATORS |
2682                 MANAGER_TEST_RUN_IGNORE_DEPENDENCIES |
2683                 run_generators * MANAGER_TEST_RUN_GENERATORS;
2684 
2685         _cleanup_(manager_freep) Manager *m = NULL;
2686         Unit *units[strv_length(filenames)];
2687         _cleanup_free_ char *var = NULL;
2688         int r, k;
2689         size_t count = 0;
2690 
2691         if (strv_isempty(filenames))
2692                 return 0;
2693 
2694         /* set the path */
2695         r = verify_generate_path(&var, filenames);
2696         if (r < 0)
2697                 return log_error_errno(r, "Failed to generate unit load path: %m");
2698 
2699         assert_se(set_unit_path(var) >= 0);
2700 
2701         r = manager_new(scope, flags, &m);
2702         if (r < 0)
2703                 return log_error_errno(r, "Failed to initialize manager: %m");
2704 
2705         log_debug("Starting manager...");
2706 
2707         r = manager_startup(m, /* serialization= */ NULL, /* fds= */ NULL, root);
2708         if (r < 0)
2709                 return r;
2710 
2711         if (profile) {
2712                 /* Ensure the temporary directory is in the search path, so that we can add drop-ins. */
2713                 r = strv_extend(&m->lookup_paths.search_path, m->lookup_paths.temporary_dir);
2714                 if (r < 0)
2715                         return log_oom();
2716         }
2717 
2718         log_debug("Loading remaining units from the command line...");
2719 
2720         STRV_FOREACH(filename, filenames) {
2721                 _cleanup_free_ char *prepared = NULL;
2722 
2723                 log_debug("Handling %s...", *filename);
2724 
2725                 k = verify_prepare_filename(*filename, &prepared);
2726                 if (k < 0) {
2727                         log_warning_errno(k, "Failed to prepare filename %s: %m", *filename);
2728                         if (r == 0)
2729                                 r = k;
2730                         continue;
2731                 }
2732 
2733                 /* When a portable image is analyzed, the profile is what provides a good chunk of
2734                  * the security-related settings, but they are obviously not shipped with the image.
2735                  * This allows to take them in consideration. */
2736                 if (profile) {
2737                         _cleanup_free_ char *unit_name = NULL, *dropin = NULL, *profile_path = NULL;
2738 
2739                         r = path_extract_filename(prepared, &unit_name);
2740                         if (r < 0)
2741                                 return log_oom();
2742 
2743                         dropin = strjoin(m->lookup_paths.temporary_dir, "/", unit_name, ".d/profile.conf");
2744                         if (!dropin)
2745                                 return log_oom();
2746                         (void) mkdir_parents(dropin, 0755);
2747 
2748                         if (!is_path(profile)) {
2749                                 r = find_portable_profile(profile, unit_name, &profile_path);
2750                                 if (r < 0)
2751                                         return log_error_errno(r, "Failed to find portable profile %s: %m", profile);
2752                                 profile = profile_path;
2753                         }
2754 
2755                         r = copy_file(profile, dropin, 0, 0644, 0, 0, 0);
2756                         if (r < 0)
2757                                 return log_error_errno(r, "Failed to copy: %m");
2758                 }
2759 
2760                 k = manager_load_startable_unit_or_warn(m, NULL, prepared, &units[count]);
2761                 if (k < 0) {
2762                         if (r == 0)
2763                                 r = k;
2764                         continue;
2765                 }
2766 
2767                 count++;
2768         }
2769 
2770         for (size_t i = 0; i < count; i++) {
2771                 k = offline_security_check(units[i], threshold, policy, pager_flags, json_format_flags);
2772                 if (k < 0 && r == 0)
2773                         r = k;
2774         }
2775 
2776         return r;
2777 }
2778 
analyze_security(sd_bus * bus,char ** units,JsonVariant * policy,LookupScope scope,bool check_man,bool run_generators,bool offline,unsigned threshold,const char * root,const char * profile,JsonFormatFlags json_format_flags,PagerFlags pager_flags,AnalyzeSecurityFlags flags)2779 static int analyze_security(sd_bus *bus,
2780                      char **units,
2781                      JsonVariant *policy,
2782                      LookupScope scope,
2783                      bool check_man,
2784                      bool run_generators,
2785                      bool offline,
2786                      unsigned threshold,
2787                      const char *root,
2788                      const char *profile,
2789                      JsonFormatFlags json_format_flags,
2790                      PagerFlags pager_flags,
2791                      AnalyzeSecurityFlags flags) {
2792 
2793         _cleanup_(table_unrefp) Table *overview_table = NULL;
2794         int ret = 0, r;
2795 
2796         assert(!!bus != offline);
2797 
2798         if (offline)
2799                 return offline_security_checks(units, policy, scope, check_man, run_generators, threshold, root, profile, pager_flags, json_format_flags);
2800 
2801         if (strv_length(units) != 1) {
2802                 overview_table = table_new("unit", "exposure", "predicate", "happy");
2803                 if (!overview_table)
2804                         return log_oom();
2805         }
2806 
2807         if (strv_isempty(units)) {
2808                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2809                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
2810                 _cleanup_strv_free_ char **list = NULL;
2811                 size_t n = 0;
2812 
2813                 r = sd_bus_call_method(
2814                                 bus,
2815                                 "org.freedesktop.systemd1",
2816                                 "/org/freedesktop/systemd1",
2817                                 "org.freedesktop.systemd1.Manager",
2818                                 "ListUnits",
2819                                 &error,
2820                                 &reply,
2821                                 NULL);
2822                 if (r < 0)
2823                         return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r));
2824 
2825                 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
2826                 if (r < 0)
2827                         return bus_log_parse_error(r);
2828 
2829                 for (;;) {
2830                         UnitInfo info;
2831                         char *copy = NULL;
2832 
2833                         r = bus_parse_unit_info(reply, &info);
2834                         if (r < 0)
2835                                 return bus_log_parse_error(r);
2836                         if (r == 0)
2837                                 break;
2838 
2839                         if (!endswith(info.id, ".service"))
2840                                 continue;
2841 
2842                         if (!GREEDY_REALLOC(list, n + 2))
2843                                 return log_oom();
2844 
2845                         copy = strdup(info.id);
2846                         if (!copy)
2847                                 return log_oom();
2848 
2849                         list[n++] = copy;
2850                         list[n] = NULL;
2851                 }
2852 
2853                 strv_sort(list);
2854 
2855                 flags |= ANALYZE_SECURITY_SHORT|ANALYZE_SECURITY_ONLY_LOADED|ANALYZE_SECURITY_ONLY_LONG_RUNNING;
2856 
2857                 STRV_FOREACH(i, list) {
2858                         r = analyze_security_one(bus, *i, overview_table, flags, threshold, policy, pager_flags, json_format_flags);
2859                         if (r < 0 && ret >= 0)
2860                                 ret = r;
2861                 }
2862 
2863         } else
2864                 STRV_FOREACH(i, units) {
2865                         _cleanup_free_ char *mangled = NULL, *instance = NULL;
2866                         const char *name;
2867 
2868                         if (!FLAGS_SET(flags, ANALYZE_SECURITY_SHORT) && i != units) {
2869                                 putc('\n', stdout);
2870                                 fflush(stdout);
2871                         }
2872 
2873                         r = unit_name_mangle(*i, 0, &mangled);
2874                         if (r < 0)
2875                                 return log_error_errno(r, "Failed to mangle unit name '%s': %m", *i);
2876 
2877                         if (!endswith(mangled, ".service"))
2878                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2879                                                        "Unit %s is not a service unit, refusing.",
2880                                                        *i);
2881 
2882                         if (unit_name_is_valid(mangled, UNIT_NAME_TEMPLATE)) {
2883                                 r = unit_name_replace_instance(mangled, "test-instance", &instance);
2884                                 if (r < 0)
2885                                         return log_oom();
2886 
2887                                 name = instance;
2888                         } else
2889                                 name = mangled;
2890 
2891                         r = analyze_security_one(bus, name, overview_table, flags, threshold, policy, pager_flags, json_format_flags);
2892                         if (r < 0 && ret >= 0)
2893                                 ret = r;
2894                 }
2895 
2896         if (overview_table) {
2897                 if (!FLAGS_SET(flags, ANALYZE_SECURITY_SHORT)) {
2898                         putc('\n', stdout);
2899                         fflush(stdout);
2900                 }
2901 
2902                 r = table_print_with_pager(overview_table, json_format_flags, pager_flags, /* show_header= */true);
2903                 if (r < 0)
2904                         return log_error_errno(r, "Failed to output table: %m");
2905         }
2906         return ret;
2907 }
2908 
verb_security(int argc,char * argv[],void * userdata)2909 int verb_security(int argc, char *argv[], void *userdata) {
2910         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2911         _cleanup_(json_variant_unrefp) JsonVariant *policy = NULL;
2912         int r;
2913         unsigned line, column;
2914 
2915         if (!arg_offline) {
2916                 r = acquire_bus(&bus, NULL);
2917                 if (r < 0)
2918                         return bus_log_connect_error(r, arg_transport);
2919         }
2920 
2921         pager_open(arg_pager_flags);
2922 
2923         if (arg_security_policy) {
2924                 r = json_parse_file(/*f=*/ NULL, arg_security_policy, /*flags=*/ 0, &policy, &line, &column);
2925                 if (r < 0)
2926                         return log_error_errno(r, "Failed to parse '%s' at %u:%u: %m", arg_security_policy, line, column);
2927         } else {
2928                 _cleanup_fclose_ FILE *f = NULL;
2929                 _cleanup_free_ char *pp = NULL;
2930 
2931                 r = search_and_fopen_nulstr("systemd-analyze-security.policy", "re", /*root=*/ NULL, CONF_PATHS_NULSTR("systemd"), &f, &pp);
2932                 if (r < 0 && r != -ENOENT)
2933                         return r;
2934 
2935                 if (f) {
2936                         r = json_parse_file(f, pp, /*flags=*/ 0, &policy, &line, &column);
2937                         if (r < 0)
2938                                 return log_error_errno(r, "[%s:%u:%u] Failed to parse JSON policy: %m", pp, line, column);
2939                 }
2940         }
2941 
2942         return analyze_security(bus,
2943                                 strv_skip(argv, 1),
2944                                 policy,
2945                                 arg_scope,
2946                                 arg_man,
2947                                 arg_generators,
2948                                 arg_offline,
2949                                 arg_threshold,
2950                                 arg_root,
2951                                 arg_profile,
2952                                 arg_json_format_flags,
2953                                 arg_pager_flags,
2954                                 /*flags=*/ 0);
2955 }
2956