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 if (signature_buffer->cbBuffer < 16)
1517 return SEC_E_INSUFFICIENT_MEMORY;
1518
1519 /* Copy original data buffer */
1520 ULONG length = data_buffer->cbBuffer;
1521 void* data = malloc(length);
1522
1523 if (!data)
1524 return SEC_E_INSUFFICIENT_MEMORY;
1525
1526 CopyMemory(data, data_buffer->pvBuffer, length);
1527 /* Compute the HMAC-MD5 hash of ConcatenationOf(seq_num,data) using the client signing key */
1528 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1529
1530 BOOL success = FALSE;
1531 {
1532 if (!hmac)
1533 goto hmac_fail;
1534 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->SendSigningKey, WINPR_MD5_DIGEST_LENGTH))
1535 goto hmac_fail;
1536
1537 winpr_Data_Write_UINT32(&value, SeqNo);
1538
1539 if (!winpr_HMAC_Update(hmac, (void*)&value, 4))
1540 goto hmac_fail;
1541 if (!winpr_HMAC_Update(hmac, data, length))
1542 goto hmac_fail;
1543 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1544 goto hmac_fail;
1545 }
1546
1547 success = TRUE;
1548
1549hmac_fail:
1550 winpr_HMAC_Free(hmac);
1551 if (!success)
1552 {
1553 free(data);
1554 return SEC_E_INSUFFICIENT_MEMORY;
1555 }
1556
1557 /* Encrypt message using with RC4, result overwrites original buffer */
1558 if ((data_buffer->BufferType & SECBUFFER_READONLY) == 0)
1559 {
1560 if (context->confidentiality)
1561 {
1562 if (!winpr_RC4_Update(context->SendRc4Seal, length, (BYTE*)data,
1563 (BYTE*)data_buffer->pvBuffer))
1564 {
1565 free(data);
1566 return SEC_E_INSUFFICIENT_MEMORY;
1567 }
1568 }
1569 else
1570 CopyMemory(data_buffer->pvBuffer, data, length);
1571 }
1572
1573#ifdef WITH_DEBUG_NTLM
1574 WLog_DBG(TAG, "Data Buffer (length = %" PRIu32 ")", length);
1575 winpr_HexDump(TAG, WLOG_DEBUG, data, length);
1576 WLog_DBG(TAG, "Encrypted Data Buffer (length = %" PRIu32 ")", data_buffer->cbBuffer);
1577 winpr_HexDump(TAG, WLOG_DEBUG, data_buffer->pvBuffer, data_buffer->cbBuffer);
1578#endif
1579 free(data);
1580 /* RC4-encrypt first 8 bytes of digest */
1581 if (!winpr_RC4_Update(context->SendRc4Seal, 8, digest, checksum))
1582 return SEC_E_INSUFFICIENT_MEMORY;
1583 if ((signature_buffer->BufferType & SECBUFFER_READONLY) == 0)
1584 {
1585 BYTE* signature = signature_buffer->pvBuffer;
1586 /* Concatenate version, ciphertext and sequence number to build signature */
1587 winpr_Data_Write_UINT32(signature, version);
1588 CopyMemory(&signature[4], (void*)checksum, 8);
1589 winpr_Data_Write_UINT32(&signature[12], SeqNo);
1590 }
1591 context->SendSeqNum++;
1592#ifdef WITH_DEBUG_NTLM
1593 WLog_DBG(TAG, "Signature (length = %" PRIu32 ")", signature_buffer->cbBuffer);
1594 winpr_HexDump(TAG, WLOG_DEBUG, signature_buffer->pvBuffer, signature_buffer->cbBuffer);
1595#endif
1596 return SEC_E_OK;
1597}
1598
1599static SECURITY_STATUS SEC_ENTRY ntlm_DecryptMessage(PCtxtHandle phContext, PSecBufferDesc pMessage,
1600 ULONG MessageSeqNo,
1601 WINPR_ATTR_UNUSED PULONG pfQOP)
1602{
1603 const UINT32 SeqNo = (UINT32)MessageSeqNo;
1604 UINT32 value = 0;
1605 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1606 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1607 UINT32 version = 1;
1608 BYTE expected_signature[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1609 PSecBuffer data_buffer = nullptr;
1610 PSecBuffer signature_buffer = nullptr;
1611 NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
1612 if (!check_context(context))
1613 return SEC_E_INVALID_HANDLE;
1614
1615 for (ULONG index = 0; index < pMessage->cBuffers; index++)
1616 {
1617 if (pMessage->pBuffers[index].BufferType == SECBUFFER_DATA)
1618 data_buffer = &pMessage->pBuffers[index];
1619 else if (pMessage->pBuffers[index].BufferType == SECBUFFER_TOKEN)
1620 signature_buffer = &pMessage->pBuffers[index];
1621 }
1622
1623 if (!data_buffer)
1624 return SEC_E_INVALID_TOKEN;
1625
1626 if (!signature_buffer)
1627 return SEC_E_INVALID_TOKEN;
1628
1629 if (signature_buffer->cbBuffer < 16)
1630 return SEC_E_INVALID_TOKEN;
1631
1632 /* Copy original data buffer */
1633 const ULONG length = data_buffer->cbBuffer;
1634 void* data = malloc(length);
1635
1636 if (!data)
1637 return SEC_E_INSUFFICIENT_MEMORY;
1638
1639 CopyMemory(data, data_buffer->pvBuffer, length);
1640
1641 /* Decrypt message using with RC4, result overwrites original buffer */
1642
1643 if (context->confidentiality)
1644 {
1645 if (!winpr_RC4_Update(context->RecvRc4Seal, length, (BYTE*)data,
1646 (BYTE*)data_buffer->pvBuffer))
1647 {
1648 free(data);
1649 return SEC_E_INSUFFICIENT_MEMORY;
1650 }
1651 }
1652 else
1653 CopyMemory(data_buffer->pvBuffer, data, length);
1654
1655 /* Compute the HMAC-MD5 hash of ConcatenationOf(seq_num,data) using the client signing key */
1656 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1657
1658 BOOL success = FALSE;
1659 {
1660 if (!hmac)
1661 goto hmac_fail;
1662
1663 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->RecvSigningKey, WINPR_MD5_DIGEST_LENGTH))
1664 goto hmac_fail;
1665
1666 winpr_Data_Write_UINT32(&value, SeqNo);
1667
1668 if (!winpr_HMAC_Update(hmac, (void*)&value, 4))
1669 goto hmac_fail;
1670 if (!winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer))
1671 goto hmac_fail;
1672 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1673 goto hmac_fail;
1674
1675 success = TRUE;
1676 }
1677hmac_fail:
1678 winpr_HMAC_Free(hmac);
1679 if (!success)
1680 {
1681 free(data);
1682 return SEC_E_INSUFFICIENT_MEMORY;
1683 }
1684
1685#ifdef WITH_DEBUG_NTLM
1686 WLog_DBG(TAG, "Encrypted Data Buffer (length = %" PRIu32 ")", length);
1687 winpr_HexDump(TAG, WLOG_DEBUG, data, length);
1688 WLog_DBG(TAG, "Data Buffer (length = %" PRIu32 ")", data_buffer->cbBuffer);
1689 winpr_HexDump(TAG, WLOG_DEBUG, data_buffer->pvBuffer, data_buffer->cbBuffer);
1690#endif
1691 free(data);
1692 /* RC4-encrypt first 8 bytes of digest */
1693 if (!winpr_RC4_Update(context->RecvRc4Seal, 8, digest, checksum))
1694 return SEC_E_MESSAGE_ALTERED;
1695
1696 /* Concatenate version, ciphertext and sequence number to build signature */
1697 winpr_Data_Write_UINT32(expected_signature, version);
1698 CopyMemory(&expected_signature[4], (void*)checksum, 8);
1699 winpr_Data_Write_UINT32(&expected_signature[12], SeqNo);
1700 context->RecvSeqNum++;
1701
1702 if (memcmp(signature_buffer->pvBuffer, expected_signature, 16) != 0)
1703 {
1704 /* signature verification failed! */
1705 WLog_ERR(TAG, "signature verification failed, something nasty is going on!");
1706#ifdef WITH_DEBUG_NTLM
1707 WLog_ERR(TAG, "Expected Signature:");
1708 winpr_HexDump(TAG, WLOG_ERROR, expected_signature, 16);
1709 WLog_ERR(TAG, "Actual Signature:");
1710 winpr_HexDump(TAG, WLOG_ERROR, (BYTE*)signature_buffer->pvBuffer, 16);
1711#endif
1712 return SEC_E_MESSAGE_ALTERED;
1713 }
1714
1715 return SEC_E_OK;
1716}
1717
1718static SECURITY_STATUS SEC_ENTRY ntlm_MakeSignature(PCtxtHandle phContext,
1719 WINPR_ATTR_UNUSED ULONG fQOP,
1720 PSecBufferDesc pMessage, ULONG MessageSeqNo)
1721{
1722 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
1723 PSecBuffer data_buffer = nullptr;
1724 PSecBuffer sig_buffer = nullptr;
1725 UINT32 seq_no = 0;
1726 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1727 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1728
1729 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1730 if (!check_context(context))
1731 return SEC_E_INVALID_HANDLE;
1732
1733 for (ULONG i = 0; i < pMessage->cBuffers; i++)
1734 {
1735 if (pMessage->pBuffers[i].BufferType == SECBUFFER_DATA)
1736 data_buffer = &pMessage->pBuffers[i];
1737 else if (pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
1738 sig_buffer = &pMessage->pBuffers[i];
1739 }
1740
1741 if (!data_buffer || !sig_buffer)
1742 return SEC_E_INVALID_TOKEN;
1743
1744 if (sig_buffer->cbBuffer < 16)
1745 return SEC_E_INSUFFICIENT_MEMORY;
1746
1747 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1748
1749 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->SendSigningKey, WINPR_MD5_DIGEST_LENGTH))
1750 goto fail;
1751
1752 winpr_Data_Write_UINT32(&seq_no, MessageSeqNo);
1753 if (!winpr_HMAC_Update(hmac, (BYTE*)&seq_no, 4))
1754 goto fail;
1755 if (!winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer))
1756 goto fail;
1757 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1758 goto fail;
1759
1760 if (!winpr_RC4_Update(context->SendRc4Seal, 8, digest, checksum))
1761 goto fail;
1762
1763 BYTE* signature = sig_buffer->pvBuffer;
1764 winpr_Data_Write_UINT32(signature, 1L);
1765 CopyMemory(&signature[4], checksum, 8);
1766 winpr_Data_Write_UINT32(&signature[12], seq_no);
1767 sig_buffer->cbBuffer = 16;
1768
1769 status = SEC_E_OK;
1770
1771fail:
1772 winpr_HMAC_Free(hmac);
1773 return status;
1774}
1775
1776WINPR_ATTR_NODISCARD
1777static SECURITY_STATUS SEC_ENTRY ntlm_VerifySignature(PCtxtHandle phContext,
1778 PSecBufferDesc pMessage, ULONG MessageSeqNo,
1779 WINPR_ATTR_UNUSED PULONG pfQOP)
1780{
1781 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
1782 PSecBuffer data_buffer = nullptr;
1783 PSecBuffer sig_buffer = nullptr;
1784 UINT32 seq_no = 0;
1785 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1786 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1787 BYTE signature[16] = WINPR_C_ARRAY_INIT;
1788
1789 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1790 if (!check_context(context))
1791 return SEC_E_INVALID_HANDLE;
1792
1793 for (ULONG i = 0; i < pMessage->cBuffers; i++)
1794 {
1795 if (pMessage->pBuffers[i].BufferType == SECBUFFER_DATA)
1796 data_buffer = &pMessage->pBuffers[i];
1797 else if (pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
1798 sig_buffer = &pMessage->pBuffers[i];
1799 }
1800
1801 if (!data_buffer || !sig_buffer || (sig_buffer->cbBuffer < 16))
1802 return SEC_E_INVALID_TOKEN;
1803
1804 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1805
1806 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->RecvSigningKey, WINPR_MD5_DIGEST_LENGTH))
1807 goto fail;
1808
1809 winpr_Data_Write_UINT32(&seq_no, MessageSeqNo);
1810 if (!winpr_HMAC_Update(hmac, (BYTE*)&seq_no, 4))
1811 goto fail;
1812 if (!winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer))
1813 goto fail;
1814 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1815 goto fail;
1816
1817 if (!winpr_RC4_Update(context->RecvRc4Seal, 8, digest, checksum))
1818 goto fail;
1819
1820 winpr_Data_Write_UINT32(signature, 1L);
1821 CopyMemory(&signature[4], checksum, 8);
1822 winpr_Data_Write_UINT32(&signature[12], seq_no);
1823
1824 status = SEC_E_OK;
1825 if (memcmp(sig_buffer->pvBuffer, signature, 16) != 0)
1826 status = SEC_E_MESSAGE_ALTERED;
1827
1828fail:
1829 winpr_HMAC_Free(hmac);
1830 return status;
1831}
1832
1833const SecurityFunctionTableA NTLM_SecurityFunctionTableA = {
1834 3, /* dwVersion */
1835 nullptr, /* EnumerateSecurityPackages */
1836 ntlm_QueryCredentialsAttributesA, /* QueryCredentialsAttributes */
1837 ntlm_AcquireCredentialsHandleA, /* AcquireCredentialsHandle */
1838 ntlm_FreeCredentialsHandle, /* FreeCredentialsHandle */
1839 nullptr, /* Reserved2 */
1840 ntlm_InitializeSecurityContextA, /* InitializeSecurityContext */
1841 ntlm_AcceptSecurityContext, /* AcceptSecurityContext */
1842 nullptr, /* CompleteAuthToken */
1843 ntlm_DeleteSecurityContext, /* DeleteSecurityContext */
1844 nullptr, /* ApplyControlToken */
1845 ntlm_QueryContextAttributesA, /* QueryContextAttributes */
1846 ntlm_ImpersonateSecurityContext, /* ImpersonateSecurityContext */
1847 ntlm_RevertSecurityContext, /* RevertSecurityContext */
1848 ntlm_MakeSignature, /* MakeSignature */
1849 ntlm_VerifySignature, /* VerifySignature */
1850 nullptr, /* FreeContextBuffer */
1851 nullptr, /* QuerySecurityPackageInfo */
1852 nullptr, /* Reserved3 */
1853 nullptr, /* Reserved4 */
1854 nullptr, /* ExportSecurityContext */
1855 nullptr, /* ImportSecurityContext */
1856 nullptr, /* AddCredentials */
1857 nullptr, /* Reserved8 */
1858 nullptr, /* QuerySecurityContextToken */
1859 ntlm_EncryptMessage, /* EncryptMessage */
1860 ntlm_DecryptMessage, /* DecryptMessage */
1861 ntlm_SetContextAttributesA, /* SetContextAttributes */
1862 ntlm_SetCredentialsAttributesA, /* SetCredentialsAttributes */
1863};
1864
1865const SecurityFunctionTableW NTLM_SecurityFunctionTableW = {
1866 3, /* dwVersion */
1867 nullptr, /* EnumerateSecurityPackages */
1868 ntlm_QueryCredentialsAttributesW, /* QueryCredentialsAttributes */
1869 ntlm_AcquireCredentialsHandleW, /* AcquireCredentialsHandle */
1870 ntlm_FreeCredentialsHandle, /* FreeCredentialsHandle */
1871 nullptr, /* Reserved2 */
1872 ntlm_InitializeSecurityContextW, /* InitializeSecurityContext */
1873 ntlm_AcceptSecurityContext, /* AcceptSecurityContext */
1874 nullptr, /* CompleteAuthToken */
1875 ntlm_DeleteSecurityContext, /* DeleteSecurityContext */
1876 nullptr, /* ApplyControlToken */
1877 ntlm_QueryContextAttributesW, /* QueryContextAttributes */
1878 ntlm_ImpersonateSecurityContext, /* ImpersonateSecurityContext */
1879 ntlm_RevertSecurityContext, /* RevertSecurityContext */
1880 ntlm_MakeSignature, /* MakeSignature */
1881 ntlm_VerifySignature, /* VerifySignature */
1882 nullptr, /* FreeContextBuffer */
1883 nullptr, /* QuerySecurityPackageInfo */
1884 nullptr, /* Reserved3 */
1885 nullptr, /* Reserved4 */
1886 nullptr, /* ExportSecurityContext */
1887 nullptr, /* ImportSecurityContext */
1888 nullptr, /* AddCredentials */
1889 nullptr, /* Reserved8 */
1890 nullptr, /* QuerySecurityContextToken */
1891 ntlm_EncryptMessage, /* EncryptMessage */
1892 ntlm_DecryptMessage, /* DecryptMessage */
1893 ntlm_SetContextAttributesW, /* SetContextAttributes */
1894 ntlm_SetCredentialsAttributesW, /* SetCredentialsAttributes */
1895};
1896
1897const SecPkgInfoA NTLM_SecPkgInfoA = {
1898 0x00082B37, /* fCapabilities */
1899 1, /* wVersion */
1900 0x000A, /* wRPCID */
1901 0x00000B48, /* cbMaxToken */
1902 "NTLM", /* Name */
1903 "NTLM Security Package" /* Comment */
1904};
1905
1906static WCHAR NTLM_SecPkgInfoW_NameBuffer[32] = WINPR_C_ARRAY_INIT;
1907static WCHAR NTLM_SecPkgInfoW_CommentBuffer[32] = WINPR_C_ARRAY_INIT;
1908
1909const SecPkgInfoW NTLM_SecPkgInfoW = {
1910 0x00082B37, /* fCapabilities */
1911 1, /* wVersion */
1912 0x000A, /* wRPCID */
1913 0x00000B48, /* cbMaxToken */
1914 NTLM_SecPkgInfoW_NameBuffer, /* Name */
1915 NTLM_SecPkgInfoW_CommentBuffer /* Comment */
1916};
1917
1918char* ntlm_negotiate_flags_string(char* buffer, size_t size, UINT32 flags)
1919{
1920 if (!buffer || (size == 0))
1921 return buffer;
1922
1923 (void)_snprintf(buffer, size, "[0x%08" PRIx32 "] ", flags);
1924
1925 for (int x = 0; x < 31; x++)
1926 {
1927 const UINT32 mask = 1u << x;
1928 size_t len = strnlen(buffer, size);
1929 if (flags & mask)
1930 {
1931 const char* str = ntlm_get_negotiate_string(mask);
1932 const size_t flen = strlen(str);
1933
1934 if ((len > 0) && (buffer[len - 1] != ' '))
1935 {
1936 if (size - len < 1)
1937 break;
1938 winpr_str_append("|", buffer, size, nullptr);
1939 len++;
1940 }
1941
1942 if (size - len < flen)
1943 break;
1944 winpr_str_append(str, buffer, size, nullptr);
1945 }
1946 }
1947
1948 return buffer;
1949}
1950
1951const char* ntlm_message_type_string(UINT32 messageType)
1952{
1953 switch (messageType)
1954 {
1955 case MESSAGE_TYPE_NEGOTIATE:
1956 return "MESSAGE_TYPE_NEGOTIATE";
1957 case MESSAGE_TYPE_CHALLENGE:
1958 return "MESSAGE_TYPE_CHALLENGE";
1959 case MESSAGE_TYPE_AUTHENTICATE:
1960 return "MESSAGE_TYPE_AUTHENTICATE";
1961 default:
1962 return "MESSAGE_TYPE_UNKNOWN";
1963 }
1964}
1965
1966const char* ntlm_state_string(NTLM_STATE state)
1967{
1968 switch (state)
1969 {
1970 case NTLM_STATE_INITIAL:
1971 return "NTLM_STATE_INITIAL";
1972 case NTLM_STATE_NEGOTIATE:
1973 return "NTLM_STATE_NEGOTIATE";
1974 case NTLM_STATE_CHALLENGE:
1975 return "NTLM_STATE_CHALLENGE";
1976 case NTLM_STATE_AUTHENTICATE:
1977 return "NTLM_STATE_AUTHENTICATE";
1978 case NTLM_STATE_FINAL:
1979 return "NTLM_STATE_FINAL";
1980 default:
1981 return "NTLM_STATE_UNKNOWN";
1982 }
1983}
1984void ntlm_change_state(NTLM_CONTEXT* ntlm, NTLM_STATE state)
1985{
1986 WINPR_ASSERT(ntlm);
1987 WLog_DBG(TAG, "change state from %s to %s", ntlm_state_string(ntlm->state),
1988 ntlm_state_string(state));
1989 ntlm->state = state;
1990}
1991
1992NTLM_STATE ntlm_get_state(NTLM_CONTEXT* ntlm)
1993{
1994 WINPR_ASSERT(ntlm);
1995 return ntlm->state;
1996}
1997
1998BOOL ntlm_reset_cipher_state(PSecHandle phContext)
1999{
2000 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
2001
2002 if (context)
2003 {
2004 if (!check_context(context))
2005 return FALSE;
2006
2007 winpr_RC4_Free(context->SendRc4Seal);
2008 winpr_RC4_Free(context->RecvRc4Seal);
2009 context->SendRc4Seal = winpr_RC4_New(context->RecvSealingKey, 16);
2010 context->RecvRc4Seal = winpr_RC4_New(context->SendSealingKey, 16);
2011
2012 if (!context->SendRc4Seal)
2013 {
2014 WLog_ERR(TAG, "Failed to allocate context->SendRc4Seal");
2015 return FALSE;
2016 }
2017 if (!context->RecvRc4Seal)
2018 {
2019 WLog_ERR(TAG, "Failed to allocate context->RecvRc4Seal");
2020 return FALSE;
2021 }
2022 }
2023
2024 return TRUE;
2025}
2026
2027BOOL NTLM_init(void)
2028{
2029 InitializeConstWCharFromUtf8(NTLM_SecPkgInfoA.Name, NTLM_SecPkgInfoW_NameBuffer,
2030 ARRAYSIZE(NTLM_SecPkgInfoW_NameBuffer));
2031 InitializeConstWCharFromUtf8(NTLM_SecPkgInfoA.Comment, NTLM_SecPkgInfoW_CommentBuffer,
2032 ARRAYSIZE(NTLM_SecPkgInfoW_CommentBuffer));
2033
2034 return TRUE;
2035}
2036
2037BOOL ntlm_SecBufferRealloc(SecBuffer* buffer, ULONG len)
2038{
2039 sspi_SecBufferFree(buffer);
2040 return sspi_SecBufferAlloc(buffer, len) != nullptr;
2041}
WINPR_ATTR_NODISCARD psSspiNtlmHashCallback hashCallback