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