VERSION: Disable GIT_SNAPSHOT for the Samba 4.18.0rc1 release.
[Samba.git] / source4 / torture / smb2 / block.c
blobb9982b0574d675c5b44bbba1690bbf66056e2b04
1 /*
2 * Unix SMB/CIFS implementation.
4 * block SMB2 transports using iptables
6 * Copyright (C) Guenther Deschner, 2017
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
23 #include "libcli/smb2/smb2.h"
24 #include "torture/torture.h"
25 #include "torture/smb2/proto.h"
26 #include "system/network.h"
27 #include "lib/util/util_net.h"
28 #include "torture/smb2/block.h"
29 #include "libcli/smb/smbXcli_base.h"
30 #include "lib/util/tevent_ntstatus.h"
31 #include "oplock_break_handler.h"
32 #include "lease_break_handler.h"
35 * OUTPUT
36 * |
37 * -----> SMBTORTURE_OUTPUT
38 * |
39 * -----> SMBTORTURE_transportname1
40 * -----> SMBTORTURE_transportname2
44 static bool run_cmd(const char *cmd)
46 int ret;
48 DEBUG(10, ("%s will call '%s'\n", __location__, cmd));
50 ret = system(cmd);
51 if (ret) {
52 DEBUG(1, ("%s failed to execute system call: %s: %d\n",
53 __location__, cmd, ret));
54 return false;
57 return true;
60 static const char *iptables_command(struct torture_context *tctx)
62 return torture_setting_string(tctx, "iptables_command",
63 "/usr/sbin/iptables");
66 char *escape_shell_string(const char *src);
69 * iptables v1.6.1: chain name `SMBTORTURE_INPUT_tree1->session->transport'
70 * too long (must be under 29 chars)
72 * maybe truncate chainname ?
74 static const char *samba_chain_name(struct torture_context *tctx,
75 const char *name,
76 const char *prefix)
78 const char *s;
79 char *sm;
81 s = talloc_asprintf(tctx, "%s_%s", prefix, name);
82 if (s == NULL) {
83 return NULL;
86 sm = escape_shell_string(s);
87 if (sm == NULL) {
88 return NULL;
91 s = talloc_strdup(tctx, sm);
92 free(sm);
94 return s;
97 static bool iptables_setup_chain(struct torture_context *tctx,
98 const char *parent_chain,
99 const char *chain,
100 bool unblock)
102 const char *ipt = iptables_command(tctx);
103 const char *cmd;
105 if (unblock) {
106 cmd = talloc_asprintf(tctx,
107 "%s -L %s > /dev/null 2>&1 && "
109 "%s -F %s;"
110 "%s -D %s -j %s > /dev/null 2>&1 || true;"
111 "%s -X %s;"
112 ");"
113 "%s -L %s > /dev/null 2>&1 || true;",
114 ipt, chain,
115 ipt, chain,
116 ipt, parent_chain, chain,
117 ipt, chain,
118 ipt, chain);
119 } else {
120 cmd = talloc_asprintf(tctx,
121 "%s -L %s > /dev/null 2>&1 || "
123 "%s -N %s && "
124 "%s -I %s -j %s;"
125 ");"
126 "%s -F %s;",
127 ipt, chain,
128 ipt, chain,
129 ipt, parent_chain, chain,
130 ipt, chain);
133 if (cmd == NULL) {
134 return false;
137 if (!run_cmd(cmd)) {
138 return false;
141 return true;
144 uint16_t torture_get_local_port_from_transport(struct smb2_transport *t)
146 const struct sockaddr_storage *local_ss;
148 local_ss = smbXcli_conn_local_sockaddr(t->conn);
150 return get_sockaddr_port(local_ss);
153 static bool torture_block_tcp_output_port_internal(
154 struct torture_context *tctx,
155 const char *name,
156 uint16_t port,
157 bool unblock)
159 const char *ipt = iptables_command(tctx);
160 const char *chain_out = NULL;
161 char *cmd_out = NULL;
163 chain_out = samba_chain_name(tctx, name, "SMBTORTURE");
164 if (chain_out == NULL) {
165 return false;
168 torture_comment(tctx, "%sblocking %s dport %d\n",
169 unblock ? "un" : "", name, port);
171 if (!unblock) {
172 bool ok;
174 iptables_setup_chain(tctx,
175 "SMBTORTURE_OUTPUT",
176 chain_out,
177 true);
178 ok = iptables_setup_chain(tctx,
179 "SMBTORTURE_OUTPUT",
180 chain_out,
181 false);
182 if (!ok) {
183 return false;
187 cmd_out = talloc_asprintf(tctx,
188 "%s %s %s -p tcp --sport %d -j DROP",
189 ipt, unblock ? "-D" : "-I", chain_out, port);
190 if (cmd_out == NULL) {
191 return false;
194 if (!run_cmd(cmd_out)) {
195 return false;
198 if (unblock) {
199 bool ok;
201 ok = iptables_setup_chain(tctx,
202 "SMBTORTURE_OUTPUT",
203 chain_out,
204 true);
205 if (!ok) {
206 return false;
210 return true;
213 bool torture_block_tcp_output_port(struct torture_context *tctx,
214 const char *name,
215 uint16_t port)
217 return torture_block_tcp_output_port_internal(tctx, name, port, false);
220 bool torture_unblock_tcp_output_port(struct torture_context *tctx,
221 const char *name,
222 uint16_t port)
224 return torture_block_tcp_output_port_internal(tctx, name, port, true);
227 bool torture_block_tcp_output_setup(struct torture_context *tctx)
229 return iptables_setup_chain(tctx, "OUTPUT", "SMBTORTURE_OUTPUT", false);
232 bool torture_unblock_tcp_output_cleanup(struct torture_context *tctx)
234 return iptables_setup_chain(tctx, "OUTPUT", "SMBTORTURE_OUTPUT", true);
238 * Use iptables to block channels
240 static bool test_block_smb2_transport_iptables(struct torture_context *tctx,
241 struct smb2_transport *transport,
242 const char *name)
244 uint16_t local_port;
245 bool ret;
247 local_port = torture_get_local_port_from_transport(transport);
248 torture_comment(tctx, "transport[%s] uses tcp port: %d\n", name, local_port);
249 ret = torture_block_tcp_output_port(tctx, name, local_port);
250 torture_assert(tctx, ret, "we could not block tcp transport");
252 return ret;
255 static bool test_unblock_smb2_transport_iptables(struct torture_context *tctx,
256 struct smb2_transport *transport,
257 const char *name)
259 uint16_t local_port;
260 bool ret;
262 local_port = torture_get_local_port_from_transport(transport);
263 torture_comment(tctx, "transport[%s] uses tcp port: %d\n", name, local_port);
264 ret = torture_unblock_tcp_output_port(tctx, name, local_port);
265 torture_assert(tctx, ret, "we could not block tcp transport");
267 return ret;
270 static bool torture_blocked_lease_handler(struct smb2_transport *transport,
271 const struct smb2_lease_break *lb,
272 void *private_data)
274 struct smb2_transport *transport_copy =
275 talloc_get_type_abort(private_data,
276 struct smb2_transport);
277 bool lease_skip_ack = lease_break_info.lease_skip_ack;
278 bool ok;
280 lease_break_info.lease_skip_ack = true;
281 ok = transport_copy->lease.handler(transport,
283 transport_copy->lease.private_data);
284 lease_break_info.lease_skip_ack = lease_skip_ack;
286 if (!ok) {
287 return false;
290 if (lease_break_info.lease_skip_ack) {
291 return true;
294 if (lb->break_flags & SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) {
295 lease_break_info.failures++;
298 return true;
301 static bool torture_blocked_oplock_handler(struct smb2_transport *transport,
302 const struct smb2_handle *handle,
303 uint8_t level,
304 void *private_data)
306 struct smb2_transport *transport_copy =
307 talloc_get_type_abort(private_data,
308 struct smb2_transport);
309 bool oplock_skip_ack = break_info.oplock_skip_ack;
310 bool ok;
312 break_info.oplock_skip_ack = true;
313 ok = transport_copy->oplock.handler(transport,
314 handle,
315 level,
316 transport_copy->oplock.private_data);
317 break_info.oplock_skip_ack = oplock_skip_ack;
319 if (!ok) {
320 return false;
323 if (break_info.oplock_skip_ack) {
324 return true;
327 break_info.failures++;
328 break_info.failure_status = NT_STATUS_CONNECTION_DISCONNECTED;
330 return true;
333 static bool test_block_smb2_transport_fsctl_smbtorture(struct torture_context *tctx,
334 struct smb2_transport *transport,
335 const char *name)
337 struct smb2_transport *transport_copy = NULL;
338 DATA_BLOB in_input_buffer = data_blob_null;
339 DATA_BLOB in_output_buffer = data_blob_null;
340 DATA_BLOB out_input_buffer = data_blob_null;
341 DATA_BLOB out_output_buffer = data_blob_null;
342 struct tevent_req *req = NULL;
343 uint16_t local_port;
344 NTSTATUS status;
345 bool ok;
347 transport_copy = talloc_zero(transport, struct smb2_transport);
348 torture_assert(tctx, transport_copy, "talloc transport_copy");
349 transport_copy->lease = transport->lease;
350 transport_copy->oplock = transport->oplock;
352 local_port = torture_get_local_port_from_transport(transport);
353 torture_comment(tctx, "transport[%s] uses tcp port: %d\n", name, local_port);
354 req = smb2cli_ioctl_send(tctx,
355 tctx->ev,
356 transport->conn,
357 1000, /* timeout_msec */
358 NULL, /* session */
359 NULL, /* tcon */
360 UINT64_MAX, /* in_fid_persistent */
361 UINT64_MAX, /* in_fid_volatile */
362 FSCTL_SMBTORTURE_FORCE_UNACKED_TIMEOUT,
363 0, /* in_max_input_length */
364 &in_input_buffer,
365 0, /* in_max_output_length */
366 &in_output_buffer,
367 SMB2_IOCTL_FLAG_IS_FSCTL);
368 torture_assert(tctx, req != NULL, "smb2cli_ioctl_send() failed");
369 ok = tevent_req_poll_ntstatus(req, tctx->ev, &status);
370 if (ok) {
371 status = NT_STATUS_OK;
373 torture_assert_ntstatus_ok(tctx, status, "tevent_req_poll_ntstatus() failed");
374 status = smb2cli_ioctl_recv(req, tctx,
375 &out_input_buffer,
376 &out_output_buffer);
377 torture_assert_ntstatus_ok(tctx, status,
378 "FSCTL_SMBTORTURE_FORCE_UNACKED_TIMEOUT failed\n\n"
379 "On a Samba server 'smbd:FSCTL_SMBTORTURE = yes' is needed!\n\n"
380 "Otherwise you may need to use iptables like this:\n"
381 "--option='torture:use_iptables=yes'\n"
382 "And maybe something like this in addition:\n"
383 "--option='torture:iptables_command=sudo /sbin/iptables'\n\n");
384 TALLOC_FREE(req);
386 if (transport->lease.handler != NULL) {
387 transport->lease.handler = torture_blocked_lease_handler;
388 transport->lease.private_data = transport_copy;
390 if (transport->oplock.handler != NULL) {
391 transport->oplock.handler = torture_blocked_oplock_handler;
392 transport->oplock.private_data = transport_copy;
395 return true;
398 bool _test_block_smb2_transport(struct torture_context *tctx,
399 struct smb2_transport *transport,
400 const char *name)
402 bool use_iptables = torture_setting_bool(tctx,
403 "use_iptables", false);
405 if (use_iptables) {
406 return test_block_smb2_transport_iptables(tctx, transport, name);
407 } else {
408 return test_block_smb2_transport_fsctl_smbtorture(tctx, transport, name);
412 bool _test_unblock_smb2_transport(struct torture_context *tctx,
413 struct smb2_transport *transport,
414 const char *name)
416 bool use_iptables = torture_setting_bool(tctx,
417 "use_iptables", false);
419 if (use_iptables) {
420 return test_unblock_smb2_transport_iptables(tctx, transport, name);
421 } else {
422 return true;
426 bool test_setup_blocked_transports(struct torture_context *tctx)
428 bool use_iptables = torture_setting_bool(tctx,
429 "use_iptables", false);
431 if (use_iptables) {
432 return torture_block_tcp_output_setup(tctx);
435 return true;
438 void test_cleanup_blocked_transports(struct torture_context *tctx)
440 bool use_iptables = torture_setting_bool(tctx,
441 "use_iptables", false);
443 if (use_iptables) {
444 torture_unblock_tcp_output_cleanup(tctx);