Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : Samba utility functions
4 : Copyright (C) Stefan Metzmacher 2021
5 :
6 : This program is free software; you can redistribute it and/or modify
7 : it under the terms of the GNU General Public License as published by
8 : the Free Software Foundation; either version 3 of the License, or
9 : (at your option) any later version.
10 :
11 : This program is distributed in the hope that it will be useful,
12 : but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : GNU General Public License for more details.
15 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program. If not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : #include "includes.h"
21 : #include "lib/util_matching.h"
22 : #include "lib/util/string_wrappers.h"
23 :
24 : struct samba_path_matching_entry {
25 : const char *name;
26 : bool is_wild;
27 : regex_t re;
28 : };
29 :
30 : struct samba_path_matching_result {
31 : ssize_t replace_start;
32 : ssize_t replace_end;
33 : bool match;
34 : };
35 :
36 : struct samba_path_matching {
37 : bool case_sensitive;
38 : NTSTATUS (*matching_fn)(const struct samba_path_matching *pm,
39 : const struct samba_path_matching_entry *e,
40 : const char *namecomponent,
41 : struct samba_path_matching_result *result);
42 : size_t num_entries;
43 : struct samba_path_matching_entry *entries;
44 : };
45 :
46 7 : static NTSTATUS samba_path_matching_split(TALLOC_CTX *mem_ctx,
47 : const char *namelist_in,
48 : struct samba_path_matching **ppm)
49 : {
50 7 : TALLOC_CTX *frame = talloc_stackframe();
51 7 : char *name_end = NULL;
52 7 : char *namelist = NULL;
53 7 : char *namelist_end = NULL;
54 7 : char *nameptr = NULL;
55 7 : struct samba_path_matching *pm = NULL;
56 7 : size_t num_entries = 0;
57 7 : struct samba_path_matching_entry *entries = NULL;
58 :
59 7 : *ppm = NULL;
60 :
61 7 : pm = talloc_zero(mem_ctx, struct samba_path_matching);
62 7 : if (pm == NULL) {
63 0 : TALLOC_FREE(frame);
64 0 : return NT_STATUS_NO_MEMORY;
65 : }
66 7 : talloc_reparent(mem_ctx, frame, pm);
67 :
68 7 : namelist = talloc_strdup(frame, namelist_in);
69 7 : if (namelist == NULL) {
70 0 : TALLOC_FREE(frame);
71 0 : return NT_STATUS_NO_MEMORY;
72 : }
73 7 : nameptr = namelist;
74 :
75 7 : namelist_end = &namelist[strlen(namelist)];
76 :
77 : /*
78 : * We need to make two passes over the string. The
79 : * first to count the number of elements, the second
80 : * to split it.
81 : *
82 : * The 1st time entries is NULL.
83 : * the 2nd time entries is allocated.
84 : */
85 0 : again:
86 54 : while (nameptr <= namelist_end) {
87 : /* anything left? */
88 54 : if (*nameptr == '\0') {
89 0 : break;
90 : }
91 :
92 40 : if (*nameptr == '/') {
93 : /* cope with multiple (useless) /s) */
94 14 : nameptr++;
95 14 : continue;
96 : }
97 :
98 : /* find the next '/' or consume remaining */
99 26 : name_end = strchr_m(nameptr, '/');
100 26 : if (entries != NULL) {
101 13 : if (name_end != NULL) {
102 13 : *name_end = '\0';
103 : }
104 13 : entries[num_entries].name = talloc_strdup(entries,
105 : nameptr);
106 13 : if (entries[num_entries].name == NULL) {
107 0 : TALLOC_FREE(frame);
108 0 : return NT_STATUS_NO_MEMORY;
109 : }
110 : }
111 26 : num_entries++;
112 26 : if (name_end != NULL) {
113 : /* next segment please */
114 26 : nameptr = name_end + 1;
115 26 : continue;
116 : }
117 :
118 : /* no entries remaining */
119 0 : break;
120 : }
121 :
122 14 : if (num_entries == 0) {
123 : /*
124 : * No entries in the first round => we're done
125 : */
126 0 : goto done;
127 : }
128 :
129 14 : if (entries != NULL) {
130 : /*
131 : * We finished the 2nd round => we're done
132 : */
133 7 : goto done;
134 : }
135 :
136 : /*
137 : * Now allocate the array and loop again
138 : * in order to split the names.
139 : */
140 7 : entries = talloc_zero_array(pm,
141 : struct samba_path_matching_entry,
142 : num_entries);
143 7 : if (entries == NULL) {
144 0 : TALLOC_FREE(frame);
145 0 : return NT_STATUS_NO_MEMORY;
146 : }
147 7 : num_entries = 0;
148 7 : nameptr = namelist;
149 7 : goto again;
150 :
151 7 : done:
152 7 : pm->num_entries = num_entries;
153 7 : pm->entries = entries;
154 7 : *ppm = talloc_move(mem_ctx, &pm);
155 7 : TALLOC_FREE(frame);
156 7 : return NT_STATUS_OK;
157 : };
158 :
159 33 : static NTSTATUS samba_path_create_mswild_fn(const struct samba_path_matching *pm,
160 : const struct samba_path_matching_entry *e,
161 : const char *namecomponent,
162 : struct samba_path_matching_result *result)
163 : {
164 33 : bool match = false;
165 :
166 33 : if (e->is_wild) {
167 25 : match = mask_match(namecomponent, e->name, pm->case_sensitive);
168 8 : } else if (pm->case_sensitive) {
169 5 : match = (strcmp(namecomponent, e->name) == 0);
170 : } else {
171 3 : match = (strcasecmp_m(namecomponent, e->name) == 0);
172 : }
173 :
174 33 : *result = (struct samba_path_matching_result) {
175 : .match = match,
176 : .replace_start = -1,
177 : .replace_end = -1,
178 : };
179 :
180 33 : return NT_STATUS_OK;
181 : }
182 :
183 2 : NTSTATUS samba_path_matching_mswild_create(TALLOC_CTX *mem_ctx,
184 : bool case_sensitive,
185 : const char *namelist_in,
186 : struct samba_path_matching **ppm)
187 : {
188 2 : NTSTATUS status;
189 2 : TALLOC_CTX *frame = talloc_stackframe();
190 2 : struct samba_path_matching *pm = NULL;
191 2 : size_t i;
192 :
193 2 : *ppm = NULL;
194 :
195 2 : status = samba_path_matching_split(mem_ctx, namelist_in, &pm);
196 2 : if (!NT_STATUS_IS_OK(status)) {
197 0 : TALLOC_FREE(frame);
198 0 : return status;
199 : }
200 2 : talloc_reparent(mem_ctx, frame, pm);
201 :
202 10 : for (i = 0; i < pm->num_entries; i++) {
203 6 : struct samba_path_matching_entry *e = &pm->entries[i];
204 :
205 6 : e->is_wild = ms_has_wild(e->name);
206 : }
207 :
208 2 : pm->case_sensitive = case_sensitive;
209 2 : pm->matching_fn = samba_path_create_mswild_fn;
210 2 : *ppm = talloc_move(mem_ctx, &pm);
211 2 : TALLOC_FREE(frame);
212 2 : return NT_STATUS_OK;
213 : };
214 :
215 1 : static int samba_path_matching_regex_sub1_destructor(struct samba_path_matching *pm)
216 : {
217 1 : ssize_t i;
218 :
219 4 : for (i = 0; i < pm->num_entries; i++) {
220 3 : struct samba_path_matching_entry *e = &pm->entries[i];
221 :
222 3 : regfree(&e->re);
223 : }
224 :
225 1 : pm->num_entries = 0;
226 :
227 1 : return 0;
228 : }
229 :
230 18 : static NTSTATUS samba_path_create_regex_sub1_fn(const struct samba_path_matching *pm,
231 : const struct samba_path_matching_entry *e,
232 : const char *namecomponent,
233 : struct samba_path_matching_result *result)
234 : {
235 18 : if (e->re.re_nsub == 1) {
236 18 : regmatch_t matches[2] = { };
237 18 : int ret;
238 :
239 18 : ret = regexec(&e->re, namecomponent, 2, matches, 0);
240 18 : if (ret == 0) {
241 3 : *result = (struct samba_path_matching_result) {
242 : .match = true,
243 3 : .replace_start = matches[1].rm_so,
244 3 : .replace_end = matches[1].rm_eo,
245 : };
246 :
247 3 : return NT_STATUS_OK;
248 : }
249 : }
250 :
251 15 : *result = (struct samba_path_matching_result) {
252 : .match = false,
253 : .replace_start = -1,
254 : .replace_end = -1,
255 : };
256 :
257 15 : return NT_STATUS_OK;
258 : }
259 :
260 5 : NTSTATUS samba_path_matching_regex_sub1_create(TALLOC_CTX *mem_ctx,
261 : const char *namelist_in,
262 : struct samba_path_matching **ppm)
263 : {
264 5 : NTSTATUS status;
265 5 : TALLOC_CTX *frame = talloc_stackframe();
266 5 : struct samba_path_matching *pm = NULL;
267 5 : ssize_t i;
268 :
269 5 : *ppm = NULL;
270 :
271 5 : status = samba_path_matching_split(mem_ctx, namelist_in, &pm);
272 5 : if (!NT_STATUS_IS_OK(status)) {
273 0 : TALLOC_FREE(frame);
274 0 : return status;
275 : }
276 5 : talloc_reparent(mem_ctx, frame, pm);
277 :
278 13 : for (i = 0; i < pm->num_entries; i++) {
279 7 : struct samba_path_matching_entry *e = &pm->entries[i];
280 7 : int ret;
281 :
282 7 : ret = regcomp(&e->re, e->name, 0);
283 7 : if (ret != 0) {
284 2 : fstring buf = { 0,};
285 :
286 2 : regerror(ret, &e->re, buf, sizeof(buf));
287 :
288 2 : DBG_ERR("idx[%zu] regcomp: /%s/ - %d - %s\n",
289 : i, e->name, ret, buf);
290 :
291 2 : status = NT_STATUS_INVALID_PARAMETER;
292 2 : i--;
293 2 : goto cleanup;
294 : }
295 :
296 5 : if (e->re.re_nsub != 1) {
297 2 : DBG_ERR("idx[%zu] regcomp: /%s/ - re_nsub[%zu] != 1\n",
298 : i, e->name, e->re.re_nsub);
299 2 : status = NT_STATUS_INVALID_PARAMETER;
300 2 : goto cleanup;
301 : }
302 : }
303 :
304 1 : talloc_set_destructor(pm, samba_path_matching_regex_sub1_destructor);
305 :
306 1 : pm->case_sensitive = true;
307 1 : pm->matching_fn = samba_path_create_regex_sub1_fn;
308 1 : *ppm = talloc_move(mem_ctx, &pm);
309 1 : TALLOC_FREE(frame);
310 1 : return NT_STATUS_OK;
311 :
312 0 : cleanup:
313 6 : for (; i >= 0; i--) {
314 2 : struct samba_path_matching_entry *e = &pm->entries[i];
315 :
316 2 : regfree(&e->re);
317 : }
318 :
319 4 : TALLOC_FREE(frame);
320 4 : return status;
321 : };
322 :
323 21 : NTSTATUS samba_path_matching_check_last_component(struct samba_path_matching *pm,
324 : const char *name,
325 : ssize_t *p_match_idx,
326 : ssize_t *p_replace_start,
327 : ssize_t *p_replace_end)
328 : {
329 21 : struct samba_path_matching_result result = {
330 : .match = false,
331 : .replace_start = -1,
332 : .replace_end = -1,
333 : };
334 21 : ssize_t match_idx = -1;
335 21 : NTSTATUS status = NT_STATUS_OK;
336 21 : const char *last_component = NULL;
337 21 : size_t i;
338 :
339 21 : if (pm->num_entries == 0) {
340 0 : goto finish;
341 : }
342 :
343 : /* Get the last component of the unix name. */
344 21 : last_component = strrchr_m(name, '/');
345 21 : if (last_component == NULL) {
346 0 : last_component = name;
347 : } else {
348 21 : last_component++; /* Go past '/' */
349 : }
350 :
351 60 : for (i = 0; i < pm->num_entries; i++) {
352 51 : struct samba_path_matching_entry *e = &pm->entries[i];
353 :
354 51 : status = pm->matching_fn(pm, e, last_component, &result);
355 51 : if (!NT_STATUS_IS_OK(status)) {
356 0 : result = (struct samba_path_matching_result) {
357 : .match = false,
358 : .replace_start = -1,
359 : .replace_end = -1,
360 : };
361 0 : goto finish;
362 : }
363 :
364 51 : if (result.match) {
365 12 : match_idx = i;
366 12 : goto finish;
367 : }
368 : }
369 :
370 9 : finish:
371 21 : *p_match_idx = match_idx;
372 21 : if (p_replace_start != NULL) {
373 21 : size_t last_ofs = 0;
374 :
375 21 : if (result.replace_start >= 0) {
376 3 : last_ofs = PTR_DIFF(last_component, name);
377 : }
378 :
379 21 : *p_replace_start = last_ofs + result.replace_start;
380 : }
381 21 : if (p_replace_end != NULL) {
382 21 : size_t last_ofs = 0;
383 :
384 21 : if (result.replace_end >= 0) {
385 3 : last_ofs = PTR_DIFF(last_component, name);
386 : }
387 :
388 21 : *p_replace_end = last_ofs + result.replace_end;
389 : }
390 21 : return status;
391 : }
|