21 #include <winpr/config.h>
23 #include <winpr/crt.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>
32 #include "negotiate.h"
34 #include "../NTLM/ntlm.h"
35 #include "../NTLM/ntlm_export.h"
36 #include "../Kerberos/kerberos.h"
38 #include "../../log.h"
39 #define TAG WINPR_TAG("negotiate")
41 static const char NEGO_REG_KEY[] =
42 "Software\\" WINPR_VENDOR_STRING
"\\" WINPR_PRODUCT_STRING
"\\SSPI\\Negotiate";
72 "Microsoft Package Negotiator"
75 static WCHAR NEGOTIATE_SecPkgInfoW_NameBuffer[32] = { 0 };
76 static WCHAR NEGOTIATE_SecPkgInfoW_CommentBuffer[32] = { 0 };
83 NEGOTIATE_SecPkgInfoW_NameBuffer,
84 NEGOTIATE_SecPkgInfoW_CommentBuffer
87 static 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" };
90 static 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" };
93 static const WinPrAsn1_OID ntlm_OID = { 10, (BYTE*)
"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a" };
95 static const WinPrAsn1_OID negoex_OID = { 10, (BYTE*)
"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x1e" };
98 static 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 }
104 static 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 },
110 static const SecPkg SecPkgTable[] = { { NTLM_SSP_NAME, &NTLM_SecurityFunctionTableA,
111 &NTLM_SecurityFunctionTableW } };
113 static const Mech MechTable[] = {
114 { &ntlm_OID, &SecPkgTable[0], 0, FALSE },
118 static const size_t MECH_COUNT =
sizeof(MechTable) /
sizeof(Mech);
131 enum NegState negState;
139 static 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);
176 static 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";
194 static 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];
214 static 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)
236 static 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)
252 static BOOL negotiate_get_config_from_auth_package_list(
void* pAuthData, BOOL* kerberos, BOOL* ntlm)
254 char* tok_ctx = NULL;
255 char* tok_ptr = NULL;
256 char* PackageList = NULL;
261 tok_ptr = strtok_s(PackageList,
",", &tok_ctx);
265 char* PackageName = tok_ptr;
266 BOOL PackageInclude = TRUE;
268 if (PackageName[0] ==
'!')
270 PackageName = &PackageName[1];
271 PackageInclude = FALSE;
274 if (!_stricmp(PackageName,
"ntlm"))
276 *ntlm = PackageInclude;
278 else if (!_stricmp(PackageName,
"kerberos"))
280 *kerberos = PackageInclude;
284 WLog_WARN(TAG,
"Unknown authentication package name: %s", PackageName);
287 tok_ptr = strtok_s(NULL,
",", &tok_ctx);
294 static BOOL negotiate_get_config(
void* pAuthData, BOOL* kerberos, BOOL* ntlm)
299 WINPR_ASSERT(kerberos);
302 #if !defined(WITH_KRB5_NO_NTLM_FALLBACK)
309 if (negotiate_get_config_from_auth_package_list(pAuthData, kerberos, ntlm))
314 rc = RegOpenKeyExA(HKEY_LOCAL_MACHINE, NEGO_REG_KEY, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
315 if (rc == ERROR_SUCCESS)
319 if (negotiate_get_dword(hKey,
"kerberos", &dwValue))
320 *kerberos = (dwValue != 0) ? TRUE : FALSE;
322 #if !defined(WITH_KRB5_NO_NTLM_FALLBACK)
323 if (negotiate_get_dword(hKey,
"ntlm", &dwValue))
324 *ntlm = (dwValue != 0) ? TRUE : FALSE;
333 static BOOL negotiate_write_neg_token(
PSecBuffer output_buffer, NegToken* token)
335 WINPR_ASSERT(output_buffer);
339 WinPrAsn1Encoder* enc = NULL;
346 enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER);
354 if (!WinPrAsn1EncAppContainer(enc, 0))
358 if (!WinPrAsn1EncOID(enc, &spnego_OID))
363 if (!WinPrAsn1EncContextualSeqContainer(enc, token->init ? 0 : 1))
366 WLog_DBG(TAG, token->init ?
"Writing negTokenInit..." :
"Writing negTokenResp...");
371 if (!WinPrAsn1EncContextualRawContent(enc, 0, &mechTypes))
373 WLog_DBG(TAG,
"\tmechTypes [0] (%li bytes)", token->mechTypes.cbBuffer);
376 else if (token->negState != NOSTATE)
378 if (!WinPrAsn1EncContextualEnumerated(enc, 0, token->negState))
380 WLog_DBG(TAG,
"\tnegState [0] (%d)", token->negState);
384 if (token->supportedMech.len)
386 if (!WinPrAsn1EncContextualOID(enc, 1, &token->supportedMech))
388 WLog_DBG(TAG,
"\tsupportedMech [1] (%s)", negotiate_mech_name(&token->supportedMech));
392 if (token->mechToken.cbBuffer)
394 if (WinPrAsn1EncContextualOctetString(enc, 2, &mechToken) == 0)
396 WLog_DBG(TAG,
"\tmechToken [2] (%li bytes)", token->mechToken.cbBuffer);
400 if (token->mic.cbBuffer)
402 if (WinPrAsn1EncContextualOctetString(enc, 3, &mechListMic) == 0)
404 WLog_DBG(TAG,
"\tmechListMIC [3] (%li bytes)", token->mic.cbBuffer);
408 if (!WinPrAsn1EncEndContainer(enc))
414 if (!WinPrAsn1EncEndContainer(enc))
418 if (!WinPrAsn1EncStreamSize(enc, &len) || len > output_buffer->cbBuffer)
421 if (len > UINT32_MAX)
424 Stream_StaticInit(&s, output_buffer->pvBuffer, len);
426 if (WinPrAsn1EncToStream(enc, &s))
428 output_buffer->cbBuffer = (UINT32)len;
433 WinPrAsn1Encoder_Free(&enc);
437 static BOOL negotiate_read_neg_token(
PSecBuffer input, NegToken* token)
442 WinPrAsn1_tagId contextual = 0;
443 WinPrAsn1_tag tag = 0;
451 WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, input->pvBuffer, input->cbBuffer);
453 if (!WinPrAsn1DecPeekTag(&dec, &tag))
459 if (!WinPrAsn1DecReadApp(&dec, &tag, &dec2) || tag != 0)
464 if (!WinPrAsn1DecReadOID(&dec, &oid, FALSE))
467 if (!sspi_gss_oid_compare(&spnego_OID, &oid))
471 if (!WinPrAsn1DecReadContextualSequence(&dec, 0, &err, &dec2))
477 else if (!WinPrAsn1DecReadContextualSequence(&dec, 1, &err, &dec2))
481 WLog_DBG(TAG, token->init ?
"Reading negTokenInit..." :
"Reading negTokenResp...");
486 if (!WinPrAsn1DecReadContextualTag(&dec, &contextual, &dec2))
495 wStream s = WinPrAsn1DecGetStream(&dec2);
496 token->mechTypes.BufferType = SECBUFFER_TOKEN;
497 const size_t mlen = Stream_Length(&s);
498 if (mlen > UINT32_MAX)
500 token->mechTypes.cbBuffer = (UINT32)mlen;
501 token->mechTypes.pvBuffer = Stream_Buffer(&s);
502 WLog_DBG(TAG,
"\tmechTypes [0] (%li bytes)", token->mechTypes.cbBuffer);
507 WinPrAsn1_ENUMERATED rd = 0;
508 if (!WinPrAsn1DecReadEnumerated(&dec2, &rd))
510 token->negState = rd;
511 WLog_DBG(TAG,
"\tnegState [0] (%d)", token->negState);
518 if (!WinPrAsn1DecPeekTagAndLen(&dec2, &tag, &len) || (tag != ER_TAG_BIT_STRING))
520 WLog_DBG(TAG,
"\treqFlags [1] (%li bytes)", len);
525 if (!WinPrAsn1DecReadOID(&dec2, &token->supportedMech, FALSE))
527 WLog_DBG(TAG,
"\tsupportedMech [1] (%s)",
528 negotiate_mech_name(&token->supportedMech));
533 if (!WinPrAsn1DecReadOctetString(&dec2, &octet_string, FALSE))
535 if (octet_string.len > UINT32_MAX)
537 token->mechToken.cbBuffer = (UINT32)octet_string.len;
538 token->mechToken.pvBuffer = octet_string.data;
539 token->mechToken.BufferType = SECBUFFER_TOKEN;
540 WLog_DBG(TAG,
"\tmechToken [2] (%li bytes)", octet_string.len);
544 if (!WinPrAsn1DecReadOctetString(&dec2, &octet_string, FALSE))
546 if (octet_string.len > UINT32_MAX)
548 token->mic.cbBuffer = (UINT32)octet_string.len;
549 token->mic.pvBuffer = octet_string.data;
550 token->mic.BufferType = SECBUFFER_TOKEN;
551 WLog_DBG(TAG,
"\tmechListMIC [3] (%li bytes)", octet_string.len);
554 WLog_ERR(TAG,
"unknown contextual item %d", contextual);
557 }
while (WinPrAsn1DecPeekTag(&dec, &tag));
562 static SECURITY_STATUS negotiate_mic_exchange(
NEGOTIATE_CONTEXT* context, NegToken* input_token,
563 NegToken* output_token,
PSecBuffer output_buffer)
566 SecBufferDesc mic_buffer_desc = { SECBUFFER_VERSION, 2, mic_buffers };
567 SECURITY_STATUS status = 0;
569 WINPR_ASSERT(context);
570 WINPR_ASSERT(input_token);
571 WINPR_ASSERT(output_token);
572 WINPR_ASSERT(context->mech);
573 WINPR_ASSERT(context->mech->pkg);
578 mic_buffers[0] = context->mechTypes;
581 if (input_token->mic.cbBuffer > 0)
583 mic_buffers[1] = input_token->mic;
585 status = table->VerifySignature(&context->sub_context, &mic_buffer_desc, 0, 0);
586 if (status != SEC_E_OK)
589 output_token->negState = ACCEPT_COMPLETED;
593 if (input_token->negState != ACCEPT_COMPLETED)
596 output_token->mic.BufferType = SECBUFFER_TOKEN;
599 output_token->mic.cbBuffer = output_buffer->cbBuffer - output_token->mechToken.cbBuffer;
600 output_token->mic.pvBuffer =
601 (BYTE*)output_buffer->pvBuffer + output_token->mechToken.cbBuffer;
603 mic_buffers[1] = output_token->mic;
605 status = table->MakeSignature(&context->sub_context, 0, &mic_buffer_desc, 0);
606 if (status != SEC_E_OK)
609 output_token->mic = mic_buffers[1];
613 const TCHAR* name = sspi_SecureHandleGetUpperPointer(&context->sub_context);
615 return SEC_E_INTERNAL_ERROR;
617 if (_tcsncmp(name, NTLM_SSP_NAME, ARRAYSIZE(NTLM_SSP_NAME)) == 0)
619 if (!ntlm_reset_cipher_state(&context->sub_context))
620 return SEC_E_INTERNAL_ERROR;
626 static SECURITY_STATUS SEC_ENTRY negotiate_InitializeSecurityContextW(
628 ULONG Reserved1, ULONG TargetDataRep,
PSecBufferDesc pInput, ULONG Reserved2,
633 MechCred* creds = NULL;
636 NegToken input_token = empty_neg_token;
637 NegToken output_token = empty_neg_token;
642 SecBufferDesc mech_input = { SECBUFFER_VERSION, 2, mech_input_buffers };
643 SecBufferDesc mech_output = { SECBUFFER_VERSION, 1, &output_token.mechToken };
644 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
645 SECURITY_STATUS sub_status = SEC_E_INTERNAL_ERROR;
646 WinPrAsn1Encoder* enc = NULL;
648 const Mech* mech = NULL;
650 if (!phCredential || !SecIsValidHandle(phCredential))
651 return SEC_E_NO_CREDENTIALS;
653 creds = sspi_SecureHandleGetLowerPointer(phCredential);
656 if (phContext && !phContext->dwLower && !phContext->dwUpper)
657 return SEC_E_INVALID_HANDLE;
659 context = sspi_SecureHandleGetLowerPointer(phContext);
663 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
664 bindings_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_CHANNEL_BINDINGS);
667 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
671 enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER);
673 return SEC_E_INSUFFICIENT_MEMORY;
675 if (!WinPrAsn1EncSeqContainer(enc))
678 for (
size_t i = 0; i < MECH_COUNT; i++)
680 MechCred* cred = &creds[i];
681 const SecPkg* pkg = MechTable[i].pkg;
683 WINPR_ASSERT(pkg->table_w);
689 if (!init_context.mech)
695 CopyMemory(&output_token.mechToken, output_buffer,
sizeof(
SecBuffer));
698 mech_input_buffers[0] = *bindings_buffer;
700 WINPR_ASSERT(pkg->table_w->InitializeSecurityContextW);
701 sub_status = pkg->table_w->InitializeSecurityContextW(
702 &cred->cred, NULL, pszTargetName, fContextReq | cred->mech->flags, Reserved1,
703 TargetDataRep, &mech_input, Reserved2, &init_context.sub_context, &mech_output,
704 pfContextAttr, ptsExpiry);
707 if (IsSecurityStatusError(sub_status))
709 if (SecIsValidHandle(&init_context.sub_context))
711 WINPR_ASSERT(pkg->table_w->DeleteSecurityContext);
712 pkg->table_w->DeleteSecurityContext(&init_context.sub_context);
718 init_context.mech = cred->mech;
721 if (!WinPrAsn1EncOID(enc, cred->mech->oid))
723 WLog_DBG(TAG,
"Available mechanism: %s", negotiate_mech_name(cred->mech->oid));
727 if (!init_context.mech)
731 if (init_context.mech->oid == &ntlm_OID)
733 init_context.spnego = FALSE;
734 output_buffer->cbBuffer = output_token.mechToken.cbBuffer;
735 WLog_DBG(TAG,
"Using direct NTLM");
739 init_context.spnego = TRUE;
740 init_context.mechTypes.BufferType = SECBUFFER_DATA;
741 const size_t cb = WinPrAsn1EncEndContainer(enc);
742 WINPR_ASSERT(cb <= UINT32_MAX);
743 init_context.mechTypes.cbBuffer = (UINT32)cb;
747 context = negotiate_ContextNew(&init_context);
750 init_context.mech->pkg->table->DeleteSecurityContext(&init_context.sub_context);
751 WinPrAsn1Encoder_Free(&enc);
752 return SEC_E_INSUFFICIENT_MEMORY;
755 sspi_SecureHandleSetUpperPointer(phNewContext, NEGO_SSP_NAME);
756 sspi_SecureHandleSetLowerPointer(phNewContext, context);
758 if (!context->spnego)
765 Stream_StaticInit(&s, context->mechTypes.pvBuffer, context->mechTypes.cbBuffer);
766 if (!WinPrAsn1EncToStream(enc, &s))
769 output_token.mechTypes.cbBuffer = context->mechTypes.cbBuffer;
770 output_token.mechTypes.pvBuffer = context->mechTypes.pvBuffer;
771 output_token.init = TRUE;
773 if (sub_status == SEC_E_OK)
774 context->state = NEGOTIATE_STATE_FINAL_OPTIMISTIC;
779 return SEC_E_INVALID_TOKEN;
781 sub_context = &context->sub_context;
782 sub_cred = negotiate_FindCredential(creds, context->mech);
784 if (!context->spnego)
786 return context->mech->pkg->table_w->InitializeSecurityContextW(
787 sub_cred, sub_context, pszTargetName, fContextReq | context->mech->flags, Reserved1,
788 TargetDataRep, pInput, Reserved2, sub_context, pOutput, pfContextAttr, ptsExpiry);
791 if (!negotiate_read_neg_token(input_buffer, &input_token))
792 return SEC_E_INVALID_TOKEN;
795 if (context->state < NEGOTIATE_STATE_NEGORESP && input_token.supportedMech.len &&
796 !sspi_gss_oid_compare(&input_token.supportedMech, context->mech->oid))
798 mech = negotiate_GetMechByOID(&input_token.supportedMech);
800 return SEC_E_INVALID_TOKEN;
803 sub_cred = negotiate_FindCredential(creds, mech);
805 return SEC_E_INVALID_TOKEN;
808 context->mech->pkg->table_w->DeleteSecurityContext(&context->sub_context);
811 context->mech = mech;
816 if (context->state < NEGOTIATE_STATE_NEGORESP)
818 switch (input_token.negState)
821 return SEC_E_INVALID_TOKEN;
823 return SEC_E_LOGON_DENIED;
828 case ACCEPT_INCOMPLETE:
829 context->state = NEGOTIATE_STATE_NEGORESP;
831 case ACCEPT_COMPLETED:
832 if (context->state == NEGOTIATE_STATE_INITIAL)
833 context->state = NEGOTIATE_STATE_NEGORESP;
835 context->state = NEGOTIATE_STATE_FINAL;
841 WLog_DBG(TAG,
"Negotiated mechanism: %s", negotiate_mech_name(context->mech->oid));
844 if (context->state == NEGOTIATE_STATE_NEGORESP)
849 CopyMemory(&output_token.mechToken, output_buffer,
sizeof(
SecBuffer));
851 mech_input_buffers[0] = input_token.mechToken;
853 mech_input_buffers[1] = *bindings_buffer;
855 status = context->mech->pkg->table_w->InitializeSecurityContextW(
856 sub_cred, sub_context, pszTargetName, fContextReq | context->mech->flags, Reserved1,
857 TargetDataRep, input_token.mechToken.cbBuffer ? &mech_input : NULL, Reserved2,
858 &context->sub_context, &mech_output, pfContextAttr, ptsExpiry);
860 if (IsSecurityStatusError(status))
864 if (status == SEC_E_OK)
866 if (output_token.mechToken.cbBuffer > 0)
867 context->state = NEGOTIATE_STATE_MIC;
869 context->state = NEGOTIATE_STATE_FINAL;
873 if (context->state == NEGOTIATE_STATE_FINAL && input_token.mic.cbBuffer == 0)
875 if (context->mic || input_token.negState != ACCEPT_COMPLETED)
876 return SEC_E_INVALID_TOKEN;
879 output_buffer->cbBuffer = 0;
883 if ((context->state == NEGOTIATE_STATE_MIC && context->mic) ||
884 context->state == NEGOTIATE_STATE_FINAL)
886 status = negotiate_mic_exchange(context, &input_token, &output_token, output_buffer);
887 if (status != SEC_E_OK)
892 if (input_token.negState == ACCEPT_COMPLETED)
895 output_buffer->cbBuffer = 0;
899 if (output_token.negState == ACCEPT_COMPLETED)
902 status = SEC_I_CONTINUE_NEEDED;
904 if (!negotiate_write_neg_token(output_buffer, &output_token))
905 status = SEC_E_INTERNAL_ERROR;
908 WinPrAsn1Encoder_Free(&enc);
912 static SECURITY_STATUS SEC_ENTRY negotiate_InitializeSecurityContextA(
914 ULONG Reserved1, ULONG TargetDataRep,
PSecBufferDesc pInput, ULONG Reserved2,
917 SECURITY_STATUS status = 0;
918 SEC_WCHAR* pszTargetNameW = NULL;
922 pszTargetNameW = ConvertUtf8ToWCharAlloc(pszTargetName, NULL);
924 return SEC_E_INTERNAL_ERROR;
927 status = negotiate_InitializeSecurityContextW(
928 phCredential, phContext, pszTargetNameW, fContextReq, Reserved1, TargetDataRep, pInput,
929 Reserved2, phNewContext, pOutput, pfContextAttr, ptsExpiry);
930 free(pszTargetNameW);
938 WinPrAsn1_tagId tag = 0;
939 const char ssp[] =
"NTLMSSP";
944 if (input_buffer->cbBuffer >= 8 && strncmp(input_buffer->pvBuffer, ssp,
sizeof(ssp)) == 0)
947 return negotiate_GetMechByOID(&ntlm_OID);
951 WinPrAsn1Decoder_InitMem(&decoder, WINPR_ASN1_DER, input_buffer->pvBuffer,
952 input_buffer->cbBuffer);
954 if (!WinPrAsn1DecReadApp(&decoder, &tag, &appDecoder) || tag != 0)
957 if (!WinPrAsn1DecReadOID(&appDecoder, oid, FALSE))
960 if (sspi_gss_oid_compare(oid, &spnego_OID))
966 return negotiate_GetMechByOID(oid);
969 static SECURITY_STATUS SEC_ENTRY negotiate_AcceptSecurityContext(
976 MechCred* creds = NULL;
978 NegToken input_token = empty_neg_token;
979 NegToken output_token = empty_neg_token;
982 SecBufferDesc mech_input = { SECBUFFER_VERSION, 1, &input_token.mechToken };
983 SecBufferDesc mech_output = { SECBUFFER_VERSION, 1, &output_token.mechToken };
984 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
987 WinPrAsn1_tagId tag = 0;
989 const Mech* first_mech = NULL;
991 if (!phCredential || !SecIsValidHandle(phCredential))
992 return SEC_E_NO_CREDENTIALS;
994 creds = sspi_SecureHandleGetLowerPointer(phCredential);
997 return SEC_E_INVALID_TOKEN;
1000 if (phContext && !phContext->dwLower && !phContext->dwUpper)
1001 return SEC_E_INVALID_HANDLE;
1003 context = sspi_SecureHandleGetLowerPointer(phContext);
1005 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
1007 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
1011 init_context.mech = guessMech(input_buffer, &init_context.spnego, &oid);
1012 if (!init_context.mech && !init_context.spnego)
1013 return SEC_E_INVALID_TOKEN;
1015 WLog_DBG(TAG,
"Mechanism: %s", negotiate_mech_name(&oid));
1017 if (init_context.spnego)
1020 if (!negotiate_read_neg_token(input_buffer, &input_token))
1021 return SEC_E_INVALID_TOKEN;
1024 if (!input_token.init || input_token.mechTypes.cbBuffer == 0)
1025 return SEC_E_INVALID_TOKEN;
1027 init_context.mechTypes.BufferType = SECBUFFER_DATA;
1028 init_context.mechTypes.cbBuffer = input_token.mechTypes.cbBuffer;
1031 WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, input_token.mechTypes.pvBuffer,
1032 input_token.mechTypes.cbBuffer);
1034 if (!WinPrAsn1DecReadSequence(&dec, &dec2))
1035 return SEC_E_INVALID_TOKEN;
1039 if (input_token.mechToken.cbBuffer)
1041 if (!WinPrAsn1DecReadOID(&dec, &oid, FALSE))
1042 return SEC_E_INVALID_TOKEN;
1044 init_context.mech = negotiate_GetMechByOID(&oid);
1046 if (init_context.mech)
1049 output_token.mechToken = *output_buffer;
1050 WLog_DBG(TAG,
"Requested mechanism: %s",
1051 negotiate_mech_name(init_context.mech->oid));
1056 if (init_context.mech)
1058 sub_cred = negotiate_FindCredential(creds, init_context.mech);
1060 status = init_context.mech->pkg->table->AcceptSecurityContext(
1061 sub_cred, NULL, init_context.spnego ? &mech_input : pInput, fContextReq,
1062 TargetDataRep, &init_context.sub_context,
1063 init_context.spnego ? &mech_output : pOutput, pfContextAttr, ptsTimeStamp);
1066 if (IsSecurityStatusError(status))
1068 if (!init_context.spnego)
1071 init_context.mic = TRUE;
1072 first_mech = init_context.mech;
1073 init_context.mech = NULL;
1074 output_token.mechToken.cbBuffer = 0;
1077 while (!init_context.mech && WinPrAsn1DecPeekTag(&dec, &tag))
1080 if (!WinPrAsn1DecReadOID(&dec, &oid, FALSE))
1081 return SEC_E_INVALID_TOKEN;
1083 init_context.mech = negotiate_GetMechByOID(&oid);
1084 WLog_DBG(TAG,
"Requested mechanism: %s", negotiate_mech_name(&oid));
1087 if (init_context.mech == first_mech)
1088 init_context.mech = NULL;
1090 if (init_context.mech && !negotiate_FindCredential(creds, init_context.mech))
1091 init_context.mech = NULL;
1094 if (!init_context.mech)
1095 return SEC_E_INTERNAL_ERROR;
1097 context = negotiate_ContextNew(&init_context);
1100 if (!IsSecurityStatusError(status))
1101 init_context.mech->pkg->table->DeleteSecurityContext(&init_context.sub_context);
1102 return SEC_E_INSUFFICIENT_MEMORY;
1105 sspi_SecureHandleSetUpperPointer(phNewContext, NEGO_SSP_NAME);
1106 sspi_SecureHandleSetLowerPointer(phNewContext, context);
1108 if (!init_context.spnego)
1111 CopyMemory(init_context.mechTypes.pvBuffer, input_token.mechTypes.pvBuffer,
1112 input_token.mechTypes.cbBuffer);
1114 if (!context->mech->preferred)
1116 output_token.negState = REQUEST_MIC;
1117 context->mic = TRUE;
1121 output_token.negState = ACCEPT_INCOMPLETE;
1124 if (status == SEC_E_OK)
1125 context->state = NEGOTIATE_STATE_FINAL;
1127 context->state = NEGOTIATE_STATE_NEGORESP;
1129 output_token.supportedMech = oid;
1130 WLog_DBG(TAG,
"Accepted mechanism: %s", negotiate_mech_name(&output_token.supportedMech));
1134 sub_cred = negotiate_FindCredential(creds, context->mech);
1136 return SEC_E_NO_CREDENTIALS;
1138 if (!context->spnego)
1140 return context->mech->pkg->table->AcceptSecurityContext(
1141 sub_cred, &context->sub_context, pInput, fContextReq, TargetDataRep,
1142 &context->sub_context, pOutput, pfContextAttr, ptsTimeStamp);
1145 if (!negotiate_read_neg_token(input_buffer, &input_token))
1146 return SEC_E_INVALID_TOKEN;
1149 if (input_token.mechToken.cbBuffer > 0)
1151 if (context->state != NEGOTIATE_STATE_NEGORESP)
1152 return SEC_E_INVALID_TOKEN;
1156 CopyMemory(&output_token.mechToken, output_buffer,
sizeof(
SecBuffer));
1158 status = context->mech->pkg->table->AcceptSecurityContext(
1159 sub_cred, &context->sub_context, &mech_input, fContextReq | context->mech->flags,
1160 TargetDataRep, &context->sub_context, &mech_output, pfContextAttr, ptsTimeStamp);
1162 if (IsSecurityStatusError(status))
1165 if (status == SEC_E_OK)
1166 context->state = NEGOTIATE_STATE_FINAL;
1168 else if (context->state == NEGOTIATE_STATE_NEGORESP)
1169 return SEC_E_INVALID_TOKEN;
1172 if (context->state == NEGOTIATE_STATE_FINAL)
1175 if (context->mic && output_token.mechToken.cbBuffer == 0 && input_token.mic.cbBuffer == 0)
1176 return SEC_E_INVALID_TOKEN;
1178 if (context->mic || input_token.mic.cbBuffer > 0)
1180 status = negotiate_mic_exchange(context, &input_token, &output_token, output_buffer);
1181 if (status != SEC_E_OK)
1185 output_token.negState = ACCEPT_COMPLETED;
1188 if (input_token.negState == ACCEPT_COMPLETED)
1191 output_buffer->cbBuffer = 0;
1195 if (output_token.negState == ACCEPT_COMPLETED)
1198 status = SEC_I_CONTINUE_NEEDED;
1200 if (!negotiate_write_neg_token(output_buffer, &output_token))
1201 return SEC_E_INTERNAL_ERROR;
1206 static SECURITY_STATUS SEC_ENTRY negotiate_CompleteAuthToken(
PCtxtHandle phContext,
1210 SECURITY_STATUS status = SEC_E_OK;
1214 return SEC_E_INVALID_HANDLE;
1216 WINPR_ASSERT(context->mech);
1217 WINPR_ASSERT(context->mech->pkg);
1218 WINPR_ASSERT(context->mech->pkg->table);
1219 if (context->mech->pkg->table->CompleteAuthToken)
1220 status = context->mech->pkg->table->CompleteAuthToken(&context->sub_context, pToken);
1225 static SECURITY_STATUS SEC_ENTRY negotiate_DeleteSecurityContext(
PCtxtHandle phContext)
1228 SECURITY_STATUS status = SEC_E_OK;
1230 const SecPkg* pkg = NULL;
1233 return SEC_E_INVALID_HANDLE;
1235 WINPR_ASSERT(context->mech);
1236 WINPR_ASSERT(context->mech->pkg);
1237 WINPR_ASSERT(context->mech->pkg->table);
1238 pkg = context->mech->pkg;
1240 if (pkg->table->DeleteSecurityContext)
1241 status = pkg->table->DeleteSecurityContext(&context->sub_context);
1243 negotiate_ContextFree(context);
1247 static SECURITY_STATUS SEC_ENTRY negotiate_ImpersonateSecurityContext(
PCtxtHandle phContext)
1252 static SECURITY_STATUS SEC_ENTRY negotiate_RevertSecurityContext(
PCtxtHandle phContext)
1257 static SECURITY_STATUS SEC_ENTRY negotiate_QueryContextAttributesW(
PCtxtHandle phContext,
1258 ULONG ulAttribute,
void* pBuffer)
1263 return SEC_E_INVALID_HANDLE;
1265 WINPR_ASSERT(context->mech);
1266 WINPR_ASSERT(context->mech->pkg);
1267 WINPR_ASSERT(context->mech->pkg->table_w);
1268 if (context->mech->pkg->table_w->QueryContextAttributesW)
1269 return context->mech->pkg->table_w->QueryContextAttributesW(&context->sub_context,
1270 ulAttribute, pBuffer);
1272 return SEC_E_UNSUPPORTED_FUNCTION;
1275 static SECURITY_STATUS SEC_ENTRY negotiate_QueryContextAttributesA(
PCtxtHandle phContext,
1276 ULONG ulAttribute,
void* pBuffer)
1281 return SEC_E_INVALID_HANDLE;
1283 WINPR_ASSERT(context->mech);
1284 WINPR_ASSERT(context->mech->pkg);
1285 WINPR_ASSERT(context->mech->pkg->table);
1286 if (context->mech->pkg->table->QueryContextAttributesA)
1287 return context->mech->pkg->table->QueryContextAttributesA(&context->sub_context,
1288 ulAttribute, pBuffer);
1290 return SEC_E_UNSUPPORTED_FUNCTION;
1293 static SECURITY_STATUS SEC_ENTRY negotiate_SetContextAttributesW(
PCtxtHandle phContext,
1294 ULONG ulAttribute,
void* pBuffer,
1300 return SEC_E_INVALID_HANDLE;
1302 WINPR_ASSERT(context->mech);
1303 WINPR_ASSERT(context->mech->pkg);
1304 WINPR_ASSERT(context->mech->pkg->table_w);
1305 if (context->mech->pkg->table_w->SetContextAttributesW)
1306 return context->mech->pkg->table_w->SetContextAttributesW(&context->sub_context,
1307 ulAttribute, pBuffer, cbBuffer);
1309 return SEC_E_UNSUPPORTED_FUNCTION;
1312 static SECURITY_STATUS SEC_ENTRY negotiate_SetContextAttributesA(
PCtxtHandle phContext,
1313 ULONG ulAttribute,
void* pBuffer,
1319 return SEC_E_INVALID_HANDLE;
1321 WINPR_ASSERT(context->mech);
1322 WINPR_ASSERT(context->mech->pkg);
1323 WINPR_ASSERT(context->mech->pkg->table);
1324 if (context->mech->pkg->table->SetContextAttributesA)
1325 return context->mech->pkg->table->SetContextAttributesA(&context->sub_context, ulAttribute,
1328 return SEC_E_UNSUPPORTED_FUNCTION;
1331 static SECURITY_STATUS SEC_ENTRY negotiate_SetCredentialsAttributesW(
PCredHandle phCredential,
1333 void* pBuffer, ULONG cbBuffer)
1335 MechCred* creds = NULL;
1336 BOOL success = FALSE;
1337 SECURITY_STATUS secStatus = 0;
1339 creds = sspi_SecureHandleGetLowerPointer(phCredential);
1342 return SEC_E_INVALID_HANDLE;
1344 for (
size_t i = 0; i < MECH_COUNT; i++)
1346 MechCred* cred = &creds[i];
1348 WINPR_ASSERT(cred->mech);
1349 WINPR_ASSERT(cred->mech->pkg);
1350 WINPR_ASSERT(cred->mech->pkg->table);
1351 WINPR_ASSERT(cred->mech->pkg->table_w->SetCredentialsAttributesW);
1352 secStatus = cred->mech->pkg->table_w->SetCredentialsAttributesW(&cred->cred, ulAttribute,
1355 if (secStatus == SEC_E_OK)
1362 return (success ? SEC_E_OK : SEC_E_UNSUPPORTED_FUNCTION);
1365 static SECURITY_STATUS SEC_ENTRY negotiate_SetCredentialsAttributesA(
PCredHandle phCredential,
1367 void* pBuffer, ULONG cbBuffer)
1369 MechCred* creds = NULL;
1370 BOOL success = FALSE;
1371 SECURITY_STATUS secStatus = 0;
1373 creds = sspi_SecureHandleGetLowerPointer(phCredential);
1376 return SEC_E_INVALID_HANDLE;
1378 for (
size_t i = 0; i < MECH_COUNT; i++)
1380 MechCred* cred = &creds[i];
1385 WINPR_ASSERT(cred->mech);
1386 WINPR_ASSERT(cred->mech->pkg);
1387 WINPR_ASSERT(cred->mech->pkg->table);
1388 WINPR_ASSERT(cred->mech->pkg->table->SetCredentialsAttributesA);
1389 secStatus = cred->mech->pkg->table->SetCredentialsAttributesA(&cred->cred, ulAttribute,
1392 if (secStatus == SEC_E_OK)
1399 return (success ? SEC_E_OK : SEC_E_UNSUPPORTED_FUNCTION);
1402 static SECURITY_STATUS SEC_ENTRY negotiate_AcquireCredentialsHandleW(
1403 SEC_WCHAR* pszPrincipal, SEC_WCHAR* pszPackage, ULONG fCredentialUse,
void* pvLogonID,
1404 void* pAuthData, SEC_GET_KEY_FN pGetKeyFn,
void* pvGetKeyArgument,
PCredHandle phCredential,
1410 if (!negotiate_get_config(pAuthData, &kerberos, &ntlm))
1411 return SEC_E_INTERNAL_ERROR;
1413 MechCred* creds = calloc(MECH_COUNT,
sizeof(MechCred));
1416 return SEC_E_INTERNAL_ERROR;
1418 for (
size_t i = 0; i < MECH_COUNT; i++)
1420 MechCred* cred = &creds[i];
1421 const SecPkg* pkg = MechTable[i].pkg;
1422 cred->mech = &MechTable[i];
1424 if (!kerberos && _tcsncmp(pkg->name, KERBEROS_SSP_NAME, ARRAYSIZE(KERBEROS_SSP_NAME)) == 0)
1426 if (!ntlm && _tcsncmp(SecPkgTable[i].name, NTLM_SSP_NAME, ARRAYSIZE(NTLM_SSP_NAME)) == 0)
1429 WINPR_ASSERT(pkg->table_w);
1430 WINPR_ASSERT(pkg->table_w->AcquireCredentialsHandleW);
1431 if (pkg->table_w->AcquireCredentialsHandleW(
1432 pszPrincipal, pszPackage, fCredentialUse, pvLogonID, pAuthData, pGetKeyFn,
1433 pvGetKeyArgument, &cred->cred, ptsExpiry) != SEC_E_OK)
1439 sspi_SecureHandleSetLowerPointer(phCredential, (
void*)creds);
1440 sspi_SecureHandleSetUpperPointer(phCredential, (
void*)NEGO_SSP_NAME);
1444 static SECURITY_STATUS SEC_ENTRY negotiate_AcquireCredentialsHandleA(
1445 SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, ULONG fCredentialUse,
void* pvLogonID,
1446 void* pAuthData, SEC_GET_KEY_FN pGetKeyFn,
void* pvGetKeyArgument,
PCredHandle phCredential,
1452 if (!negotiate_get_config(pAuthData, &kerberos, &ntlm))
1453 return SEC_E_INTERNAL_ERROR;
1455 MechCred* creds = calloc(MECH_COUNT,
sizeof(MechCred));
1458 return SEC_E_INTERNAL_ERROR;
1460 for (
size_t i = 0; i < MECH_COUNT; i++)
1462 const SecPkg* pkg = MechTable[i].pkg;
1463 MechCred* cred = &creds[i];
1465 cred->mech = &MechTable[i];
1467 if (!kerberos && _tcsncmp(pkg->name, KERBEROS_SSP_NAME, ARRAYSIZE(KERBEROS_SSP_NAME)) == 0)
1469 if (!ntlm && _tcsncmp(SecPkgTable[i].name, NTLM_SSP_NAME, ARRAYSIZE(NTLM_SSP_NAME)) == 0)
1472 WINPR_ASSERT(pkg->table);
1473 WINPR_ASSERT(pkg->table->AcquireCredentialsHandleA);
1474 if (pkg->table->AcquireCredentialsHandleA(pszPrincipal, pszPackage, fCredentialUse,
1475 pvLogonID, pAuthData, pGetKeyFn, pvGetKeyArgument,
1476 &cred->cred, ptsExpiry) != SEC_E_OK)
1482 sspi_SecureHandleSetLowerPointer(phCredential, (
void*)creds);
1483 sspi_SecureHandleSetUpperPointer(phCredential, (
void*)NEGO_SSP_NAME);
1487 static SECURITY_STATUS SEC_ENTRY negotiate_QueryCredentialsAttributesW(
PCredHandle phCredential,
1491 WLog_ERR(TAG,
"TODO: Implement");
1492 return SEC_E_UNSUPPORTED_FUNCTION;
1495 static SECURITY_STATUS SEC_ENTRY negotiate_QueryCredentialsAttributesA(
PCredHandle phCredential,
1499 WLog_ERR(TAG,
"TODO: Implement");
1500 return SEC_E_UNSUPPORTED_FUNCTION;
1503 static SECURITY_STATUS SEC_ENTRY negotiate_FreeCredentialsHandle(
PCredHandle phCredential)
1505 MechCred* creds = NULL;
1507 creds = sspi_SecureHandleGetLowerPointer(phCredential);
1509 return SEC_E_INVALID_HANDLE;
1511 for (
size_t i = 0; i < MECH_COUNT; i++)
1513 MechCred* cred = &creds[i];
1515 WINPR_ASSERT(cred->mech);
1516 WINPR_ASSERT(cred->mech->pkg);
1517 WINPR_ASSERT(cred->mech->pkg->table);
1518 WINPR_ASSERT(cred->mech->pkg->table->FreeCredentialsHandle);
1519 cred->mech->pkg->table->FreeCredentialsHandle(&cred->cred);
1526 static SECURITY_STATUS SEC_ENTRY negotiate_EncryptMessage(
PCtxtHandle phContext, ULONG fQOP,
1533 return SEC_E_INVALID_HANDLE;
1538 WINPR_ASSERT(context->mech);
1539 WINPR_ASSERT(context->mech->pkg);
1540 WINPR_ASSERT(context->mech->pkg->table);
1541 if (context->mech->pkg->table->EncryptMessage)
1542 return context->mech->pkg->table->EncryptMessage(&context->sub_context, fQOP, pMessage,
1545 return SEC_E_UNSUPPORTED_FUNCTION;
1548 static SECURITY_STATUS SEC_ENTRY negotiate_DecryptMessage(
PCtxtHandle phContext,
1550 ULONG MessageSeqNo, ULONG* pfQOP)
1555 return SEC_E_INVALID_HANDLE;
1560 WINPR_ASSERT(context->mech);
1561 WINPR_ASSERT(context->mech->pkg);
1562 WINPR_ASSERT(context->mech->pkg->table);
1563 if (context->mech->pkg->table->DecryptMessage)
1564 return context->mech->pkg->table->DecryptMessage(&context->sub_context, pMessage,
1565 MessageSeqNo, pfQOP);
1567 return SEC_E_UNSUPPORTED_FUNCTION;
1570 static SECURITY_STATUS SEC_ENTRY negotiate_MakeSignature(
PCtxtHandle phContext, ULONG fQOP,
1577 return SEC_E_INVALID_HANDLE;
1582 WINPR_ASSERT(context->mech);
1583 WINPR_ASSERT(context->mech->pkg);
1584 WINPR_ASSERT(context->mech->pkg->table);
1585 if (context->mech->pkg->table->MakeSignature)
1586 return context->mech->pkg->table->MakeSignature(&context->sub_context, fQOP, pMessage,
1589 return SEC_E_UNSUPPORTED_FUNCTION;
1592 static SECURITY_STATUS SEC_ENTRY negotiate_VerifySignature(
PCtxtHandle phContext,
1594 ULONG MessageSeqNo, ULONG* pfQOP)
1599 return SEC_E_INVALID_HANDLE;
1604 WINPR_ASSERT(context->mech);
1605 WINPR_ASSERT(context->mech->pkg);
1606 WINPR_ASSERT(context->mech->pkg->table);
1607 if (context->mech->pkg->table->VerifySignature)
1608 return context->mech->pkg->table->VerifySignature(&context->sub_context, pMessage,
1609 MessageSeqNo, pfQOP);
1611 return SEC_E_UNSUPPORTED_FUNCTION;
1617 negotiate_QueryCredentialsAttributesA,
1618 negotiate_AcquireCredentialsHandleA,
1619 negotiate_FreeCredentialsHandle,
1621 negotiate_InitializeSecurityContextA,
1622 negotiate_AcceptSecurityContext,
1623 negotiate_CompleteAuthToken,
1624 negotiate_DeleteSecurityContext,
1626 negotiate_QueryContextAttributesA,
1627 negotiate_ImpersonateSecurityContext,
1628 negotiate_RevertSecurityContext,
1629 negotiate_MakeSignature,
1630 negotiate_VerifySignature,
1640 negotiate_EncryptMessage,
1641 negotiate_DecryptMessage,
1642 negotiate_SetContextAttributesA,
1643 negotiate_SetCredentialsAttributesA,
1649 negotiate_QueryCredentialsAttributesW,
1650 negotiate_AcquireCredentialsHandleW,
1651 negotiate_FreeCredentialsHandle,
1653 negotiate_InitializeSecurityContextW,
1654 negotiate_AcceptSecurityContext,
1655 negotiate_CompleteAuthToken,
1656 negotiate_DeleteSecurityContext,
1658 negotiate_QueryContextAttributesW,
1659 negotiate_ImpersonateSecurityContext,
1660 negotiate_RevertSecurityContext,
1661 negotiate_MakeSignature,
1662 negotiate_VerifySignature,
1672 negotiate_EncryptMessage,
1673 negotiate_DecryptMessage,
1674 negotiate_SetContextAttributesW,
1675 negotiate_SetCredentialsAttributesW,
1678 BOOL NEGOTIATE_init(
void)
1680 InitializeConstWCharFromUtf8(NEGOTIATE_SecPkgInfoA.Name, NEGOTIATE_SecPkgInfoW_NameBuffer,
1681 ARRAYSIZE(NEGOTIATE_SecPkgInfoW_NameBuffer));
1682 InitializeConstWCharFromUtf8(NEGOTIATE_SecPkgInfoA.Comment, NEGOTIATE_SecPkgInfoW_CommentBuffer,
1683 ARRAYSIZE(NEGOTIATE_SecPkgInfoW_CommentBuffer));