From 3a2b966711dbe6fa937c485d7ad56916219badb2 Mon Sep 17 00:00:00 2001
From: Tobias Markmann <tm@ayena.de>
Date: Thu, 17 Sep 2015 10:14:57 +0200
Subject: 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

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,5 +1,5 @@
 /*
- * Copyright (c) 2012-2013 Isode Limited.
+ * Copyright (c) 2012-2015 Isode Limited.
  * All rights reserved.
  * See the COPYING file for more information.
  */
@@ -11,12 +11,15 @@ extern "C" {
 	#include <idna.h>
 }
 
-#include <vector>
 #include <cassert>
 #include <cstdlib>
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+
 #include <Swiften/Base/ByteArray.h>
 #include <Swiften/Base/SafeAllocator.h>
-#include <boost/shared_ptr.hpp>
+#include <Swiften/IDN/UTF8Validator.h>
 
 using namespace Swift;
 
@@ -37,6 +40,10 @@ namespace {
 	template<typename StringType, typename ContainerType>
 	ContainerType getStringPreparedInternal(const StringType& s, IDNConverter::StringPrepProfile profile) {
 		ContainerType input(s.begin(), s.end());
+		if (!UTF8IsValid(s.data(), s.size())) {
+			return ContainerType();
+		}
+
 		input.resize(MAX_STRINGPREP_SIZE);
 		if (stringprep(&input[0], MAX_STRINGPREP_SIZE, static_cast<Stringprep_profile_flags>(0), getLibIDNProfile(profile)) == 0) {
 			return input;
diff --git a/Swiften/IDN/SConscript b/Swiften/IDN/SConscript
index 4c1a71d..7a3c061 100644
--- a/Swiften/IDN/SConscript
+++ b/Swiften/IDN/SConscript
@@ -14,7 +14,9 @@ if myenv.get("HAVE_LIBIDN") :
 	myenv.MergeFlags(swiften_env["LIBIDN_FLAGS"])
 	myenv.Append(CPPDEFINES = ["HAVE_LIBIDN"])
 	objects += myenv.SwiftenObject(["LibIDNConverter.cpp"])
-objects += myenv.SwiftenObject(["PlatformIDNConverter.cpp"])
+objects += myenv.SwiftenObject([
+	"PlatformIDNConverter.cpp"
+	])
 
 swiften_env.Append(SWIFTEN_OBJECTS = [objects])
 
@@ -23,6 +25,7 @@ if env["TEST"] :
 	test_env.UseFlags(swiften_env["CPPUNIT_FLAGS"])
 	env.Append(UNITTEST_OBJECTS = test_env.SwiftenObject([
 				File("UnitTest/IDNConverterTest.cpp"),
+				File("UnitTest/UTF8ValidatorTest.cpp")
 	]))
 
 
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 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <cstddef>
+
+namespace Swift {
+
+// UTF-8 validation based on the description in https://tools.ietf.org/html/rfc3629#section-3 .
+template <typename CharType>
+bool UTF8IsValid(const CharType* data, size_t length) {
+	bool isValid = true;
+	const CharType* current = data;
+	const CharType* end = data + length;
+	while (isValid && (current < end)) {
+		// one byte sequences
+		if ((*current & 0x80) == 0x0) {
+			current++;
+			continue;
+		}
+		// longer byte sequences
+		else {
+			// two byte sequences
+			if ((*current & 0xE0) == 0xC0) {
+				current++;
+				if ( (current < end) && ((*current & 0xC0) == 0x80) ) {
+					current++;
+					continue;
+				}
+			}
+			// three byte sequences
+			else if ((*current & 0xF0) == 0xE0) {
+				current++;
+				if ( ((current + 1) < end) && ((*current & 0xC0) == 0x80) ) {
+					current++;
+					if ((*current & 0xC0) == 0x80) {
+						current++;
+						continue;
+					}
+				}
+			}
+			// four byte sequences
+			else if ((*current & 0xF8) == 0xF0) {
+				current++;
+				if ( ((current + 2) < end) && ((*current & 0xC0) == 0x80) ) {
+					current++;
+					if ((*current & 0xC0) == 0x80) {
+						current++;
+						if ((*current & 0xC0) == 0x80) {
+							current++;
+							continue;
+						}
+					}
+				}
+			}
+			// invalid sequences
+			isValid = false;
+		}
+	}
+	return isValid;
+}
+
+}
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 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include <Swiften/IDN/UTF8Validator.h>
+
+using namespace Swift;
+
+class UTF8ValidatorTest : public CppUnit::TestFixture {
+	CPPUNIT_TEST_SUITE(UTF8ValidatorTest);
+
+	CPPUNIT_TEST(testValidUTF8Sequences);
+	CPPUNIT_TEST(testInvalidUTF8Sequences);
+
+	CPPUNIT_TEST_SUITE_END();
+
+public:
+	void testValidUTF8Sequences() {
+		{
+			unsigned char test[] = {0x74, 0x65, 0x73, 0x74};
+			CPPUNIT_ASSERT(UTF8IsValid(test, sizeof(test)));
+		}
+
+		{
+			unsigned char test[] = {0xf4, 0x8f, 0x80, 0xbf};
+			CPPUNIT_ASSERT(UTF8IsValid(test, sizeof(test)));
+		}
+	}
+
+	void testInvalidUTF8Sequences() {
+		{
+			unsigned char test[] = {0x41, 0xC2, 0x3E, 0x42};
+			CPPUNIT_ASSERT(!UTF8IsValid(test, sizeof(test)));
+		}
+
+		{
+			unsigned char test[] = {0xf4};
+			CPPUNIT_ASSERT(!UTF8IsValid(test, sizeof(test)));
+		}
+
+		{
+			unsigned char test[] = {0xf4, 0x8f, 0x65, 0x73, 0x80, 0xbf};
+			CPPUNIT_ASSERT(!UTF8IsValid(test, sizeof(test)));
+		}
+	}
+
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(UTF8ValidatorTest);
-- 
cgit v0.10.2-6-g49f6