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