diff options
Diffstat (limited to 'Swiften/SASL')
28 files changed, 2082 insertions, 858 deletions
diff --git a/Swiften/SASL/ClientAuthenticator.cpp b/Swiften/SASL/ClientAuthenticator.cpp index e0900a3..9ad1d9b 100644 --- a/Swiften/SASL/ClientAuthenticator.cpp +++ b/Swiften/SASL/ClientAuthenticator.cpp @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010-2011 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2011 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <Swiften/SASL/ClientAuthenticator.h> diff --git a/Swiften/SASL/ClientAuthenticator.h b/Swiften/SASL/ClientAuthenticator.h index bc5b4f1..b22a81d 100644 --- a/Swiften/SASL/ClientAuthenticator.h +++ b/Swiften/SASL/ClientAuthenticator.h @@ -1,54 +1,55 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once -#include <boost/optional.hpp> #include <string> #include <vector> +#include <boost/optional.hpp> + #include <Swiften/Base/API.h> -#include <Swiften/Base/SafeByteArray.h> #include <Swiften/Base/ByteArray.h> +#include <Swiften/Base/SafeByteArray.h> namespace Swift { - class SWIFTEN_API ClientAuthenticator { - public: - ClientAuthenticator(const std::string& name); - virtual ~ClientAuthenticator(); - - const std::string& getName() const { - return name; - } - - void setCredentials(const std::string& authcid, const SafeByteArray& password, const std::string& authzid = std::string()) { - this->authcid = authcid; - this->password = password; - this->authzid = authzid; - } - - virtual boost::optional<SafeByteArray> getResponse() const = 0; - virtual bool setChallenge(const boost::optional<ByteArray>&) = 0; - - const std::string& getAuthenticationID() const { - return authcid; - } - - const std::string& getAuthorizationID() const { - return authzid; - } - - const SafeByteArray& getPassword() const { - return password; - } - - private: - std::string name; - std::string authcid; - SafeByteArray password; - std::string authzid; - }; + class SWIFTEN_API ClientAuthenticator { + public: + ClientAuthenticator(const std::string& name); + virtual ~ClientAuthenticator(); + + const std::string& getName() const { + return name; + } + + void setCredentials(const std::string& authcid, const SafeByteArray& password, const std::string& authzid = std::string()) { + this->authcid = authcid; + this->password = password; + this->authzid = authzid; + } + + virtual boost::optional<SafeByteArray> getResponse() const = 0; + virtual bool setChallenge(const boost::optional<ByteArray>&) = 0; + + const std::string& getAuthenticationID() const { + return authcid; + } + + const std::string& getAuthorizationID() const { + return authzid; + } + + const SafeByteArray& getPassword() const { + return password; + } + + private: + std::string name; + std::string authcid; + SafeByteArray password; + std::string authzid; + }; } diff --git a/Swiften/SASL/DIGESTMD5ClientAuthenticator.cpp b/Swiften/SASL/DIGESTMD5ClientAuthenticator.cpp index 74cdb85..a736a77 100644 --- a/Swiften/SASL/DIGESTMD5ClientAuthenticator.cpp +++ b/Swiften/SASL/DIGESTMD5ClientAuthenticator.cpp @@ -1,17 +1,17 @@ /* - * Copyright (c) 2010-2013 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <Swiften/SASL/DIGESTMD5ClientAuthenticator.h> #include <cassert> -#include <Swiften/StringCodecs/Hexify.h> -#include <Swiften/Base/Concat.h> #include <Swiften/Base/Algorithm.h> +#include <Swiften/Base/Concat.h> #include <Swiften/Crypto/CryptoProvider.h> +#include <Swiften/StringCodecs/Hexify.h> namespace Swift { @@ -19,78 +19,78 @@ DIGESTMD5ClientAuthenticator::DIGESTMD5ClientAuthenticator(const std::string& ho } boost::optional<SafeByteArray> DIGESTMD5ClientAuthenticator::getResponse() const { - if (step == Initial) { - return boost::optional<SafeByteArray>(); - } - else if (step == Response) { - std::string realm; - if (challenge.getValue("realm")) { - realm = *challenge.getValue("realm"); - } - std::string qop = "auth"; - std::string digestURI = "xmpp/" + host; - std::string nc = "00000001"; + if (step == Initial) { + return boost::optional<SafeByteArray>(); + } + else if (step == Response) { + std::string realm; + if (challenge.getValue("realm")) { + realm = *challenge.getValue("realm"); + } + std::string qop = "auth"; + std::string digestURI = "xmpp/" + host; + std::string nc = "00000001"; - // Compute the response value - ByteArray A1 = concat( - crypto->getMD5Hash( - concat(createSafeByteArray(getAuthenticationID().c_str()), createSafeByteArray(":"), createSafeByteArray(realm.c_str()), createSafeByteArray(":"), getPassword())), - createByteArray(":"), createByteArray(*challenge.getValue("nonce")), createByteArray(":"), createByteArray(cnonce)); - if (!getAuthorizationID().empty()) { - append(A1, createByteArray(":" + getAuthenticationID())); - } - ByteArray A2 = createByteArray("AUTHENTICATE:" + digestURI); + // Compute the response value + ByteArray A1 = concat( + crypto->getMD5Hash( + concat(createSafeByteArray(getAuthenticationID().c_str()), createSafeByteArray(":"), createSafeByteArray(realm.c_str()), createSafeByteArray(":"), getPassword())), + createByteArray(":"), createByteArray(*challenge.getValue("nonce")), createByteArray(":"), createByteArray(cnonce)); + if (!getAuthorizationID().empty()) { + append(A1, createByteArray(":" + getAuthenticationID())); + } + ByteArray A2 = createByteArray("AUTHENTICATE:" + digestURI); - std::string responseValue = Hexify::hexify(crypto->getMD5Hash(createByteArray( - Hexify::hexify(crypto->getMD5Hash(A1)) + ":" - + *challenge.getValue("nonce") + ":" + nc + ":" + cnonce + ":" + qop + ":" - + Hexify::hexify(crypto->getMD5Hash(A2))))); + std::string responseValue = Hexify::hexify(crypto->getMD5Hash(createByteArray( + Hexify::hexify(crypto->getMD5Hash(A1)) + ":" + + *challenge.getValue("nonce") + ":" + nc + ":" + cnonce + ":" + qop + ":" + + Hexify::hexify(crypto->getMD5Hash(A2))))); - DIGESTMD5Properties response; - response.setValue("username", getAuthenticationID()); - if (!realm.empty()) { - response.setValue("realm", realm); - } - response.setValue("nonce", *challenge.getValue("nonce")); - response.setValue("cnonce", cnonce); - response.setValue("nc", "00000001"); - response.setValue("qop", qop); - response.setValue("digest-uri", digestURI); - response.setValue("charset", "utf-8"); - response.setValue("response", responseValue); - if (!getAuthorizationID().empty()) { - response.setValue("authzid", getAuthorizationID()); - } - return createSafeByteArray(response.serialize()); - } - else { - return boost::optional<SafeByteArray>(); - } + DIGESTMD5Properties response; + response.setValue("username", getAuthenticationID()); + if (!realm.empty()) { + response.setValue("realm", realm); + } + response.setValue("nonce", *challenge.getValue("nonce")); + response.setValue("cnonce", cnonce); + response.setValue("nc", "00000001"); + response.setValue("qop", qop); + response.setValue("digest-uri", digestURI); + response.setValue("charset", "utf-8"); + response.setValue("response", responseValue); + if (!getAuthorizationID().empty()) { + response.setValue("authzid", getAuthorizationID()); + } + return createSafeByteArray(response.serialize()); + } + else { + return boost::optional<SafeByteArray>(); + } } bool DIGESTMD5ClientAuthenticator::setChallenge(const boost::optional<ByteArray>& challengeData) { - if (step == Initial) { - if (!challengeData) { - return false; - } - challenge = DIGESTMD5Properties::parse(*challengeData); + if (step == Initial) { + if (!challengeData) { + return false; + } + challenge = DIGESTMD5Properties::parse(*challengeData); - // Sanity checks - if (!challenge.getValue("nonce")) { - return false; - } - if (!challenge.getValue("charset") || *challenge.getValue("charset") != "utf-8") { - return false; - } - step = Response; - return true; - } - else { - step = Final; - // TODO: Check RSPAuth - return true; - } + // Sanity checks + if (!challenge.getValue("nonce")) { + return false; + } + if (!challenge.getValue("charset") || *challenge.getValue("charset") != "utf-8") { + return false; + } + step = Response; + return true; + } + else { + step = Final; + // TODO: Check RSPAuth + return true; + } } } diff --git a/Swiften/SASL/DIGESTMD5ClientAuthenticator.h b/Swiften/SASL/DIGESTMD5ClientAuthenticator.h index d141401..4a857d3 100644 --- a/Swiften/SASL/DIGESTMD5ClientAuthenticator.h +++ b/Swiften/SASL/DIGESTMD5ClientAuthenticator.h @@ -1,39 +1,39 @@ /* - * Copyright (c) 2010-2013 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once #include <map> - #include <string> #include <vector> + #include <Swiften/Base/API.h> +#include <Swiften/Base/SafeByteArray.h> #include <Swiften/SASL/ClientAuthenticator.h> #include <Swiften/SASL/DIGESTMD5Properties.h> -#include <Swiften/Base/SafeByteArray.h> namespace Swift { - class CryptoProvider; + class CryptoProvider; + + class SWIFTEN_API DIGESTMD5ClientAuthenticator : public ClientAuthenticator { + public: + DIGESTMD5ClientAuthenticator(const std::string& host, const std::string& nonce, CryptoProvider*); - class SWIFTEN_API DIGESTMD5ClientAuthenticator : public ClientAuthenticator { - public: - DIGESTMD5ClientAuthenticator(const std::string& host, const std::string& nonce, CryptoProvider*); - - virtual boost::optional<SafeByteArray> getResponse() const; - virtual bool setChallenge(const boost::optional<std::vector<unsigned char> >&); + virtual boost::optional<SafeByteArray> getResponse() const; + virtual bool setChallenge(const boost::optional<std::vector<unsigned char> >&); - private: - enum Step { - Initial, - Response, - Final - } step; - std::string host; - std::string cnonce; - CryptoProvider* crypto; - DIGESTMD5Properties challenge; - }; + private: + enum Step { + Initial, + Response, + Final + } step; + std::string host; + std::string cnonce; + CryptoProvider* crypto; + DIGESTMD5Properties challenge; + }; } diff --git a/Swiften/SASL/DIGESTMD5Properties.cpp b/Swiften/SASL/DIGESTMD5Properties.cpp index 23a3476..1f33c8f 100644 --- a/Swiften/SASL/DIGESTMD5Properties.cpp +++ b/Swiften/SASL/DIGESTMD5Properties.cpp @@ -1,130 +1,131 @@ /* - * Copyright (c) 2010-2013 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <Swiften/SASL/DIGESTMD5Properties.h> + #include <Swiften/Base/Algorithm.h> namespace Swift { namespace { - bool insideQuotes(const ByteArray& v) { - if (v.empty()) { - return false; - } - else if (v.size() == 1) { - return v[0] == '"'; - } - else if (v[0] == '"') { - return v[v.size() - 1] != '"'; - } - else { - return false; - } - } + bool insideQuotes(const ByteArray& v) { + if (v.empty()) { + return false; + } + else if (v.size() == 1) { + return v[0] == '"'; + } + else if (v[0] == '"') { + return v[v.size() - 1] != '"'; + } + else { + return false; + } + } - ByteArray stripQuotes(const ByteArray& v) { - const char* data = reinterpret_cast<const char*>(vecptr(v)); - size_t size = v.size(); - if (v[0] == '"') { - data++; - size--; - } - if (v[v.size() - 1] == '"') { - size--; - } - return createByteArray(data, size); - } + ByteArray stripQuotes(const ByteArray& v) { + const char* data = reinterpret_cast<const char*>(vecptr(v)); + size_t size = v.size(); + if (v[0] == '"') { + data++; + size--; + } + if (v[v.size() - 1] == '"') { + size--; + } + return createByteArray(data, size); + } } DIGESTMD5Properties::DIGESTMD5Properties() { } DIGESTMD5Properties DIGESTMD5Properties::parse(const ByteArray& data) { - DIGESTMD5Properties result; - bool inKey = true; - ByteArray currentKey; - ByteArray currentValue; - for (size_t i = 0; i < data.size(); ++i) { - char c = static_cast<char>(data[i]); - if (inKey) { - if (c == '=') { - inKey = false; - } - else { - currentKey.push_back(static_cast<unsigned char>(c)); - } - } - else { - if (c == ',' && !insideQuotes(currentValue)) { - std::string key = byteArrayToString(currentKey); - if (isQuoted(key)) { - result.setValue(key, byteArrayToString(stripQuotes(currentValue))); - } - else { - result.setValue(key, byteArrayToString(currentValue)); - } - inKey = true; - currentKey = ByteArray(); - currentValue = ByteArray(); - } - else { - currentValue.push_back(static_cast<unsigned char>(c)); - } - } - } + DIGESTMD5Properties result; + bool inKey = true; + ByteArray currentKey; + ByteArray currentValue; + for (unsigned char i : data) { + char c = static_cast<char>(i); + if (inKey) { + if (c == '=') { + inKey = false; + } + else { + currentKey.push_back(static_cast<unsigned char>(c)); + } + } + else { + if (c == ',' && !insideQuotes(currentValue)) { + std::string key = byteArrayToString(currentKey); + if (isQuoted(key)) { + result.setValue(key, byteArrayToString(stripQuotes(currentValue))); + } + else { + result.setValue(key, byteArrayToString(currentValue)); + } + inKey = true; + currentKey = ByteArray(); + currentValue = ByteArray(); + } + else { + currentValue.push_back(static_cast<unsigned char>(c)); + } + } + } - if (!currentKey.empty()) { - std::string key = byteArrayToString(currentKey); - if (isQuoted(key)) { - result.setValue(key, byteArrayToString(stripQuotes(currentValue))); - } - else { - result.setValue(key, byteArrayToString(currentValue)); - } - } + if (!currentKey.empty()) { + std::string key = byteArrayToString(currentKey); + if (isQuoted(key)) { + result.setValue(key, byteArrayToString(stripQuotes(currentValue))); + } + else { + result.setValue(key, byteArrayToString(currentValue)); + } + } - return result; + return result; } ByteArray DIGESTMD5Properties::serialize() const { - ByteArray result; - for(DIGESTMD5PropertiesMap::const_iterator i = properties.begin(); i != properties.end(); ++i) { - if (i != properties.begin()) { - result.push_back(','); - } - append(result, createByteArray(i->first)); - result.push_back('='); - if (isQuoted(i->first)) { - append(result, createByteArray("\"")); - append(result, i->second); - append(result, createByteArray("\"")); - } - else { - append(result, i->second); - } - } - return result; + ByteArray result; + for(DIGESTMD5PropertiesMap::const_iterator i = properties.begin(); i != properties.end(); ++i) { + if (i != properties.begin()) { + result.push_back(','); + } + append(result, createByteArray(i->first)); + result.push_back('='); + if (isQuoted(i->first)) { + append(result, createByteArray("\"")); + append(result, i->second); + append(result, createByteArray("\"")); + } + else { + append(result, i->second); + } + } + return result; } boost::optional<std::string> DIGESTMD5Properties::getValue(const std::string& key) const { - DIGESTMD5PropertiesMap::const_iterator i = properties.find(key); - if (i != properties.end()) { - return byteArrayToString(i->second); - } - else { - return boost::optional<std::string>(); - } + DIGESTMD5PropertiesMap::const_iterator i = properties.find(key); + if (i != properties.end()) { + return byteArrayToString(i->second); + } + else { + return boost::optional<std::string>(); + } } void DIGESTMD5Properties::setValue(const std::string& key, const std::string& value) { - properties.insert(DIGESTMD5PropertiesMap::value_type(key, createByteArray(value))); + properties.insert(DIGESTMD5PropertiesMap::value_type(key, createByteArray(value))); } bool DIGESTMD5Properties::isQuoted(const std::string& p) { - return p == "authzid" || p == "cnonce" || p == "digest-uri" || p == "nonce" || p == "realm" || p == "username"; + return p == "authzid" || p == "cnonce" || p == "digest-uri" || p == "nonce" || p == "realm" || p == "username"; } } diff --git a/Swiften/SASL/DIGESTMD5Properties.h b/Swiften/SASL/DIGESTMD5Properties.h index 654208d..a4d2d65 100644 --- a/Swiften/SASL/DIGESTMD5Properties.h +++ b/Swiften/SASL/DIGESTMD5Properties.h @@ -1,36 +1,37 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once #include <map> +#include <string> + #include <boost/optional.hpp> -#include <string> #include <Swiften/Base/API.h> #include <Swiften/Base/ByteArray.h> namespace Swift { - class SWIFTEN_API DIGESTMD5Properties { - public: - DIGESTMD5Properties(); - - boost::optional<std::string> getValue(const std::string& key) const; + class SWIFTEN_API DIGESTMD5Properties { + public: + DIGESTMD5Properties(); + + boost::optional<std::string> getValue(const std::string& key) const; - void setValue(const std::string& key, const std::string& value); + void setValue(const std::string& key, const std::string& value); - ByteArray serialize() const; + ByteArray serialize() const; - static DIGESTMD5Properties parse(const ByteArray&); + static DIGESTMD5Properties parse(const ByteArray&); - private: - static bool isQuoted(const std::string& property); + private: + static bool isQuoted(const std::string& property); - private: - typedef std::multimap<std::string, ByteArray> DIGESTMD5PropertiesMap; - DIGESTMD5PropertiesMap properties; - }; + private: + typedef std::multimap<std::string, ByteArray> DIGESTMD5PropertiesMap; + DIGESTMD5PropertiesMap properties; + }; } diff --git a/Swiften/SASL/EXTERNALClientAuthenticator.cpp b/Swiften/SASL/EXTERNALClientAuthenticator.cpp index a3016d1..027bc89 100644 --- a/Swiften/SASL/EXTERNALClientAuthenticator.cpp +++ b/Swiften/SASL/EXTERNALClientAuthenticator.cpp @@ -1,7 +1,7 @@ /* - * Copyright (c) 2012 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2012 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <Swiften/SASL/EXTERNALClientAuthenticator.h> @@ -12,15 +12,21 @@ EXTERNALClientAuthenticator::EXTERNALClientAuthenticator() : ClientAuthenticator } boost::optional<SafeByteArray> EXTERNALClientAuthenticator::getResponse() const { - return boost::optional<SafeByteArray>(); + const std::string& authorizationID = getAuthorizationID(); + + if (authorizationID.empty()) { + return boost::optional<SafeByteArray>(); + } else { + return createSafeByteArray(authorizationID); + } } bool EXTERNALClientAuthenticator::setChallenge(const boost::optional<ByteArray>&) { - if (finished) { - return false; - } - finished = true; - return true; + if (finished) { + return false; + } + finished = true; + return true; } } diff --git a/Swiften/SASL/EXTERNALClientAuthenticator.h b/Swiften/SASL/EXTERNALClientAuthenticator.h index b986295..e42e3fd 100644 --- a/Swiften/SASL/EXTERNALClientAuthenticator.h +++ b/Swiften/SASL/EXTERNALClientAuthenticator.h @@ -1,23 +1,24 @@ /* - * Copyright (c) 2012 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2012-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once -#include <Swiften/SASL/ClientAuthenticator.h> +#include <Swiften/Base/API.h> #include <Swiften/Base/ByteArray.h> +#include <Swiften/SASL/ClientAuthenticator.h> namespace Swift { - class EXTERNALClientAuthenticator : public ClientAuthenticator { - public: - EXTERNALClientAuthenticator(); + class SWIFTEN_API EXTERNALClientAuthenticator : public ClientAuthenticator { + public: + EXTERNALClientAuthenticator(); - virtual boost::optional<SafeByteArray> getResponse() const; - virtual bool setChallenge(const boost::optional<ByteArray>&); + virtual boost::optional<SafeByteArray> getResponse() const; + virtual bool setChallenge(const boost::optional<ByteArray>&); - private: - bool finished; - }; + private: + bool finished; + }; } diff --git a/Swiften/SASL/PLAINClientAuthenticator.cpp b/Swiften/SASL/PLAINClientAuthenticator.cpp index 7872174..11bd14c 100644 --- a/Swiften/SASL/PLAINClientAuthenticator.cpp +++ b/Swiften/SASL/PLAINClientAuthenticator.cpp @@ -1,10 +1,11 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <Swiften/SASL/PLAINClientAuthenticator.h> + #include <Swiften/Base/Concat.h> namespace Swift { @@ -13,11 +14,11 @@ PLAINClientAuthenticator::PLAINClientAuthenticator() : ClientAuthenticator("PLAI } boost::optional<SafeByteArray> PLAINClientAuthenticator::getResponse() const { - return concat(createSafeByteArray(getAuthorizationID()), createSafeByteArray('\0'), createSafeByteArray(getAuthenticationID()), createSafeByteArray('\0'), getPassword()); + return concat(createSafeByteArray(getAuthorizationID()), createSafeByteArray('\0'), createSafeByteArray(getAuthenticationID()), createSafeByteArray('\0'), getPassword()); } bool PLAINClientAuthenticator::setChallenge(const boost::optional<ByteArray>&) { - return true; + return true; } } diff --git a/Swiften/SASL/PLAINClientAuthenticator.h b/Swiften/SASL/PLAINClientAuthenticator.h index ad3a695..64ef741 100644 --- a/Swiften/SASL/PLAINClientAuthenticator.h +++ b/Swiften/SASL/PLAINClientAuthenticator.h @@ -1,21 +1,21 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once #include <Swiften/Base/API.h> -#include <Swiften/SASL/ClientAuthenticator.h> #include <Swiften/Base/ByteArray.h> +#include <Swiften/SASL/ClientAuthenticator.h> namespace Swift { - class SWIFTEN_API PLAINClientAuthenticator : public ClientAuthenticator { - public: - PLAINClientAuthenticator(); + class SWIFTEN_API PLAINClientAuthenticator : public ClientAuthenticator { + public: + PLAINClientAuthenticator(); - virtual boost::optional<SafeByteArray> getResponse() const; - virtual bool setChallenge(const boost::optional<ByteArray>&); - }; + virtual boost::optional<SafeByteArray> getResponse() const; + virtual bool setChallenge(const boost::optional<ByteArray>&); + }; } diff --git a/Swiften/SASL/PLAINMessage.cpp b/Swiften/SASL/PLAINMessage.cpp index c43b446..a55f604 100644 --- a/Swiften/SASL/PLAINMessage.cpp +++ b/Swiften/SASL/PLAINMessage.cpp @@ -1,10 +1,11 @@ /* - * Copyright (c) 2010-2013 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <Swiften/SASL/PLAINMessage.h> + #include <Swiften/Base/Concat.h> namespace Swift { @@ -13,32 +14,32 @@ PLAINMessage::PLAINMessage(const std::string& authcid, const SafeByteArray& pass } PLAINMessage::PLAINMessage(const SafeByteArray& value) { - size_t i = 0; - while (i < value.size() && value[i] != '\0') { - authzid += static_cast<char>(value[i]); - ++i; - } - if (i == value.size()) { - return; - } - ++i; - while (i < value.size() && value[i] != '\0') { - authcid += static_cast<char>(value[i]); - ++i; - } - if (i == value.size()) { - authcid = ""; - return; - } - ++i; - while (i < value.size()) { - password.push_back(value[i]); - ++i; - } + size_t i = 0; + while (i < value.size() && value[i] != '\0') { + authzid += static_cast<char>(value[i]); + ++i; + } + if (i == value.size()) { + return; + } + ++i; + while (i < value.size() && value[i] != '\0') { + authcid += static_cast<char>(value[i]); + ++i; + } + if (i == value.size()) { + authcid = ""; + return; + } + ++i; + while (i < value.size()) { + password.push_back(value[i]); + ++i; + } } SafeByteArray PLAINMessage::getValue() const { - return concat(createSafeByteArray(authzid), createSafeByteArray('\0'), createSafeByteArray(authcid), createSafeByteArray('\0'), password); + return concat(createSafeByteArray(authzid), createSafeByteArray('\0'), createSafeByteArray(authcid), createSafeByteArray('\0'), password); } } diff --git a/Swiften/SASL/PLAINMessage.h b/Swiften/SASL/PLAINMessage.h index 3811b31..80479f9 100644 --- a/Swiften/SASL/PLAINMessage.h +++ b/Swiften/SASL/PLAINMessage.h @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ // TODO: Get rid of this @@ -14,28 +14,28 @@ #include <Swiften/Base/SafeByteArray.h> namespace Swift { - class SWIFTEN_API PLAINMessage { - public: - PLAINMessage(const std::string& authcid, const SafeByteArray& password, const std::string& authzid = ""); - PLAINMessage(const SafeByteArray& value); - - SafeByteArray getValue() const; - - const std::string& getAuthenticationID() const { - return authcid; - } - - const SafeByteArray& getPassword() const { - return password; - } - - const std::string& getAuthorizationID() const { - return authzid; - } - - private: - std::string authcid; - std::string authzid; - SafeByteArray password; - }; + class SWIFTEN_API PLAINMessage { + public: + PLAINMessage(const std::string& authcid, const SafeByteArray& password, const std::string& authzid = ""); + PLAINMessage(const SafeByteArray& value); + + SafeByteArray getValue() const; + + const std::string& getAuthenticationID() const { + return authcid; + } + + const SafeByteArray& getPassword() const { + return password; + } + + const std::string& getAuthorizationID() const { + return authzid; + } + + private: + std::string authcid; + std::string authzid; + SafeByteArray password; + }; } diff --git a/Swiften/SASL/SCRAMSHA1ClientAuthenticator.cpp b/Swiften/SASL/SCRAMSHA1ClientAuthenticator.cpp index 44fef76..3c00402 100644 --- a/Swiften/SASL/SCRAMSHA1ClientAuthenticator.cpp +++ b/Swiften/SASL/SCRAMSHA1ClientAuthenticator.cpp @@ -1,37 +1,38 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <Swiften/SASL/SCRAMSHA1ClientAuthenticator.h> #include <cassert> #include <map> + #include <boost/lexical_cast.hpp> +#include <Swiften/Base/Concat.h> #include <Swiften/Crypto/CryptoProvider.h> +#include <Swiften/IDN/IDNConverter.h> #include <Swiften/StringCodecs/Base64.h> #include <Swiften/StringCodecs/PBKDF2.h> -#include <Swiften/IDN/IDNConverter.h> -#include <Swiften/Base/Concat.h> namespace Swift { static std::string escape(const std::string& s) { - std::string result; - for (size_t i = 0; i < s.size(); ++i) { - if (s[i] == ',') { - result += "=2C"; - } - else if (s[i] == '=') { - result += "=3D"; - } - else { - result += s[i]; - } - } - return result; + std::string result; + for (char i : s) { + if (i == ',') { + result += "=2C"; + } + else if (i == '=') { + result += "=3D"; + } + else { + result += i; + } + } + return result; } @@ -39,144 +40,144 @@ SCRAMSHA1ClientAuthenticator::SCRAMSHA1ClientAuthenticator(const std::string& no } boost::optional<SafeByteArray> SCRAMSHA1ClientAuthenticator::getResponse() const { - if (step == Initial) { - return createSafeByteArray(concat(getGS2Header(), getInitialBareClientMessage())); - } - else if (step == Proof) { - ByteArray clientKey = crypto->getHMACSHA1(saltedPassword, createByteArray("Client Key")); - ByteArray storedKey = crypto->getSHA1Hash(clientKey); - ByteArray clientSignature = crypto->getHMACSHA1(createSafeByteArray(storedKey), authMessage); - ByteArray clientProof = clientKey; - for (unsigned int i = 0; i < clientProof.size(); ++i) { - clientProof[i] ^= clientSignature[i]; - } - ByteArray result = concat(getFinalMessageWithoutProof(), createByteArray(",p="), createByteArray(Base64::encode(clientProof))); - return createSafeByteArray(result); - } - else { - return boost::optional<SafeByteArray>(); - } + if (step == Initial) { + return createSafeByteArray(concat(getGS2Header(), getInitialBareClientMessage())); + } + else if (step == Proof) { + ByteArray clientKey = crypto->getHMACSHA1(saltedPassword, createByteArray("Client Key")); + ByteArray storedKey = crypto->getSHA1Hash(clientKey); + ByteArray clientSignature = crypto->getHMACSHA1(createSafeByteArray(storedKey), authMessage); + ByteArray clientProof = clientKey; + for (unsigned int i = 0; i < clientProof.size(); ++i) { + clientProof[i] ^= clientSignature[i]; + } + ByteArray result = concat(getFinalMessageWithoutProof(), createByteArray(",p="), createByteArray(Base64::encode(clientProof))); + return createSafeByteArray(result); + } + else { + return boost::optional<SafeByteArray>(); + } } bool SCRAMSHA1ClientAuthenticator::setChallenge(const boost::optional<ByteArray>& challenge) { - if (step == Initial) { - if (!challenge) { - return false; - } - initialServerMessage = *challenge; - - std::map<char, std::string> keys = parseMap(byteArrayToString(initialServerMessage)); - - // Extract the salt - ByteArray salt = Base64::decode(keys['s']); - - // Extract the server nonce - std::string clientServerNonce = keys['r']; - if (clientServerNonce.size() <= clientnonce.size()) { - return false; - } - std::string receivedClientNonce = clientServerNonce.substr(0, clientnonce.size()); - if (receivedClientNonce != clientnonce) { - return false; - } - serverNonce = createByteArray(clientServerNonce.substr(clientnonce.size(), clientServerNonce.npos)); - - // Extract the number of iterations - int iterations = 0; - try { - iterations = boost::lexical_cast<int>(keys['i']); - } - catch (const boost::bad_lexical_cast&) { - return false; - } - if (iterations <= 0) { - return false; - } - - // Compute all the values needed for the server signature - try { - saltedPassword = PBKDF2::encode(idnConverter->getStringPrepared(getPassword(), IDNConverter::SASLPrep), salt, iterations, crypto); - } - catch (const std::exception&) { - } - authMessage = concat(getInitialBareClientMessage(), createByteArray(","), initialServerMessage, createByteArray(","), getFinalMessageWithoutProof()); - ByteArray serverKey = crypto->getHMACSHA1(saltedPassword, createByteArray("Server Key")); - serverSignature = crypto->getHMACSHA1(serverKey, authMessage); - - step = Proof; - return true; - } - else if (step == Proof) { - ByteArray result = concat(createByteArray("v="), createByteArray(Base64::encode(serverSignature))); - step = Final; - return challenge && challenge == result; - } - else { - return true; - } + if (step == Initial) { + if (!challenge) { + return false; + } + initialServerMessage = *challenge; + + std::map<char, std::string> keys = parseMap(byteArrayToString(initialServerMessage)); + + // Extract the salt + ByteArray salt = Base64::decode(keys['s']); + + // Extract the server nonce + std::string clientServerNonce = keys['r']; + if (clientServerNonce.size() <= clientnonce.size()) { + return false; + } + std::string receivedClientNonce = clientServerNonce.substr(0, clientnonce.size()); + if (receivedClientNonce != clientnonce) { + return false; + } + serverNonce = createByteArray(clientServerNonce.substr(clientnonce.size(), clientServerNonce.npos)); + + // Extract the number of iterations + int iterations = 0; + try { + iterations = boost::lexical_cast<int>(keys['i']); + } + catch (const boost::bad_lexical_cast&) { + return false; + } + if (iterations <= 0) { + return false; + } + + // Compute all the values needed for the server signature + try { + saltedPassword = PBKDF2::encode(idnConverter->getStringPrepared(getPassword(), IDNConverter::SASLPrep), salt, iterations, crypto); + } + catch (const std::exception&) { + } + authMessage = concat(getInitialBareClientMessage(), createByteArray(","), initialServerMessage, createByteArray(","), getFinalMessageWithoutProof()); + ByteArray serverKey = crypto->getHMACSHA1(saltedPassword, createByteArray("Server Key")); + serverSignature = crypto->getHMACSHA1(serverKey, authMessage); + + step = Proof; + return true; + } + else if (step == Proof) { + ByteArray result = concat(createByteArray("v="), createByteArray(Base64::encode(serverSignature))); + step = Final; + return challenge && challenge == result; + } + else { + return true; + } } std::map<char, std::string> SCRAMSHA1ClientAuthenticator::parseMap(const std::string& s) { - std::map<char, std::string> result; - if (s.size() > 0) { - char key = 0; - std::string value; - size_t i = 0; - bool expectKey = true; - while (i < s.size()) { - if (expectKey) { - key = s[i]; - expectKey = false; - i++; - } - else if (s[i] == ',') { - result[key] = value; - value = ""; - expectKey = true; - } - else { - value += s[i]; - } - i++; - } - result[key] = value; - } - return result; + std::map<char, std::string> result; + if (s.size() > 0) { + char key = 0; + std::string value; + size_t i = 0; + bool expectKey = true; + while (i < s.size()) { + if (expectKey) { + key = s[i]; + expectKey = false; + i++; + } + else if (s[i] == ',') { + result[key] = value; + value = ""; + expectKey = true; + } + else { + value += s[i]; + } + i++; + } + result[key] = value; + } + return result; } ByteArray SCRAMSHA1ClientAuthenticator::getInitialBareClientMessage() const { - std::string authenticationID; - try { - authenticationID = idnConverter->getStringPrepared(getAuthenticationID(), IDNConverter::SASLPrep); - } - catch (const std::exception&) { - } - return createByteArray(std::string("n=" + escape(authenticationID) + ",r=" + clientnonce)); + std::string authenticationID; + try { + authenticationID = idnConverter->getStringPrepared(getAuthenticationID(), IDNConverter::SASLPrep); + } + catch (const std::exception&) { + } + return createByteArray(std::string("n=" + escape(authenticationID) + ",r=" + clientnonce)); } ByteArray SCRAMSHA1ClientAuthenticator::getGS2Header() const { - ByteArray channelBindingHeader(createByteArray("n")); - if (tlsChannelBindingData) { - if (useChannelBinding) { - channelBindingHeader = createByteArray("p=tls-unique"); - } - else { - channelBindingHeader = createByteArray("y"); - } - } - return concat(channelBindingHeader, createByteArray(","), (getAuthorizationID().empty() ? ByteArray() : createByteArray("a=" + escape(getAuthorizationID()))), createByteArray(",")); + ByteArray channelBindingHeader(createByteArray("n")); + if (tlsChannelBindingData) { + if (useChannelBinding) { + channelBindingHeader = createByteArray("p=tls-unique"); + } + else { + channelBindingHeader = createByteArray("y"); + } + } + return concat(channelBindingHeader, createByteArray(","), (getAuthorizationID().empty() ? ByteArray() : createByteArray("a=" + escape(getAuthorizationID()))), createByteArray(",")); } void SCRAMSHA1ClientAuthenticator::setTLSChannelBindingData(const ByteArray& channelBindingData) { - this->tlsChannelBindingData = channelBindingData; + this->tlsChannelBindingData = channelBindingData; } ByteArray SCRAMSHA1ClientAuthenticator::getFinalMessageWithoutProof() const { - ByteArray channelBindData; - if (useChannelBinding && tlsChannelBindingData) { - channelBindData = *tlsChannelBindingData; - } - return concat(createByteArray("c=" + Base64::encode(concat(getGS2Header(), channelBindData)) + ",r=" + clientnonce), serverNonce); + ByteArray channelBindData; + if (useChannelBinding && tlsChannelBindingData) { + channelBindData = *tlsChannelBindingData; + } + return concat(createByteArray("c=" + Base64::encode(concat(getGS2Header(), channelBindData)) + ",r=" + clientnonce), serverNonce); } diff --git a/Swiften/SASL/SCRAMSHA1ClientAuthenticator.h b/Swiften/SASL/SCRAMSHA1ClientAuthenticator.h index b713f9f..5dc169a 100644 --- a/Swiften/SASL/SCRAMSHA1ClientAuthenticator.h +++ b/Swiften/SASL/SCRAMSHA1ClientAuthenticator.h @@ -1,54 +1,55 @@ /* - * Copyright (c) 2010-2013 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once #include <map> +#include <string> + #include <boost/optional.hpp> -#include <string> +#include <Swiften/Base/API.h> #include <Swiften/Base/ByteArray.h> #include <Swiften/SASL/ClientAuthenticator.h> -#include <Swiften/Base/API.h> namespace Swift { - class IDNConverter; - class CryptoProvider; - - class SWIFTEN_API SCRAMSHA1ClientAuthenticator : public ClientAuthenticator { - public: - SCRAMSHA1ClientAuthenticator(const std::string& nonce, bool useChannelBinding, IDNConverter*, CryptoProvider*); - - void setTLSChannelBindingData(const ByteArray& channelBindingData); - - virtual boost::optional<SafeByteArray> getResponse() const; - virtual bool setChallenge(const boost::optional<ByteArray>&); - - private: - ByteArray getInitialBareClientMessage() const; - ByteArray getGS2Header() const; - ByteArray getFinalMessageWithoutProof() const; - - static std::map<char, std::string> parseMap(const std::string&); - - private: - enum Step { - Initial, - Proof, - Final - } step; - std::string clientnonce; - ByteArray initialServerMessage; - ByteArray serverNonce; - ByteArray authMessage; - ByteArray saltedPassword; - ByteArray serverSignature; - bool useChannelBinding; - IDNConverter* idnConverter; - CryptoProvider* crypto; - boost::optional<ByteArray> tlsChannelBindingData; - }; + class IDNConverter; + class CryptoProvider; + + class SWIFTEN_API SCRAMSHA1ClientAuthenticator : public ClientAuthenticator { + public: + SCRAMSHA1ClientAuthenticator(const std::string& nonce, bool useChannelBinding, IDNConverter*, CryptoProvider*); + + void setTLSChannelBindingData(const ByteArray& channelBindingData); + + virtual boost::optional<SafeByteArray> getResponse() const; + virtual bool setChallenge(const boost::optional<ByteArray>&); + + private: + ByteArray getInitialBareClientMessage() const; + ByteArray getGS2Header() const; + ByteArray getFinalMessageWithoutProof() const; + + static std::map<char, std::string> parseMap(const std::string&); + + private: + enum Step { + Initial, + Proof, + Final + } step; + std::string clientnonce; + ByteArray initialServerMessage; + ByteArray serverNonce; + ByteArray authMessage; + ByteArray saltedPassword; + ByteArray serverSignature; + bool useChannelBinding; + IDNConverter* idnConverter; + CryptoProvider* crypto; + boost::optional<ByteArray> tlsChannelBindingData; + }; } diff --git a/Swiften/SASL/SConscript b/Swiften/SASL/SConscript index 6509547..8a248cc 100644 --- a/Swiften/SASL/SConscript +++ b/Swiften/SASL/SConscript @@ -3,20 +3,32 @@ Import("swiften_env", "env") myenv = swiften_env.Clone() objects = myenv.SwiftenObject([ - "ClientAuthenticator.cpp", - "EXTERNALClientAuthenticator.cpp", - "PLAINClientAuthenticator.cpp", - "PLAINMessage.cpp", - "SCRAMSHA1ClientAuthenticator.cpp", - "DIGESTMD5Properties.cpp", - "DIGESTMD5ClientAuthenticator.cpp", - ]) + "ClientAuthenticator.cpp", + "EXTERNALClientAuthenticator.cpp", + "PLAINClientAuthenticator.cpp", + "PLAINMessage.cpp", + "SCRAMSHA1ClientAuthenticator.cpp", + "DIGESTMD5Properties.cpp", + "DIGESTMD5ClientAuthenticator.cpp", + ]) +if myenv["PLATFORM"] == "win32" : + objects += myenv.SwiftenObject([ + "WindowsServicePrincipalName.cpp", + "WindowsAuthentication.cpp", + "WindowsGSSAPIClientAuthenticator.cpp" + ]) + swiften_env.Append(SWIFTEN_OBJECTS = [objects]) env.Append(UNITTEST_SOURCES = [ - File("UnitTest/PLAINMessageTest.cpp"), - File("UnitTest/PLAINClientAuthenticatorTest.cpp"), - File("UnitTest/SCRAMSHA1ClientAuthenticatorTest.cpp"), - File("UnitTest/DIGESTMD5PropertiesTest.cpp"), - File("UnitTest/DIGESTMD5ClientAuthenticatorTest.cpp"), - ]) + File("UnitTest/PLAINMessageTest.cpp"), + File("UnitTest/PLAINClientAuthenticatorTest.cpp"), + File("UnitTest/EXTERNALClientAuthenticatorTest.cpp"), + File("UnitTest/SCRAMSHA1ClientAuthenticatorTest.cpp"), + 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/DIGESTMD5ClientAuthenticatorTest.cpp b/Swiften/SASL/UnitTest/DIGESTMD5ClientAuthenticatorTest.cpp index 94bcd0a..d29af59 100644 --- a/Swiften/SASL/UnitTest/DIGESTMD5ClientAuthenticatorTest.cpp +++ b/Swiften/SASL/UnitTest/DIGESTMD5ClientAuthenticatorTest.cpp @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010-2013 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <QA/Checker/IO.h> @@ -9,63 +9,62 @@ #include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/TestFactoryRegistry.h> -#include <Swiften/SASL/DIGESTMD5ClientAuthenticator.h> #include <Swiften/Base/ByteArray.h> - #include <Swiften/Crypto/CryptoProvider.h> #include <Swiften/Crypto/PlatformCryptoProvider.h> +#include <Swiften/SASL/DIGESTMD5ClientAuthenticator.h> using namespace Swift; class DIGESTMD5ClientAuthenticatorTest : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(DIGESTMD5ClientAuthenticatorTest); - CPPUNIT_TEST(testGetInitialResponse); - CPPUNIT_TEST(testGetResponse); - CPPUNIT_TEST(testGetResponse_WithAuthorizationID); - //CPPUNIT_TEST(testSetChallenge); - CPPUNIT_TEST_SUITE_END(); - - public: - void setUp() { - crypto = boost::shared_ptr<CryptoProvider>(PlatformCryptoProvider::create()); - } - - void testGetInitialResponse() { - DIGESTMD5ClientAuthenticator testling("xmpp.example.com", "abcdefgh", crypto.get()); - - CPPUNIT_ASSERT(!testling.getResponse()); - } - - void testGetResponse() { - DIGESTMD5ClientAuthenticator testling("xmpp.example.com", "abcdefgh", crypto.get()); - - testling.setCredentials("user", createSafeByteArray("pass"), ""); - testling.setChallenge(createByteArray( - "realm=\"example.com\"," - "nonce=\"O6skKPuaCZEny3hteI19qXMBXSadoWs840MchORo\"," - "qop=auth,charset=utf-8,algorithm=md5-sess")); - - SafeByteArray response = *testling.getResponse(); - - CPPUNIT_ASSERT_EQUAL(createSafeByteArray("charset=utf-8,cnonce=\"abcdefgh\",digest-uri=\"xmpp/xmpp.example.com\",nc=00000001,nonce=\"O6skKPuaCZEny3hteI19qXMBXSadoWs840MchORo\",qop=auth,realm=\"example.com\",response=088891c800ecff1b842159ad6459104a,username=\"user\""), response); - } - - void testGetResponse_WithAuthorizationID() { - DIGESTMD5ClientAuthenticator testling("xmpp.example.com", "abcdefgh", crypto.get()); - - testling.setCredentials("user", createSafeByteArray("pass"), "myauthzid"); - testling.setChallenge(createByteArray( - "realm=\"example.com\"," - "nonce=\"O6skKPuaCZEny3hteI19qXMBXSadoWs840MchORo\"," - "qop=auth,charset=utf-8,algorithm=md5-sess")); - - SafeByteArray response = *testling.getResponse(); - - CPPUNIT_ASSERT_EQUAL(createSafeByteArray("authzid=\"myauthzid\",charset=utf-8,cnonce=\"abcdefgh\",digest-uri=\"xmpp/xmpp.example.com\",nc=00000001,nonce=\"O6skKPuaCZEny3hteI19qXMBXSadoWs840MchORo\",qop=auth,realm=\"example.com\",response=4293834432b6e7889a2dee7e8fe7dd06,username=\"user\""), response); - } - - private: - boost::shared_ptr<CryptoProvider> crypto; + CPPUNIT_TEST_SUITE(DIGESTMD5ClientAuthenticatorTest); + CPPUNIT_TEST(testGetInitialResponse); + CPPUNIT_TEST(testGetResponse); + CPPUNIT_TEST(testGetResponse_WithAuthorizationID); + //CPPUNIT_TEST(testSetChallenge); + CPPUNIT_TEST_SUITE_END(); + + public: + void setUp() { + crypto = std::shared_ptr<CryptoProvider>(PlatformCryptoProvider::create()); + } + + void testGetInitialResponse() { + DIGESTMD5ClientAuthenticator testling("xmpp.example.com", "abcdefgh", crypto.get()); + + CPPUNIT_ASSERT(!testling.getResponse()); + } + + void testGetResponse() { + DIGESTMD5ClientAuthenticator testling("xmpp.example.com", "abcdefgh", crypto.get()); + + testling.setCredentials("user", createSafeByteArray("pass"), ""); + testling.setChallenge(createByteArray( + "realm=\"example.com\"," + "nonce=\"O6skKPuaCZEny3hteI19qXMBXSadoWs840MchORo\"," + "qop=auth,charset=utf-8,algorithm=md5-sess")); + + SafeByteArray response = *testling.getResponse(); + + CPPUNIT_ASSERT_EQUAL(createSafeByteArray("charset=utf-8,cnonce=\"abcdefgh\",digest-uri=\"xmpp/xmpp.example.com\",nc=00000001,nonce=\"O6skKPuaCZEny3hteI19qXMBXSadoWs840MchORo\",qop=auth,realm=\"example.com\",response=088891c800ecff1b842159ad6459104a,username=\"user\""), response); + } + + void testGetResponse_WithAuthorizationID() { + DIGESTMD5ClientAuthenticator testling("xmpp.example.com", "abcdefgh", crypto.get()); + + testling.setCredentials("user", createSafeByteArray("pass"), "myauthzid"); + testling.setChallenge(createByteArray( + "realm=\"example.com\"," + "nonce=\"O6skKPuaCZEny3hteI19qXMBXSadoWs840MchORo\"," + "qop=auth,charset=utf-8,algorithm=md5-sess")); + + SafeByteArray response = *testling.getResponse(); + + CPPUNIT_ASSERT_EQUAL(createSafeByteArray("authzid=\"myauthzid\",charset=utf-8,cnonce=\"abcdefgh\",digest-uri=\"xmpp/xmpp.example.com\",nc=00000001,nonce=\"O6skKPuaCZEny3hteI19qXMBXSadoWs840MchORo\",qop=auth,realm=\"example.com\",response=4293834432b6e7889a2dee7e8fe7dd06,username=\"user\""), response); + } + + private: + std::shared_ptr<CryptoProvider> crypto; }; CPPUNIT_TEST_SUITE_REGISTRATION(DIGESTMD5ClientAuthenticatorTest); diff --git a/Swiften/SASL/UnitTest/DIGESTMD5PropertiesTest.cpp b/Swiften/SASL/UnitTest/DIGESTMD5PropertiesTest.cpp index d664f14..841f580 100644 --- a/Swiften/SASL/UnitTest/DIGESTMD5PropertiesTest.cpp +++ b/Swiften/SASL/UnitTest/DIGESTMD5PropertiesTest.cpp @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <cppunit/extensions/HelperMacros.h> @@ -12,44 +12,44 @@ using namespace Swift; class DIGESTMD5PropertiesTest : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(DIGESTMD5PropertiesTest); - CPPUNIT_TEST(testParse); - CPPUNIT_TEST(testSerialize); - CPPUNIT_TEST_SUITE_END(); - - public: - void testParse() { - DIGESTMD5Properties properties = DIGESTMD5Properties::parse(createByteArray( - "realm=\"myrealm1\",realm=\"myrealm2\",nonce=\"mynonce\"," - "algorithm=md5-sess,charset=utf-8")); - - CPPUNIT_ASSERT(properties.getValue("realm")); - CPPUNIT_ASSERT_EQUAL(std::string("myrealm1"), *properties.getValue("realm")); - CPPUNIT_ASSERT(properties.getValue("nonce")); - CPPUNIT_ASSERT_EQUAL(std::string("mynonce"), *properties.getValue("nonce")); - CPPUNIT_ASSERT(properties.getValue("algorithm")); - CPPUNIT_ASSERT_EQUAL(std::string("md5-sess"), *properties.getValue("algorithm")); - CPPUNIT_ASSERT(properties.getValue("charset")); - CPPUNIT_ASSERT_EQUAL(std::string("utf-8"), *properties.getValue("charset")); - } - - void testSerialize() { - DIGESTMD5Properties properties; - properties.setValue("authzid", "myauthzid"); - properties.setValue("charset", "utf-8"); - properties.setValue("cnonce", "mycnonce"); - properties.setValue("digest-uri", "mydigesturi"); - properties.setValue("nc", "1"); - properties.setValue("nonce", "mynonce"); - properties.setValue("qop", "auth"); - properties.setValue("realm", "myrealm"); - properties.setValue("response", "myresponse"); - properties.setValue("username", "myuser"); - - ByteArray result = properties.serialize(); - ByteArray expected(createByteArray("authzid=\"myauthzid\",charset=utf-8,cnonce=\"mycnonce\",digest-uri=\"mydigesturi\",nc=1,nonce=\"mynonce\",qop=auth,realm=\"myrealm\",response=myresponse,username=\"myuser\"")); - CPPUNIT_ASSERT_EQUAL(byteArrayToString(expected), byteArrayToString(result)); - } + CPPUNIT_TEST_SUITE(DIGESTMD5PropertiesTest); + CPPUNIT_TEST(testParse); + CPPUNIT_TEST(testSerialize); + CPPUNIT_TEST_SUITE_END(); + + public: + void testParse() { + DIGESTMD5Properties properties = DIGESTMD5Properties::parse(createByteArray( + "realm=\"myrealm1\",realm=\"myrealm2\",nonce=\"mynonce\"," + "algorithm=md5-sess,charset=utf-8")); + + CPPUNIT_ASSERT(properties.getValue("realm")); + CPPUNIT_ASSERT_EQUAL(std::string("myrealm1"), *properties.getValue("realm")); + CPPUNIT_ASSERT(properties.getValue("nonce")); + CPPUNIT_ASSERT_EQUAL(std::string("mynonce"), *properties.getValue("nonce")); + CPPUNIT_ASSERT(properties.getValue("algorithm")); + CPPUNIT_ASSERT_EQUAL(std::string("md5-sess"), *properties.getValue("algorithm")); + CPPUNIT_ASSERT(properties.getValue("charset")); + CPPUNIT_ASSERT_EQUAL(std::string("utf-8"), *properties.getValue("charset")); + } + + void testSerialize() { + DIGESTMD5Properties properties; + properties.setValue("authzid", "myauthzid"); + properties.setValue("charset", "utf-8"); + properties.setValue("cnonce", "mycnonce"); + properties.setValue("digest-uri", "mydigesturi"); + properties.setValue("nc", "1"); + properties.setValue("nonce", "mynonce"); + properties.setValue("qop", "auth"); + properties.setValue("realm", "myrealm"); + properties.setValue("response", "myresponse"); + properties.setValue("username", "myuser"); + + ByteArray result = properties.serialize(); + ByteArray expected(createByteArray("authzid=\"myauthzid\",charset=utf-8,cnonce=\"mycnonce\",digest-uri=\"mydigesturi\",nc=1,nonce=\"mynonce\",qop=auth,realm=\"myrealm\",response=myresponse,username=\"myuser\"")); + CPPUNIT_ASSERT_EQUAL(byteArrayToString(expected), byteArrayToString(result)); + } }; CPPUNIT_TEST_SUITE_REGISTRATION(DIGESTMD5PropertiesTest); diff --git a/Swiften/SASL/UnitTest/EXTERNALClientAuthenticatorTest.cpp b/Swiften/SASL/UnitTest/EXTERNALClientAuthenticatorTest.cpp new file mode 100644 index 0000000..728eed6 --- /dev/null +++ b/Swiften/SASL/UnitTest/EXTERNALClientAuthenticatorTest.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include <QA/Checker/IO.h> + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include <Swiften/SASL/EXTERNALClientAuthenticator.h> + +using namespace Swift; + +class EXTERNALClientAuthenticatorTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(EXTERNALClientAuthenticatorTest); + CPPUNIT_TEST(testGetResponse_WithoutAuthzID); + CPPUNIT_TEST(testGetResponse_WithAuthzID); + CPPUNIT_TEST_SUITE_END(); + + public: + void testGetResponse_WithoutAuthzID() { + EXTERNALClientAuthenticator testling; + + // Authcid and password are not used (ignored) + testling.setCredentials("user", createSafeByteArray("pass")); + + boost::optional<SafeByteArray> response = testling.getResponse(); + + // No data should have been returned + bool result = !response; + + CPPUNIT_ASSERT(result); + } + + void testGetResponse_WithAuthzID() { + EXTERNALClientAuthenticator testling; + + // Authcid and password are not used (ignored) + testling.setCredentials("user", createSafeByteArray("pass"), "authz"); + + CPPUNIT_ASSERT_EQUAL(*testling.getResponse(), createSafeByteArray("authz", 5)); + } +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(EXTERNALClientAuthenticatorTest); diff --git a/Swiften/SASL/UnitTest/PLAINClientAuthenticatorTest.cpp b/Swiften/SASL/UnitTest/PLAINClientAuthenticatorTest.cpp index 3416923..070b4d9 100644 --- a/Swiften/SASL/UnitTest/PLAINClientAuthenticatorTest.cpp +++ b/Swiften/SASL/UnitTest/PLAINClientAuthenticatorTest.cpp @@ -1,39 +1,40 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ -#include <Swiften/SASL/PLAINClientAuthenticator.h> - #include <QA/Checker/IO.h> + #include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/TestFactoryRegistry.h> +#include <Swiften/SASL/PLAINClientAuthenticator.h> + using namespace Swift; class PLAINClientAuthenticatorTest : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(PLAINClientAuthenticatorTest); - CPPUNIT_TEST(testGetResponse_WithoutAuthzID); - CPPUNIT_TEST(testGetResponse_WithAuthzID); - CPPUNIT_TEST_SUITE_END(); + CPPUNIT_TEST_SUITE(PLAINClientAuthenticatorTest); + CPPUNIT_TEST(testGetResponse_WithoutAuthzID); + CPPUNIT_TEST(testGetResponse_WithAuthzID); + CPPUNIT_TEST_SUITE_END(); - public: - void testGetResponse_WithoutAuthzID() { - PLAINClientAuthenticator testling; + public: + void testGetResponse_WithoutAuthzID() { + PLAINClientAuthenticator testling; - testling.setCredentials("user", createSafeByteArray("pass")); + testling.setCredentials("user", createSafeByteArray("pass")); - CPPUNIT_ASSERT_EQUAL(*testling.getResponse(), createSafeByteArray("\0user\0pass", 10)); - } + CPPUNIT_ASSERT_EQUAL(*testling.getResponse(), createSafeByteArray("\0user\0pass", 10)); + } - void testGetResponse_WithAuthzID() { - PLAINClientAuthenticator testling; + void testGetResponse_WithAuthzID() { + PLAINClientAuthenticator testling; - testling.setCredentials("user", createSafeByteArray("pass"), "authz"); + testling.setCredentials("user", createSafeByteArray("pass"), "authz"); - CPPUNIT_ASSERT_EQUAL(*testling.getResponse(), createSafeByteArray("authz\0user\0pass", 15)); - } + CPPUNIT_ASSERT_EQUAL(*testling.getResponse(), createSafeByteArray("authz\0user\0pass", 15)); + } }; CPPUNIT_TEST_SUITE_REGISTRATION(PLAINClientAuthenticatorTest); diff --git a/Swiften/SASL/UnitTest/PLAINMessageTest.cpp b/Swiften/SASL/UnitTest/PLAINMessageTest.cpp index e917af5..271b827 100644 --- a/Swiften/SASL/UnitTest/PLAINMessageTest.cpp +++ b/Swiften/SASL/UnitTest/PLAINMessageTest.cpp @@ -1,70 +1,70 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ -#include <Swiften/Base/ByteArray.h> #include <QA/Checker/IO.h> #include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/TestFactoryRegistry.h> +#include <Swiften/Base/ByteArray.h> #include <Swiften/SASL/PLAINMessage.h> using namespace Swift; class PLAINMessageTest : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(PLAINMessageTest); - CPPUNIT_TEST(testGetValue_WithoutAuthzID); - CPPUNIT_TEST(testGetValue_WithAuthzID); - CPPUNIT_TEST(testConstructor_WithoutAuthzID); - CPPUNIT_TEST(testConstructor_WithAuthzID); - CPPUNIT_TEST(testConstructor_NoAuthcid); - CPPUNIT_TEST(testConstructor_NoPassword); - CPPUNIT_TEST_SUITE_END(); - - public: - PLAINMessageTest() {} - - void testGetValue_WithoutAuthzID() { - PLAINMessage message("user", createSafeByteArray("pass")); - CPPUNIT_ASSERT_EQUAL(message.getValue(), createSafeByteArray("\0user\0pass", 10)); - } - - void testGetValue_WithAuthzID() { - PLAINMessage message("user", createSafeByteArray("pass"), "authz"); - CPPUNIT_ASSERT_EQUAL(message.getValue(), createSafeByteArray("authz\0user\0pass", 15)); - } - - void testConstructor_WithoutAuthzID() { - PLAINMessage message(createSafeByteArray("\0user\0pass", 10)); - - CPPUNIT_ASSERT_EQUAL(std::string(""), message.getAuthorizationID()); - CPPUNIT_ASSERT_EQUAL(std::string("user"), message.getAuthenticationID()); - CPPUNIT_ASSERT_EQUAL(createSafeByteArray("pass"), message.getPassword()); - } - - void testConstructor_WithAuthzID() { - PLAINMessage message(createSafeByteArray("authz\0user\0pass", 15)); - - CPPUNIT_ASSERT_EQUAL(std::string("authz"), message.getAuthorizationID()); - CPPUNIT_ASSERT_EQUAL(std::string("user"), message.getAuthenticationID()); - CPPUNIT_ASSERT_EQUAL(createSafeByteArray("pass"), message.getPassword()); - } - - void testConstructor_NoAuthcid() { - PLAINMessage message(createSafeByteArray("authzid", 7)); - - CPPUNIT_ASSERT_EQUAL(std::string(""), message.getAuthenticationID()); - } - - void testConstructor_NoPassword() { - PLAINMessage message(createSafeByteArray("authzid\0authcid", 15)); - - CPPUNIT_ASSERT_EQUAL(std::string(""), message.getAuthenticationID()); - } + CPPUNIT_TEST_SUITE(PLAINMessageTest); + CPPUNIT_TEST(testGetValue_WithoutAuthzID); + CPPUNIT_TEST(testGetValue_WithAuthzID); + CPPUNIT_TEST(testConstructor_WithoutAuthzID); + CPPUNIT_TEST(testConstructor_WithAuthzID); + CPPUNIT_TEST(testConstructor_NoAuthcid); + CPPUNIT_TEST(testConstructor_NoPassword); + CPPUNIT_TEST_SUITE_END(); + + public: + PLAINMessageTest() {} + + void testGetValue_WithoutAuthzID() { + PLAINMessage message("user", createSafeByteArray("pass")); + CPPUNIT_ASSERT_EQUAL(message.getValue(), createSafeByteArray("\0user\0pass", 10)); + } + + void testGetValue_WithAuthzID() { + PLAINMessage message("user", createSafeByteArray("pass"), "authz"); + CPPUNIT_ASSERT_EQUAL(message.getValue(), createSafeByteArray("authz\0user\0pass", 15)); + } + + void testConstructor_WithoutAuthzID() { + PLAINMessage message(createSafeByteArray("\0user\0pass", 10)); + + CPPUNIT_ASSERT_EQUAL(std::string(""), message.getAuthorizationID()); + CPPUNIT_ASSERT_EQUAL(std::string("user"), message.getAuthenticationID()); + CPPUNIT_ASSERT_EQUAL(createSafeByteArray("pass"), message.getPassword()); + } + + void testConstructor_WithAuthzID() { + PLAINMessage message(createSafeByteArray("authz\0user\0pass", 15)); + + CPPUNIT_ASSERT_EQUAL(std::string("authz"), message.getAuthorizationID()); + CPPUNIT_ASSERT_EQUAL(std::string("user"), message.getAuthenticationID()); + CPPUNIT_ASSERT_EQUAL(createSafeByteArray("pass"), message.getPassword()); + } + + void testConstructor_NoAuthcid() { + PLAINMessage message(createSafeByteArray("authzid", 7)); + + CPPUNIT_ASSERT_EQUAL(std::string(""), message.getAuthenticationID()); + } + + void testConstructor_NoPassword() { + PLAINMessage message(createSafeByteArray("authzid\0authcid", 15)); + + CPPUNIT_ASSERT_EQUAL(std::string(""), message.getAuthenticationID()); + } }; CPPUNIT_TEST_SUITE_REGISTRATION(PLAINMessageTest); diff --git a/Swiften/SASL/UnitTest/SCRAMSHA1ClientAuthenticatorTest.cpp b/Swiften/SASL/UnitTest/SCRAMSHA1ClientAuthenticatorTest.cpp index 3341ad8..7f408c6 100644 --- a/Swiften/SASL/UnitTest/SCRAMSHA1ClientAuthenticatorTest.cpp +++ b/Swiften/SASL/UnitTest/SCRAMSHA1ClientAuthenticatorTest.cpp @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010-2013 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <QA/Checker/IO.h> @@ -9,225 +9,225 @@ #include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/TestFactoryRegistry.h> -#include <Swiften/SASL/SCRAMSHA1ClientAuthenticator.h> #include <Swiften/Base/ByteArray.h> -#include <Swiften/IDN/IDNConverter.h> -#include <Swiften/IDN/PlatformIDNConverter.h> #include <Swiften/Crypto/CryptoProvider.h> #include <Swiften/Crypto/PlatformCryptoProvider.h> +#include <Swiften/IDN/IDNConverter.h> +#include <Swiften/IDN/PlatformIDNConverter.h> +#include <Swiften/SASL/SCRAMSHA1ClientAuthenticator.h> using namespace Swift; class SCRAMSHA1ClientAuthenticatorTest : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(SCRAMSHA1ClientAuthenticatorTest); - CPPUNIT_TEST(testGetInitialResponse); - CPPUNIT_TEST(testGetInitialResponse_UsernameHasSpecialChars); - CPPUNIT_TEST(testGetInitialResponse_WithAuthorizationID); - CPPUNIT_TEST(testGetInitialResponse_WithAuthorizationIDWithSpecialChars); - CPPUNIT_TEST(testGetInitialResponse_WithoutChannelBindingWithTLSChannelBindingData); - CPPUNIT_TEST(testGetInitialResponse_WithChannelBindingWithTLSChannelBindingData); - CPPUNIT_TEST(testGetFinalResponse); - CPPUNIT_TEST(testGetFinalResponse_WithoutChannelBindingWithTLSChannelBindingData); - CPPUNIT_TEST(testGetFinalResponse_WithChannelBindingWithTLSChannelBindingData); - CPPUNIT_TEST(testSetChallenge); - CPPUNIT_TEST(testSetChallenge_InvalidClientNonce); - CPPUNIT_TEST(testSetChallenge_OnlyClientNonce); - CPPUNIT_TEST(testSetChallenge_InvalidIterations); - CPPUNIT_TEST(testSetChallenge_ZeroIterations); - CPPUNIT_TEST(testSetChallenge_NegativeIterations); - CPPUNIT_TEST(testSetChallenge_MissingIterations); - CPPUNIT_TEST(testSetFinalChallenge); - CPPUNIT_TEST(testSetFinalChallenge_InvalidChallenge); - CPPUNIT_TEST(testGetResponseAfterFinalChallenge); - CPPUNIT_TEST_SUITE_END(); + CPPUNIT_TEST_SUITE(SCRAMSHA1ClientAuthenticatorTest); + CPPUNIT_TEST(testGetInitialResponse); + CPPUNIT_TEST(testGetInitialResponse_UsernameHasSpecialChars); + CPPUNIT_TEST(testGetInitialResponse_WithAuthorizationID); + CPPUNIT_TEST(testGetInitialResponse_WithAuthorizationIDWithSpecialChars); + CPPUNIT_TEST(testGetInitialResponse_WithoutChannelBindingWithTLSChannelBindingData); + CPPUNIT_TEST(testGetInitialResponse_WithChannelBindingWithTLSChannelBindingData); + CPPUNIT_TEST(testGetFinalResponse); + CPPUNIT_TEST(testGetFinalResponse_WithoutChannelBindingWithTLSChannelBindingData); + CPPUNIT_TEST(testGetFinalResponse_WithChannelBindingWithTLSChannelBindingData); + CPPUNIT_TEST(testSetChallenge); + CPPUNIT_TEST(testSetChallenge_InvalidClientNonce); + CPPUNIT_TEST(testSetChallenge_OnlyClientNonce); + CPPUNIT_TEST(testSetChallenge_InvalidIterations); + CPPUNIT_TEST(testSetChallenge_ZeroIterations); + CPPUNIT_TEST(testSetChallenge_NegativeIterations); + CPPUNIT_TEST(testSetChallenge_MissingIterations); + CPPUNIT_TEST(testSetFinalChallenge); + CPPUNIT_TEST(testSetFinalChallenge_InvalidChallenge); + CPPUNIT_TEST(testGetResponseAfterFinalChallenge); + CPPUNIT_TEST_SUITE_END(); - public: - void setUp() { - idnConverter = boost::shared_ptr<IDNConverter>(PlatformIDNConverter::create()); - crypto = boost::shared_ptr<CryptoProvider>(PlatformCryptoProvider::create()); - } + public: + void setUp() { + idnConverter = std::shared_ptr<IDNConverter>(PlatformIDNConverter::create()); + crypto = std::shared_ptr<CryptoProvider>(PlatformCryptoProvider::create()); + } - void testGetInitialResponse() { - SCRAMSHA1ClientAuthenticator testling("abcdefghABCDEFGH", false, idnConverter.get(), crypto.get()); - testling.setCredentials("user", createSafeByteArray("pass"), ""); + void testGetInitialResponse() { + SCRAMSHA1ClientAuthenticator testling("abcdefghABCDEFGH", false, idnConverter.get(), crypto.get()); + testling.setCredentials("user", createSafeByteArray("pass"), ""); - SafeByteArray response = *testling.getResponse(); + SafeByteArray response = *testling.getResponse(); - CPPUNIT_ASSERT_EQUAL(createSafeByteArray("n,,n=user,r=abcdefghABCDEFGH"), response); - } + CPPUNIT_ASSERT_EQUAL(createSafeByteArray("n,,n=user,r=abcdefghABCDEFGH"), response); + } - void testGetInitialResponse_UsernameHasSpecialChars() { - SCRAMSHA1ClientAuthenticator testling("abcdefghABCDEFGH", false, idnConverter.get(), crypto.get()); - testling.setCredentials(",us=,er=", createSafeByteArray("pass"), ""); + void testGetInitialResponse_UsernameHasSpecialChars() { + SCRAMSHA1ClientAuthenticator testling("abcdefghABCDEFGH", false, idnConverter.get(), crypto.get()); + testling.setCredentials(",us=,er=", createSafeByteArray("pass"), ""); - SafeByteArray response = *testling.getResponse(); + SafeByteArray response = *testling.getResponse(); - CPPUNIT_ASSERT_EQUAL(createSafeByteArray("n,,n==2Cus=3D=2Cer=3D,r=abcdefghABCDEFGH"), response); - } + CPPUNIT_ASSERT_EQUAL(createSafeByteArray("n,,n==2Cus=3D=2Cer=3D,r=abcdefghABCDEFGH"), response); + } - void testGetInitialResponse_WithAuthorizationID() { - SCRAMSHA1ClientAuthenticator testling("abcdefghABCDEFGH", false, idnConverter.get(), crypto.get()); - testling.setCredentials("user", createSafeByteArray("pass"), "auth"); + void testGetInitialResponse_WithAuthorizationID() { + SCRAMSHA1ClientAuthenticator testling("abcdefghABCDEFGH", false, idnConverter.get(), crypto.get()); + testling.setCredentials("user", createSafeByteArray("pass"), "auth"); - SafeByteArray response = *testling.getResponse(); + SafeByteArray response = *testling.getResponse(); - CPPUNIT_ASSERT_EQUAL(createSafeByteArray("n,a=auth,n=user,r=abcdefghABCDEFGH"), response); - } + CPPUNIT_ASSERT_EQUAL(createSafeByteArray("n,a=auth,n=user,r=abcdefghABCDEFGH"), response); + } - void testGetInitialResponse_WithAuthorizationIDWithSpecialChars() { - SCRAMSHA1ClientAuthenticator testling("abcdefghABCDEFGH", false, idnConverter.get(), crypto.get()); - testling.setCredentials("user", createSafeByteArray("pass"), "a=u,th"); + void testGetInitialResponse_WithAuthorizationIDWithSpecialChars() { + SCRAMSHA1ClientAuthenticator testling("abcdefghABCDEFGH", false, idnConverter.get(), crypto.get()); + testling.setCredentials("user", createSafeByteArray("pass"), "a=u,th"); - SafeByteArray response = *testling.getResponse(); + SafeByteArray response = *testling.getResponse(); - CPPUNIT_ASSERT_EQUAL(createSafeByteArray("n,a=a=3Du=2Cth,n=user,r=abcdefghABCDEFGH"), response); - } + CPPUNIT_ASSERT_EQUAL(createSafeByteArray("n,a=a=3Du=2Cth,n=user,r=abcdefghABCDEFGH"), response); + } - void testGetInitialResponse_WithoutChannelBindingWithTLSChannelBindingData() { - SCRAMSHA1ClientAuthenticator testling("abcdefghABCDEFGH", false, idnConverter.get(), crypto.get()); - testling.setTLSChannelBindingData(createByteArray("xyza")); - testling.setCredentials("user", createSafeByteArray("pass"), ""); + void testGetInitialResponse_WithoutChannelBindingWithTLSChannelBindingData() { + SCRAMSHA1ClientAuthenticator testling("abcdefghABCDEFGH", false, idnConverter.get(), crypto.get()); + testling.setTLSChannelBindingData(createByteArray("xyza")); + testling.setCredentials("user", createSafeByteArray("pass"), ""); - SafeByteArray response = *testling.getResponse(); + SafeByteArray response = *testling.getResponse(); - CPPUNIT_ASSERT_EQUAL(createSafeByteArray("y,,n=user,r=abcdefghABCDEFGH"), response); - } + CPPUNIT_ASSERT_EQUAL(createSafeByteArray("y,,n=user,r=abcdefghABCDEFGH"), response); + } - void testGetInitialResponse_WithChannelBindingWithTLSChannelBindingData() { - SCRAMSHA1ClientAuthenticator testling("abcdefghABCDEFGH", true, idnConverter.get(), crypto.get()); - testling.setTLSChannelBindingData(createByteArray("xyza")); - testling.setCredentials("user", createSafeByteArray("pass"), ""); + void testGetInitialResponse_WithChannelBindingWithTLSChannelBindingData() { + SCRAMSHA1ClientAuthenticator testling("abcdefghABCDEFGH", true, idnConverter.get(), crypto.get()); + testling.setTLSChannelBindingData(createByteArray("xyza")); + testling.setCredentials("user", createSafeByteArray("pass"), ""); - SafeByteArray response = *testling.getResponse(); + SafeByteArray response = *testling.getResponse(); - CPPUNIT_ASSERT_EQUAL(createSafeByteArray("p=tls-unique,,n=user,r=abcdefghABCDEFGH"), response); - } + CPPUNIT_ASSERT_EQUAL(createSafeByteArray("p=tls-unique,,n=user,r=abcdefghABCDEFGH"), response); + } - void testGetFinalResponse() { - SCRAMSHA1ClientAuthenticator testling("abcdefgh", false, idnConverter.get(), crypto.get()); - testling.setCredentials("user", createSafeByteArray("pass"), ""); - testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096")); + void testGetFinalResponse() { + SCRAMSHA1ClientAuthenticator testling("abcdefgh", false, idnConverter.get(), crypto.get()); + testling.setCredentials("user", createSafeByteArray("pass"), ""); + testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096")); - SafeByteArray response = *testling.getResponse(); + SafeByteArray response = *testling.getResponse(); - CPPUNIT_ASSERT_EQUAL(createSafeByteArray("c=biws,r=abcdefghABCDEFGH,p=CZbjGDpIteIJwQNBgO0P8pKkMGY="), response); - } + CPPUNIT_ASSERT_EQUAL(createSafeByteArray("c=biws,r=abcdefghABCDEFGH,p=CZbjGDpIteIJwQNBgO0P8pKkMGY="), response); + } - void testGetFinalResponse_WithoutChannelBindingWithTLSChannelBindingData() { - SCRAMSHA1ClientAuthenticator testling("abcdefgh", false, idnConverter.get(), crypto.get()); - testling.setCredentials("user", createSafeByteArray("pass"), ""); - testling.setTLSChannelBindingData(createByteArray("xyza")); - testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096")); + void testGetFinalResponse_WithoutChannelBindingWithTLSChannelBindingData() { + SCRAMSHA1ClientAuthenticator testling("abcdefgh", false, idnConverter.get(), crypto.get()); + testling.setCredentials("user", createSafeByteArray("pass"), ""); + testling.setTLSChannelBindingData(createByteArray("xyza")); + testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096")); - SafeByteArray response = *testling.getResponse(); + SafeByteArray response = *testling.getResponse(); - CPPUNIT_ASSERT_EQUAL(createSafeByteArray("c=eSws,r=abcdefghABCDEFGH,p=JNpsiFEcxZvNZ1+FFBBqrYvYxMk="), response); - } + CPPUNIT_ASSERT_EQUAL(createSafeByteArray("c=eSws,r=abcdefghABCDEFGH,p=JNpsiFEcxZvNZ1+FFBBqrYvYxMk="), response); + } - void testGetFinalResponse_WithChannelBindingWithTLSChannelBindingData() { - SCRAMSHA1ClientAuthenticator testling("abcdefgh", true, idnConverter.get(), crypto.get()); - testling.setCredentials("user", createSafeByteArray("pass"), ""); - testling.setTLSChannelBindingData(createByteArray("xyza")); - testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096")); + void testGetFinalResponse_WithChannelBindingWithTLSChannelBindingData() { + SCRAMSHA1ClientAuthenticator testling("abcdefgh", true, idnConverter.get(), crypto.get()); + testling.setCredentials("user", createSafeByteArray("pass"), ""); + testling.setTLSChannelBindingData(createByteArray("xyza")); + testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096")); - SafeByteArray response = *testling.getResponse(); + SafeByteArray response = *testling.getResponse(); - CPPUNIT_ASSERT_EQUAL(createSafeByteArray("c=cD10bHMtdW5pcXVlLCx4eXph,r=abcdefghABCDEFGH,p=i6Rghite81P1ype8XxaVAa5l7v0="), response); - } + CPPUNIT_ASSERT_EQUAL(createSafeByteArray("c=cD10bHMtdW5pcXVlLCx4eXph,r=abcdefghABCDEFGH,p=i6Rghite81P1ype8XxaVAa5l7v0="), response); + } - void testSetFinalChallenge() { - SCRAMSHA1ClientAuthenticator testling("abcdefgh", false, idnConverter.get(), crypto.get()); - testling.setCredentials("user", createSafeByteArray("pass"), ""); - testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096")); + void testSetFinalChallenge() { + SCRAMSHA1ClientAuthenticator testling("abcdefgh", false, idnConverter.get(), crypto.get()); + testling.setCredentials("user", createSafeByteArray("pass"), ""); + testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096")); - bool result = testling.setChallenge(createByteArray("v=Dd+Q20knZs9jeeK0pi1Mx1Se+yo=")); + bool result = testling.setChallenge(createByteArray("v=Dd+Q20knZs9jeeK0pi1Mx1Se+yo=")); - CPPUNIT_ASSERT(result); - } + CPPUNIT_ASSERT(result); + } - void testSetChallenge() { - SCRAMSHA1ClientAuthenticator testling("abcdefgh", false, idnConverter.get(), crypto.get()); - testling.setCredentials("user", createSafeByteArray("pass"), ""); + void testSetChallenge() { + SCRAMSHA1ClientAuthenticator testling("abcdefgh", false, idnConverter.get(), crypto.get()); + testling.setCredentials("user", createSafeByteArray("pass"), ""); - bool result = testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096")); + bool result = testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096")); - CPPUNIT_ASSERT(result); - } + CPPUNIT_ASSERT(result); + } - void testSetChallenge_InvalidClientNonce() { - SCRAMSHA1ClientAuthenticator testling("abcdefgh", false, idnConverter.get(), crypto.get()); - testling.setCredentials("user", createSafeByteArray("pass"), ""); + void testSetChallenge_InvalidClientNonce() { + SCRAMSHA1ClientAuthenticator testling("abcdefgh", false, idnConverter.get(), crypto.get()); + testling.setCredentials("user", createSafeByteArray("pass"), ""); - bool result = testling.setChallenge(createByteArray("r=abcdefgiABCDEFGH,s=MTIzNDU2NzgK,i=4096")); + bool result = testling.setChallenge(createByteArray("r=abcdefgiABCDEFGH,s=MTIzNDU2NzgK,i=4096")); - CPPUNIT_ASSERT(!result); - } + CPPUNIT_ASSERT(!result); + } - void testSetChallenge_OnlyClientNonce() { - SCRAMSHA1ClientAuthenticator testling("abcdefgh", false, idnConverter.get(), crypto.get()); - testling.setCredentials("user", createSafeByteArray("pass"), ""); + void testSetChallenge_OnlyClientNonce() { + SCRAMSHA1ClientAuthenticator testling("abcdefgh", false, idnConverter.get(), crypto.get()); + testling.setCredentials("user", createSafeByteArray("pass"), ""); - bool result = testling.setChallenge(createByteArray("r=abcdefgh,s=MTIzNDU2NzgK,i=4096")); + bool result = testling.setChallenge(createByteArray("r=abcdefgh,s=MTIzNDU2NzgK,i=4096")); - CPPUNIT_ASSERT(!result); - } + CPPUNIT_ASSERT(!result); + } - void testSetChallenge_InvalidIterations() { - SCRAMSHA1ClientAuthenticator testling("abcdefgh", false, idnConverter.get(), crypto.get()); - testling.setCredentials("user", createSafeByteArray("pass"), ""); + void testSetChallenge_InvalidIterations() { + SCRAMSHA1ClientAuthenticator testling("abcdefgh", false, idnConverter.get(), crypto.get()); + testling.setCredentials("user", createSafeByteArray("pass"), ""); - bool result = testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=bla")); + bool result = testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=bla")); - CPPUNIT_ASSERT(!result); - } + CPPUNIT_ASSERT(!result); + } - void testSetChallenge_MissingIterations() { - SCRAMSHA1ClientAuthenticator testling("abcdefgh", false, idnConverter.get(), crypto.get()); - testling.setCredentials("user", createSafeByteArray("pass"), ""); + void testSetChallenge_MissingIterations() { + SCRAMSHA1ClientAuthenticator testling("abcdefgh", false, idnConverter.get(), crypto.get()); + testling.setCredentials("user", createSafeByteArray("pass"), ""); - bool result = testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK")); + bool result = testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK")); - CPPUNIT_ASSERT(!result); - } + CPPUNIT_ASSERT(!result); + } - void testSetChallenge_ZeroIterations() { - SCRAMSHA1ClientAuthenticator testling("abcdefgh", false, idnConverter.get(), crypto.get()); - testling.setCredentials("user", createSafeByteArray("pass"), ""); + void testSetChallenge_ZeroIterations() { + SCRAMSHA1ClientAuthenticator testling("abcdefgh", false, idnConverter.get(), crypto.get()); + testling.setCredentials("user", createSafeByteArray("pass"), ""); - bool result = testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=0")); + bool result = testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=0")); - CPPUNIT_ASSERT(!result); - } + CPPUNIT_ASSERT(!result); + } - void testSetChallenge_NegativeIterations() { - SCRAMSHA1ClientAuthenticator testling("abcdefgh", false, idnConverter.get(), crypto.get()); - testling.setCredentials("user", createSafeByteArray("pass"), ""); + void testSetChallenge_NegativeIterations() { + SCRAMSHA1ClientAuthenticator testling("abcdefgh", false, idnConverter.get(), crypto.get()); + testling.setCredentials("user", createSafeByteArray("pass"), ""); - bool result = testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=-1")); + bool result = testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=-1")); - CPPUNIT_ASSERT(!result); - } + CPPUNIT_ASSERT(!result); + } - void testSetFinalChallenge_InvalidChallenge() { - SCRAMSHA1ClientAuthenticator testling("abcdefgh", false, idnConverter.get(), crypto.get()); - testling.setCredentials("user", createSafeByteArray("pass"), ""); - testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096")); - bool result = testling.setChallenge(createByteArray("v=e26kI69ICb6zosapLLxrER/631A=")); + void testSetFinalChallenge_InvalidChallenge() { + SCRAMSHA1ClientAuthenticator testling("abcdefgh", false, idnConverter.get(), crypto.get()); + testling.setCredentials("user", createSafeByteArray("pass"), ""); + testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096")); + bool result = testling.setChallenge(createByteArray("v=e26kI69ICb6zosapLLxrER/631A=")); - CPPUNIT_ASSERT(!result); - } + CPPUNIT_ASSERT(!result); + } - void testGetResponseAfterFinalChallenge() { - SCRAMSHA1ClientAuthenticator testling("abcdefgh", false, idnConverter.get(), crypto.get()); - testling.setCredentials("user", createSafeByteArray("pass"), ""); - testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096")); - testling.setChallenge(createByteArray("v=Dd+Q20knZs9jeeK0pi1Mx1Se+yo=")); + void testGetResponseAfterFinalChallenge() { + SCRAMSHA1ClientAuthenticator testling("abcdefgh", false, idnConverter.get(), crypto.get()); + testling.setCredentials("user", createSafeByteArray("pass"), ""); + testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096")); + testling.setChallenge(createByteArray("v=Dd+Q20knZs9jeeK0pi1Mx1Se+yo=")); - CPPUNIT_ASSERT(!testling.getResponse()); - } + CPPUNIT_ASSERT(!testling.getResponse()); + } - boost::shared_ptr<IDNConverter> idnConverter; - boost::shared_ptr<CryptoProvider> crypto; + std::shared_ptr<IDNConverter> idnConverter; + std::shared_ptr<CryptoProvider> crypto; }; CPPUNIT_TEST_SUITE_REGISTRATION(SCRAMSHA1ClientAuthenticatorTest); diff --git a/Swiften/SASL/UnitTest/WindowsServicePrincipalNameTest.cpp b/Swiften/SASL/UnitTest/WindowsServicePrincipalNameTest.cpp new file mode 100644 index 0000000..ef3a9b3 --- /dev/null +++ b/Swiften/SASL/UnitTest/WindowsServicePrincipalNameTest.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2015-2018 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(55222); + CPPUNIT_ASSERT_EQUAL(spn.toString(), std::string("xmpp/mlink.adlon.isode.net:55222/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(55222); + CPPUNIT_ASSERT_EQUAL(spn2.toString(), std::string("xmpp/mlink.adlon.isode.net:55222")); + + 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/WindowsAuthentication.cpp b/Swiften/SASL/WindowsAuthentication.cpp new file mode 100644 index 0000000..f951fac --- /dev/null +++ b/Swiften/SASL/WindowsAuthentication.cpp @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2015-2019 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include <Swiften/SASL/WindowsAuthentication.h> + +#include <iomanip> + +#include <Secext.h> + +#include <Swiften/Base/Log.h> +#include <Swiften/Base/String.h> + +#define ASSIGN_ERROR(status, errorCode) \ +{ \ + errorCode = std::make_shared<boost::system::error_code>(status, boost::system::system_category()); \ + SWIFT_LOG(debug) << std::hex << "status: 0x" << status << ": " << errorCode->message(); \ +} + +#define ASSIGN_SEC_ERROR(status, errorCode) \ +{ \ + if (status == SEC_E_OK) \ + { \ + SWIFT_LOG(debug) << "success"; \ + } \ + else { \ + ASSIGN_ERROR(status, errorCode); \ + } \ +} + +namespace Swift { + +std::shared_ptr<boost::system::error_code> getUserNameEx(std::string& userName, std::string& clientName, std::string& serverName) { + ULONG length = 512; + DWORD status = ERROR_MORE_DATA; + bool firstCall = true; + std::shared_ptr<boost::system::error_code> errorCode; + + while (status == ERROR_MORE_DATA) { + 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 */ + if (GetUserNameExW(NameSamCompatible, vecptr(value), &length)) { + std::size_t position; + + userName = convertWStringToString(std::wstring(vecptr(value), length)); + SWIFT_LOG(debug) << "User Name: " << userName; + + position = userName.find("\\"); + clientName = userName.substr(position + 1); + SWIFT_LOG(debug) << "Client name: " << clientName; + + serverName = userName.substr(0, position); + SWIFT_LOG(debug) << "Server name: " << serverName; + + break; + } + + status = GetLastError(); + if ((firstCall == false) || (status != ERROR_MORE_DATA)) { + ASSIGN_ERROR(status, errorCode); + break; + } + + firstCall = false; + } + + return errorCode; +} + +std::shared_ptr<boost::system::error_code> acquireCredentialsHandle(PCredHandle credentialsHandle) { + SECURITY_STATUS status; + std::shared_ptr<boost::system::error_code> errorCode; + TimeStamp validity; + + status = AcquireCredentialsHandle( + NULL, /* NULL indicates credentials of the user under whose security context it is executing */ + "Kerberos", + SECPKG_CRED_OUTBOUND, /* client credential */ + NULL, + NULL, /* use default credentials */ + NULL, /* not used */ + NULL, /* not used */ + credentialsHandle, + &validity); + ASSIGN_SEC_ERROR(status, errorCode); + + return errorCode; +} + +std::shared_ptr<boost::system::error_code> freeCredentialsHandle(PCredHandle credentialsHandle) { + SECURITY_STATUS status; + std::shared_ptr<boost::system::error_code> errorCode; + + status = FreeCredentialsHandle(credentialsHandle); + ASSIGN_SEC_ERROR(status, errorCode); + + return errorCode; +} + +std::shared_ptr<boost::system::error_code> initializeSecurityContext(const boost::optional<ByteArray>& inputToken, const std::string& servicePrincipalNameString, const PCredHandle credentialsHandle, bool haveContextHandle, PCtxtHandle contextHandle, ULONG contextRequested, ULONG* contextSupported, bool* haveCompleteContext, SafeByteArray& outputToken) { + SECURITY_STATUS status; + std::shared_ptr<boost::system::error_code> errorCode; + SecBufferDesc input; + SecBufferDesc output; + SecBuffer inputTokenBuffer; + SecBuffer outputTokenBuffer; + TimeStamp validity; + + *haveCompleteContext = false; + + input.ulVersion = 0; + input.cBuffers = 1; + input.pBuffers = &inputTokenBuffer; + + inputTokenBuffer.BufferType = SECBUFFER_TOKEN; + inputTokenBuffer.cbBuffer = 0; + inputTokenBuffer.pvBuffer = NULL; + if (inputToken && inputToken->size()) { + inputTokenBuffer.cbBuffer = inputToken->size(); + inputTokenBuffer.pvBuffer = (void *) vecptr(*inputToken); + } + + output.ulVersion = 0; + output.cBuffers = 1; + output.pBuffers = &outputTokenBuffer; + + outputTokenBuffer.BufferType = SECBUFFER_TOKEN; + outputTokenBuffer.cbBuffer = 0; + outputTokenBuffer.pvBuffer = NULL; + + status = InitializeSecurityContext( + credentialsHandle, /* previously acquired handle */ + haveContextHandle ? contextHandle : NULL, /* use partial context on subsequent calls */ + const_cast<char *>(servicePrincipalNameString.c_str()), + contextRequested | ISC_REQ_ALLOCATE_MEMORY, + 0, /* not used */ + SECURITY_NETWORK_DREP, + haveContextHandle ? &input : NULL, + 0, /* not used */ + contextHandle, + &output, + contextSupported, + &validity); + ASSIGN_SEC_ERROR(status, errorCode); /* errorCode set here will only be returned to caller if there was a non-success status */ + if ((status == SEC_I_COMPLETE_AND_CONTINUE) || (status == SEC_I_COMPLETE_NEEDED)) { + /* The Windows documentation suggests that this function is used only for Digest and only on the server side, but still asks to call this function for Kerberos clients. Most likely this function will never be called, but including it for compliance with documentation. */ + errorCode = completeAuthToken(contextHandle, &output); + if (!errorCode) { + /* success, move on */ + } + else { + freeContextBuffer(outputTokenBuffer.pvBuffer); + return errorCode; + } + } + if ((status == SEC_E_OK) || (status == SEC_I_COMPLETE_NEEDED)) { + *haveCompleteContext = true; + } + if ((status == SEC_E_OK) || (status == SEC_I_COMPLETE_AND_CONTINUE) || (status == SEC_I_COMPLETE_NEEDED) || (status == SEC_I_CONTINUE_NEEDED)) { + outputToken = createSafeByteArray (static_cast<unsigned char *>(outputTokenBuffer.pvBuffer), outputTokenBuffer.cbBuffer); + SWIFT_LOG(debug) << "outputToken.size(): " << outputToken.size(); + freeContextBuffer(outputTokenBuffer.pvBuffer); + + return std::shared_ptr<boost::system::error_code>(); /* success */ + } + + return errorCode; +} + +std::shared_ptr<boost::system::error_code> deleteSecurityContext(PCtxtHandle contextHandle) { + SECURITY_STATUS status; + std::shared_ptr<boost::system::error_code> errorCode; + + status = DeleteSecurityContext(contextHandle); + ASSIGN_SEC_ERROR(status, errorCode); + + return errorCode; +} + +std::shared_ptr<boost::system::error_code> completeAuthToken(const PCtxtHandle contextHandle, PSecBufferDesc token) { + SECURITY_STATUS status; + std::shared_ptr<boost::system::error_code> errorCode; + + status = CompleteAuthToken( + contextHandle, /* partial context */ + token); + ASSIGN_SEC_ERROR(status, errorCode); + + return errorCode; +} + +std::shared_ptr<boost::system::error_code> freeContextBuffer(PVOID contextBuffer) { + SECURITY_STATUS status; + std::shared_ptr<boost::system::error_code> errorCode; + + if (contextBuffer == NULL) { + return errorCode; + } + + status = FreeContextBuffer(contextBuffer); + ASSIGN_SEC_ERROR(status, errorCode); + + return errorCode; +} + +std::shared_ptr<boost::system::error_code> decryptMessage(const PCtxtHandle contextHandle, const ByteArray& message, SafeByteArray& decrypted) { + /* Following https://msdn.microsoft.com/en-us/library/windows/desktop/aa380496%28v=vs.85%29.aspx */ + + SECURITY_STATUS status; + std::shared_ptr<boost::system::error_code> errorCode; + SecBufferDesc inOut; + SecBuffer messageBuffer[2]; + SafeByteArray inputMessage; + ULONG qualityOfProtection; + + inOut.ulVersion = SECBUFFER_VERSION; + inOut.cBuffers = 2; + inOut.pBuffers = messageBuffer; + + inputMessage = createSafeByteArray (message); /* Make a copy as DecryptMessage decrypts the input in place, overwriting it */ + messageBuffer[0].BufferType = SECBUFFER_STREAM; + messageBuffer[0].cbBuffer = inputMessage.size(); + messageBuffer[0].pvBuffer = static_cast<void *>(vecptr(inputMessage)); + + messageBuffer[1].BufferType = SECBUFFER_DATA; + messageBuffer[1].cbBuffer = 0; + messageBuffer[1].pvBuffer = NULL; + + SWIFT_LOG(debug) << "inputMessage.size(): " << inputMessage.size(); + + status = DecryptMessage( + contextHandle, + &inOut, + 0, /* Don't maintain sequence numbers */ + &qualityOfProtection); + ASSIGN_SEC_ERROR(status, errorCode); + if (status == SEC_E_OK) { + if (qualityOfProtection == SECQOP_WRAP_NO_ENCRYPT) { + SWIFT_LOG(debug) << "Message was signed only"; + } + else { + SWIFT_LOG(debug) << "Message was encrypted"; + } + + SWIFT_LOG(debug) << "messageBuffer[1].cbBuffer: " << messageBuffer[1].cbBuffer; + + decrypted = createSafeByteArray (static_cast<unsigned char *>(messageBuffer[1].pvBuffer), messageBuffer[1].cbBuffer); + } + + return errorCode; +} + +std::shared_ptr<boost::system::error_code> encryptMessage(const PCtxtHandle contextHandle, const SecPkgContext_Sizes& sizes, const SafeByteArray& message, SafeByteArray& output) { + /* Following https://msdn.microsoft.com/en-us/library/windows/desktop/aa380496%28v=vs.85%29.aspx */ + + SECURITY_STATUS status; + std::shared_ptr<boost::system::error_code> errorCode; + SecBufferDesc inOut; + SecBuffer messageBuffer[3]; + SafeByteArray securityTrailer(sizes.cbSecurityTrailer); + SafeByteArray blockSize(sizes.cbBlockSize); + SafeByteArray inputMessage; + + inOut.ulVersion = SECBUFFER_VERSION; + inOut.cBuffers = 3; + inOut.pBuffers = messageBuffer; + + messageBuffer[0].BufferType = SECBUFFER_TOKEN; + messageBuffer[0].cbBuffer = sizes.cbSecurityTrailer; + messageBuffer[0].pvBuffer = vecptr(securityTrailer); + + inputMessage = createSafeByteArray (vecptr(message), message.size()); /* Make a copy as EncryptMessage encrypts the input in place, overwriting it */ + messageBuffer[1].BufferType = SECBUFFER_DATA; + messageBuffer[1].cbBuffer = inputMessage.size(); + messageBuffer[1].pvBuffer = (void *) vecptr(inputMessage); + + messageBuffer[2].BufferType = SECBUFFER_PADDING; + messageBuffer[2].cbBuffer = sizes.cbBlockSize; + messageBuffer[2].pvBuffer = vecptr(blockSize); + + SWIFT_LOG(debug) << "sizes.cbSecurityTrailer: " << sizes.cbSecurityTrailer; + SWIFT_LOG(debug) << "inputMessage.size(): " << inputMessage.size(); + SWIFT_LOG(debug) << "sizes.cbBlockSize: " << sizes.cbBlockSize; + + status = EncryptMessage( + contextHandle, + SECQOP_WRAP_NO_ENCRYPT, + &inOut, + 0); /* Don't maintain sequence numbers */ + ASSIGN_SEC_ERROR(status, errorCode); + if (status == SEC_E_OK) { + unsigned char* pointer; + + SWIFT_LOG(debug) << "messageBuffer[0].cbBuffer: " << messageBuffer[0].cbBuffer; + SWIFT_LOG(debug) << "messageBuffer[1].cbBuffer: " << messageBuffer[1].cbBuffer; + SWIFT_LOG(debug) << "messageBuffer[2].cbBuffer: " << messageBuffer[2].cbBuffer; + + output.resize(messageBuffer[0].cbBuffer + messageBuffer[1].cbBuffer + messageBuffer[2].cbBuffer); + pointer = vecptr(output); + for (size_t i = 0; i < inOut.cBuffers; i++) { + if (messageBuffer[i].cbBuffer) { + memcpy(pointer, messageBuffer[i].pvBuffer, messageBuffer[i].cbBuffer); + pointer += messageBuffer[i].cbBuffer; + } + } + } + + return errorCode; +} + +std::shared_ptr<boost::system::error_code> queryContextAttributes(const PCtxtHandle contextHandle, ULONG attribute, PVOID buffer) { + SECURITY_STATUS status; + std::shared_ptr<boost::system::error_code> errorCode; + + status = QueryContextAttributes( + contextHandle, + attribute, + buffer); + ASSIGN_SEC_ERROR(status, errorCode); + + return errorCode; +} + +} diff --git a/Swiften/SASL/WindowsAuthentication.h b/Swiften/SASL/WindowsAuthentication.h new file mode 100644 index 0000000..311c4c1 --- /dev/null +++ b/Swiften/SASL/WindowsAuthentication.h @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2015-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include <string> + +#define SECURITY_WIN32 +#include <Windows.h> +#include <Sspi.h> + +#include <Swiften/Base/API.h> +#include <boost/signals2.hpp> +#include <Swiften/Base/SafeByteArray.h> + +namespace Swift { + /** + * Retrieves the names & Windows server domain of the user associated + * with the calling thread. + * + * @param userName Will return the user name in the form "DOMAIN\user" + * @param clientName Will return the client name in the form "user" + * @param serverName Will return the server name in the form "DOMAIN" + * + * @return NULL for success, otherwise the error code returned by + * Windows. + */ + SWIFTEN_API std::shared_ptr<boost::system::error_code> getUserNameEx(std::string& userName, std::string& clientName, std::string& serverName); + + /** + * Retrieves the handle to preexisting client credentials for the + * Kerberos security package that were established through a system + * logon. + * freeCredentialsHandle() should be called if this function is + * successful and when credentials are no longer needed. + * + * @param credentialsHandle Pointer to the returned credentials handle. + * + * @return NULL for success, otherwise the error code returned by + * Windows. + */ + SWIFTEN_API std::shared_ptr<boost::system::error_code> acquireCredentialsHandle(PCredHandle credentialsHandle); + + /** + * Releases the credentials handle obtained by the + * acquireCredentialsHandle() function. + * freeCredentialsHandle() should be called when credentials are no + * longer needed. + * + * @param credentialsHandle Pointer to the credentials handle. + * + * @return NULL for success, otherwise the error code returned by + * Windows. + */ + SWIFTEN_API std::shared_ptr<boost::system::error_code> freeCredentialsHandle(PCredHandle credentialsHandle); + + /** + * Builds the security context between the client and remote peer. + * Kerberos security package that were established through a system + * logon. + * + * @param inputToken NULL or empty on the first call, otherwise the + * token returned by the server. + * @param servicePrincipalNameString Service principal name of the + * server. + * @param credentialsHandle Pointer to the credentials handle acquired + * before. + * @param haveContextHandle False on the first call to this function, + * true otherwise. + * @param contextHandle Pointer to the context handle returned on the + * first call and passed on subsequent calls. + * @param contextRequested Context related requests by the caller. See + * the Windows API InitializeSecurityContext for allowed values. + * @param contextSupported Pointer to context related attributes + * returned when context is completely established (when + * haveCompleteContext contains true). See the Windows API + * InitializeSecurityContext for allowed values. + * @param haveCompleteContext Pointer to boolean - this will only be + * true on return when the context is completely established and + * there is no need to call this function again. + * @param outputToken Returned security token to be sent to the server, + * may be empty. + * + * @return NULL for success, otherwise the error code returned by + * Windows. + */ + SWIFTEN_API std::shared_ptr<boost::system::error_code> initializeSecurityContext(const boost::optional<ByteArray>& inputToken, const std::string& servicePrincipalNameString, const PCredHandle credentialsHandle, bool haveContextHandle, PCtxtHandle contextHandle, ULONG contextRequested, ULONG* contextSupported, bool* haveCompleteContext, SafeByteArray& outputToken); + + /** + * Releases the context handle obtained by the + * initializeSecurityContext() function. + * deleteSecurityContext() should be called when the context is no + * longer needed. + * + * @param contextHandle Pointer to the context handle. + * + * @return NULL for success, otherwise the error code returned by + * Windows. + */ + SWIFTEN_API std::shared_ptr<boost::system::error_code> deleteSecurityContext(PCtxtHandle contextHandle); + + /** + * Completes an authentication token for a partial security context. + * + * @param contextHandle Pointer to the context handle. + * @param token authentication token. + * + * @return NULL for success, otherwise the error code returned by + * Windows. + */ + SWIFTEN_API std::shared_ptr<boost::system::error_code> completeAuthToken(const PCtxtHandle contextHandle, PSecBufferDesc token); + + /** + * Frees a memory buffer allocated by the security package. + * + * @param contextBuffer Pointer to buffer to be freed. + * + * @return NULL for success, otherwise the error code returned by + * Windows. + */ + SWIFTEN_API std::shared_ptr<boost::system::error_code> freeContextBuffer(PVOID contextBuffer); + + /** + * Decrypt message (assumes that sequence numbers are not maintained). + * + * @param contextHandle Pointer to the context handle. + * @param message Message to decrypt. + * @param decrypted Returned decrypted message. + * + * @return NULL for success, otherwise the error code returned by + * Windows. + */ + SWIFTEN_API std::shared_ptr<boost::system::error_code> decryptMessage(const PCtxtHandle contextHandle, const ByteArray& message, SafeByteArray& decrypted); + + /** + * Produces a header or trailer for the message but does not encrypt it + * (also assumes that sequence numbers are not maintained). + * + * @param contextHandle Pointer to the context handle. + * @param sizes SecPkgContext_Sizes obtained for the context. + * @param message Input message. + * @param output Returned output message. + * + * @return NULL for success, otherwise the error code returned by + * Windows. + */ + SWIFTEN_API std::shared_ptr<boost::system::error_code> encryptMessage(const PCtxtHandle contextHandle, const SecPkgContext_Sizes& sizes, const SafeByteArray& message, SafeByteArray& output); + + /** + * Queries the security package for attributes of the security context. + * + * @param contextHandle Pointer to the context handle. + * @param attribute Attribute to query. See the Windows API + * QueryContextAttributes for allowed values. + * @param buffer Pointer to a structure that receives the output. + * The type of structure depends on the queried attribute and + * memory for it must be allocated by caller. If the SSP allocates + * any memory required to hold some members, that memory should be + * freed using the function freeContextBuffer(). See the Windows + * API QueryContextAttributes for details. + * + * @return NULL for success, otherwise the error code returned by + * Windows. + */ + SWIFTEN_API std::shared_ptr<boost::system::error_code> queryContextAttributes(const PCtxtHandle contextHandle, ULONG attribute, PVOID buffer); + +} diff --git a/Swiften/SASL/WindowsGSSAPIClientAuthenticator.cpp b/Swiften/SASL/WindowsGSSAPIClientAuthenticator.cpp new file mode 100644 index 0000000..10e8c89 --- /dev/null +++ b/Swiften/SASL/WindowsGSSAPIClientAuthenticator.cpp @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2015-2019 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include <Swiften/SASL/WindowsGSSAPIClientAuthenticator.h> + +#include <Swiften/Base/Log.h> +#include <Swiften/SASL/WindowsAuthentication.h> +#include <Swiften/SASL/WindowsServicePrincipalName.h> + +#define SECURITY_LAYER_NONE 1 + +namespace Swift { + +WindowsGSSAPIClientAuthenticator::WindowsGSSAPIClientAuthenticator(const std::string& hostname, const std::string& domainname, int port) : ClientAuthenticator("GSSAPI"), step_(BuildingSecurityContext), error_(false), haveCredentialsHandle_(false), haveContextHandle_(false), haveCompleteContext_(false) { + WindowsServicePrincipalName servicePrincipalName(domainname); + servicePrincipalName.setInstanceName(hostname); + if ((port != -1) && (port != 5222)) { + servicePrincipalName.setInstancePort(port); + } + servicePrincipalNameString_ = servicePrincipalName.toString(); + + errorCode_ = acquireCredentialsHandle(&credentialsHandle_); + if (isError()) { + return; + } + else { + haveCredentialsHandle_ = true; + } + + buildSecurityContext(boost::optional<ByteArray>()); +} + +WindowsGSSAPIClientAuthenticator::~WindowsGSSAPIClientAuthenticator() { + if (haveContextHandle_) { + deleteSecurityContext(&contextHandle_); + } + + if (haveCredentialsHandle_) { + freeCredentialsHandle(&credentialsHandle_); + } +} + +boost::optional<SafeByteArray> WindowsGSSAPIClientAuthenticator::getResponse() const { + SWIFT_LOG(debug) << "response_.size(): " << response_.size(); + return response_; +} + +bool WindowsGSSAPIClientAuthenticator::setChallenge(const boost::optional<ByteArray>& challengeData) { + /* Following http://tools.ietf.org/html/rfc4752, https://msdn.microsoft.com/en-us/library/windows/desktop/aa380496%28v=vs.85%29.aspx */ + + if (step_ == BuildingSecurityContext) { + buildSecurityContext(challengeData); + } + else if (step_ == SecurityLayerNegotiation) { + if (!challengeData) { + SWIFT_LOG(debug) << "Empty message received from the server"; + error_ = true; + return false; + } + + SafeByteArray challenge; + errorCode_ = decryptMessage(&contextHandle_, challengeData.get(), challenge); + if (isError()) { + return false; + } + + if (challenge.size() != 4) { + SWIFT_LOG(debug) << "Token received from the server of incorrect length: " << challenge.size(); + error_ = true; + return false; + } + + unsigned char* challengePointer = vecptr(challenge); + + unsigned char serverSecurityLayer = challengePointer[0]; + if (serverSecurityLayer == 0) { + SWIFT_LOG(debug) << "Server supports unknown security layer, assuming no security layer"; + serverSecurityLayer = SECURITY_LAYER_NONE; + } + else if (serverSecurityLayer == SECURITY_LAYER_NONE) { + SWIFT_LOG(debug) << "Server supports no security layer"; + } + else { + SWIFT_LOG(debug) << "Server supports security layer"; + } + + unsigned int serverMaximumBuffer = (challengePointer[1] << 16) | + (challengePointer[2] << 8) | + (challengePointer[3] << 0); + + if ((serverSecurityLayer == SECURITY_LAYER_NONE) && (serverMaximumBuffer != 0)) { + SWIFT_LOG(debug) << "Server supports no security layer but has maximum buffer size" << serverMaximumBuffer; + error_ = true; + return false; + } + + SafeByteArray message(4); + + /* Commenting this out as streamSizes was not obtained before + if (message.size() > streamSizes_.cbMaximumMessage) { + error_ = true; + return false; + } */ + + unsigned char* messagePointer = vecptr(message); + messagePointer[0] = SECURITY_LAYER_NONE; + + /* The next 3 bytes indicate the client's maximum size buffer which is set to 0 as we do not support a security layer */ + messagePointer[1] = 0; + messagePointer[2] = 0; + messagePointer[3] = 0; + + /* The authorization identity is omitted as it is the same as the authentication identity */ + + errorCode_ = encryptMessage(&contextHandle_, sizes_, message, response_); + if (isError()) { + return false; + } + + step_ = ServerAuthenticated; + } + + if (isError()) { + return false; + } + + return true; +} + +bool WindowsGSSAPIClientAuthenticator::isError() { + if (error_) { + return true; + } + + if (!errorCode_) { + return false; + } + + return true; +} + +void WindowsGSSAPIClientAuthenticator::buildSecurityContext(const boost::optional<ByteArray>& inputToken) { + ULONG contextSupported; + + /* An XMPP server may not support Kerberos encryption or SASL security layer so not requesting integrity or confidentiality */ + errorCode_ = initializeSecurityContext(inputToken, servicePrincipalNameString_, &credentialsHandle_, haveContextHandle_, &contextHandle_, ISC_REQ_MUTUAL_AUTH, &contextSupported, &haveCompleteContext_, response_); + if (isError()) { + return; + } + + haveContextHandle_ = true; + + if (!haveCompleteContext_) { + return; + } + + if (contextSupported & ISC_REQ_MUTUAL_AUTH == 0) { + SWIFT_LOG(debug) << "Mutual authentication not supported"; + error_ = true; + return; + } + + errorCode_ = queryContextAttributes(&contextHandle_, SECPKG_ATTR_SIZES, &sizes_); + if (isError()) { + return; + } + + /* Commenting this out as it gives the error code 0x80090302: The function requested is not supported + errorCode_ = queryContextAttributes(&contextHandle_, SECPKG_ATTR_STREAM_SIZES, &streamSizes_); + if (isError()) { + return; + }*/ + + SecPkgContext_Names names; + errorCode_ = queryContextAttributes(&contextHandle_, SECPKG_ATTR_NAMES, &names); + if (isError()) { + return; + } + + userName_ = names.sUserName; + SWIFT_LOG(debug) << "User name: " << userName_; + + std::size_t position = userName_.find("\\"); + clientName_ = userName_.substr(position + 1); + SWIFT_LOG(debug) << "Client name: " << clientName_; + + serverName_ = userName_.substr(0, position); + SWIFT_LOG(debug) << "Server name: " << serverName_; + + freeContextBuffer(names.sUserName); + step_ = SecurityLayerNegotiation; +} + +} diff --git a/Swiften/SASL/WindowsGSSAPIClientAuthenticator.h b/Swiften/SASL/WindowsGSSAPIClientAuthenticator.h new file mode 100644 index 0000000..72616ed --- /dev/null +++ b/Swiften/SASL/WindowsGSSAPIClientAuthenticator.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2015-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include <string> +#include <vector> + +#define SECURITY_WIN32 +#include <Windows.h> +#include <Sspi.h> + +#include <Swiften/Base/API.h> +#include <Swiften/Base/SafeByteArray.h> +#include <Swiften/SASL/ClientAuthenticator.h> + +namespace Swift { + class SWIFTEN_API WindowsGSSAPIClientAuthenticator : public ClientAuthenticator { + public: + WindowsGSSAPIClientAuthenticator(const std::string& hostname, const std::string& domainname, int port); + + virtual ~WindowsGSSAPIClientAuthenticator(); + + virtual boost::optional<SafeByteArray> getResponse() const; + virtual bool setChallenge(const boost::optional<std::vector<unsigned char> >&); + + /** + * Returns whether the authenticator has the complete + * security context. It could be true before any + * message exchanges with the server or after some + * messages have been exchanged. + * + * @return True if security context is complete. + */ + bool isCompleteContext() { + return haveCompleteContext_; + } + + /** + * Retrieves the user name associated with the + * security context. It will only be set when + * isCompleteContext() returns true. + * + * @return User name in the form "EXAMPLE.COM\user". + */ + const std::string& getUserName() { + return userName_; + } + + /** + * Retrieves the client part of the user name + * associated with the security context. It will only + * be set when isCompleteContext() returns true. + * + * @return Client name in the form "user" when the user + * name is "EXAMPLE.COM\user". + */ + const std::string& getClientName() { + return clientName_; + } + + /** + * Retrieves the server name associated with the + * security context. It will only be set when + * isCompleteContext() returns true. + * + * @return Server name in the form "EXAMPLE.COM". + */ + const std::string& getServerName() { + return serverName_; + } + + /** + * Returns whether an error has occurred at any point, + * including in the constructor. + * + * @return True if an error has occured. + */ + bool isError(); + + /** + * Returns error details if isError() returns true. + * May be empty if there are no details to be provided + * for the error. + * + * @return Error details. + */ + std::shared_ptr<boost::system::error_code> getErrorCode() { + return errorCode_; + } + + private: + void buildSecurityContext(const boost::optional<ByteArray>& inputToken); + + private: + enum Step { + BuildingSecurityContext, + SecurityLayerNegotiation, + ServerAuthenticated + } step_; + bool error_; + std::shared_ptr<boost::system::error_code> errorCode_; + std::string servicePrincipalNameString_; + bool haveCredentialsHandle_; + bool haveContextHandle_; + bool haveCompleteContext_; + CredHandle credentialsHandle_; + CtxtHandle contextHandle_; + SecPkgContext_Sizes sizes_; + SecPkgContext_StreamSizes streamSizes_; + std::string userName_; + std::string clientName_; + std::string serverName_; + SafeByteArray response_; + }; +} diff --git a/Swiften/SASL/WindowsServicePrincipalName.cpp b/Swiften/SASL/WindowsServicePrincipalName.cpp new file mode 100644 index 0000000..6c87925 --- /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; + return str; +} + +DWORD WindowsServicePrincipalName::dsMakeSpn(DWORD* length, wchar_t* value) { + DWORD status; + +#ifdef UNICODE + SWIFT_LOG(debug) << "UNICODE is defined"; +#else + SWIFT_LOG(debug) << "UNICODE is not defined"; +#endif + + SWIFT_LOG(debug) << "serviceClass_: " << convertWStringToString(serviceClass_.c_str()); + SWIFT_LOG(debug) << "serviceName_: " << convertWStringToString(serviceName_.c_str()); + SWIFT_LOG(debug) << "instanceName_: " << convertWStringToString(instanceName_.c_str()); + SWIFT_LOG(debug) << "referrer_: " << convertWStringToString(referrer_.c_str()); + SWIFT_LOG(debug) << "instancePort_: " << instancePort_; + SWIFT_LOG(debug) << "length: " << *length; + + /* 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(); + } + + return status; +} + +} diff --git a/Swiften/SASL/WindowsServicePrincipalName.h b/Swiften/SASL/WindowsServicePrincipalName.h new file mode 100644 index 0000000..2e4e5c4 --- /dev/null +++ b/Swiften/SASL/WindowsServicePrincipalName.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2015-2018 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(unsigned short 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_; + }; +} |