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