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 sspi_SecureHandleInvalidate(phCredential);
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 sspi_SecureHandleInvalidate(phContext);
787 ntlm_ContextFree(context);
788 return SEC_E_OK;
789}
790
791SECURITY_STATUS ntlm_computeProofValue(NTLM_CONTEXT* ntlm, SecBuffer* ntproof)
792{
793 BYTE* blob = nullptr;
794 SecBuffer* target = nullptr;
795
796 WINPR_ASSERT(ntlm);
797 WINPR_ASSERT(ntproof);
798
799 target = &ntlm->ChallengeTargetInfo;
800
801 if (!sspi_SecBufferAlloc(ntproof, 36 + target->cbBuffer))
802 return SEC_E_INSUFFICIENT_MEMORY;
803
804 blob = (BYTE*)ntproof->pvBuffer;
805 CopyMemory(blob, ntlm->ServerChallenge, 8); /* Server challenge. */
806 blob[8] = 1; /* Response version. */
807 blob[9] = 1; /* Highest response version understood by the client. */
808 /* Reserved 6B. */
809 CopyMemory(&blob[16], ntlm->Timestamp, 8); /* Time. */
810 CopyMemory(&blob[24], ntlm->ClientChallenge, 8); /* Client challenge. */
811 /* Reserved 4B. */
812 /* Server name. */
813 CopyMemory(&blob[36], target->pvBuffer, target->cbBuffer);
814 return SEC_E_OK;
815}
816
817SECURITY_STATUS ntlm_computeMicValue(NTLM_CONTEXT* ntlm, SecBuffer* micvalue)
818{
819 BYTE* blob = nullptr;
820 ULONG msgSize = 0;
821
822 WINPR_ASSERT(ntlm);
823 WINPR_ASSERT(micvalue);
824
825 msgSize = ntlm->NegotiateMessage.cbBuffer + ntlm->ChallengeMessage.cbBuffer +
826 ntlm->AuthenticateMessage.cbBuffer;
827
828 if (!sspi_SecBufferAlloc(micvalue, msgSize))
829 return SEC_E_INSUFFICIENT_MEMORY;
830
831 blob = (BYTE*)micvalue->pvBuffer;
832 CopyMemory(blob, ntlm->NegotiateMessage.pvBuffer, ntlm->NegotiateMessage.cbBuffer);
833 blob += ntlm->NegotiateMessage.cbBuffer;
834 CopyMemory(blob, ntlm->ChallengeMessage.pvBuffer, ntlm->ChallengeMessage.cbBuffer);
835 blob += ntlm->ChallengeMessage.cbBuffer;
836 CopyMemory(blob, ntlm->AuthenticateMessage.pvBuffer, ntlm->AuthenticateMessage.cbBuffer);
837 blob += ntlm->MessageIntegrityCheckOffset;
838 ZeroMemory(blob, 16);
839 return SEC_E_OK;
840}
841
842/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa379337/ */
843
844static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesW(PCtxtHandle phContext,
845 ULONG ulAttribute, void* pBuffer)
846{
847 if (!phContext)
848 return SEC_E_INVALID_HANDLE;
849
850 if (!pBuffer)
851 return SEC_E_INSUFFICIENT_MEMORY;
852
853 NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
854 if (!check_context(context))
855 return SEC_E_INVALID_HANDLE;
856
857 if (ulAttribute == SECPKG_ATTR_SIZES)
858 {
859 SecPkgContext_Sizes* ContextSizes = (SecPkgContext_Sizes*)pBuffer;
860 ContextSizes->cbMaxToken = 2010;
861 ContextSizes->cbMaxSignature = 16; /* the size of expected signature is 16 bytes */
862 ContextSizes->cbBlockSize = 0; /* no padding */
863 ContextSizes->cbSecurityTrailer = 16; /* no security trailer appended in NTLM
864 contrary to Kerberos */
865 return SEC_E_OK;
866 }
867 else if (ulAttribute == SECPKG_ATTR_AUTH_IDENTITY)
868 {
869 SSPI_CREDENTIALS* credentials = nullptr;
870 const SecPkgContext_AuthIdentity empty = WINPR_C_ARRAY_INIT;
872
873 WINPR_ASSERT(AuthIdentity);
874 *AuthIdentity = empty;
875
876 context->UseSamFileDatabase = FALSE;
877 credentials = context->credentials;
878
879 if (credentials->identity.UserLength > 0)
880 {
881 if (ConvertWCharNToUtf8(credentials->identity.User, credentials->identity.UserLength,
882 AuthIdentity->User, ARRAYSIZE(AuthIdentity->User)) <= 0)
883 return SEC_E_INTERNAL_ERROR;
884 }
885
886 if (credentials->identity.DomainLength > 0)
887 {
888 if (ConvertWCharNToUtf8(credentials->identity.Domain,
889 credentials->identity.DomainLength, AuthIdentity->Domain,
890 ARRAYSIZE(AuthIdentity->Domain)) <= 0)
891 return SEC_E_INTERNAL_ERROR;
892 }
893
894 return SEC_E_OK;
895 }
896 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_NTPROOF_VALUE)
897 {
898 return ntlm_computeProofValue(context, (SecBuffer*)pBuffer);
899 }
900 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_RANDKEY)
901 {
902 SecBuffer* randkey = nullptr;
903 randkey = (SecBuffer*)pBuffer;
904
905 if (!sspi_SecBufferAlloc(randkey, 16))
906 return (SEC_E_INSUFFICIENT_MEMORY);
907
908 CopyMemory(randkey->pvBuffer, context->EncryptedRandomSessionKey, 16);
909 return (SEC_E_OK);
910 }
911 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MIC)
912 {
913 SecBuffer* mic = (SecBuffer*)pBuffer;
914 NTLM_AUTHENTICATE_MESSAGE* message = &context->AUTHENTICATE_MESSAGE;
915
916 if (!sspi_SecBufferAlloc(mic, 16))
917 return (SEC_E_INSUFFICIENT_MEMORY);
918
919 CopyMemory(mic->pvBuffer, message->MessageIntegrityCheck, 16);
920 return (SEC_E_OK);
921 }
922 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MIC_VALUE)
923 {
924 return ntlm_computeMicValue(context, (SecBuffer*)pBuffer);
925 }
926 else if (ulAttribute == SECPKG_ATTR_PACKAGE_INFO)
927 {
929 size_t size = sizeof(SecPkgInfoA);
930 SecPkgInfoA* pPackageInfo =
931 (SecPkgInfoA*)sspi_ContextBufferAlloc(QuerySecurityPackageInfoIndex, size);
932
933 if (!pPackageInfo)
934 return SEC_E_INSUFFICIENT_MEMORY;
935
936 pPackageInfo->fCapabilities = NTLM_SecPkgInfoA.fCapabilities;
937 pPackageInfo->wVersion = NTLM_SecPkgInfoA.wVersion;
938 pPackageInfo->wRPCID = NTLM_SecPkgInfoA.wRPCID;
939 pPackageInfo->cbMaxToken = NTLM_SecPkgInfoA.cbMaxToken;
940 pPackageInfo->Name = _strdup(NTLM_SecPkgInfoA.Name);
941 pPackageInfo->Comment = _strdup(NTLM_SecPkgInfoA.Comment);
942
943 if (!pPackageInfo->Name || !pPackageInfo->Comment)
944 {
945 sspi_ContextBufferFree(pPackageInfo);
946 return SEC_E_INSUFFICIENT_MEMORY;
947 }
948 PackageInfo->PackageInfo = pPackageInfo;
949 return SEC_E_OK;
950 }
951
952 WLog_ERR(TAG, "TODO: Implement ulAttribute=0x%08" PRIx32, ulAttribute);
953 return SEC_E_UNSUPPORTED_FUNCTION;
954}
955
956static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesA(PCtxtHandle phContext,
957 ULONG ulAttribute, void* pBuffer)
958{
959 return ntlm_QueryContextAttributesW(phContext, ulAttribute, pBuffer);
960}
961
962static SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesW(PCtxtHandle phContext,
963 ULONG ulAttribute, void* pBuffer,
964 ULONG cbBuffer)
965{
966 if (!phContext)
967 return SEC_E_INVALID_HANDLE;
968
969 if (!pBuffer)
970 return SEC_E_INVALID_PARAMETER;
971
972 NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
973 if (!context)
974 return SEC_E_INVALID_HANDLE;
975
976 if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_HASH)
977 {
979
980 if (cbBuffer < sizeof(SecPkgContext_AuthNtlmHash))
981 return SEC_E_INVALID_PARAMETER;
982
983 if (AuthNtlmHash->Version == 1)
984 CopyMemory(context->NtlmHash, AuthNtlmHash->NtlmHash, 16);
985 else if (AuthNtlmHash->Version == 2)
986 CopyMemory(context->NtlmV2Hash, AuthNtlmHash->NtlmHash, 16);
987
988 return SEC_E_OK;
989 }
990 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MESSAGE)
991 {
993
994 if (cbBuffer < sizeof(SecPkgContext_AuthNtlmMessage))
995 return SEC_E_INVALID_PARAMETER;
996
997 if (AuthNtlmMessage->type == 1)
998 {
999 sspi_SecBufferFree(&context->NegotiateMessage);
1000
1001 if (!sspi_SecBufferAlloc(&context->NegotiateMessage, AuthNtlmMessage->length))
1002 return SEC_E_INSUFFICIENT_MEMORY;
1003
1004 CopyMemory(context->NegotiateMessage.pvBuffer, AuthNtlmMessage->buffer,
1005 AuthNtlmMessage->length);
1006 }
1007 else if (AuthNtlmMessage->type == 2)
1008 {
1009 sspi_SecBufferFree(&context->ChallengeMessage);
1010
1011 if (!sspi_SecBufferAlloc(&context->ChallengeMessage, AuthNtlmMessage->length))
1012 return SEC_E_INSUFFICIENT_MEMORY;
1013
1014 CopyMemory(context->ChallengeMessage.pvBuffer, AuthNtlmMessage->buffer,
1015 AuthNtlmMessage->length);
1016 }
1017 else if (AuthNtlmMessage->type == 3)
1018 {
1019 sspi_SecBufferFree(&context->AuthenticateMessage);
1020
1021 if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, AuthNtlmMessage->length))
1022 return SEC_E_INSUFFICIENT_MEMORY;
1023
1024 CopyMemory(context->AuthenticateMessage.pvBuffer, AuthNtlmMessage->buffer,
1025 AuthNtlmMessage->length);
1026 }
1027
1028 return SEC_E_OK;
1029 }
1030 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_TIMESTAMP)
1031 {
1032 SecPkgContext_AuthNtlmTimestamp* AuthNtlmTimestamp =
1034
1035 if (cbBuffer < sizeof(SecPkgContext_AuthNtlmTimestamp))
1036 return SEC_E_INVALID_PARAMETER;
1037
1038 if (AuthNtlmTimestamp->ChallengeOrResponse)
1039 CopyMemory(context->ChallengeTimestamp, AuthNtlmTimestamp->Timestamp, 8);
1040 else
1041 CopyMemory(context->Timestamp, AuthNtlmTimestamp->Timestamp, 8);
1042
1043 return SEC_E_OK;
1044 }
1045 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE)
1046 {
1047 SecPkgContext_AuthNtlmClientChallenge* AuthNtlmClientChallenge =
1049
1050 if (cbBuffer < sizeof(SecPkgContext_AuthNtlmClientChallenge))
1051 return SEC_E_INVALID_PARAMETER;
1052
1053 CopyMemory(context->ClientChallenge, AuthNtlmClientChallenge->ClientChallenge, 8);
1054 return SEC_E_OK;
1055 }
1056 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE)
1057 {
1058 SecPkgContext_AuthNtlmServerChallenge* AuthNtlmServerChallenge =
1060
1061 if (cbBuffer < sizeof(SecPkgContext_AuthNtlmServerChallenge))
1062 return SEC_E_INVALID_PARAMETER;
1063
1064 CopyMemory(context->ServerChallenge, AuthNtlmServerChallenge->ServerChallenge, 8);
1065 return SEC_E_OK;
1066 }
1067
1068 WLog_ERR(TAG, "TODO: Implement ulAttribute=%08" PRIx32, ulAttribute);
1069 return SEC_E_UNSUPPORTED_FUNCTION;
1070}
1071
1072static SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesA(PCtxtHandle phContext,
1073 ULONG ulAttribute, void* pBuffer,
1074 ULONG cbBuffer)
1075{
1076 return ntlm_SetContextAttributesW(phContext, ulAttribute, pBuffer, cbBuffer);
1077}
1078
1079static SECURITY_STATUS SEC_ENTRY ntlm_SetCredentialsAttributesW(
1080 WINPR_ATTR_UNUSED PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
1081 WINPR_ATTR_UNUSED void* pBuffer, WINPR_ATTR_UNUSED ULONG cbBuffer)
1082{
1083 return SEC_E_UNSUPPORTED_FUNCTION;
1084}
1085
1086static SECURITY_STATUS SEC_ENTRY ntlm_SetCredentialsAttributesA(
1087 WINPR_ATTR_UNUSED PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
1088 WINPR_ATTR_UNUSED void* pBuffer, WINPR_ATTR_UNUSED ULONG cbBuffer)
1089{
1090 return SEC_E_UNSUPPORTED_FUNCTION;
1091}
1092
1093static SECURITY_STATUS SEC_ENTRY ntlm_RevertSecurityContext(WINPR_ATTR_UNUSED PCtxtHandle phContext)
1094{
1095 return SEC_E_OK;
1096}
1097
1098static SECURITY_STATUS SEC_ENTRY ntlm_EncryptMessage(PCtxtHandle phContext,
1099 WINPR_ATTR_UNUSED ULONG fQOP,
1100 PSecBufferDesc pMessage, ULONG MessageSeqNo)
1101{
1102 const UINT32 SeqNo = MessageSeqNo;
1103 UINT32 value = 0;
1104 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1105 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1106 ULONG version = 1;
1107 PSecBuffer data_buffer = nullptr;
1108 PSecBuffer signature_buffer = nullptr;
1109 NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
1110 if (!check_context(context))
1111 return SEC_E_INVALID_HANDLE;
1112
1113 for (ULONG index = 0; index < pMessage->cBuffers; index++)
1114 {
1115 SecBuffer* cur = &pMessage->pBuffers[index];
1116
1117 if (cur->BufferType & SECBUFFER_DATA)
1118 data_buffer = cur;
1119 else if (cur->BufferType & SECBUFFER_TOKEN)
1120 signature_buffer = cur;
1121 }
1122
1123 if (!data_buffer)
1124 return SEC_E_INVALID_TOKEN;
1125
1126 if (!signature_buffer)
1127 return SEC_E_INVALID_TOKEN;
1128
1129 /* Copy original data buffer */
1130 ULONG length = data_buffer->cbBuffer;
1131 void* data = malloc(length);
1132
1133 if (!data)
1134 return SEC_E_INSUFFICIENT_MEMORY;
1135
1136 CopyMemory(data, data_buffer->pvBuffer, length);
1137 /* Compute the HMAC-MD5 hash of ConcatenationOf(seq_num,data) using the client signing key */
1138 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1139
1140 BOOL success = FALSE;
1141 {
1142 if (!hmac)
1143 goto hmac_fail;
1144 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->SendSigningKey, WINPR_MD5_DIGEST_LENGTH))
1145 goto hmac_fail;
1146
1147 winpr_Data_Write_UINT32(&value, SeqNo);
1148
1149 if (!winpr_HMAC_Update(hmac, (void*)&value, 4))
1150 goto hmac_fail;
1151 if (!winpr_HMAC_Update(hmac, data, length))
1152 goto hmac_fail;
1153 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1154 goto hmac_fail;
1155 }
1156
1157 success = TRUE;
1158
1159hmac_fail:
1160 winpr_HMAC_Free(hmac);
1161 if (!success)
1162 {
1163 free(data);
1164 return SEC_E_INSUFFICIENT_MEMORY;
1165 }
1166
1167 /* Encrypt message using with RC4, result overwrites original buffer */
1168 if ((data_buffer->BufferType & SECBUFFER_READONLY) == 0)
1169 {
1170 if (context->confidentiality)
1171 {
1172 if (!winpr_RC4_Update(context->SendRc4Seal, length, (BYTE*)data,
1173 (BYTE*)data_buffer->pvBuffer))
1174 {
1175 free(data);
1176 return SEC_E_INSUFFICIENT_MEMORY;
1177 }
1178 }
1179 else
1180 CopyMemory(data_buffer->pvBuffer, data, length);
1181 }
1182
1183#ifdef WITH_DEBUG_NTLM
1184 WLog_DBG(TAG, "Data Buffer (length = %" PRIu32 ")", length);
1185 winpr_HexDump(TAG, WLOG_DEBUG, data, length);
1186 WLog_DBG(TAG, "Encrypted Data Buffer (length = %" PRIu32 ")", data_buffer->cbBuffer);
1187 winpr_HexDump(TAG, WLOG_DEBUG, data_buffer->pvBuffer, data_buffer->cbBuffer);
1188#endif
1189 free(data);
1190 /* RC4-encrypt first 8 bytes of digest */
1191 if (!winpr_RC4_Update(context->SendRc4Seal, 8, digest, checksum))
1192 return SEC_E_INSUFFICIENT_MEMORY;
1193 if ((signature_buffer->BufferType & SECBUFFER_READONLY) == 0)
1194 {
1195 BYTE* signature = signature_buffer->pvBuffer;
1196 /* Concatenate version, ciphertext and sequence number to build signature */
1197 winpr_Data_Write_UINT32(signature, version);
1198 CopyMemory(&signature[4], (void*)checksum, 8);
1199 winpr_Data_Write_UINT32(&signature[12], SeqNo);
1200 }
1201 context->SendSeqNum++;
1202#ifdef WITH_DEBUG_NTLM
1203 WLog_DBG(TAG, "Signature (length = %" PRIu32 ")", signature_buffer->cbBuffer);
1204 winpr_HexDump(TAG, WLOG_DEBUG, signature_buffer->pvBuffer, signature_buffer->cbBuffer);
1205#endif
1206 return SEC_E_OK;
1207}
1208
1209static SECURITY_STATUS SEC_ENTRY ntlm_DecryptMessage(PCtxtHandle phContext, PSecBufferDesc pMessage,
1210 ULONG MessageSeqNo,
1211 WINPR_ATTR_UNUSED PULONG pfQOP)
1212{
1213 const UINT32 SeqNo = (UINT32)MessageSeqNo;
1214 UINT32 value = 0;
1215 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1216 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1217 UINT32 version = 1;
1218 BYTE expected_signature[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1219 PSecBuffer data_buffer = nullptr;
1220 PSecBuffer signature_buffer = nullptr;
1221 NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
1222 if (!check_context(context))
1223 return SEC_E_INVALID_HANDLE;
1224
1225 for (ULONG index = 0; index < pMessage->cBuffers; index++)
1226 {
1227 if (pMessage->pBuffers[index].BufferType == SECBUFFER_DATA)
1228 data_buffer = &pMessage->pBuffers[index];
1229 else if (pMessage->pBuffers[index].BufferType == SECBUFFER_TOKEN)
1230 signature_buffer = &pMessage->pBuffers[index];
1231 }
1232
1233 if (!data_buffer)
1234 return SEC_E_INVALID_TOKEN;
1235
1236 if (!signature_buffer)
1237 return SEC_E_INVALID_TOKEN;
1238
1239 /* Copy original data buffer */
1240 const ULONG length = data_buffer->cbBuffer;
1241 void* data = malloc(length);
1242
1243 if (!data)
1244 return SEC_E_INSUFFICIENT_MEMORY;
1245
1246 CopyMemory(data, data_buffer->pvBuffer, length);
1247
1248 /* Decrypt message using with RC4, result overwrites original buffer */
1249
1250 if (context->confidentiality)
1251 {
1252 if (!winpr_RC4_Update(context->RecvRc4Seal, length, (BYTE*)data,
1253 (BYTE*)data_buffer->pvBuffer))
1254 {
1255 free(data);
1256 return SEC_E_INSUFFICIENT_MEMORY;
1257 }
1258 }
1259 else
1260 CopyMemory(data_buffer->pvBuffer, data, length);
1261
1262 /* Compute the HMAC-MD5 hash of ConcatenationOf(seq_num,data) using the client signing key */
1263 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1264
1265 BOOL success = FALSE;
1266 {
1267 if (!hmac)
1268 goto hmac_fail;
1269
1270 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->RecvSigningKey, WINPR_MD5_DIGEST_LENGTH))
1271 goto hmac_fail;
1272
1273 winpr_Data_Write_UINT32(&value, SeqNo);
1274
1275 if (!winpr_HMAC_Update(hmac, (void*)&value, 4))
1276 goto hmac_fail;
1277 if (!winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer))
1278 goto hmac_fail;
1279 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1280 goto hmac_fail;
1281
1282 success = TRUE;
1283 }
1284hmac_fail:
1285 winpr_HMAC_Free(hmac);
1286 if (!success)
1287 {
1288 free(data);
1289 return SEC_E_INSUFFICIENT_MEMORY;
1290 }
1291
1292#ifdef WITH_DEBUG_NTLM
1293 WLog_DBG(TAG, "Encrypted Data Buffer (length = %" PRIu32 ")", length);
1294 winpr_HexDump(TAG, WLOG_DEBUG, data, length);
1295 WLog_DBG(TAG, "Data Buffer (length = %" PRIu32 ")", data_buffer->cbBuffer);
1296 winpr_HexDump(TAG, WLOG_DEBUG, data_buffer->pvBuffer, data_buffer->cbBuffer);
1297#endif
1298 free(data);
1299 /* RC4-encrypt first 8 bytes of digest */
1300 if (!winpr_RC4_Update(context->RecvRc4Seal, 8, digest, checksum))
1301 return SEC_E_MESSAGE_ALTERED;
1302
1303 /* Concatenate version, ciphertext and sequence number to build signature */
1304 winpr_Data_Write_UINT32(expected_signature, version);
1305 CopyMemory(&expected_signature[4], (void*)checksum, 8);
1306 winpr_Data_Write_UINT32(&expected_signature[12], SeqNo);
1307 context->RecvSeqNum++;
1308
1309 if (memcmp(signature_buffer->pvBuffer, expected_signature, 16) != 0)
1310 {
1311 /* signature verification failed! */
1312 WLog_ERR(TAG, "signature verification failed, something nasty is going on!");
1313#ifdef WITH_DEBUG_NTLM
1314 WLog_ERR(TAG, "Expected Signature:");
1315 winpr_HexDump(TAG, WLOG_ERROR, expected_signature, 16);
1316 WLog_ERR(TAG, "Actual Signature:");
1317 winpr_HexDump(TAG, WLOG_ERROR, (BYTE*)signature_buffer->pvBuffer, 16);
1318#endif
1319 return SEC_E_MESSAGE_ALTERED;
1320 }
1321
1322 return SEC_E_OK;
1323}
1324
1325static SECURITY_STATUS SEC_ENTRY ntlm_MakeSignature(PCtxtHandle phContext,
1326 WINPR_ATTR_UNUSED ULONG fQOP,
1327 PSecBufferDesc pMessage, ULONG MessageSeqNo)
1328{
1329 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
1330 PSecBuffer data_buffer = nullptr;
1331 PSecBuffer sig_buffer = nullptr;
1332 UINT32 seq_no = 0;
1333 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1334 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1335
1336 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1337 if (!check_context(context))
1338 return SEC_E_INVALID_HANDLE;
1339
1340 for (ULONG i = 0; i < pMessage->cBuffers; i++)
1341 {
1342 if (pMessage->pBuffers[i].BufferType == SECBUFFER_DATA)
1343 data_buffer = &pMessage->pBuffers[i];
1344 else if (pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
1345 sig_buffer = &pMessage->pBuffers[i];
1346 }
1347
1348 if (!data_buffer || !sig_buffer)
1349 return SEC_E_INVALID_TOKEN;
1350
1351 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1352
1353 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->SendSigningKey, WINPR_MD5_DIGEST_LENGTH))
1354 goto fail;
1355
1356 winpr_Data_Write_UINT32(&seq_no, MessageSeqNo);
1357 if (!winpr_HMAC_Update(hmac, (BYTE*)&seq_no, 4))
1358 goto fail;
1359 if (!winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer))
1360 goto fail;
1361 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1362 goto fail;
1363
1364 if (!winpr_RC4_Update(context->SendRc4Seal, 8, digest, checksum))
1365 goto fail;
1366
1367 BYTE* signature = sig_buffer->pvBuffer;
1368 winpr_Data_Write_UINT32(signature, 1L);
1369 CopyMemory(&signature[4], checksum, 8);
1370 winpr_Data_Write_UINT32(&signature[12], seq_no);
1371 sig_buffer->cbBuffer = 16;
1372
1373 status = SEC_E_OK;
1374
1375fail:
1376 winpr_HMAC_Free(hmac);
1377 return status;
1378}
1379
1380static SECURITY_STATUS SEC_ENTRY ntlm_VerifySignature(PCtxtHandle phContext,
1381 PSecBufferDesc pMessage, ULONG MessageSeqNo,
1382 WINPR_ATTR_UNUSED PULONG pfQOP)
1383{
1384 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
1385 PSecBuffer data_buffer = nullptr;
1386 PSecBuffer sig_buffer = nullptr;
1387 UINT32 seq_no = 0;
1388 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1389 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1390 BYTE signature[16] = WINPR_C_ARRAY_INIT;
1391
1392 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1393 if (!check_context(context))
1394 return SEC_E_INVALID_HANDLE;
1395
1396 for (ULONG i = 0; i < pMessage->cBuffers; i++)
1397 {
1398 if (pMessage->pBuffers[i].BufferType == SECBUFFER_DATA)
1399 data_buffer = &pMessage->pBuffers[i];
1400 else if (pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
1401 sig_buffer = &pMessage->pBuffers[i];
1402 }
1403
1404 if (!data_buffer || !sig_buffer)
1405 return SEC_E_INVALID_TOKEN;
1406
1407 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1408
1409 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->RecvSigningKey, WINPR_MD5_DIGEST_LENGTH))
1410 goto fail;
1411
1412 winpr_Data_Write_UINT32(&seq_no, MessageSeqNo);
1413 if (!winpr_HMAC_Update(hmac, (BYTE*)&seq_no, 4))
1414 goto fail;
1415 if (!winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer))
1416 goto fail;
1417 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1418 goto fail;
1419
1420 if (!winpr_RC4_Update(context->RecvRc4Seal, 8, digest, checksum))
1421 goto fail;
1422
1423 winpr_Data_Write_UINT32(signature, 1L);
1424 CopyMemory(&signature[4], checksum, 8);
1425 winpr_Data_Write_UINT32(&signature[12], seq_no);
1426
1427 status = SEC_E_OK;
1428 if (memcmp(sig_buffer->pvBuffer, signature, 16) != 0)
1429 status = SEC_E_MESSAGE_ALTERED;
1430
1431fail:
1432 winpr_HMAC_Free(hmac);
1433 return status;
1434}
1435
1436const SecurityFunctionTableA NTLM_SecurityFunctionTableA = {
1437 3, /* dwVersion */
1438 nullptr, /* EnumerateSecurityPackages */
1439 ntlm_QueryCredentialsAttributesA, /* QueryCredentialsAttributes */
1440 ntlm_AcquireCredentialsHandleA, /* AcquireCredentialsHandle */
1441 ntlm_FreeCredentialsHandle, /* FreeCredentialsHandle */
1442 nullptr, /* Reserved2 */
1443 ntlm_InitializeSecurityContextA, /* InitializeSecurityContext */
1444 ntlm_AcceptSecurityContext, /* AcceptSecurityContext */
1445 nullptr, /* CompleteAuthToken */
1446 ntlm_DeleteSecurityContext, /* DeleteSecurityContext */
1447 nullptr, /* ApplyControlToken */
1448 ntlm_QueryContextAttributesA, /* QueryContextAttributes */
1449 ntlm_ImpersonateSecurityContext, /* ImpersonateSecurityContext */
1450 ntlm_RevertSecurityContext, /* RevertSecurityContext */
1451 ntlm_MakeSignature, /* MakeSignature */
1452 ntlm_VerifySignature, /* VerifySignature */
1453 nullptr, /* FreeContextBuffer */
1454 nullptr, /* QuerySecurityPackageInfo */
1455 nullptr, /* Reserved3 */
1456 nullptr, /* Reserved4 */
1457 nullptr, /* ExportSecurityContext */
1458 nullptr, /* ImportSecurityContext */
1459 nullptr, /* AddCredentials */
1460 nullptr, /* Reserved8 */
1461 nullptr, /* QuerySecurityContextToken */
1462 ntlm_EncryptMessage, /* EncryptMessage */
1463 ntlm_DecryptMessage, /* DecryptMessage */
1464 ntlm_SetContextAttributesA, /* SetContextAttributes */
1465 ntlm_SetCredentialsAttributesA, /* SetCredentialsAttributes */
1466};
1467
1468const SecurityFunctionTableW NTLM_SecurityFunctionTableW = {
1469 3, /* dwVersion */
1470 nullptr, /* EnumerateSecurityPackages */
1471 ntlm_QueryCredentialsAttributesW, /* QueryCredentialsAttributes */
1472 ntlm_AcquireCredentialsHandleW, /* AcquireCredentialsHandle */
1473 ntlm_FreeCredentialsHandle, /* FreeCredentialsHandle */
1474 nullptr, /* Reserved2 */
1475 ntlm_InitializeSecurityContextW, /* InitializeSecurityContext */
1476 ntlm_AcceptSecurityContext, /* AcceptSecurityContext */
1477 nullptr, /* CompleteAuthToken */
1478 ntlm_DeleteSecurityContext, /* DeleteSecurityContext */
1479 nullptr, /* ApplyControlToken */
1480 ntlm_QueryContextAttributesW, /* QueryContextAttributes */
1481 ntlm_ImpersonateSecurityContext, /* ImpersonateSecurityContext */
1482 ntlm_RevertSecurityContext, /* RevertSecurityContext */
1483 ntlm_MakeSignature, /* MakeSignature */
1484 ntlm_VerifySignature, /* VerifySignature */
1485 nullptr, /* FreeContextBuffer */
1486 nullptr, /* QuerySecurityPackageInfo */
1487 nullptr, /* Reserved3 */
1488 nullptr, /* Reserved4 */
1489 nullptr, /* ExportSecurityContext */
1490 nullptr, /* ImportSecurityContext */
1491 nullptr, /* AddCredentials */
1492 nullptr, /* Reserved8 */
1493 nullptr, /* QuerySecurityContextToken */
1494 ntlm_EncryptMessage, /* EncryptMessage */
1495 ntlm_DecryptMessage, /* DecryptMessage */
1496 ntlm_SetContextAttributesW, /* SetContextAttributes */
1497 ntlm_SetCredentialsAttributesW, /* SetCredentialsAttributes */
1498};
1499
1500const SecPkgInfoA NTLM_SecPkgInfoA = {
1501 0x00082B37, /* fCapabilities */
1502 1, /* wVersion */
1503 0x000A, /* wRPCID */
1504 0x00000B48, /* cbMaxToken */
1505 "NTLM", /* Name */
1506 "NTLM Security Package" /* Comment */
1507};
1508
1509static WCHAR NTLM_SecPkgInfoW_NameBuffer[32] = WINPR_C_ARRAY_INIT;
1510static WCHAR NTLM_SecPkgInfoW_CommentBuffer[32] = WINPR_C_ARRAY_INIT;
1511
1512const SecPkgInfoW NTLM_SecPkgInfoW = {
1513 0x00082B37, /* fCapabilities */
1514 1, /* wVersion */
1515 0x000A, /* wRPCID */
1516 0x00000B48, /* cbMaxToken */
1517 NTLM_SecPkgInfoW_NameBuffer, /* Name */
1518 NTLM_SecPkgInfoW_CommentBuffer /* Comment */
1519};
1520
1521char* ntlm_negotiate_flags_string(char* buffer, size_t size, UINT32 flags)
1522{
1523 if (!buffer || (size == 0))
1524 return buffer;
1525
1526 (void)_snprintf(buffer, size, "[0x%08" PRIx32 "] ", flags);
1527
1528 for (int x = 0; x < 31; x++)
1529 {
1530 const UINT32 mask = 1u << x;
1531 size_t len = strnlen(buffer, size);
1532 if (flags & mask)
1533 {
1534 const char* str = ntlm_get_negotiate_string(mask);
1535 const size_t flen = strlen(str);
1536
1537 if ((len > 0) && (buffer[len - 1] != ' '))
1538 {
1539 if (size - len < 1)
1540 break;
1541 winpr_str_append("|", buffer, size, nullptr);
1542 len++;
1543 }
1544
1545 if (size - len < flen)
1546 break;
1547 winpr_str_append(str, buffer, size, nullptr);
1548 }
1549 }
1550
1551 return buffer;
1552}
1553
1554const char* ntlm_message_type_string(UINT32 messageType)
1555{
1556 switch (messageType)
1557 {
1558 case MESSAGE_TYPE_NEGOTIATE:
1559 return "MESSAGE_TYPE_NEGOTIATE";
1560 case MESSAGE_TYPE_CHALLENGE:
1561 return "MESSAGE_TYPE_CHALLENGE";
1562 case MESSAGE_TYPE_AUTHENTICATE:
1563 return "MESSAGE_TYPE_AUTHENTICATE";
1564 default:
1565 return "MESSAGE_TYPE_UNKNOWN";
1566 }
1567}
1568
1569const char* ntlm_state_string(NTLM_STATE state)
1570{
1571 switch (state)
1572 {
1573 case NTLM_STATE_INITIAL:
1574 return "NTLM_STATE_INITIAL";
1575 case NTLM_STATE_NEGOTIATE:
1576 return "NTLM_STATE_NEGOTIATE";
1577 case NTLM_STATE_CHALLENGE:
1578 return "NTLM_STATE_CHALLENGE";
1579 case NTLM_STATE_AUTHENTICATE:
1580 return "NTLM_STATE_AUTHENTICATE";
1581 case NTLM_STATE_FINAL:
1582 return "NTLM_STATE_FINAL";
1583 default:
1584 return "NTLM_STATE_UNKNOWN";
1585 }
1586}
1587void ntlm_change_state(NTLM_CONTEXT* ntlm, NTLM_STATE state)
1588{
1589 WINPR_ASSERT(ntlm);
1590 WLog_DBG(TAG, "change state from %s to %s", ntlm_state_string(ntlm->state),
1591 ntlm_state_string(state));
1592 ntlm->state = state;
1593}
1594
1595NTLM_STATE ntlm_get_state(NTLM_CONTEXT* ntlm)
1596{
1597 WINPR_ASSERT(ntlm);
1598 return ntlm->state;
1599}
1600
1601BOOL ntlm_reset_cipher_state(PSecHandle phContext)
1602{
1603 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1604
1605 if (context)
1606 {
1607 check_context(context);
1608 winpr_RC4_Free(context->SendRc4Seal);
1609 winpr_RC4_Free(context->RecvRc4Seal);
1610 context->SendRc4Seal = winpr_RC4_New(context->RecvSealingKey, 16);
1611 context->RecvRc4Seal = winpr_RC4_New(context->SendSealingKey, 16);
1612
1613 if (!context->SendRc4Seal)
1614 {
1615 WLog_ERR(TAG, "Failed to allocate context->SendRc4Seal");
1616 return FALSE;
1617 }
1618 if (!context->RecvRc4Seal)
1619 {
1620 WLog_ERR(TAG, "Failed to allocate context->RecvRc4Seal");
1621 return FALSE;
1622 }
1623 }
1624
1625 return TRUE;
1626}
1627
1628BOOL NTLM_init(void)
1629{
1630 InitializeConstWCharFromUtf8(NTLM_SecPkgInfoA.Name, NTLM_SecPkgInfoW_NameBuffer,
1631 ARRAYSIZE(NTLM_SecPkgInfoW_NameBuffer));
1632 InitializeConstWCharFromUtf8(NTLM_SecPkgInfoA.Comment, NTLM_SecPkgInfoW_CommentBuffer,
1633 ARRAYSIZE(NTLM_SecPkgInfoW_CommentBuffer));
1634
1635 return TRUE;
1636}