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