forked from msg555/sjail
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfilter_net.cpp
316 lines (281 loc) · 8.54 KB
/
filter_net.cpp
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <string>
#include <regex.h>
#include "config.h"
#include "filter.h"
#include "memory.h"
#include "report.h"
#include "process_state.h"
#define SOCKOP_socket 1
#define SOCKOP_bind 2
#define SOCKOP_connect 3
#define SOCKOP_listen 4
#define SOCKOP_accept 5
#define SOCKOP_getsockname 6
#define SOCKOP_getpeername 7
#define SOCKOP_socketpair 8
#define SOCKOP_send 9
#define SOCKOP_recv 10
#define SOCKOP_sendto 11
#define SOCKOP_recvfrom 12
#define SOCKOP_shutdown 13
#define SOCKOP_setsockopt 14
#define SOCKOP_getsockopt 15
#define SOCKOP_sendmsg 16
#define SOCKOP_recvmsg 17
#define SOCKOP_accept4 18
#define SOCKOP_recvmmsg 19
#define SOCKOP_sendmmsg 20
#define SOCK_TYPE_MASK 0xF
static int regex_init = false;
static regex_t net_reg;
static bool remote_client_allowed(pid_data& pdata, param_t* args) {
pid_t pid = pdata.pid;
param_t addr = args[0];
param_t addr_sz = args[1];
if(addr == 0) {
return true;
} else if(addr_sz < sizeof(sa_family_t)) {
log_violation(pid, "address too small");
return false;
}
char buf[INET6_ADDRSTRLEN];
void* vaddr = safemem_read_pid(pdata, addr, addr_sz);
if(!vaddr) {
log_violation(pid, "unreadable remote address family");
return false;
}
std::string rem_addr;
sa_family_t family = *(sa_family_t*)vaddr;
switch(family) {
case AF_INET:
case AF_INET6: {
switch(addr_sz) {
case sizeof(sockaddr_in): {
sockaddr_in* sin = (sockaddr_in*)vaddr;
if(!inet_ntop(family, &sin->sin_addr, buf, sizeof(buf))) {
log_violation(pid, "could not convert network "
"address to presentation form");
return false;
}
rem_addr = std::string(buf) + ";" +
convert<std::string>(ntohs(sin->sin_port));
break;
} case sizeof(sockaddr_in6): {
sockaddr_in6* sin = (sockaddr_in6*)vaddr;
if(!inet_ntop(family, &sin->sin6_addr, buf, sizeof(buf))) {
log_violation(pid, "could not convert network "
"address to presentation form");
return false;
}
rem_addr = std::string(buf) + ";" +
convert<std::string>(ntohs(sin->sin6_port));
break;
} default: {
log_violation(pid, "unexpected remote address struct size");
return false;
}
}
rem_addr += family == AF_INET ? ";INET" : ";INET6";
break;
} case AF_UNIX: {
if(sizeof(sockaddr_un) > addr_sz) {
log_violation(pid, "unexpected remote address struct size");
return false;
}
sockaddr_un* sin = (sockaddr_un*)vaddr;
for(size_t i = 0; ; i++) {
if(i == sizeof(sin->sun_path) || sin->sun_path[i] == ';') {
log_violation(pid, "unix path malformed");
return false;
}
if(!sin->sun_path[i]) {
break;
}
}
rem_addr = std::string(sin->sun_path) + ";0;UNIX";
break;
} default: {
log_violation(pid, "unsupported remote address family");
return false;
}
}
if(!regex_init && !get_net_regexp().empty()) {
if(regcomp(&net_reg, get_net_regexp().c_str(), REG_EXTENDED | REG_NOSUB)) {
log_violation(pid, "failed to compile net regexp");
return false;
}
regex_init = true;
}
if(!regex_init || regexec(&net_reg, rem_addr.c_str(), (size_t)0, NULL, 0)) {
log_violation(pid, std::string("attempted to communicate with ") +
rem_addr);
return false;
}
if(!get_passive()) {
uintptr_t rem_addr = safemem_remote_addr(pdata, vaddr);
if(!rem_addr) {
log_violation(pid, "cannot allow net op without safe mem installed");
return false;
}
args[0] = rem_addr;
}
log_info(pid, 1, "net address ok: " + rem_addr);
return true;
}
net_filter::net_filter() {
}
net_filter::~net_filter() {
}
filter_action net_filter::filter_syscall_enter(pid_data& pdata,
process_state& st) {
int op;
bool generic_call = false;
pid_t pid = pdata.pid;
switch(st.get_syscall()) {
case sys_socketcall:
generic_call = true;
op = st.get_param(0);
break;
#define DOCASE(x) case sys_ ## x: op = SOCKOP_ ## x; break
DOCASE(socket);
DOCASE(bind);
DOCASE(connect);
DOCASE(listen);
DOCASE(accept);
DOCASE(getsockname);
DOCASE(getpeername);
DOCASE(socketpair);
DOCASE(send);
DOCASE(recv);
DOCASE(sendto);
DOCASE(recvfrom);
DOCASE(shutdown);
DOCASE(setsockopt);
DOCASE(getsockopt);
DOCASE(sendmsg);
DOCASE(recvmsg);
DOCASE(accept4);
DOCASE(recvmmsg);
DOCASE(sendmmsg);
#undef DOCASE
default:
return FILTER_NO_ACTION;
}
/* Quickly filter out always yes/no syscalls. */
size_t nargs = 0;
size_t addr_pos = 6;
switch(op) {
case SOCKOP_bind:
case SOCKOP_listen:
case SOCKOP_accept:
case SOCKOP_accept4:
return get_listen() ? FILTER_PERMIT_SYSCALL : FILTER_BLOCK_SYSCALL;
case SOCKOP_getsockname:
case SOCKOP_getpeername:
case SOCKOP_shutdown:
return FILTER_PERMIT_SYSCALL;
/* Need to vet the remote address by peering into msg to support this. */
case SOCKOP_sendmsg:
case SOCKOP_recvmsg:
case SOCKOP_sendmmsg:
case SOCKOP_recvmmsg:
return FILTER_BLOCK_SYSCALL;
/* We probably can support some options by I'm not comfortable white listing
* this method entirely. */
case SOCKOP_setsockopt:
return FILTER_BLOCK_SYSCALL;
case SOCKOP_getsockopt:
return FILTER_PERMIT_SYSCALL;
/* We only vet the remote address. To use these you must have already
* successfully set one up. */
case SOCKOP_send:
case SOCKOP_recv:
case SOCKOP_recvfrom:
return FILTER_PERMIT_SYSCALL;
/* We need to load and sanitize the arguments before we vet. */
case SOCKOP_socket: nargs = 3; break;
case SOCKOP_socketpair: nargs = 4; break;
case SOCKOP_connect: nargs = 3; addr_pos = 1; break;
case SOCKOP_sendto: nargs = 6; addr_pos = 4; break;
default:
log_violation(pid, "unknown socket operation");
return FILTER_BLOCK_SYSCALL;
}
/* Read in parameters wherever they come from. */
void* params = NULL;
param_t s_params[6];
if(generic_call) {
size_t width = st.word_width();
params = safemem_read_pid(pdata, st.get_param(1), nargs * width);
if(!params) {
log_violation(pid, "could not read socketcall parameters");
return FILTER_BLOCK_SYSCALL;
}
for(size_t i = 0; i < nargs; i++) {
s_params[i] = st.read_uword((char*)params + i * width);
}
} else {
for(size_t i = 0; i < nargs; i++) {
s_params[i] = st.get_param(i);
}
}
if(addr_pos < nargs &&
!remote_client_allowed(pdata, s_params + addr_pos)) {
return FILTER_BLOCK_SYSCALL;
}
switch(op) {
case SOCKOP_socket:
case SOCKOP_socketpair: {
if(s_params[0] != PF_INET && s_params[0] != PF_INET6 &&
s_params[0] != PF_UNIX) {
log_violation(pid, "unsupported socket domain used");
return FILTER_BLOCK_SYSCALL;
}
param_t sock_type = s_params[1];
#ifdef SOCK_TYPE_MASK
sock_type &= SOCK_TYPE_MASK;
#endif
if(sock_type != SOCK_STREAM && sock_type != SOCK_DGRAM) {
log_violation(pid, "protocol other than tcp or udp used");
return FILTER_BLOCK_SYSCALL;
}
if(s_params[1] == SOCK_STREAM && !get_tcp()) {
log_violation(pid, "tcp used");
return FILTER_BLOCK_SYSCALL;
}
if(s_params[1] == SOCK_DGRAM && !get_udp()) {
log_violation(pid, "udp used");
return FILTER_BLOCK_SYSCALL;
}
} break;
/* Their parameters are vetted earlier. */
case SOCKOP_connect:
case SOCKOP_sendto:
break;
default:
log_violation(pid, "internal error");
return FILTER_BLOCK_SYSCALL;
}
/* If socketcall was used the parameters are held in the client address space
* and we need to move them to a safe location. */
if(generic_call && !get_passive()) {
size_t width = st.word_width();
for(size_t i = 0; i < nargs; i++) {
st.write_uword((char*)params + i * width, s_params[i]);
}
uintptr_t rem_addr = safemem_remote_addr(pdata, params);
if(!rem_addr) {
log_violation(pid, "cannot allow socketcall without safe mem installed");
return FILTER_BLOCK_SYSCALL;
}
st.set_param(1, rem_addr);
}
return FILTER_PERMIT_SYSCALL;
}