1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
/*
* Copyright (C) 2023 Thomas Weißschuh <thomas@t-8ch.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stddef.h>
#include <stdbool.h>
#include <getopt.h>
#include <linux/unistd.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <linux/audit.h>
#include <sys/prctl.h>
#include "c.h"
#include "audit-arch.h"
#include "exitcodes.h"
#define syscall_nr (offsetof(struct seccomp_data, nr))
struct syscall {
const char *const name;
int number;
};
const struct syscall syscalls[] = {
#ifdef __NR_move_mount
{ "move_mount", __NR_move_mount },
#endif
#ifdef __NR_open_tree
{ "open_tree", __NR_open_tree },
#endif
#ifdef __NR_fsopen
{ "fsopen", __NR_fsopen },
#endif
#ifdef __NR_mount_setattr
{ "mount_setattr", __NR_mount_setattr },
#endif
};
int main(int argc, char **argv)
{
int c;
size_t i;
bool found;
static const struct option longopts[] = {
{ "syscall", required_argument, NULL, 's' },
{ 0 }
};
bool blocked_syscalls[ARRAY_SIZE(syscalls)] = {};
while ((c = getopt_long (argc, argv, "s:", longopts, NULL)) != -1) {
switch (c) {
case 's':
found = 0;
for (i = 0; i < ARRAY_SIZE(syscalls); i++) {
if (strcmp(optarg, syscalls[i].name) == 0) {
blocked_syscalls[i] = true;
found = 1;
break;
}
}
if (!found)
errx(EXIT_FAILURE, "Unknown syscall '%s'", optarg);
break;
default:
errx(EXIT_FAILURE, "Unknown option");
}
}
if (optind >= argc)
errx(EXIT_FAILURE, "No executable specified");
#define N_FILTERS (ARRAY_SIZE(syscalls) + 3)
struct sock_filter filter[N_FILTERS] = {
[0] = BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_nr),
[N_FILTERS - 2] = BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
[N_FILTERS - 1] = BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | ENOSYS),
};
const struct sock_filter nop = BPF_JUMP(BPF_JMP | BPF_JA, 0, 0, 0);
for (i = 0; i < ARRAY_SIZE(syscalls); i++) {
if (blocked_syscalls[i]) {
const struct sock_filter block = BPF_JUMP(
BPF_JMP | BPF_JEQ | BPF_K,
syscalls[i].number,
N_FILTERS - 3 - i, 0);
filter[i + 1] = block;
} else {
filter[i + 1] = nop;
}
}
struct sock_fprog prog = {
.len = ARRAY_SIZE(filter),
.filter = filter,
};
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
err(EXIT_NOTSUPP, "prctl(PR_SET_NO_NEW_PRIVS)");
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog))
err(EXIT_NOTSUPP, "prctl(PR_SET_SECCOMP)");
if (execvp(argv[optind], argv + optind))
err(EXIT_NOTSUPP, "Could not exec");
}
|