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/>.
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"
37 * -----> SMBTORTURE_OUTPUT
39 * -----> SMBTORTURE_transportname1
40 * -----> SMBTORTURE_transportname2
44 static bool run_cmd(const char *cmd
)
48 DEBUG(10, ("%s will call '%s'\n", __location__
, cmd
));
52 DEBUG(1, ("%s failed to execute system call: %s: %d\n",
53 __location__
, cmd
, ret
));
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
,
81 s
= talloc_asprintf(tctx
, "%s_%s", prefix
, name
);
86 sm
= escape_shell_string(s
);
91 s
= talloc_strdup(tctx
, sm
);
97 static bool iptables_setup_chain(struct torture_context
*tctx
,
98 const char *parent_chain
,
102 const char *ipt
= iptables_command(tctx
);
106 cmd
= talloc_asprintf(tctx
,
107 "%s -L %s > /dev/null 2>&1 && "
110 "%s -D %s -j %s > /dev/null 2>&1 || true;"
113 "%s -L %s > /dev/null 2>&1 || true;",
116 ipt
, parent_chain
, chain
,
120 cmd
= talloc_asprintf(tctx
,
121 "%s -L %s > /dev/null 2>&1 || "
129 ipt
, parent_chain
, chain
,
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
,
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
) {
168 torture_comment(tctx
, "%sblocking %s dport %d\n",
169 unblock
? "un" : "", name
, port
);
174 iptables_setup_chain(tctx
,
178 ok
= iptables_setup_chain(tctx
,
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
) {
194 if (!run_cmd(cmd_out
)) {
201 ok
= iptables_setup_chain(tctx
,
213 bool torture_block_tcp_output_port(struct torture_context
*tctx
,
217 return torture_block_tcp_output_port_internal(tctx
, name
, port
, false);
220 bool torture_unblock_tcp_output_port(struct torture_context
*tctx
,
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
,
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");
255 static bool test_unblock_smb2_transport_iptables(struct torture_context
*tctx
,
256 struct smb2_transport
*transport
,
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");
270 static bool torture_blocked_lease_handler(struct smb2_transport
*transport
,
271 const struct smb2_lease_break
*lb
,
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
;
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
;
290 if (lease_break_info
.lease_skip_ack
) {
294 if (lb
->break_flags
& SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED
) {
295 lease_break_info
.failures
++;
301 static bool torture_blocked_oplock_handler(struct smb2_transport
*transport
,
302 const struct smb2_handle
*handle
,
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
;
312 break_info
.oplock_skip_ack
= true;
313 ok
= transport_copy
->oplock
.handler(transport
,
316 transport_copy
->oplock
.private_data
);
317 break_info
.oplock_skip_ack
= oplock_skip_ack
;
323 if (break_info
.oplock_skip_ack
) {
327 break_info
.failures
++;
328 break_info
.failure_status
= NT_STATUS_CONNECTION_DISCONNECTED
;
333 static bool test_block_smb2_transport_fsctl_smbtorture(struct torture_context
*tctx
,
334 struct smb2_transport
*transport
,
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
;
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
,
357 1000, /* timeout_msec */
360 UINT64_MAX
, /* in_fid_persistent */
361 UINT64_MAX
, /* in_fid_volatile */
362 FSCTL_SMBTORTURE_FORCE_UNACKED_TIMEOUT
,
363 0, /* in_max_input_length */
365 0, /* in_max_output_length */
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
);
371 status
= NT_STATUS_OK
;
373 torture_assert_ntstatus_ok(tctx
, status
, "tevent_req_poll_ntstatus() failed");
374 status
= smb2cli_ioctl_recv(req
, tctx
,
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");
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
;
398 bool _test_block_smb2_transport(struct torture_context
*tctx
,
399 struct smb2_transport
*transport
,
402 bool use_iptables
= torture_setting_bool(tctx
,
403 "use_iptables", false);
406 return test_block_smb2_transport_iptables(tctx
, transport
, name
);
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
,
416 bool use_iptables
= torture_setting_bool(tctx
,
417 "use_iptables", false);
420 return test_unblock_smb2_transport_iptables(tctx
, transport
, name
);
426 bool test_setup_blocked_transports(struct torture_context
*tctx
)
428 bool use_iptables
= torture_setting_bool(tctx
,
429 "use_iptables", false);
432 return torture_block_tcp_output_setup(tctx
);
438 void test_cleanup_blocked_transports(struct torture_context
*tctx
)
440 bool use_iptables
= torture_setting_bool(tctx
,
441 "use_iptables", false);
444 torture_unblock_tcp_output_cleanup(tctx
);