diff options
| author | Tobias Markmann <tm@ayena.de> | 2015-09-17 08:14:57 (GMT) |
|---|---|---|
| committer | Swift Review <review@swift.im> | 2015-10-16 10:38:19 (GMT) |
| commit | 3a2b966711dbe6fa937c485d7ad56916219badb2 (patch) | |
| tree | 30e9f30bc3f2a3ca6b4ed0c5c11f4ae0703485d0 | |
| parent | 582ca915b5b82ada46d1183a7b882455ee01b7b1 (diff) | |
| download | swift-3a2b966711dbe6fa937c485d7ad56916219badb2.zip swift-3a2b966711dbe6fa937c485d7ad56916219badb2.tar.bz2 | |
Add UTF-8 validation function and validate input to libIDN functions
This is required to protect against the CVE-2015-2059 vulnerability in
libIDN.
Test-Information:
Added unit tests for UTF-8 validation and tested that existing unit
tests still pass.
Change-Id: I0a94136894c6e0004081456c59155a78a3dabf5f
| -rw-r--r-- | Swiften/IDN/LibIDNConverter.cpp | 13 | ||||
| -rw-r--r-- | Swiften/IDN/SConscript | 5 | ||||
| -rw-r--r-- | Swiften/IDN/UTF8Validator.h | 67 | ||||
| -rw-r--r-- | Swiften/IDN/UnitTest/UTF8ValidatorTest.cpp | 54 |
4 files changed, 135 insertions, 4 deletions
diff --git a/Swiften/IDN/LibIDNConverter.cpp b/Swiften/IDN/LibIDNConverter.cpp index f36929a..78303b1 100644 --- a/Swiften/IDN/LibIDNConverter.cpp +++ b/Swiften/IDN/LibIDNConverter.cpp | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Copyright (c) 2012-2013 Isode Limited. | 2 | * Copyright (c) 2012-2015 Isode Limited. |
| 3 | * All rights reserved. | 3 | * All rights reserved. |
| 4 | * See the COPYING file for more information. | 4 | * See the COPYING file for more information. |
| 5 | */ | 5 | */ |
| 6 | 6 | ||
| 7 | #include <Swiften/IDN/LibIDNConverter.h> | 7 | #include <Swiften/IDN/LibIDNConverter.h> |
| @@ -9,16 +9,19 @@ | |||
| 9 | extern "C" { | 9 | extern "C" { |
| 10 | #include <stringprep.h> | 10 | #include <stringprep.h> |
| 11 | #include <idna.h> | 11 | #include <idna.h> |
| 12 | } | 12 | } |
| 13 | 13 | ||
| 14 | #include <vector> | ||
| 15 | #include <cassert> | 14 | #include <cassert> |
| 16 | #include <cstdlib> | 15 | #include <cstdlib> |
| 16 | #include <vector> | ||
| 17 | |||
| 18 | #include <boost/shared_ptr.hpp> | ||
| 19 | |||
| 17 | #include <Swiften/Base/ByteArray.h> | 20 | #include <Swiften/Base/ByteArray.h> |
| 18 | #include <Swiften/Base/SafeAllocator.h> | 21 | #include <Swiften/Base/SafeAllocator.h> |
| 19 | #include <boost/shared_ptr.hpp> | 22 | #include <Swiften/IDN/UTF8Validator.h> |
| 20 | 23 | ||
| 21 | using namespace Swift; | 24 | using namespace Swift; |
| 22 | 25 | ||
| 23 | namespace { | 26 | namespace { |
| 24 | static const int MAX_STRINGPREP_SIZE = 1024; | 27 | static const int MAX_STRINGPREP_SIZE = 1024; |
| @@ -35,10 +38,14 @@ namespace { | |||
| 35 | } | 38 | } |
| 36 | 39 | ||
| 37 | template<typename StringType, typename ContainerType> | 40 | template<typename StringType, typename ContainerType> |
| 38 | ContainerType getStringPreparedInternal(const StringType& s, IDNConverter::StringPrepProfile profile) { | 41 | ContainerType getStringPreparedInternal(const StringType& s, IDNConverter::StringPrepProfile profile) { |
| 39 | ContainerType input(s.begin(), s.end()); | 42 | ContainerType input(s.begin(), s.end()); |
| 43 | if (!UTF8IsValid(s.data(), s.size())) { | ||
| 44 | return ContainerType(); | ||
| 45 | } | ||
| 46 | |||
| 40 | input.resize(MAX_STRINGPREP_SIZE); | 47 | input.resize(MAX_STRINGPREP_SIZE); |
| 41 | if (stringprep(&input[0], MAX_STRINGPREP_SIZE, static_cast<Stringprep_profile_flags>(0), getLibIDNProfile(profile)) == 0) { | 48 | if (stringprep(&input[0], MAX_STRINGPREP_SIZE, static_cast<Stringprep_profile_flags>(0), getLibIDNProfile(profile)) == 0) { |
| 42 | return input; | 49 | return input; |
| 43 | } | 50 | } |
| 44 | else { | 51 | else { |
diff --git a/Swiften/IDN/SConscript b/Swiften/IDN/SConscript index 4c1a71d..7a3c061 100644 --- a/Swiften/IDN/SConscript +++ b/Swiften/IDN/SConscript | |||
| @@ -12,17 +12,20 @@ if myenv.get("HAVE_ICU") : | |||
| 12 | objects += myenv.SwiftenObject(["ICUConverter.cpp"]) | 12 | objects += myenv.SwiftenObject(["ICUConverter.cpp"]) |
| 13 | if myenv.get("HAVE_LIBIDN") : | 13 | if myenv.get("HAVE_LIBIDN") : |
| 14 | myenv.MergeFlags(swiften_env["LIBIDN_FLAGS"]) | 14 | myenv.MergeFlags(swiften_env["LIBIDN_FLAGS"]) |
| 15 | myenv.Append(CPPDEFINES = ["HAVE_LIBIDN"]) | 15 | myenv.Append(CPPDEFINES = ["HAVE_LIBIDN"]) |
| 16 | objects += myenv.SwiftenObject(["LibIDNConverter.cpp"]) | 16 | objects += myenv.SwiftenObject(["LibIDNConverter.cpp"]) |
| 17 | objects += myenv.SwiftenObject(["PlatformIDNConverter.cpp"]) | 17 | objects += myenv.SwiftenObject([ |
| 18 | "PlatformIDNConverter.cpp" | ||
| 19 | ]) | ||
| 18 | 20 | ||
| 19 | swiften_env.Append(SWIFTEN_OBJECTS = [objects]) | 21 | swiften_env.Append(SWIFTEN_OBJECTS = [objects]) |
| 20 | 22 | ||
| 21 | if env["TEST"] : | 23 | if env["TEST"] : |
| 22 | test_env = myenv.Clone() | 24 | test_env = myenv.Clone() |
| 23 | test_env.UseFlags(swiften_env["CPPUNIT_FLAGS"]) | 25 | test_env.UseFlags(swiften_env["CPPUNIT_FLAGS"]) |
| 24 | env.Append(UNITTEST_OBJECTS = test_env.SwiftenObject([ | 26 | env.Append(UNITTEST_OBJECTS = test_env.SwiftenObject([ |
| 25 | File("UnitTest/IDNConverterTest.cpp"), | 27 | File("UnitTest/IDNConverterTest.cpp"), |
| 28 | File("UnitTest/UTF8ValidatorTest.cpp") | ||
| 26 | ])) | 29 | ])) |
| 27 | 30 | ||
| 28 | 31 | ||
diff --git a/Swiften/IDN/UTF8Validator.h b/Swiften/IDN/UTF8Validator.h new file mode 100644 index 0000000..5df8769 --- /dev/null +++ b/Swiften/IDN/UTF8Validator.h | |||
| @@ -0,0 +1,67 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2015 Isode Limited. | ||
| 3 | * All rights reserved. | ||
| 4 | * See the COPYING file for more information. | ||
| 5 | */ | ||
| 6 | |||
| 7 | #pragma once | ||
| 8 | |||
| 9 | #include <cstddef> | ||
| 10 | |||
| 11 | namespace Swift { | ||
| 12 | |||
| 13 | // UTF-8 validation based on the description in https://tools.ietf.org/html/rfc3629#section-3 . | ||
| 14 | template <typename CharType> | ||
| 15 | bool UTF8IsValid(const CharType* data, size_t length) { | ||
| 16 | bool isValid = true; | ||
| 17 | const CharType* current = data; | ||
| 18 | const CharType* end = data + length; | ||
| 19 | while (isValid && (current < end)) { | ||
| 20 | // one byte sequences | ||
| 21 | if ((*current & 0x80) == 0x0) { | ||
| 22 | current++; | ||
| 23 | continue; | ||
| 24 | } | ||
| 25 | // longer byte sequences | ||
| 26 | else { | ||
| 27 | // two byte sequences | ||
| 28 | if ((*current & 0xE0) == 0xC0) { | ||
| 29 | current++; | ||
| 30 | if ( (current < end) && ((*current & 0xC0) == 0x80) ) { | ||
| 31 | current++; | ||
| 32 | continue; | ||
| 33 | } | ||
| 34 | } | ||
| 35 | // three byte sequences | ||
| 36 | else if ((*current & 0xF0) == 0xE0) { | ||
| 37 | current++; | ||
| 38 | if ( ((current + 1) < end) && ((*current & 0xC0) == 0x80) ) { | ||
| 39 | current++; | ||
| 40 | if ((*current & 0xC0) == 0x80) { | ||
| 41 | current++; | ||
| 42 | continue; | ||
| 43 | } | ||
| 44 | } | ||
| 45 | } | ||
| 46 | // four byte sequences | ||
| 47 | else if ((*current & 0xF8) == 0xF0) { | ||
| 48 | current++; | ||
| 49 | if ( ((current + 2) < end) && ((*current & 0xC0) == 0x80) ) { | ||
| 50 | current++; | ||
| 51 | if ((*current & 0xC0) == 0x80) { | ||
| 52 | current++; | ||
| 53 | if ((*current & 0xC0) == 0x80) { | ||
| 54 | current++; | ||
| 55 | continue; | ||
| 56 | } | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | // invalid sequences | ||
| 61 | isValid = false; | ||
| 62 | } | ||
| 63 | } | ||
| 64 | return isValid; | ||
| 65 | } | ||
| 66 | |||
| 67 | } | ||
diff --git a/Swiften/IDN/UnitTest/UTF8ValidatorTest.cpp b/Swiften/IDN/UnitTest/UTF8ValidatorTest.cpp new file mode 100644 index 0000000..0295757 --- /dev/null +++ b/Swiften/IDN/UnitTest/UTF8ValidatorTest.cpp | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2015 Isode Limited. | ||
| 3 | * All rights reserved. | ||
| 4 | * See the COPYING file for more information. | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include <cppunit/extensions/HelperMacros.h> | ||
| 8 | #include <cppunit/extensions/TestFactoryRegistry.h> | ||
| 9 | |||
| 10 | #include <Swiften/IDN/UTF8Validator.h> | ||
| 11 | |||
| 12 | using namespace Swift; | ||
| 13 | |||
| 14 | class UTF8ValidatorTest : public CppUnit::TestFixture { | ||
| 15 | CPPUNIT_TEST_SUITE(UTF8ValidatorTest); | ||
| 16 | |||
| 17 | CPPUNIT_TEST(testValidUTF8Sequences); | ||
| 18 | CPPUNIT_TEST(testInvalidUTF8Sequences); | ||
| 19 | |||
| 20 | CPPUNIT_TEST_SUITE_END(); | ||
| 21 | |||
| 22 | public: | ||
| 23 | void testValidUTF8Sequences() { | ||
| 24 | { | ||
| 25 | unsigned char test[] = {0x74, 0x65, 0x73, 0x74}; | ||
| 26 | CPPUNIT_ASSERT(UTF8IsValid(test, sizeof(test))); | ||
| 27 | } | ||
| 28 | |||
| 29 | { | ||
| 30 | unsigned char test[] = {0xf4, 0x8f, 0x80, 0xbf}; | ||
| 31 | CPPUNIT_ASSERT(UTF8IsValid(test, sizeof(test))); | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | void testInvalidUTF8Sequences() { | ||
| 36 | { | ||
| 37 | unsigned char test[] = {0x41, 0xC2, 0x3E, 0x42}; | ||
| 38 | CPPUNIT_ASSERT(!UTF8IsValid(test, sizeof(test))); | ||
| 39 | } | ||
| 40 | |||
| 41 | { | ||
| 42 | unsigned char test[] = {0xf4}; | ||
| 43 | CPPUNIT_ASSERT(!UTF8IsValid(test, sizeof(test))); | ||
| 44 | } | ||
| 45 | |||
| 46 | { | ||
| 47 | unsigned char test[] = {0xf4, 0x8f, 0x65, 0x73, 0x80, 0xbf}; | ||
| 48 | CPPUNIT_ASSERT(!UTF8IsValid(test, sizeof(test))); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | }; | ||
| 53 | |||
| 54 | CPPUNIT_TEST_SUITE_REGISTRATION(UTF8ValidatorTest); | ||
Swift