1
0
mirror of https://github.com/samba-team/samba.git synced 2025-02-28 01:58:17 +03:00

s4:torture/smb2: refactor block.c to block the OUTPUT path

In order to create useful tests, we should block the outgoing
tcp packets only. That means we're able to see incoming
break notifications, but prevent outgoing TCP ACKs to be delivered
to the server.

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>
This commit is contained in:
Stefan Metzmacher 2020-05-29 09:18:12 -07:00
parent a52e7114df
commit 8c7bb245ff
3 changed files with 116 additions and 246 deletions

View File

@ -29,12 +29,12 @@
#include "libcli/smb/smbXcli_base.h"
/*
* INPUT
* OUTPUT
* |
* -----> SAMBA_INPUT
* -----> SMBTORTURE_OUTPUT
* |
* -----> SAMBA_INPUT_transportname1
* -----> SAMBA_INPUT_transportname2
* -----> SMBTORTURE_transportname1
* -----> SMBTORTURE_transportname2
*/
@ -54,38 +54,6 @@ static bool run_cmd(const char *cmd)
return true;
}
int smbrun(const char *cmd, int *outfd, char * const *env);
static bool run_cmd_return_buf(TALLOC_CTX *mem_ctx,
const char *cmd,
int *num_lines, char ***buf)
{
int ret;
int fd = -1;
DEBUG(10, ("%s will call '%s'\n", __location__, cmd));
ret = smbrun(cmd, &fd, NULL);
if (ret) {
DEBUG(1, ("%s failed to execute system call: %s: %d\n",
__location__, cmd, ret));
if (fd != -1) {
close(fd);
}
return false;
}
*buf = fd_lines_load(fd, num_lines, 0, mem_ctx);
if (fd != -1) {
close(fd);
}
if (*buf == NULL) {
return false;
}
return true;
}
static const char *iptables_command(struct torture_context *tctx)
{
return torture_setting_string(tctx, "iptables_command",
@ -95,7 +63,7 @@ static const char *iptables_command(struct torture_context *tctx)
char *escape_shell_string(const char *src);
/*
* iptables v1.6.1: chain name `SAMBA_INPUT_tree1->session->transport'
* iptables v1.6.1: chain name `SMBTORTURE_INPUT_tree1->session->transport'
* too long (must be under 29 chars)
*
* maybe truncate chainname ?
@ -123,170 +91,49 @@ static const char *samba_chain_name(struct torture_context *tctx,
return s;
}
static bool filter_tcp_setup(struct torture_context *tctx,
bool unblock)
static bool iptables_setup_chain(struct torture_context *tctx,
const char *parent_chain,
const char *chain,
bool unblock)
{
const char *cmd_in, *cmd_out;
const char *ipt = iptables_command(tctx);
const char *cmd;
if (unblock) {
cmd_in = talloc_asprintf(tctx,
"%s -L SAMBA_INPUT > /dev/null 2>&1 && "
cmd = talloc_asprintf(tctx,
"%s -L %s > /dev/null 2>&1 && "
"("
"%s -F SAMBA_INPUT; "
"%s -D INPUT -j SAMBA_INPUT; "
"%s -X SAMBA_INPUT;"
")",
ipt, ipt, ipt, ipt);
cmd_out = talloc_asprintf(tctx,
"%s -L SAMBA_OUTPUT > /dev/null 2>&1 && "
"("
"%s -F SAMBA_OUTPUT;"
"%s -D OUTPUT -j SAMBA_OUTPUT;"
"%s -X SAMBA_OUTPUT;"
")",
ipt, ipt, ipt, ipt);
"%s -F %s;"
"%s -D %s -j %s > /dev/null 2>&1 || true;"
"%s -X %s;"
");"
"%s -L %s > /dev/null 2>&1 || true;",
ipt, chain,
ipt, chain,
ipt, parent_chain, chain,
ipt, chain,
ipt, chain);
} else {
cmd_in = talloc_asprintf(tctx,
"%s -L SAMBA_INPUT > /dev/null 2>&1 || "
cmd = talloc_asprintf(tctx,
"%s -L %s > /dev/null 2>&1 || "
"("
"%s -N SAMBA_INPUT && "
"%s -I INPUT -j SAMBA_INPUT "
")",
ipt, ipt, ipt);
cmd_out = talloc_asprintf(tctx,
"%s -L SAMBA_OUTPUT > /dev/null 2>&1 || "
"("
"%s -N SAMBA_OUTPUT && "
"%s -I OUTPUT -j SAMBA_OUTPUT;"
")",
ipt, ipt, ipt);
"%s -N %s && "
"%s -I %s -j %s;"
");"
"%s -F %s;",
ipt, chain,
ipt, chain,
ipt, parent_chain, chain,
ipt, chain);
}
if (cmd_in == NULL || cmd_out == NULL) {
return false;
}
if (!run_cmd(cmd_in)) {
return false;
}
/* if (!run_cmd(cmd_out)) { return false; } */
return true;
}
static bool filter_tcp_setup_name(struct torture_context *tctx,
const char *name, bool unblock)
{
const char *cmd_in, *cmd_out;
const char *chain_in, *chain_out;
const char *ipt = iptables_command(tctx);
chain_in = samba_chain_name(tctx, name, "SAMBA_INPUT");
chain_out = samba_chain_name(tctx, name, "SAMBA_OUTPUT");
if (chain_in == NULL || chain_out == NULL) {
return false;
}
if (unblock) {
cmd_in = talloc_asprintf(tctx, "%s -F %s; "
"%s -D SAMBA_INPUT -j %s; "
"%s -X %s",
ipt, chain_in,
ipt, chain_in,
ipt, chain_in);
cmd_out = talloc_asprintf(tctx, "%s -F %s; "
"%s -D SAMBA_OUTPUT -j %s; "
"%s -X %s",
ipt, chain_out,
ipt, chain_out,
ipt, chain_out);
} else {
cmd_in = talloc_asprintf(tctx, "%s -L %s > /dev/null 2>&1 || "
"%s -N %s && "
"%s -I SAMBA_INPUT -j %s",
ipt, chain_in,
ipt, chain_in,
ipt, chain_in);
cmd_out = talloc_asprintf(tctx, "%s -L %s > /dev/null 2>&1 || "
"%s -N %s && "
"%s -I SAMBA_OUTPUT -j %s",
ipt, chain_out,
ipt, chain_out,
ipt, chain_out);
}
if (cmd_in == NULL || cmd_out == NULL) {
return false;
}
if (!run_cmd(cmd_in)) {
return false;
}
/* if (!run_cmd(cmd_out)) return false; */
return true;
}
/* '11 452 DROP tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:43062' */
static bool get_packet_count(const char *s, uint32_t *count)
{
int i = 0;
char *p;
if (s == NULL) {
return false;
}
while (s[i] == ' ') {
s++;
}
p = strchr(s, ' ');
if (p == NULL) {
return false;
}
*p = '\0';
*count = atoi(s);
return true;
}
bool torture_list_tcp_transport_name(struct torture_context *tctx,
const char *name,
uint32_t *_packets)
{
const char *chain_in, *cmd;
int num_lines;
char **buf;
uint32_t packets = 0;
const char *ipt = iptables_command(tctx);
chain_in = samba_chain_name(tctx, name, "SAMBA_INPUT");
if (chain_in == NULL) {
return false;
}
cmd = talloc_asprintf(tctx, "%s -L %s -v -n", ipt, chain_in);
if (cmd == NULL) {
return false;
}
if (!run_cmd_return_buf(tctx, cmd, &num_lines, &buf)) {
if (!run_cmd(cmd)) {
return false;
}
SMB_ASSERT(num_lines >= 3);
if (!get_packet_count(buf[2], &packets)) {
return false;
}
torture_comment(tctx, "chain: '%s', packets: %d\n", name, (int)packets);
if (_packets != NULL) {
*_packets = packets;
}
return true;
}
@ -300,71 +147,86 @@ uint16_t torture_get_local_port_from_transport(struct smb2_transport *t)
return get_sockaddr_port(local_ss);
}
static bool torture_block_tcp_transport_name_internal(
static bool torture_block_tcp_output_port_internal(
struct torture_context *tctx,
struct smb2_transport *t,
const char *name,
uint16_t port,
bool unblock)
{
char *cmd_in;
char *cmd_out;
const char *chain_in, *chain_out;
uint16_t port = torture_get_local_port_from_transport(t);
const char *ipt = iptables_command(tctx);
const char *chain_out = NULL;
char *cmd_out = NULL;
chain_in = samba_chain_name(tctx, name, "SAMBA_INPUT");
chain_out = samba_chain_name(tctx, name, "SAMBA_OUTPUT");
if (chain_in == NULL || chain_out == NULL) {
chain_out = samba_chain_name(tctx, name, "SMBTORTURE");
if (chain_out == NULL) {
return false;
}
if (!unblock) {
filter_tcp_setup(tctx, false);
filter_tcp_setup_name(tctx, name, false);
}
torture_comment(tctx, "%sblocking %s dport %d\n",
unblock ? "un" : "", name, port);
cmd_in = talloc_asprintf(tctx,
"%s %s %s -p tcp --dport %d -j DROP",
ipt, unblock ? "-D" : "-I", chain_in, port);
if (!unblock) {
bool ok;
iptables_setup_chain(tctx,
"SMBTORTURE_OUTPUT",
chain_out,
true);
ok = iptables_setup_chain(tctx,
"SMBTORTURE_OUTPUT",
chain_out,
false);
if (!ok) {
return false;
}
}
cmd_out = talloc_asprintf(tctx,
"%s %s %s -p tcp --sport %d -j DROP",
ipt, unblock ? "-D" : "-I", chain_out, port);
if (cmd_in == NULL || cmd_out == NULL) {
if (cmd_out == NULL) {
return false;
}
if (!run_cmd(cmd_in)) {
if (!run_cmd(cmd_out)) {
return false;
}
/* if (!run_cmd(cmd_out)) return false; */
if (unblock) {
filter_tcp_setup_name(tctx, name, true);
/* better don't cleanup here */
/* filter_tcp_setup(tctx, true); */
bool ok;
ok = iptables_setup_chain(tctx,
"SMBTORTURE_OUTPUT",
chain_out,
true);
if (!ok) {
return false;
}
}
return true;
}
bool torture_block_tcp_transport_name(struct torture_context *tctx,
struct smb2_transport *t,
const char *name)
bool torture_block_tcp_output_port(struct torture_context *tctx,
const char *name,
uint16_t port)
{
return torture_block_tcp_transport_name_internal(tctx, t, name, false);
return torture_block_tcp_output_port_internal(tctx, name, port, false);
}
bool torture_unblock_tcp_transport_name(struct torture_context *tctx,
struct smb2_transport *t,
const char *name)
bool torture_unblock_tcp_output_port(struct torture_context *tctx,
const char *name,
uint16_t port)
{
return torture_block_tcp_transport_name_internal(tctx, t, name, true);
return torture_block_tcp_output_port_internal(tctx, name, port, true);
}
void torture_unblock_cleanup(struct torture_context *tctx)
bool torture_block_tcp_output_setup(struct torture_context *tctx)
{
filter_tcp_setup(tctx, true);
return iptables_setup_chain(tctx, "OUTPUT", "SMBTORTURE_OUTPUT", false);
}
bool torture_unblock_tcp_output_cleanup(struct torture_context *tctx)
{
return iptables_setup_chain(tctx, "OUTPUT", "SMBTORTURE_OUTPUT", true);
}

View File

@ -19,27 +19,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
bool torture_list_tcp_transport_name(struct torture_context *tctx,
const char *name,
uint32_t *packets);
bool torture_block_tcp_transport_name(struct torture_context *tctx,
struct smb2_transport *t,
const char *name);
bool torture_unblock_tcp_transport_name(struct torture_context *tctx,
struct smb2_transport *t,
const char *name);
void torture_unblock_cleanup(struct torture_context *tctx);
uint16_t torture_get_local_port_from_transport(struct smb2_transport *t);
#define torture_block_tcp_transport(_tctx, _t) \
torture_block_tcp_transport_name(_tctx, _t, #_t)
bool torture_block_tcp_output_port(struct torture_context *tctx,
const char *name,
uint16_t port);
bool torture_unblock_tcp_output_port(struct torture_context *tctx,
const char *name,
uint16_t port);
bool torture_block_tcp_output_setup(struct torture_context *tctx);
bool torture_unblock_tcp_output_cleanup(struct torture_context *tctx);
#define torture_unblock_tcp_transport(_tctx, _t) \
torture_unblock_tcp_transport_name(_tctx, _t, #_t)
#define torture_list_tcp_transport(_tctx, _t, _packets) \
torture_list_tcp_transport_name(_tctx, #_t, _packets)

View File

@ -399,8 +399,8 @@ static bool test_iptables_block_channel(struct torture_context *tctx,
bool ret;
local_port = torture_get_local_port_from_transport(transport);
torture_comment(tctx, "transport uses tcp port: %d\n", local_port);
ret = torture_block_tcp_transport_name(tctx, transport, name);
torture_comment(tctx, "transport[%s] uses tcp port: %d\n", name, local_port);
ret = torture_block_tcp_output_port(tctx, name, local_port);
torture_assert(tctx, ret, "we could not block tcp transport");
return ret;
@ -414,8 +414,8 @@ static bool test_iptables_unblock_channel(struct torture_context *tctx,
bool ret;
local_port = torture_get_local_port_from_transport(transport);
torture_comment(tctx, "transport uses tcp port: %d\n", local_port);
ret = torture_unblock_tcp_transport_name(tctx, transport, name);
torture_comment(tctx, "transport[%s] uses tcp port: %d\n", name, local_port);
ret = torture_unblock_tcp_output_port(tctx, name, local_port);
torture_assert(tctx, ret, "we could not block tcp transport");
return ret;
@ -451,13 +451,25 @@ static bool _test_unblock_channel(struct torture_context *tctx,
}
}
static bool test_setup_blocked_channels(struct torture_context *tctx)
{
bool use_iptables = torture_setting_bool(tctx,
"use_iptables", false);
if (use_iptables) {
return torture_block_tcp_output_setup(tctx);
}
return true;
}
static void test_cleanup_blocked_channels(struct torture_context *tctx)
{
bool use_iptables = torture_setting_bool(tctx,
"use_iptables", false);
if (use_iptables) {
torture_unblock_cleanup(tctx);
torture_unblock_tcp_output_cleanup(tctx);
}
}
@ -695,6 +707,7 @@ static bool test_multichannel_oplock_break_test2(struct torture_context *tctx,
struct smb2_session *session1 = tree1->session;
uint16_t local_port = 0;
DATA_BLOB blob;
bool block_setup = false;
bool block_ok = false;
bool unblock_ok = false;
@ -779,6 +792,9 @@ static bool test_multichannel_oplock_break_test2(struct torture_context *tctx,
transport2 = break_info.received_transport;
torture_reset_break_info(tctx, &break_info);
block_setup = test_setup_blocked_channels(tctx);
torture_assert(tctx, block_setup, "test_setup_blocked_channels");
/* block channel */
block_ok = test_block_channel(tctx, transport2);
@ -1156,6 +1172,7 @@ static bool test_multichannel_lease_break_test2(struct torture_context *tctx,
struct smb2_lease ls1;
struct smb2_lease ls2;
struct smb2_lease ls3;
bool block_setup = false;
bool block_ok = false;
bool unblock_ok = false;
@ -1231,6 +1248,8 @@ static bool test_multichannel_lease_break_test2(struct torture_context *tctx,
CHECK_VAL(io2.out.durable_open, false);
CHECK_VAL(lease_break_info.count, 0);
block_setup = test_setup_blocked_channels(tctx);
torture_assert(tctx, block_setup, "test_setup_blocked_channels");
torture_comment(tctx, "Blocking 2A\n");
/* Block 2A */
@ -1384,7 +1403,9 @@ done:
if (block_ok && !unblock_ok) {
test_unblock_channel(tctx, transport2A);
}
test_cleanup_blocked_channels(tctx);
if (block_setup) {
test_cleanup_blocked_channels(tctx);
}
tree1->session = session1;