FreeRDP
Loading...
Searching...
No Matches
TestSspiNTLM.c
1
2#include <winpr/crt.h>
3#include <winpr/assert.h>
4#include <winpr/sspi.h>
5#include <winpr/print.h>
6#include <winpr/wlog.h>
7#include <winpr/ntlm.h>
8#include <winpr/tchar.h>
9
10struct test_input_t
11{
12 const char* user;
13 const char* domain;
14 const char* pwd;
15 const BYTE* ntlm;
16 const BYTE* ntlmv2;
17 BOOL dynamic;
18 BOOL expected;
19};
20
21typedef struct
22{
23 CtxtHandle context;
24 ULONG cbMaxToken;
25 ULONG fContextReq;
26 ULONG pfContextAttr;
27 TimeStamp expiration;
28 PSecBuffer pBuffer;
29 SecBuffer inputBuffer[2];
30 SecBuffer outputBuffer[2];
31 BOOL haveContext;
32 BOOL haveInputBuffer;
33 BOOL UseNtlmV2Hash;
34 BOOL UseCallback;
35 LPTSTR ServicePrincipalName;
36 SecBufferDesc inputBufferDesc;
37 SecBufferDesc outputBufferDesc;
38 CredHandle credentials;
39 BOOL confidentiality;
40 SecPkgInfo* pPackageInfo;
41 SecurityFunctionTable* table;
44} TEST_NTLM_SERVER;
45
46static BYTE TEST_NTLM_TIMESTAMP[8] = { 0x33, 0x57, 0xbd, 0xb1, 0x07, 0x8b, 0xcf, 0x01 };
47
48static BYTE TEST_NTLM_CLIENT_CHALLENGE[8] = { 0x20, 0xc0, 0x2b, 0x3d, 0xc0, 0x61, 0xa7, 0x73 };
49
50static BYTE TEST_NTLM_SERVER_CHALLENGE[8] = { 0xa4, 0xf1, 0xba, 0xa6, 0x7c, 0xdc, 0x1a, 0x12 };
51
52static BYTE TEST_NTLM_NEGOTIATE[] =
53 "\x4e\x54\x4c\x4d\x53\x53\x50\x00\x01\x00\x00\x00\x07\x82\x08\xa2"
54 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
55 "\x06\x03\x80\x25\x00\x00\x00\x0f";
56
57static BYTE TEST_NTLM_CHALLENGE[] =
58 "\x4e\x54\x4c\x4d\x53\x53\x50\x00\x02\x00\x00\x00\x00\x00\x00\x00"
59 "\x38\x00\x00\x00\x07\x82\x88\xa2\xa4\xf1\xba\xa6\x7c\xdc\x1a\x12"
60 "\x00\x00\x00\x00\x00\x00\x00\x00\x66\x00\x66\x00\x38\x00\x00\x00"
61 "\x06\x03\x80\x25\x00\x00\x00\x0f\x02\x00\x0e\x00\x4e\x00\x45\x00"
62 "\x57\x00\x59\x00\x45\x00\x41\x00\x52\x00\x01\x00\x0e\x00\x4e\x00"
63 "\x45\x00\x57\x00\x59\x00\x45\x00\x41\x00\x52\x00\x04\x00\x1c\x00"
64 "\x6c\x00\x61\x00\x62\x00\x2e\x00\x77\x00\x61\x00\x79\x00\x6b\x00"
65 "\x2e\x00\x6c\x00\x6f\x00\x63\x00\x61\x00\x6c\x00\x03\x00\x0e\x00"
66 "\x6e\x00\x65\x00\x77\x00\x79\x00\x65\x00\x61\x00\x72\x00\x07\x00"
67 "\x08\x00\x33\x57\xbd\xb1\x07\x8b\xcf\x01\x00\x00\x00\x00";
68
69static BYTE TEST_NTLM_AUTHENTICATE[] =
70 "\x4e\x54\x4c\x4d\x53\x53\x50\x00\x03\x00\x00\x00\x18\x00\x18\x00"
71 "\x82\x00\x00\x00\x08\x01\x08\x01\x9a\x00\x00\x00\x0c\x00\x0c\x00"
72 "\x58\x00\x00\x00\x10\x00\x10\x00\x64\x00\x00\x00\x0e\x00\x0e\x00"
73 "\x74\x00\x00\x00\x00\x00\x00\x00\xa2\x01\x00\x00\x05\x82\x88\xa2"
74 "\x06\x03\x80\x25\x00\x00\x00\x0f\x12\xe5\x5a\xf5\x80\xee\x3f\x29"
75 "\xe1\xde\x90\x4d\x73\x77\x06\x25\x44\x00\x6f\x00\x6d\x00\x61\x00"
76 "\x69\x00\x6e\x00\x55\x00\x73\x00\x65\x00\x72\x00\x6e\x00\x61\x00"
77 "\x6d\x00\x65\x00\x4e\x00\x45\x00\x57\x00\x59\x00\x45\x00\x41\x00"
78 "\x52\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
79 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x62\x14\x68\xc8\x98\x12"
80 "\xe7\x39\xd8\x76\x1b\xe9\xf7\x54\xb5\xe3\x01\x01\x00\x00\x00\x00"
81 "\x00\x00\x33\x57\xbd\xb1\x07\x8b\xcf\x01\x20\xc0\x2b\x3d\xc0\x61"
82 "\xa7\x73\x00\x00\x00\x00\x02\x00\x0e\x00\x4e\x00\x45\x00\x57\x00"
83 "\x59\x00\x45\x00\x41\x00\x52\x00\x01\x00\x0e\x00\x4e\x00\x45\x00"
84 "\x57\x00\x59\x00\x45\x00\x41\x00\x52\x00\x04\x00\x1c\x00\x6c\x00"
85 "\x61\x00\x62\x00\x2e\x00\x77\x00\x61\x00\x79\x00\x6b\x00\x2e\x00"
86 "\x6c\x00\x6f\x00\x63\x00\x61\x00\x6c\x00\x03\x00\x0e\x00\x6e\x00"
87 "\x65\x00\x77\x00\x79\x00\x65\x00\x61\x00\x72\x00\x07\x00\x08\x00"
88 "\x33\x57\xbd\xb1\x07\x8b\xcf\x01\x06\x00\x04\x00\x02\x00\x00\x00"
89 "\x08\x00\x30\x00\x30\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00"
90 "\x00\x20\x00\x00\x1e\x10\xf5\x2c\x54\x2f\x2e\x77\x1c\x13\xbf\xc3"
91 "\x3f\xe1\x7b\x28\x7e\x0b\x93\x5a\x39\xd2\xce\x12\xd7\xbd\x8c\x4e"
92 "\x2b\xb5\x0b\xf5\x0a\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00"
93 "\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x1a\x00\x48\x00\x54\x00"
94 "\x54\x00\x50\x00\x2f\x00\x72\x00\x77\x00\x2e\x00\x6c\x00\x6f\x00"
95 "\x63\x00\x61\x00\x6c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
96 "\x00\x00";
97
98#define TEST_SSPI_INTERFACE SSPI_INTERFACE_WINPR
99
100static const char* TEST_NTLM_USER = "Username";
101static const char* TEST_NTLM_DOMAIN = "Domain";
102static const char* TEST_NTLM_PASSWORD = "P4ss123!";
103
104// static const char* TEST_NTLM_HASH_STRING = "d5922a65c4d5c082ca444af1be0001db";
105
106static const BYTE TEST_NTLM_HASH[16] = { 0xd5, 0x92, 0x2a, 0x65, 0xc4, 0xd5, 0xc0, 0x82,
107 0xca, 0x44, 0x4a, 0xf1, 0xbe, 0x00, 0x01, 0xdb };
108
109// static const char* TEST_NTLM_HASH_V2_STRING = "4c7f706f7dde05a9d1a0f4e7ffe3bfb8";
110
111static const BYTE TEST_NTLM_V2_HASH[16] = { 0x4c, 0x7f, 0x70, 0x6f, 0x7d, 0xde, 0x05, 0xa9,
112 0xd1, 0xa0, 0xf4, 0xe7, 0xff, 0xe3, 0xbf, 0xb8 };
113
114static const BYTE TEST_EMPTY_PWD_NTLM_HASH[] = { 0x31, 0xd6, 0xcf, 0xe0, 0xd1, 0x6a, 0xe9, 0x31,
115 0xb7, 0x3c, 0x59, 0xd7, 0xe0, 0xc0, 0x89, 0xc0 };
116
117static const BYTE TEST_EMPTY_PWD_NTLM_V2_HASH[] = {
118 0x0b, 0xce, 0x54, 0x87, 0x4e, 0x94, 0x20, 0x9e, 0x34, 0x48, 0x97, 0xc1, 0x60, 0x03, 0x6e, 0x3b
119};
120
121#define NTLM_PACKAGE_NAME NTLM_SSP_NAME
122
123typedef struct
124{
125 CtxtHandle context;
126 ULONG cbMaxToken;
127 ULONG fContextReq;
128 ULONG pfContextAttr;
129 TimeStamp expiration;
130 PSecBuffer pBuffer;
131 SecBuffer inputBuffer[2];
132 SecBuffer outputBuffer[2];
133 BOOL haveContext;
134 BOOL haveInputBuffer;
135 LPTSTR ServicePrincipalName;
136 SecBufferDesc inputBufferDesc;
137 SecBufferDesc outputBufferDesc;
138 CredHandle credentials;
139 BOOL confidentiality;
140 SecPkgInfo* pPackageInfo;
141 SecurityFunctionTable* table;
142 SEC_WINNT_AUTH_IDENTITY identity;
143} TEST_NTLM_CLIENT;
144
145#if !defined(_WIN32)
146WINPR_ATTR_NODISCARD
147static BOOL test_attributes_read(SecurityFunctionTable* table, CtxtHandle* context,
148 const void* expected, size_t expectedLen)
149{
150 WINPR_ASSERT(table);
151 WINPR_ASSERT(context);
152 WINPR_ASSERT(expected);
153 WINPR_ASSERT(expectedLen > 0);
154 WINPR_ASSERT(expectedLen <= UINT32_MAX);
155
156 struct attr_pair_t
157 {
158 ULONG val;
159 ULONG len;
160 };
161 const struct attr_pair_t attributes[] = {
162 { SECPKG_ATTR_AUTH_NTLM_HOSTNAME, SECPKG_ATTR_AUTH_NTLM_HOSTNAME_LEN },
163 { SECPKG_ATTR_AUTH_NTLM_NB_COMPUTER_NAME, SECPKG_ATTR_AUTH_NTLM_NB_COMPUTER_NAME_LEN },
164 { SECPKG_ATTR_AUTH_NTLM_NB_DOMAIN_NAME, SECPKG_ATTR_AUTH_NTLM_NB_DOMAIN_NAME_LEN },
165 { SECPKG_ATTR_AUTH_NTLM_DNS_COMPUTER_NAME, SECPKG_ATTR_AUTH_NTLM_DNS_COMPUTER_NAME_LEN },
166 { SECPKG_ATTR_AUTH_NTLM_DNS_DOMAIN_NAME, SECPKG_ATTR_AUTH_NTLM_DNS_DOMAIN_NAME_LEN }
167 };
168 BOOL rc = FALSE;
169
170 TCHAR* buffer = calloc(expectedLen + 1, sizeof(TCHAR));
171 if (!buffer)
172 return FALSE;
173 for (size_t x = 0; x < ARRAYSIZE(attributes); x++)
174 {
175 const struct attr_pair_t attribute = attributes[x];
176
177 {
178 ULONG len = 0;
179 SECURITY_STATUS status = table->QueryContextAttributes(context, attribute.len, &len);
180 if (status != SEC_E_OK)
181 goto fail;
182 if (len != expectedLen)
183 goto fail;
184 }
185
186 {
187 memset(buffer, 0, expectedLen);
188 SECURITY_STATUS status = table->QueryContextAttributes(context, attribute.val, buffer);
189 if (status != SEC_E_OK)
190 goto fail;
191 if (memcmp(expected, buffer, expectedLen) != 0)
192 goto fail;
193 }
194 }
195
196 rc = TRUE;
197fail:
198 free(buffer);
199 return rc;
200}
201
202WINPR_ATTR_NODISCARD
203static BOOL test_attributes_write(SecurityFunctionTable* table, CtxtHandle* context,
204 const void* expected, size_t expectedLen)
205{
206 WINPR_ASSERT(table);
207 WINPR_ASSERT(context);
208 WINPR_ASSERT(expected);
209 WINPR_ASSERT(expectedLen > 0);
210 WINPR_ASSERT(expectedLen <= UINT32_MAX);
211
212 struct attr_pair_t
213 {
214 ULONG val;
215 ULONG len;
216 };
217 const struct attr_pair_t attributes[] = {
218 { SECPKG_ATTR_AUTH_NTLM_HOSTNAME, SECPKG_ATTR_AUTH_NTLM_HOSTNAME_LEN },
219 { SECPKG_ATTR_AUTH_NTLM_NB_COMPUTER_NAME, SECPKG_ATTR_AUTH_NTLM_NB_COMPUTER_NAME_LEN },
220 { SECPKG_ATTR_AUTH_NTLM_NB_DOMAIN_NAME, SECPKG_ATTR_AUTH_NTLM_NB_DOMAIN_NAME_LEN },
221 { SECPKG_ATTR_AUTH_NTLM_DNS_COMPUTER_NAME, SECPKG_ATTR_AUTH_NTLM_DNS_COMPUTER_NAME_LEN },
222 { SECPKG_ATTR_AUTH_NTLM_DNS_DOMAIN_NAME, SECPKG_ATTR_AUTH_NTLM_DNS_DOMAIN_NAME_LEN }
223 };
224 BOOL rc = FALSE;
225
226 for (size_t x = 0; x < ARRAYSIZE(attributes); x++)
227 {
228 const struct attr_pair_t attribute = attributes[x];
229
230 SECURITY_STATUS status =
231 table->SetContextAttributes(context, attribute.val, expected, expectedLen);
232 if (status != SEC_E_OK)
233 goto fail;
234 }
235
236 rc = TRUE;
237fail:
238 return rc;
239}
240#endif
241
242WINPR_ATTR_NODISCARD
243static BOOL test_attributes(WINPR_ATTR_UNUSED SecurityFunctionTable* table,
244 WINPR_ATTR_UNUSED CtxtHandle* context)
245{
246#if !defined(_WIN32)
247 const TCHAR* testvalues[] = { _T("foobar"), _T("gaga"), _T("gag"), _T("ga"), _T("a") };
248
249 for (size_t x = 0; x < ARRAYSIZE(testvalues); x++)
250 {
251 const TCHAR* cur = testvalues[x];
252 const size_t len = _tcslen(cur);
253 if (!test_attributes_write(table, context, cur, len))
254 return FALSE;
255 if (!test_attributes_read(table, context, cur, len))
256 return FALSE;
257 }
258#endif
259 return TRUE;
260}
261
262WINPR_ATTR_NODISCARD
263static void* getServerAuthData(TEST_NTLM_SERVER* ntlm, const struct test_input_t* arg,
264 psSspiNtlmHashCallback fkt)
265{
266
267 if (fkt)
268 {
269 ntlm->authData.identity.Version = SEC_WINNT_AUTH_IDENTITY_VERSION;
270 ntlm->authData.identity.Length = sizeof(SEC_WINNT_AUTH_IDENTITY_EX);
271 ntlm->authData.identity.Flags |=
272 SEC_WINNT_AUTH_IDENTITY_EXTENDED | SEC_WINNT_AUTH_IDENTITY_UNICODE;
273 ntlm->settings.hashCallback = fkt;
274 ntlm->settings.hashCallbackArg = arg;
275 ntlm->authData.ntlmSettings = &ntlm->settings;
276 ntlm->UseCallback = TRUE;
277 return &ntlm->authData;
278 }
279 ntlm->UseCallback = FALSE;
280 return nullptr;
281}
282
283WINPR_ATTR_NODISCARD
284static int test_ntlm_client_init(TEST_NTLM_CLIENT* ntlm, const char* user, const char* domain,
285 const char* password)
286{
287 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
288
289 WINPR_ASSERT(ntlm);
290
291 SecInvalidateHandle(&(ntlm->context));
292 ntlm->table = InitSecurityInterfaceEx(TEST_SSPI_INTERFACE);
293 if (!ntlm->table)
294 return -1;
295 const int rc = sspi_SetAuthIdentity(&(ntlm->identity), user, domain, password);
296 if (rc < 0)
297 return rc;
298
299 status = ntlm->table->QuerySecurityPackageInfo(NTLM_PACKAGE_NAME, &ntlm->pPackageInfo);
300
301 if (status != SEC_E_OK)
302 {
303 (void)fprintf(stderr, "QuerySecurityPackageInfo status: %s (0x%08" PRIX32 ")\n",
304 GetSecurityStatusString(status), status);
305 return -1;
306 }
307
308 ntlm->cbMaxToken = ntlm->pPackageInfo->cbMaxToken;
309 status = ntlm->table->AcquireCredentialsHandle(nullptr, NTLM_PACKAGE_NAME, SECPKG_CRED_OUTBOUND,
310 nullptr, &ntlm->identity, nullptr, nullptr,
311 &ntlm->credentials, &ntlm->expiration);
312
313 if (status != SEC_E_OK)
314 {
315 (void)fprintf(stderr, "AcquireCredentialsHandle status: %s (0x%08" PRIX32 ")\n",
316 GetSecurityStatusString(status), status);
317 return -1;
318 }
319
320 ntlm->haveContext = FALSE;
321 ntlm->haveInputBuffer = FALSE;
322 ZeroMemory(&ntlm->inputBuffer, sizeof(SecBuffer));
323 ZeroMemory(&ntlm->outputBuffer, sizeof(SecBuffer));
324 ntlm->fContextReq = 0;
325
326 /* NLA authentication flags */
327 ntlm->fContextReq |= ISC_REQ_MUTUAL_AUTH;
328 ntlm->fContextReq |= ISC_REQ_CONFIDENTIALITY;
329 ntlm->fContextReq |= ISC_REQ_USE_SESSION_KEY;
330 return 1;
331}
332
333static void test_ntlm_client_uninit(TEST_NTLM_CLIENT* ntlm)
334{
335 if (!ntlm)
336 return;
337
338 if (ntlm->outputBuffer[0].pvBuffer)
339 {
340 free(ntlm->outputBuffer[0].pvBuffer);
341 ntlm->outputBuffer[0].pvBuffer = nullptr;
342 }
343
344 free(ntlm->identity.User);
345 free(ntlm->identity.Domain);
346 free(ntlm->identity.Password);
347 free(ntlm->ServicePrincipalName);
348
349 if (ntlm->table)
350 {
351 SECURITY_STATUS status = ntlm->table->FreeCredentialsHandle(&ntlm->credentials);
352 WINPR_ASSERT((status == SEC_E_OK) || (status == SEC_E_SECPKG_NOT_FOUND) ||
353 (status == SEC_E_UNSUPPORTED_FUNCTION));
354
355 status = ntlm->table->FreeContextBuffer(ntlm->pPackageInfo);
356 WINPR_ASSERT((status == SEC_E_OK) || (status = SEC_E_INVALID_HANDLE));
357
358 status = ntlm->table->DeleteSecurityContext(&ntlm->context);
359 WINPR_ASSERT((status == SEC_E_OK) || (status == SEC_E_SECPKG_NOT_FOUND) ||
360 (status == SEC_E_UNSUPPORTED_FUNCTION));
361 }
362}
363
401WINPR_ATTR_NODISCARD
402static BOOL IsSecurityStatusError(SECURITY_STATUS status)
403{
404 BOOL error = TRUE;
405
406 switch (status)
407 {
408 case SEC_E_OK:
409 case SEC_I_CONTINUE_NEEDED:
410 case SEC_I_COMPLETE_NEEDED:
411 case SEC_I_COMPLETE_AND_CONTINUE:
412 case SEC_I_LOCAL_LOGON:
413 case SEC_I_CONTEXT_EXPIRED:
414 case SEC_I_INCOMPLETE_CREDENTIALS:
415 case SEC_I_RENEGOTIATE:
416 case SEC_I_NO_LSA_CONTEXT:
417 case SEC_I_SIGNATURE_NEEDED:
418 case SEC_I_NO_RENEGOTIATION:
419 error = FALSE;
420 break;
421 default:
422 break;
423 }
424
425 return error;
426}
427
428WINPR_ATTR_NODISCARD
429static int test_ntlm_client_authenticate(TEST_NTLM_CLIENT* ntlm)
430{
431 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
432
433 WINPR_ASSERT(ntlm);
434 if (ntlm->outputBuffer[0].pvBuffer)
435 {
436 free(ntlm->outputBuffer[0].pvBuffer);
437 ntlm->outputBuffer[0].pvBuffer = nullptr;
438 }
439
440 ntlm->outputBufferDesc.ulVersion = SECBUFFER_VERSION;
441 ntlm->outputBufferDesc.cBuffers = 1;
442 ntlm->outputBufferDesc.pBuffers = ntlm->outputBuffer;
443 ntlm->outputBuffer[0].BufferType = SECBUFFER_TOKEN;
444 ntlm->outputBuffer[0].cbBuffer = ntlm->cbMaxToken;
445 ntlm->outputBuffer[0].pvBuffer = malloc(ntlm->outputBuffer[0].cbBuffer);
446
447 if (!ntlm->outputBuffer[0].pvBuffer)
448 return -1;
449
450 if (ntlm->haveInputBuffer)
451 {
452 ntlm->inputBufferDesc.ulVersion = SECBUFFER_VERSION;
453 ntlm->inputBufferDesc.cBuffers = 1;
454 ntlm->inputBufferDesc.pBuffers = ntlm->inputBuffer;
455 ntlm->inputBuffer[0].BufferType = SECBUFFER_TOKEN;
456 }
457
458 if ((!ntlm) || (!ntlm->table))
459 {
460 (void)fprintf(stderr, "ntlm_authenticate: invalid ntlm context\n");
461 return -1;
462 }
463
464 status = ntlm->table->InitializeSecurityContext(
465 &ntlm->credentials, (ntlm->haveContext) ? &ntlm->context : nullptr,
466 (ntlm->ServicePrincipalName) ? ntlm->ServicePrincipalName : nullptr, ntlm->fContextReq, 0,
467 SECURITY_NATIVE_DREP, (ntlm->haveInputBuffer) ? &ntlm->inputBufferDesc : nullptr, 0,
468 &ntlm->context, &ntlm->outputBufferDesc, &ntlm->pfContextAttr, &ntlm->expiration);
469
470 if (IsSecurityStatusError(status))
471 return -1;
472
473 if ((status == SEC_I_COMPLETE_AND_CONTINUE) || (status == SEC_I_COMPLETE_NEEDED))
474 {
475 if (ntlm->table->CompleteAuthToken)
476 {
477 SECURITY_STATUS rc =
478 ntlm->table->CompleteAuthToken(&ntlm->context, &ntlm->outputBufferDesc);
479 if (rc != SEC_E_OK)
480 return -1;
481 }
482
483 if (status == SEC_I_COMPLETE_NEEDED)
484 status = SEC_E_OK;
485 else if (status == SEC_I_COMPLETE_AND_CONTINUE)
486 status = SEC_I_CONTINUE_NEEDED;
487 }
488
489 if (ntlm->haveInputBuffer)
490 {
491 free(ntlm->inputBuffer[0].pvBuffer);
492 ntlm->inputBuffer[0].pvBuffer = nullptr;
493 }
494
495 ntlm->haveInputBuffer = TRUE;
496 ntlm->haveContext = TRUE;
497 return (status == SEC_I_CONTINUE_NEEDED) ? 1 : 0;
498}
499
500static void test_ntlm_client_free(TEST_NTLM_CLIENT* ntlm)
501{
502 if (!ntlm)
503 return;
504
505 test_ntlm_client_uninit(ntlm);
506 free(ntlm);
507}
508
509WINPR_ATTR_MALLOC(test_ntlm_client_free, 1)
510static TEST_NTLM_CLIENT* test_ntlm_client_new(void)
511{
512 TEST_NTLM_CLIENT* ntlm = (TEST_NTLM_CLIENT*)calloc(1, sizeof(TEST_NTLM_CLIENT));
513
514 if (!ntlm)
515 return nullptr;
516
517 return ntlm;
518}
519
520WINPR_ATTR_NODISCARD
521static int test_ntlm_server_init(TEST_NTLM_SERVER* ntlm, const struct test_input_t* arg,
522 psSspiNtlmHashCallback fkt)
523{
524 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
525
526 WINPR_ASSERT(ntlm);
527
528 ntlm->UseNtlmV2Hash = TRUE;
529 SecInvalidateHandle(&(ntlm->context));
530 ntlm->table = InitSecurityInterfaceEx(TEST_SSPI_INTERFACE);
531 if (!ntlm->table)
532 return SEC_E_INTERNAL_ERROR;
533
534 status = ntlm->table->QuerySecurityPackageInfo(NTLM_PACKAGE_NAME, &ntlm->pPackageInfo);
535
536 if (status != SEC_E_OK)
537 {
538 (void)fprintf(stderr, "QuerySecurityPackageInfo status: %s (0x%08" PRIX32 ")\n",
539 GetSecurityStatusString(status), status);
540 return -1;
541 }
542
543 void* authData = getServerAuthData(ntlm, arg, fkt);
544 ntlm->cbMaxToken = ntlm->pPackageInfo->cbMaxToken;
545 status = ntlm->table->AcquireCredentialsHandle(nullptr, NTLM_PACKAGE_NAME, SECPKG_CRED_INBOUND,
546 nullptr, authData, nullptr, nullptr,
547 &ntlm->credentials, &ntlm->expiration);
548
549 if (status != SEC_E_OK)
550 {
551 (void)fprintf(stderr, "AcquireCredentialsHandle status: %s (0x%08" PRIX32 ")\n",
552 GetSecurityStatusString(status), status);
553 return -1;
554 }
555
556 ntlm->haveContext = FALSE;
557 ntlm->haveInputBuffer = FALSE;
558 ZeroMemory(&ntlm->inputBuffer, sizeof(SecBuffer));
559 ZeroMemory(&ntlm->outputBuffer, sizeof(SecBuffer));
560 ntlm->fContextReq = 0;
561 /* NLA authentication flags */
562 ntlm->fContextReq |= ASC_REQ_MUTUAL_AUTH;
563 ntlm->fContextReq |= ASC_REQ_CONFIDENTIALITY;
564 ntlm->fContextReq |= ASC_REQ_CONNECTION;
565 ntlm->fContextReq |= ASC_REQ_USE_SESSION_KEY;
566 ntlm->fContextReq |= ASC_REQ_REPLAY_DETECT;
567 ntlm->fContextReq |= ASC_REQ_SEQUENCE_DETECT;
568 ntlm->fContextReq |= ASC_REQ_EXTENDED_ERROR;
569 return 1;
570}
571
572static void test_ntlm_server_uninit(TEST_NTLM_SERVER* ntlm)
573{
574 if (!ntlm)
575 return;
576
577 if (ntlm->outputBuffer[0].pvBuffer)
578 {
579 free(ntlm->outputBuffer[0].pvBuffer);
580 ntlm->outputBuffer[0].pvBuffer = nullptr;
581 }
582
583 free(ntlm->ServicePrincipalName);
584
585 if (ntlm->table)
586 {
587 SECURITY_STATUS status = ntlm->table->FreeCredentialsHandle(&ntlm->credentials);
588 WINPR_ASSERT(status == SEC_E_OK);
589 status = ntlm->table->FreeContextBuffer(ntlm->pPackageInfo);
590 WINPR_ASSERT(status == SEC_E_OK);
591 status = ntlm->table->DeleteSecurityContext(&ntlm->context);
592 WINPR_ASSERT(status == SEC_E_OK);
593 }
594}
595
596WINPR_ATTR_NODISCARD
597static int test_ntlm_server_authenticate(const struct test_input_t* targ, TEST_NTLM_SERVER* ntlm)
598{
599 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
600
601 WINPR_ASSERT(ntlm);
602 WINPR_ASSERT(targ);
603
604 ntlm->inputBufferDesc.ulVersion = SECBUFFER_VERSION;
605 ntlm->inputBufferDesc.cBuffers = 1;
606 ntlm->inputBufferDesc.pBuffers = ntlm->inputBuffer;
607 ntlm->inputBuffer[0].BufferType = SECBUFFER_TOKEN;
608 ntlm->outputBufferDesc.ulVersion = SECBUFFER_VERSION;
609 ntlm->outputBufferDesc.cBuffers = 1;
610 ntlm->outputBufferDesc.pBuffers = &ntlm->outputBuffer[0];
611 ntlm->outputBuffer[0].BufferType = SECBUFFER_TOKEN;
612 ntlm->outputBuffer[0].cbBuffer = ntlm->cbMaxToken;
613 ntlm->outputBuffer[0].pvBuffer = malloc(ntlm->outputBuffer[0].cbBuffer);
614
615 if (!ntlm->outputBuffer[0].pvBuffer)
616 return -1;
617
618 status = ntlm->table->AcceptSecurityContext(
619 &ntlm->credentials, ntlm->haveContext ? &ntlm->context : nullptr, &ntlm->inputBufferDesc,
620 ntlm->fContextReq, SECURITY_NATIVE_DREP, &ntlm->context, &ntlm->outputBufferDesc,
621 &ntlm->pfContextAttr, &ntlm->expiration);
622
623 if (status == SEC_I_CONTINUE_NEEDED)
624 {
625 SecPkgContext_AuthNtlmHash AuthNtlmHash = WINPR_C_ARRAY_INIT;
626
627 if (!ntlm->UseCallback)
628 {
629 if (ntlm->UseNtlmV2Hash)
630 {
631 AuthNtlmHash.Version = 2;
632 CopyMemory(AuthNtlmHash.NtlmHash, targ->ntlmv2, 16);
633 }
634 else
635 {
636 AuthNtlmHash.Version = 1;
637 CopyMemory(AuthNtlmHash.NtlmHash, targ->ntlm, 16);
638 }
639
640 status = ntlm->table->SetContextAttributes(&ntlm->context, SECPKG_ATTR_AUTH_NTLM_HASH,
641 &AuthNtlmHash,
643 }
644 }
645
646 if ((status != SEC_E_OK) && (status != SEC_I_CONTINUE_NEEDED))
647 {
648 (void)fprintf(stderr, "AcceptSecurityContext status: %s (0x%08" PRIX32 ")\n",
649 GetSecurityStatusString(status), status);
650 return -1; /* Access Denied */
651 }
652
653 ntlm->haveContext = TRUE;
654 return (status == SEC_I_CONTINUE_NEEDED) ? 1 : 0;
655}
656
657static void test_ntlm_server_free(TEST_NTLM_SERVER* ntlm)
658{
659 if (!ntlm)
660 return;
661
662 test_ntlm_server_uninit(ntlm);
663 free(ntlm);
664}
665
666WINPR_ATTR_MALLOC(test_ntlm_server_free, 1)
667static TEST_NTLM_SERVER* test_ntlm_server_new(void)
668{
669 TEST_NTLM_SERVER* ntlm = (TEST_NTLM_SERVER*)calloc(1, sizeof(TEST_NTLM_SERVER));
670
671 if (!ntlm)
672 return nullptr;
673
674 return ntlm;
675}
676
677WINPR_ATTR_NODISCARD
678static BOOL test_default(const struct test_input_t* arg, psSspiNtlmHashCallback fkt)
679{
680 BOOL rc = FALSE;
681
682 WINPR_ASSERT(arg);
683
684 printf("testcase {user=%s, domain=%s, password=%s, dynamic=%s}\n", arg->user, arg->domain,
685 arg->pwd, arg->dynamic ? "TRUE" : "FALSE");
686
690 TEST_NTLM_CLIENT* client = test_ntlm_client_new();
691 TEST_NTLM_SERVER* server = test_ntlm_server_new();
692
693 if (!client || !server)
694 {
695 printf("Memory allocation failed\n");
696 goto fail;
697 }
698
699 int status = test_ntlm_client_init(client, arg->user, arg->domain, arg->pwd);
700
701 if (status < 0)
702 {
703 printf("test_ntlm_client_init failure\n");
704 goto fail;
705 }
706
711 status = test_ntlm_server_init(server, arg, fkt);
712
713 if (status < 0)
714 {
715 printf("test_ntlm_server_init failure\n");
716 goto fail;
717 }
718
722 status = test_ntlm_client_authenticate(client);
723
724 if (status < 0)
725 {
726 printf("test_ntlm_client_authenticate failure\n");
727 goto fail;
728 }
729
730 if (client->haveContext)
731 {
732 if (!test_attributes(client->table, &client->context))
733 goto fail;
734 }
735
736 if (!arg->dynamic)
737 {
738 SecPkgContext_AuthNtlmTimestamp AuthNtlmTimestamp = WINPR_C_ARRAY_INIT;
739 SecPkgContext_AuthNtlmClientChallenge AuthNtlmClientChallenge = WINPR_C_ARRAY_INIT;
740 SecPkgContext_AuthNtlmServerChallenge AuthNtlmServerChallenge = WINPR_C_ARRAY_INIT;
741 CopyMemory(AuthNtlmTimestamp.Timestamp, TEST_NTLM_TIMESTAMP, 8);
742 AuthNtlmTimestamp.ChallengeOrResponse = TRUE;
743 SECURITY_STATUS rc = client->table->SetContextAttributes(
744 &client->context, SECPKG_ATTR_AUTH_NTLM_TIMESTAMP, &AuthNtlmTimestamp,
746 WINPR_ASSERT((rc == SEC_E_OK) || (rc == SEC_E_SECPKG_NOT_FOUND));
747
748 AuthNtlmTimestamp.ChallengeOrResponse = FALSE;
749 rc = client->table->SetContextAttributes(&client->context, SECPKG_ATTR_AUTH_NTLM_TIMESTAMP,
750 &AuthNtlmTimestamp,
752 WINPR_ASSERT((rc == SEC_E_OK) || (rc == SEC_E_SECPKG_NOT_FOUND));
753
754 CopyMemory(AuthNtlmClientChallenge.ClientChallenge, TEST_NTLM_CLIENT_CHALLENGE, 8);
755 CopyMemory(AuthNtlmServerChallenge.ServerChallenge, TEST_NTLM_SERVER_CHALLENGE, 8);
756 rc = client->table->SetContextAttributes(
757 &client->context, SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE, &AuthNtlmClientChallenge,
759 WINPR_ASSERT((rc == SEC_E_OK) || (rc == SEC_E_SECPKG_NOT_FOUND));
760
761 rc = client->table->SetContextAttributes(
762 &client->context, SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE, &AuthNtlmServerChallenge,
764 WINPR_ASSERT((rc == SEC_E_OK) || (rc == SEC_E_SECPKG_NOT_FOUND));
765 }
766
767 PSecBuffer pSecBuffer = &(client->outputBuffer[0]);
768
769 if (!arg->dynamic)
770 {
771 pSecBuffer->cbBuffer = sizeof(TEST_NTLM_NEGOTIATE) - 1;
772 free(pSecBuffer->pvBuffer);
773 pSecBuffer->pvBuffer = malloc(pSecBuffer->cbBuffer);
774
775 if (!pSecBuffer->pvBuffer)
776 {
777 printf("Memory allocation failed\n");
778 goto fail;
779 }
780
781 CopyMemory(pSecBuffer->pvBuffer, TEST_NTLM_NEGOTIATE, pSecBuffer->cbBuffer);
782 }
783
784 (void)fprintf(stderr, "NTLM_NEGOTIATE (length = %" PRIu32 "):\n", pSecBuffer->cbBuffer);
785 winpr_HexDump("sspi.test", WLOG_DEBUG, (BYTE*)pSecBuffer->pvBuffer, pSecBuffer->cbBuffer);
790 server->haveInputBuffer = TRUE;
791 server->inputBuffer[0].BufferType = SECBUFFER_TOKEN;
792 server->inputBuffer[0].pvBuffer = pSecBuffer->pvBuffer;
793 server->inputBuffer[0].cbBuffer = pSecBuffer->cbBuffer;
794 status = test_ntlm_server_authenticate(arg, server);
795
796 if (status < 0)
797 {
798 printf("test_ntlm_server_authenticate failure\n");
799 goto fail;
800 }
801
802 if (server->haveContext)
803 {
804 if (!test_attributes(server->table, &server->context))
805 goto fail;
806 }
807
808 if (!arg->dynamic)
809 {
810 SecPkgContext_AuthNtlmTimestamp AuthNtlmTimestamp = WINPR_C_ARRAY_INIT;
811 SecPkgContext_AuthNtlmClientChallenge AuthNtlmClientChallenge = WINPR_C_ARRAY_INIT;
812 SecPkgContext_AuthNtlmServerChallenge AuthNtlmServerChallenge = WINPR_C_ARRAY_INIT;
813 CopyMemory(AuthNtlmTimestamp.Timestamp, TEST_NTLM_TIMESTAMP, 8);
814 AuthNtlmTimestamp.ChallengeOrResponse = TRUE;
815 SECURITY_STATUS rc = client->table->SetContextAttributes(
816 &server->context, SECPKG_ATTR_AUTH_NTLM_TIMESTAMP, &AuthNtlmTimestamp,
818 WINPR_ASSERT(rc == SEC_E_OK);
819
820 AuthNtlmTimestamp.ChallengeOrResponse = FALSE;
821 rc = client->table->SetContextAttributes(&server->context, SECPKG_ATTR_AUTH_NTLM_TIMESTAMP,
822 &AuthNtlmTimestamp,
824 WINPR_ASSERT(rc == SEC_E_OK);
825
826 CopyMemory(AuthNtlmClientChallenge.ClientChallenge, TEST_NTLM_CLIENT_CHALLENGE, 8);
827 CopyMemory(AuthNtlmServerChallenge.ServerChallenge, TEST_NTLM_SERVER_CHALLENGE, 8);
828 rc = server->table->SetContextAttributes(
829 &server->context, SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE, &AuthNtlmClientChallenge,
831 WINPR_ASSERT(rc == SEC_E_OK);
832
833 rc = server->table->SetContextAttributes(
834 &server->context, SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE, &AuthNtlmServerChallenge,
836 WINPR_ASSERT(rc == SEC_E_OK);
837 }
838
839 pSecBuffer = &(server->outputBuffer[0]);
840
841 if (!arg->dynamic)
842 {
843 SecPkgContext_AuthNtlmMessage AuthNtlmMessage = WINPR_C_ARRAY_INIT;
844 pSecBuffer->cbBuffer = sizeof(TEST_NTLM_CHALLENGE) - 1;
845 free(pSecBuffer->pvBuffer);
846 pSecBuffer->pvBuffer = malloc(pSecBuffer->cbBuffer);
847
848 if (!pSecBuffer->pvBuffer)
849 {
850 printf("Memory allocation failed\n");
851 goto fail;
852 }
853
854 CopyMemory(pSecBuffer->pvBuffer, TEST_NTLM_CHALLENGE, pSecBuffer->cbBuffer);
855 AuthNtlmMessage.type = 2;
856 AuthNtlmMessage.length = pSecBuffer->cbBuffer;
857 AuthNtlmMessage.buffer = (BYTE*)pSecBuffer->pvBuffer;
858 SECURITY_STATUS rc = server->table->SetContextAttributes(
859 &server->context, SECPKG_ATTR_AUTH_NTLM_MESSAGE, &AuthNtlmMessage,
861 if (rc != SEC_E_OK)
862 goto fail;
863 }
864
865 (void)fprintf(stderr, "NTLM_CHALLENGE (length = %" PRIu32 "):\n", pSecBuffer->cbBuffer);
866 winpr_HexDump("sspi.test", WLOG_DEBUG, (BYTE*)pSecBuffer->pvBuffer, pSecBuffer->cbBuffer);
871 client->haveInputBuffer = TRUE;
872 client->inputBuffer[0].BufferType = SECBUFFER_TOKEN;
873 client->inputBuffer[0].pvBuffer = pSecBuffer->pvBuffer;
874 client->inputBuffer[0].cbBuffer = pSecBuffer->cbBuffer;
875 status = test_ntlm_client_authenticate(client);
876
877 if (status < 0)
878 {
879 printf("test_ntlm_client_authenticate failure\n");
880 goto fail;
881 }
882
883 pSecBuffer = &(client->outputBuffer[0]);
884
885 if (!arg->dynamic)
886 {
887 pSecBuffer->cbBuffer = sizeof(TEST_NTLM_AUTHENTICATE) - 1;
888 free(pSecBuffer->pvBuffer);
889 pSecBuffer->pvBuffer = malloc(pSecBuffer->cbBuffer);
890
891 if (!pSecBuffer->pvBuffer)
892 {
893 printf("Memory allocation failed\n");
894 goto fail;
895 }
896
897 CopyMemory(pSecBuffer->pvBuffer, TEST_NTLM_AUTHENTICATE, pSecBuffer->cbBuffer);
898 }
899
900 (void)fprintf(stderr, "NTLM_AUTHENTICATE (length = %" PRIu32 "):\n", pSecBuffer->cbBuffer);
901 winpr_HexDump("sspi.test", WLOG_DEBUG, (BYTE*)pSecBuffer->pvBuffer, pSecBuffer->cbBuffer);
905 server->haveInputBuffer = TRUE;
906 server->inputBuffer[0].BufferType = SECBUFFER_TOKEN;
907 server->inputBuffer[0].pvBuffer = pSecBuffer->pvBuffer;
908 server->inputBuffer[0].cbBuffer = pSecBuffer->cbBuffer;
909 status = test_ntlm_server_authenticate(arg, server);
910
911 if (status < 0)
912 {
913 printf("test_ntlm_server_authenticate failure\n");
914 goto fail;
915 }
916
917 rc = TRUE;
918
919fail:
923 test_ntlm_client_free(client);
924 test_ntlm_server_free(server);
925
926 printf("testcase {user=%s, domain=%s, password=%s, dynamic=%s} returns %d\n", arg->user,
927 arg->domain, arg->pwd, arg->dynamic ? "TRUE" : "FALSE", rc);
928 return rc;
929}
930
931WINPR_ATTR_NODISCARD
932static SECURITY_STATUS testCallback(void* client, const SEC_WINNT_AUTH_IDENTITY* authIdentity,
933 const SecBuffer* ntproofvalue, const BYTE* randkey,
934 const BYTE* mic, const SecBuffer* micvalue, BYTE* ntlmhash)
935{
936 const struct test_input_t* arg = client;
937 WINPR_ASSERT(arg);
938 WINPR_ASSERT(authIdentity);
939 WINPR_ASSERT(ntproofvalue);
940 WINPR_ASSERT(randkey);
941 WINPR_ASSERT(mic);
942 WINPR_ASSERT(micvalue);
943 WINPR_ASSERT(ntlmhash);
944
945 if ((authIdentity->Flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) != 0)
946 {
947 if (!NTOWFv2FromHashW(arg->ntlm, authIdentity->User, authIdentity->UserLength * 2,
948 authIdentity->Domain, authIdentity->DomainLength * 2, ntlmhash))
949 return SEC_E_ENCRYPT_FAILURE;
950 }
951 else
952 {
953 if (!NTOWFv2FromHashA(arg->ntlm, (char*)authIdentity->User, authIdentity->UserLength,
954 (char*)authIdentity->Domain, authIdentity->DomainLength, ntlmhash))
955 return SEC_E_ENCRYPT_FAILURE;
956 }
957
958 if (memcmp(ntlmhash, arg->ntlmv2, 16) != 0)
959 return SEC_E_DECRYPT_FAILURE;
960
961 return SEC_E_OK;
962}
963
964WINPR_ATTR_NODISCARD
965static SECURITY_STATUS testFailCallback(void* client, const SEC_WINNT_AUTH_IDENTITY* authIdentity,
966 const SecBuffer* ntproofvalue, const BYTE* randkey,
967 const BYTE* mic, const SecBuffer* micvalue, BYTE* ntlmhash)
968{
969 const struct test_input_t* arg = client;
970 WINPR_ASSERT(arg);
971 WINPR_ASSERT(authIdentity);
972 WINPR_ASSERT(ntproofvalue);
973 WINPR_ASSERT(randkey);
974 WINPR_ASSERT(mic);
975 WINPR_ASSERT(micvalue);
976 WINPR_ASSERT(ntlmhash);
977
978 if ((authIdentity->Flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) != 0)
979 {
980 if (!NTOWFv2FromHashW(arg->ntlm, authIdentity->User, authIdentity->UserLength * 2,
981 authIdentity->Domain, authIdentity->DomainLength * 2, ntlmhash))
982 return SEC_E_ENCRYPT_FAILURE;
983 }
984 else
985 {
986 if (!NTOWFv2FromHashA(arg->ntlm, (char*)authIdentity->User, authIdentity->UserLength,
987 (char*)authIdentity->Domain, authIdentity->DomainLength, ntlmhash))
988 return SEC_E_ENCRYPT_FAILURE;
989 }
990
991 if (memcmp(ntlmhash, arg->ntlmv2, 16) != 0)
992 return SEC_E_DECRYPT_FAILURE;
993
994 return SEC_E_DECRYPT_FAILURE;
995}
996
997int TestSspiNTLM(int argc, char* argv[])
998{
999 WINPR_UNUSED(argc);
1000 WINPR_UNUSED(argv);
1001
1002 const struct test_input_t inputs[] = {
1003 { TEST_NTLM_USER, TEST_NTLM_DOMAIN, TEST_NTLM_PASSWORD, TEST_NTLM_HASH, TEST_NTLM_V2_HASH,
1004 TRUE, TRUE },
1005 { TEST_NTLM_USER, TEST_NTLM_DOMAIN, TEST_NTLM_PASSWORD, TEST_NTLM_HASH, TEST_NTLM_V2_HASH,
1006 FALSE, TRUE },
1007 { TEST_NTLM_USER, TEST_NTLM_DOMAIN, "", TEST_EMPTY_PWD_NTLM_HASH,
1008 TEST_EMPTY_PWD_NTLM_V2_HASH, TRUE, TRUE },
1009 { TEST_NTLM_USER, TEST_NTLM_DOMAIN, nullptr, TEST_EMPTY_PWD_NTLM_HASH,
1010 TEST_EMPTY_PWD_NTLM_V2_HASH, TRUE, FALSE }
1011 };
1012
1013 for (size_t x = 0; x < ARRAYSIZE(inputs); x++)
1014 {
1015 const struct test_input_t* cur = &inputs[x];
1016 const BOOL res = test_default(cur, nullptr);
1017 if (res != cur->expected)
1018 {
1019 printf("%s [%" PRIuz "] fail 1!\n", __func__, x);
1020 return -1;
1021 }
1022 const BOOL res2 = test_default(cur, testCallback);
1023 if (res2 != cur->expected)
1024 {
1025 printf("%s [%" PRIuz "] fail 2!\n", __func__, x);
1026 return -2;
1027 }
1028 const BOOL res3 = test_default(cur, testFailCallback);
1029 if (res3 != FALSE)
1030 {
1031 printf("%s [%" PRIuz "] fail 2!\n", __func__, x);
1032 return -2;
1033 }
1034 }
1035 printf("%s success!\n", __func__);
1036 return 0;
1037}