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