]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/enosys.c
flock: initialize timevals [-Werror=maybe-uninitialized]
[thirdparty/util-linux.git] / misc-utils / enosys.c
CommitLineData
c93114cd
TW
1/*
2 * Copyright (C) 2023 Thomas Weißschuh <thomas@t-8ch.de>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it would be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19#include <stddef.h>
20#include <stdbool.h>
21#include <getopt.h>
22
23#include <linux/unistd.h>
24#include <linux/filter.h>
25#include <linux/seccomp.h>
26#include <linux/audit.h>
27#include <sys/prctl.h>
6c1bd545 28#include <sys/syscall.h>
65bf3c73 29#include <sys/ioctl.h>
c93114cd
TW
30
31#include "c.h"
32#include "exitcodes.h"
13d1cbce 33#include "nls.h"
afb669af 34#include "bitops.h"
29e3f737 35#include "audit-arch.h"
358e3e43
TW
36#include "list.h"
37#include "xalloc.h"
38#include "strutils.h"
c93114cd 39
afb669af 40#define IS_LITTLE_ENDIAN (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
7626054c 41
c93114cd 42#define syscall_nr (offsetof(struct seccomp_data, nr))
9e8fb1f5 43#define syscall_arch (offsetof(struct seccomp_data, arch))
afb669af 44#define syscall_arg(n) (offsetof(struct seccomp_data, args[n]))
c93114cd 45
ced82526
TW
46static int set_seccomp_filter(const void *prog)
47{
48#if defined(__NR_seccomp) && defined(SECCOMP_SET_MODE_FILTER) && defined(SECCOMP_FILTER_FLAG_SPEC_ALLOW)
49 if (!syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_SPEC_ALLOW, prog))
50 return 0;
51#endif
52
53 return prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, prog);
54}
55
c93114cd
TW
56struct syscall {
57 const char *const name;
58fe2636 58 long number;
c93114cd
TW
59};
60
1078c4ae
TW
61/* When the alias arrays are empty the compiler emits -Wtype-limits warnings.
62 * Avoid those by going through this function. */
63static inline bool lt(size_t a, size_t b)
64{
65 return a < b;
66}
67
165fbfd4 68static const struct syscall syscalls[] = {
69e542bd
TW
69#define UL_SYSCALL(name, nr) { name, nr },
70#include "syscalls.h"
71#undef UL_SYSCALL
c93114cd
TW
72};
73
65bf3c73
TW
74static const struct syscall ioctls[] = {
75 { "FIOCLEX", FIOCLEX },
76};
65bf3c73 77
344ba205
TW
78static void __attribute__((__noreturn__)) usage(void)
79{
80 FILE *out = stdout;
81
82 fputs(USAGE_HEADER, out);
83 fprintf(out, _(" %s [options] -- <command>\n"), program_invocation_short_name);
84
85 fputs(USAGE_OPTIONS, out);
86 fputs(_(" -s, --syscall syscall to block\n"), out);
65bf3c73 87 fputs(_(" -i, --ioctl ioctl to block\n"), out);
7f104027 88 fputs(_(" -l, --list list known syscalls\n"), out);
344ba205
TW
89
90 fputs(USAGE_SEPARATOR, out);
91 fprintf(out, USAGE_HELP_OPTIONS(25));
92
93 fprintf(out, USAGE_MAN_TAIL("enosys(1)"));
94
95 exit(EXIT_SUCCESS);
96}
97
358e3e43
TW
98struct blocked_number {
99 struct list_head head;
100 long number;
101};
102
c93114cd
TW
103int main(int argc, char **argv)
104{
2aeb519a 105 int c;
c93114cd
TW
106 size_t i;
107 bool found;
108 static const struct option longopts[] = {
65bf3c73
TW
109 { "syscall", required_argument, NULL, 's' },
110 { "ioctl", required_argument, NULL, 'i' },
111 { "list", no_argument, NULL, 'l' },
112 { "list-ioctl", no_argument, NULL, 'm' },
113 { "version", no_argument, NULL, 'V' },
114 { "help", no_argument, NULL, 'h' },
c93114cd
TW
115 { 0 }
116 };
117
358e3e43
TW
118 long blocked_number;
119 struct blocked_number *blocked;
65bf3c73 120 struct list_head *loop_ctr;
358e3e43
TW
121 struct list_head blocked_syscalls;
122 INIT_LIST_HEAD(&blocked_syscalls);
65bf3c73
TW
123 struct list_head blocked_ioctls;
124 INIT_LIST_HEAD(&blocked_ioctls);
c93114cd 125
d191a7be
TW
126 setlocale(LC_ALL, "");
127 bindtextdomain(PACKAGE, LOCALEDIR);
128 textdomain(PACKAGE);
129
65bf3c73 130 while ((c = getopt_long (argc, argv, "+Vhs:i:lm", longopts, NULL)) != -1) {
c93114cd
TW
131 switch (c) {
132 case 's':
133 found = 0;
1078c4ae 134 for (i = 0; lt(i, ARRAY_SIZE(syscalls)); i++) {
c93114cd 135 if (strcmp(optarg, syscalls[i].name) == 0) {
358e3e43 136 blocked_number = syscalls[i].number;
c93114cd
TW
137 found = 1;
138 break;
139 }
140 }
141 if (!found)
358e3e43
TW
142 blocked_number = str2num_or_err(
143 optarg, 10, _("Unknown syscall"), 0, LONG_MAX);
144
145 blocked = xmalloc(sizeof(*blocked));
146 blocked->number = blocked_number;
147 list_add(&blocked->head, &blocked_syscalls);
148
65bf3c73
TW
149 break;
150 case 'i':
151 found = 0;
1078c4ae 152 for (i = 0; lt(i, ARRAY_SIZE(ioctls)); i++) {
65bf3c73
TW
153 if (strcmp(optarg, ioctls[i].name) == 0) {
154 blocked_number = ioctls[i].number;
155 found = 1;
156 break;
157 }
158 }
159 if (!found)
160 blocked_number = str2num_or_err(
161 optarg, 10, _("Unknown ioctl"), 0, LONG_MAX);
162
163 blocked = xmalloc(sizeof(*blocked));
164 blocked->number = blocked_number;
165 list_add(&blocked->head, &blocked_ioctls);
166
c93114cd 167 break;
7f104027 168 case 'l':
1078c4ae 169 for (i = 0; lt(i, ARRAY_SIZE(syscalls)); i++)
e9e52c79 170 printf("%5ld %s\n", syscalls[i].number, syscalls[i].name);
7f104027 171 return EXIT_SUCCESS;
65bf3c73 172 case 'm':
1078c4ae 173 for (i = 0; lt(i, ARRAY_SIZE(ioctls)); i++)
65bf3c73
TW
174 printf("%5ld %s\n", ioctls[i].number, ioctls[i].name);
175 return EXIT_SUCCESS;
344ba205
TW
176 case 'V':
177 print_version(EXIT_SUCCESS);
178 case 'h':
179 usage();
c93114cd 180 default:
344ba205 181 errtryhelp(EXIT_FAILURE);
c93114cd
TW
182 }
183 }
184
185 if (optind >= argc)
344ba205 186 errtryhelp(EXIT_FAILURE);
c93114cd 187
3085fabe
TW
188 struct sock_filter filter[BPF_MAXINSNS];
189 struct sock_filter *f = filter;
190
191#define INSTR(_instruction) \
192 if (f == &filter[ARRAY_SIZE(filter)]) \
193 errx(EXIT_FAILURE, _("filter too big")); \
194 *f++ = (struct sock_filter) _instruction
195
196 INSTR(BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_arch));
197 INSTR(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SECCOMP_ARCH_NATIVE, 1, 0));
198 INSTR(BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRAP));
199
200 /* Blocking "execve" normally would also block our own call to
201 * it and the end of main. To distinguish between our execve
202 * and the execve to be blocked, compare the environ pointer.
203 *
204 * See https://lore.kernel.org/all/CAAnLoWnS74dK9Wq4EQ-uzQ0qCRfSK-dLqh+HCais-5qwDjrVzg@mail.gmail.com/
205 */
206 INSTR(BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_nr));
207 INSTR(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_execve, 0, 5));
208 INSTR(BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_arg(2) + 4 * !IS_LITTLE_ENDIAN));
209 INSTR(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, (uint64_t)(uintptr_t) environ, 0, 3));
210 INSTR(BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_arg(2) + 4 * IS_LITTLE_ENDIAN));
211 INSTR(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, (uint64_t)(uintptr_t) environ >> 32, 0, 1));
212 INSTR(BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW));
213
214 INSTR(BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_nr));
c93114cd 215
358e3e43
TW
216 list_for_each(loop_ctr, &blocked_syscalls) {
217 blocked = list_entry(loop_ctr, struct blocked_number, head);
3085fabe 218
358e3e43 219 INSTR(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, blocked->number, 0, 1));
3085fabe 220 INSTR(BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | ENOSYS));
c93114cd
TW
221 }
222
65bf3c73
TW
223 INSTR(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_ioctl, 1, 0));
224 INSTR(BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW));
225
226 INSTR(BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_arg(1)));
227
228 list_for_each(loop_ctr, &blocked_ioctls) {
229 blocked = list_entry(loop_ctr, struct blocked_number, head);
230
231 INSTR(BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_arg(1) + 4 * !IS_LITTLE_ENDIAN));
232 INSTR(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, (uint64_t) blocked->number, 0, 3));
233 INSTR(BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_arg(1) + 4 * IS_LITTLE_ENDIAN));
234 INSTR(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, (uint64_t) blocked->number >> 32, 0, 1));
235 INSTR(BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | ENOTTY));
236 }
237
3085fabe
TW
238 INSTR(BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW));
239
c93114cd 240 struct sock_fprog prog = {
3085fabe 241 .len = f - filter,
c93114cd
TW
242 .filter = filter,
243 };
244
f080b635
TW
245 /* *SET* below will return EINVAL when either the filter is invalid or
246 * seccomp is not supported. To distinguish those cases do a *GET* here
247 */
248 if (prctl(PR_GET_SECCOMP) == -1 && errno == EINVAL)
13d1cbce 249 err(EXIT_NOTSUPP, _("Seccomp non-functional"));
f080b635 250
c93114cd 251 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
a3fdbe0c 252 err_nosys(EXIT_FAILURE, _("Could not run prctl(PR_SET_NO_NEW_PRIVS)"));
c93114cd 253
ced82526
TW
254 if (set_seccomp_filter(&prog))
255 err_nosys(EXIT_FAILURE, _("Could not seccomp filter"));
c93114cd
TW
256
257 if (execvp(argv[optind], argv + optind))
13d1cbce 258 err(EXIT_NOTSUPP, _("Could not exec"));
c93114cd 259}