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