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";
44static const char PACKAGE_NAME_DISABLE_ALL[] =
"none";
45static const char PACKAGE_NAME_NTLM[] =
"ntlm";
46static const char PACKAGE_NAME_KERBEROS[] =
"kerberos";
47static const char PACKAGE_NAME_KERBEROS_U2U[] =
"u2u";
77 "Microsoft Package Negotiator"
80static WCHAR NEGOTIATE_SecPkgInfoW_NameBuffer[32] = { 0 };
81static WCHAR NEGOTIATE_SecPkgInfoW_CommentBuffer[32] = { 0 };
88 NEGOTIATE_SecPkgInfoW_NameBuffer,
89 NEGOTIATE_SecPkgInfoW_CommentBuffer
92static const WinPrAsn1_OID spnego_OID = { 6, (BYTE*)
"\x2b\x06\x01\x05\x05\x02" };
94 (BYTE*)
"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x03" };
95static const WinPrAsn1_OID kerberos_OID = { 9, (BYTE*)
"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
97 (BYTE*)
"\x2a\x86\x48\x82\xf7\x12\x01\x02\x02" };
98static const WinPrAsn1_OID ntlm_OID = { 10, (BYTE*)
"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a" };
100static const WinPrAsn1_OID negoex_OID = { 10, (BYTE*)
"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x1e" };
103static const SecPkg SecPkgTable[] = {
104 { KERBEROS_SSP_NAME, &KERBEROS_SecurityFunctionTableA, &KERBEROS_SecurityFunctionTableW },
105 { KERBEROS_SSP_NAME, &KERBEROS_SecurityFunctionTableA, &KERBEROS_SecurityFunctionTableW },
106 { NTLM_SSP_NAME, &NTLM_SecurityFunctionTableA, &NTLM_SecurityFunctionTableW }
109static const Mech MechTable[] = {
110 { &kerberos_u2u_OID, &SecPkgTable[0], ISC_REQ_INTEGRITY | ISC_REQ_USE_SESSION_KEY, TRUE },
111 { &kerberos_OID, &SecPkgTable[1], ISC_REQ_INTEGRITY, TRUE },
112 { &ntlm_OID, &SecPkgTable[2], 0, FALSE },
115static const SecPkg SecPkgTable[] = { { NTLM_SSP_NAME, &NTLM_SecurityFunctionTableA,
116 &NTLM_SecurityFunctionTableW } };
118static const Mech MechTable[] = {
119 { &ntlm_OID, &SecPkgTable[0], 0, FALSE },
123static const size_t MECH_COUNT =
sizeof(MechTable) /
sizeof(Mech);
136 enum NegState negState;
144static const NegToken empty_neg_token = { NOSTATE, FALSE, { 0, NULL },
145 { 0, 0, NULL }, { 0, 0, NULL }, { 0, 0, NULL } };
151 WINPR_ASSERT(init_context);
157 if (init_context->spnego)
159 init_context->mechTypes.pvBuffer = malloc(init_context->mechTypes.cbBuffer);
160 if (!init_context->mechTypes.pvBuffer)
167 *context = *init_context;
174 WINPR_ASSERT(context);
176 if (context->mechTypes.pvBuffer)
177 free(context->mechTypes.pvBuffer);
181static const char* negotiate_mech_name(
const WinPrAsn1_OID* oid)
183 if (sspi_gss_oid_compare(oid, &spnego_OID))
184 return "SPNEGO (1.3.6.1.5.5.2)";
185 else if (sspi_gss_oid_compare(oid, &kerberos_u2u_OID))
186 return "Kerberos user to user (1.2.840.113554.1.2.2.3)";
187 else if (sspi_gss_oid_compare(oid, &kerberos_OID))
188 return "Kerberos (1.2.840.113554.1.2.2)";
189 else if (sspi_gss_oid_compare(oid, &kerberos_wrong_OID))
190 return "Kerberos [wrong OID] (1.2.840.48018.1.2.2)";
191 else if (sspi_gss_oid_compare(oid, &ntlm_OID))
192 return "NTLM (1.3.6.1.4.1.311.2.2.10)";
193 else if (sspi_gss_oid_compare(oid, &negoex_OID))
194 return "NegoEx (1.3.6.1.4.1.311.2.2.30)";
196 return "Unknown mechanism";
199static const Mech* negotiate_GetMechByOID(
const WinPrAsn1_OID* oid)
205 if (sspi_gss_oid_compare(&testOid, &kerberos_wrong_OID))
207 testOid.len = kerberos_OID.len;
208 testOid.data = kerberos_OID.data;
211 for (
size_t i = 0; i < MECH_COUNT; i++)
213 if (sspi_gss_oid_compare(&testOid, MechTable[i].oid))
214 return &MechTable[i];
219static PSecHandle negotiate_FindCredential(MechCred* creds,
const Mech* mech)
226 for (
size_t i = 0; i < MECH_COUNT; i++)
228 MechCred* cred = &creds[i];
230 if (cred->mech == mech)
241static BOOL negotiate_get_dword(HKEY hKey,
const char* subkey, DWORD* pdwValue)
245 DWORD dwSize =
sizeof(dwValue);
246 LONG rc = RegQueryValueExA(hKey, subkey, NULL, &dwType, (BYTE*)&dwValue, &dwSize);
248 if (rc != ERROR_SUCCESS)
250 if (dwType != REG_DWORD)
257static BOOL negotiate_get_config_from_auth_package_list(
void* pAuthData, BOOL* kerberos, BOOL* ntlm,
261 char* tok_ctx = NULL;
262 char* PackageList = NULL;
267 char* tok_ptr = strtok_s(PackageList,
",", &tok_ctx);
271 const char* PackageName = tok_ptr;
272 BOOL PackageInclude = TRUE;
274 if (PackageName[0] ==
'!')
276 PackageName = &PackageName[1];
277 PackageInclude = FALSE;
280 if (_stricmp(PackageName, PACKAGE_NAME_NTLM) == 0)
282 *ntlm = PackageInclude;
284 else if (_stricmp(PackageName, PACKAGE_NAME_KERBEROS) == 0)
286 *kerberos = PackageInclude;
288 else if (_stricmp(PackageName, PACKAGE_NAME_KERBEROS_U2U) == 0)
290 *u2u = PackageInclude;
292 else if (_stricmp(PackageName, PACKAGE_NAME_DISABLE_ALL) == 0)
298 if (PackageName != PackageList)
300 WLog_WARN(TAG,
"Special keyword '%s' not first in list, aborting", PackageName);
306 WLog_WARN(TAG,
"Unknown authentication package name: %s, ignoring", PackageName);
309 tok_ptr = strtok_s(NULL,
",", &tok_ctx);
318static BOOL negotiate_get_config(
void* pAuthData, BOOL* kerberos, BOOL* ntlm, BOOL* u2u)
323 WINPR_ASSERT(kerberos);
327#if !defined(WITH_KRB5_NO_NTLM_FALLBACK)
332#if defined(WITH_KRB5)
340 if (negotiate_get_config_from_auth_package_list(pAuthData, kerberos, ntlm, u2u))
345 rc = RegOpenKeyExA(HKEY_LOCAL_MACHINE, NEGO_REG_KEY, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
346 if (rc == ERROR_SUCCESS)
350 if (negotiate_get_dword(hKey, PACKAGE_NAME_KERBEROS, &dwValue))
351 *kerberos = (dwValue != 0) ? TRUE : FALSE;
353 if (negotiate_get_dword(hKey, PACKAGE_NAME_KERBEROS_U2U, &dwValue))
354 *u2u = (dwValue != 0) ? TRUE : FALSE;
356#if !defined(WITH_KRB5_NO_NTLM_FALLBACK)
357 if (negotiate_get_dword(hKey, PACKAGE_NAME_NTLM, &dwValue))
358 *ntlm = (dwValue != 0) ? TRUE : FALSE;
367static BOOL negotiate_write_neg_token(
PSecBuffer output_buffer, NegToken* token)
369 WINPR_ASSERT(output_buffer);
373 WinPrAsn1Encoder* enc = NULL;
380 enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER);
388 if (!WinPrAsn1EncAppContainer(enc, 0))
392 if (!WinPrAsn1EncOID(enc, &spnego_OID))
397 if (!WinPrAsn1EncContextualSeqContainer(enc, token->init ? 0 : 1))
400 WLog_DBG(TAG, token->init ?
"Writing negTokenInit..." :
"Writing negTokenResp...");
405 if (!WinPrAsn1EncContextualRawContent(enc, 0, &mechTypes))
407 WLog_DBG(TAG,
"\tmechTypes [0] (%li bytes)", token->mechTypes.cbBuffer);
410 else if (token->negState != NOSTATE)
412 if (!WinPrAsn1EncContextualEnumerated(enc, 0, token->negState))
414 WLog_DBG(TAG,
"\tnegState [0] (%d)", token->negState);
418 if (token->supportedMech.len)
420 if (!WinPrAsn1EncContextualOID(enc, 1, &token->supportedMech))
422 WLog_DBG(TAG,
"\tsupportedMech [1] (%s)", negotiate_mech_name(&token->supportedMech));
426 if (token->mechToken.cbBuffer)
428 if (WinPrAsn1EncContextualOctetString(enc, 2, &mechToken) == 0)
430 WLog_DBG(TAG,
"\tmechToken [2] (%li bytes)", token->mechToken.cbBuffer);
434 if (token->mic.cbBuffer)
436 if (WinPrAsn1EncContextualOctetString(enc, 3, &mechListMic) == 0)
438 WLog_DBG(TAG,
"\tmechListMIC [3] (%li bytes)", token->mic.cbBuffer);
442 if (!WinPrAsn1EncEndContainer(enc))
448 if (!WinPrAsn1EncEndContainer(enc))
452 if (!WinPrAsn1EncStreamSize(enc, &len) || len > output_buffer->cbBuffer)
455 if (len > UINT32_MAX)
458 Stream_StaticInit(&s, output_buffer->pvBuffer, len);
460 if (WinPrAsn1EncToStream(enc, &s))
462 output_buffer->cbBuffer = (UINT32)len;
467 WinPrAsn1Encoder_Free(&enc);
471static BOOL negotiate_read_neg_token(
PSecBuffer input, NegToken* token)
476 WinPrAsn1_tagId contextual = 0;
477 WinPrAsn1_tag tag = 0;
485 WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, input->pvBuffer, input->cbBuffer);
487 if (!WinPrAsn1DecPeekTag(&dec, &tag))
493 if (!WinPrAsn1DecReadApp(&dec, &tag, &dec2) || tag != 0)
498 if (!WinPrAsn1DecReadOID(&dec, &oid, FALSE))
501 if (!sspi_gss_oid_compare(&spnego_OID, &oid))
505 if (!WinPrAsn1DecReadContextualSequence(&dec, 0, &err, &dec2))
511 else if (!WinPrAsn1DecReadContextualSequence(&dec, 1, &err, &dec2))
515 WLog_DBG(TAG, token->init ?
"Reading negTokenInit..." :
"Reading negTokenResp...");
520 if (!WinPrAsn1DecReadContextualTag(&dec, &contextual, &dec2))
529 wStream s = WinPrAsn1DecGetStream(&dec2);
530 token->mechTypes.BufferType = SECBUFFER_TOKEN;
531 const size_t mlen = Stream_Length(&s);
532 if (mlen > UINT32_MAX)
534 token->mechTypes.cbBuffer = (UINT32)mlen;
535 token->mechTypes.pvBuffer = Stream_Buffer(&s);
536 WLog_DBG(TAG,
"\tmechTypes [0] (%li bytes)", token->mechTypes.cbBuffer);
541 WinPrAsn1_ENUMERATED rd = 0;
542 if (!WinPrAsn1DecReadEnumerated(&dec2, &rd))
544 token->negState = rd;
545 WLog_DBG(TAG,
"\tnegState [0] (%d)", token->negState);
552 if (!WinPrAsn1DecPeekTagAndLen(&dec2, &tag, &len) || (tag != ER_TAG_BIT_STRING))
554 WLog_DBG(TAG,
"\treqFlags [1] (%li bytes)", len);
559 if (!WinPrAsn1DecReadOID(&dec2, &token->supportedMech, FALSE))
561 WLog_DBG(TAG,
"\tsupportedMech [1] (%s)",
562 negotiate_mech_name(&token->supportedMech));
567 if (!WinPrAsn1DecReadOctetString(&dec2, &octet_string, FALSE))
569 if (octet_string.len > UINT32_MAX)
571 token->mechToken.cbBuffer = (UINT32)octet_string.len;
572 token->mechToken.pvBuffer = octet_string.data;
573 token->mechToken.BufferType = SECBUFFER_TOKEN;
574 WLog_DBG(TAG,
"\tmechToken [2] (%li bytes)", octet_string.len);
578 if (!WinPrAsn1DecReadOctetString(&dec2, &octet_string, FALSE))
580 if (octet_string.len > UINT32_MAX)
582 token->mic.cbBuffer = (UINT32)octet_string.len;
583 token->mic.pvBuffer = octet_string.data;
584 token->mic.BufferType = SECBUFFER_TOKEN;
585 WLog_DBG(TAG,
"\tmechListMIC [3] (%li bytes)", octet_string.len);
588 WLog_ERR(TAG,
"unknown contextual item %d", contextual);
591 }
while (WinPrAsn1DecPeekTag(&dec, &tag));
596static SECURITY_STATUS negotiate_mic_exchange(
NEGOTIATE_CONTEXT* context, NegToken* input_token,
597 NegToken* output_token,
PSecBuffer output_buffer)
600 SecBufferDesc mic_buffer_desc = { SECBUFFER_VERSION, 2, mic_buffers };
601 SECURITY_STATUS status = 0;
603 WINPR_ASSERT(context);
604 WINPR_ASSERT(input_token);
605 WINPR_ASSERT(output_token);
606 WINPR_ASSERT(context->mech);
607 WINPR_ASSERT(context->mech->pkg);
612 mic_buffers[0] = context->mechTypes;
615 if (input_token->mic.cbBuffer > 0)
617 mic_buffers[1] = input_token->mic;
619 status = table->VerifySignature(&context->sub_context, &mic_buffer_desc, 0, 0);
620 if (status != SEC_E_OK)
623 output_token->negState = ACCEPT_COMPLETED;
627 if (input_token->negState != ACCEPT_COMPLETED)
630 output_token->mic.BufferType = SECBUFFER_TOKEN;
633 output_token->mic.cbBuffer = output_buffer->cbBuffer - output_token->mechToken.cbBuffer;
634 output_token->mic.pvBuffer =
635 (BYTE*)output_buffer->pvBuffer + output_token->mechToken.cbBuffer;
637 mic_buffers[1] = output_token->mic;
639 status = table->MakeSignature(&context->sub_context, 0, &mic_buffer_desc, 0);
640 if (status != SEC_E_OK)
643 output_token->mic = mic_buffers[1];
647 const TCHAR* name = sspi_SecureHandleGetUpperPointer(&context->sub_context);
649 return SEC_E_INTERNAL_ERROR;
651 if (_tcsncmp(name, NTLM_SSP_NAME, ARRAYSIZE(NTLM_SSP_NAME)) == 0)
653 if (!ntlm_reset_cipher_state(&context->sub_context))
654 return SEC_E_INTERNAL_ERROR;
660static SECURITY_STATUS SEC_ENTRY negotiate_InitializeSecurityContextW(
662 ULONG Reserved1, ULONG TargetDataRep,
PSecBufferDesc pInput, ULONG Reserved2,
667 MechCred* creds = NULL;
670 NegToken input_token = empty_neg_token;
671 NegToken output_token = empty_neg_token;
676 SecBufferDesc mech_input = { SECBUFFER_VERSION, 2, mech_input_buffers };
677 SecBufferDesc mech_output = { SECBUFFER_VERSION, 1, &output_token.mechToken };
678 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
679 SECURITY_STATUS sub_status = SEC_E_INTERNAL_ERROR;
680 WinPrAsn1Encoder* enc = NULL;
682 const Mech* mech = NULL;
684 if (!phCredential || !SecIsValidHandle(phCredential))
685 return SEC_E_NO_CREDENTIALS;
687 creds = sspi_SecureHandleGetLowerPointer(phCredential);
690 if (phContext && !phContext->dwLower && !phContext->dwUpper)
691 return SEC_E_INVALID_HANDLE;
693 context = sspi_SecureHandleGetLowerPointer(phContext);
697 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
698 bindings_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_CHANNEL_BINDINGS);
701 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
705 enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER);
707 return SEC_E_INSUFFICIENT_MEMORY;
709 if (!WinPrAsn1EncSeqContainer(enc))
712 for (
size_t i = 0; i < MECH_COUNT; i++)
714 MechCred* cred = &creds[i];
715 const SecPkg* pkg = MechTable[i].pkg;
717 WINPR_ASSERT(pkg->table_w);
721 WLog_DBG(TAG,
"Unavailable mechanism: %s", negotiate_mech_name(cred->mech->oid));
726 if (!init_context.mech)
732 CopyMemory(&output_token.mechToken, output_buffer,
sizeof(
SecBuffer));
735 mech_input_buffers[0] = *bindings_buffer;
737 WINPR_ASSERT(pkg->table_w->InitializeSecurityContextW);
738 sub_status = pkg->table_w->InitializeSecurityContextW(
739 &cred->cred, NULL, pszTargetName, fContextReq | cred->mech->flags, Reserved1,
740 TargetDataRep, &mech_input, Reserved2, &init_context.sub_context, &mech_output,
741 pfContextAttr, ptsExpiry);
744 if (IsSecurityStatusError(sub_status))
746 if (SecIsValidHandle(&init_context.sub_context))
748 WINPR_ASSERT(pkg->table_w->DeleteSecurityContext);
749 pkg->table_w->DeleteSecurityContext(&init_context.sub_context);
755 init_context.mech = cred->mech;
758 if (!WinPrAsn1EncOID(enc, cred->mech->oid))
760 WLog_DBG(TAG,
"Available mechanism: %s", negotiate_mech_name(cred->mech->oid));
764 if (!init_context.mech)
768 if (init_context.mech->oid == &ntlm_OID)
770 init_context.spnego = FALSE;
771 output_buffer->cbBuffer = output_token.mechToken.cbBuffer;
772 WLog_DBG(TAG,
"Using direct NTLM");
776 init_context.spnego = TRUE;
777 init_context.mechTypes.BufferType = SECBUFFER_DATA;
778 const size_t cb = WinPrAsn1EncEndContainer(enc);
779 WINPR_ASSERT(cb <= UINT32_MAX);
780 init_context.mechTypes.cbBuffer = (UINT32)cb;
784 context = negotiate_ContextNew(&init_context);
787 init_context.mech->pkg->table->DeleteSecurityContext(&init_context.sub_context);
788 WinPrAsn1Encoder_Free(&enc);
789 return SEC_E_INSUFFICIENT_MEMORY;
792 sspi_SecureHandleSetUpperPointer(phNewContext, NEGO_SSP_NAME);
793 sspi_SecureHandleSetLowerPointer(phNewContext, context);
795 if (!context->spnego)
802 Stream_StaticInit(&s, context->mechTypes.pvBuffer, context->mechTypes.cbBuffer);
803 if (!WinPrAsn1EncToStream(enc, &s))
806 output_token.mechTypes.cbBuffer = context->mechTypes.cbBuffer;
807 output_token.mechTypes.pvBuffer = context->mechTypes.pvBuffer;
808 output_token.init = TRUE;
810 if (sub_status == SEC_E_OK)
811 context->state = NEGOTIATE_STATE_FINAL_OPTIMISTIC;
816 return SEC_E_INVALID_TOKEN;
818 sub_context = &context->sub_context;
819 sub_cred = negotiate_FindCredential(creds, context->mech);
821 if (!context->spnego)
823 return context->mech->pkg->table_w->InitializeSecurityContextW(
824 sub_cred, sub_context, pszTargetName, fContextReq | context->mech->flags, Reserved1,
825 TargetDataRep, pInput, Reserved2, sub_context, pOutput, pfContextAttr, ptsExpiry);
828 if (!negotiate_read_neg_token(input_buffer, &input_token))
829 return SEC_E_INVALID_TOKEN;
832 if (context->state < NEGOTIATE_STATE_NEGORESP && input_token.supportedMech.len &&
833 !sspi_gss_oid_compare(&input_token.supportedMech, context->mech->oid))
835 mech = negotiate_GetMechByOID(&input_token.supportedMech);
837 return SEC_E_INVALID_TOKEN;
840 sub_cred = negotiate_FindCredential(creds, mech);
842 return SEC_E_INVALID_TOKEN;
845 context->mech->pkg->table_w->DeleteSecurityContext(&context->sub_context);
848 context->mech = mech;
853 if (context->state < NEGOTIATE_STATE_NEGORESP)
855 switch (input_token.negState)
858 return SEC_E_INVALID_TOKEN;
860 return SEC_E_LOGON_DENIED;
865 case ACCEPT_INCOMPLETE:
866 context->state = NEGOTIATE_STATE_NEGORESP;
868 case ACCEPT_COMPLETED:
869 if (context->state == NEGOTIATE_STATE_INITIAL)
870 context->state = NEGOTIATE_STATE_NEGORESP;
872 context->state = NEGOTIATE_STATE_FINAL;
878 WLog_DBG(TAG,
"Negotiated mechanism: %s", negotiate_mech_name(context->mech->oid));
881 if (context->state == NEGOTIATE_STATE_NEGORESP)
886 CopyMemory(&output_token.mechToken, output_buffer,
sizeof(
SecBuffer));
888 mech_input_buffers[0] = input_token.mechToken;
890 mech_input_buffers[1] = *bindings_buffer;
892 status = context->mech->pkg->table_w->InitializeSecurityContextW(
893 sub_cred, sub_context, pszTargetName, fContextReq | context->mech->flags, Reserved1,
894 TargetDataRep, input_token.mechToken.cbBuffer ? &mech_input : NULL, Reserved2,
895 &context->sub_context, &mech_output, pfContextAttr, ptsExpiry);
897 if (IsSecurityStatusError(status))
901 if (status == SEC_E_OK)
903 if (output_token.mechToken.cbBuffer > 0)
904 context->state = NEGOTIATE_STATE_MIC;
906 context->state = NEGOTIATE_STATE_FINAL;
910 if (context->state == NEGOTIATE_STATE_FINAL && input_token.mic.cbBuffer == 0)
912 if (context->mic || input_token.negState != ACCEPT_COMPLETED)
913 return SEC_E_INVALID_TOKEN;
916 output_buffer->cbBuffer = 0;
920 if ((context->state == NEGOTIATE_STATE_MIC && context->mic) ||
921 context->state == NEGOTIATE_STATE_FINAL)
923 status = negotiate_mic_exchange(context, &input_token, &output_token, output_buffer);
924 if (status != SEC_E_OK)
929 if (input_token.negState == ACCEPT_COMPLETED)
932 output_buffer->cbBuffer = 0;
936 if (output_token.negState == ACCEPT_COMPLETED)
939 status = SEC_I_CONTINUE_NEEDED;
941 if (!negotiate_write_neg_token(output_buffer, &output_token))
942 status = SEC_E_INTERNAL_ERROR;
945 WinPrAsn1Encoder_Free(&enc);
949static SECURITY_STATUS SEC_ENTRY negotiate_InitializeSecurityContextA(
951 ULONG Reserved1, ULONG TargetDataRep,
PSecBufferDesc pInput, ULONG Reserved2,
954 SECURITY_STATUS status = 0;
955 SEC_WCHAR* pszTargetNameW = NULL;
959 pszTargetNameW = ConvertUtf8ToWCharAlloc(pszTargetName, NULL);
961 return SEC_E_INTERNAL_ERROR;
964 status = negotiate_InitializeSecurityContextW(
965 phCredential, phContext, pszTargetNameW, fContextReq, Reserved1, TargetDataRep, pInput,
966 Reserved2, phNewContext, pOutput, pfContextAttr, ptsExpiry);
967 free(pszTargetNameW);
975 WinPrAsn1_tagId tag = 0;
976 const char ssp[] =
"NTLMSSP";
981 if (input_buffer->cbBuffer >= 8 && strncmp(input_buffer->pvBuffer, ssp,
sizeof(ssp)) == 0)
984 return negotiate_GetMechByOID(&ntlm_OID);
988 WinPrAsn1Decoder_InitMem(&decoder, WINPR_ASN1_DER, input_buffer->pvBuffer,
989 input_buffer->cbBuffer);
991 if (!WinPrAsn1DecReadApp(&decoder, &tag, &appDecoder) || tag != 0)
994 if (!WinPrAsn1DecReadOID(&appDecoder, oid, FALSE))
997 if (sspi_gss_oid_compare(oid, &spnego_OID))
1003 return negotiate_GetMechByOID(oid);
1006static SECURITY_STATUS SEC_ENTRY negotiate_AcceptSecurityContext(
1013 MechCred* creds = NULL;
1015 NegToken input_token = empty_neg_token;
1016 NegToken output_token = empty_neg_token;
1019 SecBufferDesc mech_input = { SECBUFFER_VERSION, 1, &input_token.mechToken };
1020 SecBufferDesc mech_output = { SECBUFFER_VERSION, 1, &output_token.mechToken };
1021 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
1024 WinPrAsn1_tagId tag = 0;
1026 const Mech* first_mech = NULL;
1028 if (!phCredential || !SecIsValidHandle(phCredential))
1029 return SEC_E_NO_CREDENTIALS;
1031 creds = sspi_SecureHandleGetLowerPointer(phCredential);
1034 return SEC_E_INVALID_TOKEN;
1037 if (phContext && !phContext->dwLower && !phContext->dwUpper)
1038 return SEC_E_INVALID_HANDLE;
1040 context = sspi_SecureHandleGetLowerPointer(phContext);
1042 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
1044 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
1048 init_context.mech = guessMech(input_buffer, &init_context.spnego, &oid);
1049 if (!init_context.mech && !init_context.spnego)
1050 return SEC_E_INVALID_TOKEN;
1052 WLog_DBG(TAG,
"Mechanism: %s", negotiate_mech_name(&oid));
1054 if (init_context.spnego)
1057 if (!negotiate_read_neg_token(input_buffer, &input_token))
1058 return SEC_E_INVALID_TOKEN;
1061 if (!input_token.init || input_token.mechTypes.cbBuffer == 0)
1062 return SEC_E_INVALID_TOKEN;
1064 init_context.mechTypes.BufferType = SECBUFFER_DATA;
1065 init_context.mechTypes.cbBuffer = input_token.mechTypes.cbBuffer;
1068 WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, input_token.mechTypes.pvBuffer,
1069 input_token.mechTypes.cbBuffer);
1071 if (!WinPrAsn1DecReadSequence(&dec, &dec2))
1072 return SEC_E_INVALID_TOKEN;
1076 if (input_token.mechToken.cbBuffer)
1078 if (!WinPrAsn1DecReadOID(&dec, &oid, FALSE))
1079 return SEC_E_INVALID_TOKEN;
1081 init_context.mech = negotiate_GetMechByOID(&oid);
1083 if (init_context.mech)
1086 output_token.mechToken = *output_buffer;
1087 WLog_DBG(TAG,
"Requested mechanism: %s",
1088 negotiate_mech_name(init_context.mech->oid));
1093 if (init_context.mech)
1095 sub_cred = negotiate_FindCredential(creds, init_context.mech);
1097 status = init_context.mech->pkg->table->AcceptSecurityContext(
1098 sub_cred, NULL, init_context.spnego ? &mech_input : pInput, fContextReq,
1099 TargetDataRep, &init_context.sub_context,
1100 init_context.spnego ? &mech_output : pOutput, pfContextAttr, ptsTimeStamp);
1103 if (IsSecurityStatusError(status))
1105 if (!init_context.spnego)
1108 init_context.mic = TRUE;
1109 first_mech = init_context.mech;
1110 init_context.mech = NULL;
1111 output_token.mechToken.cbBuffer = 0;
1114 while (!init_context.mech && WinPrAsn1DecPeekTag(&dec, &tag))
1117 if (!WinPrAsn1DecReadOID(&dec, &oid, FALSE))
1118 return SEC_E_INVALID_TOKEN;
1120 init_context.mech = negotiate_GetMechByOID(&oid);
1121 WLog_DBG(TAG,
"Requested mechanism: %s", negotiate_mech_name(&oid));
1124 if (init_context.mech == first_mech)
1125 init_context.mech = NULL;
1127 if (init_context.mech && !negotiate_FindCredential(creds, init_context.mech))
1128 init_context.mech = NULL;
1131 if (!init_context.mech)
1132 return SEC_E_INTERNAL_ERROR;
1134 context = negotiate_ContextNew(&init_context);
1137 if (!IsSecurityStatusError(status))
1138 init_context.mech->pkg->table->DeleteSecurityContext(&init_context.sub_context);
1139 return SEC_E_INSUFFICIENT_MEMORY;
1142 sspi_SecureHandleSetUpperPointer(phNewContext, NEGO_SSP_NAME);
1143 sspi_SecureHandleSetLowerPointer(phNewContext, context);
1145 if (!init_context.spnego)
1148 CopyMemory(init_context.mechTypes.pvBuffer, input_token.mechTypes.pvBuffer,
1149 input_token.mechTypes.cbBuffer);
1151 if (!context->mech->preferred)
1153 output_token.negState = REQUEST_MIC;
1154 context->mic = TRUE;
1158 output_token.negState = ACCEPT_INCOMPLETE;
1161 if (status == SEC_E_OK)
1162 context->state = NEGOTIATE_STATE_FINAL;
1164 context->state = NEGOTIATE_STATE_NEGORESP;
1166 output_token.supportedMech = oid;
1167 WLog_DBG(TAG,
"Accepted mechanism: %s", negotiate_mech_name(&output_token.supportedMech));
1171 sub_cred = negotiate_FindCredential(creds, context->mech);
1173 return SEC_E_NO_CREDENTIALS;
1175 if (!context->spnego)
1177 return context->mech->pkg->table->AcceptSecurityContext(
1178 sub_cred, &context->sub_context, pInput, fContextReq, TargetDataRep,
1179 &context->sub_context, pOutput, pfContextAttr, ptsTimeStamp);
1182 if (!negotiate_read_neg_token(input_buffer, &input_token))
1183 return SEC_E_INVALID_TOKEN;
1186 if (input_token.mechToken.cbBuffer > 0)
1188 if (context->state != NEGOTIATE_STATE_NEGORESP)
1189 return SEC_E_INVALID_TOKEN;
1193 CopyMemory(&output_token.mechToken, output_buffer,
sizeof(
SecBuffer));
1195 status = context->mech->pkg->table->AcceptSecurityContext(
1196 sub_cred, &context->sub_context, &mech_input, fContextReq | context->mech->flags,
1197 TargetDataRep, &context->sub_context, &mech_output, pfContextAttr, ptsTimeStamp);
1199 if (IsSecurityStatusError(status))
1202 if (status == SEC_E_OK)
1203 context->state = NEGOTIATE_STATE_FINAL;
1205 else if (context->state == NEGOTIATE_STATE_NEGORESP)
1206 return SEC_E_INVALID_TOKEN;
1209 if (context->state == NEGOTIATE_STATE_FINAL)
1212 if (context->mic && output_token.mechToken.cbBuffer == 0 && input_token.mic.cbBuffer == 0)
1213 return SEC_E_INVALID_TOKEN;
1215 if (context->mic || input_token.mic.cbBuffer > 0)
1217 status = negotiate_mic_exchange(context, &input_token, &output_token, output_buffer);
1218 if (status != SEC_E_OK)
1222 output_token.negState = ACCEPT_COMPLETED;
1225 if (input_token.negState == ACCEPT_COMPLETED)
1228 output_buffer->cbBuffer = 0;
1232 if (output_token.negState == ACCEPT_COMPLETED)
1235 status = SEC_I_CONTINUE_NEEDED;
1237 if (!negotiate_write_neg_token(output_buffer, &output_token))
1238 return SEC_E_INTERNAL_ERROR;
1243static SECURITY_STATUS SEC_ENTRY negotiate_CompleteAuthToken(
PCtxtHandle phContext,
1247 SECURITY_STATUS status = SEC_E_OK;
1251 return SEC_E_INVALID_HANDLE;
1253 WINPR_ASSERT(context->mech);
1254 WINPR_ASSERT(context->mech->pkg);
1255 WINPR_ASSERT(context->mech->pkg->table);
1256 if (context->mech->pkg->table->CompleteAuthToken)
1257 status = context->mech->pkg->table->CompleteAuthToken(&context->sub_context, pToken);
1262static SECURITY_STATUS SEC_ENTRY negotiate_DeleteSecurityContext(
PCtxtHandle phContext)
1265 SECURITY_STATUS status = SEC_E_OK;
1267 const SecPkg* pkg = NULL;
1270 return SEC_E_INVALID_HANDLE;
1272 WINPR_ASSERT(context->mech);
1273 WINPR_ASSERT(context->mech->pkg);
1274 WINPR_ASSERT(context->mech->pkg->table);
1275 pkg = context->mech->pkg;
1277 if (pkg->table->DeleteSecurityContext)
1278 status = pkg->table->DeleteSecurityContext(&context->sub_context);
1280 negotiate_ContextFree(context);
1284static SECURITY_STATUS SEC_ENTRY
1285negotiate_ImpersonateSecurityContext(WINPR_ATTR_UNUSED
PCtxtHandle phContext)
1290static SECURITY_STATUS SEC_ENTRY
1291negotiate_RevertSecurityContext(WINPR_ATTR_UNUSED
PCtxtHandle phContext)
1296static SECURITY_STATUS SEC_ENTRY negotiate_QueryContextAttributesW(
PCtxtHandle phContext,
1297 ULONG ulAttribute,
void* pBuffer)
1302 return SEC_E_INVALID_HANDLE;
1304 WINPR_ASSERT(context->mech);
1305 WINPR_ASSERT(context->mech->pkg);
1306 WINPR_ASSERT(context->mech->pkg->table_w);
1307 if (context->mech->pkg->table_w->QueryContextAttributesW)
1308 return context->mech->pkg->table_w->QueryContextAttributesW(&context->sub_context,
1309 ulAttribute, pBuffer);
1311 return SEC_E_UNSUPPORTED_FUNCTION;
1314static SECURITY_STATUS SEC_ENTRY negotiate_QueryContextAttributesA(
PCtxtHandle phContext,
1315 ULONG ulAttribute,
void* pBuffer)
1320 return SEC_E_INVALID_HANDLE;
1322 WINPR_ASSERT(context->mech);
1323 WINPR_ASSERT(context->mech->pkg);
1324 WINPR_ASSERT(context->mech->pkg->table);
1325 if (context->mech->pkg->table->QueryContextAttributesA)
1326 return context->mech->pkg->table->QueryContextAttributesA(&context->sub_context,
1327 ulAttribute, pBuffer);
1329 return SEC_E_UNSUPPORTED_FUNCTION;
1332static SECURITY_STATUS SEC_ENTRY negotiate_SetContextAttributesW(
PCtxtHandle phContext,
1333 ULONG ulAttribute,
void* pBuffer,
1339 return SEC_E_INVALID_HANDLE;
1341 WINPR_ASSERT(context->mech);
1342 WINPR_ASSERT(context->mech->pkg);
1343 WINPR_ASSERT(context->mech->pkg->table_w);
1344 if (context->mech->pkg->table_w->SetContextAttributesW)
1345 return context->mech->pkg->table_w->SetContextAttributesW(&context->sub_context,
1346 ulAttribute, pBuffer, cbBuffer);
1348 return SEC_E_UNSUPPORTED_FUNCTION;
1351static SECURITY_STATUS SEC_ENTRY negotiate_SetContextAttributesA(
PCtxtHandle phContext,
1352 ULONG ulAttribute,
void* pBuffer,
1358 return SEC_E_INVALID_HANDLE;
1360 WINPR_ASSERT(context->mech);
1361 WINPR_ASSERT(context->mech->pkg);
1362 WINPR_ASSERT(context->mech->pkg->table);
1363 if (context->mech->pkg->table->SetContextAttributesA)
1364 return context->mech->pkg->table->SetContextAttributesA(&context->sub_context, ulAttribute,
1367 return SEC_E_UNSUPPORTED_FUNCTION;
1370static SECURITY_STATUS SEC_ENTRY negotiate_SetCredentialsAttributesW(
PCredHandle phCredential,
1372 void* pBuffer, ULONG cbBuffer)
1374 MechCred* creds = NULL;
1375 BOOL success = FALSE;
1376 SECURITY_STATUS secStatus = 0;
1378 creds = sspi_SecureHandleGetLowerPointer(phCredential);
1381 return SEC_E_INVALID_HANDLE;
1383 for (
size_t i = 0; i < MECH_COUNT; i++)
1385 MechCred* cred = &creds[i];
1387 WINPR_ASSERT(cred->mech);
1388 WINPR_ASSERT(cred->mech->pkg);
1389 WINPR_ASSERT(cred->mech->pkg->table);
1390 WINPR_ASSERT(cred->mech->pkg->table_w->SetCredentialsAttributesW);
1391 secStatus = cred->mech->pkg->table_w->SetCredentialsAttributesW(&cred->cred, ulAttribute,
1394 if (secStatus == SEC_E_OK)
1401 return (success ? SEC_E_OK : SEC_E_UNSUPPORTED_FUNCTION);
1404static SECURITY_STATUS SEC_ENTRY negotiate_SetCredentialsAttributesA(
PCredHandle phCredential,
1406 void* pBuffer, ULONG cbBuffer)
1408 MechCred* creds = NULL;
1409 BOOL success = FALSE;
1410 SECURITY_STATUS secStatus = 0;
1412 creds = sspi_SecureHandleGetLowerPointer(phCredential);
1415 return SEC_E_INVALID_HANDLE;
1417 for (
size_t i = 0; i < MECH_COUNT; i++)
1419 MechCred* cred = &creds[i];
1424 WINPR_ASSERT(cred->mech);
1425 WINPR_ASSERT(cred->mech->pkg);
1426 WINPR_ASSERT(cred->mech->pkg->table);
1427 WINPR_ASSERT(cred->mech->pkg->table->SetCredentialsAttributesA);
1428 secStatus = cred->mech->pkg->table->SetCredentialsAttributesA(&cred->cred, ulAttribute,
1431 if (secStatus == SEC_E_OK)
1438 return (success ? SEC_E_OK : SEC_E_UNSUPPORTED_FUNCTION);
1441static SECURITY_STATUS SEC_ENTRY negotiate_AcquireCredentialsHandleW(
1442 SEC_WCHAR* pszPrincipal, SEC_WCHAR* pszPackage, ULONG fCredentialUse,
void* pvLogonID,
1443 void* pAuthData, SEC_GET_KEY_FN pGetKeyFn,
void* pvGetKeyArgument,
PCredHandle phCredential,
1446 BOOL kerberos = FALSE;
1450 if (!negotiate_get_config(pAuthData, &kerberos, &ntlm, &u2u))
1451 return SEC_E_INTERNAL_ERROR;
1453 MechCred* creds = calloc(MECH_COUNT,
sizeof(MechCred));
1456 return SEC_E_INTERNAL_ERROR;
1458 for (
size_t i = 0; i < MECH_COUNT; i++)
1460 MechCred* cred = &creds[i];
1461 const SecPkg* pkg = MechTable[i].pkg;
1462 cred->mech = &MechTable[i];
1464 if (!kerberos && sspi_gss_oid_compare(MechTable[i].oid, &kerberos_OID))
1466 if (!u2u && sspi_gss_oid_compare(MechTable[i].oid, &kerberos_u2u_OID))
1468 if (!ntlm && _tcsncmp(SecPkgTable[i].name, NTLM_SSP_NAME, ARRAYSIZE(NTLM_SSP_NAME)) == 0)
1471 WINPR_ASSERT(pkg->table_w);
1472 WINPR_ASSERT(pkg->table_w->AcquireCredentialsHandleW);
1473 if (pkg->table_w->AcquireCredentialsHandleW(
1474 pszPrincipal, pszPackage, fCredentialUse, pvLogonID, pAuthData, pGetKeyFn,
1475 pvGetKeyArgument, &cred->cred, ptsExpiry) != SEC_E_OK)
1481 sspi_SecureHandleSetLowerPointer(phCredential, (
void*)creds);
1482 sspi_SecureHandleSetUpperPointer(phCredential, (
void*)NEGO_SSP_NAME);
1486static SECURITY_STATUS SEC_ENTRY negotiate_AcquireCredentialsHandleA(
1487 SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, ULONG fCredentialUse,
void* pvLogonID,
1488 void* pAuthData, SEC_GET_KEY_FN pGetKeyFn,
void* pvGetKeyArgument,
PCredHandle phCredential,
1491 BOOL kerberos = FALSE;
1495 if (!negotiate_get_config(pAuthData, &kerberos, &ntlm, &u2u))
1496 return SEC_E_INTERNAL_ERROR;
1498 MechCred* creds = calloc(MECH_COUNT,
sizeof(MechCred));
1501 return SEC_E_INTERNAL_ERROR;
1503 for (
size_t i = 0; i < MECH_COUNT; i++)
1505 const SecPkg* pkg = MechTable[i].pkg;
1506 MechCred* cred = &creds[i];
1508 cred->mech = &MechTable[i];
1510 if (!kerberos && sspi_gss_oid_compare(MechTable[i].oid, &kerberos_OID))
1512 if (!u2u && sspi_gss_oid_compare(MechTable[i].oid, &kerberos_u2u_OID))
1514 if (!ntlm && _tcsncmp(SecPkgTable[i].name, NTLM_SSP_NAME, ARRAYSIZE(NTLM_SSP_NAME)) == 0)
1517 WINPR_ASSERT(pkg->table);
1518 WINPR_ASSERT(pkg->table->AcquireCredentialsHandleA);
1519 if (pkg->table->AcquireCredentialsHandleA(pszPrincipal, pszPackage, fCredentialUse,
1520 pvLogonID, pAuthData, pGetKeyFn, pvGetKeyArgument,
1521 &cred->cred, ptsExpiry) != SEC_E_OK)
1527 sspi_SecureHandleSetLowerPointer(phCredential, (
void*)creds);
1528 sspi_SecureHandleSetUpperPointer(phCredential, (
void*)NEGO_SSP_NAME);
1532static SECURITY_STATUS SEC_ENTRY negotiate_QueryCredentialsAttributesW(
1533 WINPR_ATTR_UNUSED
PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
1534 WINPR_ATTR_UNUSED
void* pBuffer)
1536 WLog_ERR(TAG,
"TODO: Implement");
1537 return SEC_E_UNSUPPORTED_FUNCTION;
1540static SECURITY_STATUS SEC_ENTRY negotiate_QueryCredentialsAttributesA(
1541 WINPR_ATTR_UNUSED
PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
1542 WINPR_ATTR_UNUSED
void* pBuffer)
1544 WLog_ERR(TAG,
"TODO: Implement");
1545 return SEC_E_UNSUPPORTED_FUNCTION;
1548static SECURITY_STATUS SEC_ENTRY negotiate_FreeCredentialsHandle(
PCredHandle phCredential)
1550 MechCred* creds = NULL;
1552 creds = sspi_SecureHandleGetLowerPointer(phCredential);
1554 return SEC_E_INVALID_HANDLE;
1556 for (
size_t i = 0; i < MECH_COUNT; i++)
1558 MechCred* cred = &creds[i];
1560 WINPR_ASSERT(cred->mech);
1561 WINPR_ASSERT(cred->mech->pkg);
1562 WINPR_ASSERT(cred->mech->pkg->table);
1563 WINPR_ASSERT(cred->mech->pkg->table->FreeCredentialsHandle);
1564 cred->mech->pkg->table->FreeCredentialsHandle(&cred->cred);
1571static SECURITY_STATUS SEC_ENTRY negotiate_EncryptMessage(
PCtxtHandle phContext, ULONG fQOP,
1578 return SEC_E_INVALID_HANDLE;
1583 WINPR_ASSERT(context->mech);
1584 WINPR_ASSERT(context->mech->pkg);
1585 WINPR_ASSERT(context->mech->pkg->table);
1586 if (context->mech->pkg->table->EncryptMessage)
1587 return context->mech->pkg->table->EncryptMessage(&context->sub_context, fQOP, pMessage,
1590 return SEC_E_UNSUPPORTED_FUNCTION;
1593static SECURITY_STATUS SEC_ENTRY negotiate_DecryptMessage(
PCtxtHandle phContext,
1595 ULONG MessageSeqNo, ULONG* pfQOP)
1600 return SEC_E_INVALID_HANDLE;
1605 WINPR_ASSERT(context->mech);
1606 WINPR_ASSERT(context->mech->pkg);
1607 WINPR_ASSERT(context->mech->pkg->table);
1608 if (context->mech->pkg->table->DecryptMessage)
1609 return context->mech->pkg->table->DecryptMessage(&context->sub_context, pMessage,
1610 MessageSeqNo, pfQOP);
1612 return SEC_E_UNSUPPORTED_FUNCTION;
1615static SECURITY_STATUS SEC_ENTRY negotiate_MakeSignature(
PCtxtHandle phContext, ULONG fQOP,
1622 return SEC_E_INVALID_HANDLE;
1627 WINPR_ASSERT(context->mech);
1628 WINPR_ASSERT(context->mech->pkg);
1629 WINPR_ASSERT(context->mech->pkg->table);
1630 if (context->mech->pkg->table->MakeSignature)
1631 return context->mech->pkg->table->MakeSignature(&context->sub_context, fQOP, pMessage,
1634 return SEC_E_UNSUPPORTED_FUNCTION;
1637static SECURITY_STATUS SEC_ENTRY negotiate_VerifySignature(
PCtxtHandle phContext,
1639 ULONG MessageSeqNo, ULONG* pfQOP)
1644 return SEC_E_INVALID_HANDLE;
1649 WINPR_ASSERT(context->mech);
1650 WINPR_ASSERT(context->mech->pkg);
1651 WINPR_ASSERT(context->mech->pkg->table);
1652 if (context->mech->pkg->table->VerifySignature)
1653 return context->mech->pkg->table->VerifySignature(&context->sub_context, pMessage,
1654 MessageSeqNo, pfQOP);
1656 return SEC_E_UNSUPPORTED_FUNCTION;
1662 negotiate_QueryCredentialsAttributesA,
1663 negotiate_AcquireCredentialsHandleA,
1664 negotiate_FreeCredentialsHandle,
1666 negotiate_InitializeSecurityContextA,
1667 negotiate_AcceptSecurityContext,
1668 negotiate_CompleteAuthToken,
1669 negotiate_DeleteSecurityContext,
1671 negotiate_QueryContextAttributesA,
1672 negotiate_ImpersonateSecurityContext,
1673 negotiate_RevertSecurityContext,
1674 negotiate_MakeSignature,
1675 negotiate_VerifySignature,
1685 negotiate_EncryptMessage,
1686 negotiate_DecryptMessage,
1687 negotiate_SetContextAttributesA,
1688 negotiate_SetCredentialsAttributesA,
1694 negotiate_QueryCredentialsAttributesW,
1695 negotiate_AcquireCredentialsHandleW,
1696 negotiate_FreeCredentialsHandle,
1698 negotiate_InitializeSecurityContextW,
1699 negotiate_AcceptSecurityContext,
1700 negotiate_CompleteAuthToken,
1701 negotiate_DeleteSecurityContext,
1703 negotiate_QueryContextAttributesW,
1704 negotiate_ImpersonateSecurityContext,
1705 negotiate_RevertSecurityContext,
1706 negotiate_MakeSignature,
1707 negotiate_VerifySignature,
1717 negotiate_EncryptMessage,
1718 negotiate_DecryptMessage,
1719 negotiate_SetContextAttributesW,
1720 negotiate_SetCredentialsAttributesW,
1723BOOL NEGOTIATE_init(
void)
1725 InitializeConstWCharFromUtf8(NEGOTIATE_SecPkgInfoA.Name, NEGOTIATE_SecPkgInfoW_NameBuffer,
1726 ARRAYSIZE(NEGOTIATE_SecPkgInfoW_NameBuffer));
1727 InitializeConstWCharFromUtf8(NEGOTIATE_SecPkgInfoA.Comment, NEGOTIATE_SecPkgInfoW_CommentBuffer,
1728 ARRAYSIZE(NEGOTIATE_SecPkgInfoW_CommentBuffer));