LCOV - code coverage report
Current view: top level - lib/util - substitute.c (source / functions) Hit Total Coverage
Test: coverage report for v4-17-lts 011c5a9f Lines: 107 195 54.9 %
Date: 2026-05-29 04:08:37 Functions: 8 9 88.9 %

          Line data    Source code
       1             : /* 
       2             :    Unix SMB/CIFS implementation.
       3             :    Samba utility functions
       4             :    
       5             :    Copyright (C) Andrew Tridgell 1992-2001
       6             :    Copyright (C) Simo Sorce      2001-2002
       7             :    Copyright (C) Martin Pool     2003
       8             :    Copyright (C) James Peach     2005
       9             :    
      10             :    This program is free software; you can redistribute it and/or modify
      11             :    it under the terms of the GNU General Public License as published by
      12             :    the Free Software Foundation; either version 3 of the License, or
      13             :    (at your option) any later version.
      14             :    
      15             :    This program is distributed in the hope that it will be useful,
      16             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      17             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18             :    GNU General Public License for more details.
      19             :    
      20             :    You should have received a copy of the GNU General Public License
      21             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      22             : */
      23             : 
      24             : #include "replace.h"
      25             : #include "system/locale.h"
      26             : #include "debug.h"
      27             : #ifndef SAMBA_UTIL_CORE_ONLY
      28             : #include "lib/util/fault.h"
      29             : #include "lib/util/talloc_stack.h"
      30             : #include "charset/charset.h"
      31             : #else
      32             : #include "charset_compat.h"
      33             : #endif
      34             : #include "substitute.h"
      35             : 
      36             : /**
      37             :  * @file
      38             :  * @brief Substitute utilities.
      39             :  **/
      40             : 
      41             : static inline
      42      534360 : char mask_unsafe_character(char in,
      43             :                            bool is_last,
      44             :                            bool allow_trailing_dollar,
      45             :                            const char *unsafe_characters,
      46             :                            char safe_out)
      47             : {
      48      534360 :         const char *unsafe = NULL;
      49             : 
      50      534360 :         if (unsafe_characters == NULL) {
      51         188 :                 return in;
      52             :         }
      53             : 
      54             :         /* allow a trailing $ (as in machine accounts) */
      55      534172 :         if (allow_trailing_dollar && is_last && in == '$') {
      56           0 :                 return in;
      57             :         }
      58             : 
      59      534172 :         if (iscntrl(in)) {
      60           0 :                 return safe_out;
      61             :         }
      62             : 
      63      534172 :         unsafe = strchr(unsafe_characters, in);
      64      534172 :         if (unsafe != NULL) {
      65         522 :                 return safe_out;
      66             :         }
      67             : 
      68             :         /* ok */
      69      533650 :         return in;
      70             : }
      71             : 
      72             : /**
      73             :  Substitute a string for a pattern in another string. Make sure there is
      74             :  enough room!
      75             : 
      76             :  This routine looks for pattern in s and replaces it with
      77             :  insert. It may do multiple replacements or just one.
      78             : 
      79             :  Any of STRING_SUB_UNSAFE_CHARACTERS and any character
      80             :  caught by calling iscntrl() in the insert string are replaced with _
      81             : 
      82             :  if len==0 then the string cannot be extended. This is different from the old
      83             :  use of len==0 which was for no length checks to be done.
      84             : **/
      85             : 
      86           0 : void string_sub(char *s, const char *pattern, const char *insert, size_t len)
      87             : {
      88           0 :         const char *unsafe_characters = STRING_SUB_UNSAFE_CHARACTERS;
      89           0 :         char safe_character = '_';
      90             :         char *p;
      91             :         size_t ls, lp, li, i;
      92             : 
      93           0 :         if (!insert || !pattern || !*pattern || !s)
      94           0 :                 return;
      95             : 
      96           0 :         ls = strlen(s);
      97           0 :         lp = strlen(pattern);
      98           0 :         li = strlen(insert);
      99             : 
     100           0 :         if (len == 0)
     101           0 :                 len = ls + 1; /* len is number of *bytes* */
     102             : 
     103           0 :         while (lp <= ls && (p = strstr_m(s,pattern))) {
     104           0 :                 if (ls + li - lp >= len) {
     105           0 :                         DBG_ERR("ERROR: string overflow by "
     106             :                                 "%zu in string_sub(%.50s, %zu)\n",
     107             :                                 ls + li - lp + 1 - len,
     108             :                                 pattern,
     109             :                                 len);
     110           0 :                         break;
     111             :                 }
     112           0 :                 if (li != lp) {
     113           0 :                         memmove(p+li,p+lp,strlen(p+lp)+1);
     114             :                 }
     115           0 :                 for (i=0;i<li;i++) {
     116             :                         /*
     117             :                          * Without allow_trailing_dollar we don't
     118             :                          * need to calculate is_last...
     119             :                          */
     120           0 :                         const bool is_last = false;
     121           0 :                         const bool allow_trailing_dollar = false;
     122             : 
     123           0 :                         p[i] = mask_unsafe_character(insert[i],
     124             :                                                      is_last,
     125             :                                                      allow_trailing_dollar,
     126             :                                                      unsafe_characters,
     127             :                                                      safe_character);
     128             :                 }
     129           0 :                 s = p + li;
     130           0 :                 ls = ls + li - lp;
     131             :         }
     132             : }
     133             : 
     134             : /**
     135             :  Similar to string_sub() but allows for any character to be substituted. 
     136             :  Use with caution!
     137             :  if len==0 then the string cannot be extended. This is different from the old
     138             :  use of len==0 which was for no length checks to be done.
     139             : **/
     140             : 
     141       13932 : _PUBLIC_ void all_string_sub(char *s,const char *pattern,const char *insert, size_t len)
     142             : {
     143             :         char *p;
     144             :         size_t ls,lp,li;
     145             : 
     146       13932 :         if (!insert || !pattern || !s)
     147           0 :                 return;
     148             : 
     149       13932 :         ls = strlen(s);
     150       13932 :         lp = strlen(pattern);
     151       13932 :         li = strlen(insert);
     152             : 
     153       13932 :         if (!*pattern)
     154           0 :                 return;
     155             : 
     156       13932 :         if (len == 0)
     157       13932 :                 len = ls + 1; /* len is number of *bytes* */
     158             : 
     159       25544 :         while (lp <= ls && (p = strstr_m(s,pattern))) {
     160        1950 :                 if (ls + li - lp >= len) {
     161           0 :                         DBG_ERR("ERROR: string overflow by "
     162             :                                 "%zu in all_string_sub(%.50s, %zu)\n",
     163             :                                 ls + li - lp + 1 - len,
     164             :                                 pattern,
     165             :                                 len);
     166           0 :                         break;
     167             :                 }
     168        1950 :                 if (li != lp) {
     169          18 :                         memmove(p+li,p+lp,strlen(p+lp)+1);
     170             :                 }
     171        1950 :                 memcpy(p, insert, li);
     172        1950 :                 s = p + li;
     173        1950 :                 ls = ls + li - lp;
     174             :         }
     175             : }
     176             : 
     177             : /*
     178             :  * Internal guts of talloc_string_sub and talloc_all_string_sub.
     179             :  * talloc version of string_sub2.
     180             :  */
     181             : 
     182       64731 : bool realloc_string_sub_raw(char **_string,
     183             :                             const char *pattern,
     184             :                             const char *insert,
     185             :                             bool replace_once,
     186             :                             bool allow_trailing_dollar,
     187             :                             const char *unsafe_characters,
     188             :                             char safe_character)
     189             : {
     190       64731 :         char *p = NULL;
     191       64731 :         char *s = NULL;
     192       64731 :         char *string = NULL;
     193             :         ssize_t ls,lp,li,ld, i;
     194             : 
     195       64731 :         if (!insert || !pattern || !*pattern || !_string|| !*_string) {
     196           0 :                 return false;
     197             :         }
     198             : 
     199       64731 :         s = string = *_string;
     200             : 
     201       64731 :         ls = (ssize_t)strlen(s);
     202       64731 :         lp = (ssize_t)strlen(pattern);
     203       64731 :         li = (ssize_t)strlen(insert);
     204       64731 :         ld = li - lp;
     205             : 
     206      166032 :         while ((p = strstr_m(s,pattern))) {
     207       58077 :                 if (ld > 0) {
     208       50088 :                         ptrdiff_t offset = PTR_DIFF(s,string);
     209       50088 :                         string = talloc_realloc(NULL, string, char, ls + ld + 1);
     210       50088 :                         if (!string) {
     211           0 :                                 DBG_ERR("out of memory(realloc)!\n");
     212           0 :                                 return false;
     213             :                         }
     214       50088 :                         *_string = string;
     215       50088 :                         p = string + offset + (p - s);
     216             :                 }
     217       58077 :                 if (li != lp) {
     218       58077 :                         memmove(p+li,p+lp,strlen(p+lp)+1);
     219             :                 }
     220      592437 :                 for (i=0; i < li; i++) {
     221      534360 :                         bool is_last = (i == li - 1);
     222             : 
     223      534360 :                         p[i] = mask_unsafe_character(insert[i],
     224             :                                                      is_last,
     225             :                                                      allow_trailing_dollar,
     226             :                                                      unsafe_characters,
     227             :                                                      safe_character);
     228             :                 }
     229       58077 :                 s = p + li;
     230       58077 :                 ls += ld;
     231             : 
     232       58077 :                 if (replace_once) {
     233           0 :                         break;
     234             :                 }
     235             :         }
     236       64731 :         return true;
     237             : }
     238             : 
     239       21015 : char *talloc_string_sub2(TALLOC_CTX *mem_ctx,
     240             :                          const char *src,
     241             :                          const char *pattern,
     242             :                          const char *insert,
     243             :                          bool remove_unsafe_characters,
     244             :                          bool replace_once,
     245             :                          bool allow_trailing_dollar)
     246             : {
     247       21015 :         const char *unsafe_characters = NULL;
     248       21015 :         char safe_character = '\0';
     249       21015 :         char *string = NULL;
     250             :         bool ok;
     251             : 
     252       21015 :         if (!insert || !pattern || !*pattern || !src) {
     253           0 :                 return NULL;
     254             :         }
     255             : 
     256       21015 :         if (remove_unsafe_characters) {
     257       16924 :                 unsafe_characters = STRING_SUB_UNSAFE_CHARACTERS;
     258       16924 :                 safe_character = '_';
     259             :         }
     260             : 
     261       21015 :         string = talloc_strdup(mem_ctx, src);
     262       21015 :         if (string == NULL) {
     263           0 :                 DBG_ERR("out of memory, talloc_strdup(src)!\n");
     264           0 :                 return NULL;
     265             :         }
     266             : 
     267       21015 :         ok = realloc_string_sub_raw(&string,
     268             :                                     pattern,
     269             :                                     insert,
     270             :                                     replace_once,
     271             :                                     allow_trailing_dollar,
     272             :                                     unsafe_characters,
     273             :                                     safe_character);
     274       21015 :         if (!ok) {
     275           0 :                 TALLOC_FREE(string);
     276           0 :                 DBG_ERR("out of memory, realloc_string_sub_raw()!\n");
     277           0 :                 return NULL;
     278             :         }
     279             : 
     280       21015 :         return string;
     281             : }
     282             : 
     283             : /* Same as string_sub, but returns a talloc'ed string */
     284             : 
     285       16924 : char *talloc_string_sub(TALLOC_CTX *mem_ctx,
     286             :                         const char *src,
     287             :                         const char *pattern,
     288             :                         const char *insert)
     289             : {
     290       16924 :         return talloc_string_sub2(mem_ctx, src, pattern, insert,
     291             :                         true, false, false);
     292             : }
     293             : 
     294        4091 : char *talloc_all_string_sub(TALLOC_CTX *ctx,
     295             :                                 const char *src,
     296             :                                 const char *pattern,
     297             :                                 const char *insert)
     298             : {
     299        4091 :         return talloc_string_sub2(ctx, src, pattern, insert,
     300             :                         false, false, false);
     301             : }
     302             : 
     303             : #ifndef SAMBA_UTIL_CORE_ONLY
     304             : 
     305        3505 : bool talloc_string_sub_mixed_quoting(const char *full_cmd, char variable_char)
     306             : {
     307             :         /*
     308             :          * Try to make sure talloc_string_sub_unsafe()
     309             :          * won't return NULL, instead talloc_stackframe_pool()
     310             :          * would panic
     311             :          */
     312        3505 :         size_t cmd_len = full_cmd != NULL ? strlen(full_cmd) : 0;
     313        3505 :         size_t pool_size = 512 + cmd_len;
     314        3505 :         TALLOC_CTX *frame = talloc_stackframe_pool(pool_size);
     315        3505 :         char *cmd = NULL;
     316        3505 :         bool modified = false;
     317        3505 :         bool masked = false;
     318        3505 :         bool mixed_fallback = false;
     319             : 
     320        3505 :         cmd = talloc_string_sub_unsafe(frame,
     321             :                                        full_cmd,
     322             :                                        variable_char,
     323             :                                        "U",  /* unsafe_value */
     324             :                                        "'\"%", /* unsafe_characters */
     325             :                                        '_',    /* safe_character */
     326             :                                        "F",  /* fallback_value */
     327             :                                        &modified,
     328             :                                        &masked,
     329             :                                        &mixed_fallback);
     330        3505 :         if (cmd == NULL) {
     331           0 :                 mixed_fallback = false;
     332             :         }
     333        3505 :         TALLOC_FREE(frame);
     334        3505 :         return mixed_fallback;
     335             : }
     336             : 
     337        3505 : char *talloc_string_sub_unsafe(TALLOC_CTX *mem_ctx,
     338             :                                const char *orig_cmd,
     339             :                                char variable_char,
     340             :                                const char *unsafe_value,
     341             :                                const char *unsafe_characters,
     342             :                                char safe_character,
     343             :                                const char *fallback_value,
     344             :                                bool *_modified,
     345             :                                bool *_masked,
     346             :                                bool *_mixed_fallback)
     347             : {
     348        3505 :         TALLOC_CTX *frame = talloc_stackframe();
     349        3505 :         const char variable[3] =
     350             :                 { '%', variable_char, '\0' };
     351        3505 :         const char variable_s_quoted[5] =
     352             :                 { '\'', '%', variable_char, '\'', '\0' };
     353        3505 :         const char variable_d_quoted[5] =
     354             :                 { '"', '%', variable_char, '"', '\0' };
     355        3505 :         char *cmd = NULL;
     356        3505 :         char *masked_value = NULL;
     357        3505 :         char *quoted_value = NULL;
     358             :         bool has_s_quotes;
     359             :         bool has_d_quotes;
     360             :         bool has_variable;
     361             :         bool has_variable_s_quoted;
     362             :         bool has_variable_d_quoted;
     363        3505 :         bool modified = false;
     364        3505 :         bool masked = false;
     365        3505 :         bool mixed_fallback = false;
     366             :         bool ok;
     367             : 
     368             :         /*
     369             :          * The unsafe_characters argument should contain
     370             :          * single and double quotes.
     371             :          * Otherwise We can't safely handle this.
     372             :          */
     373        3505 :         SMB_ASSERT(unsafe_characters != NULL);
     374        3505 :         SMB_ASSERT(strchr(unsafe_characters, '\'') != NULL);
     375        3505 :         SMB_ASSERT(strchr(unsafe_characters, '"') != NULL);
     376        3505 :         SMB_ASSERT(strchr(unsafe_characters, '%') != NULL);
     377             : 
     378        3505 :         cmd = talloc_strdup(mem_ctx, orig_cmd);
     379        3505 :         if (cmd == NULL) {
     380           0 :                 TALLOC_FREE(frame);
     381           0 :                 return NULL;
     382             :         }
     383        3505 :         cmd = talloc_steal(frame, cmd);
     384             : 
     385        3505 :         has_variable = strstr(orig_cmd, variable) != NULL;
     386        3505 :         if (!has_variable) {
     387             :                 /*
     388             :                  * Nothing to do...
     389             :                  */
     390        3505 :                 goto done;
     391             :         }
     392           0 :         modified = true;
     393             : 
     394             :         /*
     395             :          * Replace all unsafe characters as well as control
     396             :          * characters.
     397             :          *
     398             :          * Note that we start with masked_value = "%u"
     399             :          * and then replace "%u" with unsafe_value,
     400             :          * as a result we have a masked version of
     401             :          * unsafe_value.
     402             :          *
     403             :          * And don't allow option injected like
     404             :          *
     405             :          * '-h value'
     406             :          * '--help value'
     407             :          *
     408             :          */
     409           0 :         masked_value = talloc_strdup(frame, variable);
     410           0 :         if (masked_value == NULL) {
     411           0 :                 goto nomem;
     412             :         }
     413           0 :         ok = realloc_string_sub_raw(&masked_value,
     414             :                                     variable,
     415             :                                     unsafe_value,
     416             :                                     false, /* replace_once */
     417             :                                     false, /* allow_trailing_dollar */
     418             :                                     unsafe_characters,
     419             :                                     safe_character);
     420           0 :         if (!ok) {
     421           0 :                 goto nomem;
     422             :         }
     423           0 :         if (masked_value[0] == '-') {
     424           0 :                 masked_value[0] = safe_character;
     425             :         }
     426           0 :         masked = strcmp(masked_value, unsafe_value) != 0;
     427             : 
     428           0 : retry:
     429             : 
     430           0 :         has_s_quotes = strchr(cmd, '\'') != NULL;
     431           0 :         has_d_quotes = strchr(cmd, '"') != NULL;
     432           0 :         has_variable = strstr(cmd, variable) != NULL;
     433           0 :         has_variable_s_quoted = strstr(cmd, variable_s_quoted) != NULL;
     434           0 :         has_variable_d_quoted = strstr(cmd, variable_d_quoted) != NULL;
     435             : 
     436           0 :         if (has_variable_s_quoted) {
     437             :                 /*
     438             :                  * In smb.conf we have something like
     439             :                  *
     440             :                  * some script = /usr/bin/script '%u'
     441             :                  *
     442             :                  * It is safe to replace '%u' (or '%J' etc, depending
     443             :                  * on variable_char) with '<masked_value>' if
     444             :                  * masked_value does not contain single quotes. We
     445             :                  * have checked that.
     446             :                  */
     447             : 
     448           0 :                 if (quoted_value == NULL) {
     449           0 :                         quoted_value = talloc_asprintf(frame, "'%s'",
     450             :                                                        masked_value);
     451           0 :                         if (quoted_value == NULL) {
     452           0 :                                 goto nomem;
     453             :                         }
     454             :                 }
     455             : 
     456           0 :                 ok = realloc_string_sub_raw(&cmd,
     457             :                                             variable_s_quoted,
     458             :                                             quoted_value,
     459             :                                             false, /* replace_once */
     460             :                                             false, /* allow_trailing_dollar */
     461             :                                             NULL,  /* unsafe_characters */
     462             :                                             '\0'); /* safe_character */
     463           0 :                 if (!ok) {
     464           0 :                         goto nomem;
     465             :                 }
     466             : 
     467           0 :                 goto retry;
     468             :         }
     469             : 
     470           0 :         if (has_variable_d_quoted && !has_s_quotes) {
     471             :                 /*
     472             :                  * replace the "%u"
     473             :                  *
     474             :                  * some script = /usr/bin/script "%u"
     475             :                  *
     476             :                  * with '%u' and try the '%u' -> 'variable' substitution
     477             :                  * again.
     478             :                  */
     479             : 
     480           0 :                 ok = realloc_string_sub_raw(&cmd,
     481             :                                             variable_d_quoted,
     482             :                                             variable_s_quoted,
     483             :                                             false, /* replace_once */
     484             :                                             false, /* allow_trailing_dollar */
     485             :                                             NULL,  /* unsafe_characters */
     486             :                                             '\0'); /* safe_character */
     487           0 :                 if (!ok) {
     488           0 :                         goto nomem;
     489             :                 }
     490             : 
     491           0 :                 goto retry;
     492             :         }
     493             : 
     494           0 :         if (has_variable && !has_s_quotes && !has_d_quotes) {
     495             :                 /*
     496             :                  * In this case:
     497             :                  *
     498             :                  * some script = /usr/bin/script %u
     499             :                  *
     500             :                  * we can safely substitute %u -> '%u' and try the
     501             :                  * single quote test again.
     502             :                  */
     503             : 
     504           0 :                 ok = realloc_string_sub_raw(&cmd,
     505             :                                             variable,
     506             :                                             variable_s_quoted,
     507             :                                             false, /* replace_once */
     508             :                                             false, /* allow_trailing_dollar */
     509             :                                             NULL,  /* unsafe_characters */
     510             :                                             '\0'); /* safe_character */
     511           0 :                 if (!ok) {
     512           0 :                         goto nomem;
     513             :                 }
     514             : 
     515           0 :                 goto retry;
     516             :         }
     517             : 
     518           0 :         if (has_variable) {
     519             :                 /*
     520             :                  * There are single or double quotes, but not tightly
     521             :                  * bound around a %u.
     522             :                  *
     523             :                  * Or there's a mix of single and double quotes.
     524             :                  *
     525             :                  * We just use a generic fallback value.
     526             :                  * and let the caller warn about this
     527             :                  * and give the admin a hind to fix the smb.conf
     528             :                  * option.
     529             :                  */
     530           0 :                 mixed_fallback = true;
     531             : 
     532           0 :                 ok = realloc_string_sub_raw(&cmd,
     533             :                                             variable,
     534             :                                             fallback_value,
     535             :                                             false, /* replace_once */
     536             :                                             false, /* allow_trailing_dollar */
     537             :                                             NULL,  /* unsafe_characters */
     538             :                                             '\0'); /* safe_character */
     539           0 :                 if (!ok) {
     540           0 :                         goto nomem;
     541             :                 }
     542             :         }
     543             : 
     544           0 : done:
     545        3505 :         *_modified = modified;
     546        3505 :         *_masked = masked;
     547        3505 :         *_mixed_fallback = mixed_fallback;
     548        3505 :         cmd = talloc_steal(mem_ctx, cmd);
     549        3505 :         TALLOC_FREE(frame);
     550        3505 :         return cmd;
     551             : 
     552           0 : nomem:
     553           0 :         *_modified = false;
     554           0 :         *_masked = false;
     555           0 :         *_mixed_fallback = false;
     556           0 :         TALLOC_FREE(frame);
     557           0 :         return NULL;
     558             : }
     559             : #endif /* ! SAMBA_UTIL_CORE_ONLY */

Generated by: LCOV version 1.13