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 0 : static NTSTATUS samba_path_matching_split(TALLOC_CTX *mem_ctx,
47 : const char *namelist_in,
48 : struct samba_path_matching **ppm)
49 : {
50 0 : TALLOC_CTX *frame = talloc_stackframe();
51 0 : char *name_end = NULL;
52 0 : char *namelist = NULL;
53 0 : char *namelist_end = NULL;
54 0 : char *nameptr = NULL;
55 0 : struct samba_path_matching *pm = NULL;
56 0 : size_t num_entries = 0;
57 0 : struct samba_path_matching_entry *entries = NULL;
58 :
59 0 : *ppm = NULL;
60 :
61 0 : pm = talloc_zero(mem_ctx, struct samba_path_matching);
62 0 : if (pm == NULL) {
63 0 : TALLOC_FREE(frame);
64 0 : return NT_STATUS_NO_MEMORY;
65 : }
66 0 : talloc_reparent(mem_ctx, frame, pm);
67 :
68 0 : namelist = talloc_strdup(frame, namelist_in);
69 0 : if (namelist == NULL) {
70 0 : TALLOC_FREE(frame);
71 0 : return NT_STATUS_NO_MEMORY;
72 : }
73 0 : nameptr = namelist;
74 :
75 0 : 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 0 : while (nameptr <= namelist_end) {
87 : /* anything left? */
88 0 : if (*nameptr == '\0') {
89 0 : break;
90 : }
91 :
92 0 : if (*nameptr == '/') {
93 : /* cope with multiple (useless) /s) */
94 0 : nameptr++;
95 0 : continue;
96 : }
97 :
98 : /* find the next '/' or consume remaining */
99 0 : name_end = strchr_m(nameptr, '/');
100 0 : if (entries != NULL) {
101 0 : if (name_end != NULL) {
102 0 : *name_end = '\0';
103 : }
104 0 : entries[num_entries].name = talloc_strdup(entries,
105 : nameptr);
106 0 : if (entries[num_entries].name == NULL) {
107 0 : TALLOC_FREE(frame);
108 0 : return NT_STATUS_NO_MEMORY;
109 : }
110 : }
111 0 : num_entries++;
112 0 : if (name_end != NULL) {
113 : /* next segment please */
114 0 : nameptr = name_end + 1;
115 0 : continue;
116 : }
117 :
118 : /* no entries remaining */
119 0 : break;
120 : }
121 :
122 0 : if (num_entries == 0) {
123 : /*
124 : * No entries in the first round => we're done
125 : */
126 0 : goto done;
127 : }
128 :
129 0 : if (entries != NULL) {
130 : /*
131 : * We finished the 2nd round => we're done
132 : */
133 0 : goto done;
134 : }
135 :
136 : /*
137 : * Now allocate the array and loop again
138 : * in order to split the names.
139 : */
140 0 : entries = talloc_zero_array(pm,
141 : struct samba_path_matching_entry,
142 : num_entries);
143 0 : if (entries == NULL) {
144 0 : TALLOC_FREE(frame);
145 0 : return NT_STATUS_NO_MEMORY;
146 : }
147 0 : num_entries = 0;
148 0 : nameptr = namelist;
149 0 : goto again;
150 :
151 0 : done:
152 0 : pm->num_entries = num_entries;
153 0 : pm->entries = entries;
154 0 : *ppm = talloc_move(mem_ctx, &pm);
155 0 : TALLOC_FREE(frame);
156 0 : return NT_STATUS_OK;
157 : };
158 :
159 0 : 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 0 : bool match = false;
165 :
166 0 : if (e->is_wild) {
167 0 : match = mask_match(namecomponent, e->name, pm->case_sensitive);
168 0 : } else if (pm->case_sensitive) {
169 0 : match = (strcmp(namecomponent, e->name) == 0);
170 : } else {
171 0 : match = (strcasecmp_m(namecomponent, e->name) == 0);
172 : }
173 :
174 0 : *result = (struct samba_path_matching_result) {
175 : .match = match,
176 : .replace_start = -1,
177 : .replace_end = -1,
178 : };
179 :
180 0 : return NT_STATUS_OK;
181 : }
182 :
183 0 : 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 : NTSTATUS status;
189 0 : TALLOC_CTX *frame = talloc_stackframe();
190 0 : struct samba_path_matching *pm = NULL;
191 : size_t i;
192 :
193 0 : *ppm = NULL;
194 :
195 0 : status = samba_path_matching_split(mem_ctx, namelist_in, &pm);
196 0 : if (!NT_STATUS_IS_OK(status)) {
197 0 : TALLOC_FREE(frame);
198 0 : return status;
199 : }
200 0 : talloc_reparent(mem_ctx, frame, pm);
201 :
202 0 : for (i = 0; i < pm->num_entries; i++) {
203 0 : struct samba_path_matching_entry *e = &pm->entries[i];
204 :
205 0 : e->is_wild = ms_has_wild(e->name);
206 : }
207 :
208 0 : pm->case_sensitive = case_sensitive;
209 0 : pm->matching_fn = samba_path_create_mswild_fn;
210 0 : *ppm = talloc_move(mem_ctx, &pm);
211 0 : TALLOC_FREE(frame);
212 0 : return NT_STATUS_OK;
213 : };
214 :
215 0 : static int samba_path_matching_regex_sub1_destructor(struct samba_path_matching *pm)
216 : {
217 : ssize_t i;
218 :
219 0 : for (i = 0; i < pm->num_entries; i++) {
220 0 : struct samba_path_matching_entry *e = &pm->entries[i];
221 :
222 0 : regfree(&e->re);
223 : }
224 :
225 0 : pm->num_entries = 0;
226 :
227 0 : return 0;
228 : }
229 :
230 0 : 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 0 : if (e->re.re_nsub == 1) {
236 0 : regmatch_t matches[2] = { };
237 : int ret;
238 :
239 0 : ret = regexec(&e->re, namecomponent, 2, matches, 0);
240 0 : if (ret == 0) {
241 0 : *result = (struct samba_path_matching_result) {
242 : .match = true,
243 0 : .replace_start = matches[1].rm_so,
244 0 : .replace_end = matches[1].rm_eo,
245 : };
246 :
247 0 : return NT_STATUS_OK;
248 : }
249 : }
250 :
251 0 : *result = (struct samba_path_matching_result) {
252 : .match = false,
253 : .replace_start = -1,
254 : .replace_end = -1,
255 : };
256 :
257 0 : return NT_STATUS_OK;
258 : }
259 :
260 0 : NTSTATUS samba_path_matching_regex_sub1_create(TALLOC_CTX *mem_ctx,
261 : const char *namelist_in,
262 : struct samba_path_matching **ppm)
263 : {
264 : NTSTATUS status;
265 0 : TALLOC_CTX *frame = talloc_stackframe();
266 0 : struct samba_path_matching *pm = NULL;
267 : ssize_t i;
268 :
269 0 : *ppm = NULL;
270 :
271 0 : status = samba_path_matching_split(mem_ctx, namelist_in, &pm);
272 0 : if (!NT_STATUS_IS_OK(status)) {
273 0 : TALLOC_FREE(frame);
274 0 : return status;
275 : }
276 0 : talloc_reparent(mem_ctx, frame, pm);
277 :
278 0 : for (i = 0; i < pm->num_entries; i++) {
279 0 : struct samba_path_matching_entry *e = &pm->entries[i];
280 : int ret;
281 :
282 0 : ret = regcomp(&e->re, e->name, 0);
283 0 : if (ret != 0) {
284 0 : fstring buf = { 0,};
285 :
286 0 : regerror(ret, &e->re, buf, sizeof(buf));
287 :
288 0 : DBG_ERR("idx[%zu] regcomp: /%s/ - %d - %s\n",
289 : i, e->name, ret, buf);
290 :
291 0 : status = NT_STATUS_INVALID_PARAMETER;
292 0 : i--;
293 0 : goto cleanup;
294 : }
295 :
296 0 : if (e->re.re_nsub != 1) {
297 0 : DBG_ERR("idx[%zu] regcomp: /%s/ - re_nsub[%zu] != 1\n",
298 : i, e->name, e->re.re_nsub);
299 0 : status = NT_STATUS_INVALID_PARAMETER;
300 0 : goto cleanup;
301 : }
302 : }
303 :
304 0 : talloc_set_destructor(pm, samba_path_matching_regex_sub1_destructor);
305 :
306 0 : pm->case_sensitive = true;
307 0 : pm->matching_fn = samba_path_create_regex_sub1_fn;
308 0 : *ppm = talloc_move(mem_ctx, &pm);
309 0 : TALLOC_FREE(frame);
310 0 : return NT_STATUS_OK;
311 :
312 0 : cleanup:
313 0 : for (; i >= 0; i--) {
314 0 : struct samba_path_matching_entry *e = &pm->entries[i];
315 :
316 0 : regfree(&e->re);
317 : }
318 :
319 0 : TALLOC_FREE(frame);
320 0 : return status;
321 : };
322 :
323 0 : 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 0 : struct samba_path_matching_result result = {
330 : .match = false,
331 : .replace_start = -1,
332 : .replace_end = -1,
333 : };
334 0 : ssize_t match_idx = -1;
335 0 : NTSTATUS status = NT_STATUS_OK;
336 0 : const char *last_component = NULL;
337 : size_t i;
338 :
339 0 : if (pm->num_entries == 0) {
340 0 : goto finish;
341 : }
342 :
343 : /* Get the last component of the unix name. */
344 0 : last_component = strrchr_m(name, '/');
345 0 : if (last_component == NULL) {
346 0 : last_component = name;
347 : } else {
348 0 : last_component++; /* Go past '/' */
349 : }
350 :
351 0 : for (i = 0; i < pm->num_entries; i++) {
352 0 : struct samba_path_matching_entry *e = &pm->entries[i];
353 :
354 0 : status = pm->matching_fn(pm, e, last_component, &result);
355 0 : 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 0 : if (result.match) {
365 0 : match_idx = i;
366 0 : goto finish;
367 : }
368 : }
369 :
370 0 : finish:
371 0 : *p_match_idx = match_idx;
372 0 : if (p_replace_start != NULL) {
373 0 : size_t last_ofs = 0;
374 :
375 0 : if (result.replace_start >= 0) {
376 0 : last_ofs = PTR_DIFF(last_component, name);
377 : }
378 :
379 0 : *p_replace_start = last_ofs + result.replace_start;
380 : }
381 0 : if (p_replace_end != NULL) {
382 0 : size_t last_ofs = 0;
383 :
384 0 : if (result.replace_end >= 0) {
385 0 : last_ofs = PTR_DIFF(last_component, name);
386 : }
387 :
388 0 : *p_replace_end = last_ofs + result.replace_end;
389 : }
390 0 : return status;
391 : }
|