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