FreeRDP
Loading...
Searching...
No Matches
sspi/test/TestNTLM.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
9struct test_input_t
10{
11 const char* user;
12 const char* domain;
13 const char* pwd;
14 const BYTE* ntlm;
15 const BYTE* ntlmv2;
16 BOOL dynamic;
17 BOOL expected;
18};
19
20typedef struct
21{
22 CtxtHandle context;
23 ULONG cbMaxToken;
24 ULONG fContextReq;
25 ULONG pfContextAttr;
26 TimeStamp expiration;
27 PSecBuffer pBuffer;
28 SecBuffer inputBuffer[2];
29 SecBuffer outputBuffer[2];
30 BOOL haveContext;
31 BOOL haveInputBuffer;
32 BOOL UseNtlmV2Hash;
33 BOOL UseCallback;
34 LPTSTR ServicePrincipalName;
35 SecBufferDesc inputBufferDesc;
36 SecBufferDesc outputBufferDesc;
37 CredHandle credentials;
38 BOOL confidentiality;
39 SecPkgInfo* pPackageInfo;
40 SecurityFunctionTable* table;
43} TEST_NTLM_SERVER;
44
45static BYTE TEST_NTLM_TIMESTAMP[8] = { 0x33, 0x57, 0xbd, 0xb1, 0x07, 0x8b, 0xcf, 0x01 };
46
47static BYTE TEST_NTLM_CLIENT_CHALLENGE[8] = { 0x20, 0xc0, 0x2b, 0x3d, 0xc0, 0x61, 0xa7, 0x73 };
48
49static BYTE TEST_NTLM_SERVER_CHALLENGE[8] = { 0xa4, 0xf1, 0xba, 0xa6, 0x7c, 0xdc, 0x1a, 0x12 };
50
51static BYTE TEST_NTLM_NEGOTIATE[] =
52 "\x4e\x54\x4c\x4d\x53\x53\x50\x00\x01\x00\x00\x00\x07\x82\x08\xa2"
53 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
54 "\x06\x03\x80\x25\x00\x00\x00\x0f";
55
56static BYTE TEST_NTLM_CHALLENGE[] =
57 "\x4e\x54\x4c\x4d\x53\x53\x50\x00\x02\x00\x00\x00\x00\x00\x00\x00"
58 "\x38\x00\x00\x00\x07\x82\x88\xa2\xa4\xf1\xba\xa6\x7c\xdc\x1a\x12"
59 "\x00\x00\x00\x00\x00\x00\x00\x00\x66\x00\x66\x00\x38\x00\x00\x00"
60 "\x06\x03\x80\x25\x00\x00\x00\x0f\x02\x00\x0e\x00\x4e\x00\x45\x00"
61 "\x57\x00\x59\x00\x45\x00\x41\x00\x52\x00\x01\x00\x0e\x00\x4e\x00"
62 "\x45\x00\x57\x00\x59\x00\x45\x00\x41\x00\x52\x00\x04\x00\x1c\x00"
63 "\x6c\x00\x61\x00\x62\x00\x2e\x00\x77\x00\x61\x00\x79\x00\x6b\x00"
64 "\x2e\x00\x6c\x00\x6f\x00\x63\x00\x61\x00\x6c\x00\x03\x00\x0e\x00"
65 "\x6e\x00\x65\x00\x77\x00\x79\x00\x65\x00\x61\x00\x72\x00\x07\x00"
66 "\x08\x00\x33\x57\xbd\xb1\x07\x8b\xcf\x01\x00\x00\x00\x00";
67
68static BYTE TEST_NTLM_AUTHENTICATE[] =
69 "\x4e\x54\x4c\x4d\x53\x53\x50\x00\x03\x00\x00\x00\x18\x00\x18\x00"
70 "\x82\x00\x00\x00\x08\x01\x08\x01\x9a\x00\x00\x00\x0c\x00\x0c\x00"
71 "\x58\x00\x00\x00\x10\x00\x10\x00\x64\x00\x00\x00\x0e\x00\x0e\x00"
72 "\x74\x00\x00\x00\x00\x00\x00\x00\xa2\x01\x00\x00\x05\x82\x88\xa2"
73 "\x06\x03\x80\x25\x00\x00\x00\x0f\x12\xe5\x5a\xf5\x80\xee\x3f\x29"
74 "\xe1\xde\x90\x4d\x73\x77\x06\x25\x44\x00\x6f\x00\x6d\x00\x61\x00"
75 "\x69\x00\x6e\x00\x55\x00\x73\x00\x65\x00\x72\x00\x6e\x00\x61\x00"
76 "\x6d\x00\x65\x00\x4e\x00\x45\x00\x57\x00\x59\x00\x45\x00\x41\x00"
77 "\x52\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
78 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x62\x14\x68\xc8\x98\x12"
79 "\xe7\x39\xd8\x76\x1b\xe9\xf7\x54\xb5\xe3\x01\x01\x00\x00\x00\x00"
80 "\x00\x00\x33\x57\xbd\xb1\x07\x8b\xcf\x01\x20\xc0\x2b\x3d\xc0\x61"
81 "\xa7\x73\x00\x00\x00\x00\x02\x00\x0e\x00\x4e\x00\x45\x00\x57\x00"
82 "\x59\x00\x45\x00\x41\x00\x52\x00\x01\x00\x0e\x00\x4e\x00\x45\x00"
83 "\x57\x00\x59\x00\x45\x00\x41\x00\x52\x00\x04\x00\x1c\x00\x6c\x00"
84 "\x61\x00\x62\x00\x2e\x00\x77\x00\x61\x00\x79\x00\x6b\x00\x2e\x00"
85 "\x6c\x00\x6f\x00\x63\x00\x61\x00\x6c\x00\x03\x00\x0e\x00\x6e\x00"
86 "\x65\x00\x77\x00\x79\x00\x65\x00\x61\x00\x72\x00\x07\x00\x08\x00"
87 "\x33\x57\xbd\xb1\x07\x8b\xcf\x01\x06\x00\x04\x00\x02\x00\x00\x00"
88 "\x08\x00\x30\x00\x30\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00"
89 "\x00\x20\x00\x00\x1e\x10\xf5\x2c\x54\x2f\x2e\x77\x1c\x13\xbf\xc3"
90 "\x3f\xe1\x7b\x28\x7e\x0b\x93\x5a\x39\xd2\xce\x12\xd7\xbd\x8c\x4e"
91 "\x2b\xb5\x0b\xf5\x0a\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00"
92 "\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x1a\x00\x48\x00\x54\x00"
93 "\x54\x00\x50\x00\x2f\x00\x72\x00\x77\x00\x2e\x00\x6c\x00\x6f\x00"
94 "\x63\x00\x61\x00\x6c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
95 "\x00\x00";
96
97#define TEST_SSPI_INTERFACE SSPI_INTERFACE_WINPR
98
99static const char* TEST_NTLM_USER = "Username";
100static const char* TEST_NTLM_DOMAIN = "Domain";
101static const char* TEST_NTLM_PASSWORD = "P4ss123!";
102
103// static const char* TEST_NTLM_HASH_STRING = "d5922a65c4d5c082ca444af1be0001db";
104
105static const BYTE TEST_NTLM_HASH[16] = { 0xd5, 0x92, 0x2a, 0x65, 0xc4, 0xd5, 0xc0, 0x82,
106 0xca, 0x44, 0x4a, 0xf1, 0xbe, 0x00, 0x01, 0xdb };
107
108// static const char* TEST_NTLM_HASH_V2_STRING = "4c7f706f7dde05a9d1a0f4e7ffe3bfb8";
109
110static const BYTE TEST_NTLM_V2_HASH[16] = { 0x4c, 0x7f, 0x70, 0x6f, 0x7d, 0xde, 0x05, 0xa9,
111 0xd1, 0xa0, 0xf4, 0xe7, 0xff, 0xe3, 0xbf, 0xb8 };
112
113static const BYTE TEST_EMPTY_PWD_NTLM_HASH[] = { 0x31, 0xd6, 0xcf, 0xe0, 0xd1, 0x6a, 0xe9, 0x31,
114 0xb7, 0x3c, 0x59, 0xd7, 0xe0, 0xc0, 0x89, 0xc0 };
115
116static const BYTE TEST_EMPTY_PWD_NTLM_V2_HASH[] = {
117 0x0b, 0xce, 0x54, 0x87, 0x4e, 0x94, 0x20, 0x9e, 0x34, 0x48, 0x97, 0xc1, 0x60, 0x03, 0x6e, 0x3b
118};
119
120#define NTLM_PACKAGE_NAME NTLM_SSP_NAME
121
122typedef struct
123{
124 CtxtHandle context;
125 ULONG cbMaxToken;
126 ULONG fContextReq;
127 ULONG pfContextAttr;
128 TimeStamp expiration;
129 PSecBuffer pBuffer;
130 SecBuffer inputBuffer[2];
131 SecBuffer outputBuffer[2];
132 BOOL haveContext;
133 BOOL haveInputBuffer;
134 LPTSTR ServicePrincipalName;
135 SecBufferDesc inputBufferDesc;
136 SecBufferDesc outputBufferDesc;
137 CredHandle credentials;
138 BOOL confidentiality;
139 SecPkgInfo* pPackageInfo;
140 SecurityFunctionTable* table;
141 SEC_WINNT_AUTH_IDENTITY identity;
142} TEST_NTLM_CLIENT;
143
144WINPR_ATTR_NODISCARD
145static void* getServerAuthData(TEST_NTLM_SERVER* ntlm, const struct test_input_t* arg,
146 psSspiNtlmHashCallback fkt)
147{
148
149 if (fkt)
150 {
151 ntlm->authData.identity.Version = SEC_WINNT_AUTH_IDENTITY_VERSION;
152 ntlm->authData.identity.Length = sizeof(SEC_WINNT_AUTH_IDENTITY_EX);
153 ntlm->authData.identity.Flags |=
154 SEC_WINNT_AUTH_IDENTITY_EXTENDED | SEC_WINNT_AUTH_IDENTITY_UNICODE;
155 ntlm->settings.hashCallback = fkt;
156 ntlm->settings.hashCallbackArg = arg;
157 ntlm->authData.ntlmSettings = &ntlm->settings;
158 ntlm->UseCallback = TRUE;
159 return &ntlm->authData;
160 }
161 ntlm->UseCallback = FALSE;
162 return nullptr;
163}
164
165static int test_ntlm_client_init(TEST_NTLM_CLIENT* ntlm, const char* user, const char* domain,
166 const char* password)
167{
168 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
169
170 WINPR_ASSERT(ntlm);
171
172 SecInvalidateHandle(&(ntlm->context));
173 ntlm->table = InitSecurityInterfaceEx(TEST_SSPI_INTERFACE);
174 if (!ntlm->table)
175 return -1;
176 const int rc = sspi_SetAuthIdentity(&(ntlm->identity), user, domain, password);
177 if (rc < 0)
178 return rc;
179
180 status = ntlm->table->QuerySecurityPackageInfo(NTLM_PACKAGE_NAME, &ntlm->pPackageInfo);
181
182 if (status != SEC_E_OK)
183 {
184 (void)fprintf(stderr, "QuerySecurityPackageInfo status: %s (0x%08" PRIX32 ")\n",
185 GetSecurityStatusString(status), status);
186 return -1;
187 }
188
189 ntlm->cbMaxToken = ntlm->pPackageInfo->cbMaxToken;
190 status = ntlm->table->AcquireCredentialsHandle(nullptr, NTLM_PACKAGE_NAME, SECPKG_CRED_OUTBOUND,
191 nullptr, &ntlm->identity, nullptr, nullptr,
192 &ntlm->credentials, &ntlm->expiration);
193
194 if (status != SEC_E_OK)
195 {
196 (void)fprintf(stderr, "AcquireCredentialsHandle status: %s (0x%08" PRIX32 ")\n",
197 GetSecurityStatusString(status), status);
198 return -1;
199 }
200
201 ntlm->haveContext = FALSE;
202 ntlm->haveInputBuffer = FALSE;
203 ZeroMemory(&ntlm->inputBuffer, sizeof(SecBuffer));
204 ZeroMemory(&ntlm->outputBuffer, sizeof(SecBuffer));
205 ntlm->fContextReq = 0;
206
207 /* NLA authentication flags */
208 ntlm->fContextReq |= ISC_REQ_MUTUAL_AUTH;
209 ntlm->fContextReq |= ISC_REQ_CONFIDENTIALITY;
210 ntlm->fContextReq |= ISC_REQ_USE_SESSION_KEY;
211 return 1;
212}
213
214static void test_ntlm_client_uninit(TEST_NTLM_CLIENT* ntlm)
215{
216 if (!ntlm)
217 return;
218
219 if (ntlm->outputBuffer[0].pvBuffer)
220 {
221 free(ntlm->outputBuffer[0].pvBuffer);
222 ntlm->outputBuffer[0].pvBuffer = nullptr;
223 }
224
225 free(ntlm->identity.User);
226 free(ntlm->identity.Domain);
227 free(ntlm->identity.Password);
228 free(ntlm->ServicePrincipalName);
229
230 if (ntlm->table)
231 {
232 SECURITY_STATUS status = ntlm->table->FreeCredentialsHandle(&ntlm->credentials);
233 WINPR_ASSERT((status == SEC_E_OK) || (status == SEC_E_SECPKG_NOT_FOUND) ||
234 (status == SEC_E_UNSUPPORTED_FUNCTION));
235
236 status = ntlm->table->FreeContextBuffer(ntlm->pPackageInfo);
237 WINPR_ASSERT((status == SEC_E_OK) || (status = SEC_E_INVALID_HANDLE));
238
239 status = ntlm->table->DeleteSecurityContext(&ntlm->context);
240 WINPR_ASSERT((status == SEC_E_OK) || (status == SEC_E_SECPKG_NOT_FOUND) ||
241 (status == SEC_E_UNSUPPORTED_FUNCTION));
242 }
243}
244
282static BOOL IsSecurityStatusError(SECURITY_STATUS status)
283{
284 BOOL error = TRUE;
285
286 switch (status)
287 {
288 case SEC_E_OK:
289 case SEC_I_CONTINUE_NEEDED:
290 case SEC_I_COMPLETE_NEEDED:
291 case SEC_I_COMPLETE_AND_CONTINUE:
292 case SEC_I_LOCAL_LOGON:
293 case SEC_I_CONTEXT_EXPIRED:
294 case SEC_I_INCOMPLETE_CREDENTIALS:
295 case SEC_I_RENEGOTIATE:
296 case SEC_I_NO_LSA_CONTEXT:
297 case SEC_I_SIGNATURE_NEEDED:
298 case SEC_I_NO_RENEGOTIATION:
299 error = FALSE;
300 break;
301 default:
302 break;
303 }
304
305 return error;
306}
307
308static int test_ntlm_client_authenticate(TEST_NTLM_CLIENT* ntlm)
309{
310 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
311
312 WINPR_ASSERT(ntlm);
313 if (ntlm->outputBuffer[0].pvBuffer)
314 {
315 free(ntlm->outputBuffer[0].pvBuffer);
316 ntlm->outputBuffer[0].pvBuffer = nullptr;
317 }
318
319 ntlm->outputBufferDesc.ulVersion = SECBUFFER_VERSION;
320 ntlm->outputBufferDesc.cBuffers = 1;
321 ntlm->outputBufferDesc.pBuffers = ntlm->outputBuffer;
322 ntlm->outputBuffer[0].BufferType = SECBUFFER_TOKEN;
323 ntlm->outputBuffer[0].cbBuffer = ntlm->cbMaxToken;
324 ntlm->outputBuffer[0].pvBuffer = malloc(ntlm->outputBuffer[0].cbBuffer);
325
326 if (!ntlm->outputBuffer[0].pvBuffer)
327 return -1;
328
329 if (ntlm->haveInputBuffer)
330 {
331 ntlm->inputBufferDesc.ulVersion = SECBUFFER_VERSION;
332 ntlm->inputBufferDesc.cBuffers = 1;
333 ntlm->inputBufferDesc.pBuffers = ntlm->inputBuffer;
334 ntlm->inputBuffer[0].BufferType = SECBUFFER_TOKEN;
335 }
336
337 if ((!ntlm) || (!ntlm->table))
338 {
339 (void)fprintf(stderr, "ntlm_authenticate: invalid ntlm context\n");
340 return -1;
341 }
342
343 status = ntlm->table->InitializeSecurityContext(
344 &ntlm->credentials, (ntlm->haveContext) ? &ntlm->context : nullptr,
345 (ntlm->ServicePrincipalName) ? ntlm->ServicePrincipalName : nullptr, ntlm->fContextReq, 0,
346 SECURITY_NATIVE_DREP, (ntlm->haveInputBuffer) ? &ntlm->inputBufferDesc : nullptr, 0,
347 &ntlm->context, &ntlm->outputBufferDesc, &ntlm->pfContextAttr, &ntlm->expiration);
348
349 if (IsSecurityStatusError(status))
350 return -1;
351
352 if ((status == SEC_I_COMPLETE_AND_CONTINUE) || (status == SEC_I_COMPLETE_NEEDED))
353 {
354 if (ntlm->table->CompleteAuthToken)
355 {
356 SECURITY_STATUS rc =
357 ntlm->table->CompleteAuthToken(&ntlm->context, &ntlm->outputBufferDesc);
358 if (rc != SEC_E_OK)
359 return -1;
360 }
361
362 if (status == SEC_I_COMPLETE_NEEDED)
363 status = SEC_E_OK;
364 else if (status == SEC_I_COMPLETE_AND_CONTINUE)
365 status = SEC_I_CONTINUE_NEEDED;
366 }
367
368 if (ntlm->haveInputBuffer)
369 {
370 free(ntlm->inputBuffer[0].pvBuffer);
371 }
372
373 ntlm->haveInputBuffer = TRUE;
374 ntlm->haveContext = TRUE;
375 return (status == SEC_I_CONTINUE_NEEDED) ? 1 : 0;
376}
377
378static void test_ntlm_client_free(TEST_NTLM_CLIENT* ntlm)
379{
380 if (!ntlm)
381 return;
382
383 test_ntlm_client_uninit(ntlm);
384 free(ntlm);
385}
386
387WINPR_ATTR_MALLOC(test_ntlm_client_free, 1)
388static TEST_NTLM_CLIENT* test_ntlm_client_new(void)
389{
390 TEST_NTLM_CLIENT* ntlm = (TEST_NTLM_CLIENT*)calloc(1, sizeof(TEST_NTLM_CLIENT));
391
392 if (!ntlm)
393 return nullptr;
394
395 return ntlm;
396}
397
398static int test_ntlm_server_init(TEST_NTLM_SERVER* ntlm, const struct test_input_t* arg,
399 psSspiNtlmHashCallback fkt)
400{
401 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
402
403 WINPR_ASSERT(ntlm);
404
405 ntlm->UseNtlmV2Hash = TRUE;
406 SecInvalidateHandle(&(ntlm->context));
407 ntlm->table = InitSecurityInterfaceEx(TEST_SSPI_INTERFACE);
408 if (!ntlm->table)
409 return SEC_E_INTERNAL_ERROR;
410
411 status = ntlm->table->QuerySecurityPackageInfo(NTLM_PACKAGE_NAME, &ntlm->pPackageInfo);
412
413 if (status != SEC_E_OK)
414 {
415 (void)fprintf(stderr, "QuerySecurityPackageInfo status: %s (0x%08" PRIX32 ")\n",
416 GetSecurityStatusString(status), status);
417 return -1;
418 }
419
420 void* authData = getServerAuthData(ntlm, arg, fkt);
421 ntlm->cbMaxToken = ntlm->pPackageInfo->cbMaxToken;
422 status = ntlm->table->AcquireCredentialsHandle(nullptr, NTLM_PACKAGE_NAME, SECPKG_CRED_INBOUND,
423 nullptr, authData, nullptr, nullptr,
424 &ntlm->credentials, &ntlm->expiration);
425
426 if (status != SEC_E_OK)
427 {
428 (void)fprintf(stderr, "AcquireCredentialsHandle status: %s (0x%08" PRIX32 ")\n",
429 GetSecurityStatusString(status), status);
430 return -1;
431 }
432
433 ntlm->haveContext = FALSE;
434 ntlm->haveInputBuffer = FALSE;
435 ZeroMemory(&ntlm->inputBuffer, sizeof(SecBuffer));
436 ZeroMemory(&ntlm->outputBuffer, sizeof(SecBuffer));
437 ntlm->fContextReq = 0;
438 /* NLA authentication flags */
439 ntlm->fContextReq |= ASC_REQ_MUTUAL_AUTH;
440 ntlm->fContextReq |= ASC_REQ_CONFIDENTIALITY;
441 ntlm->fContextReq |= ASC_REQ_CONNECTION;
442 ntlm->fContextReq |= ASC_REQ_USE_SESSION_KEY;
443 ntlm->fContextReq |= ASC_REQ_REPLAY_DETECT;
444 ntlm->fContextReq |= ASC_REQ_SEQUENCE_DETECT;
445 ntlm->fContextReq |= ASC_REQ_EXTENDED_ERROR;
446 return 1;
447}
448
449static void test_ntlm_server_uninit(TEST_NTLM_SERVER* ntlm)
450{
451 if (!ntlm)
452 return;
453
454 if (ntlm->outputBuffer[0].pvBuffer)
455 {
456 free(ntlm->outputBuffer[0].pvBuffer);
457 ntlm->outputBuffer[0].pvBuffer = nullptr;
458 }
459
460 free(ntlm->ServicePrincipalName);
461
462 if (ntlm->table)
463 {
464 SECURITY_STATUS status = ntlm->table->FreeCredentialsHandle(&ntlm->credentials);
465 WINPR_ASSERT(status == SEC_E_OK);
466 status = ntlm->table->FreeContextBuffer(ntlm->pPackageInfo);
467 WINPR_ASSERT(status == SEC_E_OK);
468 status = ntlm->table->DeleteSecurityContext(&ntlm->context);
469 WINPR_ASSERT(status == SEC_E_OK);
470 }
471}
472
473static int test_ntlm_server_authenticate(const struct test_input_t* targ, TEST_NTLM_SERVER* ntlm)
474{
475 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
476
477 WINPR_ASSERT(ntlm);
478 WINPR_ASSERT(targ);
479
480 ntlm->inputBufferDesc.ulVersion = SECBUFFER_VERSION;
481 ntlm->inputBufferDesc.cBuffers = 1;
482 ntlm->inputBufferDesc.pBuffers = ntlm->inputBuffer;
483 ntlm->inputBuffer[0].BufferType = SECBUFFER_TOKEN;
484 ntlm->outputBufferDesc.ulVersion = SECBUFFER_VERSION;
485 ntlm->outputBufferDesc.cBuffers = 1;
486 ntlm->outputBufferDesc.pBuffers = &ntlm->outputBuffer[0];
487 ntlm->outputBuffer[0].BufferType = SECBUFFER_TOKEN;
488 ntlm->outputBuffer[0].cbBuffer = ntlm->cbMaxToken;
489 ntlm->outputBuffer[0].pvBuffer = malloc(ntlm->outputBuffer[0].cbBuffer);
490
491 if (!ntlm->outputBuffer[0].pvBuffer)
492 return -1;
493
494 status = ntlm->table->AcceptSecurityContext(
495 &ntlm->credentials, ntlm->haveContext ? &ntlm->context : nullptr, &ntlm->inputBufferDesc,
496 ntlm->fContextReq, SECURITY_NATIVE_DREP, &ntlm->context, &ntlm->outputBufferDesc,
497 &ntlm->pfContextAttr, &ntlm->expiration);
498
499 if (status == SEC_I_CONTINUE_NEEDED)
500 {
501 SecPkgContext_AuthNtlmHash AuthNtlmHash = WINPR_C_ARRAY_INIT;
502
503 if (!ntlm->UseCallback)
504 {
505 if (ntlm->UseNtlmV2Hash)
506 {
507 AuthNtlmHash.Version = 2;
508 CopyMemory(AuthNtlmHash.NtlmHash, targ->ntlmv2, 16);
509 }
510 else
511 {
512 AuthNtlmHash.Version = 1;
513 CopyMemory(AuthNtlmHash.NtlmHash, targ->ntlm, 16);
514 }
515
516 status = ntlm->table->SetContextAttributes(&ntlm->context, SECPKG_ATTR_AUTH_NTLM_HASH,
517 &AuthNtlmHash,
519 }
520 }
521
522 if ((status != SEC_E_OK) && (status != SEC_I_CONTINUE_NEEDED))
523 {
524 (void)fprintf(stderr, "AcceptSecurityContext status: %s (0x%08" PRIX32 ")\n",
525 GetSecurityStatusString(status), status);
526 return -1; /* Access Denied */
527 }
528
529 ntlm->haveContext = TRUE;
530 return (status == SEC_I_CONTINUE_NEEDED) ? 1 : 0;
531}
532
533static void test_ntlm_server_free(TEST_NTLM_SERVER* ntlm)
534{
535 if (!ntlm)
536 return;
537
538 test_ntlm_server_uninit(ntlm);
539 free(ntlm);
540}
541
542WINPR_ATTR_MALLOC(test_ntlm_server_free, 1)
543static TEST_NTLM_SERVER* test_ntlm_server_new(void)
544{
545 TEST_NTLM_SERVER* ntlm = (TEST_NTLM_SERVER*)calloc(1, sizeof(TEST_NTLM_SERVER));
546
547 if (!ntlm)
548 return nullptr;
549
550 return ntlm;
551}
552
553static BOOL test_default(const struct test_input_t* arg, psSspiNtlmHashCallback fkt)
554{
555 BOOL rc = FALSE;
556 PSecBuffer pSecBuffer = nullptr;
557
558 WINPR_ASSERT(arg);
559
560 printf("testcase {user=%s, domain=%s, password=%s, dynamic=%s}\n", arg->user, arg->domain,
561 arg->pwd, arg->dynamic ? "TRUE" : "FALSE");
562
566 TEST_NTLM_CLIENT* client = test_ntlm_client_new();
567 TEST_NTLM_SERVER* server = test_ntlm_server_new();
568
569 if (!client || !server)
570 {
571 printf("Memory allocation failed\n");
572 goto fail;
573 }
574
575 int status = test_ntlm_client_init(client, arg->user, arg->domain, arg->pwd);
576
577 if (status < 0)
578 {
579 printf("test_ntlm_client_init failure\n");
580 goto fail;
581 }
582
587 status = test_ntlm_server_init(server, arg, fkt);
588
589 if (status < 0)
590 {
591 printf("test_ntlm_server_init failure\n");
592 goto fail;
593 }
594
598 status = test_ntlm_client_authenticate(client);
599
600 if (status < 0)
601 {
602 printf("test_ntlm_client_authenticate failure\n");
603 goto fail;
604 }
605
606 if (!arg->dynamic)
607 {
608 SecPkgContext_AuthNtlmTimestamp AuthNtlmTimestamp = WINPR_C_ARRAY_INIT;
609 SecPkgContext_AuthNtlmClientChallenge AuthNtlmClientChallenge = WINPR_C_ARRAY_INIT;
610 SecPkgContext_AuthNtlmServerChallenge AuthNtlmServerChallenge = WINPR_C_ARRAY_INIT;
611 CopyMemory(AuthNtlmTimestamp.Timestamp, TEST_NTLM_TIMESTAMP, 8);
612 AuthNtlmTimestamp.ChallengeOrResponse = TRUE;
613 SECURITY_STATUS rc = client->table->SetContextAttributes(
614 &client->context, SECPKG_ATTR_AUTH_NTLM_TIMESTAMP, &AuthNtlmTimestamp,
616 WINPR_ASSERT((rc == SEC_E_OK) || (rc == SEC_E_SECPKG_NOT_FOUND));
617
618 AuthNtlmTimestamp.ChallengeOrResponse = FALSE;
619 rc = client->table->SetContextAttributes(&client->context, SECPKG_ATTR_AUTH_NTLM_TIMESTAMP,
620 &AuthNtlmTimestamp,
622 WINPR_ASSERT((rc == SEC_E_OK) || (rc == SEC_E_SECPKG_NOT_FOUND));
623
624 CopyMemory(AuthNtlmClientChallenge.ClientChallenge, TEST_NTLM_CLIENT_CHALLENGE, 8);
625 CopyMemory(AuthNtlmServerChallenge.ServerChallenge, TEST_NTLM_SERVER_CHALLENGE, 8);
626 rc = client->table->SetContextAttributes(
627 &client->context, SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE, &AuthNtlmClientChallenge,
629 WINPR_ASSERT((rc == SEC_E_OK) || (rc == SEC_E_SECPKG_NOT_FOUND));
630
631 rc = client->table->SetContextAttributes(
632 &client->context, SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE, &AuthNtlmServerChallenge,
634 WINPR_ASSERT((rc == SEC_E_OK) || (rc == SEC_E_SECPKG_NOT_FOUND));
635 }
636
637 pSecBuffer = &(client->outputBuffer[0]);
638
639 if (!arg->dynamic)
640 {
641 pSecBuffer->cbBuffer = sizeof(TEST_NTLM_NEGOTIATE) - 1;
642 free(pSecBuffer->pvBuffer);
643 pSecBuffer->pvBuffer = malloc(pSecBuffer->cbBuffer);
644
645 if (!pSecBuffer->pvBuffer)
646 {
647 printf("Memory allocation failed\n");
648 goto fail;
649 }
650
651 CopyMemory(pSecBuffer->pvBuffer, TEST_NTLM_NEGOTIATE, pSecBuffer->cbBuffer);
652 }
653
654 (void)fprintf(stderr, "NTLM_NEGOTIATE (length = %" PRIu32 "):\n", pSecBuffer->cbBuffer);
655 winpr_HexDump("sspi.test", WLOG_DEBUG, (BYTE*)pSecBuffer->pvBuffer, pSecBuffer->cbBuffer);
660 server->haveInputBuffer = TRUE;
661 server->inputBuffer[0].BufferType = SECBUFFER_TOKEN;
662 server->inputBuffer[0].pvBuffer = pSecBuffer->pvBuffer;
663 server->inputBuffer[0].cbBuffer = pSecBuffer->cbBuffer;
664 status = test_ntlm_server_authenticate(arg, server);
665
666 if (status < 0)
667 {
668 printf("test_ntlm_server_authenticate failure\n");
669 goto fail;
670 }
671
672 if (!arg->dynamic)
673 {
674 SecPkgContext_AuthNtlmTimestamp AuthNtlmTimestamp = WINPR_C_ARRAY_INIT;
675 SecPkgContext_AuthNtlmClientChallenge AuthNtlmClientChallenge = WINPR_C_ARRAY_INIT;
676 SecPkgContext_AuthNtlmServerChallenge AuthNtlmServerChallenge = WINPR_C_ARRAY_INIT;
677 CopyMemory(AuthNtlmTimestamp.Timestamp, TEST_NTLM_TIMESTAMP, 8);
678 AuthNtlmTimestamp.ChallengeOrResponse = TRUE;
679 SECURITY_STATUS rc = client->table->SetContextAttributes(
680 &server->context, SECPKG_ATTR_AUTH_NTLM_TIMESTAMP, &AuthNtlmTimestamp,
682 WINPR_ASSERT(rc == SEC_E_OK);
683
684 AuthNtlmTimestamp.ChallengeOrResponse = FALSE;
685 rc = client->table->SetContextAttributes(&server->context, SECPKG_ATTR_AUTH_NTLM_TIMESTAMP,
686 &AuthNtlmTimestamp,
688 WINPR_ASSERT(rc == SEC_E_OK);
689
690 CopyMemory(AuthNtlmClientChallenge.ClientChallenge, TEST_NTLM_CLIENT_CHALLENGE, 8);
691 CopyMemory(AuthNtlmServerChallenge.ServerChallenge, TEST_NTLM_SERVER_CHALLENGE, 8);
692 rc = server->table->SetContextAttributes(
693 &server->context, SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE, &AuthNtlmClientChallenge,
695 WINPR_ASSERT(rc == SEC_E_OK);
696
697 rc = server->table->SetContextAttributes(
698 &server->context, SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE, &AuthNtlmServerChallenge,
700 WINPR_ASSERT(rc == SEC_E_OK);
701 }
702
703 pSecBuffer = &(server->outputBuffer[0]);
704
705 if (!arg->dynamic)
706 {
707 SecPkgContext_AuthNtlmMessage AuthNtlmMessage = WINPR_C_ARRAY_INIT;
708 pSecBuffer->cbBuffer = sizeof(TEST_NTLM_CHALLENGE) - 1;
709 free(pSecBuffer->pvBuffer);
710 pSecBuffer->pvBuffer = malloc(pSecBuffer->cbBuffer);
711
712 if (!pSecBuffer->pvBuffer)
713 {
714 printf("Memory allocation failed\n");
715 goto fail;
716 }
717
718 CopyMemory(pSecBuffer->pvBuffer, TEST_NTLM_CHALLENGE, pSecBuffer->cbBuffer);
719 AuthNtlmMessage.type = 2;
720 AuthNtlmMessage.length = pSecBuffer->cbBuffer;
721 AuthNtlmMessage.buffer = (BYTE*)pSecBuffer->pvBuffer;
722 SECURITY_STATUS rc = server->table->SetContextAttributes(
723 &server->context, SECPKG_ATTR_AUTH_NTLM_MESSAGE, &AuthNtlmMessage,
725 if (rc != SEC_E_OK)
726 goto fail;
727 }
728
729 (void)fprintf(stderr, "NTLM_CHALLENGE (length = %" PRIu32 "):\n", pSecBuffer->cbBuffer);
730 winpr_HexDump("sspi.test", WLOG_DEBUG, (BYTE*)pSecBuffer->pvBuffer, pSecBuffer->cbBuffer);
735 client->haveInputBuffer = TRUE;
736 client->inputBuffer[0].BufferType = SECBUFFER_TOKEN;
737 client->inputBuffer[0].pvBuffer = pSecBuffer->pvBuffer;
738 client->inputBuffer[0].cbBuffer = pSecBuffer->cbBuffer;
739 status = test_ntlm_client_authenticate(client);
740
741 if (status < 0)
742 {
743 printf("test_ntlm_client_authenticate failure\n");
744 goto fail;
745 }
746
747 pSecBuffer = &(client->outputBuffer[0]);
748
749 if (!arg->dynamic)
750 {
751 pSecBuffer->cbBuffer = sizeof(TEST_NTLM_AUTHENTICATE) - 1;
752 free(pSecBuffer->pvBuffer);
753 pSecBuffer->pvBuffer = malloc(pSecBuffer->cbBuffer);
754
755 if (!pSecBuffer->pvBuffer)
756 {
757 printf("Memory allocation failed\n");
758 goto fail;
759 }
760
761 CopyMemory(pSecBuffer->pvBuffer, TEST_NTLM_AUTHENTICATE, pSecBuffer->cbBuffer);
762 }
763
764 (void)fprintf(stderr, "NTLM_AUTHENTICATE (length = %" PRIu32 "):\n", pSecBuffer->cbBuffer);
765 winpr_HexDump("sspi.test", WLOG_DEBUG, (BYTE*)pSecBuffer->pvBuffer, pSecBuffer->cbBuffer);
769 server->haveInputBuffer = TRUE;
770 server->inputBuffer[0].BufferType = SECBUFFER_TOKEN;
771 server->inputBuffer[0].pvBuffer = pSecBuffer->pvBuffer;
772 server->inputBuffer[0].cbBuffer = pSecBuffer->cbBuffer;
773 status = test_ntlm_server_authenticate(arg, server);
774
775 if (status < 0)
776 {
777 printf("test_ntlm_server_authenticate failure\n");
778 goto fail;
779 }
780
781 rc = TRUE;
782
783fail:
787 test_ntlm_client_free(client);
788 test_ntlm_server_free(server);
789
790 printf("testcase {user=%s, domain=%s, password=%s, dynamic=%s} returns %d\n", arg->user,
791 arg->domain, arg->pwd, arg->dynamic ? "TRUE" : "FALSE", rc);
792 return rc;
793}
794
795static SECURITY_STATUS testCallback(void* client, const SEC_WINNT_AUTH_IDENTITY* authIdentity,
796 const SecBuffer* ntproofvalue, const BYTE* randkey,
797 const BYTE* mic, const SecBuffer* micvalue, BYTE* ntlmhash)
798{
799 const struct test_input_t* arg = client;
800 WINPR_ASSERT(arg);
801 WINPR_ASSERT(authIdentity);
802 WINPR_ASSERT(ntproofvalue);
803 WINPR_ASSERT(randkey);
804 WINPR_ASSERT(mic);
805 WINPR_ASSERT(micvalue);
806 WINPR_ASSERT(ntlmhash);
807
808 if ((authIdentity->Flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) != 0)
809 {
810 if (!NTOWFv2FromHashW(arg->ntlm, authIdentity->User, authIdentity->UserLength * 2,
811 authIdentity->Domain, authIdentity->DomainLength * 2, ntlmhash))
812 return SEC_E_ENCRYPT_FAILURE;
813 }
814 else
815 {
816 if (!NTOWFv2FromHashA(arg->ntlm, (char*)authIdentity->User, authIdentity->UserLength,
817 (char*)authIdentity->Domain, authIdentity->DomainLength, ntlmhash))
818 return SEC_E_ENCRYPT_FAILURE;
819 }
820
821 if (memcmp(ntlmhash, arg->ntlmv2, 16) != 0)
822 return SEC_E_DECRYPT_FAILURE;
823
824 return SEC_E_OK;
825}
826
827static SECURITY_STATUS testFailCallback(void* client, const SEC_WINNT_AUTH_IDENTITY* authIdentity,
828 const SecBuffer* ntproofvalue, const BYTE* randkey,
829 const BYTE* mic, const SecBuffer* micvalue, BYTE* ntlmhash)
830{
831 const struct test_input_t* arg = client;
832 WINPR_ASSERT(arg);
833 WINPR_ASSERT(authIdentity);
834 WINPR_ASSERT(ntproofvalue);
835 WINPR_ASSERT(randkey);
836 WINPR_ASSERT(mic);
837 WINPR_ASSERT(micvalue);
838 WINPR_ASSERT(ntlmhash);
839
840 if ((authIdentity->Flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) != 0)
841 {
842 if (!NTOWFv2FromHashW(arg->ntlm, authIdentity->User, authIdentity->UserLength * 2,
843 authIdentity->Domain, authIdentity->DomainLength * 2, ntlmhash))
844 return SEC_E_ENCRYPT_FAILURE;
845 }
846 else
847 {
848 if (!NTOWFv2FromHashA(arg->ntlm, (char*)authIdentity->User, authIdentity->UserLength,
849 (char*)authIdentity->Domain, authIdentity->DomainLength, ntlmhash))
850 return SEC_E_ENCRYPT_FAILURE;
851 }
852
853 if (memcmp(ntlmhash, arg->ntlmv2, 16) != 0)
854 return SEC_E_DECRYPT_FAILURE;
855
856 return SEC_E_DECRYPT_FAILURE;
857}
858
859int TestNTLM(int argc, char* argv[])
860{
861 WINPR_UNUSED(argc);
862 WINPR_UNUSED(argv);
863
864 const struct test_input_t inputs[] = {
865 { TEST_NTLM_USER, TEST_NTLM_DOMAIN, TEST_NTLM_PASSWORD, TEST_NTLM_HASH, TEST_NTLM_V2_HASH,
866 TRUE, TRUE },
867 { TEST_NTLM_USER, TEST_NTLM_DOMAIN, TEST_NTLM_PASSWORD, TEST_NTLM_HASH, TEST_NTLM_V2_HASH,
868 FALSE, TRUE },
869 { TEST_NTLM_USER, TEST_NTLM_DOMAIN, "", TEST_EMPTY_PWD_NTLM_HASH,
870 TEST_EMPTY_PWD_NTLM_V2_HASH, TRUE, TRUE },
871 { TEST_NTLM_USER, TEST_NTLM_DOMAIN, nullptr, TEST_EMPTY_PWD_NTLM_HASH,
872 TEST_EMPTY_PWD_NTLM_V2_HASH, TRUE, FALSE }
873 };
874
875 for (size_t x = 0; x < ARRAYSIZE(inputs); x++)
876 {
877 const struct test_input_t* cur = &inputs[x];
878 const BOOL res = test_default(cur, nullptr);
879 if (res != cur->expected)
880 {
881 printf("%s [%" PRIuz "] fail 1!\n", __func__, x);
882 return -1;
883 }
884 const BOOL res2 = test_default(cur, testCallback);
885 if (res2 != cur->expected)
886 {
887 printf("%s [%" PRIuz "] fail 2!\n", __func__, x);
888 return -2;
889 }
890 const BOOL res3 = test_default(cur, testFailCallback);
891 if (res3 != FALSE)
892 {
893 printf("%s [%" PRIuz "] fail 2!\n", __func__, x);
894 return -2;
895 }
896 }
897 printf("%s success!\n", __func__);
898 return 0;
899}