summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMili Verma <mili.verma@isode.com>2015-07-02 10:22:55 (GMT)
committerKevin Smith <kevin.smith@isode.com>2015-07-06 08:03:43 (GMT)
commit54dc62706f601bf2a19f9ecd752b531aa3bbf418 (patch)
treed1e2d97ea66490d2db04739c339fdb4f173a12ad
parent87137e983ed986df774a3373168a7611dff583c1 (diff)
downloadswift-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/SConscript1
-rw-r--r--Swiften/SASL/WindowsGSSAPIClientAuthenticator.cpp194
-rw-r--r--Swiften/SASL/WindowsGSSAPIClientAuthenticator.h119
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" :
15 objects += myenv.SwiftenObject([ 15 objects += myenv.SwiftenObject([
16 "WindowsServicePrincipalName.cpp", 16 "WindowsServicePrincipalName.cpp",
17 "WindowsAuthentication.cpp", 17 "WindowsAuthentication.cpp",
18 "WindowsGSSAPIClientAuthenticator.cpp"
18 ]) 19 ])
19 20
20swiften_env.Append(SWIFTEN_OBJECTS = [objects]) 21swiften_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 @@
1/*
2 * Copyright (c) 2015 Isode Limited.
3 * All rights reserved.
4 * See the COPYING file for more information.
5 */
6
7#include <Swiften/SASL/WindowsGSSAPIClientAuthenticator.h>
8
9#include <Swiften/Base/Log.h>
10#include <Swiften/SASL/WindowsAuthentication.h>
11#include <Swiften/SASL/WindowsServicePrincipalName.h>
12
13#define SECURITY_LAYER_NONE 1
14
15namespace Swift {
16
17WindowsGSSAPIClientAuthenticator::WindowsGSSAPIClientAuthenticator(const std::string& hostname, const std::string& domainname) : ClientAuthenticator("GSSAPI"), step_(BuildingSecurityContext), error_(false), haveCredentialsHandle_(false), haveContextHandle_(false), haveCompleteContext_(false) {
18 WindowsServicePrincipalName servicePrincipalName(domainname);
19 servicePrincipalName.setInstanceName(hostname);
20 servicePrincipalNameString_ = servicePrincipalName.toString();
21
22 errorCode_ = acquireCredentialsHandle(&credentialsHandle_);
23 if (isError()) {
24 return;
25 }
26 else {
27 haveCredentialsHandle_ = true;
28 }
29
30 buildSecurityContext(NULL);
31}
32
33WindowsGSSAPIClientAuthenticator::~WindowsGSSAPIClientAuthenticator() {
34 if (haveContextHandle_) {
35 deleteSecurityContext(&contextHandle_);
36 }
37
38 if (haveCredentialsHandle_) {
39 freeCredentialsHandle(&credentialsHandle_);
40 }
41}
42
43boost::optional<SafeByteArray> WindowsGSSAPIClientAuthenticator::getResponse() const {
44 SWIFT_LOG(debug) << "response_.size(): " << response_.size() << std::endl;
45 return response_;
46}
47
48bool WindowsGSSAPIClientAuthenticator::setChallenge(const boost::optional<ByteArray>& challengeData) {
49 /* Following http://tools.ietf.org/html/rfc4752, https://msdn.microsoft.com/en-us/library/windows/desktop/aa380496%28v=vs.85%29.aspx */
50
51 if (step_ == BuildingSecurityContext) {
52 buildSecurityContext(challengeData);
53 }
54 else if (step_ == SecurityLayerNegotiation) {
55 if (!challengeData) {
56 SWIFT_LOG(debug) << "Empty message received from the server" << std::endl;
57 error_ = true;
58 return false;
59 }
60
61 SafeByteArray challenge;
62 errorCode_ = decryptMessage(&contextHandle_, challengeData.get(), challenge);
63 if (isError()) {
64 return false;
65 }
66
67 if (challenge.size() != 4) {
68 SWIFT_LOG(debug) << "Token received from the server of incorrect length: " << challenge.size() << std::endl;
69 error_ = true;
70 return false;
71 }
72
73 unsigned char* challengePointer = vecptr(challenge);
74
75 unsigned char serverSecurityLayer = challengePointer[0];
76 if (serverSecurityLayer == 0) {
77 SWIFT_LOG(debug) << "Server supports unknown security layer, assuming no security layer" << std::endl;
78 serverSecurityLayer = SECURITY_LAYER_NONE;
79 }
80 else if (serverSecurityLayer == SECURITY_LAYER_NONE) {
81 SWIFT_LOG(debug) << "Server supports no security layer" << std::endl;
82 }
83 else {
84 SWIFT_LOG(debug) << "Server supports security layer" << std::endl;
85 }
86
87 unsigned int serverMaximumBuffer = (challengePointer[1] << 16) |
88 (challengePointer[2] << 8) |
89 (challengePointer[3] << 0);
90
91 if ((serverSecurityLayer == SECURITY_LAYER_NONE) && (serverMaximumBuffer != 0)) {
92 SWIFT_LOG(debug) << "Server supports no security layer but has maximum buffer size" << serverMaximumBuffer << std::endl;
93 error_ = true;
94 return false;
95 }
96
97 SafeByteArray message(4);
98
99 /* Commenting this out as streamSizes was not obtained before
100 if (message.size() > streamSizes_.cbMaximumMessage) {
101 error_ = true;
102 return false;
103 } */
104
105 unsigned char* messagePointer = vecptr(message);
106 messagePointer[0] = SECURITY_LAYER_NONE;
107
108 /* The next 3 bytes indicate the client's maximum size buffer which is set to 0 as we do not support a security layer */
109 messagePointer[1] = 0;
110 messagePointer[2] = 0;
111 messagePointer[3] = 0;
112
113 /* The authorization identity is omitted as it is the same as the authentication identity */
114
115 errorCode_ = encryptMessage(&contextHandle_, sizes_, message, response_);
116 if (isError()) {
117 return false;
118 }
119
120 step_ = ServerAuthenticated;
121 }
122
123 if (isError()) {
124 return false;
125 }
126
127 return true;
128}
129
130bool WindowsGSSAPIClientAuthenticator::isError() {
131 if (error_) {
132 return true;
133 }
134
135 if (!errorCode_) {
136 return false;
137 }
138
139 return true;
140}
141
142void WindowsGSSAPIClientAuthenticator::buildSecurityContext(const boost::optional<ByteArray>& inputToken) {
143 ULONG contextSupported;
144
145 /* An XMPP server may not support Kerberos encryption or SASL security layer so not requesting integrity or confidentiality */
146 errorCode_ = initializeSecurityContext(inputToken, servicePrincipalNameString_, &credentialsHandle_, haveContextHandle_, &contextHandle_, ISC_REQ_MUTUAL_AUTH, &contextSupported, &haveCompleteContext_, response_);
147 if (isError()) {
148 return;
149 }
150
151 haveContextHandle_ = true;
152
153 if (!haveCompleteContext_) {
154 return;
155 }
156
157 if (contextSupported & ISC_REQ_MUTUAL_AUTH == 0) {
158 SWIFT_LOG(debug) << "Mutual authentication not supported" << std::endl;
159 error_ = true;
160 return;
161 }
162
163 errorCode_ = queryContextAttributes(&contextHandle_, SECPKG_ATTR_SIZES, &sizes_);
164 if (isError()) {
165 return;
166 }
167
168 /* Commenting this out as it gives the error code 0x80090302: The function requested is not supported
169 errorCode_ = queryContextAttributes(&contextHandle_, SECPKG_ATTR_STREAM_SIZES, &streamSizes_);
170 if (isError()) {
171 return;
172 }*/
173
174 SecPkgContext_Names names;
175 errorCode_ = queryContextAttributes(&contextHandle_, SECPKG_ATTR_NAMES, &names);
176 if (isError()) {
177 return;
178 }
179
180 userName_ = names.sUserName;
181 SWIFT_LOG(debug) << "User name: " << userName_ << std::endl;
182
183 std::size_t position = userName_.find("\\");
184 clientName_ = userName_.substr(position + 1);
185 SWIFT_LOG(debug) << "Client name: " << clientName_ << std::endl;
186
187 serverName_ = userName_.substr(0, position);
188 SWIFT_LOG(debug) << "Server name: " << serverName_ << std::endl;
189
190 freeContextBuffer(names.sUserName);
191 step_ = SecurityLayerNegotiation;
192}
193
194}
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 @@
1/*
2 * Copyright (c) 2015 Isode Limited.
3 * All rights reserved.
4 * See the COPYING file for more information.
5 */
6
7#pragma once
8
9#include <string>
10#include <vector>
11
12#define SECURITY_WIN32
13#include <Windows.h>
14#include <Sspi.h>
15
16#include <Swiften/Base/API.h>
17#include <Swiften/Base/SafeByteArray.h>
18#include <Swiften/SASL/ClientAuthenticator.h>
19
20namespace Swift {
21 class SWIFTEN_API WindowsGSSAPIClientAuthenticator : public ClientAuthenticator {
22 public:
23 WindowsGSSAPIClientAuthenticator(const std::string& hostname, const std::string& domainname);
24
25 ~WindowsGSSAPIClientAuthenticator();
26
27 virtual boost::optional<SafeByteArray> getResponse() const;
28 virtual bool setChallenge(const boost::optional<std::vector<unsigned char> >&);
29
30 /**
31 * Returns whether the authenticator has the complete
32 * security context. It could be true before any
33 * message exchanges with the server or after some
34 * messages have been exchanged.
35 *
36 * @return True if security context is complete.
37 */
38 bool isCompleteContext() {
39 return haveCompleteContext_;
40 }
41
42 /**
43 * Retrieves the user name associated with the
44 * security context. It will only be set when
45 * isCompleteContext() returns true.
46 *
47 * @return User name in the form "EXAMPLE.COM\user".
48 */
49 const std::string& getUserName() {
50 return userName_;
51 }
52
53 /**
54 * Retrieves the client part of the user name
55 * associated with the security context. It will only
56 * be set when isCompleteContext() returns true.
57 *
58 * @return Client name in the form "user" when the user
59 * name is "EXAMPLE.COM\user".
60 */
61 const std::string& getClientName() {
62 return clientName_;
63 }
64
65 /**
66 * Retrieves the server name associated with the
67 * security context. It will only be set when
68 * isCompleteContext() returns true.
69 *
70 * @return Server name in the form "EXAMPLE.COM".
71 */
72 const std::string& getServerName() {
73 return serverName_;
74 }
75
76 /**
77 * Returns whether an error has occurred at any point,
78 * including in the constructor.
79 *
80 * @return True if an error has occured.
81 */
82 bool isError();
83
84 /**
85 * Returns error details if isError() returns true.
86 * May be empty if there are no details to be provided
87 * for the error.
88 *
89 * @return Error details.
90 */
91 boost::shared_ptr<boost::system::error_code> getErrorCode() {
92 return errorCode_;
93 }
94
95 private:
96 void buildSecurityContext(const boost::optional<ByteArray>& inputToken);
97
98 private:
99 enum Step {
100 BuildingSecurityContext,
101 SecurityLayerNegotiation,
102 ServerAuthenticated
103 } step_;
104 bool error_;
105 boost::shared_ptr<boost::system::error_code> errorCode_;
106 std::string servicePrincipalNameString_;
107 bool haveCredentialsHandle_;
108 bool haveContextHandle_;
109 bool haveCompleteContext_;
110 CredHandle credentialsHandle_;
111 CtxtHandle contextHandle_;
112 SecPkgContext_Sizes sizes_;
113 SecPkgContext_StreamSizes streamSizes_;
114 std::string userName_;
115 std::string clientName_;
116 std::string serverName_;
117 SafeByteArray response_;
118 };
119}