s4:torture/smb2: make use of FSCTL_SMBTORTURE_FORCE_UNACKED_TIMEOUT
authorStefan Metzmacher <metze@samba.org>
Mon, 8 Jun 2020 14:33:45 +0000 (16:33 +0200)
committerStefan Metzmacher <metze@samba.org>
Wed, 8 Jul 2020 15:54:39 +0000 (15:54 +0000)
This is a way to test without being able to use iptables.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=11897

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Günther Deschner <gd@samba.org>
source4/torture/smb2/multichannel.c

index 7cf39dadbe32b1289c6b2dae1a85f854b92a8ef7..3742cb8bac8717412207614879e486ffb172643c 100644 (file)
@@ -36,6 +36,7 @@
 #include "oplock_break_handler.h"
 #include "lease_break_handler.h"
 #include "torture/smb2/block.h"
+#include "lib/util/tevent_ntstatus.h"
 
 #define BASEDIR "multichanneltestdir"
 
@@ -366,28 +367,6 @@ static void test_multichannel_init_smb_create(struct smb2_create *io)
        /* windows 2016 returns 300000 0x493E0 */
 }
 
-/*
- * We simulate blocking incoming oplock break requests by simply ignoring
- * the incoming break requests.
- */
-static bool test_set_ignore_break_handler(struct torture_context *tctx,
-                                         struct smb2_transport *transport)
-{
-       transport->oplock.handler = torture_oplock_ignore_handler;
-       transport->lease.handler = torture_lease_ignore_handler;
-
-       return true;
-}
-
-static bool test_reset_break_handler(struct torture_context *tctx,
-                                    struct smb2_transport *transport)
-{
-       transport->oplock.handler = torture_oplock_ack_handler;
-       transport->lease.handler = torture_lease_handler;
-
-       return true;
-}
-
 /*
  * Use iptables to block channels
  */
@@ -421,6 +400,134 @@ static bool test_iptables_unblock_channel(struct torture_context *tctx,
        return ret;
 }
 
+static bool torture_blocked_lease_handler(struct smb2_transport *transport,
+                                         const struct smb2_lease_break *lb,
+                                         void *private_data)
+{
+       struct smb2_transport *transport_copy =
+               talloc_get_type_abort(private_data,
+               struct smb2_transport);
+       bool lease_skip_ack = lease_break_info.lease_skip_ack;
+       bool ok;
+
+       lease_break_info.lease_skip_ack = true;
+       ok = transport_copy->lease.handler(transport,
+                                          lb,
+                                          transport_copy->lease.private_data);
+       lease_break_info.lease_skip_ack = lease_skip_ack;
+
+       if (!ok) {
+               return false;
+       }
+
+       if (lease_break_info.lease_skip_ack) {
+               return true;
+       }
+
+       if (lb->break_flags & SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) {
+               lease_break_info.failures++;
+       }
+
+       return true;
+}
+
+static bool torture_blocked_oplock_handler(struct smb2_transport *transport,
+                                          const struct smb2_handle *handle,
+                                          uint8_t level,
+                                          void *private_data)
+{
+       struct smb2_transport *transport_copy =
+               talloc_get_type_abort(private_data,
+               struct smb2_transport);
+       bool oplock_skip_ack = break_info.oplock_skip_ack;
+       bool ok;
+
+       break_info.oplock_skip_ack = true;
+       ok = transport_copy->oplock.handler(transport,
+                                           handle,
+                                           level,
+                                           transport_copy->oplock.private_data);
+       break_info.oplock_skip_ack = oplock_skip_ack;
+
+       if (!ok) {
+               return false;
+       }
+
+       if (break_info.oplock_skip_ack) {
+               return true;
+       }
+
+       break_info.failures++;
+       break_info.failure_status = NT_STATUS_CONNECTION_DISCONNECTED;
+
+       return true;
+}
+
+static bool test_block_smb2_transport_fsctl_smbtorture(struct torture_context *tctx,
+                                                      struct smb2_transport *transport,
+                                                      const char *name)
+{
+       struct smb2_transport *transport_copy = NULL;
+       DATA_BLOB in_input_buffer = data_blob_null;
+       DATA_BLOB in_output_buffer = data_blob_null;
+       DATA_BLOB out_input_buffer = data_blob_null;
+       DATA_BLOB out_output_buffer = data_blob_null;
+       struct tevent_req *req = NULL;
+       uint16_t local_port;
+       NTSTATUS status;
+       bool ok;
+
+       transport_copy = talloc_zero(transport, struct smb2_transport);
+       torture_assert(tctx, transport_copy, "talloc transport_copy");
+       transport_copy->lease = transport->lease;
+       transport_copy->oplock = transport->oplock;
+
+       local_port = torture_get_local_port_from_transport(transport);
+       torture_comment(tctx, "transport[%s] uses tcp port: %d\n", name, local_port);
+       req = smb2cli_ioctl_send(tctx,
+                                tctx->ev,
+                                transport->conn,
+                                1000, /* timeout_msec */
+                                NULL, /* session */
+                                NULL, /* tcon */
+                                UINT64_MAX, /* in_fid_persistent */
+                                UINT64_MAX, /* in_fid_volatile */
+                                FSCTL_SMBTORTURE_FORCE_UNACKED_TIMEOUT,
+                                0, /* in_max_input_length */
+                                &in_input_buffer,
+                                0, /* in_max_output_length */
+                                &in_output_buffer,
+                                SMB2_IOCTL_FLAG_IS_FSCTL);
+       torture_assert(tctx, req != NULL, "smb2cli_ioctl_send() failed");
+       ok = tevent_req_poll_ntstatus(req, tctx->ev, &status);
+       if (ok) {
+               status = NT_STATUS_OK;
+       }
+       torture_assert_ntstatus_ok(tctx, status, "tevent_req_poll_ntstatus() failed");
+       status = smb2cli_ioctl_recv(req, tctx,
+                                   &out_input_buffer,
+                                   &out_output_buffer);
+       torture_assert_ntstatus_ok(tctx, status,
+               "FSCTL_SMBTORTURE_FORCE_UNACKED_TIMEOUT failed\n\n"
+               "On a Samba server 'smbd:FSCTL_SMBTORTURE = yes' is needed!\n\n"
+               "Otherwise you may need to use iptables like this:\n"
+               "--option='torture:use_iptables=yes'\n"
+               "And maybe something like this in addition:\n"
+               "--option='torture:iptables_command=sudo /sbin/iptables'\n\n");
+       TALLOC_FREE(req);
+
+       if (transport->lease.handler != NULL) {
+               transport->lease.handler = torture_blocked_lease_handler;
+               transport->lease.private_data = transport_copy;
+       }
+       if (transport->oplock.handler != NULL) {
+               transport->oplock.handler = torture_blocked_oplock_handler;
+               transport->oplock.private_data = transport_copy;
+       }
+
+       return true;
+}
+
 #define test_block_channel(_tctx, _t) _test_block_channel(_tctx, _t, #_t)
 static bool _test_block_channel(struct torture_context *tctx,
                                          struct smb2_transport *transport,
@@ -432,7 +539,7 @@ static bool _test_block_channel(struct torture_context *tctx,
        if (use_iptables) {
                return test_iptables_block_channel(tctx, transport, name);
        } else {
-               return test_set_ignore_break_handler(tctx, transport);
+               return test_block_smb2_transport_fsctl_smbtorture(tctx, transport, name);
        }
 }
 
@@ -447,7 +554,7 @@ static bool _test_unblock_channel(struct torture_context *tctx,
        if (use_iptables) {
                return test_iptables_unblock_channel(tctx, transport, name);
        } else {
-               return test_reset_break_handler(tctx, transport);
+               return true;
        }
 }