21#include <winpr/config.h>
24#include <winpr/wtypes.h>
25#include <winpr/assert.h>
26#include <winpr/sspi.h>
27#include <winpr/tchar.h>
28#include <winpr/registry.h>
29#include <winpr/build-config.h>
30#include <winpr/asn1.h>
34#include "../NTLM/ntlm.h"
35#include "../NTLM/ntlm_export.h"
36#include "../Kerberos/kerberos.h"
39#define TAG WINPR_TAG("negotiate")
41static const char NEGO_REG_KEY[] =
42 "Software\\" WINPR_VENDOR_STRING
"\\" WINPR_PRODUCT_STRING
"\\SSPI\\Negotiate";
72 "Microsoft Package Negotiator"
75static WCHAR NEGOTIATE_SecPkgInfoW_NameBuffer[32] = { 0 };
76static WCHAR NEGOTIATE_SecPkgInfoW_CommentBuffer[32] = { 0 };
83 NEGOTIATE_SecPkgInfoW_NameBuffer,
84 NEGOTIATE_SecPkgInfoW_CommentBuffer
87static const WinPrAsn1_OID spnego_OID = { 6, (BYTE*)
"\x2b\x06\x01\x05\x05\x02" };
89 (BYTE*)
"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x03" };
90static const WinPrAsn1_OID kerberos_OID = { 9, (BYTE*)
"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
92 (BYTE*)
"\x2a\x86\x48\x82\xf7\x12\x01\x02\x02" };
93static const WinPrAsn1_OID ntlm_OID = { 10, (BYTE*)
"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a" };
95static const WinPrAsn1_OID negoex_OID = { 10, (BYTE*)
"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x1e" };
98static const SecPkg SecPkgTable[] = {
99 { KERBEROS_SSP_NAME, &KERBEROS_SecurityFunctionTableA, &KERBEROS_SecurityFunctionTableW },
100 { KERBEROS_SSP_NAME, &KERBEROS_SecurityFunctionTableA, &KERBEROS_SecurityFunctionTableW },
101 { NTLM_SSP_NAME, &NTLM_SecurityFunctionTableA, &NTLM_SecurityFunctionTableW }
104static const Mech MechTable[] = {
105 { &kerberos_u2u_OID, &SecPkgTable[0], ISC_REQ_INTEGRITY | ISC_REQ_USE_SESSION_KEY, TRUE },
106 { &kerberos_OID, &SecPkgTable[1], ISC_REQ_INTEGRITY, TRUE },
107 { &ntlm_OID, &SecPkgTable[2], 0, FALSE },
110static const SecPkg SecPkgTable[] = { { NTLM_SSP_NAME, &NTLM_SecurityFunctionTableA,
111 &NTLM_SecurityFunctionTableW } };
113static const Mech MechTable[] = {
114 { &ntlm_OID, &SecPkgTable[0], 0, FALSE },
118static const size_t MECH_COUNT =
sizeof(MechTable) /
sizeof(Mech);
131 enum NegState negState;
139static const NegToken empty_neg_token = { NOSTATE, FALSE, { 0, NULL },
140 { 0, 0, NULL }, { 0, 0, NULL }, { 0, 0, NULL } };
146 WINPR_ASSERT(init_context);
152 if (init_context->spnego)
154 init_context->mechTypes.pvBuffer = malloc(init_context->mechTypes.cbBuffer);
155 if (!init_context->mechTypes.pvBuffer)
162 *context = *init_context;
169 WINPR_ASSERT(context);
171 if (context->mechTypes.pvBuffer)
172 free(context->mechTypes.pvBuffer);
176static const char* negotiate_mech_name(
const WinPrAsn1_OID* oid)
178 if (sspi_gss_oid_compare(oid, &spnego_OID))
179 return "SPNEGO (1.3.6.1.5.5.2)";
180 else if (sspi_gss_oid_compare(oid, &kerberos_u2u_OID))
181 return "Kerberos user to user (1.2.840.113554.1.2.2.3)";
182 else if (sspi_gss_oid_compare(oid, &kerberos_OID))
183 return "Kerberos (1.2.840.113554.1.2.2)";
184 else if (sspi_gss_oid_compare(oid, &kerberos_wrong_OID))
185 return "Kerberos [wrong OID] (1.2.840.48018.1.2.2)";
186 else if (sspi_gss_oid_compare(oid, &ntlm_OID))
187 return "NTLM (1.3.6.1.4.1.311.2.2.10)";
188 else if (sspi_gss_oid_compare(oid, &negoex_OID))
189 return "NegoEx (1.3.6.1.4.1.311.2.2.30)";
191 return "Unknown mechanism";
194static const Mech* negotiate_GetMechByOID(
const WinPrAsn1_OID* oid)
200 if (sspi_gss_oid_compare(&testOid, &kerberos_wrong_OID))
202 testOid.len = kerberos_OID.len;
203 testOid.data = kerberos_OID.data;
206 for (
size_t i = 0; i < MECH_COUNT; i++)
208 if (sspi_gss_oid_compare(&testOid, MechTable[i].oid))
209 return &MechTable[i];
214static PSecHandle negotiate_FindCredential(MechCred* creds,
const Mech* mech)
221 for (
size_t i = 0; i < MECH_COUNT; i++)
223 MechCred* cred = &creds[i];
225 if (cred->mech == mech)
236static BOOL negotiate_get_dword(HKEY hKey,
const char* subkey, DWORD* pdwValue)
240 DWORD dwSize =
sizeof(dwValue);
241 LONG rc = RegQueryValueExA(hKey, subkey, NULL, &dwType, (BYTE*)&dwValue, &dwSize);
243 if (rc != ERROR_SUCCESS)
245 if (dwType != REG_DWORD)
252static BOOL negotiate_get_config_from_auth_package_list(
void* pAuthData, BOOL* kerberos, BOOL* ntlm,
255 char* tok_ctx = NULL;
256 char* tok_ptr = NULL;
257 char* PackageList = NULL;
262 tok_ptr = strtok_s(PackageList,
",", &tok_ctx);
266 char* PackageName = tok_ptr;
267 BOOL PackageInclude = TRUE;
269 if (PackageName[0] ==
'!')
271 PackageName = &PackageName[1];
272 PackageInclude = FALSE;
275 if (_stricmp(PackageName,
"ntlm") == 0)
277 *ntlm = PackageInclude;
279 else if (_stricmp(PackageName,
"kerberos") == 0)
281 *kerberos = PackageInclude;
283 else if (_stricmp(PackageName,
"u2u") == 0)
285 *u2u = PackageInclude;
289 WLog_WARN(TAG,
"Unknown authentication package name: %s", PackageName);
292 tok_ptr = strtok_s(NULL,
",", &tok_ctx);
299static BOOL negotiate_get_config(
void* pAuthData, BOOL* kerberos, BOOL* ntlm, BOOL* u2u)
304 WINPR_ASSERT(kerberos);
308#if !defined(WITH_KRB5_NO_NTLM_FALLBACK)
313#if defined(WITH_KRB5)
321 if (negotiate_get_config_from_auth_package_list(pAuthData, kerberos, ntlm, u2u))
326 rc = RegOpenKeyExA(HKEY_LOCAL_MACHINE, NEGO_REG_KEY, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
327 if (rc == ERROR_SUCCESS)
331 if (negotiate_get_dword(hKey,
"kerberos", &dwValue))
332 *kerberos = (dwValue != 0) ? TRUE : FALSE;
334#if !defined(WITH_KRB5_NO_NTLM_FALLBACK)
335 if (negotiate_get_dword(hKey,
"ntlm", &dwValue))
336 *ntlm = (dwValue != 0) ? TRUE : FALSE;
345static BOOL negotiate_write_neg_token(
PSecBuffer output_buffer, NegToken* token)
347 WINPR_ASSERT(output_buffer);
351 WinPrAsn1Encoder* enc = NULL;
358 enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER);
366 if (!WinPrAsn1EncAppContainer(enc, 0))
370 if (!WinPrAsn1EncOID(enc, &spnego_OID))
375 if (!WinPrAsn1EncContextualSeqContainer(enc, token->init ? 0 : 1))
378 WLog_DBG(TAG, token->init ?
"Writing negTokenInit..." :
"Writing negTokenResp...");
383 if (!WinPrAsn1EncContextualRawContent(enc, 0, &mechTypes))
385 WLog_DBG(TAG,
"\tmechTypes [0] (%li bytes)", token->mechTypes.cbBuffer);
388 else if (token->negState != NOSTATE)
390 if (!WinPrAsn1EncContextualEnumerated(enc, 0, token->negState))
392 WLog_DBG(TAG,
"\tnegState [0] (%d)", token->negState);
396 if (token->supportedMech.len)
398 if (!WinPrAsn1EncContextualOID(enc, 1, &token->supportedMech))
400 WLog_DBG(TAG,
"\tsupportedMech [1] (%s)", negotiate_mech_name(&token->supportedMech));
404 if (token->mechToken.cbBuffer)
406 if (WinPrAsn1EncContextualOctetString(enc, 2, &mechToken) == 0)
408 WLog_DBG(TAG,
"\tmechToken [2] (%li bytes)", token->mechToken.cbBuffer);
412 if (token->mic.cbBuffer)
414 if (WinPrAsn1EncContextualOctetString(enc, 3, &mechListMic) == 0)
416 WLog_DBG(TAG,
"\tmechListMIC [3] (%li bytes)", token->mic.cbBuffer);
420 if (!WinPrAsn1EncEndContainer(enc))
426 if (!WinPrAsn1EncEndContainer(enc))
430 if (!WinPrAsn1EncStreamSize(enc, &len) || len > output_buffer->cbBuffer)
433 if (len > UINT32_MAX)
436 Stream_StaticInit(&s, output_buffer->pvBuffer, len);
438 if (WinPrAsn1EncToStream(enc, &s))
440 output_buffer->cbBuffer = (UINT32)len;
445 WinPrAsn1Encoder_Free(&enc);
449static BOOL negotiate_read_neg_token(
PSecBuffer input, NegToken* token)
454 WinPrAsn1_tagId contextual = 0;
455 WinPrAsn1_tag tag = 0;
463 WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, input->pvBuffer, input->cbBuffer);
465 if (!WinPrAsn1DecPeekTag(&dec, &tag))
471 if (!WinPrAsn1DecReadApp(&dec, &tag, &dec2) || tag != 0)
476 if (!WinPrAsn1DecReadOID(&dec, &oid, FALSE))
479 if (!sspi_gss_oid_compare(&spnego_OID, &oid))
483 if (!WinPrAsn1DecReadContextualSequence(&dec, 0, &err, &dec2))
489 else if (!WinPrAsn1DecReadContextualSequence(&dec, 1, &err, &dec2))
493 WLog_DBG(TAG, token->init ?
"Reading negTokenInit..." :
"Reading negTokenResp...");
498 if (!WinPrAsn1DecReadContextualTag(&dec, &contextual, &dec2))
507 wStream s = WinPrAsn1DecGetStream(&dec2);
508 token->mechTypes.BufferType = SECBUFFER_TOKEN;
509 const size_t mlen = Stream_Length(&s);
510 if (mlen > UINT32_MAX)
512 token->mechTypes.cbBuffer = (UINT32)mlen;
513 token->mechTypes.pvBuffer = Stream_Buffer(&s);
514 WLog_DBG(TAG,
"\tmechTypes [0] (%li bytes)", token->mechTypes.cbBuffer);
519 WinPrAsn1_ENUMERATED rd = 0;
520 if (!WinPrAsn1DecReadEnumerated(&dec2, &rd))
522 token->negState = rd;
523 WLog_DBG(TAG,
"\tnegState [0] (%d)", token->negState);
530 if (!WinPrAsn1DecPeekTagAndLen(&dec2, &tag, &len) || (tag != ER_TAG_BIT_STRING))
532 WLog_DBG(TAG,
"\treqFlags [1] (%li bytes)", len);
537 if (!WinPrAsn1DecReadOID(&dec2, &token->supportedMech, FALSE))
539 WLog_DBG(TAG,
"\tsupportedMech [1] (%s)",
540 negotiate_mech_name(&token->supportedMech));
545 if (!WinPrAsn1DecReadOctetString(&dec2, &octet_string, FALSE))
547 if (octet_string.len > UINT32_MAX)
549 token->mechToken.cbBuffer = (UINT32)octet_string.len;
550 token->mechToken.pvBuffer = octet_string.data;
551 token->mechToken.BufferType = SECBUFFER_TOKEN;
552 WLog_DBG(TAG,
"\tmechToken [2] (%li bytes)", octet_string.len);
556 if (!WinPrAsn1DecReadOctetString(&dec2, &octet_string, FALSE))
558 if (octet_string.len > UINT32_MAX)
560 token->mic.cbBuffer = (UINT32)octet_string.len;
561 token->mic.pvBuffer = octet_string.data;
562 token->mic.BufferType = SECBUFFER_TOKEN;
563 WLog_DBG(TAG,
"\tmechListMIC [3] (%li bytes)", octet_string.len);
566 WLog_ERR(TAG,
"unknown contextual item %d", contextual);
569 }
while (WinPrAsn1DecPeekTag(&dec, &tag));
574static SECURITY_STATUS negotiate_mic_exchange(
NEGOTIATE_CONTEXT* context, NegToken* input_token,
575 NegToken* output_token,
PSecBuffer output_buffer)
578 SecBufferDesc mic_buffer_desc = { SECBUFFER_VERSION, 2, mic_buffers };
579 SECURITY_STATUS status = 0;
581 WINPR_ASSERT(context);
582 WINPR_ASSERT(input_token);
583 WINPR_ASSERT(output_token);
584 WINPR_ASSERT(context->mech);
585 WINPR_ASSERT(context->mech->pkg);
590 mic_buffers[0] = context->mechTypes;
593 if (input_token->mic.cbBuffer > 0)
595 mic_buffers[1] = input_token->mic;
597 status = table->VerifySignature(&context->sub_context, &mic_buffer_desc, 0, 0);
598 if (status != SEC_E_OK)
601 output_token->negState = ACCEPT_COMPLETED;
605 if (input_token->negState != ACCEPT_COMPLETED)
608 output_token->mic.BufferType = SECBUFFER_TOKEN;
611 output_token->mic.cbBuffer = output_buffer->cbBuffer - output_token->mechToken.cbBuffer;
612 output_token->mic.pvBuffer =
613 (BYTE*)output_buffer->pvBuffer + output_token->mechToken.cbBuffer;
615 mic_buffers[1] = output_token->mic;
617 status = table->MakeSignature(&context->sub_context, 0, &mic_buffer_desc, 0);
618 if (status != SEC_E_OK)
621 output_token->mic = mic_buffers[1];
625 const TCHAR* name = sspi_SecureHandleGetUpperPointer(&context->sub_context);
627 return SEC_E_INTERNAL_ERROR;
629 if (_tcsncmp(name, NTLM_SSP_NAME, ARRAYSIZE(NTLM_SSP_NAME)) == 0)
631 if (!ntlm_reset_cipher_state(&context->sub_context))
632 return SEC_E_INTERNAL_ERROR;
638static SECURITY_STATUS SEC_ENTRY negotiate_InitializeSecurityContextW(
640 ULONG Reserved1, ULONG TargetDataRep,
PSecBufferDesc pInput, ULONG Reserved2,
645 MechCred* creds = NULL;
648 NegToken input_token = empty_neg_token;
649 NegToken output_token = empty_neg_token;
654 SecBufferDesc mech_input = { SECBUFFER_VERSION, 2, mech_input_buffers };
655 SecBufferDesc mech_output = { SECBUFFER_VERSION, 1, &output_token.mechToken };
656 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
657 SECURITY_STATUS sub_status = SEC_E_INTERNAL_ERROR;
658 WinPrAsn1Encoder* enc = NULL;
660 const Mech* mech = NULL;
662 if (!phCredential || !SecIsValidHandle(phCredential))
663 return SEC_E_NO_CREDENTIALS;
665 creds = sspi_SecureHandleGetLowerPointer(phCredential);
668 if (phContext && !phContext->dwLower && !phContext->dwUpper)
669 return SEC_E_INVALID_HANDLE;
671 context = sspi_SecureHandleGetLowerPointer(phContext);
675 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
676 bindings_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_CHANNEL_BINDINGS);
679 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
683 enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER);
685 return SEC_E_INSUFFICIENT_MEMORY;
687 if (!WinPrAsn1EncSeqContainer(enc))
690 for (
size_t i = 0; i < MECH_COUNT; i++)
692 MechCred* cred = &creds[i];
693 const SecPkg* pkg = MechTable[i].pkg;
695 WINPR_ASSERT(pkg->table_w);
701 if (!init_context.mech)
707 CopyMemory(&output_token.mechToken, output_buffer,
sizeof(
SecBuffer));
710 mech_input_buffers[0] = *bindings_buffer;
712 WINPR_ASSERT(pkg->table_w->InitializeSecurityContextW);
713 sub_status = pkg->table_w->InitializeSecurityContextW(
714 &cred->cred, NULL, pszTargetName, fContextReq | cred->mech->flags, Reserved1,
715 TargetDataRep, &mech_input, Reserved2, &init_context.sub_context, &mech_output,
716 pfContextAttr, ptsExpiry);
719 if (IsSecurityStatusError(sub_status))
721 if (SecIsValidHandle(&init_context.sub_context))
723 WINPR_ASSERT(pkg->table_w->DeleteSecurityContext);
724 pkg->table_w->DeleteSecurityContext(&init_context.sub_context);
730 init_context.mech = cred->mech;
733 if (!WinPrAsn1EncOID(enc, cred->mech->oid))
735 WLog_DBG(TAG,
"Available mechanism: %s", negotiate_mech_name(cred->mech->oid));
739 if (!init_context.mech)
743 if (init_context.mech->oid == &ntlm_OID)
745 init_context.spnego = FALSE;
746 output_buffer->cbBuffer = output_token.mechToken.cbBuffer;
747 WLog_DBG(TAG,
"Using direct NTLM");
751 init_context.spnego = TRUE;
752 init_context.mechTypes.BufferType = SECBUFFER_DATA;
753 const size_t cb = WinPrAsn1EncEndContainer(enc);
754 WINPR_ASSERT(cb <= UINT32_MAX);
755 init_context.mechTypes.cbBuffer = (UINT32)cb;
759 context = negotiate_ContextNew(&init_context);
762 init_context.mech->pkg->table->DeleteSecurityContext(&init_context.sub_context);
763 WinPrAsn1Encoder_Free(&enc);
764 return SEC_E_INSUFFICIENT_MEMORY;
767 sspi_SecureHandleSetUpperPointer(phNewContext, NEGO_SSP_NAME);
768 sspi_SecureHandleSetLowerPointer(phNewContext, context);
770 if (!context->spnego)
777 Stream_StaticInit(&s, context->mechTypes.pvBuffer, context->mechTypes.cbBuffer);
778 if (!WinPrAsn1EncToStream(enc, &s))
781 output_token.mechTypes.cbBuffer = context->mechTypes.cbBuffer;
782 output_token.mechTypes.pvBuffer = context->mechTypes.pvBuffer;
783 output_token.init = TRUE;
785 if (sub_status == SEC_E_OK)
786 context->state = NEGOTIATE_STATE_FINAL_OPTIMISTIC;
791 return SEC_E_INVALID_TOKEN;
793 sub_context = &context->sub_context;
794 sub_cred = negotiate_FindCredential(creds, context->mech);
796 if (!context->spnego)
798 return context->mech->pkg->table_w->InitializeSecurityContextW(
799 sub_cred, sub_context, pszTargetName, fContextReq | context->mech->flags, Reserved1,
800 TargetDataRep, pInput, Reserved2, sub_context, pOutput, pfContextAttr, ptsExpiry);
803 if (!negotiate_read_neg_token(input_buffer, &input_token))
804 return SEC_E_INVALID_TOKEN;
807 if (context->state < NEGOTIATE_STATE_NEGORESP && input_token.supportedMech.len &&
808 !sspi_gss_oid_compare(&input_token.supportedMech, context->mech->oid))
810 mech = negotiate_GetMechByOID(&input_token.supportedMech);
812 return SEC_E_INVALID_TOKEN;
815 sub_cred = negotiate_FindCredential(creds, mech);
817 return SEC_E_INVALID_TOKEN;
820 context->mech->pkg->table_w->DeleteSecurityContext(&context->sub_context);
823 context->mech = mech;
828 if (context->state < NEGOTIATE_STATE_NEGORESP)
830 switch (input_token.negState)
833 return SEC_E_INVALID_TOKEN;
835 return SEC_E_LOGON_DENIED;
840 case ACCEPT_INCOMPLETE:
841 context->state = NEGOTIATE_STATE_NEGORESP;
843 case ACCEPT_COMPLETED:
844 if (context->state == NEGOTIATE_STATE_INITIAL)
845 context->state = NEGOTIATE_STATE_NEGORESP;
847 context->state = NEGOTIATE_STATE_FINAL;
853 WLog_DBG(TAG,
"Negotiated mechanism: %s", negotiate_mech_name(context->mech->oid));
856 if (context->state == NEGOTIATE_STATE_NEGORESP)
861 CopyMemory(&output_token.mechToken, output_buffer,
sizeof(
SecBuffer));
863 mech_input_buffers[0] = input_token.mechToken;
865 mech_input_buffers[1] = *bindings_buffer;
867 status = context->mech->pkg->table_w->InitializeSecurityContextW(
868 sub_cred, sub_context, pszTargetName, fContextReq | context->mech->flags, Reserved1,
869 TargetDataRep, input_token.mechToken.cbBuffer ? &mech_input : NULL, Reserved2,
870 &context->sub_context, &mech_output, pfContextAttr, ptsExpiry);
872 if (IsSecurityStatusError(status))
876 if (status == SEC_E_OK)
878 if (output_token.mechToken.cbBuffer > 0)
879 context->state = NEGOTIATE_STATE_MIC;
881 context->state = NEGOTIATE_STATE_FINAL;
885 if (context->state == NEGOTIATE_STATE_FINAL && input_token.mic.cbBuffer == 0)
887 if (context->mic || input_token.negState != ACCEPT_COMPLETED)
888 return SEC_E_INVALID_TOKEN;
891 output_buffer->cbBuffer = 0;
895 if ((context->state == NEGOTIATE_STATE_MIC && context->mic) ||
896 context->state == NEGOTIATE_STATE_FINAL)
898 status = negotiate_mic_exchange(context, &input_token, &output_token, output_buffer);
899 if (status != SEC_E_OK)
904 if (input_token.negState == ACCEPT_COMPLETED)
907 output_buffer->cbBuffer = 0;
911 if (output_token.negState == ACCEPT_COMPLETED)
914 status = SEC_I_CONTINUE_NEEDED;
916 if (!negotiate_write_neg_token(output_buffer, &output_token))
917 status = SEC_E_INTERNAL_ERROR;
920 WinPrAsn1Encoder_Free(&enc);
924static SECURITY_STATUS SEC_ENTRY negotiate_InitializeSecurityContextA(
926 ULONG Reserved1, ULONG TargetDataRep,
PSecBufferDesc pInput, ULONG Reserved2,
929 SECURITY_STATUS status = 0;
930 SEC_WCHAR* pszTargetNameW = NULL;
934 pszTargetNameW = ConvertUtf8ToWCharAlloc(pszTargetName, NULL);
936 return SEC_E_INTERNAL_ERROR;
939 status = negotiate_InitializeSecurityContextW(
940 phCredential, phContext, pszTargetNameW, fContextReq, Reserved1, TargetDataRep, pInput,
941 Reserved2, phNewContext, pOutput, pfContextAttr, ptsExpiry);
942 free(pszTargetNameW);
950 WinPrAsn1_tagId tag = 0;
951 const char ssp[] =
"NTLMSSP";
956 if (input_buffer->cbBuffer >= 8 && strncmp(input_buffer->pvBuffer, ssp,
sizeof(ssp)) == 0)
959 return negotiate_GetMechByOID(&ntlm_OID);
963 WinPrAsn1Decoder_InitMem(&decoder, WINPR_ASN1_DER, input_buffer->pvBuffer,
964 input_buffer->cbBuffer);
966 if (!WinPrAsn1DecReadApp(&decoder, &tag, &appDecoder) || tag != 0)
969 if (!WinPrAsn1DecReadOID(&appDecoder, oid, FALSE))
972 if (sspi_gss_oid_compare(oid, &spnego_OID))
978 return negotiate_GetMechByOID(oid);
981static SECURITY_STATUS SEC_ENTRY negotiate_AcceptSecurityContext(
988 MechCred* creds = NULL;
990 NegToken input_token = empty_neg_token;
991 NegToken output_token = empty_neg_token;
994 SecBufferDesc mech_input = { SECBUFFER_VERSION, 1, &input_token.mechToken };
995 SecBufferDesc mech_output = { SECBUFFER_VERSION, 1, &output_token.mechToken };
996 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
999 WinPrAsn1_tagId tag = 0;
1001 const Mech* first_mech = NULL;
1003 if (!phCredential || !SecIsValidHandle(phCredential))
1004 return SEC_E_NO_CREDENTIALS;
1006 creds = sspi_SecureHandleGetLowerPointer(phCredential);
1009 return SEC_E_INVALID_TOKEN;
1012 if (phContext && !phContext->dwLower && !phContext->dwUpper)
1013 return SEC_E_INVALID_HANDLE;
1015 context = sspi_SecureHandleGetLowerPointer(phContext);
1017 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
1019 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
1023 init_context.mech = guessMech(input_buffer, &init_context.spnego, &oid);
1024 if (!init_context.mech && !init_context.spnego)
1025 return SEC_E_INVALID_TOKEN;
1027 WLog_DBG(TAG,
"Mechanism: %s", negotiate_mech_name(&oid));
1029 if (init_context.spnego)
1032 if (!negotiate_read_neg_token(input_buffer, &input_token))
1033 return SEC_E_INVALID_TOKEN;
1036 if (!input_token.init || input_token.mechTypes.cbBuffer == 0)
1037 return SEC_E_INVALID_TOKEN;
1039 init_context.mechTypes.BufferType = SECBUFFER_DATA;
1040 init_context.mechTypes.cbBuffer = input_token.mechTypes.cbBuffer;
1043 WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, input_token.mechTypes.pvBuffer,
1044 input_token.mechTypes.cbBuffer);
1046 if (!WinPrAsn1DecReadSequence(&dec, &dec2))
1047 return SEC_E_INVALID_TOKEN;
1051 if (input_token.mechToken.cbBuffer)
1053 if (!WinPrAsn1DecReadOID(&dec, &oid, FALSE))
1054 return SEC_E_INVALID_TOKEN;
1056 init_context.mech = negotiate_GetMechByOID(&oid);
1058 if (init_context.mech)
1061 output_token.mechToken = *output_buffer;
1062 WLog_DBG(TAG,
"Requested mechanism: %s",
1063 negotiate_mech_name(init_context.mech->oid));
1068 if (init_context.mech)
1070 sub_cred = negotiate_FindCredential(creds, init_context.mech);
1072 status = init_context.mech->pkg->table->AcceptSecurityContext(
1073 sub_cred, NULL, init_context.spnego ? &mech_input : pInput, fContextReq,
1074 TargetDataRep, &init_context.sub_context,
1075 init_context.spnego ? &mech_output : pOutput, pfContextAttr, ptsTimeStamp);
1078 if (IsSecurityStatusError(status))
1080 if (!init_context.spnego)
1083 init_context.mic = TRUE;
1084 first_mech = init_context.mech;
1085 init_context.mech = NULL;
1086 output_token.mechToken.cbBuffer = 0;
1089 while (!init_context.mech && WinPrAsn1DecPeekTag(&dec, &tag))
1092 if (!WinPrAsn1DecReadOID(&dec, &oid, FALSE))
1093 return SEC_E_INVALID_TOKEN;
1095 init_context.mech = negotiate_GetMechByOID(&oid);
1096 WLog_DBG(TAG,
"Requested mechanism: %s", negotiate_mech_name(&oid));
1099 if (init_context.mech == first_mech)
1100 init_context.mech = NULL;
1102 if (init_context.mech && !negotiate_FindCredential(creds, init_context.mech))
1103 init_context.mech = NULL;
1106 if (!init_context.mech)
1107 return SEC_E_INTERNAL_ERROR;
1109 context = negotiate_ContextNew(&init_context);
1112 if (!IsSecurityStatusError(status))
1113 init_context.mech->pkg->table->DeleteSecurityContext(&init_context.sub_context);
1114 return SEC_E_INSUFFICIENT_MEMORY;
1117 sspi_SecureHandleSetUpperPointer(phNewContext, NEGO_SSP_NAME);
1118 sspi_SecureHandleSetLowerPointer(phNewContext, context);
1120 if (!init_context.spnego)
1123 CopyMemory(init_context.mechTypes.pvBuffer, input_token.mechTypes.pvBuffer,
1124 input_token.mechTypes.cbBuffer);
1126 if (!context->mech->preferred)
1128 output_token.negState = REQUEST_MIC;
1129 context->mic = TRUE;
1133 output_token.negState = ACCEPT_INCOMPLETE;
1136 if (status == SEC_E_OK)
1137 context->state = NEGOTIATE_STATE_FINAL;
1139 context->state = NEGOTIATE_STATE_NEGORESP;
1141 output_token.supportedMech = oid;
1142 WLog_DBG(TAG,
"Accepted mechanism: %s", negotiate_mech_name(&output_token.supportedMech));
1146 sub_cred = negotiate_FindCredential(creds, context->mech);
1148 return SEC_E_NO_CREDENTIALS;
1150 if (!context->spnego)
1152 return context->mech->pkg->table->AcceptSecurityContext(
1153 sub_cred, &context->sub_context, pInput, fContextReq, TargetDataRep,
1154 &context->sub_context, pOutput, pfContextAttr, ptsTimeStamp);
1157 if (!negotiate_read_neg_token(input_buffer, &input_token))
1158 return SEC_E_INVALID_TOKEN;
1161 if (input_token.mechToken.cbBuffer > 0)
1163 if (context->state != NEGOTIATE_STATE_NEGORESP)
1164 return SEC_E_INVALID_TOKEN;
1168 CopyMemory(&output_token.mechToken, output_buffer,
sizeof(
SecBuffer));
1170 status = context->mech->pkg->table->AcceptSecurityContext(
1171 sub_cred, &context->sub_context, &mech_input, fContextReq | context->mech->flags,
1172 TargetDataRep, &context->sub_context, &mech_output, pfContextAttr, ptsTimeStamp);
1174 if (IsSecurityStatusError(status))
1177 if (status == SEC_E_OK)
1178 context->state = NEGOTIATE_STATE_FINAL;
1180 else if (context->state == NEGOTIATE_STATE_NEGORESP)
1181 return SEC_E_INVALID_TOKEN;
1184 if (context->state == NEGOTIATE_STATE_FINAL)
1187 if (context->mic && output_token.mechToken.cbBuffer == 0 && input_token.mic.cbBuffer == 0)
1188 return SEC_E_INVALID_TOKEN;
1190 if (context->mic || input_token.mic.cbBuffer > 0)
1192 status = negotiate_mic_exchange(context, &input_token, &output_token, output_buffer);
1193 if (status != SEC_E_OK)
1197 output_token.negState = ACCEPT_COMPLETED;
1200 if (input_token.negState == ACCEPT_COMPLETED)
1203 output_buffer->cbBuffer = 0;
1207 if (output_token.negState == ACCEPT_COMPLETED)
1210 status = SEC_I_CONTINUE_NEEDED;
1212 if (!negotiate_write_neg_token(output_buffer, &output_token))
1213 return SEC_E_INTERNAL_ERROR;
1218static SECURITY_STATUS SEC_ENTRY negotiate_CompleteAuthToken(
PCtxtHandle phContext,
1222 SECURITY_STATUS status = SEC_E_OK;
1226 return SEC_E_INVALID_HANDLE;
1228 WINPR_ASSERT(context->mech);
1229 WINPR_ASSERT(context->mech->pkg);
1230 WINPR_ASSERT(context->mech->pkg->table);
1231 if (context->mech->pkg->table->CompleteAuthToken)
1232 status = context->mech->pkg->table->CompleteAuthToken(&context->sub_context, pToken);
1237static SECURITY_STATUS SEC_ENTRY negotiate_DeleteSecurityContext(
PCtxtHandle phContext)
1240 SECURITY_STATUS status = SEC_E_OK;
1242 const SecPkg* pkg = NULL;
1245 return SEC_E_INVALID_HANDLE;
1247 WINPR_ASSERT(context->mech);
1248 WINPR_ASSERT(context->mech->pkg);
1249 WINPR_ASSERT(context->mech->pkg->table);
1250 pkg = context->mech->pkg;
1252 if (pkg->table->DeleteSecurityContext)
1253 status = pkg->table->DeleteSecurityContext(&context->sub_context);
1255 negotiate_ContextFree(context);
1259static SECURITY_STATUS SEC_ENTRY
1260negotiate_ImpersonateSecurityContext(WINPR_ATTR_UNUSED
PCtxtHandle phContext)
1265static SECURITY_STATUS SEC_ENTRY
1266negotiate_RevertSecurityContext(WINPR_ATTR_UNUSED
PCtxtHandle phContext)
1271static SECURITY_STATUS SEC_ENTRY negotiate_QueryContextAttributesW(
PCtxtHandle phContext,
1272 ULONG ulAttribute,
void* pBuffer)
1277 return SEC_E_INVALID_HANDLE;
1279 WINPR_ASSERT(context->mech);
1280 WINPR_ASSERT(context->mech->pkg);
1281 WINPR_ASSERT(context->mech->pkg->table_w);
1282 if (context->mech->pkg->table_w->QueryContextAttributesW)
1283 return context->mech->pkg->table_w->QueryContextAttributesW(&context->sub_context,
1284 ulAttribute, pBuffer);
1286 return SEC_E_UNSUPPORTED_FUNCTION;
1289static SECURITY_STATUS SEC_ENTRY negotiate_QueryContextAttributesA(
PCtxtHandle phContext,
1290 ULONG ulAttribute,
void* pBuffer)
1295 return SEC_E_INVALID_HANDLE;
1297 WINPR_ASSERT(context->mech);
1298 WINPR_ASSERT(context->mech->pkg);
1299 WINPR_ASSERT(context->mech->pkg->table);
1300 if (context->mech->pkg->table->QueryContextAttributesA)
1301 return context->mech->pkg->table->QueryContextAttributesA(&context->sub_context,
1302 ulAttribute, pBuffer);
1304 return SEC_E_UNSUPPORTED_FUNCTION;
1307static SECURITY_STATUS SEC_ENTRY negotiate_SetContextAttributesW(
PCtxtHandle phContext,
1308 ULONG ulAttribute,
void* pBuffer,
1314 return SEC_E_INVALID_HANDLE;
1316 WINPR_ASSERT(context->mech);
1317 WINPR_ASSERT(context->mech->pkg);
1318 WINPR_ASSERT(context->mech->pkg->table_w);
1319 if (context->mech->pkg->table_w->SetContextAttributesW)
1320 return context->mech->pkg->table_w->SetContextAttributesW(&context->sub_context,
1321 ulAttribute, pBuffer, cbBuffer);
1323 return SEC_E_UNSUPPORTED_FUNCTION;
1326static SECURITY_STATUS SEC_ENTRY negotiate_SetContextAttributesA(
PCtxtHandle phContext,
1327 ULONG ulAttribute,
void* pBuffer,
1333 return SEC_E_INVALID_HANDLE;
1335 WINPR_ASSERT(context->mech);
1336 WINPR_ASSERT(context->mech->pkg);
1337 WINPR_ASSERT(context->mech->pkg->table);
1338 if (context->mech->pkg->table->SetContextAttributesA)
1339 return context->mech->pkg->table->SetContextAttributesA(&context->sub_context, ulAttribute,
1342 return SEC_E_UNSUPPORTED_FUNCTION;
1345static SECURITY_STATUS SEC_ENTRY negotiate_SetCredentialsAttributesW(
PCredHandle phCredential,
1347 void* pBuffer, ULONG cbBuffer)
1349 MechCred* creds = NULL;
1350 BOOL success = FALSE;
1351 SECURITY_STATUS secStatus = 0;
1353 creds = sspi_SecureHandleGetLowerPointer(phCredential);
1356 return SEC_E_INVALID_HANDLE;
1358 for (
size_t i = 0; i < MECH_COUNT; i++)
1360 MechCred* cred = &creds[i];
1362 WINPR_ASSERT(cred->mech);
1363 WINPR_ASSERT(cred->mech->pkg);
1364 WINPR_ASSERT(cred->mech->pkg->table);
1365 WINPR_ASSERT(cred->mech->pkg->table_w->SetCredentialsAttributesW);
1366 secStatus = cred->mech->pkg->table_w->SetCredentialsAttributesW(&cred->cred, ulAttribute,
1369 if (secStatus == SEC_E_OK)
1376 return (success ? SEC_E_OK : SEC_E_UNSUPPORTED_FUNCTION);
1379static SECURITY_STATUS SEC_ENTRY negotiate_SetCredentialsAttributesA(
PCredHandle phCredential,
1381 void* pBuffer, ULONG cbBuffer)
1383 MechCred* creds = NULL;
1384 BOOL success = FALSE;
1385 SECURITY_STATUS secStatus = 0;
1387 creds = sspi_SecureHandleGetLowerPointer(phCredential);
1390 return SEC_E_INVALID_HANDLE;
1392 for (
size_t i = 0; i < MECH_COUNT; i++)
1394 MechCred* cred = &creds[i];
1399 WINPR_ASSERT(cred->mech);
1400 WINPR_ASSERT(cred->mech->pkg);
1401 WINPR_ASSERT(cred->mech->pkg->table);
1402 WINPR_ASSERT(cred->mech->pkg->table->SetCredentialsAttributesA);
1403 secStatus = cred->mech->pkg->table->SetCredentialsAttributesA(&cred->cred, ulAttribute,
1406 if (secStatus == SEC_E_OK)
1413 return (success ? SEC_E_OK : SEC_E_UNSUPPORTED_FUNCTION);
1416static SECURITY_STATUS SEC_ENTRY negotiate_AcquireCredentialsHandleW(
1417 SEC_WCHAR* pszPrincipal, SEC_WCHAR* pszPackage, ULONG fCredentialUse,
void* pvLogonID,
1418 void* pAuthData, SEC_GET_KEY_FN pGetKeyFn,
void* pvGetKeyArgument,
PCredHandle phCredential,
1421 BOOL kerberos = FALSE;
1425 if (!negotiate_get_config(pAuthData, &kerberos, &ntlm, &u2u))
1426 return SEC_E_INTERNAL_ERROR;
1428 MechCred* creds = calloc(MECH_COUNT,
sizeof(MechCred));
1431 return SEC_E_INTERNAL_ERROR;
1433 for (
size_t i = 0; i < MECH_COUNT; i++)
1435 MechCred* cred = &creds[i];
1436 const SecPkg* pkg = MechTable[i].pkg;
1437 cred->mech = &MechTable[i];
1439 if (!kerberos && sspi_gss_oid_compare(MechTable[i].oid, &kerberos_OID))
1441 if (!u2u && sspi_gss_oid_compare(MechTable[i].oid, &kerberos_u2u_OID))
1443 if (!ntlm && _tcsncmp(SecPkgTable[i].name, NTLM_SSP_NAME, ARRAYSIZE(NTLM_SSP_NAME)) == 0)
1446 WINPR_ASSERT(pkg->table_w);
1447 WINPR_ASSERT(pkg->table_w->AcquireCredentialsHandleW);
1448 if (pkg->table_w->AcquireCredentialsHandleW(
1449 pszPrincipal, pszPackage, fCredentialUse, pvLogonID, pAuthData, pGetKeyFn,
1450 pvGetKeyArgument, &cred->cred, ptsExpiry) != SEC_E_OK)
1456 sspi_SecureHandleSetLowerPointer(phCredential, (
void*)creds);
1457 sspi_SecureHandleSetUpperPointer(phCredential, (
void*)NEGO_SSP_NAME);
1461static SECURITY_STATUS SEC_ENTRY negotiate_AcquireCredentialsHandleA(
1462 SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, ULONG fCredentialUse,
void* pvLogonID,
1463 void* pAuthData, SEC_GET_KEY_FN pGetKeyFn,
void* pvGetKeyArgument,
PCredHandle phCredential,
1466 BOOL kerberos = FALSE;
1470 if (!negotiate_get_config(pAuthData, &kerberos, &ntlm, &u2u))
1471 return SEC_E_INTERNAL_ERROR;
1473 MechCred* creds = calloc(MECH_COUNT,
sizeof(MechCred));
1476 return SEC_E_INTERNAL_ERROR;
1478 for (
size_t i = 0; i < MECH_COUNT; i++)
1480 const SecPkg* pkg = MechTable[i].pkg;
1481 MechCred* cred = &creds[i];
1483 cred->mech = &MechTable[i];
1485 if (!kerberos && sspi_gss_oid_compare(MechTable[i].oid, &kerberos_OID))
1487 if (!u2u && sspi_gss_oid_compare(MechTable[i].oid, &kerberos_u2u_OID))
1489 if (!ntlm && _tcsncmp(SecPkgTable[i].name, NTLM_SSP_NAME, ARRAYSIZE(NTLM_SSP_NAME)) == 0)
1492 WINPR_ASSERT(pkg->table);
1493 WINPR_ASSERT(pkg->table->AcquireCredentialsHandleA);
1494 if (pkg->table->AcquireCredentialsHandleA(pszPrincipal, pszPackage, fCredentialUse,
1495 pvLogonID, pAuthData, pGetKeyFn, pvGetKeyArgument,
1496 &cred->cred, ptsExpiry) != SEC_E_OK)
1502 sspi_SecureHandleSetLowerPointer(phCredential, (
void*)creds);
1503 sspi_SecureHandleSetUpperPointer(phCredential, (
void*)NEGO_SSP_NAME);
1507static SECURITY_STATUS SEC_ENTRY negotiate_QueryCredentialsAttributesW(
1508 WINPR_ATTR_UNUSED
PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
1509 WINPR_ATTR_UNUSED
void* pBuffer)
1511 WLog_ERR(TAG,
"TODO: Implement");
1512 return SEC_E_UNSUPPORTED_FUNCTION;
1515static SECURITY_STATUS SEC_ENTRY negotiate_QueryCredentialsAttributesA(
1516 WINPR_ATTR_UNUSED
PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
1517 WINPR_ATTR_UNUSED
void* pBuffer)
1519 WLog_ERR(TAG,
"TODO: Implement");
1520 return SEC_E_UNSUPPORTED_FUNCTION;
1523static SECURITY_STATUS SEC_ENTRY negotiate_FreeCredentialsHandle(
PCredHandle phCredential)
1525 MechCred* creds = NULL;
1527 creds = sspi_SecureHandleGetLowerPointer(phCredential);
1529 return SEC_E_INVALID_HANDLE;
1531 for (
size_t i = 0; i < MECH_COUNT; i++)
1533 MechCred* cred = &creds[i];
1535 WINPR_ASSERT(cred->mech);
1536 WINPR_ASSERT(cred->mech->pkg);
1537 WINPR_ASSERT(cred->mech->pkg->table);
1538 WINPR_ASSERT(cred->mech->pkg->table->FreeCredentialsHandle);
1539 cred->mech->pkg->table->FreeCredentialsHandle(&cred->cred);
1546static SECURITY_STATUS SEC_ENTRY negotiate_EncryptMessage(
PCtxtHandle phContext, ULONG fQOP,
1553 return SEC_E_INVALID_HANDLE;
1558 WINPR_ASSERT(context->mech);
1559 WINPR_ASSERT(context->mech->pkg);
1560 WINPR_ASSERT(context->mech->pkg->table);
1561 if (context->mech->pkg->table->EncryptMessage)
1562 return context->mech->pkg->table->EncryptMessage(&context->sub_context, fQOP, pMessage,
1565 return SEC_E_UNSUPPORTED_FUNCTION;
1568static SECURITY_STATUS SEC_ENTRY negotiate_DecryptMessage(
PCtxtHandle phContext,
1570 ULONG MessageSeqNo, ULONG* pfQOP)
1575 return SEC_E_INVALID_HANDLE;
1580 WINPR_ASSERT(context->mech);
1581 WINPR_ASSERT(context->mech->pkg);
1582 WINPR_ASSERT(context->mech->pkg->table);
1583 if (context->mech->pkg->table->DecryptMessage)
1584 return context->mech->pkg->table->DecryptMessage(&context->sub_context, pMessage,
1585 MessageSeqNo, pfQOP);
1587 return SEC_E_UNSUPPORTED_FUNCTION;
1590static SECURITY_STATUS SEC_ENTRY negotiate_MakeSignature(
PCtxtHandle phContext, ULONG fQOP,
1597 return SEC_E_INVALID_HANDLE;
1602 WINPR_ASSERT(context->mech);
1603 WINPR_ASSERT(context->mech->pkg);
1604 WINPR_ASSERT(context->mech->pkg->table);
1605 if (context->mech->pkg->table->MakeSignature)
1606 return context->mech->pkg->table->MakeSignature(&context->sub_context, fQOP, pMessage,
1609 return SEC_E_UNSUPPORTED_FUNCTION;
1612static SECURITY_STATUS SEC_ENTRY negotiate_VerifySignature(
PCtxtHandle phContext,
1614 ULONG MessageSeqNo, ULONG* pfQOP)
1619 return SEC_E_INVALID_HANDLE;
1624 WINPR_ASSERT(context->mech);
1625 WINPR_ASSERT(context->mech->pkg);
1626 WINPR_ASSERT(context->mech->pkg->table);
1627 if (context->mech->pkg->table->VerifySignature)
1628 return context->mech->pkg->table->VerifySignature(&context->sub_context, pMessage,
1629 MessageSeqNo, pfQOP);
1631 return SEC_E_UNSUPPORTED_FUNCTION;
1637 negotiate_QueryCredentialsAttributesA,
1638 negotiate_AcquireCredentialsHandleA,
1639 negotiate_FreeCredentialsHandle,
1641 negotiate_InitializeSecurityContextA,
1642 negotiate_AcceptSecurityContext,
1643 negotiate_CompleteAuthToken,
1644 negotiate_DeleteSecurityContext,
1646 negotiate_QueryContextAttributesA,
1647 negotiate_ImpersonateSecurityContext,
1648 negotiate_RevertSecurityContext,
1649 negotiate_MakeSignature,
1650 negotiate_VerifySignature,
1660 negotiate_EncryptMessage,
1661 negotiate_DecryptMessage,
1662 negotiate_SetContextAttributesA,
1663 negotiate_SetCredentialsAttributesA,
1669 negotiate_QueryCredentialsAttributesW,
1670 negotiate_AcquireCredentialsHandleW,
1671 negotiate_FreeCredentialsHandle,
1673 negotiate_InitializeSecurityContextW,
1674 negotiate_AcceptSecurityContext,
1675 negotiate_CompleteAuthToken,
1676 negotiate_DeleteSecurityContext,
1678 negotiate_QueryContextAttributesW,
1679 negotiate_ImpersonateSecurityContext,
1680 negotiate_RevertSecurityContext,
1681 negotiate_MakeSignature,
1682 negotiate_VerifySignature,
1692 negotiate_EncryptMessage,
1693 negotiate_DecryptMessage,
1694 negotiate_SetContextAttributesW,
1695 negotiate_SetCredentialsAttributesW,
1698BOOL NEGOTIATE_init(
void)
1700 InitializeConstWCharFromUtf8(NEGOTIATE_SecPkgInfoA.Name, NEGOTIATE_SecPkgInfoW_NameBuffer,
1701 ARRAYSIZE(NEGOTIATE_SecPkgInfoW_NameBuffer));
1702 InitializeConstWCharFromUtf8(NEGOTIATE_SecPkgInfoA.Comment, NEGOTIATE_SecPkgInfoW_CommentBuffer,
1703 ARRAYSIZE(NEGOTIATE_SecPkgInfoW_CommentBuffer));