LCOV - code coverage report
Current view: top level - source3/lib - util_matching.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 156 177 88.1 %
Date: 2024-04-21 15:09:00 Functions: 7 7 100.0 %

          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             : }

Generated by: LCOV version 1.14