1//
2// SSPINTLMCredentials.cpp
3//
4// Library: Net
5// Package: NTLM
6// Module: SSPINTLMCredentials
7//
8// Copyright (c) 2019, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/Net/SSPINTLMCredentials.h"
16
17
18#if POCO_OS == POCO_OS_WINDOWS_NT
19
20
21#include "Poco/SharedLibrary.h"
22#include "Poco/SingletonHolder.h"
23#include "Poco/UnicodeConverter.h"
24#include <vector>
25#define WIN32_LEAN_AND_MEAN
26#include <windows.h>
27#define SECURITY_WIN32
28#include <security.h>
29
30
31namespace Poco {
32namespace Net {
33
34
35struct NTLMContextImpl
36{
37 NTLMContextImpl():
38 maxTokenSize(0)
39 {
40 SecInvalidateHandle(&credentials);
41 SecInvalidateHandle(&context);
42 }
43
44 std::size_t maxTokenSize;
45 CredHandle credentials;
46 CtxtHandle context;
47 std::wstring spn;
48};
49
50
51class SSPINTLMProvider
52{
53public:
54 SSPINTLMProvider():
55 _securityLib("security.dll"),
56 _pSecFunTable(0)
57 {
58 InitSecurityInterfaceW pInitSecurityInterface = reinterpret_cast<InitSecurityInterfaceW>(_securityLib.getSymbol("InitSecurityInterfaceW"));
59 if (pInitSecurityInterface)
60 {
61 _pSecFunTable = pInitSecurityInterface();
62 }
63 if (!_pSecFunTable) throw Poco::SystemException("Failed to initialize SSPI");
64 }
65
66 ~SSPINTLMProvider()
67 {
68 }
69
70 bool available()
71 {
72 PSecPkgInfoW pSecPkgInfo;
73 SECURITY_STATUS status = _pSecFunTable->QuerySecurityPackageInfoW(L"NTLM", &pSecPkgInfo);
74 if (status == SEC_E_OK)
75 {
76 _pSecFunTable->FreeContextBuffer(pSecPkgInfo);
77 return true;
78 }
79 else return false;
80 }
81
82 Poco::SharedPtr<NTLMContext> createNTLMContext(const std::string& host, const std::string& service)
83 {
84 PSecPkgInfoW pSecPkgInfo;
85 SECURITY_STATUS status = _pSecFunTable->QuerySecurityPackageInfoW(L"NTLM", &pSecPkgInfo);
86 if (status != SEC_E_OK) throw Poco::SystemException("NTLM SSPI not available", status);
87
88 std::size_t maxTokenSize = pSecPkgInfo->cbMaxToken;
89 _pSecFunTable->FreeContextBuffer(pSecPkgInfo);
90
91 Poco::SharedPtr<NTLMContext> pContext = new NTLMContext(new NTLMContextImpl);
92 pContext->_pImpl->maxTokenSize = maxTokenSize;
93
94 TimeStamp expiry;
95 status = _pSecFunTable->AcquireCredentialsHandleW(
96 NULL,
97 L"NTLM",
98 SECPKG_CRED_OUTBOUND,
99 NULL,
100 NULL,
101 NULL,
102 NULL,
103 &pContext->_pImpl->credentials,
104 &expiry);
105
106 if (status != SEC_E_OK) throw Poco::SystemException("Failed to acquire NTLM credentials", status);
107
108 std::string spn = service;
109 spn += '/';
110 spn += host;
111 Poco::UnicodeConverter::convert(spn, pContext->_pImpl->spn);
112
113 return pContext;
114 }
115
116 std::vector<unsigned char> negotiate(NTLMContext& context)
117 {
118 std::vector<unsigned char> buffer(context._pImpl->maxTokenSize);
119
120 SecBuffer msgBuffer;
121 msgBuffer.BufferType = SECBUFFER_TOKEN;
122 msgBuffer.pvBuffer = &buffer[0];
123 msgBuffer.cbBuffer = static_cast<unsigned long>(buffer.size());
124
125 SecBufferDesc msgBufferDesc;
126 msgBufferDesc.ulVersion = SECBUFFER_VERSION;
127 msgBufferDesc.cBuffers = 1;
128 msgBufferDesc.pBuffers = &msgBuffer;
129
130 unsigned long attrs;
131 TimeStamp expiry;
132 SECURITY_STATUS status = _pSecFunTable->InitializeSecurityContextW(
133 &context._pImpl->credentials,
134 NULL,
135 const_cast<SEC_WCHAR*>(context._pImpl->spn.c_str()),
136 0,
137 0,
138 SECURITY_NETWORK_DREP,
139 NULL,
140 0,
141 &context._pImpl->context,
142 &msgBufferDesc,
143 &attrs,
144 &expiry);
145
146 if (status == SEC_I_COMPLETE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE)
147 {
148 _pSecFunTable->CompleteAuthToken(&context._pImpl->context, &msgBufferDesc);
149 }
150 else if (status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED)
151 {
152 throw Poco::SystemException("Failed to initialize NTLM security context", status);
153 }
154
155 buffer.resize(msgBuffer.cbBuffer);
156 return buffer;
157 }
158
159 std::vector<unsigned char> authenticate(NTLMContext& context, const std::vector<unsigned char>& challenge)
160 {
161 std::vector<unsigned char> response(context._pImpl->maxTokenSize);
162
163 SecBuffer responseBuffer;
164 responseBuffer.BufferType = SECBUFFER_TOKEN;
165 responseBuffer.pvBuffer = &response[0];
166 responseBuffer.cbBuffer = static_cast<unsigned long>(response.size());
167
168 SecBufferDesc responseBufferDesc;
169 responseBufferDesc.ulVersion = SECBUFFER_VERSION;
170 responseBufferDesc.cBuffers = 1;
171 responseBufferDesc.pBuffers = &responseBuffer;
172
173 SecBuffer challengeBuffer;
174 challengeBuffer.BufferType = SECBUFFER_TOKEN;
175 challengeBuffer.pvBuffer = const_cast<unsigned char*>(&challenge[0]);
176 challengeBuffer.cbBuffer = static_cast<unsigned long>(challenge.size());
177
178 SecBufferDesc challengeBufferDesc;
179 challengeBufferDesc.ulVersion = SECBUFFER_VERSION;
180 challengeBufferDesc.cBuffers = 1;
181 challengeBufferDesc.pBuffers = &challengeBuffer;
182
183 unsigned long attrs;
184 TimeStamp expiry;
185 SECURITY_STATUS status = _pSecFunTable->InitializeSecurityContextW(
186 &context._pImpl->credentials,
187 &context._pImpl->context,
188 const_cast<SEC_WCHAR*>(context._pImpl->spn.c_str()),
189 0,
190 0,
191 SECURITY_NETWORK_DREP,
192 &challengeBufferDesc,
193 0,
194 &context._pImpl->context,
195 &responseBufferDesc,
196 &attrs,
197 &expiry);
198
199 if (status != SEC_E_OK)
200 {
201 throw Poco::SystemException("Failed to create NTLM authenticate message", status);
202 }
203
204 response.resize(responseBuffer.cbBuffer);
205 return response;
206 }
207
208 void clearNTLMContext(NTLMContext& ctx)
209 {
210 if (SecIsValidHandle(&ctx._pImpl->context))
211 {
212 _pSecFunTable->DeleteSecurityContext(&ctx._pImpl->context);
213 }
214 if (SecIsValidHandle(&ctx._pImpl->credentials))
215 {
216 _pSecFunTable->FreeCredentialsHandle(&ctx._pImpl->credentials);
217 }
218 }
219
220 static SSPINTLMProvider& instance();
221
222private:
223 typedef PSecurityFunctionTableW(APIENTRY *InitSecurityInterfaceW)(VOID);
224
225 Poco::SharedLibrary _securityLib;
226 PSecurityFunctionTableW _pSecFunTable;
227};
228
229
230namespace
231{
232 static Poco::SingletonHolder<SSPINTLMProvider> sspintlmProviderHolder;
233}
234
235
236SSPINTLMProvider& SSPINTLMProvider::instance()
237{
238 return *sspintlmProviderHolder.get();
239}
240
241
242} } // namespace Poco::Net
243
244
245#endif // POCO_OS == POCO_OS_WINDOWS_NT
246
247
248namespace Poco {
249namespace Net {
250
251
252const std::string SSPINTLMCredentials::SERVICE_HTTP("HTTP");
253const std::string SSPINTLMCredentials::SERVICE_SMTP("SMTP");
254
255
256NTLMContext::NTLMContext(NTLMContextImpl* pImpl):
257 _pImpl(pImpl)
258{
259}
260
261
262NTLMContext::~NTLMContext()
263{
264#if POCO_OS == POCO_OS_WINDOWS_NT
265 SSPINTLMProvider::instance().clearNTLMContext(*this);
266 delete _pImpl;
267#endif
268}
269
270
271bool SSPINTLMCredentials::available()
272{
273#if POCO_OS == POCO_OS_WINDOWS_NT
274 try
275 {
276 return SSPINTLMProvider::instance().available();
277 }
278 catch (...)
279 {
280 return false;
281 }
282#else
283 return false;
284#endif
285}
286
287
288Poco::SharedPtr<NTLMContext> SSPINTLMCredentials::createNTLMContext(const std::string& workstation, const std::string& service)
289{
290#if POCO_OS == POCO_OS_WINDOWS_NT
291 return SSPINTLMProvider::instance().createNTLMContext(workstation, service);
292#else
293 throw Poco::NotImplementedException("SSPINTLMCredentials::createNTLMContext() is only available on Windows");
294#endif
295}
296
297
298std::vector<unsigned char> SSPINTLMCredentials::negotiate(NTLMContext& context)
299{
300#if POCO_OS == POCO_OS_WINDOWS_NT
301 return SSPINTLMProvider::instance().negotiate(context);
302#else
303 throw Poco::NotImplementedException("SSPINTLMCredentials::negotiate() is only available on Windows");
304#endif
305}
306
307
308std::vector<unsigned char> SSPINTLMCredentials::authenticate(NTLMContext& context, const std::vector<unsigned char>& challenge)
309{
310#if POCO_OS == POCO_OS_WINDOWS_NT
311 return SSPINTLMProvider::instance().authenticate(context, challenge);
312#else
313 throw Poco::NotImplementedException("SSPINTLMCredentials::authenticate() is only available on Windows");
314#endif
315}
316
317
318} } // namespace Poco::Net
319