FreeRDP
Loading...
Searching...
No Matches
sspi/NTLM/ntlm.c
1
20#include <winpr/config.h>
21
22#include <winpr/crt.h>
23#include <winpr/assert.h>
24#include <winpr/sspi.h>
25#include <winpr/print.h>
26#include <winpr/string.h>
27#include <winpr/tchar.h>
28#include <winpr/sysinfo.h>
29#include <winpr/registry.h>
30#include <winpr/endian.h>
31#include <winpr/build-config.h>
32
33#include "ntlm.h"
34#include "ntlm_export.h"
35#include "../sspi.h"
36
37#include "ntlm_message.h"
38
39#include "../../utils.h"
40
41#include "../../log.h"
42#define TAG WINPR_TAG("sspi.NTLM")
43
44#ifndef MIN
45#define MIN(a, b) ((a) < (b)) ? (a) : (b)
46#endif
47
48#define WINPR_KEY "Software\\%s\\WinPR\\NTLM"
49
50static char* NTLM_PACKAGE_NAME = "NTLM";
51
52#define check_context(ctx) check_context_((ctx), __FILE__, __func__, __LINE__)
53static BOOL check_context_(NTLM_CONTEXT* context, const char* file, const char* fkt, size_t line)
54{
55 BOOL rc = TRUE;
56 wLog* log = WLog_Get(TAG);
57 const DWORD log_level = WLOG_ERROR;
58
59 if (!context)
60 {
61 if (WLog_IsLevelActive(log, log_level))
62 WLog_PrintTextMessage(log, log_level, line, file, fkt, "invalid context");
63
64 return FALSE;
65 }
66
67 if (!context->RecvRc4Seal)
68 {
69 if (WLog_IsLevelActive(log, log_level))
70 WLog_PrintTextMessage(log, log_level, line, file, fkt, "invalid context->RecvRc4Seal");
71 rc = FALSE;
72 }
73 if (!context->SendRc4Seal)
74 {
75 if (WLog_IsLevelActive(log, log_level))
76 WLog_PrintTextMessage(log, log_level, line, file, fkt, "invalid context->SendRc4Seal");
77 rc = FALSE;
78 }
79
80 if (!context->SendSigningKey)
81 {
82 if (WLog_IsLevelActive(log, log_level))
83 WLog_PrintTextMessage(log, log_level, line, file, fkt,
84 "invalid context->SendSigningKey");
85 rc = FALSE;
86 }
87 if (!context->RecvSigningKey)
88 {
89 if (WLog_IsLevelActive(log, log_level))
90 WLog_PrintTextMessage(log, log_level, line, file, fkt,
91 "invalid context->RecvSigningKey");
92 rc = FALSE;
93 }
94 if (!context->SendSealingKey)
95 {
96 if (WLog_IsLevelActive(log, log_level))
97 WLog_PrintTextMessage(log, log_level, line, file, fkt,
98 "invalid context->SendSealingKey");
99 rc = FALSE;
100 }
101 if (!context->RecvSealingKey)
102 {
103 if (WLog_IsLevelActive(log, log_level))
104 WLog_PrintTextMessage(log, log_level, line, file, fkt,
105 "invalid context->RecvSealingKey");
106 rc = FALSE;
107 }
108 return rc;
109}
110
111char* get_computer_name(COMPUTER_NAME_FORMAT type, size_t* pSize)
112{
113 DWORD nSize = 0;
114
115 if (pSize)
116 *pSize = 0;
117
118 if (GetComputerNameExA(type, nullptr, &nSize))
119 return nullptr;
120
121 if (GetLastError() != ERROR_MORE_DATA)
122 return nullptr;
123
124 char* computerName = calloc(1, nSize);
125
126 if (!computerName)
127 return nullptr;
128
129 if (!GetComputerNameExA(type, computerName, &nSize))
130 {
131 free(computerName);
132 return nullptr;
133 }
134
135 if (pSize)
136 *pSize = nSize;
137 return computerName;
138}
139
140static void ntlm_FreeContextWorkstation(NTLM_CONTEXT* context)
141{
142 WINPR_ASSERT(context);
143
144 free(context->Workstation.Buffer);
145 context->Workstation.Buffer = nullptr;
146 context->Workstation.Length = 0;
147}
148
149SECURITY_STATUS ntlm_SetContextWorkstationX(NTLM_CONTEXT* context, BOOL unicode, const void* data,
150 size_t length)
151{
152 ntlm_FreeContextWorkstation(context);
153
154 if (length == 0)
155 return SEC_E_OK;
156
157 WINPR_ASSERT(data);
158 if (unicode)
159 {
160 if (length > UINT16_MAX)
161 return SEC_E_INVALID_PARAMETER;
162
163 context->Workstation.Buffer = calloc(length + sizeof(WCHAR), 1);
164 if (!context->Workstation.Buffer)
165 return SEC_E_INSUFFICIENT_MEMORY;
166 memcpy(context->Workstation.Buffer, data, length);
167 context->Workstation.Length = WINPR_ASSERTING_INT_CAST(USHORT, length);
168 }
169 else
170 {
171 size_t s = 0;
172 void* ptr = ConvertUtf8NToWCharAlloc(data, length, &s);
173 if (!ptr)
174 return SEC_E_INSUFFICIENT_MEMORY;
175
176 s *= sizeof(WCHAR);
177 if (s > UINT16_MAX)
178 {
179 free(ptr);
180 return SEC_E_INVALID_PARAMETER;
181 }
182 context->Workstation.Buffer = ptr;
183 context->Workstation.Length = WINPR_ASSERTING_INT_CAST(USHORT, s);
184 }
185 return SEC_E_OK;
186}
187
188static int ntlm_SetContextWorkstation(NTLM_CONTEXT* context, const char* Workstation)
189{
190 const char* ws = Workstation;
191 CHAR* computerName = nullptr;
192
193 if (!Workstation)
194 {
195 computerName = get_computer_name(ComputerNameNetBIOS, nullptr);
196 if (!computerName)
197 return -1;
198 ws = computerName;
199 }
200
201 const size_t len = strlen(ws);
202 const SECURITY_STATUS status = ntlm_SetContextWorkstationX(context, FALSE, ws, len);
203 free(computerName);
204
205 return (status == SEC_E_OK) ? 1 : -1;
206}
207
208static int ntlm_SetContextServicePrincipalNameW(NTLM_CONTEXT* context, LPWSTR ServicePrincipalName)
209{
210 WINPR_ASSERT(context);
211
212 if (!ServicePrincipalName)
213 {
214 context->ServicePrincipalName.Buffer = nullptr;
215 context->ServicePrincipalName.Length = 0;
216 return 1;
217 }
218
219 context->ServicePrincipalName.Length = (USHORT)(_wcslen(ServicePrincipalName) * 2);
220 context->ServicePrincipalName.Buffer = (PWSTR)malloc(context->ServicePrincipalName.Length + 2);
221
222 if (!context->ServicePrincipalName.Buffer)
223 return -1;
224
225 memcpy(context->ServicePrincipalName.Buffer, ServicePrincipalName,
226 context->ServicePrincipalName.Length + 2);
227 return 1;
228}
229
230static int ntlm_SetContextTargetName(NTLM_CONTEXT* context, char* TargetName)
231{
232 char* name = TargetName;
233 WINPR_ASSERT(context);
234
235 if (!name)
236 {
237 size_t nSize = 0;
238 char* computerName = get_computer_name(ComputerNameNetBIOS, &nSize);
239
240 if (!computerName)
241 return -1;
242
243 if (nSize > MAX_COMPUTERNAME_LENGTH)
244 computerName[MAX_COMPUTERNAME_LENGTH] = '\0';
245
246 name = computerName;
247
248 if (!name)
249 return -1;
250
251 CharUpperA(name);
252 }
253
254 size_t len = 0;
255 context->TargetName.pvBuffer = ConvertUtf8ToWCharAlloc(name, &len);
256
257 if (!context->TargetName.pvBuffer || (len > UINT16_MAX / sizeof(WCHAR)))
258 {
259 free(context->TargetName.pvBuffer);
260 context->TargetName.pvBuffer = nullptr;
261
262 if (!TargetName)
263 free(name);
264
265 return -1;
266 }
267
268 context->TargetName.cbBuffer = (USHORT)(len * sizeof(WCHAR));
269
270 if (!TargetName)
271 free(name);
272
273 return 1;
274}
275
276static NTLM_CONTEXT* ntlm_ContextNew(void)
277{
278 HKEY hKey = nullptr;
279 DWORD dwType = 0;
280 DWORD dwSize = 0;
281 DWORD dwValue = 0;
282 NTLM_CONTEXT* context = (NTLM_CONTEXT*)calloc(1, sizeof(NTLM_CONTEXT));
283
284 if (!context)
285 return nullptr;
286
287 context->NTLMv2 = TRUE;
288 context->UseMIC = FALSE;
289 context->SendVersionInfo = TRUE;
290 context->SendSingleHostData = FALSE;
291 context->SendWorkstationName = TRUE;
292 context->NegotiateKeyExchange = TRUE;
293 context->UseSamFileDatabase = TRUE;
294
295 {
296 char* key = winpr_getApplicatonDetailsRegKey(WINPR_KEY);
297 if (key)
298 {
299 const LONG status =
300 RegOpenKeyExA(HKEY_LOCAL_MACHINE, key, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
301 free(key);
302
303 if (status == ERROR_SUCCESS)
304 {
305 if (RegQueryValueEx(hKey, _T("NTLMv2"), nullptr, &dwType, (BYTE*)&dwValue,
306 &dwSize) == ERROR_SUCCESS)
307 context->NTLMv2 = dwValue ? 1 : 0;
308
309 if (RegQueryValueEx(hKey, _T("UseMIC"), nullptr, &dwType, (BYTE*)&dwValue,
310 &dwSize) == ERROR_SUCCESS)
311 context->UseMIC = dwValue ? 1 : 0;
312
313 if (RegQueryValueEx(hKey, _T("SendVersionInfo"), nullptr, &dwType, (BYTE*)&dwValue,
314 &dwSize) == ERROR_SUCCESS)
315 context->SendVersionInfo = dwValue ? 1 : 0;
316
317 if (RegQueryValueEx(hKey, _T("SendSingleHostData"), nullptr, &dwType,
318 (BYTE*)&dwValue, &dwSize) == ERROR_SUCCESS)
319 context->SendSingleHostData = dwValue ? 1 : 0;
320
321 if (RegQueryValueEx(hKey, _T("SendWorkstationName"), nullptr, &dwType,
322 (BYTE*)&dwValue, &dwSize) == ERROR_SUCCESS)
323 context->SendWorkstationName = dwValue ? 1 : 0;
324
325 if (RegQueryValueEx(hKey, _T("WorkstationName"), nullptr, &dwType, nullptr,
326 &dwSize) == ERROR_SUCCESS)
327 {
328 char* workstation = (char*)malloc(dwSize + 1);
329
330 if (!workstation)
331 {
332 free(context);
333 return nullptr;
334 }
335
336 const LONG rc = RegQueryValueExA(hKey, "WorkstationName", nullptr, &dwType,
337 (BYTE*)workstation, &dwSize);
338 if (rc != ERROR_SUCCESS)
339 WLog_WARN(TAG, "Key ''WorkstationName' not found");
340 workstation[dwSize] = '\0';
341
342 if (ntlm_SetContextWorkstation(context, workstation) < 0)
343 {
344 free(workstation);
345 free(context);
346 return nullptr;
347 }
348
349 free(workstation);
350 }
351
352 RegCloseKey(hKey);
353 }
354 }
355 }
356
357 /*
358 * Extended Protection is enabled by default in Windows 7,
359 * but enabling it in WinPR breaks TS Gateway at this point
360 */
361 context->SuppressExtendedProtection = FALSE;
362 const LONG status =
363 RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("System\\CurrentControlSet\\Control\\LSA"), 0,
364 KEY_READ | KEY_WOW64_64KEY, &hKey);
365
366 if (status == ERROR_SUCCESS)
367 {
368 if (RegQueryValueEx(hKey, _T("SuppressExtendedProtection"), nullptr, &dwType,
369 (BYTE*)&dwValue, &dwSize) == ERROR_SUCCESS)
370 context->SuppressExtendedProtection = dwValue ? 1 : 0;
371
372 RegCloseKey(hKey);
373 }
374
375 context->NegotiateFlags = 0;
376 context->LmCompatibilityLevel = 3;
377 ntlm_change_state(context, NTLM_STATE_INITIAL);
378 FillMemory(context->MachineID, sizeof(context->MachineID), 0xAA);
379
380 if (context->NTLMv2)
381 context->UseMIC = TRUE;
382
383 return context;
384}
385
386static void ntlm_ContextFree(NTLM_CONTEXT* context)
387{
388 if (!context)
389 return;
390
391 winpr_RC4_Free(context->SendRc4Seal);
392 winpr_RC4_Free(context->RecvRc4Seal);
393 sspi_SecBufferFree(&context->NegotiateMessage);
394 sspi_SecBufferFree(&context->ChallengeMessage);
395 sspi_SecBufferFree(&context->AuthenticateMessage);
396 sspi_SecBufferFree(&context->ChallengeTargetInfo);
397 sspi_SecBufferFree(&context->TargetName);
398 sspi_SecBufferFree(&context->NtChallengeResponse);
399 sspi_SecBufferFree(&context->LmChallengeResponse);
400 free(context->ServicePrincipalName.Buffer);
401 ntlm_FreeContextWorkstation(context);
402
403 /* Zero sensitive key material before freeing the context */
404 memset(context->NtlmHash, 0, sizeof(context->NtlmHash));
405 memset(context->NtlmV2Hash, 0, sizeof(context->NtlmV2Hash));
406 memset(context->SessionBaseKey, 0, sizeof(context->SessionBaseKey));
407 memset(context->KeyExchangeKey, 0, sizeof(context->KeyExchangeKey));
408 memset(context->RandomSessionKey, 0, sizeof(context->RandomSessionKey));
409 memset(context->ExportedSessionKey, 0, sizeof(context->ExportedSessionKey));
410 memset(context->EncryptedRandomSessionKey, 0, sizeof(context->EncryptedRandomSessionKey));
411 memset(context->NtProofString, 0, sizeof(context->NtProofString));
412 free(context);
413}
414
415static SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleW(
416 WINPR_ATTR_UNUSED SEC_WCHAR* pszPrincipal, WINPR_ATTR_UNUSED SEC_WCHAR* pszPackage,
417 ULONG fCredentialUse, WINPR_ATTR_UNUSED void* pvLogonID, void* pAuthData,
418 SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential,
419 WINPR_ATTR_UNUSED PTimeStamp ptsExpiry)
420{
421 SEC_WINPR_NTLM_SETTINGS* settings = nullptr;
422
423 if ((fCredentialUse != SECPKG_CRED_OUTBOUND) && (fCredentialUse != SECPKG_CRED_INBOUND) &&
424 (fCredentialUse != SECPKG_CRED_BOTH))
425 {
426 return SEC_E_INVALID_PARAMETER;
427 }
428
429 SSPI_CREDENTIALS* credentials = sspi_CredentialsNew();
430
431 if (!credentials)
432 return SEC_E_INTERNAL_ERROR;
433
434 credentials->fCredentialUse = fCredentialUse;
435 credentials->pGetKeyFn = pGetKeyFn;
436 credentials->pvGetKeyArgument = pvGetKeyArgument;
437
438 if (pAuthData)
439 {
440 UINT32 identityFlags = sspi_GetAuthIdentityFlags(pAuthData);
441
442 if (sspi_CopyAuthIdentity(&(credentials->identity),
443 (const SEC_WINNT_AUTH_IDENTITY_INFO*)pAuthData) < 0)
444 {
445 sspi_CredentialsFree(credentials);
446 return SEC_E_INVALID_PARAMETER;
447 }
448
449 if (identityFlags & SEC_WINNT_AUTH_IDENTITY_EXTENDED)
450 settings = (((SEC_WINNT_AUTH_IDENTITY_WINPR*)pAuthData)->ntlmSettings);
451 }
452
453 if (settings)
454 {
455 if (settings->samFile)
456 {
457 credentials->ntlmSettings.samFile = _strdup(settings->samFile);
458 if (!credentials->ntlmSettings.samFile)
459 {
460 sspi_CredentialsFree(credentials);
461 return SEC_E_INSUFFICIENT_MEMORY;
462 }
463 }
464 credentials->ntlmSettings.hashCallback = settings->hashCallback;
465 credentials->ntlmSettings.hashCallbackArg = settings->hashCallbackArg;
466 }
467
468 sspi_SecureHandleSetLowerPointer(phCredential, (void*)credentials);
469 sspi_SecureHandleSetUpperPointer(phCredential, (void*)NTLM_PACKAGE_NAME);
470 return SEC_E_OK;
471}
472
473static SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleA(
474 SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID,
475 void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential,
476 PTimeStamp ptsExpiry)
477{
478 SECURITY_STATUS status = SEC_E_INSUFFICIENT_MEMORY;
479 SEC_WCHAR* principal = nullptr;
480 SEC_WCHAR* package = nullptr;
481
482 if (pszPrincipal)
483 {
484 principal = ConvertUtf8ToWCharAlloc(pszPrincipal, nullptr);
485 if (!principal)
486 goto fail;
487 }
488 if (pszPackage)
489 {
490 package = ConvertUtf8ToWCharAlloc(pszPackage, nullptr);
491 if (!package)
492 goto fail;
493 }
494
495 status =
496 ntlm_AcquireCredentialsHandleW(principal, package, fCredentialUse, pvLogonID, pAuthData,
497 pGetKeyFn, pvGetKeyArgument, phCredential, ptsExpiry);
498
499fail:
500 free(principal);
501 free(package);
502
503 return status;
504}
505
506static SECURITY_STATUS SEC_ENTRY ntlm_FreeCredentialsHandle(PCredHandle phCredential)
507{
508 if (!phCredential)
509 return SEC_E_INVALID_HANDLE;
510
511 SSPI_CREDENTIALS* credentials =
512 (SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
513 sspi_SecureHandleInvalidate(phCredential);
514 if (!credentials)
515 return SEC_E_INVALID_HANDLE;
516
517 sspi_CredentialsFree(credentials);
518 return SEC_E_OK;
519}
520
521static SECURITY_STATUS SEC_ENTRY ntlm_QueryCredentialsAttributesW(
522 WINPR_ATTR_UNUSED PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
523 WINPR_ATTR_UNUSED void* pBuffer)
524{
525 if (ulAttribute == SECPKG_CRED_ATTR_NAMES)
526 {
527 return SEC_E_OK;
528 }
529
530 WLog_ERR(TAG, "TODO: Implement");
531 return SEC_E_UNSUPPORTED_FUNCTION;
532}
533
534static SECURITY_STATUS SEC_ENTRY ntlm_QueryCredentialsAttributesA(PCredHandle phCredential,
535 ULONG ulAttribute, void* pBuffer)
536{
537 return ntlm_QueryCredentialsAttributesW(phCredential, ulAttribute, pBuffer);
538}
539
543static SECURITY_STATUS SEC_ENTRY ntlm_AcceptSecurityContext(
544 PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput, ULONG fContextReq,
545 WINPR_ATTR_UNUSED ULONG TargetDataRep, PCtxtHandle phNewContext, PSecBufferDesc pOutput,
546 WINPR_ATTR_UNUSED PULONG pfContextAttr, WINPR_ATTR_UNUSED PTimeStamp ptsTimeStamp)
547{
548 SECURITY_STATUS status = 0;
549 SSPI_CREDENTIALS* credentials = nullptr;
550 PSecBuffer input_buffer = nullptr;
551 PSecBuffer output_buffer = nullptr;
552
553 /* behave like windows SSPIs that don't want empty context */
554 if (phContext && !phContext->dwLower && !phContext->dwUpper)
555 return SEC_E_INVALID_HANDLE;
556
557 NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
558
559 if (!context)
560 {
561 context = ntlm_ContextNew();
562
563 if (!context)
564 return SEC_E_INSUFFICIENT_MEMORY;
565
566 context->server = TRUE;
567
568 if (fContextReq & ASC_REQ_CONFIDENTIALITY)
569 context->confidentiality = TRUE;
570
571 credentials = (SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
572 context->credentials = credentials;
573 context->SamFile = credentials->ntlmSettings.samFile;
574 context->HashCallback = credentials->ntlmSettings.hashCallback;
575 context->HashCallbackArg = credentials->ntlmSettings.hashCallbackArg;
576
577 ntlm_SetContextTargetName(context, nullptr);
578 sspi_SecureHandleSetLowerPointer(phNewContext, context);
579 sspi_SecureHandleSetUpperPointer(phNewContext, (void*)NTLM_PACKAGE_NAME);
580 }
581
582 switch (ntlm_get_state(context))
583 {
584 case NTLM_STATE_INITIAL:
585 {
586 ntlm_change_state(context, NTLM_STATE_NEGOTIATE);
587
588 if (!pInput)
589 return SEC_E_INVALID_TOKEN;
590
591 if (pInput->cBuffers < 1)
592 return SEC_E_INVALID_TOKEN;
593
594 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
595
596 if (!input_buffer)
597 return SEC_E_INVALID_TOKEN;
598
599 if (input_buffer->cbBuffer < 1)
600 return SEC_E_INVALID_TOKEN;
601
602 status = ntlm_read_NegotiateMessage(context, input_buffer);
603 if (status != SEC_I_CONTINUE_NEEDED)
604 return status;
605
606 if (ntlm_get_state(context) == NTLM_STATE_CHALLENGE)
607 {
608 if (!pOutput)
609 return SEC_E_INVALID_TOKEN;
610
611 if (pOutput->cBuffers < 1)
612 return SEC_E_INVALID_TOKEN;
613
614 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
615
616 if (!output_buffer->BufferType)
617 return SEC_E_INVALID_TOKEN;
618
619 if (output_buffer->cbBuffer < 1)
620 return SEC_E_INSUFFICIENT_MEMORY;
621
622 return ntlm_write_ChallengeMessage(context, output_buffer);
623 }
624
625 return SEC_E_OUT_OF_SEQUENCE;
626 }
627
628 case NTLM_STATE_AUTHENTICATE:
629 {
630 if (!pInput)
631 return SEC_E_INVALID_TOKEN;
632
633 if (pInput->cBuffers < 1)
634 return SEC_E_INVALID_TOKEN;
635
636 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
637
638 if (!input_buffer)
639 return SEC_E_INVALID_TOKEN;
640
641 if (input_buffer->cbBuffer < 1)
642 return SEC_E_INVALID_TOKEN;
643
644 status = ntlm_read_AuthenticateMessage(context, input_buffer);
645
646 if (pOutput)
647 {
648 for (ULONG i = 0; i < pOutput->cBuffers; i++)
649 {
650 pOutput->pBuffers[i].cbBuffer = 0;
651 pOutput->pBuffers[i].BufferType = SECBUFFER_TOKEN;
652 }
653 }
654
655 return status;
656 }
657
658 default:
659 return SEC_E_OUT_OF_SEQUENCE;
660 }
661}
662
663static SECURITY_STATUS SEC_ENTRY
664ntlm_ImpersonateSecurityContext(WINPR_ATTR_UNUSED PCtxtHandle phContext)
665{
666 return SEC_E_OK;
667}
668
669static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextW(
670 PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR* pszTargetName, ULONG fContextReq,
671 WINPR_ATTR_UNUSED ULONG Reserved1, WINPR_ATTR_UNUSED ULONG TargetDataRep, PSecBufferDesc pInput,
672 WINPR_ATTR_UNUSED ULONG Reserved2, PCtxtHandle phNewContext, PSecBufferDesc pOutput,
673 WINPR_ATTR_UNUSED PULONG pfContextAttr, WINPR_ATTR_UNUSED PTimeStamp ptsExpiry)
674{
675 SECURITY_STATUS status = 0;
676 SSPI_CREDENTIALS* credentials = nullptr;
677 PSecBuffer input_buffer = nullptr;
678 PSecBuffer output_buffer = nullptr;
679
680 /* behave like windows SSPIs that don't want empty context */
681 if (phContext && !phContext->dwLower && !phContext->dwUpper)
682 return SEC_E_INVALID_HANDLE;
683
684 NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
685
686 if (pInput)
687 {
688 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
689 }
690
691 if (!context)
692 {
693 context = ntlm_ContextNew();
694
695 if (!context)
696 return SEC_E_INSUFFICIENT_MEMORY;
697
698 if (fContextReq & ISC_REQ_CONFIDENTIALITY)
699 context->confidentiality = TRUE;
700
701 credentials = (SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
702 context->credentials = credentials;
703
704 if (context->Workstation.Length < 1)
705 {
706 if (ntlm_SetContextWorkstation(context, nullptr) < 0)
707 {
708 ntlm_ContextFree(context);
709 return SEC_E_INTERNAL_ERROR;
710 }
711 }
712
713 if (ntlm_SetContextServicePrincipalNameW(context, pszTargetName) < 0)
714 {
715 ntlm_ContextFree(context);
716 return SEC_E_INTERNAL_ERROR;
717 }
718
719 sspi_SecureHandleSetLowerPointer(phNewContext, context);
720 sspi_SecureHandleSetUpperPointer(phNewContext, NTLM_SSP_NAME);
721 }
722
723 if ((!input_buffer) || (ntlm_get_state(context) == NTLM_STATE_AUTHENTICATE))
724 {
725 if (!pOutput)
726 return SEC_E_INVALID_TOKEN;
727
728 if (pOutput->cBuffers < 1)
729 return SEC_E_INVALID_TOKEN;
730
731 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
732
733 if (!output_buffer)
734 return SEC_E_INVALID_TOKEN;
735
736 if (output_buffer->cbBuffer < 1)
737 return SEC_E_INVALID_TOKEN;
738
739 if (ntlm_get_state(context) == NTLM_STATE_INITIAL)
740 ntlm_change_state(context, NTLM_STATE_NEGOTIATE);
741
742 if (ntlm_get_state(context) == NTLM_STATE_NEGOTIATE)
743 return ntlm_write_NegotiateMessage(context, output_buffer);
744
745 return SEC_E_OUT_OF_SEQUENCE;
746 }
747 else
748 {
749 if (!input_buffer)
750 return SEC_E_INVALID_TOKEN;
751
752 if (input_buffer->cbBuffer < 1)
753 return SEC_E_INVALID_TOKEN;
754
755 PSecBuffer channel_bindings = sspi_FindSecBuffer(pInput, SECBUFFER_CHANNEL_BINDINGS);
756
757 if (channel_bindings)
758 {
759 context->Bindings.BindingsLength = channel_bindings->cbBuffer;
760 context->Bindings.Bindings = (SEC_CHANNEL_BINDINGS*)channel_bindings->pvBuffer;
761 }
762
763 if (ntlm_get_state(context) == NTLM_STATE_CHALLENGE)
764 {
765 status = ntlm_read_ChallengeMessage(context, input_buffer);
766
767 if (status != SEC_I_CONTINUE_NEEDED)
768 return status;
769
770 if (!pOutput)
771 return SEC_E_INVALID_TOKEN;
772
773 if (pOutput->cBuffers < 1)
774 return SEC_E_INVALID_TOKEN;
775
776 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
777
778 if (!output_buffer)
779 return SEC_E_INVALID_TOKEN;
780
781 if (output_buffer->cbBuffer < 1)
782 return SEC_E_INSUFFICIENT_MEMORY;
783
784 if (ntlm_get_state(context) == NTLM_STATE_AUTHENTICATE)
785 return ntlm_write_AuthenticateMessage(context, output_buffer);
786 }
787
788 return SEC_E_OUT_OF_SEQUENCE;
789 }
790
791 return SEC_E_OUT_OF_SEQUENCE;
792}
793
797static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextA(
798 PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR* pszTargetName, ULONG fContextReq,
799 ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2,
800 PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry)
801{
802 SECURITY_STATUS status = 0;
803 SEC_WCHAR* pszTargetNameW = nullptr;
804
805 if (pszTargetName)
806 {
807 pszTargetNameW = ConvertUtf8ToWCharAlloc(pszTargetName, nullptr);
808 if (!pszTargetNameW)
809 return SEC_E_INTERNAL_ERROR;
810 }
811
812 status = ntlm_InitializeSecurityContextW(phCredential, phContext, pszTargetNameW, fContextReq,
813 Reserved1, TargetDataRep, pInput, Reserved2,
814 phNewContext, pOutput, pfContextAttr, ptsExpiry);
815 free(pszTargetNameW);
816 return status;
817}
818
819/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375354 */
820
821static SECURITY_STATUS SEC_ENTRY ntlm_DeleteSecurityContext(PCtxtHandle phContext)
822{
823 NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
824 sspi_SecureHandleInvalidate(phContext);
825 ntlm_ContextFree(context);
826 return SEC_E_OK;
827}
828
829SECURITY_STATUS ntlm_computeProofValue(NTLM_CONTEXT* ntlm, SecBuffer* ntproof)
830{
831 BYTE* blob = nullptr;
832 SecBuffer* target = nullptr;
833
834 WINPR_ASSERT(ntlm);
835 WINPR_ASSERT(ntproof);
836
837 target = &ntlm->ChallengeTargetInfo;
838
839 if (!sspi_SecBufferAlloc(ntproof, 36 + target->cbBuffer))
840 return SEC_E_INSUFFICIENT_MEMORY;
841
842 blob = (BYTE*)ntproof->pvBuffer;
843 CopyMemory(blob, ntlm->ServerChallenge, 8); /* Server challenge. */
844 blob[8] = 1; /* Response version. */
845 blob[9] = 1; /* Highest response version understood by the client. */
846 /* Reserved 6B. */
847 CopyMemory(&blob[16], ntlm->Timestamp, 8); /* Time. */
848 CopyMemory(&blob[24], ntlm->ClientChallenge, 8); /* Client challenge. */
849 /* Reserved 4B. */
850 /* Server name. */
851 CopyMemory(&blob[36], target->pvBuffer, target->cbBuffer);
852 return SEC_E_OK;
853}
854
855SECURITY_STATUS ntlm_computeMicValue(NTLM_CONTEXT* ntlm, SecBuffer* micvalue)
856{
857 BYTE* blob = nullptr;
858 ULONG msgSize = 0;
859
860 WINPR_ASSERT(ntlm);
861 WINPR_ASSERT(micvalue);
862
863 msgSize = ntlm->NegotiateMessage.cbBuffer + ntlm->ChallengeMessage.cbBuffer +
864 ntlm->AuthenticateMessage.cbBuffer;
865
866 if (!sspi_SecBufferAlloc(micvalue, msgSize))
867 return SEC_E_INSUFFICIENT_MEMORY;
868
869 blob = (BYTE*)micvalue->pvBuffer;
870 CopyMemory(blob, ntlm->NegotiateMessage.pvBuffer, ntlm->NegotiateMessage.cbBuffer);
871 blob += ntlm->NegotiateMessage.cbBuffer;
872 CopyMemory(blob, ntlm->ChallengeMessage.pvBuffer, ntlm->ChallengeMessage.cbBuffer);
873 blob += ntlm->ChallengeMessage.cbBuffer;
874 CopyMemory(blob, ntlm->AuthenticateMessage.pvBuffer, ntlm->AuthenticateMessage.cbBuffer);
875 blob += ntlm->MessageIntegrityCheckOffset;
876 ZeroMemory(blob, 16);
877 return SEC_E_OK;
878}
879
880WINPR_ATTR_NODISCARD
881static bool identityToAuthIdentity(const SEC_WINNT_AUTH_IDENTITY* identity,
882 SecPkgContext_AuthIdentity* pAuthIdentity)
883{
884 WINPR_ASSERT(identity);
885
886 if (!pAuthIdentity)
887 return false;
888
889 const SecPkgContext_AuthIdentity empty = WINPR_C_ARRAY_INIT;
890 *pAuthIdentity = empty;
891
892 if ((identity->Flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) != 0)
893 {
894 if (identity->UserLength > 0)
895 {
896 if (ConvertWCharNToUtf8(identity->User, identity->UserLength, pAuthIdentity->User,
897 ARRAYSIZE(pAuthIdentity->User)) <= 0)
898 return false;
899 }
900
901 if (identity->DomainLength > 0)
902 {
903 if (ConvertWCharNToUtf8(identity->Domain, identity->DomainLength, pAuthIdentity->Domain,
904 ARRAYSIZE(pAuthIdentity->Domain)) <= 0)
905 return false;
906 }
907 }
908 else if ((identity->Flags & SEC_WINNT_AUTH_IDENTITY_ANSI) != 0)
909 {
910 if (identity->UserLength > 0)
911 {
912 const size_t len = MIN(ARRAYSIZE(pAuthIdentity->User) - 1, identity->UserLength);
913 strncpy(pAuthIdentity->User, (char*)identity->User, len);
914 pAuthIdentity->User[len] = '\0';
915 }
916
917 if (identity->DomainLength > 0)
918 {
919 const size_t len = MIN(ARRAYSIZE(pAuthIdentity->Domain) - 1, identity->DomainLength);
920 strncpy(pAuthIdentity->Domain, (char*)identity->Domain, len);
921 pAuthIdentity->Domain[len] = '\0';
922 }
923 }
924 else
925 return false;
926 return true;
927}
928
929WINPR_ATTR_NODISCARD
930static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesCommon(PCtxtHandle phContext,
931 ULONG ulAttribute, void* pBuffer)
932{
933 if (!phContext)
934 return SEC_E_INVALID_HANDLE;
935
936 if (!pBuffer)
937 return SEC_E_INSUFFICIENT_MEMORY;
938
939 NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
940 if (!check_context(context))
941 return SEC_E_INVALID_HANDLE;
942
943 switch (ulAttribute)
944 {
945 case SECPKG_ATTR_AUTH_NTLM_HOSTNAME_LEN:
946 {
947 ULONG* val = (ULONG*)pBuffer;
948 *val = context->Workstation.Length;
949 return SEC_E_OK;
950 }
951
952 case SECPKG_ATTR_AUTH_IDENTITY:
953 {
955 SSPI_CREDENTIALS* credentials = context->credentials;
956 if (!credentials)
957 return SEC_E_INTERNAL_ERROR;
958 if (!identityToAuthIdentity(&credentials->identity, AuthIdentity))
959 return SEC_E_INTERNAL_ERROR;
960 context->UseSamFileDatabase = FALSE;
961 return SEC_E_OK;
962 }
963 case SECPKG_ATTR_SIZES:
964 {
965 SecPkgContext_Sizes* ContextSizes = (SecPkgContext_Sizes*)pBuffer;
966 ContextSizes->cbMaxToken = 2010;
967 ContextSizes->cbMaxSignature = 16; /* the size of expected signature is 16 bytes */
968 ContextSizes->cbBlockSize = 0; /* no padding */
969 ContextSizes->cbSecurityTrailer = 16; /* no security trailer appended in NTLM
970 contrary to Kerberos */
971 return SEC_E_OK;
972 }
973 case SECPKG_ATTR_AUTH_NTLM_NTPROOF_VALUE:
974 return ntlm_computeProofValue(context, (SecBuffer*)pBuffer);
975
976 case SECPKG_ATTR_AUTH_NTLM_RANDKEY:
977 {
978 SecBuffer* randkey = (SecBuffer*)pBuffer;
979
980 if (!sspi_SecBufferAlloc(randkey, 16))
981 return (SEC_E_INSUFFICIENT_MEMORY);
982
983 CopyMemory(randkey->pvBuffer, context->EncryptedRandomSessionKey, 16);
984 return (SEC_E_OK);
985 }
986
987 case SECPKG_ATTR_AUTH_NTLM_MIC:
988 {
989 SecBuffer* mic = (SecBuffer*)pBuffer;
990 NTLM_AUTHENTICATE_MESSAGE* message = &context->AUTHENTICATE_MESSAGE;
991
992 if (!sspi_SecBufferAlloc(mic, 16))
993 return (SEC_E_INSUFFICIENT_MEMORY);
994
995 CopyMemory(mic->pvBuffer, message->MessageIntegrityCheck, 16);
996 return (SEC_E_OK);
997 }
998
999 case SECPKG_ATTR_AUTH_NTLM_MIC_VALUE:
1000 return ntlm_computeMicValue(context, (SecBuffer*)pBuffer);
1001
1002 default:
1003 WLog_ERR(TAG, "TODO: Implement ulAttribute=0x%08" PRIx32, ulAttribute);
1004 return SEC_E_UNSUPPORTED_FUNCTION;
1005 }
1006}
1007
1008/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa379337/ */
1009
1010static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesW(PCtxtHandle phContext,
1011 ULONG ulAttribute, void* pBuffer)
1012{
1013 if (!phContext)
1014 return SEC_E_INVALID_HANDLE;
1015
1016 if (!pBuffer)
1017 return SEC_E_INSUFFICIENT_MEMORY;
1018
1019 NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
1020 if (!check_context(context))
1021 return SEC_E_INVALID_HANDLE;
1022
1023 switch (ulAttribute)
1024 {
1025 case SECPKG_ATTR_AUTH_NTLM_HOSTNAME:
1026 {
1027 memcpy(pBuffer, context->Workstation.Buffer, context->Workstation.Length);
1028 return SEC_E_OK;
1029 }
1030
1031 case SECPKG_ATTR_PACKAGE_INFO:
1032 {
1034 size_t size = sizeof(SecPkgInfoW);
1035 SecPkgInfoW* pPackageInfo =
1036 (SecPkgInfoW*)sspi_ContextBufferAlloc(QuerySecurityPackageInfoIndex, size);
1037
1038 if (!pPackageInfo)
1039 return SEC_E_INSUFFICIENT_MEMORY;
1040
1041 pPackageInfo->fCapabilities = NTLM_SecPkgInfoW.fCapabilities;
1042 pPackageInfo->wVersion = NTLM_SecPkgInfoW.wVersion;
1043 pPackageInfo->wRPCID = NTLM_SecPkgInfoW.wRPCID;
1044 pPackageInfo->cbMaxToken = NTLM_SecPkgInfoW.cbMaxToken;
1045 pPackageInfo->Name = _wcsdup(NTLM_SecPkgInfoW.Name);
1046 pPackageInfo->Comment = _wcsdup(NTLM_SecPkgInfoW.Comment);
1047
1048 if (!pPackageInfo->Name || !pPackageInfo->Comment)
1049 {
1050 sspi_ContextBufferFree(pPackageInfo);
1051 return SEC_E_INSUFFICIENT_MEMORY;
1052 }
1053 PackageInfo->PackageInfo = pPackageInfo;
1054 return SEC_E_OK;
1055 }
1056 default:
1057 return ntlm_QueryContextAttributesCommon(phContext, ulAttribute, pBuffer);
1058 }
1059}
1060
1061static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesA(PCtxtHandle phContext,
1062 ULONG ulAttribute, void* pBuffer)
1063{
1064 if (!phContext)
1065 return SEC_E_INVALID_HANDLE;
1066
1067 if (!pBuffer)
1068 return SEC_E_INSUFFICIENT_MEMORY;
1069
1070 NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
1071 if (!check_context(context))
1072 return SEC_E_INVALID_HANDLE;
1073
1074 switch (ulAttribute)
1075 {
1076 case SECPKG_ATTR_AUTH_NTLM_HOSTNAME:
1077 {
1078 ConvertWCharNToUtf8(context->Workstation.Buffer, context->Workstation.Length, pBuffer,
1079 context->Workstation.Length);
1080 return SEC_E_OK;
1081 }
1082
1083 case SECPKG_ATTR_PACKAGE_INFO:
1084 {
1086 size_t size = sizeof(SecPkgInfoA);
1087 SecPkgInfoA* pPackageInfo =
1088 (SecPkgInfoA*)sspi_ContextBufferAlloc(QuerySecurityPackageInfoIndex, size);
1089
1090 if (!pPackageInfo)
1091 return SEC_E_INSUFFICIENT_MEMORY;
1092
1093 pPackageInfo->fCapabilities = NTLM_SecPkgInfoA.fCapabilities;
1094 pPackageInfo->wVersion = NTLM_SecPkgInfoA.wVersion;
1095 pPackageInfo->wRPCID = NTLM_SecPkgInfoA.wRPCID;
1096 pPackageInfo->cbMaxToken = NTLM_SecPkgInfoA.cbMaxToken;
1097 pPackageInfo->Name = _strdup(NTLM_SecPkgInfoA.Name);
1098 pPackageInfo->Comment = _strdup(NTLM_SecPkgInfoA.Comment);
1099
1100 if (!pPackageInfo->Name || !pPackageInfo->Comment)
1101 {
1102 sspi_ContextBufferFree(pPackageInfo);
1103 return SEC_E_INSUFFICIENT_MEMORY;
1104 }
1105 PackageInfo->PackageInfo = pPackageInfo;
1106 return SEC_E_OK;
1107 }
1108
1109 default:
1110 return ntlm_QueryContextAttributesCommon(phContext, ulAttribute, pBuffer);
1111 }
1112}
1113
1114static SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesCommon(PCtxtHandle phContext,
1115 ULONG ulAttribute, void* pBuffer,
1116 ULONG cbBuffer)
1117{
1118 if (!phContext)
1119 return SEC_E_INVALID_HANDLE;
1120
1121 if (!pBuffer)
1122 return SEC_E_INVALID_PARAMETER;
1123
1124 NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
1125 if (!context)
1126 return SEC_E_INVALID_HANDLE;
1127
1128 switch (ulAttribute)
1129 {
1130 case SECPKG_ATTR_AUTH_NTLM_HASH:
1131 {
1133
1134 if (cbBuffer < sizeof(SecPkgContext_AuthNtlmHash))
1135 return SEC_E_INVALID_PARAMETER;
1136
1137 if (AuthNtlmHash->Version == 1)
1138 CopyMemory(context->NtlmHash, AuthNtlmHash->NtlmHash, 16);
1139 else if (AuthNtlmHash->Version == 2)
1140 CopyMemory(context->NtlmV2Hash, AuthNtlmHash->NtlmHash, 16);
1141
1142 return SEC_E_OK;
1143 }
1144
1145 case SECPKG_ATTR_AUTH_NTLM_MESSAGE:
1146 {
1147 SecPkgContext_AuthNtlmMessage* AuthNtlmMessage =
1149
1150 if (cbBuffer < sizeof(SecPkgContext_AuthNtlmMessage))
1151 return SEC_E_INVALID_PARAMETER;
1152
1153 if (AuthNtlmMessage->type == 1)
1154 {
1155 sspi_SecBufferFree(&context->NegotiateMessage);
1156
1157 if (!sspi_SecBufferAlloc(&context->NegotiateMessage, AuthNtlmMessage->length))
1158 return SEC_E_INSUFFICIENT_MEMORY;
1159
1160 CopyMemory(context->NegotiateMessage.pvBuffer, AuthNtlmMessage->buffer,
1161 AuthNtlmMessage->length);
1162 }
1163 else if (AuthNtlmMessage->type == 2)
1164 {
1165 sspi_SecBufferFree(&context->ChallengeMessage);
1166
1167 if (!sspi_SecBufferAlloc(&context->ChallengeMessage, AuthNtlmMessage->length))
1168 return SEC_E_INSUFFICIENT_MEMORY;
1169
1170 CopyMemory(context->ChallengeMessage.pvBuffer, AuthNtlmMessage->buffer,
1171 AuthNtlmMessage->length);
1172 }
1173 else if (AuthNtlmMessage->type == 3)
1174 {
1175 sspi_SecBufferFree(&context->AuthenticateMessage);
1176
1177 if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, AuthNtlmMessage->length))
1178 return SEC_E_INSUFFICIENT_MEMORY;
1179
1180 CopyMemory(context->AuthenticateMessage.pvBuffer, AuthNtlmMessage->buffer,
1181 AuthNtlmMessage->length);
1182 }
1183
1184 return SEC_E_OK;
1185 }
1186
1187 case SECPKG_ATTR_AUTH_NTLM_TIMESTAMP:
1188 {
1189 SecPkgContext_AuthNtlmTimestamp* AuthNtlmTimestamp =
1191
1192 if (cbBuffer < sizeof(SecPkgContext_AuthNtlmTimestamp))
1193 return SEC_E_INVALID_PARAMETER;
1194
1195 if (AuthNtlmTimestamp->ChallengeOrResponse)
1196 CopyMemory(context->ChallengeTimestamp, AuthNtlmTimestamp->Timestamp, 8);
1197 else
1198 CopyMemory(context->Timestamp, AuthNtlmTimestamp->Timestamp, 8);
1199
1200 return SEC_E_OK;
1201 }
1202
1203 case SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE:
1204 {
1205 SecPkgContext_AuthNtlmClientChallenge* AuthNtlmClientChallenge =
1207
1208 if (cbBuffer < sizeof(SecPkgContext_AuthNtlmClientChallenge))
1209 return SEC_E_INVALID_PARAMETER;
1210
1211 CopyMemory(context->ClientChallenge, AuthNtlmClientChallenge->ClientChallenge, 8);
1212 return SEC_E_OK;
1213 }
1214
1215 case SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE:
1216 {
1217 SecPkgContext_AuthNtlmServerChallenge* AuthNtlmServerChallenge =
1219
1220 if (cbBuffer < sizeof(SecPkgContext_AuthNtlmServerChallenge))
1221 return SEC_E_INVALID_PARAMETER;
1222
1223 CopyMemory(context->ServerChallenge, AuthNtlmServerChallenge->ServerChallenge, 8);
1224 return SEC_E_OK;
1225 }
1226
1227 default:
1228 WLog_ERR(TAG, "TODO: Implement ulAttribute=%08" PRIx32, ulAttribute);
1229 return SEC_E_UNSUPPORTED_FUNCTION;
1230 }
1231}
1232
1233static SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesW(PCtxtHandle phContext,
1234 ULONG ulAttribute, void* pBuffer,
1235 ULONG cbBuffer)
1236{
1237 if (!phContext)
1238 return SEC_E_INVALID_HANDLE;
1239
1240 if (!pBuffer)
1241 return SEC_E_INVALID_PARAMETER;
1242
1243 NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
1244 if (!context)
1245 return SEC_E_INVALID_HANDLE;
1246
1247 switch (ulAttribute)
1248 {
1249 case SECPKG_ATTR_AUTH_NTLM_HOSTNAME:
1250 return ntlm_SetContextWorkstationX(context, TRUE, pBuffer, cbBuffer);
1251
1252 default:
1253 return ntlm_SetContextAttributesCommon(phContext, ulAttribute, pBuffer, cbBuffer);
1254 }
1255}
1256
1257static SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesA(PCtxtHandle phContext,
1258 ULONG ulAttribute, void* pBuffer,
1259 ULONG cbBuffer)
1260{
1261 if (!phContext)
1262 return SEC_E_INVALID_HANDLE;
1263
1264 if (!pBuffer)
1265 return SEC_E_INVALID_PARAMETER;
1266
1267 NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
1268 if (!context)
1269 return SEC_E_INVALID_HANDLE;
1270
1271 switch (ulAttribute)
1272 {
1273 case SECPKG_ATTR_AUTH_NTLM_HOSTNAME:
1274 return ntlm_SetContextWorkstationX(context, FALSE, pBuffer, cbBuffer);
1275
1276 default:
1277 return ntlm_SetContextAttributesCommon(phContext, ulAttribute, pBuffer, cbBuffer);
1278 }
1279}
1280
1281static SECURITY_STATUS SEC_ENTRY ntlm_SetCredentialsAttributesW(
1282 WINPR_ATTR_UNUSED PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
1283 WINPR_ATTR_UNUSED void* pBuffer, WINPR_ATTR_UNUSED ULONG cbBuffer)
1284{
1285 return SEC_E_UNSUPPORTED_FUNCTION;
1286}
1287
1288static SECURITY_STATUS SEC_ENTRY ntlm_SetCredentialsAttributesA(
1289 WINPR_ATTR_UNUSED PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
1290 WINPR_ATTR_UNUSED void* pBuffer, WINPR_ATTR_UNUSED ULONG cbBuffer)
1291{
1292 return SEC_E_UNSUPPORTED_FUNCTION;
1293}
1294
1295static SECURITY_STATUS SEC_ENTRY ntlm_RevertSecurityContext(WINPR_ATTR_UNUSED PCtxtHandle phContext)
1296{
1297 return SEC_E_OK;
1298}
1299
1300static SECURITY_STATUS SEC_ENTRY ntlm_EncryptMessage(PCtxtHandle phContext,
1301 WINPR_ATTR_UNUSED ULONG fQOP,
1302 PSecBufferDesc pMessage, ULONG MessageSeqNo)
1303{
1304 const UINT32 SeqNo = MessageSeqNo;
1305 UINT32 value = 0;
1306 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1307 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1308 ULONG version = 1;
1309 PSecBuffer data_buffer = nullptr;
1310 PSecBuffer signature_buffer = nullptr;
1311 NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
1312 if (!check_context(context))
1313 return SEC_E_INVALID_HANDLE;
1314
1315 for (ULONG index = 0; index < pMessage->cBuffers; index++)
1316 {
1317 SecBuffer* cur = &pMessage->pBuffers[index];
1318
1319 if (cur->BufferType & SECBUFFER_DATA)
1320 data_buffer = cur;
1321 else if (cur->BufferType & SECBUFFER_TOKEN)
1322 signature_buffer = cur;
1323 }
1324
1325 if (!data_buffer)
1326 return SEC_E_INVALID_TOKEN;
1327
1328 if (!signature_buffer)
1329 return SEC_E_INVALID_TOKEN;
1330
1331 /* Copy original data buffer */
1332 ULONG length = data_buffer->cbBuffer;
1333 void* data = malloc(length);
1334
1335 if (!data)
1336 return SEC_E_INSUFFICIENT_MEMORY;
1337
1338 CopyMemory(data, data_buffer->pvBuffer, length);
1339 /* Compute the HMAC-MD5 hash of ConcatenationOf(seq_num,data) using the client signing key */
1340 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1341
1342 BOOL success = FALSE;
1343 {
1344 if (!hmac)
1345 goto hmac_fail;
1346 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->SendSigningKey, WINPR_MD5_DIGEST_LENGTH))
1347 goto hmac_fail;
1348
1349 winpr_Data_Write_UINT32(&value, SeqNo);
1350
1351 if (!winpr_HMAC_Update(hmac, (void*)&value, 4))
1352 goto hmac_fail;
1353 if (!winpr_HMAC_Update(hmac, data, length))
1354 goto hmac_fail;
1355 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1356 goto hmac_fail;
1357 }
1358
1359 success = TRUE;
1360
1361hmac_fail:
1362 winpr_HMAC_Free(hmac);
1363 if (!success)
1364 {
1365 free(data);
1366 return SEC_E_INSUFFICIENT_MEMORY;
1367 }
1368
1369 /* Encrypt message using with RC4, result overwrites original buffer */
1370 if ((data_buffer->BufferType & SECBUFFER_READONLY) == 0)
1371 {
1372 if (context->confidentiality)
1373 {
1374 if (!winpr_RC4_Update(context->SendRc4Seal, length, (BYTE*)data,
1375 (BYTE*)data_buffer->pvBuffer))
1376 {
1377 free(data);
1378 return SEC_E_INSUFFICIENT_MEMORY;
1379 }
1380 }
1381 else
1382 CopyMemory(data_buffer->pvBuffer, data, length);
1383 }
1384
1385#ifdef WITH_DEBUG_NTLM
1386 WLog_DBG(TAG, "Data Buffer (length = %" PRIu32 ")", length);
1387 winpr_HexDump(TAG, WLOG_DEBUG, data, length);
1388 WLog_DBG(TAG, "Encrypted Data Buffer (length = %" PRIu32 ")", data_buffer->cbBuffer);
1389 winpr_HexDump(TAG, WLOG_DEBUG, data_buffer->pvBuffer, data_buffer->cbBuffer);
1390#endif
1391 free(data);
1392 /* RC4-encrypt first 8 bytes of digest */
1393 if (!winpr_RC4_Update(context->SendRc4Seal, 8, digest, checksum))
1394 return SEC_E_INSUFFICIENT_MEMORY;
1395 if ((signature_buffer->BufferType & SECBUFFER_READONLY) == 0)
1396 {
1397 BYTE* signature = signature_buffer->pvBuffer;
1398 /* Concatenate version, ciphertext and sequence number to build signature */
1399 winpr_Data_Write_UINT32(signature, version);
1400 CopyMemory(&signature[4], (void*)checksum, 8);
1401 winpr_Data_Write_UINT32(&signature[12], SeqNo);
1402 }
1403 context->SendSeqNum++;
1404#ifdef WITH_DEBUG_NTLM
1405 WLog_DBG(TAG, "Signature (length = %" PRIu32 ")", signature_buffer->cbBuffer);
1406 winpr_HexDump(TAG, WLOG_DEBUG, signature_buffer->pvBuffer, signature_buffer->cbBuffer);
1407#endif
1408 return SEC_E_OK;
1409}
1410
1411static SECURITY_STATUS SEC_ENTRY ntlm_DecryptMessage(PCtxtHandle phContext, PSecBufferDesc pMessage,
1412 ULONG MessageSeqNo,
1413 WINPR_ATTR_UNUSED PULONG pfQOP)
1414{
1415 const UINT32 SeqNo = (UINT32)MessageSeqNo;
1416 UINT32 value = 0;
1417 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1418 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1419 UINT32 version = 1;
1420 BYTE expected_signature[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1421 PSecBuffer data_buffer = nullptr;
1422 PSecBuffer signature_buffer = nullptr;
1423 NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
1424 if (!check_context(context))
1425 return SEC_E_INVALID_HANDLE;
1426
1427 for (ULONG index = 0; index < pMessage->cBuffers; index++)
1428 {
1429 if (pMessage->pBuffers[index].BufferType == SECBUFFER_DATA)
1430 data_buffer = &pMessage->pBuffers[index];
1431 else if (pMessage->pBuffers[index].BufferType == SECBUFFER_TOKEN)
1432 signature_buffer = &pMessage->pBuffers[index];
1433 }
1434
1435 if (!data_buffer)
1436 return SEC_E_INVALID_TOKEN;
1437
1438 if (!signature_buffer)
1439 return SEC_E_INVALID_TOKEN;
1440
1441 /* Copy original data buffer */
1442 const ULONG length = data_buffer->cbBuffer;
1443 void* data = malloc(length);
1444
1445 if (!data)
1446 return SEC_E_INSUFFICIENT_MEMORY;
1447
1448 CopyMemory(data, data_buffer->pvBuffer, length);
1449
1450 /* Decrypt message using with RC4, result overwrites original buffer */
1451
1452 if (context->confidentiality)
1453 {
1454 if (!winpr_RC4_Update(context->RecvRc4Seal, length, (BYTE*)data,
1455 (BYTE*)data_buffer->pvBuffer))
1456 {
1457 free(data);
1458 return SEC_E_INSUFFICIENT_MEMORY;
1459 }
1460 }
1461 else
1462 CopyMemory(data_buffer->pvBuffer, data, length);
1463
1464 /* Compute the HMAC-MD5 hash of ConcatenationOf(seq_num,data) using the client signing key */
1465 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1466
1467 BOOL success = FALSE;
1468 {
1469 if (!hmac)
1470 goto hmac_fail;
1471
1472 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->RecvSigningKey, WINPR_MD5_DIGEST_LENGTH))
1473 goto hmac_fail;
1474
1475 winpr_Data_Write_UINT32(&value, SeqNo);
1476
1477 if (!winpr_HMAC_Update(hmac, (void*)&value, 4))
1478 goto hmac_fail;
1479 if (!winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer))
1480 goto hmac_fail;
1481 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1482 goto hmac_fail;
1483
1484 success = TRUE;
1485 }
1486hmac_fail:
1487 winpr_HMAC_Free(hmac);
1488 if (!success)
1489 {
1490 free(data);
1491 return SEC_E_INSUFFICIENT_MEMORY;
1492 }
1493
1494#ifdef WITH_DEBUG_NTLM
1495 WLog_DBG(TAG, "Encrypted Data Buffer (length = %" PRIu32 ")", length);
1496 winpr_HexDump(TAG, WLOG_DEBUG, data, length);
1497 WLog_DBG(TAG, "Data Buffer (length = %" PRIu32 ")", data_buffer->cbBuffer);
1498 winpr_HexDump(TAG, WLOG_DEBUG, data_buffer->pvBuffer, data_buffer->cbBuffer);
1499#endif
1500 free(data);
1501 /* RC4-encrypt first 8 bytes of digest */
1502 if (!winpr_RC4_Update(context->RecvRc4Seal, 8, digest, checksum))
1503 return SEC_E_MESSAGE_ALTERED;
1504
1505 /* Concatenate version, ciphertext and sequence number to build signature */
1506 winpr_Data_Write_UINT32(expected_signature, version);
1507 CopyMemory(&expected_signature[4], (void*)checksum, 8);
1508 winpr_Data_Write_UINT32(&expected_signature[12], SeqNo);
1509 context->RecvSeqNum++;
1510
1511 if (memcmp(signature_buffer->pvBuffer, expected_signature, 16) != 0)
1512 {
1513 /* signature verification failed! */
1514 WLog_ERR(TAG, "signature verification failed, something nasty is going on!");
1515#ifdef WITH_DEBUG_NTLM
1516 WLog_ERR(TAG, "Expected Signature:");
1517 winpr_HexDump(TAG, WLOG_ERROR, expected_signature, 16);
1518 WLog_ERR(TAG, "Actual Signature:");
1519 winpr_HexDump(TAG, WLOG_ERROR, (BYTE*)signature_buffer->pvBuffer, 16);
1520#endif
1521 return SEC_E_MESSAGE_ALTERED;
1522 }
1523
1524 return SEC_E_OK;
1525}
1526
1527static SECURITY_STATUS SEC_ENTRY ntlm_MakeSignature(PCtxtHandle phContext,
1528 WINPR_ATTR_UNUSED ULONG fQOP,
1529 PSecBufferDesc pMessage, ULONG MessageSeqNo)
1530{
1531 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
1532 PSecBuffer data_buffer = nullptr;
1533 PSecBuffer sig_buffer = nullptr;
1534 UINT32 seq_no = 0;
1535 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1536 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1537
1538 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1539 if (!check_context(context))
1540 return SEC_E_INVALID_HANDLE;
1541
1542 for (ULONG i = 0; i < pMessage->cBuffers; i++)
1543 {
1544 if (pMessage->pBuffers[i].BufferType == SECBUFFER_DATA)
1545 data_buffer = &pMessage->pBuffers[i];
1546 else if (pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
1547 sig_buffer = &pMessage->pBuffers[i];
1548 }
1549
1550 if (!data_buffer || !sig_buffer)
1551 return SEC_E_INVALID_TOKEN;
1552
1553 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1554
1555 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->SendSigningKey, WINPR_MD5_DIGEST_LENGTH))
1556 goto fail;
1557
1558 winpr_Data_Write_UINT32(&seq_no, MessageSeqNo);
1559 if (!winpr_HMAC_Update(hmac, (BYTE*)&seq_no, 4))
1560 goto fail;
1561 if (!winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer))
1562 goto fail;
1563 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1564 goto fail;
1565
1566 if (!winpr_RC4_Update(context->SendRc4Seal, 8, digest, checksum))
1567 goto fail;
1568
1569 BYTE* signature = sig_buffer->pvBuffer;
1570 winpr_Data_Write_UINT32(signature, 1L);
1571 CopyMemory(&signature[4], checksum, 8);
1572 winpr_Data_Write_UINT32(&signature[12], seq_no);
1573 sig_buffer->cbBuffer = 16;
1574
1575 status = SEC_E_OK;
1576
1577fail:
1578 winpr_HMAC_Free(hmac);
1579 return status;
1580}
1581
1582static SECURITY_STATUS SEC_ENTRY ntlm_VerifySignature(PCtxtHandle phContext,
1583 PSecBufferDesc pMessage, ULONG MessageSeqNo,
1584 WINPR_ATTR_UNUSED PULONG pfQOP)
1585{
1586 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
1587 PSecBuffer data_buffer = nullptr;
1588 PSecBuffer sig_buffer = nullptr;
1589 UINT32 seq_no = 0;
1590 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1591 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1592 BYTE signature[16] = WINPR_C_ARRAY_INIT;
1593
1594 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1595 if (!check_context(context))
1596 return SEC_E_INVALID_HANDLE;
1597
1598 for (ULONG i = 0; i < pMessage->cBuffers; i++)
1599 {
1600 if (pMessage->pBuffers[i].BufferType == SECBUFFER_DATA)
1601 data_buffer = &pMessage->pBuffers[i];
1602 else if (pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
1603 sig_buffer = &pMessage->pBuffers[i];
1604 }
1605
1606 if (!data_buffer || !sig_buffer)
1607 return SEC_E_INVALID_TOKEN;
1608
1609 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1610
1611 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->RecvSigningKey, WINPR_MD5_DIGEST_LENGTH))
1612 goto fail;
1613
1614 winpr_Data_Write_UINT32(&seq_no, MessageSeqNo);
1615 if (!winpr_HMAC_Update(hmac, (BYTE*)&seq_no, 4))
1616 goto fail;
1617 if (!winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer))
1618 goto fail;
1619 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1620 goto fail;
1621
1622 if (!winpr_RC4_Update(context->RecvRc4Seal, 8, digest, checksum))
1623 goto fail;
1624
1625 winpr_Data_Write_UINT32(signature, 1L);
1626 CopyMemory(&signature[4], checksum, 8);
1627 winpr_Data_Write_UINT32(&signature[12], seq_no);
1628
1629 status = SEC_E_OK;
1630 if (memcmp(sig_buffer->pvBuffer, signature, 16) != 0)
1631 status = SEC_E_MESSAGE_ALTERED;
1632
1633fail:
1634 winpr_HMAC_Free(hmac);
1635 return status;
1636}
1637
1638const SecurityFunctionTableA NTLM_SecurityFunctionTableA = {
1639 3, /* dwVersion */
1640 nullptr, /* EnumerateSecurityPackages */
1641 ntlm_QueryCredentialsAttributesA, /* QueryCredentialsAttributes */
1642 ntlm_AcquireCredentialsHandleA, /* AcquireCredentialsHandle */
1643 ntlm_FreeCredentialsHandle, /* FreeCredentialsHandle */
1644 nullptr, /* Reserved2 */
1645 ntlm_InitializeSecurityContextA, /* InitializeSecurityContext */
1646 ntlm_AcceptSecurityContext, /* AcceptSecurityContext */
1647 nullptr, /* CompleteAuthToken */
1648 ntlm_DeleteSecurityContext, /* DeleteSecurityContext */
1649 nullptr, /* ApplyControlToken */
1650 ntlm_QueryContextAttributesA, /* QueryContextAttributes */
1651 ntlm_ImpersonateSecurityContext, /* ImpersonateSecurityContext */
1652 ntlm_RevertSecurityContext, /* RevertSecurityContext */
1653 ntlm_MakeSignature, /* MakeSignature */
1654 ntlm_VerifySignature, /* VerifySignature */
1655 nullptr, /* FreeContextBuffer */
1656 nullptr, /* QuerySecurityPackageInfo */
1657 nullptr, /* Reserved3 */
1658 nullptr, /* Reserved4 */
1659 nullptr, /* ExportSecurityContext */
1660 nullptr, /* ImportSecurityContext */
1661 nullptr, /* AddCredentials */
1662 nullptr, /* Reserved8 */
1663 nullptr, /* QuerySecurityContextToken */
1664 ntlm_EncryptMessage, /* EncryptMessage */
1665 ntlm_DecryptMessage, /* DecryptMessage */
1666 ntlm_SetContextAttributesA, /* SetContextAttributes */
1667 ntlm_SetCredentialsAttributesA, /* SetCredentialsAttributes */
1668};
1669
1670const SecurityFunctionTableW NTLM_SecurityFunctionTableW = {
1671 3, /* dwVersion */
1672 nullptr, /* EnumerateSecurityPackages */
1673 ntlm_QueryCredentialsAttributesW, /* QueryCredentialsAttributes */
1674 ntlm_AcquireCredentialsHandleW, /* AcquireCredentialsHandle */
1675 ntlm_FreeCredentialsHandle, /* FreeCredentialsHandle */
1676 nullptr, /* Reserved2 */
1677 ntlm_InitializeSecurityContextW, /* InitializeSecurityContext */
1678 ntlm_AcceptSecurityContext, /* AcceptSecurityContext */
1679 nullptr, /* CompleteAuthToken */
1680 ntlm_DeleteSecurityContext, /* DeleteSecurityContext */
1681 nullptr, /* ApplyControlToken */
1682 ntlm_QueryContextAttributesW, /* QueryContextAttributes */
1683 ntlm_ImpersonateSecurityContext, /* ImpersonateSecurityContext */
1684 ntlm_RevertSecurityContext, /* RevertSecurityContext */
1685 ntlm_MakeSignature, /* MakeSignature */
1686 ntlm_VerifySignature, /* VerifySignature */
1687 nullptr, /* FreeContextBuffer */
1688 nullptr, /* QuerySecurityPackageInfo */
1689 nullptr, /* Reserved3 */
1690 nullptr, /* Reserved4 */
1691 nullptr, /* ExportSecurityContext */
1692 nullptr, /* ImportSecurityContext */
1693 nullptr, /* AddCredentials */
1694 nullptr, /* Reserved8 */
1695 nullptr, /* QuerySecurityContextToken */
1696 ntlm_EncryptMessage, /* EncryptMessage */
1697 ntlm_DecryptMessage, /* DecryptMessage */
1698 ntlm_SetContextAttributesW, /* SetContextAttributes */
1699 ntlm_SetCredentialsAttributesW, /* SetCredentialsAttributes */
1700};
1701
1702const SecPkgInfoA NTLM_SecPkgInfoA = {
1703 0x00082B37, /* fCapabilities */
1704 1, /* wVersion */
1705 0x000A, /* wRPCID */
1706 0x00000B48, /* cbMaxToken */
1707 "NTLM", /* Name */
1708 "NTLM Security Package" /* Comment */
1709};
1710
1711static WCHAR NTLM_SecPkgInfoW_NameBuffer[32] = WINPR_C_ARRAY_INIT;
1712static WCHAR NTLM_SecPkgInfoW_CommentBuffer[32] = WINPR_C_ARRAY_INIT;
1713
1714const SecPkgInfoW NTLM_SecPkgInfoW = {
1715 0x00082B37, /* fCapabilities */
1716 1, /* wVersion */
1717 0x000A, /* wRPCID */
1718 0x00000B48, /* cbMaxToken */
1719 NTLM_SecPkgInfoW_NameBuffer, /* Name */
1720 NTLM_SecPkgInfoW_CommentBuffer /* Comment */
1721};
1722
1723char* ntlm_negotiate_flags_string(char* buffer, size_t size, UINT32 flags)
1724{
1725 if (!buffer || (size == 0))
1726 return buffer;
1727
1728 (void)_snprintf(buffer, size, "[0x%08" PRIx32 "] ", flags);
1729
1730 for (int x = 0; x < 31; x++)
1731 {
1732 const UINT32 mask = 1u << x;
1733 size_t len = strnlen(buffer, size);
1734 if (flags & mask)
1735 {
1736 const char* str = ntlm_get_negotiate_string(mask);
1737 const size_t flen = strlen(str);
1738
1739 if ((len > 0) && (buffer[len - 1] != ' '))
1740 {
1741 if (size - len < 1)
1742 break;
1743 winpr_str_append("|", buffer, size, nullptr);
1744 len++;
1745 }
1746
1747 if (size - len < flen)
1748 break;
1749 winpr_str_append(str, buffer, size, nullptr);
1750 }
1751 }
1752
1753 return buffer;
1754}
1755
1756const char* ntlm_message_type_string(UINT32 messageType)
1757{
1758 switch (messageType)
1759 {
1760 case MESSAGE_TYPE_NEGOTIATE:
1761 return "MESSAGE_TYPE_NEGOTIATE";
1762 case MESSAGE_TYPE_CHALLENGE:
1763 return "MESSAGE_TYPE_CHALLENGE";
1764 case MESSAGE_TYPE_AUTHENTICATE:
1765 return "MESSAGE_TYPE_AUTHENTICATE";
1766 default:
1767 return "MESSAGE_TYPE_UNKNOWN";
1768 }
1769}
1770
1771const char* ntlm_state_string(NTLM_STATE state)
1772{
1773 switch (state)
1774 {
1775 case NTLM_STATE_INITIAL:
1776 return "NTLM_STATE_INITIAL";
1777 case NTLM_STATE_NEGOTIATE:
1778 return "NTLM_STATE_NEGOTIATE";
1779 case NTLM_STATE_CHALLENGE:
1780 return "NTLM_STATE_CHALLENGE";
1781 case NTLM_STATE_AUTHENTICATE:
1782 return "NTLM_STATE_AUTHENTICATE";
1783 case NTLM_STATE_FINAL:
1784 return "NTLM_STATE_FINAL";
1785 default:
1786 return "NTLM_STATE_UNKNOWN";
1787 }
1788}
1789void ntlm_change_state(NTLM_CONTEXT* ntlm, NTLM_STATE state)
1790{
1791 WINPR_ASSERT(ntlm);
1792 WLog_DBG(TAG, "change state from %s to %s", ntlm_state_string(ntlm->state),
1793 ntlm_state_string(state));
1794 ntlm->state = state;
1795}
1796
1797NTLM_STATE ntlm_get_state(NTLM_CONTEXT* ntlm)
1798{
1799 WINPR_ASSERT(ntlm);
1800 return ntlm->state;
1801}
1802
1803BOOL ntlm_reset_cipher_state(PSecHandle phContext)
1804{
1805 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1806
1807 if (context)
1808 {
1809 check_context(context);
1810 winpr_RC4_Free(context->SendRc4Seal);
1811 winpr_RC4_Free(context->RecvRc4Seal);
1812 context->SendRc4Seal = winpr_RC4_New(context->RecvSealingKey, 16);
1813 context->RecvRc4Seal = winpr_RC4_New(context->SendSealingKey, 16);
1814
1815 if (!context->SendRc4Seal)
1816 {
1817 WLog_ERR(TAG, "Failed to allocate context->SendRc4Seal");
1818 return FALSE;
1819 }
1820 if (!context->RecvRc4Seal)
1821 {
1822 WLog_ERR(TAG, "Failed to allocate context->RecvRc4Seal");
1823 return FALSE;
1824 }
1825 }
1826
1827 return TRUE;
1828}
1829
1830BOOL NTLM_init(void)
1831{
1832 InitializeConstWCharFromUtf8(NTLM_SecPkgInfoA.Name, NTLM_SecPkgInfoW_NameBuffer,
1833 ARRAYSIZE(NTLM_SecPkgInfoW_NameBuffer));
1834 InitializeConstWCharFromUtf8(NTLM_SecPkgInfoA.Comment, NTLM_SecPkgInfoW_CommentBuffer,
1835 ARRAYSIZE(NTLM_SecPkgInfoW_CommentBuffer));
1836
1837 return TRUE;
1838}
WINPR_ATTR_NODISCARD psSspiNtlmHashCallback hashCallback