CVE-2015-5330: Fix handling of unicode near string endings
[samba.git] / lib / util / charset / util_str.c
index 30961d068a3eed3fa03cf24f1e6252c54e6381bd..2653bfc2d819585ad0b9acb65dc340ae402d46d1 100644 (file)
@@ -5,6 +5,8 @@
    Copyright (C) Simo Sorce 2001
    Copyright (C) Andrew Bartlett 2011
    Copyright (C) Jeremy Allison  1992-2007
+   Copyright (C) Martin Pool     2003
+   Copyright (C) James Peach    2006
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 #endif
 
 /**
- Case insensitive string compararison
+ Case insensitive string compararison, handle specified for testing
 **/
-_PUBLIC_ int strcasecmp_m(const char *s1, const char *s2)
+_PUBLIC_ int strcasecmp_m_handle(struct smb_iconv_handle *iconv_handle,
+                                const char *s1, const char *s2)
 {
        codepoint_t c1=0, c2=0;
        size_t size1, size2;
-       struct smb_iconv_handle *iconv_handle = get_iconv_handle();
 
        /* handle null ptr comparisons to simplify the use in qsort */
        if (s1 == s2) return 0;
@@ -45,6 +47,11 @@ _PUBLIC_ int strcasecmp_m(const char *s1, const char *s2)
                c1 = next_codepoint_handle(iconv_handle, s1, &size1);
                c2 = next_codepoint_handle(iconv_handle, s2, &size2);
 
+               if (c1 == INVALID_CODEPOINT ||
+                   c2 == INVALID_CODEPOINT) {
+                       return strcasecmp(s1, s2);
+               }
+
                s1 += size1;
                s2 += size2;
 
@@ -52,12 +59,6 @@ _PUBLIC_ int strcasecmp_m(const char *s1, const char *s2)
                        continue;
                }
 
-               if (c1 == INVALID_CODEPOINT ||
-                   c2 == INVALID_CODEPOINT) {
-                       /* what else can we do?? */
-                       return strcasecmp(s1, s2);
-               }
-
                if (toupper_m(c1) != toupper_m(c2)) {
                        return c1 - c2;
                }
@@ -67,13 +68,22 @@ _PUBLIC_ int strcasecmp_m(const char *s1, const char *s2)
 }
 
 /**
- Case insensitive string compararison, length limited
+ Case insensitive string compararison
 **/
-_PUBLIC_ int strncasecmp_m(const char *s1, const char *s2, size_t n)
+_PUBLIC_ int strcasecmp_m(const char *s1, const char *s2)
+{
+       struct smb_iconv_handle *iconv_handle = get_iconv_handle();
+       return strcasecmp_m_handle(iconv_handle, s1, s2);
+}
+
+/**
+ Case insensitive string compararison, length limited, handle specified for testing
+**/
+_PUBLIC_ int strncasecmp_m_handle(struct smb_iconv_handle *iconv_handle,
+                                 const char *s1, const char *s2, size_t n)
 {
        codepoint_t c1=0, c2=0;
        size_t size1, size2;
-       struct smb_iconv_handle *iconv_handle = get_iconv_handle();
 
        /* handle null ptr comparisons to simplify the use in qsort */
        if (s1 == s2) return 0;
@@ -86,6 +96,26 @@ _PUBLIC_ int strncasecmp_m(const char *s1, const char *s2, size_t n)
                c1 = next_codepoint_handle(iconv_handle, s1, &size1);
                c2 = next_codepoint_handle(iconv_handle, s2, &size2);
 
+               if (c1 == INVALID_CODEPOINT ||
+                   c2 == INVALID_CODEPOINT) {
+                       /*
+                        * n was specified in characters,
+                        * now we must convert it to bytes.
+                        * As bytes are the smallest
+                        * character unit, the following
+                        * increment and strncasecmp is always
+                        * safe.
+                        *
+                        * The source string was already known
+                        * to be n characters long, so we are
+                        * guaranteed to be able to look at the
+                        * (n remaining + size1) bytes from the
+                        * s1 position).
+                        */
+                       n += size1;
+                       return strncasecmp(s1, s2, n);
+               }
+
                s1 += size1;
                s2 += size2;
 
@@ -93,12 +123,6 @@ _PUBLIC_ int strncasecmp_m(const char *s1, const char *s2, size_t n)
                        continue;
                }
 
-               if (c1 == INVALID_CODEPOINT ||
-                   c2 == INVALID_CODEPOINT) {
-                       /* what else can we do?? */
-                       return strcasecmp(s1, s2);
-               }
-
                if (toupper_m(c1) != toupper_m(c2)) {
                        return c1 - c2;
                }
@@ -111,6 +135,15 @@ _PUBLIC_ int strncasecmp_m(const char *s1, const char *s2, size_t n)
        return *s1 - *s2;
 }
 
+/**
+ Case insensitive string compararison, length limited
+**/
+_PUBLIC_ int strncasecmp_m(const char *s1, const char *s2, size_t n)
+{
+       struct smb_iconv_handle *iconv_handle = get_iconv_handle();
+       return strncasecmp_m_handle(iconv_handle, s1, s2, n);
+}
+
 /**
  * Compare 2 strings.
  *
@@ -140,11 +173,28 @@ _PUBLIC_ bool strcsequal(const char *s1,const char *s2)
  * string which is expected to be in in src_charset encoding to the
  * destination charset (which should be a unicode charset).
  */
-_PUBLIC_ size_t strlen_m_ext(const char *s, charset_t src_charset, charset_t dst_charset)
+_PUBLIC_ size_t strlen_m_ext_handle(struct smb_iconv_handle *ic,
+                                   const char *s, charset_t src_charset, charset_t dst_charset)
 {
        size_t count = 0;
-       struct smb_iconv_handle *ic = get_iconv_handle();
 
+#ifdef DEVELOPER
+       switch (dst_charset) {
+       case CH_DOS:
+       case CH_UNIX:
+               smb_panic("cannot call strlen_m_ext() with a variable dest charset (must be UTF16* or UTF8)");
+       default:
+               break;
+       }
+
+       switch (src_charset) {
+       case CH_UTF16LE:
+       case CH_UTF16BE:
+               smb_panic("cannot call strlen_m_ext() with a UTF16 src charset (must be DOS, UNIX, DISPLAY or UTF8)");
+       default:
+               break;
+       }
+#endif
        if (!s) {
                return 0;
        }
@@ -160,7 +210,8 @@ _PUBLIC_ size_t strlen_m_ext(const char *s, charset_t src_charset, charset_t dst
 
        while (*s) {
                size_t c_size;
-               codepoint_t c = next_codepoint_handle_ext(ic, s, src_charset, &c_size);
+               codepoint_t c = next_codepoint_handle_ext(ic, s, strnlen(s, 5),
+                                                         src_charset, &c_size);
                s += c_size;
 
                switch (dst_charset) {
@@ -184,7 +235,7 @@ _PUBLIC_ size_t strlen_m_ext(const char *s, charset_t src_charset, charset_t dst
                                count += 1;
                        } else if (c < 0x800) {
                                count += 2;
-                       } else if (c < 0x1000) {
+                       } else if (c < 0x10000) {
                                count += 3;
                        } else {
                                count += 4;
@@ -203,6 +254,18 @@ _PUBLIC_ size_t strlen_m_ext(const char *s, charset_t src_charset, charset_t dst
        return count;
 }
 
+/**
+ * Calculate the number of units (8 or 16-bit, depending on the
+ * destination charset), that would be needed to convert the input
+ * string which is expected to be in in src_charset encoding to the
+ * destination charset (which should be a unicode charset).
+ */
+_PUBLIC_ size_t strlen_m_ext(const char *s, charset_t src_charset, charset_t dst_charset)
+{
+       struct smb_iconv_handle *ic = get_iconv_handle();
+       return strlen_m_ext_handle(ic, s, src_charset, dst_charset);
+}
+
 _PUBLIC_ size_t strlen_m_ext_term(const char *s, const charset_t src_charset,
                                  const charset_t dst_charset)
 {
@@ -279,7 +342,7 @@ _PUBLIC_ char *strchr_m(const char *src, char c)
 
        for (s = src; *s && !(((unsigned char)s[0]) & 0x80); s++) {
                if (*s == c)
-                       return (char *)s;
+                       return discard_const_p(char, s);
        }
 
        if (!*s)
@@ -347,7 +410,7 @@ _PUBLIC_ char *strrchr_m(const char *s, char c)
                                        break;
                                }
                                /* No - we have a match ! */
-                               return (char *)cp;
+                               return discard_const_p(char , cp);
                        }
                } while (cp-- != s);
                if (!got_mb)
@@ -369,9 +432,9 @@ _PUBLIC_ char *strrchr_m(const char *s, char c)
 /**
   return True if any (multi-byte) character is lower case
 */
-_PUBLIC_ bool strhaslower(const char *string)
+_PUBLIC_ bool strhaslower_handle(struct smb_iconv_handle *ic,
+                                const char *string)
 {
-       struct smb_iconv_handle *ic = get_iconv_handle();
        while (*string) {
                size_t c_size;
                codepoint_t s;
@@ -390,12 +453,18 @@ _PUBLIC_ bool strhaslower(const char *string)
        return false;
 }
 
+_PUBLIC_ bool strhaslower(const char *string)
+{
+       struct smb_iconv_handle *ic = get_iconv_handle();
+       return strhaslower_handle(ic, string);
+}
+
 /**
   return True if any (multi-byte) character is upper case
 */
-_PUBLIC_ bool strhasupper(const char *string)
+_PUBLIC_ bool strhasupper_handle(struct smb_iconv_handle *ic,
+                                const char *string)
 {
-       struct smb_iconv_handle *ic = get_iconv_handle();
        while (*string) {
                size_t c_size;
                codepoint_t s;
@@ -414,3 +483,89 @@ _PUBLIC_ bool strhasupper(const char *string)
        return false;
 }
 
+_PUBLIC_ bool strhasupper(const char *string)
+{
+       struct smb_iconv_handle *ic = get_iconv_handle();
+       return strhasupper_handle(ic, string);
+}
+
+/***********************************************************************
+ strstr_m - We convert via ucs2 for now.
+***********************************************************************/
+
+char *strstr_m(const char *src, const char *findstr)
+{
+       smb_ucs2_t *p;
+       smb_ucs2_t *src_w, *find_w;
+       const char *s;
+       char *s2;
+       char *retp;
+       size_t converted_size, findstr_len = 0;
+
+       TALLOC_CTX *frame; /* Only set up in the iconv case */
+
+       /* for correctness */
+       if (!findstr[0]) {
+               return discard_const_p(char, src);
+       }
+
+       /* Samba does single character findstr calls a *lot*. */
+       if (findstr[1] == '\0')
+               return strchr_m(src, *findstr);
+
+       /* We optimise for the ascii case, knowing that all our
+          supported multi-byte character sets are ascii-compatible
+          (ie. they match for the first 128 chars) */
+
+       for (s = src; *s && !(((unsigned char)s[0]) & 0x80); s++) {
+               if (*s == *findstr) {
+                       if (!findstr_len)
+                               findstr_len = strlen(findstr);
+
+                       if (strncmp(s, findstr, findstr_len) == 0) {
+                               return discard_const_p(char, s);
+                       }
+               }
+       }
+
+       if (!*s)
+               return NULL;
+
+#if 1 /* def BROKEN_UNICODE_COMPOSE_CHARACTERS */
+       /* 'make check' fails unless we do this */
+
+       /* With compose characters we must restart from the beginning. JRA. */
+       s = src;
+#endif
+
+       frame = talloc_stackframe();
+
+       if (!push_ucs2_talloc(frame, &src_w, src, &converted_size)) {
+               DEBUG(0,("strstr_m: src malloc fail\n"));
+               TALLOC_FREE(frame);
+               return NULL;
+       }
+
+       if (!push_ucs2_talloc(frame, &find_w, findstr, &converted_size)) {
+               DEBUG(0,("strstr_m: find malloc fail\n"));
+               TALLOC_FREE(frame);
+               return NULL;
+       }
+
+       p = strstr_w(src_w, find_w);
+
+       if (!p) {
+               TALLOC_FREE(frame);
+               return NULL;
+       }
+
+       *p = 0;
+       if (!pull_ucs2_talloc(frame, &s2, src_w, &converted_size)) {
+               TALLOC_FREE(frame);
+               DEBUG(0,("strstr_m: dest malloc fail\n"));
+               return NULL;
+       }
+       retp = discard_const_p(char, (s+strlen(s2)));
+       TALLOC_FREE(frame);
+       return retp;
+}