1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "analyze-syscall-filter.h"
4 #include "analyze.h"
5 #include "fd-util.h"
6 #include "fileio.h"
7 #include "nulstr-util.h"
8 #include "seccomp-util.h"
9 #include "set.h"
10 #include "strv.h"
11 #include "terminal-util.h"
12
13 #if HAVE_SECCOMP
14
load_kernel_syscalls(Set ** ret)15 static int load_kernel_syscalls(Set **ret) {
16 _cleanup_set_free_ Set *syscalls = NULL;
17 _cleanup_fclose_ FILE *f = NULL;
18 int r;
19
20 /* Let's read the available system calls from the list of available tracing events. Slightly dirty,
21 * but good enough for analysis purposes. */
22
23 f = fopen("/sys/kernel/tracing/available_events", "re");
24 if (!f) {
25 /* We tried the non-debugfs mount point and that didn't work. If it wasn't mounted, maybe the
26 * old debugfs mount point works? */
27 f = fopen("/sys/kernel/debug/tracing/available_events", "re");
28 if (!f)
29 return log_full_errno(IN_SET(errno, EPERM, EACCES, ENOENT) ? LOG_DEBUG : LOG_WARNING, errno,
30 "Can't read open tracefs' available_events file: %m");
31 }
32
33 for (;;) {
34 _cleanup_free_ char *line = NULL;
35 const char *e;
36
37 r = read_line(f, LONG_LINE_MAX, &line);
38 if (r < 0)
39 return log_error_errno(r, "Failed to read system call list: %m");
40 if (r == 0)
41 break;
42
43 e = startswith(line, "syscalls:sys_enter_");
44 if (!e)
45 continue;
46
47 /* These are named differently inside the kernel than their external name for historical
48 * reasons. Let's hide them here. */
49 if (STR_IN_SET(e, "newuname", "newfstat", "newstat", "newlstat", "sysctl"))
50 continue;
51
52 r = set_put_strdup(&syscalls, e);
53 if (r < 0)
54 return log_error_errno(r, "Failed to add system call to list: %m");
55 }
56
57 *ret = TAKE_PTR(syscalls);
58 return 0;
59 }
60
syscall_set_remove(Set * s,const SyscallFilterSet * set)61 static void syscall_set_remove(Set *s, const SyscallFilterSet *set) {
62 const char *syscall;
63
64 if (!set)
65 return;
66
67 NULSTR_FOREACH(syscall, set->value) {
68 if (syscall[0] == '@')
69 continue;
70
71 free(set_remove(s, syscall));
72 }
73 }
74
dump_syscall_filter(const SyscallFilterSet * set)75 static void dump_syscall_filter(const SyscallFilterSet *set) {
76 const char *syscall;
77
78 printf("%s%s%s\n"
79 " # %s\n",
80 ansi_highlight(),
81 set->name,
82 ansi_normal(),
83 set->help);
84
85 NULSTR_FOREACH(syscall, set->value)
86 printf(" %s%s%s\n", syscall[0] == '@' ? ansi_underline() : "", syscall, ansi_normal());
87 }
88
verb_syscall_filters(int argc,char * argv[],void * userdata)89 int verb_syscall_filters(int argc, char *argv[], void *userdata) {
90 bool first = true;
91
92 pager_open(arg_pager_flags);
93
94 if (strv_isempty(strv_skip(argv, 1))) {
95 _cleanup_set_free_ Set *kernel = NULL, *known = NULL;
96 const char *sys;
97 int k = 0; /* explicit initialization to appease gcc */
98
99 NULSTR_FOREACH(sys, syscall_filter_sets[SYSCALL_FILTER_SET_KNOWN].value)
100 if (set_put_strdup(&known, sys) < 0)
101 return log_oom();
102
103 if (!arg_quiet)
104 k = load_kernel_syscalls(&kernel);
105
106 for (int i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
107 const SyscallFilterSet *set = syscall_filter_sets + i;
108 if (!first)
109 puts("");
110
111 dump_syscall_filter(set);
112 syscall_set_remove(kernel, set);
113 if (i != SYSCALL_FILTER_SET_KNOWN)
114 syscall_set_remove(known, set);
115 first = false;
116 }
117
118 if (arg_quiet) /* Let's not show the extra stuff in quiet mode */
119 return 0;
120
121 if (!set_isempty(known)) {
122 _cleanup_free_ char **l = NULL;
123
124 printf("\n"
125 "# %sUngrouped System Calls%s (known but not included in any of the groups except @known):\n",
126 ansi_highlight(), ansi_normal());
127
128 l = set_get_strv(known);
129 if (!l)
130 return log_oom();
131
132 strv_sort(l);
133
134 STRV_FOREACH(syscall, l)
135 printf("# %s\n", *syscall);
136 }
137
138 if (k < 0) {
139 fputc('\n', stdout);
140 fflush(stdout);
141 if (!arg_quiet)
142 log_notice_errno(k, "# Not showing unlisted system calls, couldn't retrieve kernel system call list: %m");
143 } else if (!set_isempty(kernel)) {
144 _cleanup_free_ char **l = NULL;
145
146 printf("\n"
147 "# %sUnlisted System Calls%s (supported by the local kernel, but not included in any of the groups listed above):\n",
148 ansi_highlight(), ansi_normal());
149
150 l = set_get_strv(kernel);
151 if (!l)
152 return log_oom();
153
154 strv_sort(l);
155
156 STRV_FOREACH(syscall, l)
157 printf("# %s\n", *syscall);
158 }
159 } else
160 STRV_FOREACH(name, strv_skip(argv, 1)) {
161 const SyscallFilterSet *set;
162
163 if (!first)
164 puts("");
165
166 set = syscall_filter_set_find(*name);
167 if (!set) {
168 /* make sure the error appears below normal output */
169 fflush(stdout);
170
171 return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
172 "Filter set \"%s\" not found.", *name);
173 }
174
175 dump_syscall_filter(set);
176 first = false;
177 }
178
179 return 0;
180 }
181
182 #else
verb_syscall_filters(int argc,char * argv[],void * userdata)183 int verb_syscall_filters(int argc, char *argv[], void *userdata) {
184 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Not compiled with syscall filters, sorry.");
185 }
186 #endif
187