Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : SMB torture tester - mangling test
4 : Copyright (C) Andrew Tridgell 2002
5 : Copyright (C) David Mulder 2019
6 :
7 : This program is free software; you can redistribute it and/or modify
8 : it under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3 of the License, or
10 : (at your option) any later version.
11 :
12 : This program is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : GNU General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with this program. If not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include "includes.h"
22 : #include "system/filesys.h"
23 : #include "system/dir.h"
24 : #include <tdb.h>
25 : #include "../lib/util/util_tdb.h"
26 : #include "libcli/smb2/smb2.h"
27 : #include "libcli/smb2/smb2_calls.h"
28 : #include "torture/util.h"
29 : #include "torture/smb2/proto.h"
30 :
31 : #undef strcasecmp
32 :
33 : static TDB_CONTEXT *tdb;
34 :
35 : #define NAME_LENGTH 20
36 :
37 : static unsigned int total, collisions, failures;
38 :
39 0 : static bool test_one(struct torture_context *tctx, struct smb2_tree *tree,
40 : const char *name)
41 : {
42 : struct smb2_handle fnum;
43 : const char *shortname;
44 : const char *name2;
45 : NTSTATUS status;
46 : TDB_DATA data;
47 0 : struct smb2_create io = {0};
48 :
49 0 : total++;
50 :
51 0 : io.in.fname = name;
52 0 : io.in.desired_access = SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA |
53 : SEC_FILE_EXECUTE;
54 0 : io.in.create_disposition = NTCREATEX_DISP_CREATE;
55 0 : io.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
56 : NTCREATEX_SHARE_ACCESS_WRITE |
57 : NTCREATEX_SHARE_ACCESS_DELETE;
58 0 : io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
59 0 : status = smb2_create(tree, tree, &io);
60 0 : if (!NT_STATUS_IS_OK(status)) {
61 0 : torture_comment(tctx, "open of %s failed (%s)\n", name,
62 : nt_errstr(status));
63 0 : return false;
64 : }
65 0 : fnum = io.out.file.handle;
66 :
67 0 : status = smb2_util_close(tree, fnum);
68 0 : if (NT_STATUS_IS_ERR(status)) {
69 0 : torture_comment(tctx, "close of %s failed (%s)\n", name,
70 : nt_errstr(status));
71 0 : return false;
72 : }
73 :
74 : /* get the short name */
75 0 : status = smb2_qpathinfo_alt_name(tctx, tree, name, &shortname);
76 0 : if (!NT_STATUS_IS_OK(status)) {
77 0 : torture_comment(tctx, "query altname of %s failed (%s)\n",
78 : name, nt_errstr(status));
79 0 : return false;
80 : }
81 :
82 0 : name2 = talloc_asprintf(tctx, "mangle_test\\%s", shortname);
83 0 : status = smb2_util_unlink(tree, name2);
84 0 : if (NT_STATUS_IS_ERR(status)) {
85 0 : torture_comment(tctx, "unlink of %s (%s) failed (%s)\n",
86 : name2, name, nt_errstr(status));
87 0 : return false;
88 : }
89 :
90 : /* recreate by short name */
91 0 : io = (struct smb2_create){0};
92 0 : io.in.fname = name2;
93 0 : io.in.desired_access = SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA |
94 : SEC_FILE_EXECUTE;
95 0 : io.in.create_disposition = NTCREATEX_DISP_CREATE;
96 0 : io.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
97 : NTCREATEX_SHARE_ACCESS_WRITE |
98 : NTCREATEX_SHARE_ACCESS_DELETE;
99 0 : io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
100 0 : status = smb2_create(tree, tree, &io);
101 0 : if (!NT_STATUS_IS_OK(status)) {
102 0 : torture_comment(tctx, "open2 of %s failed (%s)\n", name2,
103 : nt_errstr(status));
104 0 : return false;
105 : }
106 0 : fnum = io.out.file.handle;
107 :
108 0 : status = smb2_util_close(tree, fnum);
109 0 : if (NT_STATUS_IS_ERR(status)) {
110 0 : torture_comment(tctx, "close of %s failed (%s)\n", name,
111 : nt_errstr(status));
112 0 : return false;
113 : }
114 :
115 : /* and unlink by long name */
116 0 : status = smb2_util_unlink(tree, name);
117 0 : if (NT_STATUS_IS_ERR(status)) {
118 0 : torture_comment(tctx, "unlink2 of %s (%s) failed (%s)\n",
119 : name, name2, nt_errstr(status));
120 0 : failures++;
121 0 : smb2_util_unlink(tree, name2);
122 0 : return true;
123 : }
124 :
125 : /* see if the short name is already in the tdb */
126 0 : data = tdb_fetch_bystring(tdb, shortname);
127 0 : if (data.dptr) {
128 : /* maybe its a duplicate long name? */
129 0 : if (strcasecmp(name, (const char *)data.dptr) != 0) {
130 : /* we have a collision */
131 0 : collisions++;
132 0 : torture_comment(tctx, "Collision between %s and %s"
133 : " -> %s (coll/tot: %u/%u)\n",
134 : name, data.dptr, shortname, collisions,
135 : total);
136 : }
137 0 : free(data.dptr);
138 : } else {
139 : TDB_DATA namedata;
140 : /* store it for later */
141 0 : namedata.dptr = discard_const_p(uint8_t, name);
142 0 : namedata.dsize = strlen(name)+1;
143 0 : tdb_store_bystring(tdb, shortname, namedata, TDB_REPLACE);
144 : }
145 :
146 0 : return true;
147 : }
148 :
149 :
150 0 : static char *gen_name(struct torture_context *tctx)
151 : {
152 0 : const char *chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._-$~...";
153 0 : unsigned int max_idx = strlen(chars);
154 : unsigned int len;
155 : int i;
156 0 : char *p = NULL;
157 0 : char *name = NULL;
158 :
159 0 : name = talloc_strdup(tctx, "mangle_test\\");
160 0 : if (!name) {
161 0 : return NULL;
162 : }
163 :
164 0 : len = 1 + random() % NAME_LENGTH;
165 :
166 0 : name = talloc_realloc(tctx, name, char, strlen(name) + len + 6);
167 0 : if (!name) {
168 0 : return NULL;
169 : }
170 0 : p = name + strlen(name);
171 :
172 0 : for (i=0;i<len;i++) {
173 0 : p[i] = chars[random() % max_idx];
174 : }
175 :
176 0 : p[i] = 0;
177 :
178 0 : if (ISDOT(p) || ISDOTDOT(p)) {
179 0 : p[0] = '_';
180 : }
181 :
182 : /* have a high probability of a common lead char */
183 0 : if (random() % 2 == 0) {
184 0 : p[0] = 'A';
185 : }
186 :
187 : /* and a medium probability of a common lead string */
188 0 : if ((len > 5) && (random() % 10 == 0)) {
189 0 : strlcpy(p, "ABCDE", 6);
190 : }
191 :
192 : /* and a high probability of a good extension length */
193 0 : if (random() % 2 == 0) {
194 0 : char *s = strrchr(p, '.');
195 0 : if (s) {
196 0 : s[4] = 0;
197 : }
198 : }
199 :
200 0 : return name;
201 : }
202 :
203 :
204 0 : bool torture_smb2_mangle(struct torture_context *torture,
205 : struct smb2_tree *tree)
206 : {
207 : extern int torture_numops;
208 : int i;
209 : bool ok;
210 : NTSTATUS status;
211 :
212 : /* we will use an internal tdb to store the names we have used */
213 0 : tdb = tdb_open(NULL, 100000, TDB_INTERNAL, 0, 0);
214 0 : torture_assert(torture, tdb, "ERROR: Failed to open tdb\n");
215 :
216 0 : ok = smb2_util_setup_dir(torture, tree, "mangle_test");
217 0 : torture_assert(torture, ok, "smb2_util_setup_dir failed\n");
218 :
219 0 : for (i=0;i<torture_numops;i++) {
220 : char *name;
221 :
222 0 : name = gen_name(torture);
223 0 : torture_assert(torture, name, "Name allocation failed\n");
224 :
225 0 : ok = test_one(torture, tree, name);
226 0 : torture_assert(torture, ok, talloc_asprintf(torture,
227 : "Mangle names failed with %s", name));
228 0 : if (total && total % 100 == 0) {
229 0 : if (torture_setting_bool(torture, "progress", true)) {
230 0 : torture_comment(torture,
231 : "collisions %u/%u - %.2f%% (%u failures)\r",
232 0 : collisions, total, (100.0*collisions) / total, failures);
233 : }
234 : }
235 : }
236 :
237 0 : smb2_util_unlink(tree, "mangle_test\\*");
238 0 : status = smb2_util_rmdir(tree, "mangle_test");
239 0 : torture_assert_ntstatus_ok(torture, status,
240 : "ERROR: Failed to remove directory\n");
241 :
242 0 : torture_comment(torture,
243 : "\nTotal collisions %u/%u - %.2f%% (%u failures)\n",
244 0 : collisions, total, (100.0*collisions) / total, failures);
245 :
246 0 : return (failures == 0);
247 : }
|