diff options
| author | Simon Josefsson <simon@josefsson.org> | 2015-07-08 02:06:22 +0200 |
|---|---|---|
| committer | Simon Josefsson <simon@josefsson.org> | 2015-07-08 02:11:25 +0200 |
| commit | 2e97c2796581c27213962c77f5a8571a598f9a2e (patch) | |
| tree | 8c3a917f4ca4361e10735b9a1cf6079b7f611831 | |
| parent | a8dc72ad1cc0c666a99b472e7fbcee96fe9c5c96 (diff) | |
| download | libidn-2e97c279.tar.gz | |
libidn: stringprep_utf8_to_ucs4 now rejects invalid UTF-8. CVE-2015-2059
| -rw-r--r-- | .gitignore | 8 | ||||
| -rw-r--r-- | NEWS | 31 | ||||
| -rw-r--r-- | lib/gl/Makefile.am | 10 | ||||
| -rw-r--r-- | lib/gl/m4/gnulib-cache.m4 | 3 | ||||
| -rw-r--r-- | lib/gl/m4/gnulib-comp.m4 | 5 | ||||
| -rw-r--r-- | lib/gl/unistr/u8-check.c | 105 | ||||
| -rw-r--r-- | lib/gltests/Makefile.am | 10 | ||||
| -rw-r--r-- | lib/gltests/unistr/test-u8-check.c | 188 | ||||
| -rw-r--r-- | lib/nfkc.c | 19 | ||||
| -rw-r--r-- | lib/strerror-idna.c | 2 | ||||
| -rw-r--r-- | lib/strerror-stringprep.c | 4 | ||||
| -rw-r--r-- | lib/stringprep.c | 5 | ||||
| -rw-r--r-- | lib/stringprep.h | 1 | ||||
| -rw-r--r-- | tests/Makefile.am | 2 | ||||
| -rw-r--r-- | tests/tst_badutf8.c | 50 | ||||
| -rw-r--r-- | tests/tst_stringprep.c | 22 |
16 files changed, 450 insertions, 15 deletions
@@ -385,8 +385,12 @@ lib/gl/unistr.h lib/gl/unistr/.deps/ lib/gl/unistr/.dirstamp lib/gl/unistr/.libs/ +lib/gl/unistr/u8-check.lo +lib/gl/unistr/u8-check.o lib/gl/unistr/u8-mbtoucr.lo lib/gl/unistr/u8-mbtoucr.o +lib/gl/unistr/u8-to-u32.lo +lib/gl/unistr/u8-to-u32.o lib/gl/unistr/u8-uctomb-aux.lo lib/gl/unistr/u8-uctomb-aux.o lib/gl/unistr/u8-uctomb.lo @@ -471,6 +475,7 @@ lib/gltests/test-thread_create lib/gltests/test-thread_create.o lib/gltests/test-thread_self lib/gltests/test-thread_self.o +lib/gltests/test-u8-check lib/gltests/test-u8-mbtoucr lib/gltests/test-u8-uctomb lib/gltests/test-unistd @@ -484,6 +489,7 @@ lib/gltests/unistd.h lib/gltests/unistd.o lib/gltests/unistr/.deps/ lib/gltests/unistr/.dirstamp +lib/gltests/unistr/test-u8-check.o lib/gltests/unistr/test-u8-mbtoucr.o lib/gltests/unistr/test-u8-uctomb.o lib/gltests/unistr/unistr @@ -606,6 +612,8 @@ tests/.deps/ tests/Makefile tests/Makefile.in tests/libutils.a +tests/tst_badutf8 +tests/tst_badutf8.o tests/tst_idna tests/tst_idna.o tests/tst_idna2 @@ -2,7 +2,36 @@ Libidn NEWS -- History of user-visible changes. -*- outline -*- Copyright (C) 2002-2015 Simon Josefsson See the end for copying conditions. -* Version 1.31 (unreleased) [stable] +* Version 1.31 (unreleased) [beta] + +** libidn: stringprep_utf8_to_ucs4 now rejects invalid UTF-8. CVE-2015-2059 +This function has always been documented to not validate that the +input UTF-8 string is actually valid UTF-8. Like the rest of the API, +when you call a function that works on UTF-8 data, you have to pass it +valid UTF-8 data. Application writers appear to have difficulties +using interfaces designed like that, as bugs triggered by invalid +UTF-8 has been identified in a number of projects (jabberd2, gnutls, +wget, and curl). While we could introduce a new API to perform UTF-8 +validation, so that applications can easily implement the proper +checks, this appear error prone because there is a risk that the check +will be forgotten. Instead, we took the more radical approach of +modifying the documentation and the implementation of the API. The +intention is that all functions that accepts UTF-8 data should +validate it before use. This will solve the problem for applications, +without needing to change them. This change has the unfortunate +side-effect that Surrogate codes (see section 5.5 of RFC 3454) no +longer trigger the STRINGPREP_CONTAINS_PROHIBITED error code but +instead will trigger the newly introduced STRINGPREP_ICONV_ERROR error +code, as the gnulib/libunistring-based code that we use to test +UTF-8-compliance rejects Surrogate codes. We hope that this is an +acceptable cost to live with in order to improve application security. +We welcome feedback on this solution, and we are marking this release +as beta rather than stable to signal that we may reconsider this +approach if people disagree. Reported by several people including +Thijs Alkemade, Gustavo Grieco, Daniel Stenberg, and Nikos +Mavrogiannopoulos. + +** libidn: Added STRINGPREP_ICONV_ERROR error code. ** libidn: Workaround valgrind/gcc/glibc issue. Valgrind reported a 'Invalid read of size 4' that was caused by diff --git a/lib/gl/Makefile.am b/lib/gl/Makefile.am index e453192..3902005 100644 --- a/lib/gl/Makefile.am +++ b/lib/gl/Makefile.am @@ -21,7 +21,7 @@ # the same distribution terms as the rest of that program. # # Generated by gnulib-tool. -# Reproduce by: gnulib-tool --import --dir=. --local-dir=lib/gl/override --lib=libgnu --source-base=lib/gl --m4-base=lib/gl/m4 --doc-base=doc --tests-base=lib/gltests --aux-dir=build-aux --with-tests --avoid=iconv-h-tests --avoid=string-tests --avoid=wchar-tests --lgpl=2 --no-conditional-dependencies --libtool --macro-prefix=lgl --no-vc-files gettext-h lib-msvc-compat lib-symbol-versions lib-symbol-visibility stdint striconv strverscmp +# Reproduce by: gnulib-tool --import --dir=. --local-dir=lib/gl/override --lib=libgnu --source-base=lib/gl --m4-base=lib/gl/m4 --doc-base=doc --tests-base=lib/gltests --aux-dir=build-aux --with-tests --avoid=iconv-h-tests --avoid=string-tests --avoid=wchar-tests --lgpl=2 --no-conditional-dependencies --libtool --macro-prefix=lgl --no-vc-files gettext-h lib-msvc-compat lib-symbol-versions lib-symbol-visibility stdint striconv strverscmp unistr/u8-check AUTOMAKE_OPTIONS = 1.9.6 gnits subdir-objects @@ -524,6 +524,14 @@ EXTRA_DIST += unistr.in.h ## end gnulib module unistr/base +## begin gnulib module unistr/u8-check + +if LIBUNISTRING_COMPILE_UNISTR_U8_CHECK +libgnu_la_SOURCES += unistr/u8-check.c +endif + +## end gnulib module unistr/u8-check + ## begin gnulib module unistr/u8-mbtoucr if LIBUNISTRING_COMPILE_UNISTR_U8_MBTOUCR diff --git a/lib/gl/m4/gnulib-cache.m4 b/lib/gl/m4/gnulib-cache.m4 index 16a70bb..1c59f8f 100644 --- a/lib/gl/m4/gnulib-cache.m4 +++ b/lib/gl/m4/gnulib-cache.m4 @@ -27,7 +27,7 @@ # Specification in the form of a command-line invocation: -# gnulib-tool --import --dir=. --local-dir=lib/gl/override --lib=libgnu --source-base=lib/gl --m4-base=lib/gl/m4 --doc-base=doc --tests-base=lib/gltests --aux-dir=build-aux --with-tests --avoid=iconv-h-tests --avoid=string-tests --avoid=wchar-tests --lgpl=2 --no-conditional-dependencies --libtool --macro-prefix=lgl --no-vc-files gettext-h lib-msvc-compat lib-symbol-versions lib-symbol-visibility stdint striconv strverscmp +# gnulib-tool --import --dir=. --local-dir=lib/gl/override --lib=libgnu --source-base=lib/gl --m4-base=lib/gl/m4 --doc-base=doc --tests-base=lib/gltests --aux-dir=build-aux --with-tests --avoid=iconv-h-tests --avoid=string-tests --avoid=wchar-tests --lgpl=2 --no-conditional-dependencies --libtool --macro-prefix=lgl --no-vc-files gettext-h lib-msvc-compat lib-symbol-versions lib-symbol-visibility stdint striconv strverscmp unistr/u8-check # Specification in the form of a few gnulib-tool.m4 macro invocations: gl_LOCAL_DIR([lib/gl/override]) @@ -39,6 +39,7 @@ gl_MODULES([ stdint striconv strverscmp + unistr/u8-check ]) gl_AVOID([iconv-h-tests string-tests wchar-tests]) gl_SOURCE_BASE([lib/gl]) diff --git a/lib/gl/m4/gnulib-comp.m4 b/lib/gl/m4/gnulib-comp.m4 index 83f0b0f..715104c 100644 --- a/lib/gl/m4/gnulib-comp.m4 +++ b/lib/gl/m4/gnulib-comp.m4 @@ -117,6 +117,8 @@ AC_DEFUN([lgl_EARLY], # Code from module unistd: # Code from module unistd-tests: # Code from module unistr/base: + # Code from module unistr/u8-check: + # Code from module unistr/u8-check-tests: # Code from module unistr/u8-mbtoucr: # Code from module unistr/u8-mbtoucr-tests: # Code from module unistr/u8-uctomb: @@ -182,6 +184,7 @@ AC_DEFUN([lgl_INIT], gl_SYS_TYPES_H AC_PROG_MKDIR_P gl_LIBUNISTRING_LIBHEADER([0.9.4], [unistr.h]) + gl_LIBUNISTRING_MODULE([0.9], [unistr/u8-check]) gl_MODULE_INDICATOR([unistr/u8-mbtoucr]) gl_LIBUNISTRING_MODULE([0.9], [unistr/u8-mbtoucr]) gl_MODULE_INDICATOR([unistr/u8-uctomb]) @@ -411,6 +414,7 @@ AC_DEFUN([lgl_FILE_LIST], [ lib/strverscmp.c lib/sys_types.in.h lib/unistr.in.h + lib/unistr/u8-check.c lib/unistr/u8-mbtoucr.c lib/unistr/u8-uctomb-aux.c lib/unistr/u8-uctomb.c @@ -511,6 +515,7 @@ AC_DEFUN([lgl_FILE_LIST], [ tests/test-unsetenv.c tests/test-verify.c tests/test-verify.sh + tests/unistr/test-u8-check.c tests/unistr/test-u8-mbtoucr.c tests/unistr/test-u8-uctomb.c tests=lib/alloca.in.h diff --git a/lib/gl/unistr/u8-check.c b/lib/gl/unistr/u8-check.c new file mode 100644 index 0000000..0e87f11 --- /dev/null +++ b/lib/gl/unistr/u8-check.c @@ -0,0 +1,105 @@ +/* Check UTF-8 string. + Copyright (C) 2002, 2006-2007, 2009-2015 Free Software Foundation, Inc. + Written by Bruno Haible <bruno@clisp.org>, 2002. + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> + +/* Specification. */ +#include "unistr.h" + +const uint8_t * +u8_check (const uint8_t *s, size_t n) +{ + const uint8_t *s_end = s + n; + + while (s < s_end) + { + /* Keep in sync with unistr.h and u8-mbtouc-aux.c. */ + uint8_t c = *s; + + if (c < 0x80) + { + s++; + continue; + } + if (c >= 0xc2) + { + if (c < 0xe0) + { + if (s + 2 <= s_end + && (s[1] ^ 0x80) < 0x40) + { + s += 2; + continue; + } + } + else if (c < 0xf0) + { + if (s + 3 <= s_end + && (s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40 + && (c >= 0xe1 || s[1] >= 0xa0) + && (c != 0xed || s[1] < 0xa0)) + { + s += 3; + continue; + } + } + else if (c < 0xf8) + { + if (s + 4 <= s_end + && (s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40 + && (s[3] ^ 0x80) < 0x40 + && (c >= 0xf1 || s[1] >= 0x90) +#if 1 + && (c < 0xf4 || (c == 0xf4 && s[1] < 0x90)) +#endif + ) + { + s += 4; + continue; + } + } +#if 0 + else if (c < 0xfc) + { + if (s + 5 <= s_end + && (s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40 + && (s[3] ^ 0x80) < 0x40 && (s[4] ^ 0x80) < 0x40 + && (c >= 0xf9 || s[1] >= 0x88)) + { + s += 5; + continue; + } + } + else if (c < 0xfe) + { + if (s + 6 <= s_end + && (s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40 + && (s[3] ^ 0x80) < 0x40 && (s[4] ^ 0x80) < 0x40 + && (s[5] ^ 0x80) < 0x40 + && (c >= 0xfd || s[1] >= 0x84)) + { + s += 6; + continue; + } + } +#endif + } + /* invalid or incomplete multibyte character */ + return s; + } + return NULL; +} diff --git a/lib/gltests/Makefile.am b/lib/gltests/Makefile.am index 4544746..d6de4bc 100644 --- a/lib/gltests/Makefile.am +++ b/lib/gltests/Makefile.am @@ -880,6 +880,16 @@ EXTRA_DIST += test-unistd.c ## end gnulib module unistd-tests +## begin gnulib module unistr/u8-check-tests + +TESTS += test-u8-check +check_PROGRAMS += test-u8-check +test_u8_check_SOURCES = unistr/test-u8-check.c +test_u8_check_LDADD = $(LDADD) $(LIBUNISTRING) +EXTRA_DIST += unistr/test-u8-check.c macros.h + +## end gnulib module unistr/u8-check-tests + ## begin gnulib module unistr/u8-mbtoucr-tests TESTS += test-u8-mbtoucr diff --git a/lib/gltests/unistr/test-u8-check.c b/lib/gltests/unistr/test-u8-check.c new file mode 100644 index 0000000..fcf678c --- /dev/null +++ b/lib/gltests/unistr/test-u8-check.c @@ -0,0 +1,188 @@ +/* Test of u8_check() function. + Copyright (C) 2010-2015 Free Software Foundation, Inc. + + 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <bruno@clisp.org>, 2010. */ + +#include <config.h> + +#include "unistr.h" + +#include "macros.h" + +int +main () +{ + /* Test empty string. */ + { + static const uint8_t input[] = ""; + ASSERT (u8_check (input, 0) == NULL); + } + + /* Test valid non-empty string. */ + { + static const uint8_t input[] = /* "Данило Шеган" */ + "\320\224\320\260\320\275\320\270\320\273\320\276 \320\250\320\265\320\263\320\260\320\275"; + ASSERT (u8_check (input, sizeof (input) - 1) == NULL); + } + + /* Test out-of-range character with 4 bytes: U+110000. */ + { + static const uint8_t input[] = "\320\224\320\260\364\220\200\200"; + ASSERT (u8_check (input, sizeof (input) - 1) == input + 4); + } + + /* Test out-of-range character with 5 bytes: U+200000. */ + { + static const uint8_t input[] = "\320\224\320\260\370\210\200\200\200"; + ASSERT (u8_check (input, sizeof (input) - 1) == input + 4); + } + + /* Test out-of-range character with 6 bytes: U+4000000. */ + { + static const uint8_t input[] = "\320\224\320\260\374\204\200\200\200\200"; + ASSERT (u8_check (input, sizeof (input) - 1) == input + 4); + } + + /* Test invalid lead byte. */ + { + static const uint8_t input[] = "\320\224\320\260\376\200\200\200\200\200"; + ASSERT (u8_check (input, sizeof (input) - 1) == input + 4); + } + { + static const uint8_t input[] = "\320\224\320\260\377\200\200\200\200\200"; + ASSERT (u8_check (input, sizeof (input) - 1) == input + 4); + } + + /* Test overlong 2-byte character. */ + { + static const uint8_t input[] = "\320\224\320\260\301\200"; + ASSERT (u8_check (input, sizeof (input) - 1) == input + 4); + } + + /* Test overlong 3-byte character. */ + { + static const uint8_t input[] = "\320\224\320\260\340\200\277"; + ASSERT (u8_check (input, sizeof (input) - 1) == input + 4); + } + + /* Test overlong 4-byte character. */ + { + static const uint8_t input[] = "\320\224\320\260\360\200\277\277"; + ASSERT (u8_check (input, sizeof (input) - 1) == input + 4); + } + + /* Test invalid bytes in 2-byte character. */ + { + static const uint8_t input[] = "\320\224\320\260\302\200"; + ASSERT (u8_check (input, sizeof (input) - 1) == NULL); + } + { + static const uint8_t input[] = "\320\224\320\260\302\100"; + ASSERT (u8_check (input, sizeof (input) - 1) == input + 4); + } + { + static const uint8_t input[] = "\320\224\320\260\302\300"; + ASSERT (u8_check (input, sizeof (input) - 1) == input + 4); + } + + /* Test invalid bytes in 3-byte character. */ + { + static const uint8_t input[] = "\320\224\320\260\342\200\200"; + ASSERT (u8_check (input, sizeof (input) - 1) == NULL); + } + { + static const uint8_t input[] = "\320\224\320\260\342\100\200"; + ASSERT (u8_check (input, sizeof (input) - 1) == input + 4); + } + { + static const uint8_t input[] = "\320\224\320\260\342\300\200"; + ASSERT (u8_check (input, sizeof (input) - 1) == input + 4); + } + { + static const uint8_t input[] = "\320\224\320\260\342\200\100"; + ASSERT (u8_check (input, sizeof (input) - 1) == input + 4); + } + { + static const uint8_t input[] = "\320\224\320\260\342\200\300"; + ASSERT (u8_check (input, sizeof (input) - 1) == input + 4); + } + + /* Test invalid bytes in 4-byte character. */ + { + static const uint8_t input[] = "\320\224\320\260\362\200\200\200"; + ASSERT (u8_check (input, sizeof (input) - 1) == NULL); + } + { + static const uint8_t input[] = "\320\224\320\260\362\100\200\200"; + ASSERT (u8_check (input, sizeof (input) - 1) == input + 4); + } + { + static const uint8_t input[] = "\320\224\320\260\362\300\200\200"; + ASSERT (u8_check (input, sizeof (input) - 1) == input + 4); + } + { + static const uint8_t input[] = "\320\224\320\260\362\200\100\200"; + ASSERT (u8_check (input, sizeof (input) - 1) == input + 4); + } + { + static const uint8_t input[] = "\320\224\320\260\362\200\300\200"; + ASSERT (u8_check (input, sizeof (input) - 1) == input + 4); + } + { + static const uint8_t input[] = "\320\224\320\260\362\200\200\100"; + ASSERT (u8_check (input, sizeof (input) - 1) == input + 4); + } + { + static const uint8_t input[] = "\320\224\320\260\362\200\200\300"; + ASSERT (u8_check (input, sizeof (input) - 1) == input + 4); + } + + /* Test truncated/incomplete 2-byte character. */ + { + static const uint8_t input[] = "\320\224\320\260\302"; + ASSERT (u8_check (input, sizeof (input) - 1) == input + 4); + } + + /* Test truncated/incomplete 3-byte character. */ + { + static const uint8_t input[] = "\320\224\320\260\342\200"; + ASSERT (u8_check (input, sizeof (input) - 1) == input + 4); + } + + /* Test truncated/incomplete 4-byte character. */ + { + static const uint8_t input[] = "\320\224\320\260\362\200\200"; + ASSERT (u8_check (input, sizeof (input) - 1) == input + 4); + } + + /* Test missing lead byte. */ + { + static const uint8_t input[] = "\320\224\320\260\200\200\200\200\200"; + ASSERT (u8_check (input, sizeof (input) - 1) == input + 4); + } + + /* Test surrogate codepoints. */ + { + static const uint8_t input[] = "\320\224\320\260\355\240\200\355\260\200"; + ASSERT (u8_check (input, sizeof (input) - 1) == input + 4); + } + { + static const uint8_t input[] = "\320\224\320\260\355\260\200"; + ASSERT (u8_check (input, sizeof (input) - 1) == input + 4); + } + + return 0; +} @@ -1002,6 +1002,8 @@ stringprep_unichar_to_utf8 (uint32_t c, char *outbuf) return g_unichar_to_utf8 (c, outbuf); } +#include <unistr.h> + /** * stringprep_utf8_to_ucs4: * @str: a UTF-8 encoded string @@ -1010,9 +1012,10 @@ stringprep_unichar_to_utf8 (uint32_t c, char *outbuf) * @items_written: location to store the number of characters in the * result, or %NULL. * - * Convert a string from UTF-8 to a 32-bit fixed width - * representation as UCS-4, assuming valid UTF-8 input. - * This function does no error checking on the input. + * Convert a string from UTF-8 to a 32-bit fixed width representation + * as UCS-4. The function now performs error checking to verify that + * the input is valid UTF-8 (before it was documented to not do error + * checking). * * Return value: a pointer to a newly allocated UCS-4 string. * This value must be deallocated by the caller. @@ -1020,6 +1023,16 @@ stringprep_unichar_to_utf8 (uint32_t c, char *outbuf) uint32_t * stringprep_utf8_to_ucs4 (const char *str, ssize_t len, size_t * items_written) { + size_t n; + + if (len < 0) + n = strlen (str); + else + n = len; + + if (u8_check ((const uint8_t *) str, n)) + return NULL; + return g_utf8_to_ucs4_fast (str, (glong) len, (glong *) items_written); } diff --git a/lib/strerror-idna.c b/lib/strerror-idna.c index 0163c7e..f1e94ec 100644 --- a/lib/strerror-idna.c +++ b/lib/strerror-idna.c @@ -115,7 +115,7 @@ idna_strerror (Idna_rc rc) break; case IDNA_ICONV_ERROR: - p = _("System iconv failed"); + p = _("Could not convert string in locale encoding"); break; case IDNA_MALLOC_ERROR: diff --git a/lib/strerror-stringprep.c b/lib/strerror-stringprep.c index f3a8c9c..f53fbbd 100644 --- a/lib/strerror-stringprep.c +++ b/lib/strerror-stringprep.c @@ -65,6 +65,7 @@ * This usually indicate a problem in the calling application. * STRINGPREP_UNKNOWN_PROFILE: The supplied profile name was not * known to the library. + * STRINGPREP_ICONV_ERROR: Could not convert string in locale encoding. * STRINGPREP_NFKC_FAILED: The Unicode NFKC operation failed. This * usually indicate an internal error in the library. * STRINGPREP_MALLOC_ERROR: The malloc() was out of memory. This is @@ -121,6 +122,9 @@ stringprep_strerror (Stringprep_rc rc) case STRINGPREP_UNKNOWN_PROFILE: p = _("Unknown profile"); break; + case STRINGPREP_ICONV_ERROR: + p = _("Could not convert string in locale encoding."); + break; case STRINGPREP_NFKC_FAILED: p = _("Unicode normalization failed (internal error)"); diff --git a/lib/stringprep.c b/lib/stringprep.c index fc17c5c..8ff28e6 100644 --- a/lib/stringprep.c +++ b/lib/stringprep.c @@ -380,6 +380,8 @@ stringprep (char *in, free (ucs4); ucs4 = stringprep_utf8_to_ucs4 (in, -1, &ucs4len); + if (ucs4 == NULL) + return STRINGPREP_ICONV_ERROR; maxucs4len = ucs4len + adducs4len; newp = realloc (ucs4, maxucs4len * sizeof (uint32_t)); if (!newp) @@ -402,7 +404,7 @@ stringprep (char *in, utf8 = stringprep_ucs4_to_utf8 (ucs4, ucs4len, 0, 0); free (ucs4); if (!utf8) - return STRINGPREP_MALLOC_ERROR; + return STRINGPREP_ICONV_ERROR; if (strlen (utf8) >= maxlen) { @@ -590,6 +592,7 @@ stringprep_profile (const char *in, * This usually indicate a problem in the calling application. * @STRINGPREP_UNKNOWN_PROFILE: The supplied profile name was not * known to the library. + * @STRINGPREP_ICONV_ERROR: Could not convert string in locale encoding. * @STRINGPREP_NFKC_FAILED: The Unicode NFKC operation failed. This * usually indicate an internal error in the library. * @STRINGPREP_MALLOC_ERROR: The malloc() was out of memory. This is diff --git a/lib/stringprep.h b/lib/stringprep.h index d322a3a..309d01e 100644 --- a/lib/stringprep.h +++ b/lib/stringprep.h @@ -68,6 +68,7 @@ extern "C" STRINGPREP_PROFILE_ERROR = 101, STRINGPREP_FLAG_ERROR = 102, STRINGPREP_UNKNOWN_PROFILE = 103, + STRINGPREP_ICONV_ERROR = 104, /* Internal errors. */ STRINGPREP_NFKC_FAILED = 200, STRINGPREP_MALLOC_ERROR = 201 diff --git a/tests/Makefile.am b/tests/Makefile.am index 1e4efa7..0f890fe 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -27,7 +27,7 @@ libutils_a_SOURCES = utils.h utils.c ctests = tst_stringprep tst_punycode tst_idna tst_idna2 tst_idna3 \ tst_idna4 tst_nfkc tst_pr29 tst_strerror tst_toutf8 \ - tst_symbols + tst_symbols tst_badutf8 if TLD ctests += tst_tld endif diff --git a/tests/tst_badutf8.c b/tests/tst_badutf8.c new file mode 100644 index 0000000..2feb099 --- /dev/null +++ b/tests/tst_badutf8.c @@ -0,0 +1,50 @@ +/* tst_badutf8.c --- Self tests for malformed UTF-8 regressions. + * Copyright (C) 2015 Simon Josefsson + * + * This file is part of GNU Libidn. + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> + +#include <idna.h> +#include <idn-free.h> + +#include "utils.h" + +void +doit (void) +{ + char *badutf8 = strdup ("\x7e\x64\x61\x72\x10\x2f\x2f\xf9\x2b\x71" + "\x60\x79\x7b\x2e\x63\x75\x2b\x61\x65\x72" + "\x75\x65\x56\x66\x7f\x62\xc5\x76\xe5\x00"); + char *s = NULL; + int rc; + + rc = idna_to_ascii_8z (badutf8, &s, 0); + free (badutf8); + if (rc != IDNA_ICONV_ERROR) + fail ("rc %d\n", rc); + + idn_free (s); +} diff --git a/tests/tst_stringprep.c b/tests/tst_stringprep.c index 1856d35..c352dd9 100644 --- a/tests/tst_stringprep.c +++ b/tests/tst_stringprep.c @@ -100,7 +100,8 @@ const struct stringprep strprep[] = { "\xF4\x8F\xBF\xBF", NULL, "Nameprep", 0, STRINGPREP_CONTAINS_PROHIBITED}, {"Surrogate code U+DF42", - "\xED\xBD\x82", NULL, "Nameprep", 0, STRINGPREP_CONTAINS_PROHIBITED}, + "\xED\xBD\x82", NULL, "Nameprep", 0, STRINGPREP_ICONV_ERROR + /* was STRINGPREP_CONTAINS_PROHIBITED */}, {"Non-plain text character U+FFFD", "\xEF\xBF\xBD", NULL, "Nameprep", 0, STRINGPREP_CONTAINS_PROHIBITED}, {"Ideographic description character U+2FF5", @@ -234,15 +235,22 @@ doit (void) hexprint (strprep[i].in, strlen (strprep[i].in)); binprint (strprep[i].in, strlen (strprep[i].in)); } - { uint32_t *l; - char *x; + char *x = NULL; l = stringprep_utf8_to_ucs4 (strprep[i].in, -1, NULL); - x = stringprep_ucs4_to_utf8 (l, -1, NULL, NULL); + if (l) + x = stringprep_ucs4_to_utf8 (l, -1, NULL, NULL); free (l); - - if (strcmp (strprep[i].in, x) != 0) + if (i == 29) + /* Ignoring known bad UTF-8 in entry 29 */ + continue; + else if (l == NULL) + { + fail ("bad UTF-8 in entry %ld\n", i); + continue; + } + else if (strcmp (strprep[i].in, x) != 0) { fail ("bad UTF-8 in entry %ld\n", i); if (debug) @@ -254,10 +262,12 @@ doit (void) escapeprint (x, strlen (x)); hexprint (x, strlen (x)); } + continue; } free (x); } + rc = stringprep_profile (strprep[i].in, &p, strprep[i].profile ? strprep[i].profile : |
