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