diff options
| author | Mili Verma <mili.verma@isode.com> | 2015-07-02 10:22:55 (GMT) | 
|---|---|---|
| committer | Kevin Smith <kevin.smith@isode.com> | 2015-07-06 08:03:43 (GMT) | 
| commit | 54dc62706f601bf2a19f9ecd752b531aa3bbf418 (patch) | |
| tree | d1e2d97ea66490d2db04739c339fdb4f173a12ad | |
| parent | 87137e983ed986df774a3373168a7611dff583c1 (diff) | |
| download | swift-54dc62706f601bf2a19f9ecd752b531aa3bbf418.zip swift-54dc62706f601bf2a19f9ecd752b531aa3bbf418.tar.bz2 | |
Add GSSAPI client authenticator
Test-information:
Tested on Windows using WIP code.
Unit tests pass.
Change-Id: I766294e57dc6374830b865f3e57b07b67e7d2fe2
| -rw-r--r-- | Swiften/SASL/SConscript | 1 | ||||
| -rw-r--r-- | Swiften/SASL/WindowsGSSAPIClientAuthenticator.cpp | 194 | ||||
| -rw-r--r-- | Swiften/SASL/WindowsGSSAPIClientAuthenticator.h | 119 | 
3 files changed, 314 insertions, 0 deletions
| diff --git a/Swiften/SASL/SConscript b/Swiften/SASL/SConscript index de50911..7e1fd07 100644 --- a/Swiften/SASL/SConscript +++ b/Swiften/SASL/SConscript @@ -15,6 +15,7 @@ if myenv["PLATFORM"] == "win32" :  	objects += myenv.SwiftenObject([  		"WindowsServicePrincipalName.cpp",  		"WindowsAuthentication.cpp", +		"WindowsGSSAPIClientAuthenticator.cpp"  	])  swiften_env.Append(SWIFTEN_OBJECTS = [objects]) diff --git a/Swiften/SASL/WindowsGSSAPIClientAuthenticator.cpp b/Swiften/SASL/WindowsGSSAPIClientAuthenticator.cpp new file mode 100644 index 0000000..7423243 --- /dev/null +++ b/Swiften/SASL/WindowsGSSAPIClientAuthenticator.cpp @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2015 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) : ClientAuthenticator("GSSAPI"), step_(BuildingSecurityContext), error_(false), haveCredentialsHandle_(false), haveContextHandle_(false), haveCompleteContext_(false) { +	WindowsServicePrincipalName servicePrincipalName(domainname); +	servicePrincipalName.setInstanceName(hostname); +	servicePrincipalNameString_ = servicePrincipalName.toString(); + +	errorCode_ = acquireCredentialsHandle(&credentialsHandle_); +	if (isError()) { +		return; +	} +	else { +		haveCredentialsHandle_ = true; +	} + +	buildSecurityContext(NULL); +} + +WindowsGSSAPIClientAuthenticator::~WindowsGSSAPIClientAuthenticator() { +	if (haveContextHandle_) { +		deleteSecurityContext(&contextHandle_); +	} + +	if (haveCredentialsHandle_) { +		freeCredentialsHandle(&credentialsHandle_); +	} +} + +boost::optional<SafeByteArray> WindowsGSSAPIClientAuthenticator::getResponse() const { +	SWIFT_LOG(debug) << "response_.size(): " << response_.size() << std::endl; +	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" << std::endl; +			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() << std::endl; +			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" << std::endl; +			serverSecurityLayer = SECURITY_LAYER_NONE; +		} +		else if (serverSecurityLayer == SECURITY_LAYER_NONE) { +			SWIFT_LOG(debug) << "Server supports no security layer" << std::endl; +		} +		else { +			SWIFT_LOG(debug) << "Server supports security layer" << std::endl; +		} + +		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 << std::endl; +			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" << std::endl; +		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::endl; + +	std::size_t position = userName_.find("\\"); +	clientName_ = userName_.substr(position + 1); +	SWIFT_LOG(debug) << "Client name: " << clientName_ << std::endl; + +	serverName_ = userName_.substr(0, position); +	SWIFT_LOG(debug) << "Server name: " << serverName_ << std::endl; + +	freeContextBuffer(names.sUserName); +	step_ = SecurityLayerNegotiation; +} + +} diff --git a/Swiften/SASL/WindowsGSSAPIClientAuthenticator.h b/Swiften/SASL/WindowsGSSAPIClientAuthenticator.h new file mode 100644 index 0000000..d046999 --- /dev/null +++ b/Swiften/SASL/WindowsGSSAPIClientAuthenticator.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2015 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); + +			~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. +			  */ +			boost::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_; +			boost::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_; +	}; +} | 
 Swift
 Swift