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 BOOL test_short_signature(SecurityFunctionTable* table, CtxtHandle* context)
264{
265 BYTE data[16] = WINPR_C_ARRAY_INIT;
266 BOOL rc = FALSE;
267
268 WINPR_ASSERT(table);
269 WINPR_ASSERT(context);
270
271 /* An NTLM signature is 16 bytes. A peer may announce a shorter one, so both
272 * MakeSignature and VerifySignature must reject the buffer instead of
273 * accessing the missing bytes. Allocate exactly one byte so that any access
274 * past it is detectable. */
275 BYTE* signature = malloc(1);
276 if (!signature)
277 return FALSE;
278
279 SecBuffer buffers[2] = WINPR_C_ARRAY_INIT;
280 buffers[0].BufferType = SECBUFFER_DATA;
281 buffers[0].pvBuffer = data;
282 buffers[0].cbBuffer = sizeof(data);
283 buffers[1].BufferType = SECBUFFER_TOKEN;
284 buffers[1].pvBuffer = signature;
285 buffers[1].cbBuffer = 1;
286
287 SecBufferDesc desc = { SECBUFFER_VERSION, ARRAYSIZE(buffers), buffers };
288
289 if (table->MakeSignature(context, 0, &desc, 0) == SEC_E_OK)
290 goto fail;
291
292 ULONG qop = 0;
293 if (table->VerifySignature(context, &desc, 0, &qop) == SEC_E_OK)
294 goto fail;
295
296 rc = TRUE;
297fail:
298 free(signature);
299 return rc;
300}
301
302WINPR_ATTR_NODISCARD
303static void* getServerAuthData(TEST_NTLM_SERVER* ntlm, const struct test_input_t* arg,
304 psSspiNtlmHashCallback fkt)
305{
306
307 if (fkt)
308 {
309 ntlm->authData.identity.Version = SEC_WINNT_AUTH_IDENTITY_VERSION;
310 ntlm->authData.identity.Length = sizeof(SEC_WINNT_AUTH_IDENTITY_EX);
311 ntlm->authData.identity.Flags |=
312 SEC_WINNT_AUTH_IDENTITY_EXTENDED | SEC_WINNT_AUTH_IDENTITY_UNICODE;
313 ntlm->settings.hashCallback = fkt;
314 ntlm->settings.hashCallbackArg = arg;
315 ntlm->authData.ntlmSettings = &ntlm->settings;
316 ntlm->UseCallback = TRUE;
317 return &ntlm->authData;
318 }
319 ntlm->UseCallback = FALSE;
320 return nullptr;
321}
322
323WINPR_ATTR_NODISCARD
324static int test_ntlm_client_init(TEST_NTLM_CLIENT* ntlm, const char* user, const char* domain,
325 const char* password)
326{
327 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
328
329 WINPR_ASSERT(ntlm);
330
331 SecInvalidateHandle(&(ntlm->context));
332 ntlm->table = InitSecurityInterfaceEx(TEST_SSPI_INTERFACE);
333 if (!ntlm->table)
334 return -1;
335 const int rc = sspi_SetAuthIdentity(&(ntlm->identity), user, domain, password);
336 if (rc < 0)
337 return rc;
338
339 status = ntlm->table->QuerySecurityPackageInfo(NTLM_PACKAGE_NAME, &ntlm->pPackageInfo);
340
341 if (status != SEC_E_OK)
342 {
343 (void)fprintf(stderr, "QuerySecurityPackageInfo status: %s (0x%08" PRIX32 ")\n",
344 GetSecurityStatusString(status), status);
345 return -1;
346 }
347
348 ntlm->cbMaxToken = ntlm->pPackageInfo->cbMaxToken;
349 status = ntlm->table->AcquireCredentialsHandle(nullptr, NTLM_PACKAGE_NAME, SECPKG_CRED_OUTBOUND,
350 nullptr, &ntlm->identity, nullptr, nullptr,
351 &ntlm->credentials, &ntlm->expiration);
352
353 if (status != SEC_E_OK)
354 {
355 (void)fprintf(stderr, "AcquireCredentialsHandle status: %s (0x%08" PRIX32 ")\n",
356 GetSecurityStatusString(status), status);
357 return -1;
358 }
359
360 ntlm->haveContext = FALSE;
361 ntlm->haveInputBuffer = FALSE;
362 ZeroMemory(&ntlm->inputBuffer, sizeof(SecBuffer));
363 ZeroMemory(&ntlm->outputBuffer, sizeof(SecBuffer));
364 ntlm->fContextReq = 0;
365
366 /* NLA authentication flags */
367 ntlm->fContextReq |= ISC_REQ_MUTUAL_AUTH;
368 ntlm->fContextReq |= ISC_REQ_CONFIDENTIALITY;
369 ntlm->fContextReq |= ISC_REQ_USE_SESSION_KEY;
370 return 1;
371}
372
373static void test_ntlm_client_uninit(TEST_NTLM_CLIENT* ntlm)
374{
375 if (!ntlm)
376 return;
377
378 if (ntlm->outputBuffer[0].pvBuffer)
379 {
380 free(ntlm->outputBuffer[0].pvBuffer);
381 ntlm->outputBuffer[0].pvBuffer = nullptr;
382 }
383
384 free(ntlm->identity.User);
385 free(ntlm->identity.Domain);
386 free(ntlm->identity.Password);
387 free(ntlm->ServicePrincipalName);
388
389 if (ntlm->table)
390 {
391 SECURITY_STATUS status = ntlm->table->FreeCredentialsHandle(&ntlm->credentials);
392 WINPR_ASSERT((status == SEC_E_OK) || (status == SEC_E_SECPKG_NOT_FOUND) ||
393 (status == SEC_E_UNSUPPORTED_FUNCTION));
394
395 status = ntlm->table->FreeContextBuffer(ntlm->pPackageInfo);
396 WINPR_ASSERT((status == SEC_E_OK) || (status = SEC_E_INVALID_HANDLE));
397
398 status = ntlm->table->DeleteSecurityContext(&ntlm->context);
399 WINPR_ASSERT((status == SEC_E_OK) || (status == SEC_E_SECPKG_NOT_FOUND) ||
400 (status == SEC_E_UNSUPPORTED_FUNCTION));
401 }
402}
403
441WINPR_ATTR_NODISCARD
442static BOOL IsSecurityStatusError(SECURITY_STATUS status)
443{
444 BOOL error = TRUE;
445
446 switch (status)
447 {
448 case SEC_E_OK:
449 case SEC_I_CONTINUE_NEEDED:
450 case SEC_I_COMPLETE_NEEDED:
451 case SEC_I_COMPLETE_AND_CONTINUE:
452 case SEC_I_LOCAL_LOGON:
453 case SEC_I_CONTEXT_EXPIRED:
454 case SEC_I_INCOMPLETE_CREDENTIALS:
455 case SEC_I_RENEGOTIATE:
456 case SEC_I_NO_LSA_CONTEXT:
457 case SEC_I_SIGNATURE_NEEDED:
458 case SEC_I_NO_RENEGOTIATION:
459 error = FALSE;
460 break;
461 default:
462 break;
463 }
464
465 return error;
466}
467
468WINPR_ATTR_NODISCARD
469static int test_ntlm_client_authenticate(TEST_NTLM_CLIENT* ntlm)
470{
471 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
472
473 WINPR_ASSERT(ntlm);
474 if (ntlm->outputBuffer[0].pvBuffer)
475 {
476 free(ntlm->outputBuffer[0].pvBuffer);
477 ntlm->outputBuffer[0].pvBuffer = nullptr;
478 }
479
480 ntlm->outputBufferDesc.ulVersion = SECBUFFER_VERSION;
481 ntlm->outputBufferDesc.cBuffers = 1;
482 ntlm->outputBufferDesc.pBuffers = ntlm->outputBuffer;
483 ntlm->outputBuffer[0].BufferType = SECBUFFER_TOKEN;
484 ntlm->outputBuffer[0].cbBuffer = ntlm->cbMaxToken;
485 ntlm->outputBuffer[0].pvBuffer = malloc(ntlm->outputBuffer[0].cbBuffer);
486
487 if (!ntlm->outputBuffer[0].pvBuffer)
488 return -1;
489
490 if (ntlm->haveInputBuffer)
491 {
492 ntlm->inputBufferDesc.ulVersion = SECBUFFER_VERSION;
493 ntlm->inputBufferDesc.cBuffers = 1;
494 ntlm->inputBufferDesc.pBuffers = ntlm->inputBuffer;
495 ntlm->inputBuffer[0].BufferType = SECBUFFER_TOKEN;
496 }
497
498 if ((!ntlm) || (!ntlm->table))
499 {
500 (void)fprintf(stderr, "ntlm_authenticate: invalid ntlm context\n");
501 return -1;
502 }
503
504 status = ntlm->table->InitializeSecurityContext(
505 &ntlm->credentials, (ntlm->haveContext) ? &ntlm->context : nullptr,
506 (ntlm->ServicePrincipalName) ? ntlm->ServicePrincipalName : nullptr, ntlm->fContextReq, 0,
507 SECURITY_NATIVE_DREP, (ntlm->haveInputBuffer) ? &ntlm->inputBufferDesc : nullptr, 0,
508 &ntlm->context, &ntlm->outputBufferDesc, &ntlm->pfContextAttr, &ntlm->expiration);
509
510 if (IsSecurityStatusError(status))
511 return -1;
512
513 if ((status == SEC_I_COMPLETE_AND_CONTINUE) || (status == SEC_I_COMPLETE_NEEDED))
514 {
515 if (ntlm->table->CompleteAuthToken)
516 {
517 SECURITY_STATUS rc =
518 ntlm->table->CompleteAuthToken(&ntlm->context, &ntlm->outputBufferDesc);
519 if (rc != SEC_E_OK)
520 return -1;
521 }
522
523 if (status == SEC_I_COMPLETE_NEEDED)
524 status = SEC_E_OK;
525 else if (status == SEC_I_COMPLETE_AND_CONTINUE)
526 status = SEC_I_CONTINUE_NEEDED;
527 }
528
529 if (ntlm->haveInputBuffer)
530 {
531 free(ntlm->inputBuffer[0].pvBuffer);
532 ntlm->inputBuffer[0].pvBuffer = nullptr;
533 }
534
535 ntlm->haveInputBuffer = TRUE;
536 ntlm->haveContext = TRUE;
537 return (status == SEC_I_CONTINUE_NEEDED) ? 1 : 0;
538}
539
540static void test_ntlm_client_free(TEST_NTLM_CLIENT* ntlm)
541{
542 if (!ntlm)
543 return;
544
545 test_ntlm_client_uninit(ntlm);
546 free(ntlm);
547}
548
549WINPR_ATTR_MALLOC(test_ntlm_client_free, 1)
550static TEST_NTLM_CLIENT* test_ntlm_client_new(void)
551{
552 TEST_NTLM_CLIENT* ntlm = (TEST_NTLM_CLIENT*)calloc(1, sizeof(TEST_NTLM_CLIENT));
553
554 if (!ntlm)
555 return nullptr;
556
557 return ntlm;
558}
559
560WINPR_ATTR_NODISCARD
561static int test_ntlm_server_init(TEST_NTLM_SERVER* ntlm, const struct test_input_t* arg,
562 psSspiNtlmHashCallback fkt)
563{
564 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
565
566 WINPR_ASSERT(ntlm);
567
568 ntlm->UseNtlmV2Hash = TRUE;
569 SecInvalidateHandle(&(ntlm->context));
570 ntlm->table = InitSecurityInterfaceEx(TEST_SSPI_INTERFACE);
571 if (!ntlm->table)
572 return SEC_E_INTERNAL_ERROR;
573
574 status = ntlm->table->QuerySecurityPackageInfo(NTLM_PACKAGE_NAME, &ntlm->pPackageInfo);
575
576 if (status != SEC_E_OK)
577 {
578 (void)fprintf(stderr, "QuerySecurityPackageInfo status: %s (0x%08" PRIX32 ")\n",
579 GetSecurityStatusString(status), status);
580 return -1;
581 }
582
583 void* authData = getServerAuthData(ntlm, arg, fkt);
584 ntlm->cbMaxToken = ntlm->pPackageInfo->cbMaxToken;
585 status = ntlm->table->AcquireCredentialsHandle(nullptr, NTLM_PACKAGE_NAME, SECPKG_CRED_INBOUND,
586 nullptr, authData, nullptr, nullptr,
587 &ntlm->credentials, &ntlm->expiration);
588
589 if (status != SEC_E_OK)
590 {
591 (void)fprintf(stderr, "AcquireCredentialsHandle status: %s (0x%08" PRIX32 ")\n",
592 GetSecurityStatusString(status), status);
593 return -1;
594 }
595
596 ntlm->haveContext = FALSE;
597 ntlm->haveInputBuffer = FALSE;
598 ZeroMemory(&ntlm->inputBuffer, sizeof(SecBuffer));
599 ZeroMemory(&ntlm->outputBuffer, sizeof(SecBuffer));
600 ntlm->fContextReq = 0;
601 /* NLA authentication flags */
602 ntlm->fContextReq |= ASC_REQ_MUTUAL_AUTH;
603 ntlm->fContextReq |= ASC_REQ_CONFIDENTIALITY;
604 ntlm->fContextReq |= ASC_REQ_CONNECTION;
605 ntlm->fContextReq |= ASC_REQ_USE_SESSION_KEY;
606 ntlm->fContextReq |= ASC_REQ_REPLAY_DETECT;
607 ntlm->fContextReq |= ASC_REQ_SEQUENCE_DETECT;
608 ntlm->fContextReq |= ASC_REQ_EXTENDED_ERROR;
609 return 1;
610}
611
612static void test_ntlm_server_uninit(TEST_NTLM_SERVER* ntlm)
613{
614 if (!ntlm)
615 return;
616
617 if (ntlm->outputBuffer[0].pvBuffer)
618 {
619 free(ntlm->outputBuffer[0].pvBuffer);
620 ntlm->outputBuffer[0].pvBuffer = nullptr;
621 }
622
623 free(ntlm->ServicePrincipalName);
624
625 if (ntlm->table)
626 {
627 SECURITY_STATUS status = ntlm->table->FreeCredentialsHandle(&ntlm->credentials);
628 WINPR_ASSERT(status == SEC_E_OK);
629 status = ntlm->table->FreeContextBuffer(ntlm->pPackageInfo);
630 WINPR_ASSERT(status == SEC_E_OK);
631 status = ntlm->table->DeleteSecurityContext(&ntlm->context);
632 WINPR_ASSERT(status == SEC_E_OK);
633 }
634}
635
636WINPR_ATTR_NODISCARD
637static int test_ntlm_server_authenticate(const struct test_input_t* targ, TEST_NTLM_SERVER* ntlm)
638{
639 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
640
641 WINPR_ASSERT(ntlm);
642 WINPR_ASSERT(targ);
643
644 ntlm->inputBufferDesc.ulVersion = SECBUFFER_VERSION;
645 ntlm->inputBufferDesc.cBuffers = 1;
646 ntlm->inputBufferDesc.pBuffers = ntlm->inputBuffer;
647 ntlm->inputBuffer[0].BufferType = SECBUFFER_TOKEN;
648 ntlm->outputBufferDesc.ulVersion = SECBUFFER_VERSION;
649 ntlm->outputBufferDesc.cBuffers = 1;
650 ntlm->outputBufferDesc.pBuffers = &ntlm->outputBuffer[0];
651 ntlm->outputBuffer[0].BufferType = SECBUFFER_TOKEN;
652 ntlm->outputBuffer[0].cbBuffer = ntlm->cbMaxToken;
653 ntlm->outputBuffer[0].pvBuffer = malloc(ntlm->outputBuffer[0].cbBuffer);
654
655 if (!ntlm->outputBuffer[0].pvBuffer)
656 return -1;
657
658 status = ntlm->table->AcceptSecurityContext(
659 &ntlm->credentials, ntlm->haveContext ? &ntlm->context : nullptr, &ntlm->inputBufferDesc,
660 ntlm->fContextReq, SECURITY_NATIVE_DREP, &ntlm->context, &ntlm->outputBufferDesc,
661 &ntlm->pfContextAttr, &ntlm->expiration);
662
663 if (status == SEC_I_CONTINUE_NEEDED)
664 {
665 SecPkgContext_AuthNtlmHash AuthNtlmHash = WINPR_C_ARRAY_INIT;
666
667 if (!ntlm->UseCallback)
668 {
669 if (ntlm->UseNtlmV2Hash)
670 {
671 AuthNtlmHash.Version = 2;
672 CopyMemory(AuthNtlmHash.NtlmHash, targ->ntlmv2, 16);
673 }
674 else
675 {
676 AuthNtlmHash.Version = 1;
677 CopyMemory(AuthNtlmHash.NtlmHash, targ->ntlm, 16);
678 }
679
680 status = ntlm->table->SetContextAttributes(&ntlm->context, SECPKG_ATTR_AUTH_NTLM_HASH,
681 &AuthNtlmHash,
683 }
684 }
685
686 if ((status != SEC_E_OK) && (status != SEC_I_CONTINUE_NEEDED))
687 {
688 (void)fprintf(stderr, "AcceptSecurityContext status: %s (0x%08" PRIX32 ")\n",
689 GetSecurityStatusString(status), status);
690 return -1; /* Access Denied */
691 }
692
693 ntlm->haveContext = TRUE;
694 return (status == SEC_I_CONTINUE_NEEDED) ? 1 : 0;
695}
696
697static void test_ntlm_server_free(TEST_NTLM_SERVER* ntlm)
698{
699 if (!ntlm)
700 return;
701
702 test_ntlm_server_uninit(ntlm);
703 free(ntlm);
704}
705
706WINPR_ATTR_MALLOC(test_ntlm_server_free, 1)
707static TEST_NTLM_SERVER* test_ntlm_server_new(void)
708{
709 TEST_NTLM_SERVER* ntlm = (TEST_NTLM_SERVER*)calloc(1, sizeof(TEST_NTLM_SERVER));
710
711 if (!ntlm)
712 return nullptr;
713
714 return ntlm;
715}
716
717WINPR_ATTR_NODISCARD
718static BOOL test_default(const struct test_input_t* arg, psSspiNtlmHashCallback fkt)
719{
720 BOOL rc = FALSE;
721
722 WINPR_ASSERT(arg);
723
724 printf("testcase {user=%s, domain=%s, password=%s, dynamic=%s}\n", arg->user, arg->domain,
725 arg->pwd, arg->dynamic ? "TRUE" : "FALSE");
726
730 TEST_NTLM_CLIENT* client = test_ntlm_client_new();
731 TEST_NTLM_SERVER* server = test_ntlm_server_new();
732
733 if (!client || !server)
734 {
735 printf("Memory allocation failed\n");
736 goto fail;
737 }
738
739 int status = test_ntlm_client_init(client, arg->user, arg->domain, arg->pwd);
740
741 if (status < 0)
742 {
743 printf("test_ntlm_client_init failure\n");
744 goto fail;
745 }
746
751 status = test_ntlm_server_init(server, arg, fkt);
752
753 if (status < 0)
754 {
755 printf("test_ntlm_server_init failure\n");
756 goto fail;
757 }
758
762 status = test_ntlm_client_authenticate(client);
763
764 if (status < 0)
765 {
766 printf("test_ntlm_client_authenticate failure\n");
767 goto fail;
768 }
769
770 if (client->haveContext)
771 {
772 if (!test_attributes(client->table, &client->context))
773 goto fail;
774 }
775
776 if (!arg->dynamic)
777 {
778 SecPkgContext_AuthNtlmTimestamp AuthNtlmTimestamp = WINPR_C_ARRAY_INIT;
779 SecPkgContext_AuthNtlmClientChallenge AuthNtlmClientChallenge = WINPR_C_ARRAY_INIT;
780 SecPkgContext_AuthNtlmServerChallenge AuthNtlmServerChallenge = WINPR_C_ARRAY_INIT;
781 CopyMemory(AuthNtlmTimestamp.Timestamp, TEST_NTLM_TIMESTAMP, 8);
782 AuthNtlmTimestamp.ChallengeOrResponse = TRUE;
783 SECURITY_STATUS rc = client->table->SetContextAttributes(
784 &client->context, SECPKG_ATTR_AUTH_NTLM_TIMESTAMP, &AuthNtlmTimestamp,
786 WINPR_ASSERT((rc == SEC_E_OK) || (rc == SEC_E_SECPKG_NOT_FOUND));
787
788 AuthNtlmTimestamp.ChallengeOrResponse = FALSE;
789 rc = client->table->SetContextAttributes(&client->context, SECPKG_ATTR_AUTH_NTLM_TIMESTAMP,
790 &AuthNtlmTimestamp,
792 WINPR_ASSERT((rc == SEC_E_OK) || (rc == SEC_E_SECPKG_NOT_FOUND));
793
794 CopyMemory(AuthNtlmClientChallenge.ClientChallenge, TEST_NTLM_CLIENT_CHALLENGE, 8);
795 CopyMemory(AuthNtlmServerChallenge.ServerChallenge, TEST_NTLM_SERVER_CHALLENGE, 8);
796 rc = client->table->SetContextAttributes(
797 &client->context, SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE, &AuthNtlmClientChallenge,
799 WINPR_ASSERT((rc == SEC_E_OK) || (rc == SEC_E_SECPKG_NOT_FOUND));
800
801 rc = client->table->SetContextAttributes(
802 &client->context, SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE, &AuthNtlmServerChallenge,
804 WINPR_ASSERT((rc == SEC_E_OK) || (rc == SEC_E_SECPKG_NOT_FOUND));
805 }
806
807 PSecBuffer pSecBuffer = &(client->outputBuffer[0]);
808
809 if (!arg->dynamic)
810 {
811 pSecBuffer->cbBuffer = sizeof(TEST_NTLM_NEGOTIATE) - 1;
812 free(pSecBuffer->pvBuffer);
813 pSecBuffer->pvBuffer = malloc(pSecBuffer->cbBuffer);
814
815 if (!pSecBuffer->pvBuffer)
816 {
817 printf("Memory allocation failed\n");
818 goto fail;
819 }
820
821 CopyMemory(pSecBuffer->pvBuffer, TEST_NTLM_NEGOTIATE, pSecBuffer->cbBuffer);
822 }
823
824 (void)fprintf(stderr, "NTLM_NEGOTIATE (length = %" PRIu32 "):\n", pSecBuffer->cbBuffer);
825 winpr_HexDump("sspi.test", WLOG_DEBUG, (BYTE*)pSecBuffer->pvBuffer, pSecBuffer->cbBuffer);
830 server->haveInputBuffer = TRUE;
831 server->inputBuffer[0].BufferType = SECBUFFER_TOKEN;
832 server->inputBuffer[0].pvBuffer = pSecBuffer->pvBuffer;
833 server->inputBuffer[0].cbBuffer = pSecBuffer->cbBuffer;
834 status = test_ntlm_server_authenticate(arg, server);
835
836 if (status < 0)
837 {
838 printf("test_ntlm_server_authenticate failure\n");
839 goto fail;
840 }
841
842 if (server->haveContext)
843 {
844 if (!test_attributes(server->table, &server->context))
845 goto fail;
846 }
847
848 if (!arg->dynamic)
849 {
850 SecPkgContext_AuthNtlmTimestamp AuthNtlmTimestamp = WINPR_C_ARRAY_INIT;
851 SecPkgContext_AuthNtlmClientChallenge AuthNtlmClientChallenge = WINPR_C_ARRAY_INIT;
852 SecPkgContext_AuthNtlmServerChallenge AuthNtlmServerChallenge = WINPR_C_ARRAY_INIT;
853 CopyMemory(AuthNtlmTimestamp.Timestamp, TEST_NTLM_TIMESTAMP, 8);
854 AuthNtlmTimestamp.ChallengeOrResponse = TRUE;
855 SECURITY_STATUS rc = client->table->SetContextAttributes(
856 &server->context, SECPKG_ATTR_AUTH_NTLM_TIMESTAMP, &AuthNtlmTimestamp,
858 WINPR_ASSERT(rc == SEC_E_OK);
859
860 AuthNtlmTimestamp.ChallengeOrResponse = FALSE;
861 rc = client->table->SetContextAttributes(&server->context, SECPKG_ATTR_AUTH_NTLM_TIMESTAMP,
862 &AuthNtlmTimestamp,
864 WINPR_ASSERT(rc == SEC_E_OK);
865
866 CopyMemory(AuthNtlmClientChallenge.ClientChallenge, TEST_NTLM_CLIENT_CHALLENGE, 8);
867 CopyMemory(AuthNtlmServerChallenge.ServerChallenge, TEST_NTLM_SERVER_CHALLENGE, 8);
868 rc = server->table->SetContextAttributes(
869 &server->context, SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE, &AuthNtlmClientChallenge,
871 WINPR_ASSERT(rc == SEC_E_OK);
872
873 rc = server->table->SetContextAttributes(
874 &server->context, SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE, &AuthNtlmServerChallenge,
876 WINPR_ASSERT(rc == SEC_E_OK);
877 }
878
879 pSecBuffer = &(server->outputBuffer[0]);
880
881 if (!arg->dynamic)
882 {
883 SecPkgContext_AuthNtlmMessage AuthNtlmMessage = WINPR_C_ARRAY_INIT;
884 pSecBuffer->cbBuffer = sizeof(TEST_NTLM_CHALLENGE) - 1;
885 free(pSecBuffer->pvBuffer);
886 pSecBuffer->pvBuffer = malloc(pSecBuffer->cbBuffer);
887
888 if (!pSecBuffer->pvBuffer)
889 {
890 printf("Memory allocation failed\n");
891 goto fail;
892 }
893
894 CopyMemory(pSecBuffer->pvBuffer, TEST_NTLM_CHALLENGE, pSecBuffer->cbBuffer);
895 AuthNtlmMessage.type = 2;
896 AuthNtlmMessage.length = pSecBuffer->cbBuffer;
897 AuthNtlmMessage.buffer = (BYTE*)pSecBuffer->pvBuffer;
898 SECURITY_STATUS rc = server->table->SetContextAttributes(
899 &server->context, SECPKG_ATTR_AUTH_NTLM_MESSAGE, &AuthNtlmMessage,
901 if (rc != SEC_E_OK)
902 goto fail;
903 }
904
905 (void)fprintf(stderr, "NTLM_CHALLENGE (length = %" PRIu32 "):\n", pSecBuffer->cbBuffer);
906 winpr_HexDump("sspi.test", WLOG_DEBUG, (BYTE*)pSecBuffer->pvBuffer, pSecBuffer->cbBuffer);
911 client->haveInputBuffer = TRUE;
912 client->inputBuffer[0].BufferType = SECBUFFER_TOKEN;
913 client->inputBuffer[0].pvBuffer = pSecBuffer->pvBuffer;
914 client->inputBuffer[0].cbBuffer = pSecBuffer->cbBuffer;
915 status = test_ntlm_client_authenticate(client);
916
917 if (status < 0)
918 {
919 printf("test_ntlm_client_authenticate failure\n");
920 goto fail;
921 }
922
923 pSecBuffer = &(client->outputBuffer[0]);
924
925 if (!arg->dynamic)
926 {
927 pSecBuffer->cbBuffer = sizeof(TEST_NTLM_AUTHENTICATE) - 1;
928 free(pSecBuffer->pvBuffer);
929 pSecBuffer->pvBuffer = malloc(pSecBuffer->cbBuffer);
930
931 if (!pSecBuffer->pvBuffer)
932 {
933 printf("Memory allocation failed\n");
934 goto fail;
935 }
936
937 CopyMemory(pSecBuffer->pvBuffer, TEST_NTLM_AUTHENTICATE, pSecBuffer->cbBuffer);
938 }
939
940 (void)fprintf(stderr, "NTLM_AUTHENTICATE (length = %" PRIu32 "):\n", pSecBuffer->cbBuffer);
941 winpr_HexDump("sspi.test", WLOG_DEBUG, (BYTE*)pSecBuffer->pvBuffer, pSecBuffer->cbBuffer);
945 server->haveInputBuffer = TRUE;
946 server->inputBuffer[0].BufferType = SECBUFFER_TOKEN;
947 server->inputBuffer[0].pvBuffer = pSecBuffer->pvBuffer;
948 server->inputBuffer[0].cbBuffer = pSecBuffer->cbBuffer;
949 status = test_ntlm_server_authenticate(arg, server);
950
951 if (status < 0)
952 {
953 printf("test_ntlm_server_authenticate failure\n");
954 goto fail;
955 }
956
957 if (!test_short_signature(server->table, &server->context))
958 {
959 printf("test_short_signature failure\n");
960 goto fail;
961 }
962
963 rc = TRUE;
964
965fail:
969 test_ntlm_client_free(client);
970 test_ntlm_server_free(server);
971
972 printf("testcase {user=%s, domain=%s, password=%s, dynamic=%s} returns %d\n", arg->user,
973 arg->domain, arg->pwd, arg->dynamic ? "TRUE" : "FALSE", rc);
974 return rc;
975}
976
977WINPR_ATTR_NODISCARD
978static SECURITY_STATUS testCallback(void* client, const SEC_WINNT_AUTH_IDENTITY* authIdentity,
979 const SecBuffer* ntproofvalue, const BYTE* randkey,
980 const BYTE* mic, const SecBuffer* micvalue, BYTE* ntlmhash)
981{
982 const struct test_input_t* arg = client;
983 WINPR_ASSERT(arg);
984 WINPR_ASSERT(authIdentity);
985 WINPR_ASSERT(ntproofvalue);
986 WINPR_ASSERT(randkey);
987 WINPR_ASSERT(mic);
988 WINPR_ASSERT(micvalue);
989 WINPR_ASSERT(ntlmhash);
990
991 if ((authIdentity->Flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) != 0)
992 {
993 if (!NTOWFv2FromHashW(arg->ntlm, authIdentity->User, authIdentity->UserLength * 2,
994 authIdentity->Domain, authIdentity->DomainLength * 2, ntlmhash))
995 return SEC_E_ENCRYPT_FAILURE;
996 }
997 else
998 {
999 if (!NTOWFv2FromHashA(arg->ntlm, (char*)authIdentity->User, authIdentity->UserLength,
1000 (char*)authIdentity->Domain, authIdentity->DomainLength, ntlmhash))
1001 return SEC_E_ENCRYPT_FAILURE;
1002 }
1003
1004 if (memcmp(ntlmhash, arg->ntlmv2, 16) != 0)
1005 return SEC_E_DECRYPT_FAILURE;
1006
1007 return SEC_E_OK;
1008}
1009
1010WINPR_ATTR_NODISCARD
1011static SECURITY_STATUS testFailCallback(void* client, const SEC_WINNT_AUTH_IDENTITY* authIdentity,
1012 const SecBuffer* ntproofvalue, const BYTE* randkey,
1013 const BYTE* mic, const SecBuffer* micvalue, BYTE* ntlmhash)
1014{
1015 const struct test_input_t* arg = client;
1016 WINPR_ASSERT(arg);
1017 WINPR_ASSERT(authIdentity);
1018 WINPR_ASSERT(ntproofvalue);
1019 WINPR_ASSERT(randkey);
1020 WINPR_ASSERT(mic);
1021 WINPR_ASSERT(micvalue);
1022 WINPR_ASSERT(ntlmhash);
1023
1024 if ((authIdentity->Flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) != 0)
1025 {
1026 if (!NTOWFv2FromHashW(arg->ntlm, authIdentity->User, authIdentity->UserLength * 2,
1027 authIdentity->Domain, authIdentity->DomainLength * 2, ntlmhash))
1028 return SEC_E_ENCRYPT_FAILURE;
1029 }
1030 else
1031 {
1032 if (!NTOWFv2FromHashA(arg->ntlm, (char*)authIdentity->User, authIdentity->UserLength,
1033 (char*)authIdentity->Domain, authIdentity->DomainLength, ntlmhash))
1034 return SEC_E_ENCRYPT_FAILURE;
1035 }
1036
1037 if (memcmp(ntlmhash, arg->ntlmv2, 16) != 0)
1038 return SEC_E_DECRYPT_FAILURE;
1039
1040 return SEC_E_DECRYPT_FAILURE;
1041}
1042
1043int TestSspiNTLM(int argc, char* argv[])
1044{
1045 WINPR_UNUSED(argc);
1046 WINPR_UNUSED(argv);
1047
1048 const struct test_input_t inputs[] = {
1049 { TEST_NTLM_USER, TEST_NTLM_DOMAIN, TEST_NTLM_PASSWORD, TEST_NTLM_HASH, TEST_NTLM_V2_HASH,
1050 TRUE, TRUE },
1051 { TEST_NTLM_USER, TEST_NTLM_DOMAIN, TEST_NTLM_PASSWORD, TEST_NTLM_HASH, TEST_NTLM_V2_HASH,
1052 FALSE, TRUE },
1053 { TEST_NTLM_USER, TEST_NTLM_DOMAIN, "", TEST_EMPTY_PWD_NTLM_HASH,
1054 TEST_EMPTY_PWD_NTLM_V2_HASH, TRUE, TRUE },
1055 { TEST_NTLM_USER, TEST_NTLM_DOMAIN, nullptr, TEST_EMPTY_PWD_NTLM_HASH,
1056 TEST_EMPTY_PWD_NTLM_V2_HASH, TRUE, FALSE }
1057 };
1058
1059 for (size_t x = 0; x < ARRAYSIZE(inputs); x++)
1060 {
1061 const struct test_input_t* cur = &inputs[x];
1062 const BOOL res = test_default(cur, nullptr);
1063 if (res != cur->expected)
1064 {
1065 printf("%s [%" PRIuz "] fail 1!\n", __func__, x);
1066 return -1;
1067 }
1068 const BOOL res2 = test_default(cur, testCallback);
1069 if (res2 != cur->expected)
1070 {
1071 printf("%s [%" PRIuz "] fail 2!\n", __func__, x);
1072 return -2;
1073 }
1074 const BOOL res3 = test_default(cur, testFailCallback);
1075 if (res3 != FALSE)
1076 {
1077 printf("%s [%" PRIuz "] fail 2!\n", __func__, x);
1078 return -2;
1079 }
1080 }
1081 printf("%s success!\n", __func__);
1082 return 0;
1083}