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