s4/torture: add a creditting test skipping a SMB2 MID
[samba.git] / source4 / torture / smb2 / credits.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    test suite for SMB2 credits
5
6    Copyright (C) Ralph Boehme 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 "libcli/smb2/smb2_calls.h"
25 #include "torture/torture.h"
26 #include "torture/smb2/proto.h"
27 #include "../libcli/smb/smbXcli_base.h"
28 #include "lib/param/param.h"
29
30 /**
31  * Request 64k credits in negprot/sessionsetup and require at least 8k
32  *
33  * This passes against Windows 2016
34  **/
35 static bool test_session_setup_credits_granted(struct torture_context *tctx,
36                                                struct smb2_tree *_tree)
37 {
38         struct smbcli_options options;
39         struct smb2_transport *transport = NULL;
40         struct smb2_tree *tree = NULL;
41         uint16_t cur_credits;
42         NTSTATUS status;
43         bool ret = true;
44
45         transport = _tree->session->transport;
46         options = transport->options;
47
48         status = smb2_logoff(_tree->session);
49         torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
50                                         "smb2_logoff failed\n");
51         TALLOC_FREE(_tree);
52
53         options.max_credits = 65535;
54
55         ret = torture_smb2_connection_ext(tctx, 0, &options, &tree);
56         torture_assert_goto(tctx, ret == true, ret, done,
57                             "torture_smb2_connection_ext failed\n");
58
59         transport = tree->session->transport;
60
61         cur_credits = smb2cli_conn_get_cur_credits(transport->conn);
62         if (cur_credits < 8192) {
63                 torture_result(tctx, TORTURE_FAIL,
64                                "Server only granted %" PRIu16" credits\n",
65                                cur_credits);
66                 ret = false;
67                 goto done;
68         }
69
70 done:
71         TALLOC_FREE(tree);
72         return ret;
73 }
74
75 /**
76  * Request 64K credits in a single SMB2 request and requite at least 8192
77  *
78  * This passes against Windows 2016
79  **/
80 static bool test_single_req_credits_granted(struct torture_context *tctx,
81                                             struct smb2_tree *_tree)
82 {
83         struct smbcli_options options;
84         struct smb2_transport *transport = NULL;
85         struct smb2_tree *tree = NULL;
86         struct smb2_handle h = {{0}};
87         struct smb2_create create;
88         const char *fname = "single_req_credits_granted.dat";
89         uint16_t cur_credits;
90         NTSTATUS status;
91         bool ret = true;
92
93         smb2_util_unlink(_tree, fname);
94
95         transport = _tree->session->transport;
96         options = transport->options;
97
98         status = smb2_logoff(_tree->session);
99         torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
100                                         "smb2_logoff failed\n");
101         TALLOC_FREE(_tree);
102
103         options.max_credits = 1;
104
105         ret = torture_smb2_connection_ext(tctx, 0, &options, &tree);
106         torture_assert_goto(tctx, ret == true, ret, done,
107                             "torture_smb2_connection_ext failed\n");
108
109         transport = tree->session->transport;
110
111         cur_credits = smb2cli_conn_get_cur_credits(transport->conn);
112         if (cur_credits != 1) {
113                 torture_result(tctx, TORTURE_FAIL,
114                                "Only wanted 1 credit but server granted %" PRIu16"\n",
115                                cur_credits);
116                 ret = false;
117                 goto done;
118         }
119
120         smb2cli_conn_set_max_credits(transport->conn, 65535);
121
122         ZERO_STRUCT(create);
123         create.in.impersonation_level   = NTCREATEX_IMPERSONATION_IMPERSONATION;
124         create.in.desired_access        = SEC_RIGHTS_FILE_ALL;
125         create.in.file_attributes       = FILE_ATTRIBUTE_NORMAL;
126         create.in.create_disposition    = NTCREATEX_DISP_OPEN_IF;
127         create.in.fname                 = fname;
128
129         status = smb2_create(tree, tctx, &create);
130         torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
131                                         "smb2_create failed\n");
132         h = create.out.file.handle;
133
134         cur_credits = smb2cli_conn_get_cur_credits(transport->conn);
135         if (cur_credits < 8192) {
136                 torture_result(tctx, TORTURE_FAIL,
137                                "Server only granted %" PRIu16" credits\n",
138                                cur_credits);
139                 ret = false;
140                 goto done;
141         }
142
143 done:
144         if (!smb2_util_handle_empty(h)) {
145                 smb2_util_close(tree, h);
146         }
147         smb2_util_unlink(tree, fname);
148         TALLOC_FREE(tree);
149         return ret;
150 }
151
152 static bool test_crediting_skipped_mid(struct torture_context *tctx,
153                                        struct smb2_tree *_tree)
154 {
155         struct smbcli_options options;
156         struct smb2_transport *transport = NULL;
157         struct smb2_tree *tree = NULL;
158         struct smb2_handle h = {{0}};
159         struct smb2_create create;
160         const char *fname = "skipped_mid.dat";
161         uint64_t mid;
162         uint16_t cur_credits;
163         NTSTATUS status;
164         bool ret = true;
165         int i;
166
167         smb2_util_unlink(_tree, fname);
168
169         transport = _tree->session->transport;
170         options = transport->options;
171
172         status = smb2_logoff(_tree->session);
173         torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_logoff failed\n");
174         TALLOC_FREE(_tree);
175
176         options.max_credits = 8192;
177
178         ret = torture_smb2_connection_ext(tctx, 0, &options, &tree);
179         torture_assert_goto(tctx, ret == true, ret, done, "torture_smb2_connection_ext failed\n");
180
181         transport = tree->session->transport;
182
183         cur_credits = smb2cli_conn_get_cur_credits(transport->conn);
184         if (cur_credits != 8192) {
185                 torture_result(tctx, TORTURE_FAIL, "Server only granted %" PRIu16" credits\n", cur_credits);
186                 ret = false;
187                 goto done;
188         }
189
190         ZERO_STRUCT(create);
191         create.in.impersonation_level   = NTCREATEX_IMPERSONATION_IMPERSONATION;
192         create.in.desired_access        = SEC_RIGHTS_FILE_ALL;
193         create.in.file_attributes       = FILE_ATTRIBUTE_NORMAL;
194         create.in.create_disposition    = NTCREATEX_DISP_OPEN_IF;
195         create.in.fname                 = fname;
196
197         status = smb2_create(tree, tctx, &create);
198         torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed\n");
199         h = create.out.file.handle;
200
201         /*
202          * See what happens if we skip a mid. As we want to avoid triggering our
203          * client side mid window check we keep conn->smb2.cur_credits
204          * unchanged so the server keeps granting credits until it's max mid
205          * windows size is reached at which point it will disconnect us:
206          *
207          * o Windows 2016 currently has a maximum mid window size of 8192 by
208          *   default
209          *
210          * o Samba's limit is 512
211          *
212          * o Windows 2008r2 uses some special algorithm (MS-SMB2 3.3.1.1
213          *   footnote <167>) that kicks in once a mid is skipped, resulting in a
214          *   maximum window size of 100-300 depending on the number of granted
215          *   credits at the moment of skipping a mid.
216          */
217
218         mid = smb2cli_conn_get_mid(tree->session->transport->conn);
219         smb2cli_conn_set_mid(tree->session->transport->conn, mid + 1);
220
221         for (i = 0; i < 8191; i++) {
222                 status = smb2_util_write(tree, h, "\0", 0, 1);
223                 if (!NT_STATUS_IS_OK(status)) {
224                         torture_result(tctx, TORTURE_FAIL, "Server only allowed %d writes\n", i);
225                         ret = false;
226                         goto done;
227                 }
228         }
229
230         /*
231          * Now use the skipped mid (the smb2_util_close...), we should
232          * immediately get a full mid window of size 8192.
233          */
234         smb2cli_conn_set_mid(tree->session->transport->conn, mid);
235         status = smb2_util_close(tree, h);
236         torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_close failed\n");
237         ZERO_STRUCT(h);
238
239         cur_credits = smb2cli_conn_get_cur_credits(transport->conn);
240         if (cur_credits != 8192) {
241                 torture_result(tctx, TORTURE_FAIL, "Server only granted %" PRIu16" credits\n", cur_credits);
242                 ret = false;
243                 goto done;
244         }
245
246         smb2cli_conn_set_mid(tree->session->transport->conn, mid + 8192);
247
248 done:
249         if (!smb2_util_handle_empty(h)) {
250                 smb2_util_close(tree, h);
251         }
252         smb2_util_unlink(tree, fname);
253         TALLOC_FREE(tree);
254         return ret;
255 }
256
257 struct torture_suite *torture_smb2_crediting_init(void)
258 {
259         struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "credits");
260
261         torture_suite_add_1smb2_test(suite, "session_setup_credits_granted", test_session_setup_credits_granted);
262         torture_suite_add_1smb2_test(suite, "single_req_credits_granted", test_single_req_credits_granted);
263         torture_suite_add_1smb2_test(suite, "skipped_mid", test_crediting_skipped_mid);
264
265         suite->description = talloc_strdup(suite, "SMB2-CREDITS tests");
266
267         return suite;
268 }