Coverage Report

Created: 2025-07-23 07:04

/src/samba/source3/lib/charcnv.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
   Unix SMB/CIFS implementation.
3
   Character set conversion Extensions
4
   Copyright (C) Igor Vergeichik <iverg@mail.ru> 2001
5
   Copyright (C) Andrew Tridgell 2001
6
   Copyright (C) Simo Sorce 2001
7
   Copyright (C) Martin Pool 2003
8
9
   This program is free software; you can redistribute it and/or modify
10
   it under the terms of the GNU General Public License as published by
11
   the Free Software Foundation; either version 3 of the License, or
12
   (at your option) any later version.
13
14
   This program is distributed in the hope that it will be useful,
15
   but WITHOUT ANY WARRANTY; without even the implied warranty of
16
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
   GNU General Public License for more details.
18
19
   You should have received a copy of the GNU General Public License
20
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
22
*/
23
#include "includes.h"
24
25
/**
26
 * Destroy global objects allocated by init_iconv()
27
 **/
28
void gfree_charcnv(void)
29
0
{
30
0
  free_iconv_handle();
31
0
}
32
33
/**
34
 * Copy a string from a char* unix src to a dos codepage string destination.
35
 *
36
 * @return the number of bytes occupied by the string in the destination.
37
 *
38
 * @param flags can include
39
 * <dl>
40
 * <dt>STR_TERMINATE</dt> <dd>means include the null termination</dd>
41
 * <dt>STR_UPPER</dt> <dd>means uppercase in the destination</dd>
42
 * </dl>
43
 *
44
 * @param dest_len the maximum length in bytes allowed in the
45
 * destination.
46
 **/
47
size_t push_ascii(void *dest, const char *src, size_t dest_len, int flags)
48
0
{
49
0
  size_t src_len = 0;
50
0
  char *tmpbuf = NULL;
51
0
  size_t size = 0;
52
0
  bool ret;
53
54
  /* No longer allow a length of -1. */
55
0
  if (dest_len == (size_t)-1) {
56
0
    smb_panic("push_ascii - dest_len == -1");
57
0
  }
58
59
0
  if (flags & STR_UPPER) {
60
0
    tmpbuf = SMB_STRDUP(src);
61
0
    if (!tmpbuf) {
62
0
      smb_panic("malloc fail");
63
0
    }
64
0
    if (!strupper_m(tmpbuf)) {
65
0
      if ((flags & (STR_TERMINATE|STR_TERMINATE_ASCII)) &&
66
0
          dest &&
67
0
          dest_len > 0) {
68
0
        *(char *)dest = 0;
69
0
      }
70
0
      SAFE_FREE(tmpbuf);
71
0
      return 0;
72
0
    }
73
0
    src = tmpbuf;
74
0
  }
75
76
0
  src_len = strlen(src);
77
0
  if (flags & (STR_TERMINATE | STR_TERMINATE_ASCII)) {
78
0
    src_len++;
79
0
  }
80
81
0
  ret = convert_string(CH_UNIX, CH_DOS, src, src_len, dest, dest_len, &size);
82
0
  SAFE_FREE(tmpbuf);
83
0
  if (ret == false) {
84
0
    if ((flags & (STR_TERMINATE | STR_TERMINATE_ASCII)) &&
85
0
        dest_len > 0) {
86
0
      ((char *)dest)[0] = '\0';
87
0
    }
88
0
    return 0;
89
0
  }
90
0
  return size;
91
0
}
92
93
/********************************************************************
94
 Push and malloc an ascii string. src and dest null terminated.
95
********************************************************************/
96
97
/**
98
 * Copy a string from a dos codepage source to a unix char* destination.
99
 *
100
 * The resulting string in "dest" is always null terminated.
101
 *
102
 * @param flags can have:
103
 * <dl>
104
 * <dt>STR_TERMINATE</dt>
105
 * <dd>STR_TERMINATE means the string in @p src
106
 * is null terminated, and src_len is ignored.</dd>
107
 * </dl>
108
 *
109
 * @param src_len is the length of the source area in bytes.
110
 * @returns the number of bytes occupied by the string in @p src.
111
 **/
112
size_t pull_ascii(char *dest, const void *src, size_t dest_len, size_t src_len, int flags)
113
335
{
114
335
  bool ret;
115
335
  size_t size = 0;
116
117
335
  if (dest_len == (size_t)-1) {
118
    /* No longer allow dest_len of -1. */
119
0
    smb_panic("pull_ascii - invalid dest_len of -1");
120
0
  }
121
122
335
  if (flags & STR_TERMINATE) {
123
335
    if (src_len == (size_t)-1) {
124
0
      src_len = strlen((const char *)src) + 1;
125
335
    } else {
126
335
      size_t len = strnlen((const char *)src, src_len);
127
335
      if (len < src_len)
128
335
        len++;
129
335
      src_len = len;
130
335
    }
131
335
  }
132
133
335
  ret = convert_string(CH_DOS, CH_UNIX, src, src_len, dest, dest_len, &size);
134
335
  if (ret == false) {
135
62
    size = 0;
136
62
    dest_len = 0;
137
62
  }
138
139
335
  if (dest_len && size) {
140
    /* Did we already process the terminating zero ? */
141
273
    if (dest[MIN(size-1, dest_len-1)] != 0) {
142
0
      dest[MIN(size, dest_len-1)] = 0;
143
0
    }
144
273
  } else  {
145
62
    dest[0] = 0;
146
62
  }
147
148
335
  return src_len;
149
335
}
150
151
/**
152
 * Copy a string from a dos codepage source to a unix char* destination.
153
 * Talloc version.
154
 *
155
 * The resulting string in "dest" is always null terminated.
156
 *
157
 * @param flags can have:
158
 * <dl>
159
 * <dt>STR_TERMINATE</dt>
160
 * <dd>STR_TERMINATE means the string in @p src
161
 * is null terminated, and src_len is ignored.</dd>
162
 * </dl>
163
 *
164
 * @param src_len is the length of the source area in bytes.
165
 * @returns the number of bytes occupied by the string in @p src.
166
 **/
167
168
static size_t pull_ascii_base_talloc(TALLOC_CTX *ctx,
169
             char **ppdest,
170
             const void *src,
171
             size_t src_len,
172
             int flags)
173
0
{
174
0
  char *dest = NULL;
175
0
  size_t dest_len;
176
177
0
  *ppdest = NULL;
178
179
0
  if (!src_len) {
180
0
    return 0;
181
0
  }
182
183
0
  if (src_len == (size_t)-1) {
184
0
    smb_panic("src_len == -1 in pull_ascii_base_talloc");
185
0
  }
186
187
0
  if (flags & STR_TERMINATE) {
188
0
    size_t len = strnlen((const char *)src, src_len);
189
0
    if (len < src_len)
190
0
      len++;
191
0
    src_len = len;
192
    /* Ensure we don't use an insane length from the client. */
193
0
    if (src_len >= 1024*1024) {
194
0
      char *msg = talloc_asprintf(ctx,
195
0
          "Bad src length (%u) in "
196
0
          "pull_ascii_base_talloc",
197
0
          (unsigned int)src_len);
198
0
      smb_panic(msg);
199
0
    }
200
0
  }
201
202
  /* src_len != -1 here. */
203
204
0
  if (!convert_string_talloc(ctx, CH_DOS, CH_UNIX, src, src_len, &dest,
205
0
             &dest_len)) {
206
0
    dest_len = 0;
207
0
  }
208
209
0
  if (dest_len && dest) {
210
    /* Did we already process the terminating zero ? */
211
0
    if (dest[dest_len-1] != 0) {
212
0
      size_t size = talloc_get_size(dest);
213
      /* Have we got space to append the '\0' ? */
214
0
      if (size <= dest_len) {
215
        /* No, realloc. */
216
0
        dest = talloc_realloc(ctx, dest, char,
217
0
            dest_len+1);
218
0
        if (!dest) {
219
          /* talloc fail. */
220
0
          dest_len = (size_t)-1;
221
0
          return 0;
222
0
        }
223
0
      }
224
      /* Yay - space ! */
225
0
      dest[dest_len] = '\0';
226
0
      dest_len++;
227
0
    }
228
0
  } else if (dest) {
229
0
    dest[0] = 0;
230
0
  }
231
232
0
  *ppdest = dest;
233
0
  return src_len;
234
0
}
235
236
/**
237
 * Copy a string from a char* src to a unicode destination.
238
 *
239
 * @returns the number of bytes occupied by the string in the destination.
240
 *
241
 * @param flags can have:
242
 *
243
 * <dl>
244
 * <dt>STR_TERMINATE <dd>means include the null termination.
245
 * <dt>STR_UPPER     <dd>means uppercase in the destination.
246
 * <dt>STR_NOALIGN   <dd>means don't do alignment.
247
 * </dl>
248
 *
249
 * @param dest_len is the maximum length allowed in the
250
 * destination.
251
 **/
252
253
static size_t push_ucs2(const void *base_ptr, void *dest, const char *src, size_t dest_len, int flags)
254
0
{
255
0
  size_t len=0;
256
0
  size_t src_len;
257
0
  size_t size = 0;
258
0
  bool ret;
259
260
0
  if (dest_len == (size_t)-1) {
261
    /* No longer allow dest_len of -1. */
262
0
    smb_panic("push_ucs2 - invalid dest_len of -1");
263
0
  }
264
265
0
  if (flags & STR_TERMINATE)
266
0
    src_len = (size_t)-1;
267
0
  else
268
0
    src_len = strlen(src);
269
270
0
  if (ucs2_align(base_ptr, dest, flags)) {
271
0
    *(char *)dest = 0;
272
0
    dest = (void *)((char *)dest + 1);
273
0
    if (dest_len)
274
0
      dest_len--;
275
0
    len++;
276
0
  }
277
278
  /* ucs2 is always a multiple of 2 bytes */
279
0
  dest_len &= ~1;
280
281
0
  ret = convert_string(CH_UNIX, CH_UTF16LE, src, src_len, dest, dest_len, &size);
282
0
  if (ret == false) {
283
0
    if ((flags & STR_TERMINATE) &&
284
0
        dest &&
285
0
        dest_len) {
286
0
      *(char *)dest = 0;
287
0
    }
288
0
    return len;
289
0
  }
290
291
0
  len += size;
292
293
0
  if (flags & STR_UPPER) {
294
0
    smb_ucs2_t *dest_ucs2 = (smb_ucs2_t *)dest;
295
0
    size_t i;
296
297
    /* We check for i < (size / 2) below as the dest string isn't null
298
       terminated if STR_TERMINATE isn't set. */
299
300
0
    for (i = 0; i < (size / 2) && i < (dest_len / 2) && dest_ucs2[i]; i++) {
301
0
      smb_ucs2_t v = toupper_w(dest_ucs2[i]);
302
0
      if (v != dest_ucs2[i]) {
303
0
        dest_ucs2[i] = v;
304
0
      }
305
0
    }
306
0
  }
307
308
0
  return len;
309
0
}
310
311
/**
312
 Copy a string from a ucs2 source to a unix char* destination.
313
 Talloc version with a base pointer.
314
 Uses malloc if TALLOC_CTX is NULL (this is a bad interface and
315
 needs fixing. JRA).
316
 Flags can have:
317
  STR_TERMINATE means the string in src is null terminated.
318
  STR_NOALIGN   means don't try to align.
319
 if STR_TERMINATE is set then src_len is ignored if it is -1.
320
 src_len is the length of the source area in bytes
321
 Return the number of bytes occupied by the string in src.
322
 The resulting string in "dest" is always null terminated.
323
**/
324
325
static size_t pull_ucs2_base_talloc(TALLOC_CTX *ctx,
326
            const void *base_ptr,
327
            char **ppdest,
328
            const void *src,
329
            size_t src_len,
330
            int flags)
331
0
{
332
0
  char *dest;
333
0
  size_t dest_len;
334
0
  size_t ucs2_align_len = 0;
335
336
0
  *ppdest = NULL;
337
338
0
#ifdef DEVELOPER
339
  /* Ensure we never use the braindead "malloc" variant. */
340
0
  if (ctx == NULL) {
341
0
    smb_panic("NULL talloc CTX in pull_ucs2_base_talloc\n");
342
0
  }
343
0
#endif
344
345
0
  if (!src_len) {
346
0
    return 0;
347
0
  }
348
349
0
  if (src_len == (size_t)-1) {
350
    /* no longer used anywhere, but worth checking */
351
0
    smb_panic("sec_len == -1 in pull_ucs2_base_talloc");
352
0
  }
353
354
0
  if (ucs2_align(base_ptr, src, flags)) {
355
0
    src = (const void *)((const char *)src + 1);
356
0
    src_len--;
357
0
    ucs2_align_len = 1;
358
0
  }
359
360
0
  if (flags & STR_TERMINATE) {
361
    /* src_len -1 is the default for null terminated strings. */
362
0
    size_t len = strnlen_w((const smb_ucs2_t *)src,
363
0
               src_len/2);
364
0
    if (len < src_len/2)
365
0
      len++;
366
0
    src_len = len*2;
367
368
    /* Ensure we don't use an insane length from the client. */
369
0
    if (src_len >= 1024*1024) {
370
0
      smb_panic("Bad src length in pull_ucs2_base_talloc\n");
371
0
    }
372
0
  }
373
374
  /* ucs2 is always a multiple of 2 bytes */
375
0
  src_len &= ~1;
376
377
0
  if (!convert_string_talloc(ctx, CH_UTF16LE, CH_UNIX, src, src_len,
378
0
           (void *)&dest, &dest_len)) {
379
0
    dest_len = 0;
380
0
  }
381
382
0
  if (dest_len) {
383
    /* Did we already process the terminating zero ? */
384
0
    if (dest[dest_len-1] != 0) {
385
0
      size_t size = talloc_get_size(dest);
386
      /* Have we got space to append the '\0' ? */
387
0
      if (size <= dest_len) {
388
        /* No, realloc. */
389
0
        dest = talloc_realloc(ctx, dest, char,
390
0
            dest_len+1);
391
0
        if (!dest) {
392
          /* talloc fail. */
393
0
          dest_len = (size_t)-1;
394
0
          return 0;
395
0
        }
396
0
      }
397
      /* Yay - space ! */
398
0
      dest[dest_len] = '\0';
399
0
      dest_len++;
400
0
    }
401
0
  } else if (dest) {
402
0
    dest[0] = 0;
403
0
  }
404
405
0
  *ppdest = dest;
406
0
  return src_len + ucs2_align_len;
407
0
}
408
409
/**
410
 Copy a string from a char* src to a unicode or ascii
411
 dos codepage destination choosing unicode or ascii based on the 
412
 flags supplied
413
 Return the number of bytes occupied by the string in the destination.
414
 flags can have:
415
  STR_TERMINATE means include the null termination.
416
  STR_UPPER     means uppercase in the destination.
417
  STR_ASCII     use ascii even with unicode packet.
418
  STR_NOALIGN   means don't do alignment.
419
 dest_len is the maximum length allowed in the destination. If dest_len
420
 is -1 then no maximum is used.
421
**/
422
423
size_t push_string_check_fn(void *dest, const char *src,
424
       size_t dest_len, int flags)
425
0
{
426
0
  if (!(flags & STR_ASCII) && (flags & STR_UNICODE)) {
427
0
    return push_ucs2(NULL, dest, src, dest_len, flags);
428
0
  }
429
0
  return push_ascii(dest, src, dest_len, flags);
430
0
}
431
432
433
/**
434
 Copy a string from a char* src to a unicode or ascii
435
 dos codepage destination choosing unicode or ascii based on the 
436
 flags in the SMB buffer starting at base_ptr.
437
 Return the number of bytes occupied by the string in the destination.
438
 flags can have:
439
  STR_TERMINATE means include the null termination.
440
  STR_UPPER     means uppercase in the destination.
441
  STR_ASCII     use ascii even with unicode packet.
442
  STR_NOALIGN   means don't do alignment.
443
 dest_len is the maximum length allowed in the destination. If dest_len
444
 is -1 then no maximum is used.
445
**/
446
447
size_t push_string_base(const char *base, uint16_t flags2,
448
      void *dest, const char *src,
449
      size_t dest_len, int flags)
450
0
{
451
452
0
  if (!(flags & STR_ASCII) && \
453
0
      ((flags & STR_UNICODE || \
454
0
        (flags2 & FLAGS2_UNICODE_STRINGS)))) {
455
0
    return push_ucs2(base, dest, src, dest_len, flags);
456
0
  }
457
0
  return push_ascii(dest, src, dest_len, flags);
458
0
}
459
460
/**
461
 Copy a string from a unicode or ascii source (depending on
462
 the packet flags) to a char* destination.
463
 Variant that uses talloc.
464
 Flags can have:
465
  STR_TERMINATE means the string in src is null terminated.
466
  STR_UNICODE   means to force as unicode.
467
  STR_ASCII     use ascii even with unicode packet.
468
  STR_NOALIGN   means don't do alignment.
469
 if STR_TERMINATE is set then src_len is ignored is it is -1
470
 src_len is the length of the source area in bytes.
471
 Return the number of bytes occupied by the string in src.
472
 The resulting string in "dest" is always null terminated.
473
**/
474
475
size_t pull_string_talloc(TALLOC_CTX *ctx,
476
        const void *base_ptr,
477
        uint16_t smb_flags2,
478
        char **ppdest,
479
        const void *src,
480
        size_t src_len,
481
        int flags)
482
0
{
483
0
  if ((base_ptr == NULL) && ((flags & (STR_ASCII|STR_UNICODE)) == 0)) {
484
0
    smb_panic("No base ptr to get flg2 and neither ASCII nor "
485
0
        "UNICODE defined");
486
0
  }
487
488
0
  if (!(flags & STR_ASCII) &&
489
0
      ((flags & STR_UNICODE || (smb_flags2 & FLAGS2_UNICODE_STRINGS)))) {
490
0
    return pull_ucs2_base_talloc(ctx,
491
0
          base_ptr,
492
0
          ppdest,
493
0
          src,
494
0
          src_len,
495
0
          flags);
496
0
  }
497
0
  return pull_ascii_base_talloc(ctx,
498
0
          ppdest,
499
0
          src,
500
0
          src_len,
501
0
          flags);
502
0
}
503
504
/*******************************************************************
505
 Write a string in (little-endian) unicode format. src is in
506
 the current DOS codepage. len is the length in bytes of the
507
 string pointed to by dst.
508
509
 if null_terminate is True then null terminate the packet (adds 2 bytes)
510
511
 the return value is the length in bytes consumed by the string, including the
512
 null termination if applied
513
********************************************************************/
514
515
size_t dos_PutUniCode(char *dst,const char *src, size_t len, bool null_terminate)
516
0
{
517
0
  int flags = null_terminate ? STR_UNICODE|STR_NOALIGN|STR_TERMINATE
518
0
           : STR_UNICODE|STR_NOALIGN;
519
0
  return push_ucs2(NULL, dst, src, len, flags);
520
0
}
521
522
523
/* Converts a string from internal samba format to unicode. Always terminates.
524
 * Actually just a wrapper round push_ucs2_talloc().
525
 */
526
527
int rpcstr_push_talloc(TALLOC_CTX *ctx, smb_ucs2_t **dest, const char *src)
528
0
{
529
0
  size_t size;
530
0
  if (push_ucs2_talloc(ctx, dest, src, &size))
531
0
    return size;
532
0
  else
533
0
    return -1;
534
0
}