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