summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMili Verma <mili.verma@isode.com>2015-06-26 14:32:04 (GMT)
committerMili Verma <mili.verma@isode.com>2015-06-30 09:47:58 (GMT)
commit208c75672fa106fe6e3a44bcb41dc9f2808b1b71 (patch)
tree973c70b4711bc1e27c3877c89f9e15d761a1210e
parenta3564b7aca44f5ccab3881e6f723dfb64bf66884 (diff)
downloadswift-208c75672fa106fe6e3a44bcb41dc9f2808b1b71.zip
swift-208c75672fa106fe6e3a44bcb41dc9f2808b1b71.tar.bz2
Add wrappers for Windows API to be used in GSSAPI
Test-information: Tested on Windows using WIP GSSAPI code. Unit tests pass. Change-Id: I21f8f637480a21a014ec172431dd8d4a01a11620
-rw-r--r--Swiften/SASL/SConscript1
-rw-r--r--Swiften/SASL/WindowsAuthentication.cpp327
-rw-r--r--Swiften/SASL/WindowsAuthentication.h170
3 files changed, 498 insertions, 0 deletions
diff --git a/Swiften/SASL/SConscript b/Swiften/SASL/SConscript
index faf6320..de50911 100644
--- a/Swiften/SASL/SConscript
+++ b/Swiften/SASL/SConscript
@@ -14,6 +14,7 @@ objects = myenv.SwiftenObject([
14if myenv["PLATFORM"] == "win32" : 14if myenv["PLATFORM"] == "win32" :
15 objects += myenv.SwiftenObject([ 15 objects += myenv.SwiftenObject([
16 "WindowsServicePrincipalName.cpp", 16 "WindowsServicePrincipalName.cpp",
17 "WindowsAuthentication.cpp",
17 ]) 18 ])
18 19
19swiften_env.Append(SWIFTEN_OBJECTS = [objects]) 20swiften_env.Append(SWIFTEN_OBJECTS = [objects])
diff --git a/Swiften/SASL/WindowsAuthentication.cpp b/Swiften/SASL/WindowsAuthentication.cpp
new file mode 100644
index 0000000..0244fe1
--- /dev/null
+++ b/Swiften/SASL/WindowsAuthentication.cpp
@@ -0,0 +1,327 @@
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/WindowsAuthentication.h>
8
9#include <iomanip>
10
11#include <Secext.h>
12
13#include <Swiften/Base/Log.h>
14#include <Swiften/Base/String.h>
15
16#define ASSIGN_ERROR(status, errorCode) \
17{ \
18 errorCode = boost::make_shared<boost::system::error_code>(status, boost::system::system_category()); \
19 SWIFT_LOG(debug) << std::hex << "status: 0x" << status << ": " << errorCode->message() << std::endl; \
20}
21
22#define ASSIGN_SEC_ERROR(status, errorCode) \
23{ \
24 if (status == SEC_E_OK) \
25 { \
26 SWIFT_LOG(debug) << "success" << std::endl; \
27 } \
28 else { \
29 ASSIGN_ERROR(status, errorCode); \
30 } \
31}
32
33namespace Swift {
34
35boost::shared_ptr<boost::system::error_code> getUserNameEx(std::string& userName, std::string& clientName, std::string& serverName) {
36 ULONG length = 512;
37 DWORD status = ERROR_MORE_DATA;
38 bool firstCall = true;
39 boost::shared_ptr<boost::system::error_code> errorCode;
40
41 while (status == ERROR_MORE_DATA) {
42 std::vector<wchar_t> value(length);
43
44 /* length after this call will contain the required length if current length is not enough - so the next call should succeed */
45 if (GetUserNameExW(NameSamCompatible, vecptr(value), &length)) {
46 std::size_t position;
47
48 userName = convertWStringToString(std::wstring(vecptr(value), length));
49 SWIFT_LOG(debug) << "User Name: " << userName << std::endl;
50
51 position = userName.find("\\");
52 clientName = userName.substr(position + 1);
53 SWIFT_LOG(debug) << "Client name: " << clientName << std::endl;
54
55 serverName = userName.substr(0, position);
56 SWIFT_LOG(debug) << "Server name: " << serverName << std::endl;
57
58 break;
59 }
60
61 status = GetLastError();
62 if ((firstCall == false) || (status != ERROR_MORE_DATA)) {
63 ASSIGN_ERROR(status, errorCode);
64 break;
65 }
66
67 firstCall = false;
68 }
69
70 return errorCode;
71}
72
73boost::shared_ptr<boost::system::error_code> acquireCredentialsHandle(PCredHandle credentialsHandle) {
74 SECURITY_STATUS status;
75 boost::shared_ptr<boost::system::error_code> errorCode;
76 TimeStamp validity;
77
78 status = AcquireCredentialsHandle(
79 NULL, /* NULL indicates credentials of the user under whose security context it is executing */
80 "Kerberos",
81 SECPKG_CRED_OUTBOUND, /* client credential */
82 NULL,
83 NULL, /* use default credentials */
84 NULL, /* not used */
85 NULL, /* not used */
86 credentialsHandle,
87 &validity);
88 ASSIGN_SEC_ERROR(status, errorCode);
89
90 return errorCode;
91}
92
93boost::shared_ptr<boost::system::error_code> freeCredentialsHandle(PCredHandle credentialsHandle) {
94 SECURITY_STATUS status;
95 boost::shared_ptr<boost::system::error_code> errorCode;
96
97 status = FreeCredentialsHandle(credentialsHandle);
98 ASSIGN_SEC_ERROR(status, errorCode);
99
100 return errorCode;
101}
102
103boost::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) {
104 SECURITY_STATUS status;
105 boost::shared_ptr<boost::system::error_code> errorCode;
106 SecBufferDesc input;
107 SecBufferDesc output;
108 SecBuffer inputTokenBuffer;
109 SecBuffer outputTokenBuffer;
110 TimeStamp validity;
111
112 *haveCompleteContext = false;
113
114 input.ulVersion = 0;
115 input.cBuffers = 1;
116 input.pBuffers = &inputTokenBuffer;
117
118 inputTokenBuffer.BufferType = SECBUFFER_TOKEN;
119 inputTokenBuffer.cbBuffer = 0;
120 inputTokenBuffer.pvBuffer = NULL;
121 if (inputToken && inputToken->size()) {
122 inputTokenBuffer.cbBuffer = inputToken->size();
123 inputTokenBuffer.pvBuffer = (void *) vecptr(*inputToken);
124 }
125
126 output.ulVersion = 0;
127 output.cBuffers = 1;
128 output.pBuffers = &outputTokenBuffer;
129
130 outputTokenBuffer.BufferType = SECBUFFER_TOKEN;
131 outputTokenBuffer.cbBuffer = 0;
132 outputTokenBuffer.pvBuffer = NULL;
133
134 status = InitializeSecurityContext(
135 credentialsHandle, /* previously acquired handle */
136 haveContextHandle ? contextHandle : NULL, /* use partial context on subsequent calls */
137 const_cast<char *>(servicePrincipalNameString.c_str()),
138 contextRequested | ISC_REQ_ALLOCATE_MEMORY,
139 0, /* not used */
140 SECURITY_NETWORK_DREP,
141 haveContextHandle ? &input : NULL,
142 0, /* not used */
143 contextHandle,
144 &output,
145 contextSupported,
146 &validity);
147 ASSIGN_SEC_ERROR(status, errorCode); /* errorCode set here will only be returned to caller if there was a non-success status */
148 if ((status == SEC_I_COMPLETE_AND_CONTINUE) || (status == SEC_I_COMPLETE_NEEDED)) {
149 /* 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. */
150 errorCode = completeAuthToken(contextHandle, &output);
151 if (!errorCode) {
152 /* success, move on */
153 }
154 else {
155 freeContextBuffer(outputTokenBuffer.pvBuffer);
156 return errorCode;
157 }
158 }
159 if ((status == SEC_E_OK) || (status == SEC_I_COMPLETE_NEEDED)) {
160 *haveCompleteContext = true;
161 }
162 if ((status == SEC_E_OK) || (status == SEC_I_COMPLETE_AND_CONTINUE) || (status == SEC_I_COMPLETE_NEEDED) || (status == SEC_I_CONTINUE_NEEDED)) {
163 outputToken = createSafeByteArray (static_cast<unsigned char *>(outputTokenBuffer.pvBuffer), outputTokenBuffer.cbBuffer);
164 SWIFT_LOG(debug) << "outputToken.size(): " << outputToken.size() << std::endl;
165 freeContextBuffer(outputTokenBuffer.pvBuffer);
166
167 return boost::shared_ptr<boost::system::error_code>(); /* success */
168 }
169
170 return errorCode;
171}
172
173boost::shared_ptr<boost::system::error_code> deleteSecurityContext(PCtxtHandle contextHandle) {
174 SECURITY_STATUS status;
175 boost::shared_ptr<boost::system::error_code> errorCode;
176
177 status = DeleteSecurityContext(contextHandle);
178 ASSIGN_SEC_ERROR(status, errorCode);
179
180 return errorCode;
181}
182
183boost::shared_ptr<boost::system::error_code> completeAuthToken(const PCtxtHandle contextHandle, PSecBufferDesc token) {
184 SECURITY_STATUS status;
185 boost::shared_ptr<boost::system::error_code> errorCode;
186
187 status = CompleteAuthToken(
188 contextHandle, /* partial context */
189 token);
190 ASSIGN_SEC_ERROR(status, errorCode);
191
192 return errorCode;
193}
194
195boost::shared_ptr<boost::system::error_code> freeContextBuffer(PVOID contextBuffer) {
196 SECURITY_STATUS status;
197 boost::shared_ptr<boost::system::error_code> errorCode;
198
199 if (contextBuffer == NULL) {
200 return errorCode;
201 }
202
203 status = FreeContextBuffer(contextBuffer);
204 ASSIGN_SEC_ERROR(status, errorCode);
205
206 return errorCode;
207}
208
209boost::shared_ptr<boost::system::error_code> decryptMessage(const PCtxtHandle contextHandle, const ByteArray& message, SafeByteArray& decrypted) {
210 /* Following https://msdn.microsoft.com/en-us/library/windows/desktop/aa380496%28v=vs.85%29.aspx */
211
212 SECURITY_STATUS status;
213 boost::shared_ptr<boost::system::error_code> errorCode;
214 SecBufferDesc inOut;
215 SecBuffer messageBuffer[2];
216 SafeByteArray inputMessage;
217 ULONG qualityOfProtection;
218
219 inOut.ulVersion = SECBUFFER_VERSION;
220 inOut.cBuffers = 2;
221 inOut.pBuffers = messageBuffer;
222
223 inputMessage = createSafeByteArray (message); /* Make a copy as DecryptMessage decrypts the input in place, overwriting it */
224 messageBuffer[0].BufferType = SECBUFFER_STREAM;
225 messageBuffer[0].cbBuffer = inputMessage.size();
226 messageBuffer[0].pvBuffer = static_cast<void *>(vecptr(inputMessage));
227
228 messageBuffer[1].BufferType = SECBUFFER_DATA;
229 messageBuffer[1].cbBuffer = 0;
230 messageBuffer[1].pvBuffer = NULL;
231
232 SWIFT_LOG(debug) << "inputMessage.size(): " << inputMessage.size() << std::endl;
233
234 status = DecryptMessage(
235 contextHandle,
236 &inOut,
237 0, /* Don't maintain sequence numbers */
238 &qualityOfProtection);
239 ASSIGN_SEC_ERROR(status, errorCode);
240 if (status == SEC_E_OK) {
241 if (qualityOfProtection == SECQOP_WRAP_NO_ENCRYPT) {
242 SWIFT_LOG(debug) << "Message was signed only" << std::endl;
243 }
244 else {
245 SWIFT_LOG(debug) << "Message was encrypted" << std::endl;
246 }
247
248 SWIFT_LOG(debug) << "messageBuffer[1].cbBuffer: " << messageBuffer[1].cbBuffer << std::endl;
249
250 decrypted = createSafeByteArray (static_cast<unsigned char *>(messageBuffer[1].pvBuffer), messageBuffer[1].cbBuffer);
251 }
252
253 return errorCode;
254}
255
256boost::shared_ptr<boost::system::error_code> encryptMessage(const PCtxtHandle contextHandle, const SecPkgContext_Sizes& sizes, const SafeByteArray& message, SafeByteArray& output) {
257 /* Following https://msdn.microsoft.com/en-us/library/windows/desktop/aa380496%28v=vs.85%29.aspx */
258
259 SECURITY_STATUS status;
260 boost::shared_ptr<boost::system::error_code> errorCode;
261 SecBufferDesc inOut;
262 SecBuffer messageBuffer[3];
263 SafeByteArray securityTrailer(sizes.cbSecurityTrailer);
264 SafeByteArray blockSize(sizes.cbBlockSize);
265 SafeByteArray inputMessage;
266
267 inOut.ulVersion = SECBUFFER_VERSION;
268 inOut.cBuffers = 3;
269 inOut.pBuffers = messageBuffer;
270
271 messageBuffer[0].BufferType = SECBUFFER_TOKEN;
272 messageBuffer[0].cbBuffer = sizes.cbSecurityTrailer;
273 messageBuffer[0].pvBuffer = vecptr(securityTrailer);
274
275 inputMessage = createSafeByteArray (vecptr(message), message.size()); /* Make a copy as EncryptMessage encrypts the input in place, overwriting it */
276 messageBuffer[1].BufferType = SECBUFFER_DATA;
277 messageBuffer[1].cbBuffer = inputMessage.size();
278 messageBuffer[1].pvBuffer = (void *) vecptr(inputMessage);
279
280 messageBuffer[2].BufferType = SECBUFFER_PADDING;
281 messageBuffer[2].cbBuffer = sizes.cbBlockSize;
282 messageBuffer[2].pvBuffer = vecptr(blockSize);
283
284 SWIFT_LOG(debug) << "sizes.cbSecurityTrailer: " << sizes.cbSecurityTrailer << std::endl;
285 SWIFT_LOG(debug) << "inputMessage.size(): " << inputMessage.size() << std::endl;
286 SWIFT_LOG(debug) << "sizes.cbBlockSize: " << sizes.cbBlockSize << std::endl;
287
288 status = EncryptMessage(
289 contextHandle,
290 SECQOP_WRAP_NO_ENCRYPT,
291 &inOut,
292 0); /* Don't maintain sequence numbers */
293 ASSIGN_SEC_ERROR(status, errorCode);
294 if (status == SEC_E_OK) {
295 unsigned char* pointer;
296
297 SWIFT_LOG(debug) << "messageBuffer[0].cbBuffer: " << messageBuffer[0].cbBuffer << std::endl;
298 SWIFT_LOG(debug) << "messageBuffer[1].cbBuffer: " << messageBuffer[1].cbBuffer << std::endl;
299 SWIFT_LOG(debug) << "messageBuffer[2].cbBuffer: " << messageBuffer[2].cbBuffer << std::endl;
300
301 output.resize(messageBuffer[0].cbBuffer + messageBuffer[1].cbBuffer + messageBuffer[2].cbBuffer);
302 pointer = vecptr(output);
303 for (size_t i = 0; i < inOut.cBuffers; i++) {
304 if (messageBuffer[i].cbBuffer) {
305 memcpy(pointer, messageBuffer[i].pvBuffer, messageBuffer[i].cbBuffer);
306 pointer += messageBuffer[i].cbBuffer;
307 }
308 }
309 }
310
311 return errorCode;
312}
313
314boost::shared_ptr<boost::system::error_code> queryContextAttributes(const PCtxtHandle contextHandle, ULONG attribute, PVOID buffer) {
315 SECURITY_STATUS status;
316 boost::shared_ptr<boost::system::error_code> errorCode;
317
318 status = QueryContextAttributes(
319 contextHandle,
320 attribute,
321 buffer);
322 ASSIGN_SEC_ERROR(status, errorCode);
323
324 return errorCode;
325}
326
327}
diff --git a/Swiften/SASL/WindowsAuthentication.h b/Swiften/SASL/WindowsAuthentication.h
new file mode 100644
index 0000000..82e428c
--- /dev/null
+++ b/Swiften/SASL/WindowsAuthentication.h
@@ -0,0 +1,170 @@
1/*
2 * Copyright (c) 2015 Isode Limited.
3 * All rights reserved.
4 * See the COPYING file for more information.
5 */
6
7#pragma once
8
9#include <string>
10
11#define SECURITY_WIN32
12#include <Windows.h>
13#include <Sspi.h>
14
15#include <Swiften/Base/API.h>
16#include <Swiften/Base/boost_bsignals.h>
17#include <Swiften/Base/SafeByteArray.h>
18
19namespace Swift {
20 /**
21 * Retrieves the names & Windows server domain of the user associated
22 * with the calling thread.
23 *
24 * @param userName Will return the user name in the form "DOMAIN\user"
25 * @param clientName Will return the client name in the form "user"
26 * @param serverName Will return the server name in the form "DOMAIN"
27 *
28 * @return NULL for success, otherwise the error code returned by
29 * Windows.
30 */
31 SWIFTEN_API boost::shared_ptr<boost::system::error_code> getUserNameEx(std::string& userName, std::string& clientName, std::string& serverName);
32
33 /**
34 * Retrieves the handle to preexisting client credentials for the
35 * Kerberos security package that were established through a system
36 * logon.
37 * freeCredentialsHandle() should be called if this function is
38 * successful and when credentials are no longer needed.
39 *
40 * @param credentialsHandle Pointer to the returned credentials handle.
41 *
42 * @return NULL for success, otherwise the error code returned by
43 * Windows.
44 */
45 SWIFTEN_API boost::shared_ptr<boost::system::error_code> acquireCredentialsHandle(PCredHandle credentialsHandle);
46
47 /**
48 * Releases the credentials handle obtained by the
49 * acquireCredentialsHandle() function.
50 * freeCredentialsHandle() should be called when credentials are no
51 * longer needed.
52 *
53 * @param credentialsHandle Pointer to the credentials handle.
54 *
55 * @return NULL for success, otherwise the error code returned by
56 * Windows.
57 */
58 SWIFTEN_API boost::shared_ptr<boost::system::error_code> freeCredentialsHandle(PCredHandle credentialsHandle);
59
60 /**
61 * Builds the security context between the client and remote peer.
62 * Kerberos security package that were established through a system
63 * logon.
64 *
65 * @param inputToken NULL or empty on the first call, otherwise the
66 * token returned by the server.
67 * @param servicePrincipalNameString Service principal name of the
68 * server.
69 * @param credentialsHandle Pointer to the credentials handle acquired
70 * before.
71 * @param haveContextHandle False on the first call to this function,
72 * true otherwise.
73 * @param contextHandle Pointer to the context handle returned on the
74 * first call and passed on subsequent calls.
75 * @param contextRequested Context related requests by the caller. See
76 * the Windows API InitializeSecurityContext for allowed values.
77 * @param contextSupported Pointer to context related attributes
78 * returned when context is completely established (when
79 * haveCompleteContext contains true). See the Windows API
80 * InitializeSecurityContext for allowed values.
81 * @param haveCompleteContext Pointer to boolean - this will only be
82 * true on return when the context is completely established and
83 * there is no need to call this function again.
84 * @param outputToken Returned security token to be sent to the server,
85 * may be empty.
86 *
87 * @return NULL for success, otherwise the error code returned by
88 * Windows.
89 */
90 SWIFTEN_API boost::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);
91
92 /**
93 * Releases the context handle obtained by the
94 * initializeSecurityContext() function.
95 * deleteSecurityContext() should be called when the context is no
96 * longer needed.
97 *
98 * @param contextHandle Pointer to the context handle.
99 *
100 * @return NULL for success, otherwise the error code returned by
101 * Windows.
102 */
103 SWIFTEN_API boost::shared_ptr<boost::system::error_code> deleteSecurityContext(PCtxtHandle contextHandle);
104
105 /**
106 * Completes an authentication token for a partial security context.
107 *
108 * @param contextHandle Pointer to the context handle.
109 * @param token authentication token.
110 *
111 * @return NULL for success, otherwise the error code returned by
112 * Windows.
113 */
114 SWIFTEN_API boost::shared_ptr<boost::system::error_code> completeAuthToken(const PCtxtHandle contextHandle, PSecBufferDesc token);
115
116 /**
117 * Frees a memory buffer allocated by the security package.
118 *
119 * @param contextBuffer Pointer to buffer to be freed.
120 *
121 * @return NULL for success, otherwise the error code returned by
122 * Windows.
123 */
124 SWIFTEN_API boost::shared_ptr<boost::system::error_code> freeContextBuffer(PVOID contextBuffer);
125
126 /**
127 * Decrypt message (assumes that sequence numbers are not maintained).
128 *
129 * @param contextHandle Pointer to the context handle.
130 * @param message Message to decrypt.
131 * @param decrypted Returned decrypted message.
132 *
133 * @return NULL for success, otherwise the error code returned by
134 * Windows.
135 */
136 SWIFTEN_API boost::shared_ptr<boost::system::error_code> decryptMessage(const PCtxtHandle contextHandle, const ByteArray& message, SafeByteArray& decrypted);
137
138 /**
139 * Produces a header or trailer for the message but does not encrypt it
140 * (also assumes that sequence numbers are not maintained).
141 *
142 * @param contextHandle Pointer to the context handle.
143 * @param sizes SecPkgContext_Sizes obtained for the context.
144 * @param message Input message.
145 * @param output Returned output message.
146 *
147 * @return NULL for success, otherwise the error code returned by
148 * Windows.
149 */
150 SWIFTEN_API boost::shared_ptr<boost::system::error_code> encryptMessage(const PCtxtHandle contextHandle, const SecPkgContext_Sizes& sizes, const SafeByteArray& message, SafeByteArray& output);
151
152 /**
153 * Queries the security package for attributes of the security context.
154 *
155 * @param contextHandle Pointer to the context handle.
156 * @param attribute Attribute to query. See the Windows API
157 * QueryContextAttributes for allowed values.
158 * @param buffer Pointer to a structure that receives the output.
159 * The type of structure depends on the queried attribute and
160 * memory for it must be allocated by caller. If the SSP allocates
161 * any memory required to hold some members, that memory should be
162 * freed using the function freeContextBuffer(). See the Windows
163 * API QueryContextAttributes for details.
164 *
165 * @return NULL for success, otherwise the error code returned by
166 * Windows.
167 */
168 SWIFTEN_API boost::shared_ptr<boost::system::error_code> queryContextAttributes(const PCtxtHandle contextHandle, ULONG attribute, PVOID buffer);
169
170}