LCOV - code coverage report
Current view: top level - source4/torture/smb2 - block.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 72 168 42.9 %
Date: 2024-04-21 15:09:00 Functions: 8 19 42.1 %

          Line data    Source code
       1             : /*
       2             :  * Unix SMB/CIFS implementation.
       3             :  *
       4             :  * block SMB2 transports using iptables
       5             :  *
       6             :  * Copyright (C) Guenther Deschner, 2017
       7             :  *
       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.
      12             :  *
      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.
      17             :  *
      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/>.
      20             :  */
      21             : 
      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"
      33             : 
      34             : /*
      35             :  * OUTPUT
      36             :  *  |
      37             :  *  -----> SMBTORTURE_OUTPUT
      38             :  *             |
      39             :  *             -----> SMBTORTURE_transportname1
      40             :  *             -----> SMBTORTURE_transportname2
      41             :  */
      42             : 
      43             : 
      44           0 : static bool run_cmd(const char *cmd)
      45             : {
      46           0 :         int ret;
      47             : 
      48           0 :         DEBUG(10, ("%s will call '%s'\n", __location__, cmd));
      49             : 
      50           0 :         ret = system(cmd);
      51           0 :         if (ret) {
      52           0 :                 DEBUG(1, ("%s failed to execute system call: %s: %d\n",
      53             :                         __location__, cmd, ret));
      54           0 :                 return false;
      55             :         }
      56             : 
      57           0 :         return true;
      58             : }
      59             : 
      60           0 : static const char *iptables_command(struct torture_context *tctx)
      61             : {
      62           0 :         return torture_setting_string(tctx, "iptables_command",
      63             :                                       "/usr/sbin/iptables");
      64             : }
      65             : 
      66             : char *escape_shell_string(const char *src);
      67             : 
      68             : /*
      69             :  * iptables v1.6.1: chain name `SMBTORTURE_INPUT_tree1->session->transport'
      70             :  * too long (must be under 29 chars)
      71             :  *
      72             :  * maybe truncate chainname ?
      73             :  */
      74           0 : static const char *samba_chain_name(struct torture_context *tctx,
      75             :                                     const char *name,
      76             :                                     const char *prefix)
      77             : {
      78           0 :         const char *s;
      79           0 :         char *sm;
      80             : 
      81           0 :         s = talloc_asprintf(tctx, "%s_%s", prefix, name);
      82           0 :         if (s == NULL) {
      83           0 :                 return NULL;
      84             :         }
      85             : 
      86           0 :         sm = escape_shell_string(s);
      87           0 :         if (sm == NULL) {
      88           0 :                 return NULL;
      89             :         }
      90             : 
      91           0 :         s = talloc_strdup(tctx, sm);
      92           0 :         free(sm);
      93             : 
      94           0 :         return s;
      95             : }
      96             : 
      97           0 : static bool iptables_setup_chain(struct torture_context *tctx,
      98             :                                  const char *parent_chain,
      99             :                                  const char *chain,
     100             :                                  bool unblock)
     101             : {
     102           0 :         const char *ipt = iptables_command(tctx);
     103           0 :         const char *cmd;
     104             : 
     105           0 :         if (unblock) {
     106           0 :                 cmd = talloc_asprintf(tctx,
     107             :                                 "%s -L %s > /dev/null 2>&1 && "
     108             :                                 "("
     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           0 :                 cmd = talloc_asprintf(tctx,
     121             :                                 "%s -L %s > /dev/null 2>&1 || "
     122             :                                 "("
     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);
     131             :         }
     132             : 
     133           0 :         if (cmd == NULL) {
     134           0 :                 return false;
     135             :         }
     136             : 
     137           0 :         if (!run_cmd(cmd)) {
     138           0 :                 return false;
     139             :         }
     140             : 
     141           0 :         return true;
     142             : }
     143             : 
     144         857 : uint16_t torture_get_local_port_from_transport(struct smb2_transport *t)
     145             : {
     146           0 :         const struct sockaddr_storage *local_ss;
     147             : 
     148         857 :         local_ss = smbXcli_conn_local_sockaddr(t->conn);
     149             : 
     150         857 :         return get_sockaddr_port(local_ss);
     151             : }
     152             : 
     153           0 : static bool torture_block_tcp_output_port_internal(
     154             :                                                 struct torture_context *tctx,
     155             :                                                 const char *name,
     156             :                                                 uint16_t port,
     157             :                                                 bool unblock)
     158             : {
     159           0 :         const char *ipt = iptables_command(tctx);
     160           0 :         const char *chain_out = NULL;
     161           0 :         char *cmd_out = NULL;
     162             : 
     163           0 :         chain_out = samba_chain_name(tctx, name, "SMBTORTURE");
     164           0 :         if (chain_out == NULL) {
     165           0 :                 return false;
     166             :         }
     167             : 
     168           0 :         torture_comment(tctx, "%sblocking %s dport %d\n",
     169             :                         unblock ? "un" : "", name, port);
     170             : 
     171           0 :         if (!unblock) {
     172           0 :                 bool ok;
     173             : 
     174           0 :                 iptables_setup_chain(tctx,
     175             :                                      "SMBTORTURE_OUTPUT",
     176             :                                      chain_out,
     177             :                                      true);
     178           0 :                 ok = iptables_setup_chain(tctx,
     179             :                                           "SMBTORTURE_OUTPUT",
     180             :                                           chain_out,
     181             :                                           false);
     182           0 :                 if (!ok) {
     183           0 :                         return false;
     184             :                 }
     185             :         }
     186             : 
     187           0 :         cmd_out = talloc_asprintf(tctx,
     188             :                                   "%s %s %s -p tcp --sport %d -j DROP",
     189             :                                   ipt, unblock ? "-D" : "-I", chain_out, port);
     190           0 :         if (cmd_out == NULL) {
     191           0 :                 return false;
     192             :         }
     193             : 
     194           0 :         if (!run_cmd(cmd_out)) {
     195           0 :                 return false;
     196             :         }
     197             : 
     198           0 :         if (unblock) {
     199           0 :                 bool ok;
     200             : 
     201           0 :                 ok = iptables_setup_chain(tctx,
     202             :                                           "SMBTORTURE_OUTPUT",
     203             :                                           chain_out,
     204             :                                           true);
     205           0 :                 if (!ok) {
     206           0 :                         return false;
     207             :                 }
     208             :         }
     209             : 
     210           0 :         return true;
     211             : }
     212             : 
     213           0 : bool torture_block_tcp_output_port(struct torture_context *tctx,
     214             :                                    const char *name,
     215             :                                    uint16_t port)
     216             : {
     217           0 :         return torture_block_tcp_output_port_internal(tctx, name, port, false);
     218             : }
     219             : 
     220           0 : bool torture_unblock_tcp_output_port(struct torture_context *tctx,
     221             :                                      const char *name,
     222             :                                      uint16_t port)
     223             : {
     224           0 :         return torture_block_tcp_output_port_internal(tctx, name, port, true);
     225             : }
     226             : 
     227           0 : bool torture_block_tcp_output_setup(struct torture_context *tctx)
     228             : {
     229           0 :         return iptables_setup_chain(tctx, "OUTPUT", "SMBTORTURE_OUTPUT", false);
     230             : }
     231             : 
     232           0 : bool torture_unblock_tcp_output_cleanup(struct torture_context *tctx)
     233             : {
     234           0 :         return iptables_setup_chain(tctx, "OUTPUT", "SMBTORTURE_OUTPUT", true);
     235             : }
     236             : 
     237             : /*
     238             :  * Use iptables to block channels
     239             :  */
     240           0 : static bool test_block_smb2_transport_iptables(struct torture_context *tctx,
     241             :                                                struct smb2_transport *transport,
     242             :                                                const char *name)
     243             : {
     244           0 :         uint16_t local_port;
     245           0 :         bool ret;
     246             : 
     247           0 :         local_port = torture_get_local_port_from_transport(transport);
     248           0 :         torture_comment(tctx, "transport[%s] uses tcp port: %d\n", name, local_port);
     249           0 :         ret = torture_block_tcp_output_port(tctx, name, local_port);
     250           0 :         torture_assert(tctx, ret, "we could not block tcp transport");
     251             : 
     252           0 :         return ret;
     253             : }
     254             : 
     255           0 : static bool test_unblock_smb2_transport_iptables(struct torture_context *tctx,
     256             :                                                  struct smb2_transport *transport,
     257             :                                                  const char *name)
     258             : {
     259           0 :         uint16_t local_port;
     260           0 :         bool ret;
     261             : 
     262           0 :         local_port = torture_get_local_port_from_transport(transport);
     263           0 :         torture_comment(tctx, "transport[%s] uses tcp port: %d\n", name, local_port);
     264           0 :         ret = torture_unblock_tcp_output_port(tctx, name, local_port);
     265           0 :         torture_assert(tctx, ret, "we could not block tcp transport");
     266             : 
     267           0 :         return ret;
     268             : }
     269             : 
     270          66 : static bool torture_blocked_lease_handler(struct smb2_transport *transport,
     271             :                                           const struct smb2_lease_break *lb,
     272             :                                           void *private_data)
     273             : {
     274           0 :         struct smb2_transport *transport_copy =
     275          66 :                 talloc_get_type_abort(private_data,
     276             :                 struct smb2_transport);
     277          66 :         bool lease_skip_ack = lease_break_info.lease_skip_ack;
     278           0 :         bool ok;
     279             : 
     280          66 :         lease_break_info.lease_skip_ack = true;
     281          66 :         ok = transport_copy->lease.handler(transport,
     282             :                                            lb,
     283             :                                            transport_copy->lease.private_data);
     284          66 :         lease_break_info.lease_skip_ack = lease_skip_ack;
     285             : 
     286          66 :         if (!ok) {
     287           0 :                 return false;
     288             :         }
     289             : 
     290          66 :         if (lease_break_info.lease_skip_ack) {
     291          64 :                 return true;
     292             :         }
     293             : 
     294           2 :         if (lb->break_flags & SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) {
     295           2 :                 lease_break_info.failures++;
     296             :         }
     297             : 
     298           2 :         return true;
     299             : }
     300             : 
     301         134 : static bool torture_blocked_oplock_handler(struct smb2_transport *transport,
     302             :                                            const struct smb2_handle *handle,
     303             :                                            uint8_t level,
     304             :                                            void *private_data)
     305             : {
     306           0 :         struct smb2_transport *transport_copy =
     307         134 :                 talloc_get_type_abort(private_data,
     308             :                 struct smb2_transport);
     309         134 :         bool oplock_skip_ack = break_info.oplock_skip_ack;
     310           0 :         bool ok;
     311             : 
     312         134 :         break_info.oplock_skip_ack = true;
     313         134 :         ok = transport_copy->oplock.handler(transport,
     314             :                                             handle,
     315             :                                             level,
     316             :                                             transport_copy->oplock.private_data);
     317         134 :         break_info.oplock_skip_ack = oplock_skip_ack;
     318             : 
     319         134 :         if (!ok) {
     320           0 :                 return false;
     321             :         }
     322             : 
     323         134 :         if (break_info.oplock_skip_ack) {
     324         128 :                 return true;
     325             :         }
     326             : 
     327           6 :         break_info.failures++;
     328           6 :         break_info.failure_status = NT_STATUS_CONNECTION_DISCONNECTED;
     329             : 
     330           6 :         return true;
     331             : }
     332             : 
     333         393 : static bool test_block_smb2_transport_fsctl_smbtorture(struct torture_context *tctx,
     334             :                                                        struct smb2_transport *transport,
     335             :                                                        const char *name)
     336             : {
     337         393 :         struct smb2_transport *transport_copy = NULL;
     338         393 :         DATA_BLOB in_input_buffer = data_blob_null;
     339         393 :         DATA_BLOB in_output_buffer = data_blob_null;
     340         393 :         DATA_BLOB out_input_buffer = data_blob_null;
     341         393 :         DATA_BLOB out_output_buffer = data_blob_null;
     342         393 :         struct tevent_req *req = NULL;
     343           0 :         uint16_t local_port;
     344           0 :         NTSTATUS status;
     345           0 :         bool ok;
     346             : 
     347         393 :         transport_copy = talloc_zero(transport, struct smb2_transport);
     348         393 :         torture_assert(tctx, transport_copy, "talloc transport_copy");
     349         393 :         transport_copy->lease = transport->lease;
     350         393 :         transport_copy->oplock = transport->oplock;
     351             : 
     352         393 :         local_port = torture_get_local_port_from_transport(transport);
     353         393 :         torture_comment(tctx, "transport[%s] uses tcp port: %d\n", name, local_port);
     354         393 :         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         393 :         torture_assert(tctx, req != NULL, "smb2cli_ioctl_send() failed");
     369         393 :         ok = tevent_req_poll_ntstatus(req, tctx->ev, &status);
     370         393 :         if (ok) {
     371         393 :                 status = NT_STATUS_OK;
     372             :         }
     373         393 :         torture_assert_ntstatus_ok(tctx, status, "tevent_req_poll_ntstatus() failed");
     374         393 :         status = smb2cli_ioctl_recv(req, tctx,
     375             :                                     &out_input_buffer,
     376             :                                     &out_output_buffer);
     377         393 :         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         392 :         TALLOC_FREE(req);
     385             : 
     386         392 :         if (transport->lease.handler != NULL) {
     387         390 :                 transport->lease.handler = torture_blocked_lease_handler;
     388         390 :                 transport->lease.private_data = transport_copy;
     389             :         }
     390         392 :         if (transport->oplock.handler != NULL) {
     391         392 :                 transport->oplock.handler = torture_blocked_oplock_handler;
     392         392 :                 transport->oplock.private_data = transport_copy;
     393             :         }
     394             : 
     395         392 :         return true;
     396             : }
     397             : 
     398         393 : bool _test_block_smb2_transport(struct torture_context *tctx,
     399             :                                 struct smb2_transport *transport,
     400             :                                 const char *name)
     401             : {
     402         393 :         bool use_iptables = torture_setting_bool(tctx,
     403             :                                         "use_iptables", false);
     404             : 
     405         393 :         if (use_iptables) {
     406           0 :                 return test_block_smb2_transport_iptables(tctx, transport, name);
     407             :         } else {
     408         393 :                 return test_block_smb2_transport_fsctl_smbtorture(tctx, transport, name);
     409             :         }
     410             : }
     411             : 
     412         392 : bool _test_unblock_smb2_transport(struct torture_context *tctx,
     413             :                                   struct smb2_transport *transport,
     414             :                                   const char *name)
     415             : {
     416         392 :         bool use_iptables = torture_setting_bool(tctx,
     417             :                                         "use_iptables", false);
     418             : 
     419         392 :         if (use_iptables) {
     420           0 :                 return test_unblock_smb2_transport_iptables(tctx, transport, name);
     421             :         } else {
     422         392 :                 return true;
     423             :         }
     424             : }
     425             : 
     426          51 : bool test_setup_blocked_transports(struct torture_context *tctx)
     427             : {
     428          51 :         bool use_iptables = torture_setting_bool(tctx,
     429             :                                         "use_iptables", false);
     430             : 
     431          51 :         if (use_iptables) {
     432           0 :                 return torture_block_tcp_output_setup(tctx);
     433             :         }
     434             : 
     435          51 :         return true;
     436             : }
     437             : 
     438          50 : void test_cleanup_blocked_transports(struct torture_context *tctx)
     439             : {
     440          50 :         bool use_iptables = torture_setting_bool(tctx,
     441             :                                         "use_iptables", false);
     442             : 
     443          50 :         if (use_iptables) {
     444           0 :                 torture_unblock_tcp_output_cleanup(tctx);
     445             :         }
     446          50 : }

Generated by: LCOV version 1.14