diff options
| author | Mili Verma <mili.verma@isode.com> | 2015-06-25 12:13:14 (GMT) | 
|---|---|---|
| committer | Kevin Smith <kevin.smith@isode.com> | 2015-06-29 17:02:01 (GMT) | 
| commit | 394642c69bc232429621e209b787d3496967b37b (patch) | |
| tree | 12fda8350b5694b4e6dc93899debc5a8c46e0adf | |
| parent | ac45f360be049242a89ae80258e4ea8350f909ba (diff) | |
| download | swift-394642c69bc232429621e209b787d3496967b37b.zip swift-394642c69bc232429621e209b787d3496967b37b.tar.bz2 | |
Add WindowsServicePrincipalName class
Test-information:
Tested on Windows using WIP GSSAPI code.
Unit tests pass.
Change-Id: If872863d6a8b5a164f8ebec4f88e9939b4e73c62
| -rw-r--r-- | BuildTools/SCons/SConscript.boot | 2 | ||||
| -rw-r--r-- | Swiften/SASL/SConscript | 9 | ||||
| -rw-r--r-- | Swiften/SASL/UnitTest/WindowsServicePrincipalNameTest.cpp | 161 | ||||
| -rw-r--r-- | Swiften/SASL/WindowsServicePrincipalName.cpp | 106 | ||||
| -rw-r--r-- | Swiften/SASL/WindowsServicePrincipalName.h | 71 | 
5 files changed, 348 insertions, 1 deletions
| diff --git a/BuildTools/SCons/SConscript.boot b/BuildTools/SCons/SConscript.boot index ff24eeb..2415fde 100644 --- a/BuildTools/SCons/SConscript.boot +++ b/BuildTools/SCons/SConscript.boot @@ -333,7 +333,7 @@ if env.get("coverage", 0) :  	env.Append(LINKFLAGS = ["-fprofile-arcs", "-ftest-coverage"])  if env["PLATFORM"] == "win32" : -	env.Append(LIBS = ["user32", "crypt32", "dnsapi", "iphlpapi", "ws2_32", "wsock32", "Advapi32"]) +	env.Append(LIBS = ["user32", "crypt32", "dnsapi", "iphlpapi", "ws2_32", "wsock32", "Advapi32", "ntdsapi"])  	env.Append(CCFLAGS = ["/EHsc", "/nologo", "/Zm256"])  	env.Append(LINKFLAGS = ["/INCREMENTAL:no", "/NOLOGO"])  	if int(env["MSVS_VERSION"].split(".")[0]) < 10 : diff --git a/Swiften/SASL/SConscript b/Swiften/SASL/SConscript index 6509547..faf6320 100644 --- a/Swiften/SASL/SConscript +++ b/Swiften/SASL/SConscript @@ -11,6 +11,11 @@ objects = myenv.SwiftenObject([  		"DIGESTMD5Properties.cpp",  		"DIGESTMD5ClientAuthenticator.cpp",  	]) +if myenv["PLATFORM"] == "win32" : +	objects += myenv.SwiftenObject([ +		"WindowsServicePrincipalName.cpp", +	]) +  swiften_env.Append(SWIFTEN_OBJECTS = [objects])  env.Append(UNITTEST_SOURCES = [ @@ -20,3 +25,7 @@ env.Append(UNITTEST_SOURCES = [  			File("UnitTest/DIGESTMD5PropertiesTest.cpp"),  			File("UnitTest/DIGESTMD5ClientAuthenticatorTest.cpp"),  	]) +if myenv["PLATFORM"] == "win32" : +	env.Append(UNITTEST_SOURCES = [ +			File("UnitTest/WindowsServicePrincipalNameTest.cpp"), +		]) diff --git a/Swiften/SASL/UnitTest/WindowsServicePrincipalNameTest.cpp b/Swiften/SASL/UnitTest/WindowsServicePrincipalNameTest.cpp new file mode 100644 index 0000000..1b0e6f6 --- /dev/null +++ b/Swiften/SASL/UnitTest/WindowsServicePrincipalNameTest.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> + +#include <Swiften/SASL/WindowsServicePrincipalName.h> + +using namespace Swift; + +class WindowsServicePrincipalNameTest : public CppUnit::TestFixture { +		CPPUNIT_TEST_SUITE(WindowsServicePrincipalNameTest); +		CPPUNIT_TEST(testServiceClass); +		CPPUNIT_TEST(testServiceName); +		CPPUNIT_TEST(testInstanceName); +		CPPUNIT_TEST(testInstancePort); +		CPPUNIT_TEST(testReferrer); +		CPPUNIT_TEST_SUITE_END(); + +	public: +		void testServiceClass() { +			WindowsServicePrincipalName spn("mlink.adlon.isode.net"); + +			CPPUNIT_ASSERT_EQUAL(spn.toString(), std::string("xmpp/mlink.adlon.isode.net")); + +			spn.setServiceClass("ldap"); +			CPPUNIT_ASSERT_EQUAL(spn.toString(), std::string("ldap/mlink.adlon.isode.net")); + +			spn.setServiceClass("中文"); +			CPPUNIT_ASSERT_EQUAL(spn.toString(), std::string("中文/mlink.adlon.isode.net")); + +			try { +				spn.setServiceClass(""); +				spn.toString(); +				CPPUNIT_ASSERT(false); +			} catch (std::runtime_error) { +				/* expected */ +			} + +			try { +				spn.setServiceClass("xm/pp"); /* Foward slash not allowed */ +				spn.toString(); +				CPPUNIT_ASSERT(false); +			} catch (std::runtime_error) { +				/* expected */ +			} +		} + +		void testServiceName() { +			try { +				WindowsServicePrincipalName spn(""); +				spn.toString(); +				CPPUNIT_ASSERT(false); +			} catch (std::runtime_error) { +				/* expected */ +			} + +			try { +				WindowsServicePrincipalName spn2("mlink/adlon.isode.net"); /* Foward slash not allowed */ +				spn2.toString(); +				CPPUNIT_ASSERT(false); +			} catch (std::runtime_error) { +				/* expected */ +			} + +			WindowsServicePrincipalName spn3("mlinkÄ.adlon.isode.net"); +			CPPUNIT_ASSERT_EQUAL(spn3.toString(), std::string("xmpp/mlinkÄ.adlon.isode.net")); +		} + +		void testInstanceName() { +			WindowsServicePrincipalName spn("adlon.isode.net"); + +			spn.setInstanceName("mlink.adlon.isode.net"); +			CPPUNIT_ASSERT_EQUAL(spn.toString(), std::string("xmpp/mlink.adlon.isode.net/adlon.isode.net")); + +			spn.setInstanceName("127.0.0.1"); +			CPPUNIT_ASSERT_EQUAL(spn.toString(), std::string("xmpp/127.0.0.1/adlon.isode.net")); + +			spn.setInstanceName(""); +			CPPUNIT_ASSERT_EQUAL(spn.toString(), std::string("xmpp/adlon.isode.net")); + +			spn.setInstanceName("cañón.adlon.isode.net"); +			CPPUNIT_ASSERT_EQUAL(spn.toString(), std::string("xmpp/cañón.adlon.isode.net/adlon.isode.net")); + +			try { +				spn.setInstanceName("mlink/adlon.isode.net"); /* Foward slash not allowed */ +				spn.toString(); +				CPPUNIT_ASSERT(false); +			} catch (std::runtime_error) { +				/* expected */ +			} +		} + +		void testInstancePort() { +			WindowsServicePrincipalName spn("adlon.isode.net"); + +			spn.setInstanceName("mlink.adlon.isode.net"); +			spn.setInstancePort(6222); +			CPPUNIT_ASSERT_EQUAL(spn.toString(), std::string("xmpp/mlink.adlon.isode.net:6222/adlon.isode.net")); + +			spn.setInstancePort(0); +			CPPUNIT_ASSERT_EQUAL(spn.toString(), std::string("xmpp/mlink.adlon.isode.net/adlon.isode.net")); + +			WindowsServicePrincipalName spn2("mlink.adlon.isode.net"); + +			spn2.setInstancePort(6222); +			CPPUNIT_ASSERT_EQUAL(spn2.toString(), std::string("xmpp/mlink.adlon.isode.net:6222")); + +			spn2.setInstancePort(0); +			CPPUNIT_ASSERT_EQUAL(spn2.toString(), std::string("xmpp/mlink.adlon.isode.net")); +		} + +		void testReferrer() { +			WindowsServicePrincipalName spn("127.0.0.1"); + +			spn.setReferrer("referrer.net"); +			CPPUNIT_ASSERT_EQUAL(spn.toString(), std::string("xmpp/127.0.0.1/referrer.net")); + +			spn.setInstancePort(6222); +			CPPUNIT_ASSERT_EQUAL(spn.toString(), std::string("xmpp/127.0.0.1:6222/referrer.net")); + +			spn.setReferrer("हिन्दी.net"); +			CPPUNIT_ASSERT_EQUAL(spn.toString(), std::string("xmpp/127.0.0.1:6222/हिन्दी.net")); + +			try { +				spn.setReferrer("referrer/net"); /* Foward slash not allowed */ +				spn.toString(); +				CPPUNIT_ASSERT(false); +			} catch (std::runtime_error) { +				/* expected */ +			} + +			try { +				spn.setReferrer(""); /* seems like you must have referrer with an IP */ +				spn.toString(); +				CPPUNIT_ASSERT(false); +			} catch (std::runtime_error) { +				/* expected */ +			} + +			WindowsServicePrincipalName spn2("mlink.adlon.isode.net"); + +			spn2.setReferrer("referrer.net"); /* Referrer ignored if service name is not IP */ +			CPPUNIT_ASSERT_EQUAL(spn2.toString(), std::string("xmpp/mlink.adlon.isode.net")); + +			spn2.setReferrer("referrer/net"); /* Referrer ignored if service name is not IP */ +			CPPUNIT_ASSERT_EQUAL(spn2.toString(), std::string("xmpp/mlink.adlon.isode.net")); + +			WindowsServicePrincipalName spn3("adlon.isode.net"); + +			spn3.setInstanceName("mlink.adlon.isode.net"); +			spn3.setInstancePort(6222); +			spn3.setReferrer("referrer.net"); /* Referrer ignored if service name is not IP */ +			CPPUNIT_ASSERT_EQUAL(spn3.toString(), std::string("xmpp/mlink.adlon.isode.net:6222/adlon.isode.net")); + +		} +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(WindowsServicePrincipalNameTest); diff --git a/Swiften/SASL/WindowsServicePrincipalName.cpp b/Swiften/SASL/WindowsServicePrincipalName.cpp new file mode 100644 index 0000000..187e9ac --- /dev/null +++ b/Swiften/SASL/WindowsServicePrincipalName.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include <Swiften/SASL/WindowsServicePrincipalName.h> + +#include <vector> + +#include <Ntdsapi.h> + +#include <Swiften/Base/ByteArray.h> +#include <Swiften/Base/Log.h> +#include <Swiften/Base/String.h> + +namespace Swift { + +WindowsServicePrincipalName::WindowsServicePrincipalName(const std::string& serviceName) : serviceClass_(L"xmpp"), instancePort_(0) { +	serviceName_ = convertStringToWString(serviceName); +} + +WindowsServicePrincipalName::~WindowsServicePrincipalName() { +} + +void WindowsServicePrincipalName::setServiceClass(const std::string& serviceClass) { +	serviceClass_ = convertStringToWString(serviceClass); +} + +void WindowsServicePrincipalName::setInstanceName(const std::string& instanceName) { +	instanceName_ = convertStringToWString(instanceName); +} + +void WindowsServicePrincipalName::setReferrer(const std::string& referrer) { +	referrer_ = convertStringToWString(referrer); +} + +std::string WindowsServicePrincipalName::toString() { +	DWORD length = 512; +	DWORD status = ERROR_BUFFER_OVERFLOW; +	bool firstCall = true; +	std::string str; + +	while (status == ERROR_BUFFER_OVERFLOW) { +		std::vector<wchar_t> value(length); + +		/* length after this call will contain the required length if current length is not enough - so the next call should succeed */ +		status = dsMakeSpn(&length, vecptr(value)); +		if (status == ERROR_SUCCESS) { +			str = convertWStringToString(std::wstring(vecptr(value), length-1 /* trailing 0 character */)); +			break; +		} + +		if ((firstCall == false) || (status != ERROR_BUFFER_OVERFLOW)) { +			std::stringstream errorString; +			boost::system::error_code errorCode(status, boost::system::system_category()); + +			errorString << "Error creating Service Principal Name: status: 0x" << std::hex << status << ": " << errorCode.message(); + +			/* Any other error will be a programming error */ +			throw std::runtime_error(errorString.str()); +		} + +		firstCall = false; +	} + +	SWIFT_LOG(debug) << "SPN: " << str << std::endl; +	return str; +} + +DWORD WindowsServicePrincipalName::dsMakeSpn(DWORD* length, wchar_t* value) { +	DWORD status; + +#ifdef UNICODE +	SWIFT_LOG(debug) << "UNICODE is defined" << std::endl; +#else +	SWIFT_LOG(debug) << "UNICODE is not defined" << std::endl; +#endif + +	SWIFT_LOG(debug) << "serviceClass_: " << convertWStringToString(serviceClass_.c_str()) << std::endl; +	SWIFT_LOG(debug) << "serviceName_: " << convertWStringToString(serviceName_.c_str()) << std::endl; +	SWIFT_LOG(debug) << "instanceName_: " << convertWStringToString(instanceName_.c_str()) << std::endl; +	SWIFT_LOG(debug) << "referrer_: " << convertWStringToString(referrer_.c_str()) << std::endl; +	SWIFT_LOG(debug) << "instancePort_: " << instancePort_ << std::endl; +	SWIFT_LOG(debug) << "length: " << *length << std::endl; + +	/* Call the Unicode function because that is recommended: +https://msdn.microsoft.com/en-us/library/windows/desktop/ff381407%28v=vs.85%29.aspx */ +	status =  DsMakeSpnW( +			serviceClass_.c_str(), +			serviceName_.c_str(), +			instanceName_.empty() ? NULL : instanceName_.c_str(), +			instancePort_, +			referrer_.empty() ? NULL : referrer_.c_str(), +			length, +			value); +	if (status != ERROR_SUCCESS) { +		boost::system::error_code errorCode(status, boost::system::system_category()); + +		SWIFT_LOG(debug) << std::hex << "status: 0x" << status << ": " << errorCode.message() << std::endl; +	} + +	return status; +} + +} diff --git a/Swiften/SASL/WindowsServicePrincipalName.h b/Swiften/SASL/WindowsServicePrincipalName.h new file mode 100644 index 0000000..dc97c93 --- /dev/null +++ b/Swiften/SASL/WindowsServicePrincipalName.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include <string> + +#include <Windows.h> + +#include <Swiften/Base/API.h> + +namespace Swift { +	/* +	 * This represents the SPN used on Windows to identify a service +	 * instance. +	 */ +	class SWIFTEN_API WindowsServicePrincipalName { +		public: +			/* +			 * This assigns the provided service name, +			 * "xmpp" service class, no instance name or referrer, +			 * and default port. All of these (except the service +			 * name) can be set to other values using the provided +			 * methods. +			 * If the constructor is called with the service name +			 * "hostname.example.com" and the provided methods are +			 * not used to change the defaults, then toString() +			 * will return the SPN "xmpp/hostname.example.com". +			 */ +			WindowsServicePrincipalName(const std::string& serviceName); + +			~WindowsServicePrincipalName(); + +			void setServiceClass(const std::string& serviceClass); + +			void setInstanceName(const std::string& instanceName); + +			void setReferrer(const std::string& referrer); + +			/* +			 * This sets a non-default port for the service. Note +			 * that the default value is 0 which indicates the +			 * default port for the service. So if the XMPP service +			 * is using the default port of 5222 for client +			 * connections, then do not set the port to 5222 but let +			 * it remain 0 to indicate that the default port is +			 * used. +			 */ +			void setInstancePort(short int instancePort) { instancePort_ = instancePort; } + +			/* +			 * This follows the rules of SPN creation on Windows and +			 * returns the SPN string constructed from the set +			 * values. +			 */ +			std::string toString(); + +		private: +			DWORD dsMakeSpn(DWORD* length, wchar_t* value); + +		private: +			std::wstring serviceClass_; +			std::wstring serviceName_; +			std::wstring instanceName_; +			USHORT instancePort_; +			std::wstring referrer_; +	}; +} | 
 Swift
 Swift