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