FreeRDP/winpr/libwinpr/sspi/Negotiate/negotiate.c

1578 lines
48 KiB
C
Raw Normal View History

/**
* WinPR: Windows Portable Runtime
* Negotiate Security Package
*
2014-06-06 06:10:08 +04:00
* Copyright 2011-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
2017-05-11 19:51:45 +03:00
* Copyright 2017 Dorian Ducournau <dorian.ducournau@gmail.com>
2015-07-07 18:17:29 +03:00
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
2022-02-16 12:08:00 +03:00
#include <winpr/config.h>
#include <winpr/crt.h>
2022-06-24 16:03:55 +03:00
#include <winpr/assert.h>
#include <winpr/sspi.h>
2018-02-13 13:00:56 +03:00
#include <winpr/tchar.h>
#include <winpr/assert.h>
#include <winpr/registry.h>
#include <winpr/build-config.h>
#include "negotiate.h"
#include "../sspi.h"
#include "../../log.h"
#define TAG WINPR_TAG("negotiate")
static const char NEGO_REG_KEY[] =
"Software\\" WINPR_VENDOR_STRING "\\" WINPR_PRODUCT_STRING "\\SSPI\\Negotiate";
extern const SecurityFunctionTableA NTLM_SecurityFunctionTableA;
extern const SecurityFunctionTableW NTLM_SecurityFunctionTableW;
2017-05-11 19:51:45 +03:00
extern const SecurityFunctionTableA KERBEROS_SecurityFunctionTableA;
extern const SecurityFunctionTableW KERBEROS_SecurityFunctionTableW;
2022-05-28 01:29:28 +03:00
extern void ntlm_reset_cipher_state(PSecHandle);
typedef struct
{
const TCHAR* name;
const SecurityFunctionTableA* table;
const SecurityFunctionTableW* table_w;
} SecPkg;
struct Mech_st
{
const sspi_gss_OID_desc* oid;
const SecPkg* pkg;
2022-06-07 06:30:42 +03:00
const UINT flags;
const BOOL preferred;
2022-05-28 01:29:28 +03:00
};
typedef struct
{
const Mech* mech;
CredHandle cred;
BOOL valid;
} MechCred;
2017-05-11 19:51:45 +03:00
2019-11-06 17:24:51 +03:00
const SecPkgInfoA NEGOTIATE_SecPkgInfoA = {
0x00083BB3, /* fCapabilities */
1, /* wVersion */
0x0009, /* wRPCID */
0x00002FE0, /* cbMaxToken */
"Negotiate", /* Name */
"Microsoft Package Negotiator" /* Comment */
};
static WCHAR NEGOTIATE_SecPkgInfoW_Name[] = { 'N', 'e', 'g', 'o', 't', 'i', 'a', 't', 'e', '\0' };
2019-11-06 17:24:51 +03:00
static WCHAR NEGOTIATE_SecPkgInfoW_Comment[] = { 'M', 'i', 'c', 'r', 'o', 's', 'o', 'f', 't', ' ',
'P', 'a', 'c', 'k', 'a', 'g', 'e', ' ', 'N', 'e',
'g', 'o', 't', 'i', 'a', 't', 'o', 'r', '\0' };
2019-11-06 17:24:51 +03:00
const SecPkgInfoW NEGOTIATE_SecPkgInfoW = {
0x00083BB3, /* fCapabilities */
1, /* wVersion */
0x0009, /* wRPCID */
0x00002FE0, /* cbMaxToken */
NEGOTIATE_SecPkgInfoW_Name, /* Name */
NEGOTIATE_SecPkgInfoW_Comment /* Comment */
};
2022-05-28 01:29:28 +03:00
static const sspi_gss_OID_desc spnego_OID = { 6, "\x2b\x06\x01\x05\x05\x02" };
static const sspi_gss_OID_desc kerberos_OID = { 9, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
2022-05-31 04:14:32 +03:00
static const sspi_gss_OID_desc kerberos_wrong_OID = { 9, "\x2a\x86\x48\x82\xf7\x12\x01\x02\x02" };
2022-05-28 01:29:28 +03:00
static const sspi_gss_OID_desc ntlm_OID = { 10, "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a" };
static const SecPkg SecPkgTable[] = {
{ KERBEROS_SSP_NAME, &KERBEROS_SecurityFunctionTableA, &KERBEROS_SecurityFunctionTableW },
{ NTLM_SSP_NAME, &NTLM_SecurityFunctionTableA, &NTLM_SecurityFunctionTableW }
};
static const Mech MechTable[] = {
2022-06-07 06:30:42 +03:00
{ &kerberos_OID, &SecPkgTable[0], 0, TRUE },
{ &ntlm_OID, &SecPkgTable[1], 0, FALSE },
2022-05-28 01:29:28 +03:00
};
2022-06-24 16:03:55 +03:00
static const size_t MECH_COUNT = sizeof(MechTable) / sizeof(Mech);
2022-05-28 01:29:28 +03:00
enum NegState
{
NOSTATE = -1,
ACCEPT_COMPLETED,
ACCEPT_INCOMPLETE,
REJECT,
REQUEST_MIC
};
typedef struct
{
enum NegState negState;
BOOL init;
2022-05-31 04:14:32 +03:00
sspi_gss_OID_desc supportedMech;
2022-05-28 01:29:28 +03:00
SecBuffer mechTypes;
SecBuffer mechToken;
SecBuffer mic;
} NegToken;
2022-06-10 00:48:53 +03:00
static INLINE size_t asn_tlv_length(size_t len)
2022-06-02 03:22:00 +03:00
{
if (len <= 0x7F)
return 2 + len;
else if (len <= 0xFF)
return 3 + len;
else if (len <= 0xFFFF)
return 4 + len;
else if (len <= 0xFFFFFF)
return 5 + len;
else
return 6 + len;
}
2022-06-10 00:48:53 +03:00
static INLINE size_t asn_contextual_length(size_t len)
2022-06-02 03:22:00 +03:00
{
return asn_tlv_length(asn_tlv_length(len));
}
static BYTE* negotiate_write_tlv(BYTE* buf, BYTE tag, size_t len, const BYTE* value)
2017-05-11 19:51:45 +03:00
{
2022-05-28 01:29:28 +03:00
int bytes = 0;
2022-05-28 01:29:28 +03:00
*buf++ = tag;
if (len <= 0x7F)
2022-05-31 04:14:32 +03:00
*buf++ = (BYTE)len;
2022-05-28 01:29:28 +03:00
else
{
while (len >> (++bytes * 8))
;
*buf++ = bytes | 0x80;
for (int i = 0; i < bytes; i++)
buf[bytes - i - 1] = (BYTE)(len >> (i * 8));
buf += bytes;
}
if (value)
2017-05-11 19:51:45 +03:00
{
2022-05-28 01:29:28 +03:00
MoveMemory(buf, value, len);
buf += len;
2017-05-11 19:51:45 +03:00
}
2022-05-28 01:29:28 +03:00
return buf;
}
2022-06-02 03:22:00 +03:00
static BYTE* negotiate_write_contextual_tlv(BYTE* buf, BYTE contextual, BYTE tag, size_t len,
const BYTE* value)
2022-05-28 01:29:28 +03:00
{
2022-06-02 03:22:00 +03:00
buf = negotiate_write_tlv(buf, contextual, asn_tlv_length(len), NULL);
2022-05-28 01:29:28 +03:00
buf = negotiate_write_tlv(buf, tag, len, value);
return buf;
}
2022-06-02 03:22:00 +03:00
static BYTE* negotiate_read_tlv(BYTE* buf, BYTE* tag, size_t* len, size_t* bytes_remain)
2022-05-28 01:29:28 +03:00
{
2022-06-10 00:48:53 +03:00
UINT len_bytes = 0;
2022-05-28 01:29:28 +03:00
*len = 0;
2022-05-31 04:14:32 +03:00
if (*bytes_remain < 2)
return NULL;
2022-05-28 01:29:28 +03:00
*bytes_remain -= 2;
*tag = *buf++;
if (*buf <= 0x7F)
*len = *buf++;
2017-05-11 19:51:45 +03:00
else
{
2022-05-28 01:29:28 +03:00
len_bytes = *buf++ & 0x7F;
2022-05-31 04:14:32 +03:00
if (*bytes_remain < len_bytes)
return NULL;
2022-05-28 01:29:28 +03:00
*bytes_remain -= len_bytes;
for (int i = len_bytes - 1; i >= 0; i--)
*len |= *buf++ << (i * 8);
2017-05-11 19:51:45 +03:00
}
2022-05-28 01:29:28 +03:00
return buf;
2017-05-11 19:51:45 +03:00
}
2022-05-28 01:29:28 +03:00
static NEGOTIATE_CONTEXT* negotiate_ContextNew(NEGOTIATE_CONTEXT* init_context)
{
2022-05-28 01:29:28 +03:00
NEGOTIATE_CONTEXT* context = NULL;
2022-06-24 16:03:55 +03:00
WINPR_ASSERT(init_context);
2022-05-28 01:29:28 +03:00
context = calloc(1, sizeof(NEGOTIATE_CONTEXT));
if (!context)
return NULL;
2022-05-28 01:29:28 +03:00
if (init_context->spnego)
{
init_context->mechTypes.pvBuffer = malloc(init_context->mechTypes.cbBuffer);
if (!init_context->mechTypes.pvBuffer)
{
free(context);
return NULL;
}
}
2022-06-24 16:03:55 +03:00
*context = *init_context;
2022-05-28 01:29:28 +03:00
return context;
}
static void negotiate_ContextFree(NEGOTIATE_CONTEXT* context)
{
2022-06-24 16:03:55 +03:00
WINPR_ASSERT(context);
2022-05-28 01:29:28 +03:00
if (context->mechTypes.pvBuffer)
free(context->mechTypes.pvBuffer);
free(context);
}
2022-05-31 04:14:32 +03:00
static BOOL negotiate_oid_compare(const sspi_gss_OID_desc* oid1, const sspi_gss_OID_desc* oid2)
2022-05-28 01:29:28 +03:00
{
2022-06-24 16:03:55 +03:00
WINPR_ASSERT(oid1);
WINPR_ASSERT(oid2);
2022-06-23 08:57:38 +03:00
return (oid1->length == oid2->length) &&
(memcmp(oid1->elements, oid2->elements, oid1->length) == 0);
2022-05-31 04:14:32 +03:00
}
static const char* negotiate_mech_name(const sspi_gss_OID_desc* oid)
{
if (negotiate_oid_compare(oid, &spnego_OID))
2022-05-28 01:29:28 +03:00
return "SPNEGO (1.3.6.1.5.5.2)";
2022-05-31 04:14:32 +03:00
else if (negotiate_oid_compare(oid, &kerberos_OID))
2022-05-28 01:29:28 +03:00
return "Kerberos (1.2.840.113554.1.2.2)";
2022-05-31 04:14:32 +03:00
else if (negotiate_oid_compare(oid, &kerberos_wrong_OID))
return "Kerberos [wrong OID] (1.2.840.48018.1.2.2)";
else if (negotiate_oid_compare(oid, &ntlm_OID))
2022-05-28 01:29:28 +03:00
return "NTLM (1.3.6.1.4.1.311.2.2.10)";
else
return "Unknown mechanism";
}
2022-05-31 04:14:32 +03:00
static const Mech* negotiate_GetMechByOID(sspi_gss_OID_desc oid)
2022-05-28 01:29:28 +03:00
{
2022-05-31 04:14:32 +03:00
if (negotiate_oid_compare(&oid, &kerberos_wrong_OID))
{
oid.length = kerberos_OID.length;
oid.elements = kerberos_OID.elements;
}
2022-06-24 16:03:55 +03:00
for (size_t i = 0; i < MECH_COUNT; i++)
2022-05-28 01:29:28 +03:00
{
2022-05-31 04:14:32 +03:00
if (negotiate_oid_compare(&oid, MechTable[i].oid))
2022-05-28 01:29:28 +03:00
return &MechTable[i];
}
return NULL;
}
static PSecHandle negotiate_FindCredential(MechCred* creds, const Mech* mech)
{
2022-06-24 16:03:55 +03:00
WINPR_ASSERT(creds);
if (!mech)
return NULL;
for (size_t i = 0; i < MECH_COUNT; i++)
2022-05-28 01:29:28 +03:00
{
2022-06-24 16:03:55 +03:00
MechCred* cred = &creds[i];
if (cred->mech == mech)
2022-05-28 01:29:28 +03:00
{
2022-06-24 16:03:55 +03:00
if (cred->valid)
return &cred->cred;
2022-06-02 03:22:00 +03:00
return NULL;
2022-05-28 01:29:28 +03:00
}
}
return NULL;
}
2022-06-11 00:22:02 +03:00
static BOOL negotiate_get_dword(HKEY hKey, const char* subkey, DWORD* pdwValue)
{
DWORD dwValue = 0, dwType = 0;
DWORD dwSize = sizeof(dwValue);
LONG rc = RegQueryValueExA(hKey, subkey, NULL, &dwType, (BYTE*)&dwValue, &dwSize);
if (rc != ERROR_SUCCESS)
return FALSE;
if (dwType != REG_DWORD)
return FALSE;
*pdwValue = dwValue;
return TRUE;
}
static BOOL negotiate_get_config(BOOL* kerberos, BOOL* ntlm)
{
HKEY hKey = NULL;
LONG rc;
WINPR_ASSERT(kerberos);
WINPR_ASSERT(ntlm);
#if !defined(WITH_GSS_NO_NTLM_FALLBACK)
*ntlm = TRUE;
#else
*ntlm = FALSE;
#endif
*kerberos = TRUE;
rc = RegOpenKeyExA(HKEY_LOCAL_MACHINE, NEGO_REG_KEY, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
if (rc == ERROR_SUCCESS)
{
DWORD dwValue;
2022-06-11 00:22:02 +03:00
if (negotiate_get_dword(hKey, "kerberos", &dwValue))
*kerberos = (dwValue != 0) ? TRUE : FALSE;
#if !defined(WITH_GSS_NO_NTLM_FALLBACK)
2022-06-11 00:22:02 +03:00
if (negotiate_get_dword(hKey, "ntlm", &dwValue))
*ntlm = (dwValue != 0) ? TRUE : FALSE;
#endif
RegCloseKey(hKey);
}
return TRUE;
}
2022-06-23 08:57:38 +03:00
static BOOL negotiate_write_neg_token(PSecBuffer output_buffer, NegToken* token)
{
2022-06-02 03:22:00 +03:00
size_t token_len = 0;
size_t init_token_len = 0;
size_t inner_token_len = 0;
size_t total_len = 0;
2022-06-24 16:03:55 +03:00
WINPR_ASSERT(output_buffer);
WINPR_ASSERT(token);
2022-05-28 01:29:28 +03:00
BYTE* p = output_buffer->pvBuffer;
2022-06-29 15:20:38 +03:00
BYTE *mech_offset = NULL, *mic_offset = NULL;
2022-05-28 01:29:28 +03:00
/* Length of [0] MechTypeList (SEQUENCE tag already included in buffer) */
2022-06-02 03:22:00 +03:00
if (token->init)
inner_token_len += asn_tlv_length(token->mechTypes.cbBuffer);
2022-05-28 01:29:28 +03:00
/* Lenght of negState [0] ENUMERATED */
2022-06-02 03:22:00 +03:00
if (token->negState != NOSTATE)
inner_token_len += asn_contextual_length(1);
2022-05-28 01:29:28 +03:00
/* Length of supportedMech [1] OID */
2022-06-02 03:22:00 +03:00
if (token->supportedMech.length)
inner_token_len += asn_contextual_length(token->supportedMech.length);
2022-05-28 01:29:28 +03:00
/* Length of [2] OCTET STRING */
2022-06-02 03:22:00 +03:00
if (token->mechToken.cbBuffer)
2022-05-28 01:29:28 +03:00
{
2022-06-02 03:22:00 +03:00
inner_token_len += asn_contextual_length(token->mechToken.cbBuffer);
mech_offset = p + inner_token_len - token->mechToken.cbBuffer;
2022-05-28 01:29:28 +03:00
}
2022-05-28 01:29:28 +03:00
/* Length of [3] OCTET S */
2022-06-02 03:22:00 +03:00
if (token->mic.cbBuffer)
2022-05-28 01:29:28 +03:00
{
2022-06-02 03:22:00 +03:00
inner_token_len += asn_contextual_length(token->mic.cbBuffer);
mic_offset = p + inner_token_len - token->mic.cbBuffer;
2022-05-28 01:29:28 +03:00
}
2022-05-28 01:29:28 +03:00
/* Length of [0] NegTokenInit | [1] NegTokenResp */
2022-06-02 03:22:00 +03:00
token_len += asn_contextual_length(inner_token_len);
2022-05-28 01:29:28 +03:00
total_len = token_len;
2022-06-02 03:22:00 +03:00
if (token->init)
{
2022-05-28 01:29:28 +03:00
/* Length of MechType OID */
2022-06-02 03:22:00 +03:00
init_token_len = total_len + asn_tlv_length(spnego_OID.length);
2022-05-28 01:29:28 +03:00
/* Length of initialContextToken */
2022-06-02 03:22:00 +03:00
total_len = asn_tlv_length(init_token_len);
2022-05-28 01:29:28 +03:00
}
2022-05-28 01:29:28 +03:00
/* Adjust token offsets */
mech_offset += total_len - inner_token_len;
mic_offset += total_len - inner_token_len;
if (output_buffer->cbBuffer < total_len)
return FALSE;
output_buffer->cbBuffer = total_len;
/* Write the tokens stored in the buffer first so as not to be overwritten */
2022-06-02 03:22:00 +03:00
if (token->mic.cbBuffer)
MoveMemory(mic_offset, token->mic.pvBuffer, token->mic.cbBuffer);
2022-05-28 01:29:28 +03:00
2022-06-02 03:22:00 +03:00
if (token->mechToken.cbBuffer)
MoveMemory(mech_offset, token->mechToken.pvBuffer, token->mechToken.cbBuffer);
2022-05-28 01:29:28 +03:00
/* For NegTokenInit wrap in an initialContextToken */
2022-06-02 03:22:00 +03:00
if (token->init)
2022-05-28 01:29:28 +03:00
{
/* InitialContextToken [APPLICATION 0] IMPLICIT SEQUENCE */
p = negotiate_write_tlv(p, 0x60, init_token_len, NULL);
/* thisMech MechType OID */
p = negotiate_write_tlv(p, 0x06, spnego_OID.length, spnego_OID.elements);
}
2022-05-28 01:29:28 +03:00
/* innerContextToken [0] NegTokenInit or [1] NegTokenResp */
2022-06-02 03:22:00 +03:00
p = negotiate_write_contextual_tlv(p, token->init ? 0xA0 : 0xA1, 0x30, inner_token_len, NULL);
WLog_DBG(TAG, token->init ? "Writing negTokenInit..." : "Writing negTokenResp...");
2022-05-28 01:29:28 +03:00
/* mechTypes [0] MechTypeList (mechTypes already contains the SEQUENCE tag) */
2022-06-02 03:22:00 +03:00
if (token->init)
2017-05-11 19:51:45 +03:00
{
2022-06-02 03:22:00 +03:00
p = negotiate_write_tlv(p, 0xA0, token->mechTypes.cbBuffer, token->mechTypes.pvBuffer);
WLog_DBG(TAG, "\tmechTypes [0] (%li bytes)", token->mechTypes.cbBuffer);
2022-05-28 01:29:28 +03:00
}
/* negState [0] ENUMERATED */
2022-06-02 03:22:00 +03:00
else if (token->negState != NOSTATE)
2022-05-28 01:29:28 +03:00
{
2022-06-02 03:22:00 +03:00
p = negotiate_write_contextual_tlv(p, 0xA0, 0x0A, 1, (BYTE*)&token->negState);
WLog_DBG(TAG, "\tnegState [0] (%d)", token->negState);
2022-05-28 01:29:28 +03:00
}
2017-05-11 19:51:45 +03:00
2022-05-28 01:29:28 +03:00
/* supportedMech [1] OID */
2022-06-02 03:22:00 +03:00
if (token->supportedMech.length)
2022-05-28 01:29:28 +03:00
{
2022-06-02 03:22:00 +03:00
p = negotiate_write_contextual_tlv(p, 0xA1, 0x06, token->supportedMech.length,
token->supportedMech.elements);
WLog_DBG(TAG, "\tsupportedMech [1] (%s)", negotiate_mech_name(&token->supportedMech));
2022-05-28 01:29:28 +03:00
}
2017-05-11 19:51:45 +03:00
2022-05-28 01:29:28 +03:00
/* mechToken [2] OCTET STRING */
2022-06-02 03:22:00 +03:00
if (token->mechToken.cbBuffer)
2022-05-28 01:29:28 +03:00
{
2022-06-02 03:22:00 +03:00
p = negotiate_write_contextual_tlv(p, 0xA2, 0x04, token->mechToken.cbBuffer, NULL);
p += token->mechToken.cbBuffer;
WLog_DBG(TAG, "\tmechToken [2] (%li bytes)", token->mechToken.cbBuffer);
2017-05-11 19:51:45 +03:00
}
2022-05-28 01:29:28 +03:00
/* mechListMIC [3] OCTET STRING */
2022-06-02 03:22:00 +03:00
if (token->mic.cbBuffer)
2017-05-11 19:51:45 +03:00
{
2022-06-02 03:22:00 +03:00
p = negotiate_write_contextual_tlv(p, 0xA3, 0x04, token->mic.cbBuffer, NULL);
p += token->mic.cbBuffer;
WLog_DBG(TAG, "\tmechListMIC [3] (%li bytes)", token->mic.cbBuffer);
2022-05-28 01:29:28 +03:00
}
return TRUE;
}
static BOOL negotiate_read_neg_token(PSecBuffer input, NegToken* token)
{
BYTE tag;
BYTE contextual;
2022-06-02 03:22:00 +03:00
size_t len;
2022-05-28 01:29:28 +03:00
BYTE* p;
2022-06-24 16:03:55 +03:00
WINPR_ASSERT(input);
WINPR_ASSERT(token);
BYTE* buf = input->pvBuffer;
2022-06-02 03:22:00 +03:00
size_t bytes_remain = input->cbBuffer;
2022-05-28 01:29:28 +03:00
if (token->init)
{
/* initContextToken */
buf = negotiate_read_tlv(buf, &tag, &len, &bytes_remain);
2022-05-31 04:14:32 +03:00
if (!buf || len > bytes_remain || tag != 0x60)
2022-05-28 01:29:28 +03:00
return FALSE;
/* thisMech */
buf = negotiate_read_tlv(buf, &tag, &len, &bytes_remain);
2022-05-31 04:14:32 +03:00
if (!buf || len > bytes_remain || tag != 0x06)
2022-05-28 01:29:28 +03:00
return FALSE;
buf += len;
bytes_remain -= len;
}
/* [0] NegTokenInit or [1] NegTokenResp */
buf = negotiate_read_tlv(buf, &contextual, &len, &bytes_remain);
2022-05-31 04:14:32 +03:00
if (!buf)
return FALSE;
2022-05-28 01:29:28 +03:00
buf = negotiate_read_tlv(buf, &tag, &len, &bytes_remain);
2022-05-31 04:14:32 +03:00
if (!buf || len > bytes_remain)
2022-05-28 01:29:28 +03:00
return FALSE;
else if (contextual == 0xA0 && tag == 0x30)
token->init = TRUE;
else if (contextual == 0xA1 && tag == 0x30)
token->init = FALSE;
else
return FALSE;
WLog_DBG(TAG, token->init ? "Reading negTokenInit..." : "Reading negTokenResp...");
/* Read NegTokenResp sequence members */
do
{
p = negotiate_read_tlv(buf, &contextual, &len, &bytes_remain);
2022-05-31 04:14:32 +03:00
if (!p)
return FALSE;
2022-05-28 01:29:28 +03:00
buf = negotiate_read_tlv(p, &tag, &len, &bytes_remain);
2022-05-31 04:14:32 +03:00
if (!buf || len > bytes_remain)
2022-05-28 01:29:28 +03:00
return FALSE;
switch (contextual)
2017-05-11 19:51:45 +03:00
{
2022-05-28 01:29:28 +03:00
case 0xA0:
/* mechTypes [0] MechTypeList */
if (tag == 0x30 && token->init)
{
token->mechTypes.pvBuffer = p;
2022-06-02 03:22:00 +03:00
token->mechTypes.cbBuffer = asn_tlv_length(len);
2022-05-28 01:29:28 +03:00
token->mechTypes.BufferType = SECBUFFER_DATA;
WLog_DBG(TAG, "\tmechTypes [0] (%li bytes)", len);
}
/* negState [0] ENUMERATED */
else if (tag == 0x0A && len == 1 && !token->init)
{
token->negState = *buf;
WLog_DBG(TAG, "\tnegState [0] (%d)", token->negState);
}
else
return FALSE;
break;
case 0xA1:
/* reqFlags [1] ContextFlags BIT STRING (ignored) */
if (tag == 0x03 && token->init)
{
WLog_DBG(TAG, "\treqFlags [1] (%li bytes)", len);
}
/* supportedMech [1] MechType */
else if (tag == 0x06 && !token->init)
{
2022-05-31 04:14:32 +03:00
token->supportedMech.length = len;
token->supportedMech.elements = buf;
2022-05-28 01:29:28 +03:00
WLog_DBG(TAG, "\tsupportedMech [1] (%s)",
2022-05-31 04:14:32 +03:00
negotiate_mech_name(&token->supportedMech));
2022-05-28 01:29:28 +03:00
}
else
return FALSE;
break;
case 0xA2:
/* mechToken [2] OCTET STRING */
if (tag != 0x04)
return FALSE;
token->mechToken.cbBuffer = len;
token->mechToken.pvBuffer = buf;
token->mechToken.BufferType = SECBUFFER_TOKEN;
WLog_DBG(TAG, "\tmechToken [2] (%li bytes)", len);
break;
case 0xA3:
/* mechListMic [3] OCTET STRING */
if (tag != 0x04)
return FALSE;
token->mic.cbBuffer = len;
token->mic.pvBuffer = buf;
token->mic.BufferType = SECBUFFER_TOKEN;
WLog_DBG(TAG, "\tmechListMIC [3] (%li bytes)", len);
break;
default:
return FALSE;
2017-05-11 19:51:45 +03:00
}
2022-05-28 01:29:28 +03:00
buf += len;
bytes_remain -= len;
} while (bytes_remain > 0);
if (bytes_remain < 0)
return FALSE;
return TRUE;
}
static SECURITY_STATUS negotiate_mic_exchange(NEGOTIATE_CONTEXT* context, NegToken* input_token,
NegToken* output_token, PSecBuffer output_buffer)
{
2022-06-24 16:03:55 +03:00
SecBuffer mic_buffers[2] = { 0 };
2022-05-28 01:29:28 +03:00
SecBufferDesc mic_buffer_desc = { SECBUFFER_VERSION, 2, mic_buffers };
SECURITY_STATUS status;
2022-06-24 16:03:55 +03:00
WINPR_ASSERT(context);
WINPR_ASSERT(input_token);
WINPR_ASSERT(output_token);
WINPR_ASSERT(context->mech);
WINPR_ASSERT(context->mech->pkg);
2022-06-11 00:22:02 +03:00
const SecurityFunctionTableA* table = context->mech->pkg->table;
2022-06-24 16:03:55 +03:00
WINPR_ASSERT(table);
2022-05-28 01:29:28 +03:00
2022-06-24 16:03:55 +03:00
mic_buffers[0] = context->mechTypes;
2017-05-11 19:51:45 +03:00
2022-05-28 01:29:28 +03:00
/* Verify MIC if we received one */
if (input_token->mic.cbBuffer > 0)
{
2022-06-24 16:03:55 +03:00
mic_buffers[1] = input_token->mic;
2022-05-28 01:29:28 +03:00
2022-06-11 00:22:02 +03:00
status = table->VerifySignature(&context->sub_context, &mic_buffer_desc, 0, 0);
2022-05-28 01:29:28 +03:00
if (status != SEC_E_OK)
return status;
output_token->negState = ACCEPT_COMPLETED;
2017-05-11 19:51:45 +03:00
}
2022-05-28 01:29:28 +03:00
/* If peer expects a MIC then generate it */
if (input_token->negState != ACCEPT_COMPLETED)
{
/* Store the mic token after the mech token in the output buffer */
output_token->mic.BufferType = SECBUFFER_TOKEN;
output_token->mic.cbBuffer = output_buffer->cbBuffer - output_token->mechToken.cbBuffer;
2022-06-23 08:57:38 +03:00
output_token->mic.pvBuffer =
(BYTE*)output_buffer->pvBuffer + output_token->mechToken.cbBuffer;
2022-05-28 01:29:28 +03:00
2022-06-24 16:03:55 +03:00
mic_buffers[1] = output_token->mic;
2022-05-28 01:29:28 +03:00
2022-06-11 00:22:02 +03:00
status = table->MakeSignature(&context->sub_context, 0, &mic_buffer_desc, 0);
2022-05-28 01:29:28 +03:00
if (status != SEC_E_OK)
return status;
2022-06-24 16:03:55 +03:00
output_token->mic = mic_buffers[1];
2022-05-28 01:29:28 +03:00
}
/* When using NTLM cipher states need to be reset after mic exchange */
if (_tcscmp(sspi_SecureHandleGetUpperPointer(&context->sub_context), NTLM_SSP_NAME) == 0)
ntlm_reset_cipher_state(&context->sub_context);
return SEC_E_OK;
}
2022-05-28 01:29:28 +03:00
static SECURITY_STATUS SEC_ENTRY negotiate_InitializeSecurityContextW(
PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR* pszTargetName, ULONG fContextReq,
2019-11-06 17:24:51 +03:00
ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2,
PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry)
{
NEGOTIATE_CONTEXT* context = NULL;
2022-05-28 01:29:28 +03:00
NEGOTIATE_CONTEXT init_context = { 0 };
MechCred* creds;
PCtxtHandle sub_context = NULL;
PCredHandle sub_cred = NULL;
NegToken input_token = { NOSTATE, 0 };
NegToken output_token = { NOSTATE, 0 };
PSecBuffer input_buffer = NULL;
PSecBuffer output_buffer = NULL;
SecBufferDesc mech_input = { SECBUFFER_VERSION, 1, &input_token.mechToken };
SecBufferDesc mech_output = { SECBUFFER_VERSION, 1, &output_token.mechToken };
2022-06-29 15:20:38 +03:00
SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
2022-06-02 03:22:00 +03:00
size_t inner_mech_list_len = 0;
2022-05-28 01:29:28 +03:00
BYTE* p;
2022-05-31 04:14:32 +03:00
const Mech* mech;
2022-05-28 01:29:28 +03:00
if (!phCredential || !SecIsValidHandle(phCredential))
return SEC_E_NO_CREDENTIALS;
2022-05-28 01:29:28 +03:00
context = sspi_SecureHandleGetLowerPointer(phContext);
creds = sspi_SecureHandleGetLowerPointer(phCredential);
if (pInput)
input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
if (pOutput)
output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
if (!context)
{
2022-06-24 16:03:55 +03:00
for (size_t i = 0; i < MECH_COUNT; i++)
2022-05-28 01:29:28 +03:00
{
2022-06-24 16:03:55 +03:00
MechCred* cred = &creds[i];
if (!cred->valid)
2022-05-28 01:29:28 +03:00
continue;
2022-06-24 16:03:55 +03:00
inner_mech_list_len += asn_tlv_length(cred->mech->oid->length);
2022-05-28 01:29:28 +03:00
if (init_context.mech) /* We already have an optimistic mechanism */
continue;
/* Use the output buffer to store the optimistic token */
CopyMemory(&output_token.mechToken, output_buffer, sizeof(SecBuffer));
status = MechTable[i].pkg->table_w->InitializeSecurityContextW(
2022-06-24 16:03:55 +03:00
&cred->cred, NULL, pszTargetName, fContextReq | cred->mech->flags, Reserved1,
2022-06-23 08:57:38 +03:00
TargetDataRep, NULL, Reserved2, &init_context.sub_context, &mech_output,
2022-05-28 01:29:28 +03:00
pfContextAttr, ptsExpiry);
2022-05-28 01:29:28 +03:00
/* If the mechanism failed we can't use it; skip */
if (IsSecurityStatusError(status))
2022-06-24 16:03:55 +03:00
cred->valid = FALSE;
2022-05-28 01:29:28 +03:00
else
2022-06-24 16:03:55 +03:00
init_context.mech = cred->mech;
2022-05-28 01:29:28 +03:00
}
/* No usable mechanisms were found */
if (!init_context.mech)
return status;
#ifdef WITH_SPNEGO
/* If the only available mech is NTLM use it directly otherwise use spnego */
if (init_context.mech->oid == &ntlm_OID)
{
init_context.spnego = FALSE;
output_buffer->cbBuffer = output_token.mechToken.cbBuffer;
WLog_DBG(TAG, "Using direct NTLM");
}
else
{
init_context.spnego = TRUE;
init_context.mechTypes.BufferType = SECBUFFER_DATA;
2022-06-02 03:22:00 +03:00
init_context.mechTypes.cbBuffer = asn_tlv_length(inner_mech_list_len);
2022-05-28 01:29:28 +03:00
}
#else
init_context.spnego = FALSE;
output_buffer->cbBuffer = output_token.mechToken.cbBuffer;
#endif
/* Allocate memory for the new context */
context = negotiate_ContextNew(&init_context);
if (!context)
2022-05-28 01:29:28 +03:00
{
init_context.mech->pkg->table->DeleteSecurityContext(&init_context.sub_context);
return SEC_E_INSUFFICIENT_MEMORY;
}
2022-05-28 01:29:28 +03:00
sspi_SecureHandleSetUpperPointer(phNewContext, NEGO_SSP_NAME);
sspi_SecureHandleSetLowerPointer(phNewContext, context);
2022-05-28 01:29:28 +03:00
if (!context->spnego)
return status;
/* Write the SEQUENCE tag */
p = negotiate_write_tlv(context->mechTypes.pvBuffer, 0x30, inner_mech_list_len, NULL);
/* Write each enabled mechanism */
2022-06-24 16:03:55 +03:00
for (size_t i = 0; i < MECH_COUNT; i++)
2022-05-28 01:29:28 +03:00
{
2022-06-24 16:03:55 +03:00
MechCred* cred = &creds[i];
if (cred->valid)
2022-05-28 01:29:28 +03:00
{
2022-06-24 16:03:55 +03:00
p = negotiate_write_tlv(p, 0x06, cred->mech->oid->length,
cred->mech->oid->elements);
WLog_DBG(TAG, "Available mechanism: %s", negotiate_mech_name(cred->mech->oid));
2022-05-28 01:29:28 +03:00
}
}
output_token.mechTypes.cbBuffer = context->mechTypes.cbBuffer;
output_token.mechTypes.pvBuffer = context->mechTypes.pvBuffer;
output_token.init = TRUE;
}
else
2017-05-11 19:51:45 +03:00
{
2022-05-28 01:29:28 +03:00
if (!input_buffer)
return SEC_E_INVALID_TOKEN;
sub_context = &context->sub_context;
sub_cred = negotiate_FindCredential(creds, init_context.mech);
if (!context->spnego)
2017-05-11 19:51:45 +03:00
{
2022-05-28 01:29:28 +03:00
return context->mech->pkg->table_w->InitializeSecurityContextW(
sub_cred, sub_context, pszTargetName, fContextReq, Reserved1, TargetDataRep, pInput,
Reserved2, sub_context, pOutput, pfContextAttr, ptsExpiry);
2017-05-11 19:51:45 +03:00
}
2022-05-28 01:29:28 +03:00
if (!negotiate_read_neg_token(input_buffer, &input_token))
return SEC_E_INVALID_TOKEN;
2017-05-11 19:51:45 +03:00
2022-05-28 01:29:28 +03:00
/* On first response check if the server doesn't like out prefered mech */
2022-05-31 04:14:32 +03:00
if (context->state == NEGOTIATE_STATE_INITIAL && input_token.supportedMech.length &&
!negotiate_oid_compare(&input_token.supportedMech, context->mech->oid))
2017-05-11 19:51:45 +03:00
{
2022-05-31 04:14:32 +03:00
mech = negotiate_GetMechByOID(input_token.supportedMech);
if (!mech)
return SEC_E_INVALID_TOKEN;
2022-05-28 01:29:28 +03:00
/* Make sure the specified mech is supported and get the appropriate credential */
2022-05-31 04:14:32 +03:00
sub_cred = negotiate_FindCredential(creds, mech);
2022-05-28 01:29:28 +03:00
if (!sub_cred)
return SEC_E_INVALID_TOKEN;
/* Clean up the optimistic mech */
context->mech->pkg->table_w->DeleteSecurityContext(&context->sub_context);
sub_context = NULL;
2022-05-31 04:14:32 +03:00
context->mech = mech;
2022-05-28 01:29:28 +03:00
context->mic = TRUE;
2017-05-11 19:51:45 +03:00
}
2022-05-28 01:29:28 +03:00
/* Check neg_state (required on first response) */
if (context->state == NEGOTIATE_STATE_INITIAL)
2017-05-11 19:51:45 +03:00
{
2022-05-28 01:29:28 +03:00
switch (input_token.negState)
{
case NOSTATE:
return SEC_E_INVALID_TOKEN;
case REJECT:
return SEC_E_LOGON_DENIED;
case REQUEST_MIC:
context->mic = TRUE; /* fallthrough */
case ACCEPT_INCOMPLETE:
case ACCEPT_COMPLETED:
context->state = NEGOTIATE_STATE_NEGORESP;
break;
}
2022-05-31 04:14:32 +03:00
WLog_DBG(TAG, "Negotiated mechanism: %s", negotiate_mech_name(context->mech->oid));
2022-05-28 01:29:28 +03:00
}
if (context->state == NEGOTIATE_STATE_NEGORESP)
{
/* Store the mech token in the output buffer */
CopyMemory(&output_token.mechToken, output_buffer, sizeof(SecBuffer));
status = context->mech->pkg->table_w->InitializeSecurityContextW(
2022-06-23 08:57:38 +03:00
sub_cred, sub_context, pszTargetName, fContextReq | context->mech->flags, Reserved1,
TargetDataRep, input_token.mechToken.cbBuffer ? &mech_input : NULL, Reserved2,
&context->sub_context, &mech_output, pfContextAttr, ptsExpiry);
2022-05-28 01:29:28 +03:00
if (IsSecurityStatusError(status))
return status;
2017-05-11 19:51:45 +03:00
}
2022-05-28 01:29:28 +03:00
if (status == SEC_E_OK)
{
if (output_token.mechToken.cbBuffer > 0)
context->state = NEGOTIATE_STATE_MIC;
else
context->state = NEGOTIATE_STATE_FINAL;
}
/* Check if the acceptor sent its final token without a mic */
if (context->state == NEGOTIATE_STATE_FINAL && input_token.mic.cbBuffer == 0)
{
if (context->mic || input_token.negState != ACCEPT_COMPLETED)
return SEC_E_INVALID_TOKEN;
2022-06-02 03:22:00 +03:00
output_buffer->cbBuffer = 0;
return SEC_E_OK;
2022-05-28 01:29:28 +03:00
}
if ((context->state == NEGOTIATE_STATE_MIC && context->mic) ||
context->state == NEGOTIATE_STATE_FINAL)
{
status = negotiate_mic_exchange(context, &input_token, &output_token, output_buffer);
if (status != SEC_E_OK)
return status;
}
}
if (input_token.negState == ACCEPT_COMPLETED)
{
output_buffer->cbBuffer = 0;
return SEC_E_OK;
}
if (output_token.negState == ACCEPT_COMPLETED)
status = SEC_E_OK;
else
status = SEC_I_CONTINUE_NEEDED;
2022-06-02 03:22:00 +03:00
if (!negotiate_write_neg_token(output_buffer, &output_token))
2022-05-28 01:29:28 +03:00
status = SEC_E_INTERNAL_ERROR;
return status;
}
static SECURITY_STATUS SEC_ENTRY negotiate_InitializeSecurityContextA(
PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR* pszTargetName, ULONG fContextReq,
ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2,
PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry)
{
SECURITY_STATUS status;
SEC_WCHAR* pszTargetNameW = NULL;
if (pszTargetName)
{
if (ConvertToUnicode(CP_UTF8, 0, pszTargetName, -1, &pszTargetNameW, 0) <= 0)
return SEC_E_INTERNAL_ERROR;
2017-05-11 19:51:45 +03:00
}
2022-05-28 01:29:28 +03:00
status = negotiate_InitializeSecurityContextW(
phCredential, phContext, pszTargetNameW, fContextReq, Reserved1, TargetDataRep, pInput,
Reserved2, phNewContext, pOutput, pfContextAttr, ptsExpiry);
free(pszTargetNameW);
return status;
}
2019-11-06 17:24:51 +03:00
static SECURITY_STATUS SEC_ENTRY negotiate_AcceptSecurityContext(
PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput, ULONG fContextReq,
ULONG TargetDataRep, PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr,
PTimeStamp ptsTimeStamp)
{
2022-05-28 01:29:28 +03:00
NEGOTIATE_CONTEXT* context = NULL;
NEGOTIATE_CONTEXT init_context = { 0 };
MechCred* creds;
PCredHandle sub_cred = NULL;
NegToken input_token = { NOSTATE, 0 };
NegToken output_token = { NOSTATE, 0 };
PSecBuffer input_buffer = NULL;
PSecBuffer output_buffer = NULL;
SecBufferDesc mech_input = { SECBUFFER_VERSION, 1, &input_token.mechToken };
SecBufferDesc mech_output = { SECBUFFER_VERSION, 1, &output_token.mechToken };
SECURITY_STATUS status;
2022-06-29 15:18:28 +03:00
BYTE *p = NULL, tag;
size_t bytes_remain = 0, len;
2022-05-31 04:14:32 +03:00
sspi_gss_OID_desc oid = { 0 };
2022-06-07 06:30:42 +03:00
const Mech* first_mech = NULL;
2022-05-28 01:29:28 +03:00
if (!phCredential || !SecIsValidHandle(phCredential))
return SEC_E_NO_CREDENTIALS;
if (!pInput)
return SEC_E_INVALID_TOKEN;
context = sspi_SecureHandleGetLowerPointer(phContext);
creds = sspi_SecureHandleGetLowerPointer(phCredential);
input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
if (pOutput)
output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
if (!context)
{
2022-05-28 01:29:28 +03:00
/* Check for NTLM token */
2022-06-02 03:22:00 +03:00
if (input_buffer->cbBuffer >= 8 && strncmp(input_buffer->pvBuffer, "NTLMSSP", 8) == 0)
2022-05-28 01:29:28 +03:00
{
init_context.mech = negotiate_GetMechByOID(ntlm_OID);
}
else
{
/* Read initialContextToken */
bytes_remain = input_buffer->cbBuffer;
p = negotiate_read_tlv(input_buffer->pvBuffer, &tag, &len, &bytes_remain);
2022-05-31 04:14:32 +03:00
if (!p || len > bytes_remain || tag != 0x60)
2022-05-28 01:29:28 +03:00
return SEC_E_INVALID_TOKEN;
/* Read thisMech */
p = negotiate_read_tlv(p, &tag, &len, &bytes_remain);
2022-05-31 04:14:32 +03:00
if (!p || len > bytes_remain || tag != 0x06)
2022-05-28 01:29:28 +03:00
return SEC_E_INVALID_TOKEN;
2022-05-31 04:14:32 +03:00
oid.length = len;
oid.elements = p;
2022-05-28 01:29:28 +03:00
/* Check if it's a spnego token */
2022-05-31 04:14:32 +03:00
if (negotiate_oid_compare(&oid, &spnego_OID))
2022-05-28 01:29:28 +03:00
{
init_context.spnego = TRUE;
}
else
{
2022-05-31 04:14:32 +03:00
init_context.mech = negotiate_GetMechByOID(oid);
2022-05-28 01:29:28 +03:00
if (!init_context.mech)
return SEC_E_INVALID_TOKEN;
}
}
2022-05-31 04:14:32 +03:00
WLog_DBG(TAG, "Mechanism: %s", negotiate_mech_name(&oid));
2022-05-28 01:29:28 +03:00
if (init_context.spnego)
{
/* Process spnego token */
input_token.init = TRUE;
if (!negotiate_read_neg_token(input_buffer, &input_token))
return SEC_E_INVALID_TOKEN;
/* First token must be negoTokenInit and must contain a mechList */
if (!input_token.init || input_token.mechTypes.cbBuffer == 0)
return SEC_E_INVALID_TOKEN;
init_context.mechTypes.BufferType = SECBUFFER_DATA;
init_context.mechTypes.cbBuffer = input_token.mechTypes.cbBuffer;
2022-05-31 04:14:32 +03:00
/* Prepare to read mechList */
bytes_remain = input_token.mechTypes.cbBuffer;
p = negotiate_read_tlv(input_token.mechTypes.pvBuffer, &tag, &len, &bytes_remain);
if (!p || len > bytes_remain || tag != 0x30)
return SEC_E_INVALID_TOKEN;
2022-05-28 01:29:28 +03:00
p = negotiate_read_tlv(p, &tag, &len, &bytes_remain);
2022-05-31 04:14:32 +03:00
if (!p || len > bytes_remain || tag != 0x06)
2022-05-28 01:29:28 +03:00
return SEC_E_INVALID_TOKEN;
2022-06-23 08:57:38 +03:00
2022-05-31 04:14:32 +03:00
oid.length = len;
oid.elements = p;
2022-06-07 06:30:42 +03:00
p += len;
bytes_remain -= len;
2022-05-28 01:29:28 +03:00
2022-05-31 04:14:32 +03:00
init_context.mech = negotiate_GetMechByOID(oid);
if (init_context.mech)
sub_cred = negotiate_FindCredential(creds, init_context.mech);
2022-05-28 01:29:28 +03:00
if (sub_cred)
{
/* Use the output buffer to store the optimistic token */
CopyMemory(&output_token.mechToken, output_buffer, sizeof(SecBuffer));
status = init_context.mech->pkg->table->AcceptSecurityContext(
sub_cred, NULL, &mech_input, fContextReq, TargetDataRep,
&init_context.sub_context, &mech_output, pfContextAttr, ptsTimeStamp);
}
else
{
status = SEC_E_NO_CREDENTIALS;
}
2022-06-23 08:57:38 +03:00
WLog_DBG(TAG, "Initiators preferred mechanism: %s", negotiate_mech_name(&oid));
2022-05-28 01:29:28 +03:00
}
else
{
sub_cred = negotiate_FindCredential(creds, init_context.mech);
status = init_context.mech->pkg->table->AcceptSecurityContext(
sub_cred, NULL, pInput, fContextReq, TargetDataRep, &init_context.sub_context,
pOutput, pfContextAttr, ptsTimeStamp);
}
if (IsSecurityStatusError(status))
{
if (!init_context.spnego)
return status;
init_context.mic = TRUE;
2022-06-07 06:30:42 +03:00
first_mech = init_context.mech;
2022-05-28 01:29:28 +03:00
init_context.mech = NULL;
2022-06-07 06:30:42 +03:00
output_token.mechToken.cbBuffer = 0;
2022-05-28 01:29:28 +03:00
}
while (!init_context.mech && bytes_remain > 0)
{
/* Read each mechanism */
p = negotiate_read_tlv(p, &tag, &len, &bytes_remain);
2022-05-31 04:14:32 +03:00
if (!p || len > bytes_remain || tag != 0x06)
2022-05-28 01:29:28 +03:00
return SEC_E_INVALID_TOKEN;
2022-05-31 04:14:32 +03:00
oid.length = len;
oid.elements = p;
2022-06-07 06:30:42 +03:00
p += len;
bytes_remain -= len;
2022-05-31 04:14:32 +03:00
init_context.mech = negotiate_GetMechByOID(oid);
2022-06-07 06:30:42 +03:00
/* Microsoft may send two versions of the kerberos OID */
if (init_context.mech == first_mech)
init_context.mech = NULL;
2022-05-31 04:14:32 +03:00
if (init_context.mech && !negotiate_FindCredential(creds, init_context.mech))
2022-05-28 01:29:28 +03:00
init_context.mech = NULL;
}
if (!init_context.mech)
return SEC_E_INTERNAL_ERROR;
2022-05-28 01:29:28 +03:00
context = negotiate_ContextNew(&init_context);
if (!context)
{
if (!IsSecurityStatusError(status))
init_context.mech->pkg->table->DeleteSecurityContext(&init_context.sub_context);
return SEC_E_INSUFFICIENT_MEMORY;
}
sspi_SecureHandleSetUpperPointer(phNewContext, NEGO_SSP_NAME);
sspi_SecureHandleSetLowerPointer(phNewContext, context);
2022-05-28 01:29:28 +03:00
if (!init_context.spnego)
return status;
CopyMemory(init_context.mechTypes.pvBuffer, input_token.mechTypes.pvBuffer,
input_token.mechTypes.cbBuffer);
2022-06-07 06:30:42 +03:00
if (!context->mech->preferred)
2022-05-28 01:29:28 +03:00
{
2022-06-07 06:30:42 +03:00
output_token.negState = REQUEST_MIC;
context->mic = TRUE;
}
else
{
output_token.negState = ACCEPT_INCOMPLETE;
2022-05-28 01:29:28 +03:00
}
2022-06-07 06:30:42 +03:00
if (status == SEC_E_OK)
context->state = NEGOTIATE_STATE_FINAL;
else
context->state = NEGOTIATE_STATE_NEGORESP;
2022-05-31 04:14:32 +03:00
output_token.supportedMech.length = oid.length;
output_token.supportedMech.elements = oid.elements;
WLog_DBG(TAG, "Accepted mechanism: %s", negotiate_mech_name(&output_token.supportedMech));
2022-05-28 01:29:28 +03:00
}
else
{
sub_cred = negotiate_FindCredential(creds, context->mech);
if (!sub_cred)
return SEC_E_NO_CREDENTIALS;
if (!context->spnego)
{
return context->mech->pkg->table->AcceptSecurityContext(
sub_cred, &context->sub_context, pInput, fContextReq, TargetDataRep,
&context->sub_context, pOutput, pfContextAttr, ptsTimeStamp);
}
if (!negotiate_read_neg_token(input_buffer, &input_token))
return SEC_E_INVALID_TOKEN;
/* Process the mechanism token */
if (input_token.mechToken.cbBuffer > 0)
{
if (context->state != NEGOTIATE_STATE_NEGORESP)
return SEC_E_INVALID_TOKEN;
/* Use the output buffer to store the optimistic token */
CopyMemory(&output_token.mechToken, output_buffer, sizeof(SecBuffer));
status = context->mech->pkg->table->AcceptSecurityContext(
2022-06-23 08:57:38 +03:00
sub_cred, &context->sub_context, &mech_input, fContextReq | context->mech->flags,
TargetDataRep, &context->sub_context, &mech_output, pfContextAttr, ptsTimeStamp);
2022-05-28 01:29:28 +03:00
if (IsSecurityStatusError(status))
return status;
if (status == SEC_E_OK)
context->state = NEGOTIATE_STATE_FINAL;
}
else if (context->state == NEGOTIATE_STATE_NEGORESP)
return SEC_E_INVALID_TOKEN;
}
2022-05-28 01:29:28 +03:00
if (context->state == NEGOTIATE_STATE_FINAL)
{
/* Check if initiator sent the last mech token without a mic and a mic was required */
if (context->mic && output_token.mechToken.cbBuffer == 0 && input_token.mic.cbBuffer == 0)
return SEC_E_INVALID_TOKEN;
if (context->mic || input_token.mic.cbBuffer > 0)
{
status = negotiate_mic_exchange(context, &input_token, &output_token, output_buffer);
if (status != SEC_E_OK)
return status;
}
else
output_token.negState = ACCEPT_COMPLETED;
}
2022-05-28 01:29:28 +03:00
if (input_token.negState == ACCEPT_COMPLETED)
2015-07-07 18:17:29 +03:00
{
2022-05-28 01:29:28 +03:00
output_buffer->cbBuffer = 0;
return SEC_E_OK;
2015-07-07 18:17:29 +03:00
}
2017-05-11 19:51:45 +03:00
2022-05-28 01:29:28 +03:00
if (output_token.negState == ACCEPT_COMPLETED)
status = SEC_E_OK;
else
status = SEC_I_CONTINUE_NEEDED;
2022-06-02 03:22:00 +03:00
if (!negotiate_write_neg_token(output_buffer, &output_token))
2022-05-28 01:29:28 +03:00
return SEC_E_INTERNAL_ERROR;
2015-07-07 18:17:29 +03:00
return status;
}
static SECURITY_STATUS SEC_ENTRY negotiate_CompleteAuthToken(PCtxtHandle phContext,
2019-11-06 17:24:51 +03:00
PSecBufferDesc pToken)
{
NEGOTIATE_CONTEXT* context;
SECURITY_STATUS status = SEC_E_OK;
2019-11-06 17:24:51 +03:00
context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
2014-06-07 01:20:34 +04:00
if (!context)
return SEC_E_INVALID_HANDLE;
2014-06-07 01:20:34 +04:00
2022-06-24 16:03:55 +03:00
WINPR_ASSERT(context->mech);
WINPR_ASSERT(context->mech->pkg);
WINPR_ASSERT(context->mech->pkg->table);
2022-05-28 01:29:28 +03:00
if (context->mech->pkg->table->CompleteAuthToken)
status = context->mech->pkg->table->CompleteAuthToken(&context->sub_context, pToken);
return status;
}
static SECURITY_STATUS SEC_ENTRY negotiate_DeleteSecurityContext(PCtxtHandle phContext)
{
NEGOTIATE_CONTEXT* context;
SECURITY_STATUS status = SEC_E_OK;
2019-11-06 17:24:51 +03:00
context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
2022-05-28 01:29:28 +03:00
const SecPkg* pkg;
if (!context)
return SEC_E_INVALID_HANDLE;
2022-06-24 16:03:55 +03:00
WINPR_ASSERT(context->mech);
WINPR_ASSERT(context->mech->pkg);
WINPR_ASSERT(context->mech->pkg->table);
2022-05-28 01:29:28 +03:00
pkg = context->mech->pkg;
if (pkg->table->DeleteSecurityContext)
status = pkg->table->DeleteSecurityContext(&context->sub_context);
negotiate_ContextFree(context);
return status;
}
static SECURITY_STATUS SEC_ENTRY negotiate_ImpersonateSecurityContext(PCtxtHandle phContext)
{
2022-05-28 01:29:28 +03:00
return SEC_E_OK;
}
static SECURITY_STATUS SEC_ENTRY negotiate_RevertSecurityContext(PCtxtHandle phContext)
{
2022-05-28 01:29:28 +03:00
return SEC_E_OK;
}
static SECURITY_STATUS SEC_ENTRY negotiate_QueryContextAttributesW(PCtxtHandle phContext,
2019-11-06 17:24:51 +03:00
ULONG ulAttribute, void* pBuffer)
{
2022-05-28 01:29:28 +03:00
NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
2022-05-28 01:29:28 +03:00
if (!context)
return SEC_E_INVALID_HANDLE;
2022-06-24 16:03:55 +03:00
WINPR_ASSERT(context->mech);
WINPR_ASSERT(context->mech->pkg);
WINPR_ASSERT(context->mech->pkg->table_w);
2022-05-28 01:29:28 +03:00
if (context->mech->pkg->table_w->QueryContextAttributesW)
return context->mech->pkg->table_w->QueryContextAttributesW(&context->sub_context,
ulAttribute, pBuffer);
2022-06-02 03:22:00 +03:00
return SEC_E_UNSUPPORTED_FUNCTION;
}
static SECURITY_STATUS SEC_ENTRY negotiate_QueryContextAttributesA(PCtxtHandle phContext,
2019-11-06 17:24:51 +03:00
ULONG ulAttribute, void* pBuffer)
{
2022-05-28 01:29:28 +03:00
NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
2022-05-28 01:29:28 +03:00
if (!context)
return SEC_E_INVALID_HANDLE;
2022-06-24 16:03:55 +03:00
WINPR_ASSERT(context->mech);
WINPR_ASSERT(context->mech->pkg);
WINPR_ASSERT(context->mech->pkg->table);
2022-05-28 01:29:28 +03:00
if (context->mech->pkg->table->QueryContextAttributesA)
return context->mech->pkg->table->QueryContextAttributesA(&context->sub_context,
ulAttribute, pBuffer);
2022-06-23 08:57:38 +03:00
2022-06-02 03:22:00 +03:00
return SEC_E_UNSUPPORTED_FUNCTION;
}
static SECURITY_STATUS SEC_ENTRY negotiate_SetContextAttributesW(PCtxtHandle phContext,
2019-11-06 17:24:51 +03:00
ULONG ulAttribute, void* pBuffer,
ULONG cbBuffer)
{
2022-05-28 01:29:28 +03:00
NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
2022-05-28 01:29:28 +03:00
if (!context)
return SEC_E_INVALID_HANDLE;
2022-06-24 16:03:55 +03:00
WINPR_ASSERT(context->mech);
WINPR_ASSERT(context->mech->pkg);
WINPR_ASSERT(context->mech->pkg->table_w);
2022-05-28 01:29:28 +03:00
if (context->mech->pkg->table_w->SetContextAttributesW)
return context->mech->pkg->table_w->SetContextAttributesW(&context->sub_context,
ulAttribute, pBuffer, cbBuffer);
2022-06-23 08:57:38 +03:00
2022-06-02 03:22:00 +03:00
return SEC_E_UNSUPPORTED_FUNCTION;
}
static SECURITY_STATUS SEC_ENTRY negotiate_SetContextAttributesA(PCtxtHandle phContext,
2019-11-06 17:24:51 +03:00
ULONG ulAttribute, void* pBuffer,
ULONG cbBuffer)
{
2022-05-28 01:29:28 +03:00
NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
2022-05-28 01:29:28 +03:00
if (!context)
return SEC_E_INVALID_HANDLE;
2022-06-24 16:03:55 +03:00
WINPR_ASSERT(context->mech);
WINPR_ASSERT(context->mech->pkg);
WINPR_ASSERT(context->mech->pkg->table);
2022-05-28 01:29:28 +03:00
if (context->mech->pkg->table->SetContextAttributesA)
return context->mech->pkg->table->SetContextAttributesA(&context->sub_context, ulAttribute,
pBuffer, cbBuffer);
2022-06-23 08:57:38 +03:00
2022-06-02 03:22:00 +03:00
return SEC_E_UNSUPPORTED_FUNCTION;
}
2019-11-06 17:24:51 +03:00
static SECURITY_STATUS SEC_ENTRY negotiate_AcquireCredentialsHandleW(
SEC_WCHAR* pszPrincipal, SEC_WCHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID,
void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential,
PTimeStamp ptsExpiry)
{
2022-05-28 01:29:28 +03:00
BOOL kerberos, ntlm;
2022-06-24 16:03:55 +03:00
if (!negotiate_get_config(&kerberos, &ntlm))
return SEC_E_INTERNAL_ERROR;
2022-06-24 16:03:55 +03:00
MechCred* creds = calloc(MECH_COUNT, sizeof(MechCred));
if (!creds)
2022-06-11 00:22:02 +03:00
return SEC_E_INTERNAL_ERROR;
2022-06-24 16:03:55 +03:00
for (size_t i = 0; i < MECH_COUNT; i++)
2022-02-02 01:23:34 +03:00
{
2022-06-24 16:03:55 +03:00
MechCred* cred = &creds[i];
const SecPkg* pkg = MechTable[i].pkg;
cred->mech = &MechTable[i];
2022-02-02 01:23:34 +03:00
2022-05-28 01:29:28 +03:00
if (!kerberos && _tcscmp(pkg->name, KERBEROS_SSP_NAME) == 0)
continue;
if (!ntlm && _tcscmp(SecPkgTable[i].name, NTLM_SSP_NAME) == 0)
continue;
2022-02-02 01:23:34 +03:00
2022-06-24 16:03:55 +03:00
WINPR_ASSERT(pkg->table_w);
WINPR_ASSERT(pkg->table_w->AcquireCredentialsHandleW);
2022-05-28 01:29:28 +03:00
if (pkg->table_w->AcquireCredentialsHandleW(
pszPrincipal, pszPackage, fCredentialUse, pvLogonID, pAuthData, pGetKeyFn,
2022-06-24 16:03:55 +03:00
pvGetKeyArgument, &cred->cred, ptsExpiry) != SEC_E_OK)
2022-05-28 01:29:28 +03:00
continue;
2022-06-24 16:03:55 +03:00
cred->valid = TRUE;
2022-02-02 01:23:34 +03:00
}
2022-05-28 01:29:28 +03:00
sspi_SecureHandleSetLowerPointer(phCredential, (void*)creds);
2019-11-06 17:24:51 +03:00
sspi_SecureHandleSetUpperPointer(phCredential, (void*)NEGO_SSP_NAME);
return SEC_E_OK;
}
2019-11-06 17:24:51 +03:00
static SECURITY_STATUS SEC_ENTRY negotiate_AcquireCredentialsHandleA(
SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID,
void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential,
PTimeStamp ptsExpiry)
{
2022-05-28 01:29:28 +03:00
BOOL kerberos, ntlm;
2022-06-24 16:03:55 +03:00
if (!negotiate_get_config(&kerberos, &ntlm))
return SEC_E_INTERNAL_ERROR;
2022-06-24 16:03:55 +03:00
MechCred* creds = calloc(MECH_COUNT, sizeof(MechCred));
if (!creds)
2022-06-11 00:22:02 +03:00
return SEC_E_INTERNAL_ERROR;
2022-06-24 16:03:55 +03:00
for (size_t i = 0; i < MECH_COUNT; i++)
2022-02-02 01:23:34 +03:00
{
2022-06-24 16:03:55 +03:00
const SecPkg* pkg = MechTable[i].pkg;
MechCred* cred = &creds[i];
cred->mech = &MechTable[i];
2022-02-02 01:23:34 +03:00
2022-05-28 01:29:28 +03:00
if (!kerberos && _tcscmp(pkg->name, KERBEROS_SSP_NAME) == 0)
continue;
if (!ntlm && _tcscmp(SecPkgTable[i].name, NTLM_SSP_NAME) == 0)
continue;
2022-02-02 01:23:34 +03:00
2022-06-24 16:03:55 +03:00
WINPR_ASSERT(pkg->table);
WINPR_ASSERT(pkg->table->AcquireCredentialsHandleA);
2022-05-28 01:29:28 +03:00
if (pkg->table->AcquireCredentialsHandleA(pszPrincipal, pszPackage, fCredentialUse,
pvLogonID, pAuthData, pGetKeyFn, pvGetKeyArgument,
2022-06-24 16:03:55 +03:00
&cred->cred, ptsExpiry) != SEC_E_OK)
2022-05-28 01:29:28 +03:00
continue;
2022-06-24 16:03:55 +03:00
cred->valid = TRUE;
2022-02-02 01:23:34 +03:00
}
2022-05-28 01:29:28 +03:00
sspi_SecureHandleSetLowerPointer(phCredential, (void*)creds);
2019-11-06 17:24:51 +03:00
sspi_SecureHandleSetUpperPointer(phCredential, (void*)NEGO_SSP_NAME);
return SEC_E_OK;
}
static SECURITY_STATUS SEC_ENTRY negotiate_QueryCredentialsAttributesW(PCredHandle phCredential,
2019-11-06 17:24:51 +03:00
ULONG ulAttribute,
void* pBuffer)
{
WLog_ERR(TAG, "[%s]: TODO: Implement", __FUNCTION__);
return SEC_E_UNSUPPORTED_FUNCTION;
}
static SECURITY_STATUS SEC_ENTRY negotiate_QueryCredentialsAttributesA(PCredHandle phCredential,
2019-11-06 17:24:51 +03:00
ULONG ulAttribute,
void* pBuffer)
{
WLog_ERR(TAG, "[%s]: TODO: Implement", __FUNCTION__);
return SEC_E_UNSUPPORTED_FUNCTION;
}
static SECURITY_STATUS SEC_ENTRY negotiate_FreeCredentialsHandle(PCredHandle phCredential)
{
2022-05-28 01:29:28 +03:00
MechCred* creds;
2022-05-28 01:29:28 +03:00
creds = sspi_SecureHandleGetLowerPointer(phCredential);
if (!creds)
return SEC_E_INVALID_HANDLE;
2022-06-24 16:03:55 +03:00
for (size_t i = 0; i < MECH_COUNT; i++)
{
MechCred* cred = &creds[i];
WINPR_ASSERT(cred->mech);
WINPR_ASSERT(cred->mech->pkg);
WINPR_ASSERT(cred->mech->pkg->table);
WINPR_ASSERT(cred->mech->pkg->table->FreeCredentialsHandle);
cred->mech->pkg->table->FreeCredentialsHandle(&cred->cred);
}
free(creds);
return SEC_E_OK;
}
static SECURITY_STATUS SEC_ENTRY negotiate_EncryptMessage(PCtxtHandle phContext, ULONG fQOP,
2019-11-06 17:24:51 +03:00
PSecBufferDesc pMessage,
ULONG MessageSeqNo)
{
2022-05-28 01:29:28 +03:00
NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
2022-05-28 01:29:28 +03:00
if (!context)
return SEC_E_INVALID_HANDLE;
if (context->mic)
MessageSeqNo++;
2022-06-24 16:03:55 +03:00
WINPR_ASSERT(context->mech);
WINPR_ASSERT(context->mech->pkg);
WINPR_ASSERT(context->mech->pkg->table);
2022-05-28 01:29:28 +03:00
if (context->mech->pkg->table->EncryptMessage)
return context->mech->pkg->table->EncryptMessage(&context->sub_context, fQOP, pMessage,
MessageSeqNo);
2022-06-02 03:22:00 +03:00
return SEC_E_UNSUPPORTED_FUNCTION;
}
static SECURITY_STATUS SEC_ENTRY negotiate_DecryptMessage(PCtxtHandle phContext,
2019-11-06 17:24:51 +03:00
PSecBufferDesc pMessage,
ULONG MessageSeqNo, ULONG* pfQOP)
{
2022-05-28 01:29:28 +03:00
NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
2022-05-28 01:29:28 +03:00
if (!context)
return SEC_E_INVALID_HANDLE;
if (context->mic)
MessageSeqNo++;
2022-06-24 16:03:55 +03:00
WINPR_ASSERT(context->mech);
WINPR_ASSERT(context->mech->pkg);
WINPR_ASSERT(context->mech->pkg->table);
2022-05-28 01:29:28 +03:00
if (context->mech->pkg->table->DecryptMessage)
return context->mech->pkg->table->DecryptMessage(&context->sub_context, pMessage,
MessageSeqNo, pfQOP);
2022-06-23 08:57:38 +03:00
2022-06-02 03:22:00 +03:00
return SEC_E_UNSUPPORTED_FUNCTION;
}
static SECURITY_STATUS SEC_ENTRY negotiate_MakeSignature(PCtxtHandle phContext, ULONG fQOP,
2019-11-06 17:24:51 +03:00
PSecBufferDesc pMessage,
ULONG MessageSeqNo)
{
2022-05-28 01:29:28 +03:00
NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
2022-05-28 01:29:28 +03:00
if (!context)
return SEC_E_INVALID_HANDLE;
if (context->mic)
MessageSeqNo++;
2022-06-24 16:03:55 +03:00
WINPR_ASSERT(context->mech);
WINPR_ASSERT(context->mech->pkg);
WINPR_ASSERT(context->mech->pkg->table);
2022-05-28 01:29:28 +03:00
if (context->mech->pkg->table->MakeSignature)
return context->mech->pkg->table->MakeSignature(&context->sub_context, fQOP, pMessage,
MessageSeqNo);
2022-06-23 08:57:38 +03:00
2022-06-02 03:22:00 +03:00
return SEC_E_UNSUPPORTED_FUNCTION;
}
static SECURITY_STATUS SEC_ENTRY negotiate_VerifySignature(PCtxtHandle phContext,
2019-11-06 17:24:51 +03:00
PSecBufferDesc pMessage,
ULONG MessageSeqNo, ULONG* pfQOP)
{
2022-05-28 01:29:28 +03:00
NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
2022-05-28 01:29:28 +03:00
if (!context)
return SEC_E_INVALID_HANDLE;
if (context->mic)
MessageSeqNo++;
2022-06-24 16:03:55 +03:00
WINPR_ASSERT(context->mech);
WINPR_ASSERT(context->mech->pkg);
WINPR_ASSERT(context->mech->pkg->table);
2022-05-28 01:29:28 +03:00
if (context->mech->pkg->table->VerifySignature)
return context->mech->pkg->table->VerifySignature(&context->sub_context, pMessage,
MessageSeqNo, pfQOP);
2022-06-23 08:57:38 +03:00
2022-06-02 03:22:00 +03:00
return SEC_E_UNSUPPORTED_FUNCTION;
}
2019-11-06 17:24:51 +03:00
const SecurityFunctionTableA NEGOTIATE_SecurityFunctionTableA = {
1, /* dwVersion */
NULL, /* EnumerateSecurityPackages */
negotiate_QueryCredentialsAttributesA, /* QueryCredentialsAttributes */
2019-11-06 17:24:51 +03:00
negotiate_AcquireCredentialsHandleA, /* AcquireCredentialsHandle */
negotiate_FreeCredentialsHandle, /* FreeCredentialsHandle */
NULL, /* Reserved2 */
negotiate_InitializeSecurityContextA, /* InitializeSecurityContext */
negotiate_AcceptSecurityContext, /* AcceptSecurityContext */
negotiate_CompleteAuthToken, /* CompleteAuthToken */
negotiate_DeleteSecurityContext, /* DeleteSecurityContext */
NULL, /* ApplyControlToken */
negotiate_QueryContextAttributesA, /* QueryContextAttributes */
negotiate_ImpersonateSecurityContext, /* ImpersonateSecurityContext */
negotiate_RevertSecurityContext, /* RevertSecurityContext */
negotiate_MakeSignature, /* MakeSignature */
negotiate_VerifySignature, /* VerifySignature */
NULL, /* FreeContextBuffer */
NULL, /* QuerySecurityPackageInfo */
NULL, /* Reserved3 */
NULL, /* Reserved4 */
NULL, /* ExportSecurityContext */
NULL, /* ImportSecurityContext */
NULL, /* AddCredentials */
NULL, /* Reserved8 */
NULL, /* QuerySecurityContextToken */
negotiate_EncryptMessage, /* EncryptMessage */
negotiate_DecryptMessage, /* DecryptMessage */
negotiate_SetContextAttributesA, /* SetContextAttributes */
};
2019-11-06 17:24:51 +03:00
const SecurityFunctionTableW NEGOTIATE_SecurityFunctionTableW = {
1, /* dwVersion */
NULL, /* EnumerateSecurityPackages */
negotiate_QueryCredentialsAttributesW, /* QueryCredentialsAttributes */
2019-11-06 17:24:51 +03:00
negotiate_AcquireCredentialsHandleW, /* AcquireCredentialsHandle */
negotiate_FreeCredentialsHandle, /* FreeCredentialsHandle */
NULL, /* Reserved2 */
negotiate_InitializeSecurityContextW, /* InitializeSecurityContext */
negotiate_AcceptSecurityContext, /* AcceptSecurityContext */
negotiate_CompleteAuthToken, /* CompleteAuthToken */
negotiate_DeleteSecurityContext, /* DeleteSecurityContext */
NULL, /* ApplyControlToken */
negotiate_QueryContextAttributesW, /* QueryContextAttributes */
negotiate_ImpersonateSecurityContext, /* ImpersonateSecurityContext */
negotiate_RevertSecurityContext, /* RevertSecurityContext */
negotiate_MakeSignature, /* MakeSignature */
negotiate_VerifySignature, /* VerifySignature */
NULL, /* FreeContextBuffer */
NULL, /* QuerySecurityPackageInfo */
NULL, /* Reserved3 */
NULL, /* Reserved4 */
NULL, /* ExportSecurityContext */
NULL, /* ImportSecurityContext */
NULL, /* AddCredentials */
NULL, /* Reserved8 */
NULL, /* QuerySecurityContextToken */
negotiate_EncryptMessage, /* EncryptMessage */
negotiate_DecryptMessage, /* DecryptMessage */
negotiate_SetContextAttributesW, /* SetContextAttributes */
};