OpenVAS Scanner  22.7.9
charcnv.c
Go to the documentation of this file.
1 /* SPDX-FileCopyrightText: 2023 Greenbone AG
2  * SPDX-FileCopyrightText: 2003 Martin Pool
3  * SPDX-FileCopyrightText: 2001 Simo Sorce
4  * SPDX-FileCopyrightText: 2001 Andrew Tridgell
5  * SPDX-FileCopyrightText: 2001 Igor Vergeichik <iverg@mail.ru>
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  */
9 
33 #include "byteorder.h"
34 #include "iconv.h"
35 #include "proto.h"
36 #include "smb.h"
37 
38 #include <gvm/base/logging.h>
39 
40 #ifndef SMB_STRDUP
41 #define SMB_STRDUP(s) strdup (s)
42 #endif
43 
44 #ifndef uint8
45 #define uint8 uint8_t
46 #endif
47 
48 #ifndef uint16
49 #define uint16 uint16_t
50 #endif
51 
52 #ifndef _PUBLIC_
53 #define _PUBLIC_
54 #endif
55 
56 #undef G_LOG_DOMAIN
57 
60 #define G_LOG_DOMAIN "lib nasl"
61 
62 typedef unsigned int bool;
63 #define False 0
64 #define True 1
65 
68 size_t
69 convert_string_ntlmssp (charset_t from, charset_t to, void const *src,
70  size_t srclen, void *dest, size_t destlen,
71  bool allow_badcharcnv);
72 static int
74 {
75  char buf[10];
76  uint16_t c2 = 0;
77  size_t len1, len2;
78 
79  len1 = convert_string_ntlmssp (CH_UTF16LE, CH_DOS, &c, 2, buf, sizeof (buf),
80  False);
81 
82  /* convert_string_ntlmssp returns a size_t value, and uses
83  * (size_t) -1 as error code */
84  if (len1 == 0 || len1 == (size_t) -1)
85  {
86  return 0;
87  }
88  len2 = convert_string_ntlmssp (CH_DOS, CH_UTF16LE, buf, len1, &c2, 2, False);
89  if (len2 != 2)
90  {
91  return 0;
92  }
93  return (c == c2);
94 }
95 
96 /* We can parameterize this if someone complains.... JRA. */
97 
98 static char
100 {
101  return '_';
102 }
103 
120 static bool
121  conv_silent_ntlmssp; /* Should we do a debug if the conversion fails ? */
122 
123 static void
125 {
126  static int mapped_file;
127  int i;
128  const char *allowed = ".!#$%&'()_-@^`~";
129 
130  if (mapped_file)
131  {
132  /* Can't unmap files, so stick with what we have */
133  return;
134  }
135 
136  /* we're using a dynamically created valid_table.
137  * It might need to be regenerated if the code page changed.
138  * We know that we're not using a mapped file, so we can
139  * free() the old one. */
140 
141  /* use free rather than unmap */
143 
144  valid_table_ntlmssp = (uint8 *) SMB_MALLOC (0x10000);
145  for (i = 0; i < 128; i++)
146  {
147  valid_table_ntlmssp[i] = isalnum (i) || strchr (allowed, i);
148  }
149 
151 
152  for (; i < 0x10000; i++)
153  {
154  uint16_t c;
155  SSVAL (&c, 0, i);
157  }
158 }
159 
160 /*******************************************************************
161  * Count the number of characters in a uint16_t string.
162  * ********************************************************************/
163 
164 static size_t
166 {
167  size_t len;
168  uint16 c;
169 
170  for (len = 0; *(COPY_UCS2_CHAR (&c, src)); src++, len++)
171  {
172  ;
173  }
174 
175  return len;
176 }
177 
181 static const char *
183 {
184  const char *ret = NULL;
185 
186  if (ch == CH_UTF16LE)
187  ret = "UTF-16LE";
188  else if (ch == CH_UTF16BE)
189  ret = "UTF-16BE";
190  else if (ch == CH_UTF8)
191  ret = "UTF8";
192 
193 #if defined(HAVE_NL_LANGINFO) && defined(CODESET)
194  if (ret && !strcmp (ret, "LOCALE"))
195  {
196  const char *ln = NULL;
197 
198 #ifdef HAVE_SETLOCALE
199  setlocale (LC_ALL, "");
200 #endif
201  ln = nl_langinfo (CODESET);
202  if (ln)
203  {
204  /* Check whether the charset name is supported
205  by iconv */
206  smb_iconv_t handle = smb_iconv_open_ntlmssp (ln, "UCS-2LE");
207  if (handle == (smb_iconv_t) -1)
208  {
209  ln = NULL;
210  }
211  else
212  {
213  smb_iconv_close_ntlmssp (handle);
214  }
215  }
216  ret = ln;
217  }
218 #endif
219 
220  if (!ret || !*ret)
221  ret = "ASCII";
222  return ret;
223 }
224 
225 void
227 {
228  static int initialized = False;
229 
230  if (!initialized)
231  {
232  initialized = True;
234  }
235 }
236 
244 void
246 {
247  int c1, c2;
248  bool did_reload = False;
249 
250  /* so that charset_name() works we need to get the UNIX<->UCS2 going
251  first */
255 
259 
260  for (c1 = 0; c1 < NUM_CHARSETS; c1++)
261  {
262  for (c2 = 0; c2 < NUM_CHARSETS; c2++)
263  {
264  const char *n1 = charset_name_ntlmssp ((charset_t) c1);
265  const char *n2 = charset_name_ntlmssp ((charset_t) c2);
266  if (conv_handles_ntlmssp[c1][c2]
267  && strcmp (n1, conv_handles_ntlmssp[c1][c2]->from_name) == 0
268  && strcmp (n2, conv_handles_ntlmssp[c1][c2]->to_name) == 0)
269  continue;
270 
271  did_reload = True;
272 
273  if (conv_handles_ntlmssp[c1][c2])
275 
276  conv_handles_ntlmssp[c1][c2] = smb_iconv_open_ntlmssp (n2, n1);
277  if (conv_handles_ntlmssp[c1][c2] == (smb_iconv_t) -1)
278  {
279  if (c1 != CH_UTF16LE && c1 != CH_UTF16BE)
280  {
281  n1 = "ASCII";
282  }
283  if (c2 != CH_UTF16LE && c2 != CH_UTF16BE)
284  {
285  n2 = "ASCII";
286  }
287  conv_handles_ntlmssp[c1][c2] = smb_iconv_open_ntlmssp (n2, n1);
288  if (!conv_handles_ntlmssp[c1][c2])
289  {
290  g_message ("init_iconv_ntlmssp: conv_handle"
291  " initialization failed");
292  }
293  }
294  }
295  }
296 
297  if (did_reload)
298  {
299  /* XXX: Does this really get called every time the dos
300  * codepage changes? */
301  /* XXX: Is the did_reload test too strict? */
305  }
306 }
307 
324 static size_t
326  size_t srclen, void *dest, size_t destlen,
327  bool allow_bad_conv)
328 {
329  size_t i_len, o_len;
330  size_t retval;
331  const char *inbuf = (const char *) src;
332  char *outbuf = (char *) dest;
333  smb_iconv_t descriptor;
334 
336 
337  descriptor = conv_handles_ntlmssp[from][to];
338 
339  if (srclen == (size_t) -1)
340  {
341  if (from == CH_UTF16LE || from == CH_UTF16BE)
342  {
343  srclen = (strlen_w_ntlmssp ((const uint16 *) src) + 1) * 2;
344  }
345  else
346  {
347  srclen = strlen ((const char *) src) + 1;
348  }
349  }
350 
351  if (descriptor == (smb_iconv_t) -1 || descriptor == (smb_iconv_t) 0)
352  return (size_t) -1;
353 
354  i_len = srclen;
355  o_len = destlen;
356 
357 again:
358 
359  retval = smb_iconv_ntlmssp (descriptor, &inbuf, &i_len, &outbuf, &o_len);
360  if (retval == (size_t) -1)
361  {
362  switch (errno)
363  {
364  case EINVAL:
365  /* Incomplete multibyte sequence */
366  if (!conv_silent_ntlmssp)
367  if (allow_bad_conv)
368  goto use_as_is;
369  return (size_t) -1;
370  case E2BIG:
371  /* No more room */
372  break;
373  case EILSEQ:
374  /* Illegal multibyte sequence */
375  if (allow_bad_conv)
376  goto use_as_is;
377 
378  return (size_t) -1;
379  default:
380  /* unknown error */
381  return (size_t) -1;
382  }
383  }
384  return destlen - o_len;
385 
386 use_as_is:
387 
388  /*
389  * Conversion not supported. This is actually an error, but there are so
390  * many misconfigured iconv systems and smb.conf's out there we can't just
391  * fail. Do a very bad conversion instead.... JRA.
392  */
393 
394  {
395  if (o_len == 0 || i_len == 0)
396  return destlen - o_len;
397 
398  if (((from == CH_UTF16LE) || (from == CH_UTF16BE))
399  && ((to != CH_UTF16LE) && (to != CH_UTF16BE)))
400  {
401  /* Can't convert from utf16 any endian to multibyte.
402  Replace with the default fail char.
403  */
404  if (i_len < 2)
405  return destlen - o_len;
406  if (i_len >= 2)
407  {
408  *outbuf = lp_failed_convert_char_ntlmssp ();
409 
410  outbuf++;
411  o_len--;
412 
413  inbuf += 2;
414  i_len -= 2;
415  }
416 
417  if (o_len == 0 || i_len == 0)
418  return destlen - o_len;
419 
420  /* Keep trying with the next char... */
421  goto again;
422  }
423  else if (from != CH_UTF16LE && from != CH_UTF16BE && to == CH_UTF16LE)
424  {
425  /* Can't convert to UTF16LE - just widen by adding the
426  default fail char then zero.
427  */
428  if (o_len < 2)
429  return destlen - o_len;
430 
431  outbuf[0] = lp_failed_convert_char_ntlmssp ();
432  outbuf[1] = '\0';
433 
434  inbuf++;
435  i_len--;
436 
437  outbuf += 2;
438  o_len -= 2;
439 
440  if (o_len == 0 || i_len == 0)
441  return destlen - o_len;
442 
443  /* Keep trying with the next char... */
444  goto again;
445  }
446  else if (from != CH_UTF16LE && from != CH_UTF16BE && to != CH_UTF16LE
447  && to != CH_UTF16BE)
448  {
449  /* Failed multibyte to multibyte. Just copy the default fail char and
450  try again. */
451  outbuf[0] = lp_failed_convert_char_ntlmssp ();
452 
453  inbuf++;
454  i_len--;
455 
456  outbuf++;
457  o_len--;
458 
459  if (o_len == 0 || i_len == 0)
460  return destlen - o_len;
461 
462  /* Keep trying with the next char... */
463  goto again;
464  }
465  else
466  {
467  /* Keep compiler happy.... */
468  return destlen - o_len;
469  }
470  }
471 }
472 
492 size_t
493 convert_string_ntlmssp (charset_t from, charset_t to, void const *src,
494  size_t srclen, void *dest, size_t destlen,
495  bool allow_bad_conv)
496 {
497  /*
498  * NB. We deliberately don't do a strlen here if srclen == -1.
499  * This is very expensive over millions of calls and is taken
500  * care of in the slow path in convert_string_internal. JRA.
501  */
502 
503  if (srclen == 0)
504  return 0;
505 
506  if (from != CH_UTF16LE && from != CH_UTF16BE && to != CH_UTF16LE
507  && to != CH_UTF16BE)
508  {
509  const unsigned char *p = (const unsigned char *) src;
510  unsigned char *q = (unsigned char *) dest;
511  size_t slen = srclen;
512  size_t dlen = destlen;
513  unsigned char lastp = '\0';
514  size_t retval = 0;
515 
516  /* If all characters are ascii, fast path here. */
517  while (slen && dlen)
518  {
519  if ((lastp = *p) <= 0x7f)
520  {
521  *q++ = *p++;
522  if (slen != (size_t) -1)
523  {
524  slen--;
525  }
526  dlen--;
527  retval++;
528  if (!lastp)
529  break;
530  }
531  else
532  {
533 #ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS
534  goto general_case;
535 #else
536  size_t ret = convert_string_internal_ntlmssp (
537  from, to, p, slen, q, dlen, allow_bad_conv);
538  if (ret == (size_t) -1)
539  {
540  return ret;
541  }
542  return retval + ret;
543 #endif
544  }
545  }
546  if (!dlen)
547  {
548  /* Even if we fast path we should note if we ran out of room. */
549  if (((slen != (size_t) -1) && slen)
550  || ((slen == (size_t) -1) && lastp))
551  {
552  errno = E2BIG;
553  }
554  }
555  return retval;
556  }
557  else if (from == CH_UTF16LE && to != CH_UTF16LE)
558  {
559  const unsigned char *p = (const unsigned char *) src;
560  unsigned char *q = (unsigned char *) dest;
561  size_t retval = 0;
562  size_t slen = srclen;
563  size_t dlen = destlen;
564  unsigned char lastp = '\0';
565 
566  /* If all characters are ascii, fast path here. */
567  while (((slen == (size_t) -1) || (slen >= 2)) && dlen)
568  {
569  if (((lastp = *p) <= 0x7f) && (p[1] == 0))
570  {
571  *q++ = *p;
572  if (slen != (size_t) -1)
573  {
574  slen -= 2;
575  }
576  p += 2;
577  dlen--;
578  retval++;
579  if (!lastp)
580  break;
581  }
582  else
583  {
584 #ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS
585  goto general_case;
586 #else
587  return retval
588  + convert_string_internal_ntlmssp (from, to, p, slen, q,
589  dlen, allow_bad_conv);
590 #endif
591  }
592  }
593  if (!dlen)
594  {
595  /* Even if we fast path we should note if we ran out of room. */
596  if (((slen != (size_t) -1) && slen)
597  || ((slen == (size_t) -1) && lastp))
598  {
599  errno = E2BIG;
600  }
601  }
602  return retval;
603  }
604  else if (from != CH_UTF16LE && from != CH_UTF16BE && to == CH_UTF16LE)
605  {
606  const unsigned char *p = (const unsigned char *) src;
607  unsigned char *q = (unsigned char *) dest;
608  size_t retval = 0;
609  size_t slen = srclen;
610  size_t dlen = destlen;
611  unsigned char lastp = '\0';
612 
613  /* If all characters are ascii, fast path here. */
614  while (slen && (dlen >= 2))
615  {
616  if ((lastp = *p) <= 0x7F)
617  {
618  *q++ = *p++;
619  *q++ = '\0';
620  if (slen != (size_t) -1)
621  {
622  slen--;
623  }
624  dlen -= 2;
625  retval += 2;
626  if (!lastp)
627  break;
628  }
629  else
630  {
631 #ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS
632  goto general_case;
633 #else
634  return retval
635  + convert_string_internal_ntlmssp (from, to, p, slen, q,
636  dlen, allow_bad_conv);
637 #endif
638  }
639  }
640  if (!dlen)
641  {
642  /* Even if we fast path we should note if we ran out of room. */
643  if (((slen != (size_t) -1) && slen)
644  || ((slen == (size_t) -1) && lastp))
645  {
646  errno = E2BIG;
647  }
648  }
649  return retval;
650  }
651 
652 #ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS
653 general_case:
654 #endif
655  return convert_string_internal_ntlmssp (from, to, src, srclen, dest, destlen,
656  allow_bad_conv);
657 }
smb_iconv_close_ntlmssp
int smb_iconv_close_ntlmssp(smb_iconv_t cd)
Definition: iconv.c:203
valid_table_use_unmap_ntlmssp
static bool valid_table_use_unmap_ntlmssp
Definition: charcnv.c:67
valid_table_ntlmssp
static uint8 * valid_table_ntlmssp
Definition: charcnv.c:66
uint8
#define uint8
Definition: charcnv.c:45
CH_UNIX
@ CH_UNIX
Definition: charset.h:27
CH_DOS
@ CH_DOS
Definition: charset.h:29
check_dos_char_slowly_ntlmssp
static int check_dos_char_slowly_ntlmssp(uint16 c)
Definition: charcnv.c:73
NUM_CHARSETS
#define NUM_CHARSETS
Definition: charset.h:34
byteorder.h
Unix SMB/CIFS implementation. SMB Byte handling.
COPY_UCS2_CHAR
#define COPY_UCS2_CHAR(dest, src)
Definition: smb.h:164
charset_t
charset_t
Definition: charset.h:24
SSVAL
#define SSVAL(buf, pos, val)
Definition: byteorder.h:116
CH_UTF8
@ CH_UTF8
Definition: charset.h:30
CH_UTF16LE
@ CH_UTF16LE
Definition: charset.h:25
CH_UTF16BE
@ CH_UTF16BE
Definition: charset.h:31
lp_failed_convert_char_ntlmssp
static char lp_failed_convert_char_ntlmssp(void)
Definition: charcnv.c:99
init_iconv_ntlmssp
void init_iconv_ntlmssp(void)
Definition: charcnv.c:245
smb.h
Unix SMB/CIFS implementation.
charset_name_ntlmssp
static const char * charset_name_ntlmssp(charset_t ch)
Definition: charcnv.c:182
uint16
#define uint16
Definition: charcnv.c:49
proto.h
conv_handles_ntlmssp
static smb_iconv_t conv_handles_ntlmssp[NUM_CHARSETS][NUM_CHARSETS]
Definition: charcnv.c:119
convert_string_ntlmssp
size_t convert_string_ntlmssp(charset_t from, charset_t to, void const *src, size_t srclen, void *dest, size_t destlen, bool allow_badcharcnv)
Definition: charcnv.c:493
lazy_initialize_conv_ntlmssp
void lazy_initialize_conv_ntlmssp(void)
Definition: charcnv.c:226
len
uint8_t len
Definition: nasl_packet_forgery.c:1
smb_iconv_open_ntlmssp
smb_iconv_t smb_iconv_open_ntlmssp(const char *tocode, const char *fromcode)
Definition: iconv.c:101
conv_silent_ntlmssp
static bool conv_silent_ntlmssp
Definition: charcnv.c:121
EILSEQ
#define EILSEQ
Definition: iconv.h:40
init_valid_table_ntlmssp
static void init_valid_table_ntlmssp(void)
Definition: charcnv.c:124
_smb_iconv_t
Definition: smb.h:75
iconv.h
Unix SMB/CIFS implementation. iconv memory system include wrappers.
False
#define False
Definition: charcnv.c:63
True
#define True
Definition: charcnv.c:64
bool
unsigned int bool
Definition: charcnv.c:62
convert_string_internal_ntlmssp
static size_t convert_string_internal_ntlmssp(charset_t from, charset_t to, void const *src, size_t srclen, void *dest, size_t destlen, bool allow_bad_conv)
Definition: charcnv.c:325
smb_iconv_ntlmssp
size_t smb_iconv_ntlmssp(smb_iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
Definition: iconv.c:53
strlen_w_ntlmssp
static size_t strlen_w_ntlmssp(const uint16 *src)
Definition: charcnv.c:165