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) : | |||
| 333 | env.Append(LINKFLAGS = ["-fprofile-arcs", "-ftest-coverage"]) | 333 | env.Append(LINKFLAGS = ["-fprofile-arcs", "-ftest-coverage"]) |
| 334 | 334 | ||
| 335 | if env["PLATFORM"] == "win32" : | 335 | if env["PLATFORM"] == "win32" : |
| 336 | env.Append(LIBS = ["user32", "crypt32", "dnsapi", "iphlpapi", "ws2_32", "wsock32", "Advapi32"]) | 336 | env.Append(LIBS = ["user32", "crypt32", "dnsapi", "iphlpapi", "ws2_32", "wsock32", "Advapi32", "ntdsapi"]) |
| 337 | env.Append(CCFLAGS = ["/EHsc", "/nologo", "/Zm256"]) | 337 | env.Append(CCFLAGS = ["/EHsc", "/nologo", "/Zm256"]) |
| 338 | env.Append(LINKFLAGS = ["/INCREMENTAL:no", "/NOLOGO"]) | 338 | env.Append(LINKFLAGS = ["/INCREMENTAL:no", "/NOLOGO"]) |
| 339 | if int(env["MSVS_VERSION"].split(".")[0]) < 10 : | 339 | 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([ | |||
| 11 | "DIGESTMD5Properties.cpp", | 11 | "DIGESTMD5Properties.cpp", |
| 12 | "DIGESTMD5ClientAuthenticator.cpp", | 12 | "DIGESTMD5ClientAuthenticator.cpp", |
| 13 | ]) | 13 | ]) |
| 14 | if myenv["PLATFORM"] == "win32" : | ||
| 15 | objects += myenv.SwiftenObject([ | ||
| 16 | "WindowsServicePrincipalName.cpp", | ||
| 17 | ]) | ||
| 18 | |||
| 14 | swiften_env.Append(SWIFTEN_OBJECTS = [objects]) | 19 | swiften_env.Append(SWIFTEN_OBJECTS = [objects]) |
| 15 | 20 | ||
| 16 | env.Append(UNITTEST_SOURCES = [ | 21 | env.Append(UNITTEST_SOURCES = [ |
| @@ -20,3 +25,7 @@ env.Append(UNITTEST_SOURCES = [ | |||
| 20 | File("UnitTest/DIGESTMD5PropertiesTest.cpp"), | 25 | File("UnitTest/DIGESTMD5PropertiesTest.cpp"), |
| 21 | File("UnitTest/DIGESTMD5ClientAuthenticatorTest.cpp"), | 26 | File("UnitTest/DIGESTMD5ClientAuthenticatorTest.cpp"), |
| 22 | ]) | 27 | ]) |
| 28 | if myenv["PLATFORM"] == "win32" : | ||
| 29 | env.Append(UNITTEST_SOURCES = [ | ||
| 30 | File("UnitTest/WindowsServicePrincipalNameTest.cpp"), | ||
| 31 | ]) | ||
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 @@ | |||
| 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 | |||
| 9 | #include <Swiften/SASL/WindowsServicePrincipalName.h> | ||
| 10 | |||
| 11 | using namespace Swift; | ||
| 12 | |||
| 13 | class WindowsServicePrincipalNameTest : public CppUnit::TestFixture { | ||
| 14 | CPPUNIT_TEST_SUITE(WindowsServicePrincipalNameTest); | ||
| 15 | CPPUNIT_TEST(testServiceClass); | ||
| 16 | CPPUNIT_TEST(testServiceName); | ||
| 17 | CPPUNIT_TEST(testInstanceName); | ||
| 18 | CPPUNIT_TEST(testInstancePort); | ||
| 19 | CPPUNIT_TEST(testReferrer); | ||
| 20 | CPPUNIT_TEST_SUITE_END(); | ||
| 21 | |||
| 22 | public: | ||
| 23 | void testServiceClass() { | ||
| 24 | WindowsServicePrincipalName spn("mlink.adlon.isode.net"); | ||
| 25 | |||
| 26 | CPPUNIT_ASSERT_EQUAL(spn.toString(), std::string("xmpp/mlink.adlon.isode.net")); | ||
| 27 | |||
| 28 | spn.setServiceClass("ldap"); | ||
| 29 | CPPUNIT_ASSERT_EQUAL(spn.toString(), std::string("ldap/mlink.adlon.isode.net")); | ||
| 30 | |||
| 31 | spn.setServiceClass("中文"); | ||
| 32 | CPPUNIT_ASSERT_EQUAL(spn.toString(), std::string("中文/mlink.adlon.isode.net")); | ||
| 33 | |||
| 34 | try { | ||
| 35 | spn.setServiceClass(""); | ||
| 36 | spn.toString(); | ||
| 37 | CPPUNIT_ASSERT(false); | ||
| 38 | } catch (std::runtime_error) { | ||
| 39 | /* expected */ | ||
| 40 | } | ||
| 41 | |||
| 42 | try { | ||
| 43 | spn.setServiceClass("xm/pp"); /* Foward slash not allowed */ | ||
| 44 | spn.toString(); | ||
| 45 | CPPUNIT_ASSERT(false); | ||
| 46 | } catch (std::runtime_error) { | ||
| 47 | /* expected */ | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | void testServiceName() { | ||
| 52 | try { | ||
| 53 | WindowsServicePrincipalName spn(""); | ||
| 54 | spn.toString(); | ||
| 55 | CPPUNIT_ASSERT(false); | ||
| 56 | } catch (std::runtime_error) { | ||
| 57 | /* expected */ | ||
| 58 | } | ||
| 59 | |||
| 60 | try { | ||
| 61 | WindowsServicePrincipalName spn2("mlink/adlon.isode.net"); /* Foward slash not allowed */ | ||
| 62 | spn2.toString(); | ||
| 63 | CPPUNIT_ASSERT(false); | ||
| 64 | } catch (std::runtime_error) { | ||
| 65 | /* expected */ | ||
| 66 | } | ||
| 67 | |||
| 68 | WindowsServicePrincipalName spn3("mlinkÄ.adlon.isode.net"); | ||
| 69 | CPPUNIT_ASSERT_EQUAL(spn3.toString(), std::string("xmpp/mlinkÄ.adlon.isode.net")); | ||
| 70 | } | ||
| 71 | |||
| 72 | void testInstanceName() { | ||
| 73 | WindowsServicePrincipalName spn("adlon.isode.net"); | ||
| 74 | |||
| 75 | spn.setInstanceName("mlink.adlon.isode.net"); | ||
| 76 | CPPUNIT_ASSERT_EQUAL(spn.toString(), std::string("xmpp/mlink.adlon.isode.net/adlon.isode.net")); | ||
| 77 | |||
| 78 | spn.setInstanceName("127.0.0.1"); | ||
| 79 | CPPUNIT_ASSERT_EQUAL(spn.toString(), std::string("xmpp/127.0.0.1/adlon.isode.net")); | ||
| 80 | |||
| 81 | spn.setInstanceName(""); | ||
| 82 | CPPUNIT_ASSERT_EQUAL(spn.toString(), std::string("xmpp/adlon.isode.net")); | ||
| 83 | |||
| 84 | spn.setInstanceName("cañón.adlon.isode.net"); | ||
| 85 | CPPUNIT_ASSERT_EQUAL(spn.toString(), std::string("xmpp/cañón.adlon.isode.net/adlon.isode.net")); | ||
| 86 | |||
| 87 | try { | ||
| 88 | spn.setInstanceName("mlink/adlon.isode.net"); /* Foward slash not allowed */ | ||
| 89 | spn.toString(); | ||
| 90 | CPPUNIT_ASSERT(false); | ||
| 91 | } catch (std::runtime_error) { | ||
| 92 | /* expected */ | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | void testInstancePort() { | ||
| 97 | WindowsServicePrincipalName spn("adlon.isode.net"); | ||
| 98 | |||
| 99 | spn.setInstanceName("mlink.adlon.isode.net"); | ||
| 100 | spn.setInstancePort(6222); | ||
| 101 | CPPUNIT_ASSERT_EQUAL(spn.toString(), std::string("xmpp/mlink.adlon.isode.net:6222/adlon.isode.net")); | ||
| 102 | |||
| 103 | spn.setInstancePort(0); | ||
| 104 | CPPUNIT_ASSERT_EQUAL(spn.toString(), std::string("xmpp/mlink.adlon.isode.net/adlon.isode.net")); | ||
| 105 | |||
| 106 | WindowsServicePrincipalName spn2("mlink.adlon.isode.net"); | ||
| 107 | |||
| 108 | spn2.setInstancePort(6222); | ||
| 109 | CPPUNIT_ASSERT_EQUAL(spn2.toString(), std::string("xmpp/mlink.adlon.isode.net:6222")); | ||
| 110 | |||
| 111 | spn2.setInstancePort(0); | ||
| 112 | CPPUNIT_ASSERT_EQUAL(spn2.toString(), std::string("xmpp/mlink.adlon.isode.net")); | ||
| 113 | } | ||
| 114 | |||
| 115 | void testReferrer() { | ||
| 116 | WindowsServicePrincipalName spn("127.0.0.1"); | ||
| 117 | |||
| 118 | spn.setReferrer("referrer.net"); | ||
| 119 | CPPUNIT_ASSERT_EQUAL(spn.toString(), std::string("xmpp/127.0.0.1/referrer.net")); | ||
| 120 | |||
| 121 | spn.setInstancePort(6222); | ||
| 122 | CPPUNIT_ASSERT_EQUAL(spn.toString(), std::string("xmpp/127.0.0.1:6222/referrer.net")); | ||
| 123 | |||
| 124 | spn.setReferrer("हिन्दी.net"); | ||
| 125 | CPPUNIT_ASSERT_EQUAL(spn.toString(), std::string("xmpp/127.0.0.1:6222/हिन्दी.net")); | ||
| 126 | |||
| 127 | try { | ||
| 128 | spn.setReferrer("referrer/net"); /* Foward slash not allowed */ | ||
| 129 | spn.toString(); | ||
| 130 | CPPUNIT_ASSERT(false); | ||
| 131 | } catch (std::runtime_error) { | ||
| 132 | /* expected */ | ||
| 133 | } | ||
| 134 | |||
| 135 | try { | ||
| 136 | spn.setReferrer(""); /* seems like you must have referrer with an IP */ | ||
| 137 | spn.toString(); | ||
| 138 | CPPUNIT_ASSERT(false); | ||
| 139 | } catch (std::runtime_error) { | ||
| 140 | /* expected */ | ||
| 141 | } | ||
| 142 | |||
| 143 | WindowsServicePrincipalName spn2("mlink.adlon.isode.net"); | ||
| 144 | |||
| 145 | spn2.setReferrer("referrer.net"); /* Referrer ignored if service name is not IP */ | ||
| 146 | CPPUNIT_ASSERT_EQUAL(spn2.toString(), std::string("xmpp/mlink.adlon.isode.net")); | ||
| 147 | |||
| 148 | spn2.setReferrer("referrer/net"); /* Referrer ignored if service name is not IP */ | ||
| 149 | CPPUNIT_ASSERT_EQUAL(spn2.toString(), std::string("xmpp/mlink.adlon.isode.net")); | ||
| 150 | |||
| 151 | WindowsServicePrincipalName spn3("adlon.isode.net"); | ||
| 152 | |||
| 153 | spn3.setInstanceName("mlink.adlon.isode.net"); | ||
| 154 | spn3.setInstancePort(6222); | ||
| 155 | spn3.setReferrer("referrer.net"); /* Referrer ignored if service name is not IP */ | ||
| 156 | CPPUNIT_ASSERT_EQUAL(spn3.toString(), std::string("xmpp/mlink.adlon.isode.net:6222/adlon.isode.net")); | ||
| 157 | |||
| 158 | } | ||
| 159 | }; | ||
| 160 | |||
| 161 | 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 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2015 Isode Limited. | ||
| 3 | * All rights reserved. | ||
| 4 | * See the COPYING file for more information. | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include <Swiften/SASL/WindowsServicePrincipalName.h> | ||
| 8 | |||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #include <Ntdsapi.h> | ||
| 12 | |||
| 13 | #include <Swiften/Base/ByteArray.h> | ||
| 14 | #include <Swiften/Base/Log.h> | ||
| 15 | #include <Swiften/Base/String.h> | ||
| 16 | |||
| 17 | namespace Swift { | ||
| 18 | |||
| 19 | WindowsServicePrincipalName::WindowsServicePrincipalName(const std::string& serviceName) : serviceClass_(L"xmpp"), instancePort_(0) { | ||
| 20 | serviceName_ = convertStringToWString(serviceName); | ||
| 21 | } | ||
| 22 | |||
| 23 | WindowsServicePrincipalName::~WindowsServicePrincipalName() { | ||
| 24 | } | ||
| 25 | |||
| 26 | void WindowsServicePrincipalName::setServiceClass(const std::string& serviceClass) { | ||
| 27 | serviceClass_ = convertStringToWString(serviceClass); | ||
| 28 | } | ||
| 29 | |||
| 30 | void WindowsServicePrincipalName::setInstanceName(const std::string& instanceName) { | ||
| 31 | instanceName_ = convertStringToWString(instanceName); | ||
| 32 | } | ||
| 33 | |||
| 34 | void WindowsServicePrincipalName::setReferrer(const std::string& referrer) { | ||
| 35 | referrer_ = convertStringToWString(referrer); | ||
| 36 | } | ||
| 37 | |||
| 38 | std::string WindowsServicePrincipalName::toString() { | ||
| 39 | DWORD length = 512; | ||
| 40 | DWORD status = ERROR_BUFFER_OVERFLOW; | ||
| 41 | bool firstCall = true; | ||
| 42 | std::string str; | ||
| 43 | |||
| 44 | while (status == ERROR_BUFFER_OVERFLOW) { | ||
| 45 | std::vector<wchar_t> value(length); | ||
| 46 | |||
| 47 | /* length after this call will contain the required length if current length is not enough - so the next call should succeed */ | ||
| 48 | status = dsMakeSpn(&length, vecptr(value)); | ||
| 49 | if (status == ERROR_SUCCESS) { | ||
| 50 | str = convertWStringToString(std::wstring(vecptr(value), length-1 /* trailing 0 character */)); | ||
| 51 | break; | ||
| 52 | } | ||
| 53 | |||
| 54 | if ((firstCall == false) || (status != ERROR_BUFFER_OVERFLOW)) { | ||
| 55 | std::stringstream errorString; | ||
| 56 | boost::system::error_code errorCode(status, boost::system::system_category()); | ||
| 57 | |||
| 58 | errorString << "Error creating Service Principal Name: status: 0x" << std::hex << status << ": " << errorCode.message(); | ||
| 59 | |||
| 60 | /* Any other error will be a programming error */ | ||
| 61 | throw std::runtime_error(errorString.str()); | ||
| 62 | } | ||
| 63 | |||
| 64 | firstCall = false; | ||
| 65 | } | ||
| 66 | |||
| 67 | SWIFT_LOG(debug) << "SPN: " << str << std::endl; | ||
| 68 | return str; | ||
| 69 | } | ||
| 70 | |||
| 71 | DWORD WindowsServicePrincipalName::dsMakeSpn(DWORD* length, wchar_t* value) { | ||
| 72 | DWORD status; | ||
| 73 | |||
| 74 | #ifdef UNICODE | ||
| 75 | SWIFT_LOG(debug) << "UNICODE is defined" << std::endl; | ||
| 76 | #else | ||
| 77 | SWIFT_LOG(debug) << "UNICODE is not defined" << std::endl; | ||
| 78 | #endif | ||
| 79 | |||
| 80 | SWIFT_LOG(debug) << "serviceClass_: " << convertWStringToString(serviceClass_.c_str()) << std::endl; | ||
| 81 | SWIFT_LOG(debug) << "serviceName_: " << convertWStringToString(serviceName_.c_str()) << std::endl; | ||
| 82 | SWIFT_LOG(debug) << "instanceName_: " << convertWStringToString(instanceName_.c_str()) << std::endl; | ||
| 83 | SWIFT_LOG(debug) << "referrer_: " << convertWStringToString(referrer_.c_str()) << std::endl; | ||
| 84 | SWIFT_LOG(debug) << "instancePort_: " << instancePort_ << std::endl; | ||
| 85 | SWIFT_LOG(debug) << "length: " << *length << std::endl; | ||
| 86 | |||
| 87 | /* Call the Unicode function because that is recommended: | ||
| 88 | https://msdn.microsoft.com/en-us/library/windows/desktop/ff381407%28v=vs.85%29.aspx */ | ||
| 89 | status = DsMakeSpnW( | ||
| 90 | serviceClass_.c_str(), | ||
| 91 | serviceName_.c_str(), | ||
| 92 | instanceName_.empty() ? NULL : instanceName_.c_str(), | ||
| 93 | instancePort_, | ||
| 94 | referrer_.empty() ? NULL : referrer_.c_str(), | ||
| 95 | length, | ||
| 96 | value); | ||
| 97 | if (status != ERROR_SUCCESS) { | ||
| 98 | boost::system::error_code errorCode(status, boost::system::system_category()); | ||
| 99 | |||
| 100 | SWIFT_LOG(debug) << std::hex << "status: 0x" << status << ": " << errorCode.message() << std::endl; | ||
| 101 | } | ||
| 102 | |||
| 103 | return status; | ||
| 104 | } | ||
| 105 | |||
| 106 | } | ||
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 @@ | |||
| 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 <string> | ||
| 10 | |||
| 11 | #include <Windows.h> | ||
| 12 | |||
| 13 | #include <Swiften/Base/API.h> | ||
| 14 | |||
| 15 | namespace Swift { | ||
| 16 | /* | ||
| 17 | * This represents the SPN used on Windows to identify a service | ||
| 18 | * instance. | ||
| 19 | */ | ||
| 20 | class SWIFTEN_API WindowsServicePrincipalName { | ||
| 21 | public: | ||
| 22 | /* | ||
| 23 | * This assigns the provided service name, | ||
| 24 | * "xmpp" service class, no instance name or referrer, | ||
| 25 | * and default port. All of these (except the service | ||
| 26 | * name) can be set to other values using the provided | ||
| 27 | * methods. | ||
| 28 | * If the constructor is called with the service name | ||
| 29 | * "hostname.example.com" and the provided methods are | ||
| 30 | * not used to change the defaults, then toString() | ||
| 31 | * will return the SPN "xmpp/hostname.example.com". | ||
| 32 | */ | ||
| 33 | WindowsServicePrincipalName(const std::string& serviceName); | ||
| 34 | |||
| 35 | ~WindowsServicePrincipalName(); | ||
| 36 | |||
| 37 | void setServiceClass(const std::string& serviceClass); | ||
| 38 | |||
| 39 | void setInstanceName(const std::string& instanceName); | ||
| 40 | |||
| 41 | void setReferrer(const std::string& referrer); | ||
| 42 | |||
| 43 | /* | ||
| 44 | * This sets a non-default port for the service. Note | ||
| 45 | * that the default value is 0 which indicates the | ||
| 46 | * default port for the service. So if the XMPP service | ||
| 47 | * is using the default port of 5222 for client | ||
| 48 | * connections, then do not set the port to 5222 but let | ||
| 49 | * it remain 0 to indicate that the default port is | ||
| 50 | * used. | ||
| 51 | */ | ||
| 52 | void setInstancePort(short int instancePort) { instancePort_ = instancePort; } | ||
| 53 | |||
| 54 | /* | ||
| 55 | * This follows the rules of SPN creation on Windows and | ||
| 56 | * returns the SPN string constructed from the set | ||
| 57 | * values. | ||
| 58 | */ | ||
| 59 | std::string toString(); | ||
| 60 | |||
| 61 | private: | ||
| 62 | DWORD dsMakeSpn(DWORD* length, wchar_t* value); | ||
| 63 | |||
| 64 | private: | ||
| 65 | std::wstring serviceClass_; | ||
| 66 | std::wstring serviceName_; | ||
| 67 | std::wstring instanceName_; | ||
| 68 | USHORT instancePort_; | ||
| 69 | std::wstring referrer_; | ||
| 70 | }; | ||
| 71 | } | ||
Swift