Coverage Report

Created: 2025-11-16 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source3/lib/substitute.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
   string substitution functions
4
   Copyright (C) Andrew Tridgell 1992-2000
5
   Copyright (C) Gerald Carter   2006
6
7
   This program is free software; you can redistribute it and/or modify
8
   it under the terms of the GNU General Public License as published by
9
   the Free Software Foundation; either version 3 of the License, or
10
   (at your option) any later version.
11
12
   This program is distributed in the hope that it will be useful,
13
   but WITHOUT ANY WARRANTY; without even the implied warranty of
14
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
   GNU General Public License for more details.
16
17
   You should have received a copy of the GNU General Public License
18
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
*/
20
21
22
#include "includes.h"
23
#include "substitute.h"
24
#include "system/passwd.h"
25
#include "secrets.h"
26
#include "auth.h"
27
#include "lib/util/string_wrappers.h"
28
29
/* Max DNS name is 253 + '\0' */
30
#define MACHINE_NAME_SIZE 254
31
32
static char local_machine[MACHINE_NAME_SIZE];
33
static char remote_machine[MACHINE_NAME_SIZE];
34
35
userdom_struct current_user_info;
36
static fstring remote_proto="UNKNOWN";
37
38
void set_remote_proto(const char *proto)
39
0
{
40
0
  fstrcpy(remote_proto, proto);
41
0
}
42
43
/**
44
 * Set the 'local' machine name
45
 * @param local_name the name we are being called
46
 * @param if this is the 'final' name for us, not be be changed again
47
 */
48
bool set_local_machine_name(const char *local_name, bool perm)
49
0
{
50
0
  static bool already_perm = false;
51
0
  char tmp[MACHINE_NAME_SIZE];
52
53
0
  if (already_perm) {
54
0
    return true;
55
0
  }
56
57
0
  strlcpy(tmp, local_name, sizeof(tmp));
58
0
  trim_char(tmp, ' ', ' ');
59
60
0
  alpha_strcpy(local_machine,
61
0
         tmp,
62
0
         SAFE_NETBIOS_CHARS,
63
0
         sizeof(local_machine) - 1);
64
0
  if (!strlower_m(local_machine)) {
65
0
    return false;
66
0
  }
67
68
0
  already_perm = perm;
69
70
0
  return true;
71
0
}
72
73
const char *get_local_machine_name(void)
74
0
{
75
0
  if (local_machine[0] == '\0') {
76
0
    return lp_netbios_name();
77
0
  }
78
79
0
  return local_machine;
80
0
}
81
82
/**
83
 * Set the 'remote' machine name
84
 *
85
 * @param remote_name the name our client wants to be called by
86
 * @param if this is the 'final' name for them, not be be changed again
87
 */
88
bool set_remote_machine_name(const char *remote_name, bool perm)
89
0
{
90
0
  static bool already_perm = False;
91
0
  char tmp[MACHINE_NAME_SIZE];
92
93
0
  if (already_perm) {
94
0
    return true;
95
0
  }
96
97
0
  strlcpy(tmp, remote_name, sizeof(tmp));
98
0
  trim_char(tmp, ' ', ' ');
99
100
0
  alpha_strcpy(remote_machine,
101
0
         tmp,
102
0
         SAFE_NETBIOS_CHARS,
103
0
         sizeof(remote_machine) - 1);
104
0
  if (!strlower_m(remote_machine)) {
105
0
    return false;
106
0
  }
107
108
0
  already_perm = perm;
109
110
0
  return true;
111
0
}
112
113
const char *get_remote_machine_name(void)
114
0
{
115
0
  return remote_machine;
116
0
}
117
118
static char sub_peeraddr[INET6_ADDRSTRLEN];
119
static const char *sub_peername = NULL;
120
static char sub_sockaddr[INET6_ADDRSTRLEN];
121
122
void sub_set_socket_ids(const char *peeraddr, const char *peername,
123
      const char *sockaddr)
124
0
{
125
0
  const char *addr = peeraddr;
126
127
0
  if (strnequal(addr, "::ffff:", 7)) {
128
0
    addr += 7;
129
0
  }
130
0
  strlcpy(sub_peeraddr, addr, sizeof(sub_peeraddr));
131
132
0
  if (sub_peername != NULL &&
133
0
      sub_peername != sub_peeraddr) {
134
0
    talloc_free(discard_const_p(char,sub_peername));
135
0
    sub_peername = NULL;
136
0
  }
137
0
  sub_peername = talloc_strdup(NULL, peername);
138
0
  if (sub_peername == NULL) {
139
0
    sub_peername = sub_peeraddr;
140
0
  }
141
142
  /*
143
   * Shouldn't we do the ::ffff: cancellation here as well? The
144
   * original code in talloc_sub_basic() did not do it, so I'm
145
   * leaving it out here as well for compatibility.
146
   */
147
0
  strlcpy(sub_sockaddr, sockaddr, sizeof(sub_sockaddr));
148
0
}
149
150
/*******************************************************************
151
 Setup the strings used by substitutions. Called per packet. Ensure
152
 %U name is set correctly also.
153
154
 smb_name must be sanitized by alpha_strcpy
155
********************************************************************/
156
157
void set_current_user_info(const char *smb_name, const char *unix_name,
158
         const char *domain)
159
0
{
160
0
  static const void *last_smb_name;
161
0
  static const void *last_unix_name;
162
0
  static const void *last_domain;
163
164
0
  if (likely(last_smb_name == smb_name &&
165
0
      last_unix_name == unix_name &&
166
0
      last_domain == domain))
167
0
  {
168
0
    return;
169
0
  }
170
171
0
  fstrcpy(current_user_info.smb_name, smb_name);
172
0
  fstrcpy(current_user_info.unix_name, unix_name);
173
0
  fstrcpy(current_user_info.domain, domain);
174
175
0
  last_smb_name = smb_name;
176
0
  last_unix_name = unix_name;
177
0
  last_domain = domain;
178
0
}
179
180
/*******************************************************************
181
 Return the current active user name.
182
*******************************************************************/
183
184
const char *get_current_username(void)
185
0
{
186
0
  return current_user_info.smb_name;
187
0
}
188
189
const char *get_current_user_info_domain(void)
190
0
{
191
0
  return current_user_info.domain;
192
0
}
193
194
/*******************************************************************
195
 Given a pointer to a %$(NAME) in p and the whole string in str
196
 expand it as an environment variable.
197
 str must be a talloced string.
198
 Return a new allocated and expanded string.
199
 Based on code by Branko Cibej <branko.cibej@hermes.si>
200
 When this is called p points at the '%' character.
201
 May substitute multiple occurrences of the same env var.
202
********************************************************************/
203
204
static char *realloc_expand_env_var(char *str, char *p)
205
0
{
206
0
  char *envname;
207
0
  char *envval;
208
0
  char *q, *r;
209
0
  int copylen;
210
211
0
  if (p[0] != '%' || p[1] != '$' || p[2] != '(') {
212
0
    return str;
213
0
  }
214
215
  /*
216
   * Look for the terminating ')'.
217
   */
218
219
0
  if ((q = strchr_m(p,')')) == NULL) {
220
0
    DEBUG(0,("expand_env_var: Unterminated environment variable [%s]\n", p));
221
0
    return str;
222
0
  }
223
224
  /*
225
   * Extract the name from within the %$(NAME) string.
226
   */
227
228
0
  r = p + 3;
229
0
  copylen = q - r;
230
231
  /* reserve space for use later add %$() chars */
232
0
  if ( (envname = talloc_array(talloc_tos(), char, copylen + 1 + 4)) == NULL ) {
233
0
    return NULL;
234
0
  }
235
236
0
  strncpy(envname,r,copylen);
237
0
  envname[copylen] = '\0';
238
239
0
  if ((envval = getenv(envname)) == NULL) {
240
0
    DEBUG(0,("expand_env_var: Environment variable [%s] not set\n", envname));
241
0
    TALLOC_FREE(envname);
242
0
    return str;
243
0
  }
244
245
  /*
246
   * Copy the full %$(NAME) into envname so it
247
   * can be replaced.
248
   */
249
250
0
  copylen = q + 1 - p;
251
0
  strncpy(envname,p,copylen);
252
0
  envname[copylen] = '\0';
253
0
  r = realloc_string_sub(str, envname, envval);
254
0
  TALLOC_FREE(envname);
255
256
0
  return r;
257
0
}
258
259
/****************************************************************************
260
 Do some standard substitutions in a string.
261
 len is the length in bytes of the space allowed in string str. If zero means
262
 don't allow expansions.
263
****************************************************************************/
264
265
void standard_sub_basic(const char *smb_name, const char *domain_name,
266
      char *str, size_t len)
267
0
{
268
0
  char *s;
269
270
0
  if ( (s = talloc_sub_basic(talloc_tos(), smb_name, domain_name, str )) != NULL ) {
271
0
    strncpy( str, s, len );
272
0
  }
273
274
0
  TALLOC_FREE( s );
275
0
}
276
277
/*
278
 * Limit addresses to hexalpha characters and underscore, safe for path
279
 * components for Windows clients.
280
 */
281
static void make_address_pathsafe(char *addr)
282
0
{
283
0
  while(addr && *addr) {
284
0
    if(!isxdigit(*addr)) {
285
0
      *addr = '_';
286
0
    }
287
0
    ++addr;
288
0
  }
289
0
}
290
291
/****************************************************************************
292
 Do some standard substitutions in a string.
293
 This function will return a talloced string that has to be freed.
294
****************************************************************************/
295
296
char *talloc_sub_basic(TALLOC_CTX *mem_ctx,
297
      const char *smb_name,
298
      const char *domain_name,
299
      const char *str)
300
0
{
301
0
  char *b, *p, *s, *r, *a_string;
302
0
  fstring pidstr, vnnstr;
303
0
  const char *local_machine_name = get_local_machine_name();
304
0
  TALLOC_CTX *tmp_ctx = NULL;
305
306
  /* workaround to prevent a crash while looking at bug #687 */
307
308
0
  if (!str) {
309
0
    DEBUG(0,("talloc_sub_basic: NULL source string!  This should not happen\n"));
310
0
    return NULL;
311
0
  }
312
313
0
  a_string = talloc_strdup(mem_ctx, str);
314
0
  if (a_string == NULL) {
315
0
    DEBUG(0, ("talloc_sub_basic: Out of memory!\n"));
316
0
    return NULL;
317
0
  }
318
319
0
  tmp_ctx = talloc_stackframe();
320
321
0
  for (s = a_string; (p = strchr_m(s, '%')); s = a_string + (p - b)) {
322
323
0
    r = NULL;
324
0
    b = a_string;
325
326
0
    switch (*(p+1)) {
327
0
    case 'U' :
328
0
      r = strlower_talloc(tmp_ctx, smb_name);
329
0
      if (r == NULL) {
330
0
        goto error;
331
0
      }
332
0
      a_string = realloc_string_sub(a_string, "%U", r);
333
0
      break;
334
0
    case 'G' : {
335
0
      struct passwd *pass;
336
0
      bool is_domain_name = false;
337
0
      const char *sep = lp_winbind_separator();
338
339
0
      if (domain_name != NULL && domain_name[0] != '\0' &&
340
0
          (lp_security() == SEC_ADS ||
341
0
           lp_security() == SEC_DOMAIN)) {
342
0
        r = talloc_asprintf(tmp_ctx,
343
0
                "%s%c%s",
344
0
                domain_name,
345
0
                *sep,
346
0
                smb_name);
347
0
        is_domain_name = true;
348
0
      } else {
349
0
        r = talloc_strdup(tmp_ctx, smb_name);
350
0
      }
351
0
      if (r == NULL) {
352
0
        goto error;
353
0
      }
354
355
0
      pass = Get_Pwnam_alloc(tmp_ctx, r);
356
0
      if (pass != NULL) {
357
0
        char *group_name;
358
359
0
        group_name = gidtoname(pass->pw_gid);
360
0
        if (is_domain_name) {
361
0
          char *group_sep;
362
0
          group_sep = strchr_m(group_name, *sep);
363
0
          if (group_sep != NULL) {
364
0
            group_name = group_sep + 1;
365
0
          }
366
0
        }
367
0
        a_string = realloc_string_sub(a_string,
368
0
                    "%G",
369
0
                    group_name);
370
0
      }
371
0
      TALLOC_FREE(pass);
372
0
      break;
373
0
    }
374
0
    case 'D' :
375
0
      r = strupper_talloc(tmp_ctx, domain_name);
376
0
      if (r == NULL) {
377
0
        goto error;
378
0
      }
379
0
      a_string = realloc_string_sub(a_string, "%D", r);
380
0
      break;
381
0
    case 'I' : {
382
0
      a_string = realloc_string_sub(
383
0
        a_string, "%I",
384
0
        sub_peeraddr[0] ? sub_peeraddr : "0.0.0.0");
385
0
      break;
386
0
    }
387
0
    case 'J' : {
388
0
      r = talloc_strdup(tmp_ctx,
389
0
        sub_peeraddr[0] ? sub_peeraddr : "0.0.0.0");
390
0
      make_address_pathsafe(r);
391
0
      a_string = realloc_string_sub(a_string, "%J", r);
392
0
      break;
393
0
    }
394
0
    case 'i':
395
0
      a_string = realloc_string_sub(
396
0
        a_string, "%i",
397
0
        sub_sockaddr[0] ? sub_sockaddr : "0.0.0.0");
398
0
      break;
399
0
    case 'j' : {
400
0
      r = talloc_strdup(tmp_ctx,
401
0
        sub_sockaddr[0] ? sub_sockaddr : "0.0.0.0");
402
0
      make_address_pathsafe(r);
403
0
      a_string = realloc_string_sub(a_string, "%j", r);
404
0
      break;
405
0
    }
406
0
    case 'L' :
407
0
      if ( strncasecmp_m(p, "%LOGONSERVER%", strlen("%LOGONSERVER%")) == 0 ) {
408
0
        break;
409
0
      }
410
0
      if (local_machine_name && *local_machine_name) {
411
0
        a_string = realloc_string_sub(a_string, "%L", local_machine_name);
412
0
      } else {
413
0
        a_string = realloc_string_sub(a_string, "%L", lp_netbios_name());
414
0
      }
415
0
      break;
416
0
    case 'N' :
417
0
      a_string = realloc_string_sub(a_string,
418
0
                  "%N",
419
0
                  lp_netbios_name());
420
0
      break;
421
0
    case 'M' :
422
0
      a_string = realloc_string_sub(a_string, "%M",
423
0
                  sub_peername ? sub_peername : "");
424
0
      break;
425
0
    case 'R' :
426
0
      a_string = realloc_string_sub(a_string, "%R", remote_proto);
427
0
      break;
428
0
    case 'T' :
429
0
      a_string = realloc_string_sub(a_string, "%T", current_timestring(tmp_ctx, False));
430
0
      break;
431
0
    case 't' :
432
0
      a_string = realloc_string_sub(a_string, "%t",
433
0
                  current_minimal_timestring(tmp_ctx, False));
434
0
      break;
435
0
    case 'a' :
436
0
      a_string = realloc_string_sub(a_string, "%a",
437
0
          get_remote_arch_str());
438
0
      break;
439
0
    case 'd' :
440
0
      slprintf(pidstr,sizeof(pidstr)-1, "%d",(int)getpid());
441
0
      a_string = realloc_string_sub(a_string, "%d", pidstr);
442
0
      break;
443
0
    case 'h' :
444
0
      a_string = realloc_string_sub(a_string, "%h", myhostname());
445
0
      break;
446
0
    case 'm' :
447
0
      a_string = realloc_string_sub(a_string, "%m",
448
0
                  remote_machine);
449
0
      break;
450
0
    case 'v' :
451
0
      a_string = realloc_string_sub(a_string, "%v", samba_version_string());
452
0
      break;
453
0
    case 'w' :
454
0
      a_string = realloc_string_sub(a_string, "%w", lp_winbind_separator());
455
0
      break;
456
0
    case '$' :
457
0
      a_string = realloc_expand_env_var(a_string, p); /* Expand environment variables */
458
0
      break;
459
0
    case 'V' :
460
0
      slprintf(vnnstr,sizeof(vnnstr)-1, "%u", get_my_vnn());
461
0
      a_string = realloc_string_sub(a_string, "%V", vnnstr);
462
0
      break;
463
0
    default:
464
0
      break;
465
0
    }
466
467
0
    p++;
468
0
    TALLOC_FREE(r);
469
470
0
    if (a_string == NULL) {
471
0
      goto done;
472
0
    }
473
0
  }
474
475
0
  goto done;
476
477
0
error:
478
0
  TALLOC_FREE(a_string);
479
480
0
done:
481
0
  TALLOC_FREE(tmp_ctx);
482
0
  return a_string;
483
0
}
484
485
/****************************************************************************
486
 Do some specific substitutions in a string.
487
 This function will return an allocated string that have to be freed.
488
****************************************************************************/
489
490
char *talloc_sub_specified(TALLOC_CTX *mem_ctx,
491
      const char *input_string,
492
      const char *username,
493
      const char *grpname,
494
      const char *domain,
495
      uid_t uid,
496
      gid_t gid)
497
0
{
498
0
  char *a_string;
499
0
  char *ret_string = NULL;
500
0
  char *b, *p, *s;
501
0
  TALLOC_CTX *tmp_ctx;
502
503
0
  if (!(tmp_ctx = talloc_new(mem_ctx))) {
504
0
    DEBUG(0, ("talloc_new failed\n"));
505
0
    return NULL;
506
0
  }
507
508
0
  a_string = talloc_strdup(tmp_ctx, input_string);
509
0
  if (a_string == NULL) {
510
0
    DEBUG(0, ("talloc_sub_specified: Out of memory!\n"));
511
0
    goto done;
512
0
  }
513
514
0
  for (s = a_string; (p = strchr_m(s, '%')); s = a_string + (p - b)) {
515
516
0
    b = a_string;
517
518
0
    switch (*(p+1)) {
519
0
    case 'U' :
520
0
      a_string = talloc_string_sub(
521
0
        tmp_ctx, a_string, "%U", username);
522
0
      break;
523
0
    case 'u' :
524
0
      a_string = talloc_string_sub(
525
0
        tmp_ctx, a_string, "%u", username);
526
0
      break;
527
0
    case 'G' :
528
0
      if (gid != -1) {
529
0
        const char *name;
530
531
0
        if (grpname != NULL) {
532
0
          name = grpname;
533
0
        } else {
534
0
          name = gidtoname(gid);
535
0
        }
536
537
0
        a_string = talloc_string_sub(tmp_ctx,
538
0
                   a_string,
539
0
                   "%G",
540
0
                   name);
541
0
      } else {
542
0
        a_string = talloc_string_sub(
543
0
          tmp_ctx, a_string,
544
0
          "%G", "NO_GROUP");
545
0
      }
546
0
      break;
547
0
    case 'g' :
548
0
      if (gid != -1) {
549
0
        const char *name;
550
551
0
        if (grpname != NULL) {
552
0
          name = grpname;
553
0
        } else {
554
0
          name = gidtoname(gid);
555
0
        }
556
557
0
        a_string = talloc_string_sub(tmp_ctx,
558
0
                   a_string,
559
0
                   "%g",
560
0
                   name);
561
0
      } else {
562
0
        a_string = talloc_string_sub(
563
0
          tmp_ctx, a_string, "%g", "NO_GROUP");
564
0
      }
565
0
      break;
566
0
    case 'D' :
567
0
      a_string = talloc_string_sub(tmp_ctx, a_string,
568
0
                 "%D", domain);
569
0
      break;
570
0
    case 'N' :
571
0
      a_string = talloc_string_sub(tmp_ctx, a_string,
572
0
                 "%N", lp_netbios_name());
573
0
      break;
574
0
    default:
575
0
      break;
576
0
    }
577
578
0
    p++;
579
0
    if (a_string == NULL) {
580
0
      goto done;
581
0
    }
582
0
  }
583
584
  /* Watch out, using "mem_ctx" here, so all intermediate stuff goes
585
   * away with the TALLOC_FREE(tmp_ctx) further down. */
586
587
0
  ret_string = talloc_sub_basic(mem_ctx, username, domain, a_string);
588
589
0
 done:
590
0
  TALLOC_FREE(tmp_ctx);
591
0
  return ret_string;
592
0
}
593
594
/****************************************************************************
595
****************************************************************************/
596
597
char *talloc_sub_advanced(TALLOC_CTX *ctx,
598
      const char *servicename,
599
      const char *user,
600
      const char *connectpath,
601
      gid_t gid,
602
      const char *str)
603
0
{
604
0
  char *a_string;
605
0
  char *b, *p, *s;
606
607
0
  a_string = talloc_strdup(talloc_tos(), str);
608
0
  if (a_string == NULL) {
609
0
    DEBUG(0, ("talloc_sub_advanced_only: Out of memory!\n"));
610
0
    return NULL;
611
0
  }
612
613
0
  for (s = a_string; (p = strchr_m(s, '%')); s = a_string + (p - b)) {
614
615
0
    b = a_string;
616
617
0
    switch (*(p+1)) {
618
0
    case 'N':
619
0
      a_string = realloc_string_sub(a_string,
620
0
                  "%N",
621
0
                  lp_netbios_name());
622
0
      break;
623
0
    case 'H': {
624
0
      char *h;
625
0
      if ((h = get_user_home_dir(talloc_tos(), user)))
626
0
        a_string = realloc_string_sub(a_string, "%H", h);
627
0
      TALLOC_FREE(h);
628
0
      break;
629
0
    }
630
0
    case 'P':
631
0
      a_string = realloc_string_sub(a_string, "%P", connectpath);
632
0
      break;
633
0
    case 'S':
634
0
      a_string = realloc_string_sub(a_string, "%S", servicename);
635
0
      break;
636
0
    case 'g':
637
0
      a_string = realloc_string_sub(a_string, "%g", gidtoname(gid));
638
0
      break;
639
0
    case 'u':
640
0
      a_string = realloc_string_sub(a_string, "%u", user);
641
0
      break;
642
0
    default:
643
0
      break;
644
0
    }
645
646
0
    p++;
647
0
    if (a_string == NULL) {
648
0
      return NULL;
649
0
    }
650
0
  }
651
652
0
  return a_string;
653
0
}
654
655
char *talloc_sub_full(TALLOC_CTX *ctx,
656
      const char *servicename,
657
      const char *user,
658
      const char *connectpath,
659
      gid_t gid,
660
      const char *smb_name,
661
      const char *domain_name,
662
      const char *str)
663
0
{
664
0
  char *a_string, *ret_string;
665
666
0
  a_string = talloc_sub_advanced(ctx, servicename, user, connectpath,
667
0
               gid, str);
668
0
  if (a_string == NULL) {
669
0
    return NULL;
670
0
  }
671
672
0
  ret_string = talloc_sub_basic(ctx, smb_name, domain_name, a_string);
673
  TALLOC_FREE(a_string);
674
0
  return ret_string;
675
0
}