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 */
|