s4-torture: add torture_block/torture_unblock smb2 transport functions
[mdw/samba-autobuild/.git] / source4 / torture / smb2 / block.c
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
31 /*
32  * INPUT
33  *  |
34  *  -----> SAMBA_INPUT
35  *             |
36  *             -----> SAMBA_INPUT_transportname1
37  *             -----> SAMBA_INPUT_transportname2
38  */
39
40
41 static bool run_cmd(const char *cmd)
42 {
43         int ret;
44
45         DEBUG(10, ("%s will call '%s'\n", __location__, cmd));
46
47         ret = system(cmd);
48         if (ret) {
49                 DEBUG(1, ("%s failed to execute system call: %s: %d\n",
50                         __location__, cmd, ret));
51                 return false;
52         }
53
54         return true;
55 }
56
57 int smbrun(const char *cmd, int *outfd, char * const *env);
58
59 static bool run_cmd_return_buf(TALLOC_CTX *mem_ctx,
60                                const char *cmd,
61                                int *num_lines, char ***buf)
62 {
63         int ret;
64         int fd = -1;
65
66         DEBUG(10, ("%s will call '%s'\n", __location__, cmd));
67
68         ret = smbrun(cmd, &fd, NULL);
69         if (ret) {
70                 DEBUG(1, ("%s failed to execute system call: %s: %d\n",
71                         __location__, cmd, ret));
72                 if (fd != -1) {
73                         close(fd);
74                 }
75                 return false;
76         }
77
78         *buf = fd_lines_load(fd, num_lines, 0, mem_ctx);
79         if (fd != -1) {
80                 close(fd);
81         }
82         if (*buf == NULL) {
83                 return false;
84         }
85
86         return true;
87 }
88
89 static const char *iptables_command(struct torture_context *tctx)
90 {
91         return torture_setting_string(tctx, "iptables_command",
92                                       "/usr/sbin/iptables");
93 }
94
95 char *escape_shell_string(const char *src);
96
97 /*
98  * iptables v1.6.1: chain name `SAMBA_INPUT_tree1->session->transport'
99  * too long (must be under 29 chars)
100  *
101  * maybe truncate chainname ?
102  */
103 static const char *samba_chain_name(struct torture_context *tctx,
104                                     const char *name,
105                                     const char *prefix)
106 {
107         const char *s;
108         char *sm;
109
110         s = talloc_asprintf(tctx, "%s_%s", prefix, name);
111         if (s == NULL) {
112                 return NULL;
113         }
114
115         sm = escape_shell_string(s);
116         if (sm == NULL) {
117                 return NULL;
118         }
119
120         s = talloc_strdup(tctx, sm);
121         free(sm);
122
123         return s;
124 }
125
126 static bool filter_tcp_setup(struct torture_context *tctx,
127                              bool unblock)
128 {
129         const char *cmd_in, *cmd_out;
130         const char *ipt = iptables_command(tctx);
131
132         if (unblock) {
133                 cmd_in = talloc_asprintf(tctx,
134                                 "%s -L SAMBA_INPUT > /dev/null 2>&1 && "
135                                 "("
136                                 "%s -F SAMBA_INPUT; "
137                                 "%s -D INPUT -j SAMBA_INPUT; "
138                                 "%s -X SAMBA_INPUT;"
139                                 ")",
140                                 ipt, ipt, ipt, ipt);
141                 cmd_out = talloc_asprintf(tctx,
142                                 "%s -L SAMBA_OUTPUT > /dev/null 2>&1 && "
143                                 "("
144                                 "%s -F SAMBA_OUTPUT;"
145                                 "%s -D OUTPUT -j SAMBA_OUTPUT;"
146                                 "%s -X SAMBA_OUTPUT;"
147                                 ")",
148                                 ipt, ipt, ipt, ipt);
149         } else {
150                 cmd_in = talloc_asprintf(tctx,
151                                 "%s -L SAMBA_INPUT > /dev/null 2>&1 || "
152                                 "("
153                                 "%s -N SAMBA_INPUT && "
154                                 "%s -I INPUT -j SAMBA_INPUT "
155                                 ")",
156                                 ipt, ipt, ipt);
157                 cmd_out = talloc_asprintf(tctx,
158                                 "%s -L SAMBA_OUTPUT > /dev/null 2>&1 || "
159                                 "("
160                                 "%s -N SAMBA_OUTPUT && "
161                                 "%s -I OUTPUT -j SAMBA_OUTPUT;"
162                                 ")",
163                                 ipt, ipt, ipt);
164         }
165
166         if (cmd_in == NULL || cmd_out == NULL) {
167                 return false;
168         }
169
170         if (!run_cmd(cmd_in)) {
171                 return false;
172         }
173         /* if (!run_cmd(cmd_out)) { return false; } */
174
175         return true;
176 }
177
178 static bool filter_tcp_setup_name(struct torture_context *tctx,
179                                   const char *name, bool unblock)
180 {
181         const char *cmd_in, *cmd_out;
182         const char *chain_in, *chain_out;
183         const char *ipt = iptables_command(tctx);
184
185         chain_in = samba_chain_name(tctx, name, "SAMBA_INPUT");
186         chain_out = samba_chain_name(tctx, name, "SAMBA_OUTPUT");
187         if (chain_in == NULL || chain_out == NULL) {
188                 return false;
189         }
190
191         if (unblock) {
192                 cmd_in  = talloc_asprintf(tctx, "%s -F %s; "
193                                                 "%s -D SAMBA_INPUT -j %s; "
194                                                 "%s -X %s",
195                                                 ipt, chain_in,
196                                                 ipt, chain_in,
197                                                 ipt, chain_in);
198                 cmd_out = talloc_asprintf(tctx, "%s -F %s; "
199                                                 "%s -D SAMBA_OUTPUT -j %s; "
200                                                 "%s -X %s",
201                                                 ipt, chain_out,
202                                                 ipt, chain_out,
203                                                 ipt, chain_out);
204         } else {
205                 cmd_in  = talloc_asprintf(tctx, "%s -L %s > /dev/null 2>&1 || "
206                                                 "%s -N %s && "
207                                                 "%s -I SAMBA_INPUT -j %s",
208                                                 ipt, chain_in,
209                                                 ipt, chain_in,
210                                                 ipt, chain_in);
211                 cmd_out = talloc_asprintf(tctx, "%s -L %s > /dev/null 2>&1 || "
212                                                 "%s -N %s && "
213                                                 "%s -I SAMBA_OUTPUT -j %s",
214                                                 ipt, chain_out,
215                                                 ipt, chain_out,
216                                                 ipt, chain_out);
217         }
218
219         if (cmd_in == NULL || cmd_out == NULL) {
220                 return false;
221         }
222
223         if (!run_cmd(cmd_in)) {
224                 return false;
225         }
226         /* if (!run_cmd(cmd_out)) return false; */
227
228         return true;
229 }
230
231 /* '11   452 DROP tcp -- * *  0.0.0.0/0  0.0.0.0/0  tcp dpt:43062' */
232 static bool get_packet_count(const char *s, uint32_t *count)
233 {
234         int i = 0;
235         char *p;
236
237         if (s == NULL) {
238                 return false;
239         }
240
241         while (s[i] == ' ') {
242                 s++;
243         }
244
245         p = strchr(s, ' ');
246         if (p == NULL) {
247                 return false;
248         }
249         *p = '\0';
250
251         *count = atoi(s);
252
253         return true;
254 }
255
256 bool torture_list_tcp_transport_name(struct torture_context *tctx,
257                                     const char *name,
258                                     uint32_t *_packets)
259 {
260         const char *chain_in, *cmd;
261         int num_lines;
262         char **buf;
263         uint32_t packets = 0;
264         const char *ipt = iptables_command(tctx);
265
266         chain_in = samba_chain_name(tctx, name, "SAMBA_INPUT");
267         if (chain_in == NULL) {
268                 return false;
269         }
270
271         cmd = talloc_asprintf(tctx, "%s -L %s -v -n", ipt, chain_in);
272         if (cmd == NULL) {
273                 return false;
274         }
275
276         if (!run_cmd_return_buf(tctx, cmd, &num_lines, &buf)) {
277                 return false;
278         }
279         SMB_ASSERT(num_lines >= 3);
280
281         if (!get_packet_count(buf[2], &packets)) {
282                 return false;
283         }
284
285         torture_comment(tctx, "chain: '%s', packets: %d\n", name, (int)packets);
286
287         if (_packets != NULL) {
288                 *_packets = packets;
289         }
290
291         return true;
292 }
293
294 uint16_t torture_get_local_port_from_transport(struct smb2_transport *t)
295 {
296         const struct sockaddr_storage *local_ss;
297
298         local_ss = smbXcli_conn_local_sockaddr(t->conn);
299
300         return get_sockaddr_port(local_ss);
301 }
302
303 static bool torture_block_tcp_transport_name_internal(
304                                                 struct torture_context *tctx,
305                                                 struct smb2_transport *t,
306                                                 const char *name,
307                                                 bool unblock)
308 {
309         char *cmd_in;
310         char *cmd_out;
311         const char *chain_in, *chain_out;
312         uint16_t port = torture_get_local_port_from_transport(t);
313         const char *ipt = iptables_command(tctx);
314
315         chain_in = samba_chain_name(tctx, name, "SAMBA_INPUT");
316         chain_out = samba_chain_name(tctx, name, "SAMBA_OUTPUT");
317         if (chain_in == NULL || chain_out == NULL) {
318                 return false;
319         }
320
321         if (!unblock) {
322                 filter_tcp_setup(tctx, false);
323                 filter_tcp_setup_name(tctx, name, false);
324         }
325
326         torture_comment(tctx, "%sblocking %s dport %d\n",
327                         unblock ? "un" : "", name, port);
328
329         cmd_in = talloc_asprintf(tctx,
330                                  "%s %s %s -p tcp --dport %d -j DROP",
331                                  ipt, unblock ? "-D" : "-I", chain_in, port);
332         cmd_out = talloc_asprintf(tctx,
333                                   "%s %s %s -p tcp --sport %d -j DROP",
334                                   ipt, unblock ? "-D" : "-I", chain_out, port);
335         if (cmd_in == NULL || cmd_out == NULL) {
336                 return false;
337         }
338
339         if (!run_cmd(cmd_in)) {
340                 return false;
341         }
342         /* if (!run_cmd(cmd_out)) return false; */
343
344         if (unblock) {
345                 filter_tcp_setup_name(tctx, name, true);
346                 /* better dont cleanup here */
347                 /* filter_tcp_setup(tctx, true); */
348         }
349
350         return true;
351 }
352
353 bool torture_block_tcp_transport_name(struct torture_context *tctx,
354                                       struct smb2_transport *t,
355                                       const char *name)
356 {
357         return torture_block_tcp_transport_name_internal(tctx, t, name, false);
358 }
359
360 bool torture_unblock_tcp_transport_name(struct torture_context *tctx,
361                                         struct smb2_transport *t,
362                                         const char *name)
363 {
364         return torture_block_tcp_transport_name_internal(tctx, t, name, true);
365 }
366
367 void torture_unblock_cleanup(struct torture_context *tctx)
368 {
369         filter_tcp_setup(tctx, true);
370 }