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