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 free(context);
365}
366
367static SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleW(
368 WINPR_ATTR_UNUSED SEC_WCHAR* pszPrincipal, WINPR_ATTR_UNUSED SEC_WCHAR* pszPackage,
369 ULONG fCredentialUse, WINPR_ATTR_UNUSED void* pvLogonID, void* pAuthData,
370 SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential,
371 WINPR_ATTR_UNUSED PTimeStamp ptsExpiry)
372{
373 SEC_WINPR_NTLM_SETTINGS* settings = nullptr;
374
375 if ((fCredentialUse != SECPKG_CRED_OUTBOUND) && (fCredentialUse != SECPKG_CRED_INBOUND) &&
376 (fCredentialUse != SECPKG_CRED_BOTH))
377 {
378 return SEC_E_INVALID_PARAMETER;
379 }
380
381 SSPI_CREDENTIALS* credentials = sspi_CredentialsNew();
382
383 if (!credentials)
384 return SEC_E_INTERNAL_ERROR;
385
386 credentials->fCredentialUse = fCredentialUse;
387 credentials->pGetKeyFn = pGetKeyFn;
388 credentials->pvGetKeyArgument = pvGetKeyArgument;
389
390 if (pAuthData)
391 {
392 UINT32 identityFlags = sspi_GetAuthIdentityFlags(pAuthData);
393
394 if (sspi_CopyAuthIdentity(&(credentials->identity),
395 (const SEC_WINNT_AUTH_IDENTITY_INFO*)pAuthData) < 0)
396 {
397 sspi_CredentialsFree(credentials);
398 return SEC_E_INVALID_PARAMETER;
399 }
400
401 if (identityFlags & SEC_WINNT_AUTH_IDENTITY_EXTENDED)
402 settings = (((SEC_WINNT_AUTH_IDENTITY_WINPR*)pAuthData)->ntlmSettings);
403 }
404
405 if (settings)
406 {
407 if (settings->samFile)
408 {
409 credentials->ntlmSettings.samFile = _strdup(settings->samFile);
410 if (!credentials->ntlmSettings.samFile)
411 {
412 sspi_CredentialsFree(credentials);
413 return SEC_E_INSUFFICIENT_MEMORY;
414 }
415 }
416 credentials->ntlmSettings.hashCallback = settings->hashCallback;
417 credentials->ntlmSettings.hashCallbackArg = settings->hashCallbackArg;
418 }
419
420 sspi_SecureHandleSetLowerPointer(phCredential, (void*)credentials);
421 sspi_SecureHandleSetUpperPointer(phCredential, (void*)NTLM_PACKAGE_NAME);
422 return SEC_E_OK;
423}
424
425static SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleA(
426 SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID,
427 void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential,
428 PTimeStamp ptsExpiry)
429{
430 SECURITY_STATUS status = SEC_E_INSUFFICIENT_MEMORY;
431 SEC_WCHAR* principal = nullptr;
432 SEC_WCHAR* package = nullptr;
433
434 if (pszPrincipal)
435 {
436 principal = ConvertUtf8ToWCharAlloc(pszPrincipal, nullptr);
437 if (!principal)
438 goto fail;
439 }
440 if (pszPackage)
441 {
442 package = ConvertUtf8ToWCharAlloc(pszPackage, nullptr);
443 if (!package)
444 goto fail;
445 }
446
447 status =
448 ntlm_AcquireCredentialsHandleW(principal, package, fCredentialUse, pvLogonID, pAuthData,
449 pGetKeyFn, pvGetKeyArgument, phCredential, ptsExpiry);
450
451fail:
452 free(principal);
453 free(package);
454
455 return status;
456}
457
458static SECURITY_STATUS SEC_ENTRY ntlm_FreeCredentialsHandle(PCredHandle phCredential)
459{
460 if (!phCredential)
461 return SEC_E_INVALID_HANDLE;
462
463 SSPI_CREDENTIALS* credentials =
464 (SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
465
466 if (!credentials)
467 return SEC_E_INVALID_HANDLE;
468
469 sspi_CredentialsFree(credentials);
470 return SEC_E_OK;
471}
472
473static SECURITY_STATUS SEC_ENTRY ntlm_QueryCredentialsAttributesW(
474 WINPR_ATTR_UNUSED PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
475 WINPR_ATTR_UNUSED void* pBuffer)
476{
477 if (ulAttribute == SECPKG_CRED_ATTR_NAMES)
478 {
479 return SEC_E_OK;
480 }
481
482 WLog_ERR(TAG, "TODO: Implement");
483 return SEC_E_UNSUPPORTED_FUNCTION;
484}
485
486static SECURITY_STATUS SEC_ENTRY ntlm_QueryCredentialsAttributesA(PCredHandle phCredential,
487 ULONG ulAttribute, void* pBuffer)
488{
489 return ntlm_QueryCredentialsAttributesW(phCredential, ulAttribute, pBuffer);
490}
491
495static SECURITY_STATUS SEC_ENTRY ntlm_AcceptSecurityContext(
496 PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput, ULONG fContextReq,
497 WINPR_ATTR_UNUSED ULONG TargetDataRep, PCtxtHandle phNewContext, PSecBufferDesc pOutput,
498 WINPR_ATTR_UNUSED PULONG pfContextAttr, WINPR_ATTR_UNUSED PTimeStamp ptsTimeStamp)
499{
500 SECURITY_STATUS status = 0;
501 SSPI_CREDENTIALS* credentials = nullptr;
502 PSecBuffer input_buffer = nullptr;
503 PSecBuffer output_buffer = nullptr;
504
505 /* behave like windows SSPIs that don't want empty context */
506 if (phContext && !phContext->dwLower && !phContext->dwUpper)
507 return SEC_E_INVALID_HANDLE;
508
509 NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
510
511 if (!context)
512 {
513 context = ntlm_ContextNew();
514
515 if (!context)
516 return SEC_E_INSUFFICIENT_MEMORY;
517
518 context->server = TRUE;
519
520 if (fContextReq & ASC_REQ_CONFIDENTIALITY)
521 context->confidentiality = TRUE;
522
523 credentials = (SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
524 context->credentials = credentials;
525 context->SamFile = credentials->ntlmSettings.samFile;
526 context->HashCallback = credentials->ntlmSettings.hashCallback;
527 context->HashCallbackArg = credentials->ntlmSettings.hashCallbackArg;
528
529 ntlm_SetContextTargetName(context, nullptr);
530 sspi_SecureHandleSetLowerPointer(phNewContext, context);
531 sspi_SecureHandleSetUpperPointer(phNewContext, (void*)NTLM_PACKAGE_NAME);
532 }
533
534 switch (ntlm_get_state(context))
535 {
536 case NTLM_STATE_INITIAL:
537 {
538 ntlm_change_state(context, NTLM_STATE_NEGOTIATE);
539
540 if (!pInput)
541 return SEC_E_INVALID_TOKEN;
542
543 if (pInput->cBuffers < 1)
544 return SEC_E_INVALID_TOKEN;
545
546 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
547
548 if (!input_buffer)
549 return SEC_E_INVALID_TOKEN;
550
551 if (input_buffer->cbBuffer < 1)
552 return SEC_E_INVALID_TOKEN;
553
554 status = ntlm_read_NegotiateMessage(context, input_buffer);
555 if (status != SEC_I_CONTINUE_NEEDED)
556 return status;
557
558 if (ntlm_get_state(context) == NTLM_STATE_CHALLENGE)
559 {
560 if (!pOutput)
561 return SEC_E_INVALID_TOKEN;
562
563 if (pOutput->cBuffers < 1)
564 return SEC_E_INVALID_TOKEN;
565
566 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
567
568 if (!output_buffer->BufferType)
569 return SEC_E_INVALID_TOKEN;
570
571 if (output_buffer->cbBuffer < 1)
572 return SEC_E_INSUFFICIENT_MEMORY;
573
574 return ntlm_write_ChallengeMessage(context, output_buffer);
575 }
576
577 return SEC_E_OUT_OF_SEQUENCE;
578 }
579
580 case NTLM_STATE_AUTHENTICATE:
581 {
582 if (!pInput)
583 return SEC_E_INVALID_TOKEN;
584
585 if (pInput->cBuffers < 1)
586 return SEC_E_INVALID_TOKEN;
587
588 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
589
590 if (!input_buffer)
591 return SEC_E_INVALID_TOKEN;
592
593 if (input_buffer->cbBuffer < 1)
594 return SEC_E_INVALID_TOKEN;
595
596 status = ntlm_read_AuthenticateMessage(context, input_buffer);
597
598 if (pOutput)
599 {
600 for (ULONG i = 0; i < pOutput->cBuffers; i++)
601 {
602 pOutput->pBuffers[i].cbBuffer = 0;
603 pOutput->pBuffers[i].BufferType = SECBUFFER_TOKEN;
604 }
605 }
606
607 return status;
608 }
609
610 default:
611 return SEC_E_OUT_OF_SEQUENCE;
612 }
613}
614
615static SECURITY_STATUS SEC_ENTRY
616ntlm_ImpersonateSecurityContext(WINPR_ATTR_UNUSED PCtxtHandle phContext)
617{
618 return SEC_E_OK;
619}
620
621static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextW(
622 PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR* pszTargetName, ULONG fContextReq,
623 WINPR_ATTR_UNUSED ULONG Reserved1, WINPR_ATTR_UNUSED ULONG TargetDataRep, PSecBufferDesc pInput,
624 WINPR_ATTR_UNUSED ULONG Reserved2, PCtxtHandle phNewContext, PSecBufferDesc pOutput,
625 WINPR_ATTR_UNUSED PULONG pfContextAttr, WINPR_ATTR_UNUSED PTimeStamp ptsExpiry)
626{
627 SECURITY_STATUS status = 0;
628 SSPI_CREDENTIALS* credentials = nullptr;
629 PSecBuffer input_buffer = nullptr;
630 PSecBuffer output_buffer = nullptr;
631
632 /* behave like windows SSPIs that don't want empty context */
633 if (phContext && !phContext->dwLower && !phContext->dwUpper)
634 return SEC_E_INVALID_HANDLE;
635
636 NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
637
638 if (pInput)
639 {
640 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
641 }
642
643 if (!context)
644 {
645 context = ntlm_ContextNew();
646
647 if (!context)
648 return SEC_E_INSUFFICIENT_MEMORY;
649
650 if (fContextReq & ISC_REQ_CONFIDENTIALITY)
651 context->confidentiality = TRUE;
652
653 credentials = (SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
654 context->credentials = credentials;
655
656 if (context->Workstation.Length < 1)
657 {
658 if (ntlm_SetContextWorkstation(context, nullptr) < 0)
659 {
660 ntlm_ContextFree(context);
661 return SEC_E_INTERNAL_ERROR;
662 }
663 }
664
665 if (ntlm_SetContextServicePrincipalNameW(context, pszTargetName) < 0)
666 {
667 ntlm_ContextFree(context);
668 return SEC_E_INTERNAL_ERROR;
669 }
670
671 sspi_SecureHandleSetLowerPointer(phNewContext, context);
672 sspi_SecureHandleSetUpperPointer(phNewContext, NTLM_SSP_NAME);
673 }
674
675 if ((!input_buffer) || (ntlm_get_state(context) == NTLM_STATE_AUTHENTICATE))
676 {
677 if (!pOutput)
678 return SEC_E_INVALID_TOKEN;
679
680 if (pOutput->cBuffers < 1)
681 return SEC_E_INVALID_TOKEN;
682
683 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
684
685 if (!output_buffer)
686 return SEC_E_INVALID_TOKEN;
687
688 if (output_buffer->cbBuffer < 1)
689 return SEC_E_INVALID_TOKEN;
690
691 if (ntlm_get_state(context) == NTLM_STATE_INITIAL)
692 ntlm_change_state(context, NTLM_STATE_NEGOTIATE);
693
694 if (ntlm_get_state(context) == NTLM_STATE_NEGOTIATE)
695 return ntlm_write_NegotiateMessage(context, output_buffer);
696
697 return SEC_E_OUT_OF_SEQUENCE;
698 }
699 else
700 {
701 if (!input_buffer)
702 return SEC_E_INVALID_TOKEN;
703
704 if (input_buffer->cbBuffer < 1)
705 return SEC_E_INVALID_TOKEN;
706
707 PSecBuffer channel_bindings = sspi_FindSecBuffer(pInput, SECBUFFER_CHANNEL_BINDINGS);
708
709 if (channel_bindings)
710 {
711 context->Bindings.BindingsLength = channel_bindings->cbBuffer;
712 context->Bindings.Bindings = (SEC_CHANNEL_BINDINGS*)channel_bindings->pvBuffer;
713 }
714
715 if (ntlm_get_state(context) == NTLM_STATE_CHALLENGE)
716 {
717 status = ntlm_read_ChallengeMessage(context, input_buffer);
718
719 if (status != SEC_I_CONTINUE_NEEDED)
720 return status;
721
722 if (!pOutput)
723 return SEC_E_INVALID_TOKEN;
724
725 if (pOutput->cBuffers < 1)
726 return SEC_E_INVALID_TOKEN;
727
728 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
729
730 if (!output_buffer)
731 return SEC_E_INVALID_TOKEN;
732
733 if (output_buffer->cbBuffer < 1)
734 return SEC_E_INSUFFICIENT_MEMORY;
735
736 if (ntlm_get_state(context) == NTLM_STATE_AUTHENTICATE)
737 return ntlm_write_AuthenticateMessage(context, output_buffer);
738 }
739
740 return SEC_E_OUT_OF_SEQUENCE;
741 }
742
743 return SEC_E_OUT_OF_SEQUENCE;
744}
745
749static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextA(
750 PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR* pszTargetName, ULONG fContextReq,
751 ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2,
752 PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry)
753{
754 SECURITY_STATUS status = 0;
755 SEC_WCHAR* pszTargetNameW = nullptr;
756
757 if (pszTargetName)
758 {
759 pszTargetNameW = ConvertUtf8ToWCharAlloc(pszTargetName, nullptr);
760 if (!pszTargetNameW)
761 return SEC_E_INTERNAL_ERROR;
762 }
763
764 status = ntlm_InitializeSecurityContextW(phCredential, phContext, pszTargetNameW, fContextReq,
765 Reserved1, TargetDataRep, pInput, Reserved2,
766 phNewContext, pOutput, pfContextAttr, ptsExpiry);
767 free(pszTargetNameW);
768 return status;
769}
770
771/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375354 */
772
773static SECURITY_STATUS SEC_ENTRY ntlm_DeleteSecurityContext(PCtxtHandle phContext)
774{
775 NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
776 ntlm_ContextFree(context);
777 return SEC_E_OK;
778}
779
780SECURITY_STATUS ntlm_computeProofValue(NTLM_CONTEXT* ntlm, SecBuffer* ntproof)
781{
782 BYTE* blob = nullptr;
783 SecBuffer* target = nullptr;
784
785 WINPR_ASSERT(ntlm);
786 WINPR_ASSERT(ntproof);
787
788 target = &ntlm->ChallengeTargetInfo;
789
790 if (!sspi_SecBufferAlloc(ntproof, 36 + target->cbBuffer))
791 return SEC_E_INSUFFICIENT_MEMORY;
792
793 blob = (BYTE*)ntproof->pvBuffer;
794 CopyMemory(blob, ntlm->ServerChallenge, 8); /* Server challenge. */
795 blob[8] = 1; /* Response version. */
796 blob[9] = 1; /* Highest response version understood by the client. */
797 /* Reserved 6B. */
798 CopyMemory(&blob[16], ntlm->Timestamp, 8); /* Time. */
799 CopyMemory(&blob[24], ntlm->ClientChallenge, 8); /* Client challenge. */
800 /* Reserved 4B. */
801 /* Server name. */
802 CopyMemory(&blob[36], target->pvBuffer, target->cbBuffer);
803 return SEC_E_OK;
804}
805
806SECURITY_STATUS ntlm_computeMicValue(NTLM_CONTEXT* ntlm, SecBuffer* micvalue)
807{
808 BYTE* blob = nullptr;
809 ULONG msgSize = 0;
810
811 WINPR_ASSERT(ntlm);
812 WINPR_ASSERT(micvalue);
813
814 msgSize = ntlm->NegotiateMessage.cbBuffer + ntlm->ChallengeMessage.cbBuffer +
815 ntlm->AuthenticateMessage.cbBuffer;
816
817 if (!sspi_SecBufferAlloc(micvalue, msgSize))
818 return SEC_E_INSUFFICIENT_MEMORY;
819
820 blob = (BYTE*)micvalue->pvBuffer;
821 CopyMemory(blob, ntlm->NegotiateMessage.pvBuffer, ntlm->NegotiateMessage.cbBuffer);
822 blob += ntlm->NegotiateMessage.cbBuffer;
823 CopyMemory(blob, ntlm->ChallengeMessage.pvBuffer, ntlm->ChallengeMessage.cbBuffer);
824 blob += ntlm->ChallengeMessage.cbBuffer;
825 CopyMemory(blob, ntlm->AuthenticateMessage.pvBuffer, ntlm->AuthenticateMessage.cbBuffer);
826 blob += ntlm->MessageIntegrityCheckOffset;
827 ZeroMemory(blob, 16);
828 return SEC_E_OK;
829}
830
831/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa379337/ */
832
833static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesW(PCtxtHandle phContext,
834 ULONG ulAttribute, void* pBuffer)
835{
836 if (!phContext)
837 return SEC_E_INVALID_HANDLE;
838
839 if (!pBuffer)
840 return SEC_E_INSUFFICIENT_MEMORY;
841
842 NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
843 if (!check_context(context))
844 return SEC_E_INVALID_HANDLE;
845
846 if (ulAttribute == SECPKG_ATTR_SIZES)
847 {
848 SecPkgContext_Sizes* ContextSizes = (SecPkgContext_Sizes*)pBuffer;
849 ContextSizes->cbMaxToken = 2010;
850 ContextSizes->cbMaxSignature = 16; /* the size of expected signature is 16 bytes */
851 ContextSizes->cbBlockSize = 0; /* no padding */
852 ContextSizes->cbSecurityTrailer = 16; /* no security trailer appended in NTLM
853 contrary to Kerberos */
854 return SEC_E_OK;
855 }
856 else if (ulAttribute == SECPKG_ATTR_AUTH_IDENTITY)
857 {
858 SSPI_CREDENTIALS* credentials = nullptr;
859 const SecPkgContext_AuthIdentity empty = WINPR_C_ARRAY_INIT;
861
862 WINPR_ASSERT(AuthIdentity);
863 *AuthIdentity = empty;
864
865 context->UseSamFileDatabase = FALSE;
866 credentials = context->credentials;
867
868 if (credentials->identity.UserLength > 0)
869 {
870 if (ConvertWCharNToUtf8(credentials->identity.User, credentials->identity.UserLength,
871 AuthIdentity->User, ARRAYSIZE(AuthIdentity->User)) <= 0)
872 return SEC_E_INTERNAL_ERROR;
873 }
874
875 if (credentials->identity.DomainLength > 0)
876 {
877 if (ConvertWCharNToUtf8(credentials->identity.Domain,
878 credentials->identity.DomainLength, AuthIdentity->Domain,
879 ARRAYSIZE(AuthIdentity->Domain)) <= 0)
880 return SEC_E_INTERNAL_ERROR;
881 }
882
883 return SEC_E_OK;
884 }
885 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_NTPROOF_VALUE)
886 {
887 return ntlm_computeProofValue(context, (SecBuffer*)pBuffer);
888 }
889 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_RANDKEY)
890 {
891 SecBuffer* randkey = nullptr;
892 randkey = (SecBuffer*)pBuffer;
893
894 if (!sspi_SecBufferAlloc(randkey, 16))
895 return (SEC_E_INSUFFICIENT_MEMORY);
896
897 CopyMemory(randkey->pvBuffer, context->EncryptedRandomSessionKey, 16);
898 return (SEC_E_OK);
899 }
900 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MIC)
901 {
902 SecBuffer* mic = (SecBuffer*)pBuffer;
903 NTLM_AUTHENTICATE_MESSAGE* message = &context->AUTHENTICATE_MESSAGE;
904
905 if (!sspi_SecBufferAlloc(mic, 16))
906 return (SEC_E_INSUFFICIENT_MEMORY);
907
908 CopyMemory(mic->pvBuffer, message->MessageIntegrityCheck, 16);
909 return (SEC_E_OK);
910 }
911 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MIC_VALUE)
912 {
913 return ntlm_computeMicValue(context, (SecBuffer*)pBuffer);
914 }
915 else if (ulAttribute == SECPKG_ATTR_PACKAGE_INFO)
916 {
918 size_t size = sizeof(SecPkgInfoA);
919 SecPkgInfoA* pPackageInfo =
920 (SecPkgInfoA*)sspi_ContextBufferAlloc(QuerySecurityPackageInfoIndex, size);
921
922 if (!pPackageInfo)
923 return SEC_E_INSUFFICIENT_MEMORY;
924
925 pPackageInfo->fCapabilities = NTLM_SecPkgInfoA.fCapabilities;
926 pPackageInfo->wVersion = NTLM_SecPkgInfoA.wVersion;
927 pPackageInfo->wRPCID = NTLM_SecPkgInfoA.wRPCID;
928 pPackageInfo->cbMaxToken = NTLM_SecPkgInfoA.cbMaxToken;
929 pPackageInfo->Name = _strdup(NTLM_SecPkgInfoA.Name);
930 pPackageInfo->Comment = _strdup(NTLM_SecPkgInfoA.Comment);
931
932 if (!pPackageInfo->Name || !pPackageInfo->Comment)
933 {
934 sspi_ContextBufferFree(pPackageInfo);
935 return SEC_E_INSUFFICIENT_MEMORY;
936 }
937 PackageInfo->PackageInfo = pPackageInfo;
938 return SEC_E_OK;
939 }
940
941 WLog_ERR(TAG, "TODO: Implement ulAttribute=0x%08" PRIx32, ulAttribute);
942 return SEC_E_UNSUPPORTED_FUNCTION;
943}
944
945static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesA(PCtxtHandle phContext,
946 ULONG ulAttribute, void* pBuffer)
947{
948 return ntlm_QueryContextAttributesW(phContext, ulAttribute, pBuffer);
949}
950
951static SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesW(PCtxtHandle phContext,
952 ULONG ulAttribute, void* pBuffer,
953 ULONG cbBuffer)
954{
955 if (!phContext)
956 return SEC_E_INVALID_HANDLE;
957
958 if (!pBuffer)
959 return SEC_E_INVALID_PARAMETER;
960
961 NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
962 if (!context)
963 return SEC_E_INVALID_HANDLE;
964
965 if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_HASH)
966 {
968
969 if (cbBuffer < sizeof(SecPkgContext_AuthNtlmHash))
970 return SEC_E_INVALID_PARAMETER;
971
972 if (AuthNtlmHash->Version == 1)
973 CopyMemory(context->NtlmHash, AuthNtlmHash->NtlmHash, 16);
974 else if (AuthNtlmHash->Version == 2)
975 CopyMemory(context->NtlmV2Hash, AuthNtlmHash->NtlmHash, 16);
976
977 return SEC_E_OK;
978 }
979 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MESSAGE)
980 {
982
983 if (cbBuffer < sizeof(SecPkgContext_AuthNtlmMessage))
984 return SEC_E_INVALID_PARAMETER;
985
986 if (AuthNtlmMessage->type == 1)
987 {
988 sspi_SecBufferFree(&context->NegotiateMessage);
989
990 if (!sspi_SecBufferAlloc(&context->NegotiateMessage, AuthNtlmMessage->length))
991 return SEC_E_INSUFFICIENT_MEMORY;
992
993 CopyMemory(context->NegotiateMessage.pvBuffer, AuthNtlmMessage->buffer,
994 AuthNtlmMessage->length);
995 }
996 else if (AuthNtlmMessage->type == 2)
997 {
998 sspi_SecBufferFree(&context->ChallengeMessage);
999
1000 if (!sspi_SecBufferAlloc(&context->ChallengeMessage, AuthNtlmMessage->length))
1001 return SEC_E_INSUFFICIENT_MEMORY;
1002
1003 CopyMemory(context->ChallengeMessage.pvBuffer, AuthNtlmMessage->buffer,
1004 AuthNtlmMessage->length);
1005 }
1006 else if (AuthNtlmMessage->type == 3)
1007 {
1008 sspi_SecBufferFree(&context->AuthenticateMessage);
1009
1010 if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, AuthNtlmMessage->length))
1011 return SEC_E_INSUFFICIENT_MEMORY;
1012
1013 CopyMemory(context->AuthenticateMessage.pvBuffer, AuthNtlmMessage->buffer,
1014 AuthNtlmMessage->length);
1015 }
1016
1017 return SEC_E_OK;
1018 }
1019 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_TIMESTAMP)
1020 {
1021 SecPkgContext_AuthNtlmTimestamp* AuthNtlmTimestamp =
1023
1024 if (cbBuffer < sizeof(SecPkgContext_AuthNtlmTimestamp))
1025 return SEC_E_INVALID_PARAMETER;
1026
1027 if (AuthNtlmTimestamp->ChallengeOrResponse)
1028 CopyMemory(context->ChallengeTimestamp, AuthNtlmTimestamp->Timestamp, 8);
1029 else
1030 CopyMemory(context->Timestamp, AuthNtlmTimestamp->Timestamp, 8);
1031
1032 return SEC_E_OK;
1033 }
1034 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE)
1035 {
1036 SecPkgContext_AuthNtlmClientChallenge* AuthNtlmClientChallenge =
1038
1039 if (cbBuffer < sizeof(SecPkgContext_AuthNtlmClientChallenge))
1040 return SEC_E_INVALID_PARAMETER;
1041
1042 CopyMemory(context->ClientChallenge, AuthNtlmClientChallenge->ClientChallenge, 8);
1043 return SEC_E_OK;
1044 }
1045 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE)
1046 {
1047 SecPkgContext_AuthNtlmServerChallenge* AuthNtlmServerChallenge =
1049
1050 if (cbBuffer < sizeof(SecPkgContext_AuthNtlmServerChallenge))
1051 return SEC_E_INVALID_PARAMETER;
1052
1053 CopyMemory(context->ServerChallenge, AuthNtlmServerChallenge->ServerChallenge, 8);
1054 return SEC_E_OK;
1055 }
1056
1057 WLog_ERR(TAG, "TODO: Implement ulAttribute=%08" PRIx32, ulAttribute);
1058 return SEC_E_UNSUPPORTED_FUNCTION;
1059}
1060
1061static SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesA(PCtxtHandle phContext,
1062 ULONG ulAttribute, void* pBuffer,
1063 ULONG cbBuffer)
1064{
1065 return ntlm_SetContextAttributesW(phContext, ulAttribute, pBuffer, cbBuffer);
1066}
1067
1068static SECURITY_STATUS SEC_ENTRY ntlm_SetCredentialsAttributesW(
1069 WINPR_ATTR_UNUSED PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
1070 WINPR_ATTR_UNUSED void* pBuffer, WINPR_ATTR_UNUSED ULONG cbBuffer)
1071{
1072 return SEC_E_UNSUPPORTED_FUNCTION;
1073}
1074
1075static SECURITY_STATUS SEC_ENTRY ntlm_SetCredentialsAttributesA(
1076 WINPR_ATTR_UNUSED PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
1077 WINPR_ATTR_UNUSED void* pBuffer, WINPR_ATTR_UNUSED ULONG cbBuffer)
1078{
1079 return SEC_E_UNSUPPORTED_FUNCTION;
1080}
1081
1082static SECURITY_STATUS SEC_ENTRY ntlm_RevertSecurityContext(WINPR_ATTR_UNUSED PCtxtHandle phContext)
1083{
1084 return SEC_E_OK;
1085}
1086
1087static SECURITY_STATUS SEC_ENTRY ntlm_EncryptMessage(PCtxtHandle phContext,
1088 WINPR_ATTR_UNUSED ULONG fQOP,
1089 PSecBufferDesc pMessage, ULONG MessageSeqNo)
1090{
1091 const UINT32 SeqNo = MessageSeqNo;
1092 UINT32 value = 0;
1093 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1094 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1095 ULONG version = 1;
1096 PSecBuffer data_buffer = nullptr;
1097 PSecBuffer signature_buffer = nullptr;
1098 NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
1099 if (!check_context(context))
1100 return SEC_E_INVALID_HANDLE;
1101
1102 for (ULONG index = 0; index < pMessage->cBuffers; index++)
1103 {
1104 SecBuffer* cur = &pMessage->pBuffers[index];
1105
1106 if (cur->BufferType & SECBUFFER_DATA)
1107 data_buffer = cur;
1108 else if (cur->BufferType & SECBUFFER_TOKEN)
1109 signature_buffer = cur;
1110 }
1111
1112 if (!data_buffer)
1113 return SEC_E_INVALID_TOKEN;
1114
1115 if (!signature_buffer)
1116 return SEC_E_INVALID_TOKEN;
1117
1118 /* Copy original data buffer */
1119 ULONG length = data_buffer->cbBuffer;
1120 void* data = malloc(length);
1121
1122 if (!data)
1123 return SEC_E_INSUFFICIENT_MEMORY;
1124
1125 CopyMemory(data, data_buffer->pvBuffer, length);
1126 /* Compute the HMAC-MD5 hash of ConcatenationOf(seq_num,data) using the client signing key */
1127 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1128
1129 BOOL success = FALSE;
1130 {
1131 if (!hmac)
1132 goto hmac_fail;
1133 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->SendSigningKey, WINPR_MD5_DIGEST_LENGTH))
1134 goto hmac_fail;
1135
1136 winpr_Data_Write_UINT32(&value, SeqNo);
1137
1138 if (!winpr_HMAC_Update(hmac, (void*)&value, 4))
1139 goto hmac_fail;
1140 if (!winpr_HMAC_Update(hmac, data, length))
1141 goto hmac_fail;
1142 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1143 goto hmac_fail;
1144 }
1145
1146 success = TRUE;
1147
1148hmac_fail:
1149 winpr_HMAC_Free(hmac);
1150 if (!success)
1151 {
1152 free(data);
1153 return SEC_E_INSUFFICIENT_MEMORY;
1154 }
1155
1156 /* Encrypt message using with RC4, result overwrites original buffer */
1157 if ((data_buffer->BufferType & SECBUFFER_READONLY) == 0)
1158 {
1159 if (context->confidentiality)
1160 {
1161 if (!winpr_RC4_Update(context->SendRc4Seal, length, (BYTE*)data,
1162 (BYTE*)data_buffer->pvBuffer))
1163 {
1164 free(data);
1165 return SEC_E_INSUFFICIENT_MEMORY;
1166 }
1167 }
1168 else
1169 CopyMemory(data_buffer->pvBuffer, data, length);
1170 }
1171
1172#ifdef WITH_DEBUG_NTLM
1173 WLog_DBG(TAG, "Data Buffer (length = %" PRIu32 ")", length);
1174 winpr_HexDump(TAG, WLOG_DEBUG, data, length);
1175 WLog_DBG(TAG, "Encrypted Data Buffer (length = %" PRIu32 ")", data_buffer->cbBuffer);
1176 winpr_HexDump(TAG, WLOG_DEBUG, data_buffer->pvBuffer, data_buffer->cbBuffer);
1177#endif
1178 free(data);
1179 /* RC4-encrypt first 8 bytes of digest */
1180 if (!winpr_RC4_Update(context->SendRc4Seal, 8, digest, checksum))
1181 return SEC_E_INSUFFICIENT_MEMORY;
1182 if ((signature_buffer->BufferType & SECBUFFER_READONLY) == 0)
1183 {
1184 BYTE* signature = signature_buffer->pvBuffer;
1185 /* Concatenate version, ciphertext and sequence number to build signature */
1186 winpr_Data_Write_UINT32(signature, version);
1187 CopyMemory(&signature[4], (void*)checksum, 8);
1188 winpr_Data_Write_UINT32(&signature[12], SeqNo);
1189 }
1190 context->SendSeqNum++;
1191#ifdef WITH_DEBUG_NTLM
1192 WLog_DBG(TAG, "Signature (length = %" PRIu32 ")", signature_buffer->cbBuffer);
1193 winpr_HexDump(TAG, WLOG_DEBUG, signature_buffer->pvBuffer, signature_buffer->cbBuffer);
1194#endif
1195 return SEC_E_OK;
1196}
1197
1198static SECURITY_STATUS SEC_ENTRY ntlm_DecryptMessage(PCtxtHandle phContext, PSecBufferDesc pMessage,
1199 ULONG MessageSeqNo,
1200 WINPR_ATTR_UNUSED PULONG pfQOP)
1201{
1202 const UINT32 SeqNo = (UINT32)MessageSeqNo;
1203 UINT32 value = 0;
1204 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1205 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1206 UINT32 version = 1;
1207 BYTE expected_signature[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1208 PSecBuffer data_buffer = nullptr;
1209 PSecBuffer signature_buffer = nullptr;
1210 NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
1211 if (!check_context(context))
1212 return SEC_E_INVALID_HANDLE;
1213
1214 for (ULONG index = 0; index < pMessage->cBuffers; index++)
1215 {
1216 if (pMessage->pBuffers[index].BufferType == SECBUFFER_DATA)
1217 data_buffer = &pMessage->pBuffers[index];
1218 else if (pMessage->pBuffers[index].BufferType == SECBUFFER_TOKEN)
1219 signature_buffer = &pMessage->pBuffers[index];
1220 }
1221
1222 if (!data_buffer)
1223 return SEC_E_INVALID_TOKEN;
1224
1225 if (!signature_buffer)
1226 return SEC_E_INVALID_TOKEN;
1227
1228 /* Copy original data buffer */
1229 const ULONG length = data_buffer->cbBuffer;
1230 void* data = malloc(length);
1231
1232 if (!data)
1233 return SEC_E_INSUFFICIENT_MEMORY;
1234
1235 CopyMemory(data, data_buffer->pvBuffer, length);
1236
1237 /* Decrypt message using with RC4, result overwrites original buffer */
1238
1239 if (context->confidentiality)
1240 {
1241 if (!winpr_RC4_Update(context->RecvRc4Seal, length, (BYTE*)data,
1242 (BYTE*)data_buffer->pvBuffer))
1243 {
1244 free(data);
1245 return SEC_E_INSUFFICIENT_MEMORY;
1246 }
1247 }
1248 else
1249 CopyMemory(data_buffer->pvBuffer, data, length);
1250
1251 /* Compute the HMAC-MD5 hash of ConcatenationOf(seq_num,data) using the client signing key */
1252 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1253
1254 BOOL success = FALSE;
1255 {
1256 if (!hmac)
1257 goto hmac_fail;
1258
1259 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->RecvSigningKey, WINPR_MD5_DIGEST_LENGTH))
1260 goto hmac_fail;
1261
1262 winpr_Data_Write_UINT32(&value, SeqNo);
1263
1264 if (!winpr_HMAC_Update(hmac, (void*)&value, 4))
1265 goto hmac_fail;
1266 if (!winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer))
1267 goto hmac_fail;
1268 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1269 goto hmac_fail;
1270
1271 success = TRUE;
1272 }
1273hmac_fail:
1274 winpr_HMAC_Free(hmac);
1275 if (!success)
1276 {
1277 free(data);
1278 return SEC_E_INSUFFICIENT_MEMORY;
1279 }
1280
1281#ifdef WITH_DEBUG_NTLM
1282 WLog_DBG(TAG, "Encrypted Data Buffer (length = %" PRIu32 ")", length);
1283 winpr_HexDump(TAG, WLOG_DEBUG, data, length);
1284 WLog_DBG(TAG, "Data Buffer (length = %" PRIu32 ")", data_buffer->cbBuffer);
1285 winpr_HexDump(TAG, WLOG_DEBUG, data_buffer->pvBuffer, data_buffer->cbBuffer);
1286#endif
1287 free(data);
1288 /* RC4-encrypt first 8 bytes of digest */
1289 if (!winpr_RC4_Update(context->RecvRc4Seal, 8, digest, checksum))
1290 return SEC_E_MESSAGE_ALTERED;
1291
1292 /* Concatenate version, ciphertext and sequence number to build signature */
1293 winpr_Data_Write_UINT32(expected_signature, version);
1294 CopyMemory(&expected_signature[4], (void*)checksum, 8);
1295 winpr_Data_Write_UINT32(&expected_signature[12], SeqNo);
1296 context->RecvSeqNum++;
1297
1298 if (memcmp(signature_buffer->pvBuffer, expected_signature, 16) != 0)
1299 {
1300 /* signature verification failed! */
1301 WLog_ERR(TAG, "signature verification failed, something nasty is going on!");
1302#ifdef WITH_DEBUG_NTLM
1303 WLog_ERR(TAG, "Expected Signature:");
1304 winpr_HexDump(TAG, WLOG_ERROR, expected_signature, 16);
1305 WLog_ERR(TAG, "Actual Signature:");
1306 winpr_HexDump(TAG, WLOG_ERROR, (BYTE*)signature_buffer->pvBuffer, 16);
1307#endif
1308 return SEC_E_MESSAGE_ALTERED;
1309 }
1310
1311 return SEC_E_OK;
1312}
1313
1314static SECURITY_STATUS SEC_ENTRY ntlm_MakeSignature(PCtxtHandle phContext,
1315 WINPR_ATTR_UNUSED ULONG fQOP,
1316 PSecBufferDesc pMessage, ULONG MessageSeqNo)
1317{
1318 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
1319 PSecBuffer data_buffer = nullptr;
1320 PSecBuffer sig_buffer = nullptr;
1321 UINT32 seq_no = 0;
1322 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1323 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1324
1325 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1326 if (!check_context(context))
1327 return SEC_E_INVALID_HANDLE;
1328
1329 for (ULONG i = 0; i < pMessage->cBuffers; i++)
1330 {
1331 if (pMessage->pBuffers[i].BufferType == SECBUFFER_DATA)
1332 data_buffer = &pMessage->pBuffers[i];
1333 else if (pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
1334 sig_buffer = &pMessage->pBuffers[i];
1335 }
1336
1337 if (!data_buffer || !sig_buffer)
1338 return SEC_E_INVALID_TOKEN;
1339
1340 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1341
1342 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->SendSigningKey, WINPR_MD5_DIGEST_LENGTH))
1343 goto fail;
1344
1345 winpr_Data_Write_UINT32(&seq_no, MessageSeqNo);
1346 if (!winpr_HMAC_Update(hmac, (BYTE*)&seq_no, 4))
1347 goto fail;
1348 if (!winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer))
1349 goto fail;
1350 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1351 goto fail;
1352
1353 if (!winpr_RC4_Update(context->SendRc4Seal, 8, digest, checksum))
1354 goto fail;
1355
1356 BYTE* signature = sig_buffer->pvBuffer;
1357 winpr_Data_Write_UINT32(signature, 1L);
1358 CopyMemory(&signature[4], checksum, 8);
1359 winpr_Data_Write_UINT32(&signature[12], seq_no);
1360 sig_buffer->cbBuffer = 16;
1361
1362 status = SEC_E_OK;
1363
1364fail:
1365 winpr_HMAC_Free(hmac);
1366 return status;
1367}
1368
1369static SECURITY_STATUS SEC_ENTRY ntlm_VerifySignature(PCtxtHandle phContext,
1370 PSecBufferDesc pMessage, ULONG MessageSeqNo,
1371 WINPR_ATTR_UNUSED PULONG pfQOP)
1372{
1373 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
1374 PSecBuffer data_buffer = nullptr;
1375 PSecBuffer sig_buffer = nullptr;
1376 UINT32 seq_no = 0;
1377 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1378 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1379 BYTE signature[16] = WINPR_C_ARRAY_INIT;
1380
1381 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1382 if (!check_context(context))
1383 return SEC_E_INVALID_HANDLE;
1384
1385 for (ULONG i = 0; i < pMessage->cBuffers; i++)
1386 {
1387 if (pMessage->pBuffers[i].BufferType == SECBUFFER_DATA)
1388 data_buffer = &pMessage->pBuffers[i];
1389 else if (pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
1390 sig_buffer = &pMessage->pBuffers[i];
1391 }
1392
1393 if (!data_buffer || !sig_buffer)
1394 return SEC_E_INVALID_TOKEN;
1395
1396 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1397
1398 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->RecvSigningKey, WINPR_MD5_DIGEST_LENGTH))
1399 goto fail;
1400
1401 winpr_Data_Write_UINT32(&seq_no, MessageSeqNo);
1402 if (!winpr_HMAC_Update(hmac, (BYTE*)&seq_no, 4))
1403 goto fail;
1404 if (!winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer))
1405 goto fail;
1406 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1407 goto fail;
1408
1409 if (!winpr_RC4_Update(context->RecvRc4Seal, 8, digest, checksum))
1410 goto fail;
1411
1412 winpr_Data_Write_UINT32(signature, 1L);
1413 CopyMemory(&signature[4], checksum, 8);
1414 winpr_Data_Write_UINT32(&signature[12], seq_no);
1415
1416 status = SEC_E_OK;
1417 if (memcmp(sig_buffer->pvBuffer, signature, 16) != 0)
1418 status = SEC_E_MESSAGE_ALTERED;
1419
1420fail:
1421 winpr_HMAC_Free(hmac);
1422 return status;
1423}
1424
1425const SecurityFunctionTableA NTLM_SecurityFunctionTableA = {
1426 3, /* dwVersion */
1427 nullptr, /* EnumerateSecurityPackages */
1428 ntlm_QueryCredentialsAttributesA, /* QueryCredentialsAttributes */
1429 ntlm_AcquireCredentialsHandleA, /* AcquireCredentialsHandle */
1430 ntlm_FreeCredentialsHandle, /* FreeCredentialsHandle */
1431 nullptr, /* Reserved2 */
1432 ntlm_InitializeSecurityContextA, /* InitializeSecurityContext */
1433 ntlm_AcceptSecurityContext, /* AcceptSecurityContext */
1434 nullptr, /* CompleteAuthToken */
1435 ntlm_DeleteSecurityContext, /* DeleteSecurityContext */
1436 nullptr, /* ApplyControlToken */
1437 ntlm_QueryContextAttributesA, /* QueryContextAttributes */
1438 ntlm_ImpersonateSecurityContext, /* ImpersonateSecurityContext */
1439 ntlm_RevertSecurityContext, /* RevertSecurityContext */
1440 ntlm_MakeSignature, /* MakeSignature */
1441 ntlm_VerifySignature, /* VerifySignature */
1442 nullptr, /* FreeContextBuffer */
1443 nullptr, /* QuerySecurityPackageInfo */
1444 nullptr, /* Reserved3 */
1445 nullptr, /* Reserved4 */
1446 nullptr, /* ExportSecurityContext */
1447 nullptr, /* ImportSecurityContext */
1448 nullptr, /* AddCredentials */
1449 nullptr, /* Reserved8 */
1450 nullptr, /* QuerySecurityContextToken */
1451 ntlm_EncryptMessage, /* EncryptMessage */
1452 ntlm_DecryptMessage, /* DecryptMessage */
1453 ntlm_SetContextAttributesA, /* SetContextAttributes */
1454 ntlm_SetCredentialsAttributesA, /* SetCredentialsAttributes */
1455};
1456
1457const SecurityFunctionTableW NTLM_SecurityFunctionTableW = {
1458 3, /* dwVersion */
1459 nullptr, /* EnumerateSecurityPackages */
1460 ntlm_QueryCredentialsAttributesW, /* QueryCredentialsAttributes */
1461 ntlm_AcquireCredentialsHandleW, /* AcquireCredentialsHandle */
1462 ntlm_FreeCredentialsHandle, /* FreeCredentialsHandle */
1463 nullptr, /* Reserved2 */
1464 ntlm_InitializeSecurityContextW, /* InitializeSecurityContext */
1465 ntlm_AcceptSecurityContext, /* AcceptSecurityContext */
1466 nullptr, /* CompleteAuthToken */
1467 ntlm_DeleteSecurityContext, /* DeleteSecurityContext */
1468 nullptr, /* ApplyControlToken */
1469 ntlm_QueryContextAttributesW, /* QueryContextAttributes */
1470 ntlm_ImpersonateSecurityContext, /* ImpersonateSecurityContext */
1471 ntlm_RevertSecurityContext, /* RevertSecurityContext */
1472 ntlm_MakeSignature, /* MakeSignature */
1473 ntlm_VerifySignature, /* VerifySignature */
1474 nullptr, /* FreeContextBuffer */
1475 nullptr, /* QuerySecurityPackageInfo */
1476 nullptr, /* Reserved3 */
1477 nullptr, /* Reserved4 */
1478 nullptr, /* ExportSecurityContext */
1479 nullptr, /* ImportSecurityContext */
1480 nullptr, /* AddCredentials */
1481 nullptr, /* Reserved8 */
1482 nullptr, /* QuerySecurityContextToken */
1483 ntlm_EncryptMessage, /* EncryptMessage */
1484 ntlm_DecryptMessage, /* DecryptMessage */
1485 ntlm_SetContextAttributesW, /* SetContextAttributes */
1486 ntlm_SetCredentialsAttributesW, /* SetCredentialsAttributes */
1487};
1488
1489const SecPkgInfoA NTLM_SecPkgInfoA = {
1490 0x00082B37, /* fCapabilities */
1491 1, /* wVersion */
1492 0x000A, /* wRPCID */
1493 0x00000B48, /* cbMaxToken */
1494 "NTLM", /* Name */
1495 "NTLM Security Package" /* Comment */
1496};
1497
1498static WCHAR NTLM_SecPkgInfoW_NameBuffer[32] = WINPR_C_ARRAY_INIT;
1499static WCHAR NTLM_SecPkgInfoW_CommentBuffer[32] = WINPR_C_ARRAY_INIT;
1500
1501const SecPkgInfoW NTLM_SecPkgInfoW = {
1502 0x00082B37, /* fCapabilities */
1503 1, /* wVersion */
1504 0x000A, /* wRPCID */
1505 0x00000B48, /* cbMaxToken */
1506 NTLM_SecPkgInfoW_NameBuffer, /* Name */
1507 NTLM_SecPkgInfoW_CommentBuffer /* Comment */
1508};
1509
1510char* ntlm_negotiate_flags_string(char* buffer, size_t size, UINT32 flags)
1511{
1512 if (!buffer || (size == 0))
1513 return buffer;
1514
1515 (void)_snprintf(buffer, size, "[0x%08" PRIx32 "] ", flags);
1516
1517 for (int x = 0; x < 31; x++)
1518 {
1519 const UINT32 mask = 1 << x;
1520 size_t len = strnlen(buffer, size);
1521 if (flags & mask)
1522 {
1523 const char* str = ntlm_get_negotiate_string(mask);
1524 const size_t flen = strlen(str);
1525
1526 if ((len > 0) && (buffer[len - 1] != ' '))
1527 {
1528 if (size - len < 1)
1529 break;
1530 winpr_str_append("|", buffer, size, nullptr);
1531 len++;
1532 }
1533
1534 if (size - len < flen)
1535 break;
1536 winpr_str_append(str, buffer, size, nullptr);
1537 }
1538 }
1539
1540 return buffer;
1541}
1542
1543const char* ntlm_message_type_string(UINT32 messageType)
1544{
1545 switch (messageType)
1546 {
1547 case MESSAGE_TYPE_NEGOTIATE:
1548 return "MESSAGE_TYPE_NEGOTIATE";
1549 case MESSAGE_TYPE_CHALLENGE:
1550 return "MESSAGE_TYPE_CHALLENGE";
1551 case MESSAGE_TYPE_AUTHENTICATE:
1552 return "MESSAGE_TYPE_AUTHENTICATE";
1553 default:
1554 return "MESSAGE_TYPE_UNKNOWN";
1555 }
1556}
1557
1558const char* ntlm_state_string(NTLM_STATE state)
1559{
1560 switch (state)
1561 {
1562 case NTLM_STATE_INITIAL:
1563 return "NTLM_STATE_INITIAL";
1564 case NTLM_STATE_NEGOTIATE:
1565 return "NTLM_STATE_NEGOTIATE";
1566 case NTLM_STATE_CHALLENGE:
1567 return "NTLM_STATE_CHALLENGE";
1568 case NTLM_STATE_AUTHENTICATE:
1569 return "NTLM_STATE_AUTHENTICATE";
1570 case NTLM_STATE_FINAL:
1571 return "NTLM_STATE_FINAL";
1572 default:
1573 return "NTLM_STATE_UNKNOWN";
1574 }
1575}
1576void ntlm_change_state(NTLM_CONTEXT* ntlm, NTLM_STATE state)
1577{
1578 WINPR_ASSERT(ntlm);
1579 WLog_DBG(TAG, "change state from %s to %s", ntlm_state_string(ntlm->state),
1580 ntlm_state_string(state));
1581 ntlm->state = state;
1582}
1583
1584NTLM_STATE ntlm_get_state(NTLM_CONTEXT* ntlm)
1585{
1586 WINPR_ASSERT(ntlm);
1587 return ntlm->state;
1588}
1589
1590BOOL ntlm_reset_cipher_state(PSecHandle phContext)
1591{
1592 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1593
1594 if (context)
1595 {
1596 check_context(context);
1597 winpr_RC4_Free(context->SendRc4Seal);
1598 winpr_RC4_Free(context->RecvRc4Seal);
1599 context->SendRc4Seal = winpr_RC4_New(context->RecvSealingKey, 16);
1600 context->RecvRc4Seal = winpr_RC4_New(context->SendSealingKey, 16);
1601
1602 if (!context->SendRc4Seal)
1603 {
1604 WLog_ERR(TAG, "Failed to allocate context->SendRc4Seal");
1605 return FALSE;
1606 }
1607 if (!context->RecvRc4Seal)
1608 {
1609 WLog_ERR(TAG, "Failed to allocate context->RecvRc4Seal");
1610 return FALSE;
1611 }
1612 }
1613
1614 return TRUE;
1615}
1616
1617BOOL NTLM_init(void)
1618{
1619 InitializeConstWCharFromUtf8(NTLM_SecPkgInfoA.Name, NTLM_SecPkgInfoW_NameBuffer,
1620 ARRAYSIZE(NTLM_SecPkgInfoW_NameBuffer));
1621 InitializeConstWCharFromUtf8(NTLM_SecPkgInfoA.Comment, NTLM_SecPkgInfoW_CommentBuffer,
1622 ARRAYSIZE(NTLM_SecPkgInfoW_CommentBuffer));
1623
1624 return TRUE;
1625}