Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : SMB read/write torture tester
4 : Copyright (C) Andrew Tridgell 1997-2003
5 : Copyright (C) Jelmer Vernooij 2006
6 : Copyright (C) David Mulder 2019
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 : #include "includes.h"
22 : #include "torture/smbtorture.h"
23 : #include "libcli/smb2/smb2.h"
24 : #include "libcli/smb2/smb2_calls.h"
25 : #include "torture/torture.h"
26 : #include "torture/util.h"
27 : #include "torture/smb2/proto.h"
28 :
29 : #define CHECK_STATUS(_status, _expected) \
30 : torture_assert_ntstatus_equal_goto(torture, _status, _expected, \
31 : ret, done, "Incorrect status")
32 :
33 : #define CHECK_VALUE(v, correct) \
34 : torture_assert_int_equal_goto(torture, v, correct, \
35 : ret, done, "Incorrect value")
36 :
37 : #define FNAME "smb2_writetest.dat"
38 :
39 14 : static bool run_smb2_readwritetest(struct torture_context *tctx,
40 : struct smb2_tree *t1, struct smb2_tree *t2)
41 : {
42 14 : const char *lockfname = "torture2.lck";
43 14 : struct smb2_create f1 = {0};
44 14 : struct smb2_create f2 = {0};
45 14 : struct smb2_handle h1 = {{0}};
46 14 : struct smb2_handle h2 = {{0}};
47 0 : int i;
48 0 : uint8_t buf[131072];
49 14 : bool correct = true;
50 0 : NTSTATUS status;
51 14 : int ret = 0;
52 :
53 14 : ret = smb2_deltree(t1, lockfname);
54 14 : torture_assert(tctx, ret != -1, "unlink failed");
55 :
56 14 : f1.in.desired_access = SEC_FILE_ALL;
57 14 : f1.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
58 : NTCREATEX_SHARE_ACCESS_WRITE;
59 14 : f1.in.create_disposition = FILE_CREATE;
60 14 : f1.in.fname = lockfname;
61 :
62 14 : status = smb2_create(t1, tctx, &f1);
63 14 : torture_assert_ntstatus_ok_goto(tctx, status, correct, done,
64 : talloc_asprintf(tctx, "first open read/write of %s failed (%s)",
65 : lockfname, nt_errstr(status)));
66 14 : h1 = f1.out.file.handle;
67 :
68 14 : f2.in.desired_access = SEC_FILE_READ_DATA;
69 14 : f2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
70 : NTCREATEX_SHARE_ACCESS_WRITE;
71 14 : f2.in.create_disposition = FILE_OPEN;
72 14 : f2.in.fname = lockfname;
73 :
74 14 : status = smb2_create(t2, tctx, &f2);
75 14 : torture_assert_ntstatus_ok_goto(tctx, status, correct, done,
76 : talloc_asprintf(tctx, "second open read-only of %s failed (%s)",
77 : lockfname, nt_errstr(status)));
78 14 : h2 = f2.out.file.handle;
79 :
80 14 : torture_comment(tctx, "Checking data integrity over %d ops\n",
81 : torture_numops);
82 :
83 154 : for (i = 0; i < torture_numops; i++) {
84 140 : struct smb2_write w = {0};
85 140 : struct smb2_read r = {0};
86 140 : size_t buf_size = ((unsigned int)random()%(sizeof(buf)-1))+ 1;
87 :
88 140 : if (i % 10 == 0) {
89 14 : if (torture_setting_bool(tctx, "progress", true)) {
90 0 : torture_comment(tctx, "%d\r", i); fflush(stdout);
91 : }
92 : }
93 :
94 140 : generate_random_buffer(buf, buf_size);
95 :
96 140 : w.in.file.handle = h1;
97 140 : w.in.offset = 0;
98 140 : w.in.data.data = buf;
99 140 : w.in.data.length = buf_size;
100 :
101 140 : status = smb2_write(t1, &w);
102 140 : if (!NT_STATUS_IS_OK(status) || w.out.nwritten != buf_size) {
103 0 : torture_comment(tctx, "write failed (%s)\n",
104 : nt_errstr(status));
105 0 : torture_result(tctx, TORTURE_FAIL,
106 : "wrote %d, expected %d\n",
107 0 : (int)w.out.nwritten, (int)buf_size);
108 0 : correct = false;
109 0 : goto done;
110 : }
111 :
112 140 : r.in.file.handle = h2;
113 140 : r.in.offset = 0;
114 140 : r.in.length = buf_size;
115 140 : status = smb2_read(t2, tctx, &r);
116 140 : if (!NT_STATUS_IS_OK(status) || r.out.data.length != buf_size) {
117 0 : torture_comment(tctx, "read failed (%s)\n",
118 : nt_errstr(status));
119 0 : torture_result(tctx, TORTURE_FAIL,
120 : "read %d, expected %d\n",
121 0 : (int)r.out.data.length, (int)buf_size);
122 0 : correct = false;
123 0 : goto done;
124 : }
125 :
126 140 : torture_assert_mem_equal_goto(tctx, r.out.data.data, buf,
127 : buf_size, correct, done, "read/write compare failed\n");
128 : }
129 :
130 14 : status = smb2_util_close(t2, h2);
131 14 : torture_assert_ntstatus_ok_goto(tctx, status, correct, done,
132 : talloc_asprintf(tctx, "close failed (%s)", nt_errstr(status)));
133 14 : ZERO_STRUCT(h2);
134 :
135 14 : status = smb2_util_close(t1, h1);
136 14 : torture_assert_ntstatus_ok_goto(tctx, status, correct, done,
137 : talloc_asprintf(tctx, "close failed (%s)", nt_errstr(status)));
138 14 : ZERO_STRUCT(h1);
139 :
140 14 : done:
141 14 : if (!smb2_util_handle_empty(h2)) {
142 0 : smb2_util_close(t2, h2);
143 : }
144 14 : if (!smb2_util_handle_empty(h1)) {
145 0 : smb2_util_close(t1, h1);
146 : }
147 :
148 14 : status = smb2_util_unlink(t1, lockfname);
149 14 : if (!NT_STATUS_IS_OK(status)) {
150 0 : torture_comment(tctx, "unlink failed (%s)", nt_errstr(status));
151 : }
152 :
153 14 : return correct;
154 : }
155 :
156 :
157 7 : static bool run_smb2_wrap_readwritetest(struct torture_context *tctx,
158 : struct smb2_tree *tree1,
159 : struct smb2_tree *tree2)
160 : {
161 7 : return run_smb2_readwritetest(tctx, tree1, tree1);
162 : }
163 :
164 7 : static bool test_rw_invalid(struct torture_context *torture, struct smb2_tree *tree)
165 : {
166 7 : bool ret = true;
167 0 : NTSTATUS status;
168 0 : struct smb2_handle h;
169 0 : uint8_t buf[64*1024];
170 0 : struct smb2_read rd;
171 7 : struct smb2_write w = {0};
172 0 : union smb_setfileinfo sfinfo;
173 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
174 :
175 7 : ZERO_STRUCT(buf);
176 :
177 7 : smb2_util_unlink(tree, FNAME);
178 :
179 7 : status = torture_smb2_testfile(tree, FNAME, &h);
180 7 : CHECK_STATUS(status, NT_STATUS_OK);
181 :
182 : /* set delete-on-close */
183 7 : ZERO_STRUCT(sfinfo);
184 7 : sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION;
185 7 : sfinfo.disposition_info.in.delete_on_close = 1;
186 7 : sfinfo.generic.in.file.handle = h;
187 7 : status = smb2_setinfo_file(tree, &sfinfo);
188 7 : CHECK_STATUS(status, NT_STATUS_OK);
189 :
190 7 : status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
191 7 : CHECK_STATUS(status, NT_STATUS_OK);
192 :
193 7 : ZERO_STRUCT(rd);
194 7 : rd.in.file.handle = h;
195 7 : rd.in.length = 10;
196 7 : rd.in.offset = 0;
197 7 : rd.in.min_count = 1;
198 :
199 7 : status = smb2_read(tree, tmp_ctx, &rd);
200 7 : CHECK_STATUS(status, NT_STATUS_OK);
201 7 : CHECK_VALUE(rd.out.data.length, 10);
202 :
203 7 : rd.in.min_count = 0;
204 7 : rd.in.length = 10;
205 7 : rd.in.offset = sizeof(buf);
206 7 : status = smb2_read(tree, tmp_ctx, &rd);
207 7 : CHECK_STATUS(status, NT_STATUS_END_OF_FILE);
208 :
209 7 : rd.in.min_count = 0;
210 7 : rd.in.length = 0;
211 7 : rd.in.offset = sizeof(buf);
212 7 : status = smb2_read(tree, tmp_ctx, &rd);
213 7 : CHECK_STATUS(status, NT_STATUS_OK);
214 7 : CHECK_VALUE(rd.out.data.length, 0);
215 :
216 7 : rd.in.min_count = 0;
217 7 : rd.in.length = 1;
218 7 : rd.in.offset = INT64_MAX - 1;
219 7 : status = smb2_read(tree, tmp_ctx, &rd);
220 7 : CHECK_STATUS(status, NT_STATUS_END_OF_FILE);
221 :
222 7 : rd.in.min_count = 0;
223 7 : rd.in.length = 0;
224 7 : rd.in.offset = INT64_MAX;
225 7 : status = smb2_read(tree, tmp_ctx, &rd);
226 7 : CHECK_STATUS(status, NT_STATUS_OK);
227 7 : CHECK_VALUE(rd.out.data.length, 0);
228 :
229 7 : rd.in.min_count = 0;
230 7 : rd.in.length = 1;
231 7 : rd.in.offset = INT64_MAX;
232 7 : status = smb2_read(tree, tmp_ctx, &rd);
233 7 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
234 :
235 7 : rd.in.min_count = 0;
236 7 : rd.in.length = 0;
237 7 : rd.in.offset = (uint64_t)INT64_MAX + 1;
238 7 : status = smb2_read(tree, tmp_ctx, &rd);
239 7 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
240 :
241 7 : rd.in.min_count = 0;
242 7 : rd.in.length = 0;
243 7 : rd.in.offset = (uint64_t)INT64_MIN;
244 7 : status = smb2_read(tree, tmp_ctx, &rd);
245 7 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
246 :
247 7 : rd.in.min_count = 0;
248 7 : rd.in.length = 0;
249 7 : rd.in.offset = (uint64_t)(int64_t)-1;
250 7 : status = smb2_read(tree, tmp_ctx, &rd);
251 7 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
252 :
253 7 : rd.in.min_count = 0;
254 7 : rd.in.length = 0;
255 7 : rd.in.offset = (uint64_t)(int64_t)-2;
256 7 : status = smb2_read(tree, tmp_ctx, &rd);
257 7 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
258 :
259 7 : rd.in.min_count = 0;
260 7 : rd.in.length = 0;
261 7 : rd.in.offset = (uint64_t)(int64_t)-3;
262 7 : status = smb2_read(tree, tmp_ctx, &rd);
263 7 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
264 :
265 7 : w.in.file.handle = h;
266 7 : w.in.offset = (int64_t)-1;
267 7 : w.in.data.data = buf;
268 7 : w.in.data.length = ARRAY_SIZE(buf);
269 :
270 7 : status = smb2_write(tree, &w);
271 7 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
272 :
273 6 : w.in.file.handle = h;
274 6 : w.in.offset = (int64_t)-2;
275 6 : w.in.data.data = buf;
276 6 : w.in.data.length = ARRAY_SIZE(buf);
277 :
278 6 : status = smb2_write(tree, &w);
279 6 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
280 :
281 6 : w.in.file.handle = h;
282 6 : w.in.offset = INT64_MIN;
283 6 : w.in.data.data = buf;
284 6 : w.in.data.length = 1;
285 :
286 6 : status = smb2_write(tree, &w);
287 6 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
288 :
289 6 : w.in.file.handle = h;
290 6 : w.in.offset = INT64_MIN;
291 6 : w.in.data.data = buf;
292 6 : w.in.data.length = 0;
293 6 : status = smb2_write(tree, &w);
294 6 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
295 :
296 6 : w.in.file.handle = h;
297 6 : w.in.offset = INT64_MAX;
298 6 : w.in.data.data = buf;
299 6 : w.in.data.length = 0;
300 6 : status = smb2_write(tree, &w);
301 6 : CHECK_STATUS(status, NT_STATUS_OK);
302 6 : CHECK_VALUE(w.out.nwritten, 0);
303 :
304 6 : w.in.file.handle = h;
305 6 : w.in.offset = INT64_MAX;
306 6 : w.in.data.data = buf;
307 6 : w.in.data.length = 1;
308 6 : status = smb2_write(tree, &w);
309 6 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
310 :
311 6 : w.in.file.handle = h;
312 6 : w.in.offset = (uint64_t)INT64_MAX + 1;
313 6 : w.in.data.data = buf;
314 6 : w.in.data.length = 0;
315 6 : status = smb2_write(tree, &w);
316 6 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
317 :
318 6 : w.in.file.handle = h;
319 6 : w.in.offset = 0xfffffff0000; /* MAXFILESIZE */
320 6 : w.in.data.data = buf;
321 6 : w.in.data.length = 1;
322 6 : status = smb2_write(tree, &w);
323 6 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
324 :
325 6 : w.in.file.handle = h;
326 6 : w.in.offset = 0xfffffff0000 - 1; /* MAXFILESIZE - 1 */
327 6 : w.in.data.data = buf;
328 6 : w.in.data.length = 1;
329 6 : status = smb2_write(tree, &w);
330 6 : if (TARGET_IS_SAMBA3(torture) || TARGET_IS_SAMBA4(torture)) {
331 6 : CHECK_STATUS(status, NT_STATUS_OK);
332 6 : CHECK_VALUE(w.out.nwritten, 1);
333 : } else {
334 0 : CHECK_STATUS(status, NT_STATUS_DISK_FULL);
335 : }
336 :
337 6 : w.in.file.handle = h;
338 6 : w.in.offset = 0xfffffff0000; /* MAXFILESIZE */
339 6 : w.in.data.data = buf;
340 6 : w.in.data.length = 0;
341 6 : status = smb2_write(tree, &w);
342 6 : CHECK_STATUS(status, NT_STATUS_OK);
343 6 : CHECK_VALUE(w.out.nwritten, 0);
344 :
345 6 : done:
346 7 : talloc_free(tmp_ctx);
347 7 : return ret;
348 : }
349 :
350 2354 : struct torture_suite *torture_smb2_readwrite_init(TALLOC_CTX *ctx)
351 : {
352 2354 : struct torture_suite *suite = torture_suite_create(ctx, "rw");
353 :
354 2354 : torture_suite_add_2smb2_test(suite, "rw1", run_smb2_readwritetest);
355 2354 : torture_suite_add_2smb2_test(suite, "rw2", run_smb2_wrap_readwritetest);
356 2354 : torture_suite_add_1smb2_test(suite, "invalid", test_rw_invalid);
357 :
358 2354 : suite->description = talloc_strdup(suite, "SMB2 Samba4 Read/Write");
359 :
360 2354 : return suite;
361 : }
|