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