FreeRDP/winpr/libwinpr/sspi/sspi_winpr.c

1669 lines
48 KiB
C
Raw Normal View History

/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Security Support Provider Interface (SSPI)
*
* Copyright 2012-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
2017-05-11 19:51:45 +03:00
* Copyright 2017 Dorian Ducournau <dorian.ducournau@gmail.com>
*
* 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/windows.h>
#include <winpr/crt.h>
#include <winpr/sspi.h>
OpenSSL thread safety freerdp/winpr had the following issues: * The non reentrant SSL_library_init() was called concurrently (crash) * Missing code/api to set the eventually required OpenSSL static and dynamic locking callbacks * Missing code/api to free the application-global or thread-local OpenSSL data and tables This commit creates two new winpr functions: BOOL winpr_InitializeSSL(DWORD flags): Use the flag WINPR_SSL_INIT_ALREADY_INITIALIZED if you want to tell winpr that your application has already initialized OpenSSL. If required use the flag WINPR_SSL_INIT_ENABLE_LOCKING to tell winpr that it should set the OpenSSL static and dynamic locking callbacks. Otherwise just call it with the flag WINPR_SSL_INIT_DEFAULT. The recommended way is that your application calls this function once before any threads are created. However, in order to support lazy OpenSSL library initialization winpr_InitializeSSL() can also safely be called multiple times and concurrently because it uses the new InitOnceExecuteOnce() function to guarantee that the initialization is only performed successfully once during the life time of the calling process. BOOL winpr_CleanupSSL(DWORD flags): If you create a thread that uses SSL you should call this function before the thread returns using the flag WINPR_SSL_CLEANUP_THREAD in order to clean up the thread-local OpenSSL data and tables. Call the function with the flag WINPR_SSL_CLEANUP_GLOBAL before terminating your application. Note: This commit only replaced the current occurences of the SSL_load_error_strings(); SSL_library_init(); pairs in the freerdp source with winpr_InitializeSSL(). None of the server or client applications has been changed according to the recommended usage described above (TBDL).
2014-07-28 23:55:57 +04:00
#include <winpr/ssl.h>
#include <winpr/print.h>
#include "sspi.h"
#include "sspi_winpr.h"
2015-07-07 18:17:29 +03:00
#include "../log.h"
#define TAG WINPR_TAG("sspi")
/* Authentication Functions: http://msdn.microsoft.com/en-us/library/windows/desktop/aa374731/ */
2021-06-17 12:25:58 +03:00
#include "NTLM/ntlm_export.h"
#include "CredSSP/credssp.h"
#include "Kerberos/kerberos.h"
#include "Negotiate/negotiate.h"
#include "Schannel/schannel.h"
2019-11-06 17:24:51 +03:00
static const SecPkgInfoA* SecPkgInfoA_LIST[] = { &NTLM_SecPkgInfoA, &KERBEROS_SecPkgInfoA,
&NEGOTIATE_SecPkgInfoA, &CREDSSP_SecPkgInfoA,
&SCHANNEL_SecPkgInfoA };
2019-11-06 17:24:51 +03:00
static const SecPkgInfoW* SecPkgInfoW_LIST[] = { &NTLM_SecPkgInfoW, &KERBEROS_SecPkgInfoW,
&NEGOTIATE_SecPkgInfoW, &CREDSSP_SecPkgInfoW,
&SCHANNEL_SecPkgInfoW };
static SecurityFunctionTableA winpr_SecurityFunctionTableA;
static SecurityFunctionTableW winpr_SecurityFunctionTableW;
typedef struct
{
const SEC_CHAR* Name;
const SecurityFunctionTableA* SecurityFunctionTable;
} SecurityFunctionTableA_NAME;
typedef struct
{
const SEC_WCHAR* Name;
const SecurityFunctionTableW* SecurityFunctionTable;
} SecurityFunctionTableW_NAME;
2019-11-06 17:24:51 +03:00
static const SecurityFunctionTableA_NAME SecurityFunctionTableA_NAME_LIST[] = {
{ "NTLM", &NTLM_SecurityFunctionTableA },
{ "Kerberos", &KERBEROS_SecurityFunctionTableA },
{ "Negotiate", &NEGOTIATE_SecurityFunctionTableA },
{ "CREDSSP", &CREDSSP_SecurityFunctionTableA },
{ "Schannel", &SCHANNEL_SecurityFunctionTableA }
};
static const WCHAR _NTLM_NAME_W[] = { 'N', 'T', 'L', 'M', '\0' };
static const WCHAR _KERBEROS_NAME_W[] = { 'K', 'e', 'r', 'b', 'e', 'r', 'o', 's', '\0' };
static const WCHAR _NEGOTIATE_NAME_W[] = { 'N', 'e', 'g', 'o', 't', 'i', 'a', 't', 'e', '\0' };
static const WCHAR _CREDSSP_NAME_W[] = { 'C', 'r', 'e', 'd', 'S', 'S', 'P', '\0' };
static const WCHAR _SCHANNEL_NAME_W[] = { 'S', 'c', 'h', 'a', 'n', 'n', 'e', 'l', '\0' };
2019-11-06 17:24:51 +03:00
static const SecurityFunctionTableW_NAME SecurityFunctionTableW_NAME_LIST[] = {
{ _NTLM_NAME_W, &NTLM_SecurityFunctionTableW },
{ _KERBEROS_NAME_W, &KERBEROS_SecurityFunctionTableW },
{ _NEGOTIATE_NAME_W, &NEGOTIATE_SecurityFunctionTableW },
{ _CREDSSP_NAME_W, &CREDSSP_SecurityFunctionTableW },
{ _SCHANNEL_NAME_W, &SCHANNEL_SecurityFunctionTableW }
};
2019-11-06 17:24:51 +03:00
#define SecHandle_LOWER_MAX 0xFFFFFFFF
#define SecHandle_UPPER_MAX 0xFFFFFFFE
typedef struct
{
void* contextBuffer;
UINT32 allocatorIndex;
} CONTEXT_BUFFER_ALLOC_ENTRY;
typedef struct
{
UINT32 cEntries;
UINT32 cMaxEntries;
CONTEXT_BUFFER_ALLOC_ENTRY* entries;
} CONTEXT_BUFFER_ALLOC_TABLE;
static CONTEXT_BUFFER_ALLOC_TABLE ContextBufferAllocTable = { 0 };
static int sspi_ContextBufferAllocTableNew(void)
{
size_t size;
ContextBufferAllocTable.entries = NULL;
ContextBufferAllocTable.cEntries = 0;
ContextBufferAllocTable.cMaxEntries = 4;
size = sizeof(CONTEXT_BUFFER_ALLOC_ENTRY) * ContextBufferAllocTable.cMaxEntries;
2019-11-06 17:24:51 +03:00
ContextBufferAllocTable.entries = (CONTEXT_BUFFER_ALLOC_ENTRY*)calloc(1, size);
2014-06-07 01:20:34 +04:00
if (!ContextBufferAllocTable.entries)
return -1;
return 1;
}
static int sspi_ContextBufferAllocTableGrow(void)
{
size_t size;
2014-06-07 01:20:34 +04:00
CONTEXT_BUFFER_ALLOC_ENTRY* entries;
ContextBufferAllocTable.cEntries = 0;
ContextBufferAllocTable.cMaxEntries *= 2;
size = sizeof(CONTEXT_BUFFER_ALLOC_ENTRY) * ContextBufferAllocTable.cMaxEntries;
2014-06-07 01:20:34 +04:00
if (!size)
2014-06-07 01:20:34 +04:00
return -1;
2019-11-06 17:24:51 +03:00
entries = (CONTEXT_BUFFER_ALLOC_ENTRY*)realloc(ContextBufferAllocTable.entries, size);
2014-06-07 01:20:34 +04:00
if (!entries)
{
free(ContextBufferAllocTable.entries);
return -1;
}
ContextBufferAllocTable.entries = entries;
2019-11-06 17:24:51 +03:00
ZeroMemory((void*)&ContextBufferAllocTable.entries[ContextBufferAllocTable.cMaxEntries / 2],
2017-05-11 19:51:45 +03:00
size / 2);
2014-06-07 01:20:34 +04:00
return 1;
}
static void sspi_ContextBufferAllocTableFree(void)
{
2017-12-12 12:47:17 +03:00
if (ContextBufferAllocTable.cEntries != 0)
2019-11-06 17:24:51 +03:00
WLog_ERR(TAG, "ContextBufferAllocTable.entries == %" PRIu32,
ContextBufferAllocTable.cEntries);
2017-12-12 12:47:17 +03:00
ContextBufferAllocTable.cEntries = ContextBufferAllocTable.cMaxEntries = 0;
free(ContextBufferAllocTable.entries);
ContextBufferAllocTable.entries = NULL;
}
static void* sspi_ContextBufferAlloc(UINT32 allocatorIndex, size_t size)
{
UINT32 index;
void* contextBuffer;
for (index = 0; index < ContextBufferAllocTable.cMaxEntries; index++)
{
2014-06-07 01:20:34 +04:00
if (!ContextBufferAllocTable.entries[index].contextBuffer)
{
2014-06-07 01:20:34 +04:00
contextBuffer = calloc(1, size);
2015-07-07 18:17:29 +03:00
2014-06-07 01:20:34 +04:00
if (!contextBuffer)
return NULL;
ContextBufferAllocTable.cEntries++;
ContextBufferAllocTable.entries[index].contextBuffer = contextBuffer;
ContextBufferAllocTable.entries[index].allocatorIndex = allocatorIndex;
return ContextBufferAllocTable.entries[index].contextBuffer;
}
}
/* no available entry was found, the table needs to be grown */
2014-06-07 01:20:34 +04:00
if (sspi_ContextBufferAllocTableGrow() < 0)
return NULL;
/* the next call to sspi_ContextBufferAlloc() should now succeed */
return sspi_ContextBufferAlloc(allocatorIndex, size);
}
SSPI_CREDENTIALS* sspi_CredentialsNew(void)
{
SSPI_CREDENTIALS* credentials;
2019-11-06 17:24:51 +03:00
credentials = (SSPI_CREDENTIALS*)calloc(1, sizeof(SSPI_CREDENTIALS));
return credentials;
}
void sspi_CredentialsFree(SSPI_CREDENTIALS* credentials)
{
2017-05-11 19:51:45 +03:00
size_t userLength = 0;
size_t domainLength = 0;
size_t passwordLength = 0;
if (!credentials)
return;
userLength = credentials->identity.UserLength;
domainLength = credentials->identity.DomainLength;
passwordLength = credentials->identity.PasswordLength;
if (passwordLength > SSPI_CREDENTIALS_HASH_LENGTH_OFFSET) /* [pth] */
passwordLength -= SSPI_CREDENTIALS_HASH_LENGTH_OFFSET;
if (credentials->identity.Flags & SEC_WINNT_AUTH_IDENTITY_UNICODE)
{
userLength *= 2;
domainLength *= 2;
passwordLength *= 2;
}
2020-05-15 12:16:50 +03:00
if (credentials->identity.User)
memset(credentials->identity.User, 0, userLength);
if (credentials->identity.Domain)
memset(credentials->identity.Domain, 0, domainLength);
if (credentials->identity.Password)
memset(credentials->identity.Password, 0, passwordLength);
2014-06-10 16:40:04 +04:00
free(credentials->identity.User);
free(credentials->identity.Domain);
free(credentials->identity.Password);
free(credentials);
}
void* sspi_SecBufferAlloc(PSecBuffer SecBuffer, ULONG size)
{
if (!SecBuffer)
return NULL;
2014-06-07 01:20:34 +04:00
SecBuffer->pvBuffer = calloc(1, size);
2017-05-11 19:51:45 +03:00
if (!SecBuffer->pvBuffer)
return NULL;
SecBuffer->cbBuffer = size;
return SecBuffer->pvBuffer;
}
void sspi_SecBufferFree(PSecBuffer SecBuffer)
{
2015-05-11 10:07:39 +03:00
if (!SecBuffer)
return;
if (SecBuffer->pvBuffer)
memset(SecBuffer->pvBuffer, 0, SecBuffer->cbBuffer);
free(SecBuffer->pvBuffer);
SecBuffer->pvBuffer = NULL;
SecBuffer->cbBuffer = 0;
}
SecHandle* sspi_SecureHandleAlloc(void)
{
2019-11-06 17:24:51 +03:00
SecHandle* handle = (SecHandle*)calloc(1, sizeof(SecHandle));
2014-06-07 01:20:34 +04:00
if (!handle)
return NULL;
2014-06-08 02:28:02 +04:00
SecInvalidateHandle(handle);
return handle;
}
void* sspi_SecureHandleGetLowerPointer(SecHandle* handle)
{
void* pointer;
2014-06-08 02:28:02 +04:00
if (!handle || !SecIsValidHandle(handle) || !handle->dwLower)
return NULL;
2019-11-06 17:24:51 +03:00
pointer = (void*)~((size_t)handle->dwLower);
return pointer;
}
2017-07-18 14:23:22 +03:00
void sspi_SecureHandleInvalidate(SecHandle* handle)
{
if (!handle)
return;
handle->dwLower = 0;
handle->dwUpper = 0;
}
void sspi_SecureHandleSetLowerPointer(SecHandle* handle, void* pointer)
{
if (!handle)
return;
2019-11-06 17:24:51 +03:00
handle->dwLower = (ULONG_PTR)(~((size_t)pointer));
}
void* sspi_SecureHandleGetUpperPointer(SecHandle* handle)
{
void* pointer;
2014-06-08 02:28:02 +04:00
if (!handle || !SecIsValidHandle(handle) || !handle->dwUpper)
return NULL;
2019-11-06 17:24:51 +03:00
pointer = (void*)~((size_t)handle->dwUpper);
return pointer;
}
void sspi_SecureHandleSetUpperPointer(SecHandle* handle, void* pointer)
{
if (!handle)
return;
2019-11-06 17:24:51 +03:00
handle->dwUpper = (ULONG_PTR)(~((size_t)pointer));
}
void sspi_SecureHandleFree(SecHandle* handle)
{
free(handle);
}
2017-05-11 19:51:45 +03:00
int sspi_SetAuthIdentity(SEC_WINNT_AUTH_IDENTITY* identity, const char* user, const char* domain,
const char* password)
{
2018-01-24 16:11:33 +03:00
int rc;
int unicodePasswordLenW;
LPWSTR unicodePassword = NULL;
unicodePasswordLenW = ConvertToUnicode(CP_UTF8, 0, password, -1, &unicodePassword, 0);
if (unicodePasswordLenW <= 0)
return -1;
2018-01-24 16:11:33 +03:00
rc = sspi_SetAuthIdentityWithUnicodePassword(identity, user, domain, unicodePassword,
2019-11-06 17:24:51 +03:00
(ULONG)(unicodePasswordLenW - 1));
2018-01-24 16:11:33 +03:00
free(unicodePassword);
return rc;
}
int sspi_SetAuthIdentityWithUnicodePassword(SEC_WINNT_AUTH_IDENTITY* identity, const char* user,
2019-11-06 17:24:51 +03:00
const char* domain, LPWSTR password,
ULONG passwordLength)
{
2014-06-07 01:20:34 +04:00
int status;
identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
2015-05-11 10:07:39 +03:00
free(identity->User);
2019-11-06 17:24:51 +03:00
identity->User = (UINT16*)NULL;
2014-06-07 01:20:34 +04:00
identity->UserLength = 0;
if (user)
{
2019-11-06 17:24:51 +03:00
status = ConvertToUnicode(CP_UTF8, 0, user, -1, (LPWSTR*)&(identity->User), 0);
2014-06-07 01:20:34 +04:00
if (status <= 0)
return -1;
2017-05-11 19:51:45 +03:00
identity->UserLength = (ULONG)(status - 1);
}
2015-05-11 10:07:39 +03:00
free(identity->Domain);
2019-11-06 17:24:51 +03:00
identity->Domain = (UINT16*)NULL;
2014-06-07 01:20:34 +04:00
identity->DomainLength = 0;
if (domain)
{
2019-11-06 17:24:51 +03:00
status = ConvertToUnicode(CP_UTF8, 0, domain, -1, (LPWSTR*)&(identity->Domain), 0);
2014-06-07 01:20:34 +04:00
if (status <= 0)
return -1;
2017-05-11 19:51:45 +03:00
identity->DomainLength = (ULONG)(status - 1);
}
2015-05-11 10:07:39 +03:00
free(identity->Password);
2019-11-06 17:24:51 +03:00
identity->Password = (UINT16*)calloc(1, (passwordLength + 1) * sizeof(WCHAR));
if (!identity->Password)
return -1;
2014-06-07 01:20:34 +04:00
CopyMemory(identity->Password, password, passwordLength * sizeof(WCHAR));
identity->PasswordLength = passwordLength;
2014-06-07 01:20:34 +04:00
return 1;
}
int sspi_CopyAuthIdentity(SEC_WINNT_AUTH_IDENTITY* identity,
const SEC_WINNT_AUTH_IDENTITY* srcIdentity)
{
2014-06-07 01:20:34 +04:00
int status;
if (srcIdentity->Flags & SEC_WINNT_AUTH_IDENTITY_ANSI)
{
2019-11-06 17:24:51 +03:00
status = sspi_SetAuthIdentity(identity, (char*)srcIdentity->User,
(char*)srcIdentity->Domain, (char*)srcIdentity->Password);
2014-06-07 01:20:34 +04:00
if (status <= 0)
return -1;
identity->Flags &= ~SEC_WINNT_AUTH_IDENTITY_ANSI;
identity->Flags |= SEC_WINNT_AUTH_IDENTITY_UNICODE;
2014-06-07 01:20:34 +04:00
return 1;
}
identity->Flags |= SEC_WINNT_AUTH_IDENTITY_UNICODE;
2017-05-11 19:51:45 +03:00
/* login/password authentication */
identity->User = identity->Domain = identity->Password = NULL;
identity->UserLength = srcIdentity->UserLength;
if (identity->UserLength > 0)
{
2019-11-06 17:24:51 +03:00
identity->User = (UINT16*)calloc((identity->UserLength + 1), sizeof(WCHAR));
2014-06-07 01:20:34 +04:00
if (!identity->User)
return -1;
CopyMemory(identity->User, srcIdentity->User, identity->UserLength * sizeof(WCHAR));
identity->User[identity->UserLength] = 0;
}
identity->DomainLength = srcIdentity->DomainLength;
if (identity->DomainLength > 0)
{
2019-11-06 17:24:51 +03:00
identity->Domain = (UINT16*)calloc((identity->DomainLength + 1), sizeof(WCHAR));
2014-06-07 01:20:34 +04:00
if (!identity->Domain)
return -1;
CopyMemory(identity->Domain, srcIdentity->Domain, identity->DomainLength * sizeof(WCHAR));
identity->Domain[identity->DomainLength] = 0;
}
identity->PasswordLength = srcIdentity->PasswordLength;
if (identity->PasswordLength > SSPI_CREDENTIALS_HASH_LENGTH_OFFSET)
identity->PasswordLength -= SSPI_CREDENTIALS_HASH_LENGTH_OFFSET;
if (srcIdentity->Password)
{
2019-11-06 17:24:51 +03:00
identity->Password = (UINT16*)calloc((identity->PasswordLength + 1), sizeof(WCHAR));
2014-06-07 01:20:34 +04:00
if (!identity->Password)
return -1;
2019-11-06 17:24:51 +03:00
CopyMemory(identity->Password, srcIdentity->Password,
identity->PasswordLength * sizeof(WCHAR));
identity->Password[identity->PasswordLength] = 0;
}
identity->PasswordLength = srcIdentity->PasswordLength;
2017-05-11 19:51:45 +03:00
/* End of login/password authentication */
2014-06-07 01:20:34 +04:00
return 1;
}
PSecBuffer sspi_FindSecBuffer(PSecBufferDesc pMessage, ULONG BufferType)
{
ULONG index;
PSecBuffer pSecBuffer = NULL;
for (index = 0; index < pMessage->cBuffers; index++)
{
if (pMessage->pBuffers[index].BufferType == BufferType)
{
pSecBuffer = &pMessage->pBuffers[index];
break;
}
}
return pSecBuffer;
}
static BOOL CALLBACK sspi_init(PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context)
{
winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT);
sspi_ContextBufferAllocTableNew();
return TRUE;
}
void sspi_GlobalInit(void)
{
static INIT_ONCE once = INIT_ONCE_STATIC_INIT;
DWORD flags = 0;
InitOnceExecuteOnce(&once, sspi_init, &flags, NULL);
}
void sspi_GlobalFinish(void)
{
sspi_ContextBufferAllocTableFree();
}
2021-08-02 13:13:34 +03:00
static const SecurityFunctionTableA* sspi_GetSecurityFunctionTableAByNameA(const SEC_CHAR* Name)
{
int index;
UINT32 cPackages;
cPackages = sizeof(SecPkgInfoA_LIST) / sizeof(*(SecPkgInfoA_LIST));
2019-11-06 17:24:51 +03:00
for (index = 0; index < (int)cPackages; index++)
{
if (strcmp(Name, SecurityFunctionTableA_NAME_LIST[index].Name) == 0)
{
2021-08-02 13:13:34 +03:00
return (const SecurityFunctionTableA*)SecurityFunctionTableA_NAME_LIST[index]
2019-11-06 17:24:51 +03:00
.SecurityFunctionTable;
}
}
return NULL;
}
2021-08-02 13:13:34 +03:00
static const SecurityFunctionTableW* sspi_GetSecurityFunctionTableWByNameW(const SEC_WCHAR* Name)
{
int index;
UINT32 cPackages;
cPackages = sizeof(SecPkgInfoW_LIST) / sizeof(*(SecPkgInfoW_LIST));
2019-11-06 17:24:51 +03:00
for (index = 0; index < (int)cPackages; index++)
{
if (lstrcmpW(Name, SecurityFunctionTableW_NAME_LIST[index].Name) == 0)
{
2021-08-02 13:13:34 +03:00
return (const SecurityFunctionTableW*)SecurityFunctionTableW_NAME_LIST[index]
2019-11-06 17:24:51 +03:00
.SecurityFunctionTable;
}
}
return NULL;
}
2021-08-26 12:23:32 +03:00
static const SecurityFunctionTableW* sspi_GetSecurityFunctionTableWByNameA(const SEC_CHAR* Name)
{
2014-06-07 01:20:34 +04:00
int status;
SEC_WCHAR* NameW = NULL;
2021-08-02 13:13:34 +03:00
const SecurityFunctionTableW* table;
2014-06-07 01:20:34 +04:00
status = ConvertToUnicode(CP_UTF8, 0, Name, -1, &NameW, 0);
if (status <= 0)
return NULL;
table = sspi_GetSecurityFunctionTableWByNameW(NameW);
free(NameW);
return table;
}
static void FreeContextBuffer_EnumerateSecurityPackages(void* contextBuffer);
static void FreeContextBuffer_QuerySecurityPackageInfo(void* contextBuffer);
static void sspi_ContextBufferFree(void* contextBuffer)
{
UINT32 index;
UINT32 allocatorIndex;
for (index = 0; index < ContextBufferAllocTable.cMaxEntries; index++)
{
if (contextBuffer == ContextBufferAllocTable.entries[index].contextBuffer)
{
contextBuffer = ContextBufferAllocTable.entries[index].contextBuffer;
allocatorIndex = ContextBufferAllocTable.entries[index].allocatorIndex;
ContextBufferAllocTable.cEntries--;
ContextBufferAllocTable.entries[index].allocatorIndex = 0;
ContextBufferAllocTable.entries[index].contextBuffer = NULL;
switch (allocatorIndex)
{
case EnumerateSecurityPackagesIndex:
FreeContextBuffer_EnumerateSecurityPackages(contextBuffer);
break;
case QuerySecurityPackageInfoIndex:
FreeContextBuffer_QuerySecurityPackageInfo(contextBuffer);
break;
}
}
}
}
/**
* Standard SSPI API
*/
/* Package Management */
static SECURITY_STATUS SEC_ENTRY winpr_EnumerateSecurityPackagesW(ULONG* pcPackages,
2019-11-06 17:24:51 +03:00
PSecPkgInfoW* ppPackageInfo)
{
int index;
size_t size;
UINT32 cPackages;
SecPkgInfoW* pPackageInfo;
cPackages = sizeof(SecPkgInfoW_LIST) / sizeof(*(SecPkgInfoW_LIST));
size = sizeof(SecPkgInfoW) * cPackages;
2019-11-06 17:24:51 +03:00
pPackageInfo = (SecPkgInfoW*)sspi_ContextBufferAlloc(EnumerateSecurityPackagesIndex, size);
2014-06-07 01:20:34 +04:00
if (!pPackageInfo)
return SEC_E_INSUFFICIENT_MEMORY;
2019-11-06 17:24:51 +03:00
for (index = 0; index < (int)cPackages; index++)
{
pPackageInfo[index].fCapabilities = SecPkgInfoW_LIST[index]->fCapabilities;
pPackageInfo[index].wVersion = SecPkgInfoW_LIST[index]->wVersion;
pPackageInfo[index].wRPCID = SecPkgInfoW_LIST[index]->wRPCID;
pPackageInfo[index].cbMaxToken = SecPkgInfoW_LIST[index]->cbMaxToken;
pPackageInfo[index].Name = _wcsdup(SecPkgInfoW_LIST[index]->Name);
pPackageInfo[index].Comment = _wcsdup(SecPkgInfoW_LIST[index]->Comment);
}
*(pcPackages) = cPackages;
*(ppPackageInfo) = pPackageInfo;
return SEC_E_OK;
}
static SECURITY_STATUS SEC_ENTRY winpr_EnumerateSecurityPackagesA(ULONG* pcPackages,
2019-11-06 17:24:51 +03:00
PSecPkgInfoA* ppPackageInfo)
{
int index;
size_t size;
UINT32 cPackages;
SecPkgInfoA* pPackageInfo;
cPackages = sizeof(SecPkgInfoA_LIST) / sizeof(*(SecPkgInfoA_LIST));
size = sizeof(SecPkgInfoA) * cPackages;
2019-11-06 17:24:51 +03:00
pPackageInfo = (SecPkgInfoA*)sspi_ContextBufferAlloc(EnumerateSecurityPackagesIndex, size);
2014-06-07 01:20:34 +04:00
if (!pPackageInfo)
return SEC_E_INSUFFICIENT_MEMORY;
2019-11-06 17:24:51 +03:00
for (index = 0; index < (int)cPackages; index++)
{
pPackageInfo[index].fCapabilities = SecPkgInfoA_LIST[index]->fCapabilities;
pPackageInfo[index].wVersion = SecPkgInfoA_LIST[index]->wVersion;
pPackageInfo[index].wRPCID = SecPkgInfoA_LIST[index]->wRPCID;
pPackageInfo[index].cbMaxToken = SecPkgInfoA_LIST[index]->cbMaxToken;
pPackageInfo[index].Name = _strdup(SecPkgInfoA_LIST[index]->Name);
pPackageInfo[index].Comment = _strdup(SecPkgInfoA_LIST[index]->Comment);
2017-05-11 19:51:45 +03:00
if (!pPackageInfo[index].Name || !pPackageInfo[index].Comment)
{
sspi_ContextBufferFree(pPackageInfo);
return SEC_E_INSUFFICIENT_MEMORY;
}
}
*(pcPackages) = cPackages;
*(ppPackageInfo) = pPackageInfo;
return SEC_E_OK;
}
static void FreeContextBuffer_EnumerateSecurityPackages(void* contextBuffer)
{
int index;
UINT32 cPackages;
2019-11-06 17:24:51 +03:00
SecPkgInfoA* pPackageInfo = (SecPkgInfoA*)contextBuffer;
cPackages = sizeof(SecPkgInfoA_LIST) / sizeof(*(SecPkgInfoA_LIST));
if (!pPackageInfo)
return;
2019-11-06 17:24:51 +03:00
for (index = 0; index < (int)cPackages; index++)
{
2015-05-11 10:07:39 +03:00
free(pPackageInfo[index].Name);
free(pPackageInfo[index].Comment);
}
free(pPackageInfo);
}
SecurityFunctionTableW* SEC_ENTRY winpr_InitSecurityInterfaceW(void)
{
return &winpr_SecurityFunctionTableW;
}
SecurityFunctionTableA* SEC_ENTRY winpr_InitSecurityInterfaceA(void)
{
return &winpr_SecurityFunctionTableA;
}
static SECURITY_STATUS SEC_ENTRY winpr_QuerySecurityPackageInfoW(SEC_WCHAR* pszPackageName,
2019-11-06 17:24:51 +03:00
PSecPkgInfoW* ppPackageInfo)
{
int index;
size_t size;
UINT32 cPackages;
SecPkgInfoW* pPackageInfo;
cPackages = sizeof(SecPkgInfoW_LIST) / sizeof(*(SecPkgInfoW_LIST));
2019-11-06 17:24:51 +03:00
for (index = 0; index < (int)cPackages; index++)
{
if (lstrcmpW(pszPackageName, SecPkgInfoW_LIST[index]->Name) == 0)
{
size = sizeof(SecPkgInfoW);
2019-11-06 17:24:51 +03:00
pPackageInfo =
(SecPkgInfoW*)sspi_ContextBufferAlloc(QuerySecurityPackageInfoIndex, size);
2014-06-07 01:20:34 +04:00
if (!pPackageInfo)
return SEC_E_INSUFFICIENT_MEMORY;
pPackageInfo->fCapabilities = SecPkgInfoW_LIST[index]->fCapabilities;
pPackageInfo->wVersion = SecPkgInfoW_LIST[index]->wVersion;
pPackageInfo->wRPCID = SecPkgInfoW_LIST[index]->wRPCID;
pPackageInfo->cbMaxToken = SecPkgInfoW_LIST[index]->cbMaxToken;
pPackageInfo->Name = _wcsdup(SecPkgInfoW_LIST[index]->Name);
pPackageInfo->Comment = _wcsdup(SecPkgInfoW_LIST[index]->Comment);
*(ppPackageInfo) = pPackageInfo;
return SEC_E_OK;
}
}
*(ppPackageInfo) = NULL;
return SEC_E_SECPKG_NOT_FOUND;
}
static SECURITY_STATUS SEC_ENTRY winpr_QuerySecurityPackageInfoA(SEC_CHAR* pszPackageName,
2019-11-06 17:24:51 +03:00
PSecPkgInfoA* ppPackageInfo)
{
int index;
size_t size;
UINT32 cPackages;
SecPkgInfoA* pPackageInfo;
cPackages = sizeof(SecPkgInfoA_LIST) / sizeof(*(SecPkgInfoA_LIST));
2019-11-06 17:24:51 +03:00
for (index = 0; index < (int)cPackages; index++)
{
if (strcmp(pszPackageName, SecPkgInfoA_LIST[index]->Name) == 0)
{
size = sizeof(SecPkgInfoA);
2019-11-06 17:24:51 +03:00
pPackageInfo =
(SecPkgInfoA*)sspi_ContextBufferAlloc(QuerySecurityPackageInfoIndex, size);
2014-06-07 01:20:34 +04:00
if (!pPackageInfo)
return SEC_E_INSUFFICIENT_MEMORY;
pPackageInfo->fCapabilities = SecPkgInfoA_LIST[index]->fCapabilities;
pPackageInfo->wVersion = SecPkgInfoA_LIST[index]->wVersion;
pPackageInfo->wRPCID = SecPkgInfoA_LIST[index]->wRPCID;
pPackageInfo->cbMaxToken = SecPkgInfoA_LIST[index]->cbMaxToken;
pPackageInfo->Name = _strdup(SecPkgInfoA_LIST[index]->Name);
pPackageInfo->Comment = _strdup(SecPkgInfoA_LIST[index]->Comment);
2017-05-11 19:51:45 +03:00
if (!pPackageInfo->Name || !pPackageInfo->Comment)
{
sspi_ContextBufferFree(pPackageInfo);
return SEC_E_INSUFFICIENT_MEMORY;
}
*(ppPackageInfo) = pPackageInfo;
return SEC_E_OK;
}
}
*(ppPackageInfo) = NULL;
return SEC_E_SECPKG_NOT_FOUND;
}
void FreeContextBuffer_QuerySecurityPackageInfo(void* contextBuffer)
{
2019-11-06 17:24:51 +03:00
SecPkgInfo* pPackageInfo = (SecPkgInfo*)contextBuffer;
2015-05-11 10:07:39 +03:00
if (!pPackageInfo)
return;
2015-05-11 10:07:39 +03:00
free(pPackageInfo->Name);
free(pPackageInfo->Comment);
free(pPackageInfo);
}
/* Credential Management */
2019-11-06 17:24:51 +03:00
static SECURITY_STATUS SEC_ENTRY winpr_AcquireCredentialsHandleW(
SEC_WCHAR* pszPrincipal, SEC_WCHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID,
void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential,
PTimeStamp ptsExpiry)
{
SECURITY_STATUS status;
2021-08-02 13:13:34 +03:00
const SecurityFunctionTableW* table = sspi_GetSecurityFunctionTableWByNameW(pszPackage);
if (!table)
return SEC_E_SECPKG_NOT_FOUND;
2014-06-07 01:20:34 +04:00
if (!table->AcquireCredentialsHandleW)
{
WLog_WARN(TAG, "[%s]: Security module does not provide an implementation", __FUNCTION__);
return SEC_E_UNSUPPORTED_FUNCTION;
}
2019-11-06 17:24:51 +03:00
status = table->AcquireCredentialsHandleW(pszPrincipal, pszPackage, fCredentialUse, pvLogonID,
pAuthData, pGetKeyFn, pvGetKeyArgument, phCredential,
ptsExpiry);
if (IsSecurityStatusError(status))
2015-07-07 18:17:29 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "AcquireCredentialsHandleW status %s [0x%08" PRIX32 "]",
2017-05-11 19:51:45 +03:00
GetSecurityStatusString(status), status);
2015-07-07 18:17:29 +03:00
}
return status;
}
2019-11-06 17:24:51 +03:00
static SECURITY_STATUS SEC_ENTRY winpr_AcquireCredentialsHandleA(
SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID,
void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential,
PTimeStamp ptsExpiry)
{
SECURITY_STATUS status;
2021-08-02 13:13:34 +03:00
const SecurityFunctionTableA* table = sspi_GetSecurityFunctionTableAByNameA(pszPackage);
if (!table)
return SEC_E_SECPKG_NOT_FOUND;
2014-06-07 01:20:34 +04:00
if (!table->AcquireCredentialsHandleA)
{
WLog_WARN(TAG, "[%s]: Security module does not provide an implementation", __FUNCTION__);
return SEC_E_UNSUPPORTED_FUNCTION;
}
2019-11-06 17:24:51 +03:00
status = table->AcquireCredentialsHandleA(pszPrincipal, pszPackage, fCredentialUse, pvLogonID,
pAuthData, pGetKeyFn, pvGetKeyArgument, phCredential,
ptsExpiry);
2015-07-07 18:17:29 +03:00
if (IsSecurityStatusError(status))
2015-07-07 18:17:29 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "AcquireCredentialsHandleA status %s [0x%08" PRIX32 "]",
2017-05-11 19:51:45 +03:00
GetSecurityStatusString(status), status);
2015-07-07 18:17:29 +03:00
}
return status;
}
static SECURITY_STATUS SEC_ENTRY winpr_ExportSecurityContext(PCtxtHandle phContext, ULONG fFlags,
2019-11-06 17:24:51 +03:00
PSecBuffer pPackedContext,
HANDLE* pToken)
{
SEC_CHAR* Name;
SECURITY_STATUS status;
2021-08-26 12:23:32 +03:00
const SecurityFunctionTableW* table;
2019-11-06 17:24:51 +03:00
Name = (SEC_CHAR*)sspi_SecureHandleGetUpperPointer(phContext);
if (!Name)
return SEC_E_SECPKG_NOT_FOUND;
table = sspi_GetSecurityFunctionTableWByNameA(Name);
if (!table)
return SEC_E_SECPKG_NOT_FOUND;
if (!table->ExportSecurityContext)
{
WLog_WARN(TAG, "[%s]: Security module does not provide an implementation", __FUNCTION__);
return SEC_E_UNSUPPORTED_FUNCTION;
}
status = table->ExportSecurityContext(phContext, fFlags, pPackedContext, pToken);
if (IsSecurityStatusError(status))
2015-07-07 18:17:29 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "ExportSecurityContext status %s [0x%08" PRIX32 "]",
2017-05-11 19:51:45 +03:00
GetSecurityStatusString(status), status);
2015-07-07 18:17:29 +03:00
}
return status;
}
static SECURITY_STATUS SEC_ENTRY winpr_FreeCredentialsHandle(PCredHandle phCredential)
{
char* Name;
SECURITY_STATUS status;
2021-08-02 13:13:34 +03:00
const SecurityFunctionTableA* table;
2019-11-06 17:24:51 +03:00
Name = (char*)sspi_SecureHandleGetUpperPointer(phCredential);
if (!Name)
return SEC_E_SECPKG_NOT_FOUND;
table = sspi_GetSecurityFunctionTableAByNameA(Name);
if (!table)
return SEC_E_SECPKG_NOT_FOUND;
2014-06-07 01:20:34 +04:00
if (!table->FreeCredentialsHandle)
{
WLog_WARN(TAG, "[%s]: Security module does not provide an implementation", __FUNCTION__);
return SEC_E_UNSUPPORTED_FUNCTION;
}
status = table->FreeCredentialsHandle(phCredential);
if (IsSecurityStatusError(status))
2015-07-07 18:17:29 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "FreeCredentialsHandle status %s [0x%08" PRIX32 "]",
2017-05-11 19:51:45 +03:00
GetSecurityStatusString(status), status);
2015-07-07 18:17:29 +03:00
}
return status;
}
static SECURITY_STATUS SEC_ENTRY winpr_ImportSecurityContextW(SEC_WCHAR* pszPackage,
2019-11-06 17:24:51 +03:00
PSecBuffer pPackedContext,
HANDLE pToken, PCtxtHandle phContext)
{
SEC_CHAR* Name;
SECURITY_STATUS status;
2021-08-26 12:23:32 +03:00
const SecurityFunctionTableW* table;
2019-11-06 17:24:51 +03:00
Name = (SEC_CHAR*)sspi_SecureHandleGetUpperPointer(phContext);
if (!Name)
return SEC_E_SECPKG_NOT_FOUND;
table = sspi_GetSecurityFunctionTableWByNameA(Name);
if (!table)
return SEC_E_SECPKG_NOT_FOUND;
if (!table->ImportSecurityContextW)
{
WLog_WARN(TAG, "[%s]: Security module does not provide an implementation", __FUNCTION__);
return SEC_E_UNSUPPORTED_FUNCTION;
}
status = table->ImportSecurityContextW(pszPackage, pPackedContext, pToken, phContext);
if (IsSecurityStatusError(status))
2015-07-07 18:17:29 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "ImportSecurityContextW status %s [0x%08" PRIX32 "]",
2017-05-11 19:51:45 +03:00
GetSecurityStatusString(status), status);
2015-07-07 18:17:29 +03:00
}
return status;
}
static SECURITY_STATUS SEC_ENTRY winpr_ImportSecurityContextA(SEC_CHAR* pszPackage,
2019-11-06 17:24:51 +03:00
PSecBuffer pPackedContext,
HANDLE pToken, PCtxtHandle phContext)
{
char* Name = NULL;
SECURITY_STATUS status;
2021-08-02 13:13:34 +03:00
const SecurityFunctionTableA* table;
2019-11-06 17:24:51 +03:00
Name = (char*)sspi_SecureHandleGetUpperPointer(phContext);
if (!Name)
return SEC_E_SECPKG_NOT_FOUND;
table = sspi_GetSecurityFunctionTableAByNameA(Name);
if (!table)
return SEC_E_SECPKG_NOT_FOUND;
if (!table->ImportSecurityContextA)
{
WLog_WARN(TAG, "[%s]: Security module does not provide an implementation", __FUNCTION__);
return SEC_E_UNSUPPORTED_FUNCTION;
}
status = table->ImportSecurityContextA(pszPackage, pPackedContext, pToken, phContext);
if (IsSecurityStatusError(status))
2015-07-07 18:17:29 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "ImportSecurityContextA status %s [0x%08" PRIX32 "]",
2017-05-11 19:51:45 +03:00
GetSecurityStatusString(status), status);
2015-07-07 18:17:29 +03:00
}
return status;
}
static SECURITY_STATUS SEC_ENTRY winpr_QueryCredentialsAttributesW(PCredHandle phCredential,
2019-11-06 17:24:51 +03:00
ULONG ulAttribute, void* pBuffer)
{
SEC_WCHAR* Name;
SECURITY_STATUS status;
2021-08-02 13:13:34 +03:00
const SecurityFunctionTableW* table;
2019-11-06 17:24:51 +03:00
Name = (SEC_WCHAR*)sspi_SecureHandleGetUpperPointer(phCredential);
if (!Name)
return SEC_E_SECPKG_NOT_FOUND;
table = sspi_GetSecurityFunctionTableWByNameW(Name);
if (!table)
return SEC_E_SECPKG_NOT_FOUND;
2014-06-07 01:20:34 +04:00
if (!table->QueryCredentialsAttributesW)
{
WLog_WARN(TAG, "[%s]: Security module does not provide an implementation", __FUNCTION__);
return SEC_E_UNSUPPORTED_FUNCTION;
}
status = table->QueryCredentialsAttributesW(phCredential, ulAttribute, pBuffer);
if (IsSecurityStatusError(status))
2015-07-07 18:17:29 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "QueryCredentialsAttributesW status %s [0x%08" PRIX32 "]",
2017-05-11 19:51:45 +03:00
GetSecurityStatusString(status), status);
2015-07-07 18:17:29 +03:00
}
return status;
}
static SECURITY_STATUS SEC_ENTRY winpr_QueryCredentialsAttributesA(PCredHandle phCredential,
2019-11-06 17:24:51 +03:00
ULONG ulAttribute, void* pBuffer)
{
char* Name;
SECURITY_STATUS status;
2021-08-02 13:13:34 +03:00
const SecurityFunctionTableA* table;
2019-11-06 17:24:51 +03:00
Name = (char*)sspi_SecureHandleGetUpperPointer(phCredential);
if (!Name)
return SEC_E_SECPKG_NOT_FOUND;
table = sspi_GetSecurityFunctionTableAByNameA(Name);
if (!table)
return SEC_E_SECPKG_NOT_FOUND;
2014-06-07 01:20:34 +04:00
if (!table->QueryCredentialsAttributesA)
{
WLog_WARN(TAG, "[%s]: Security module does not provide an implementation", __FUNCTION__);
return SEC_E_UNSUPPORTED_FUNCTION;
}
status = table->QueryCredentialsAttributesA(phCredential, ulAttribute, pBuffer);
if (IsSecurityStatusError(status))
2015-07-07 18:17:29 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "QueryCredentialsAttributesA status %s [0x%08" PRIX32 "]",
2017-05-11 19:51:45 +03:00
GetSecurityStatusString(status), status);
2015-07-07 18:17:29 +03:00
}
return status;
}
/* Context Management */
2019-11-06 17:24:51 +03:00
static SECURITY_STATUS SEC_ENTRY
winpr_AcceptSecurityContext(PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput,
ULONG fContextReq, ULONG TargetDataRep, PCtxtHandle phNewContext,
PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsTimeStamp)
{
char* Name;
SECURITY_STATUS status;
2021-08-02 13:13:34 +03:00
const SecurityFunctionTableA* table;
2019-11-06 17:24:51 +03:00
Name = (char*)sspi_SecureHandleGetUpperPointer(phCredential);
if (!Name)
return SEC_E_SECPKG_NOT_FOUND;
table = sspi_GetSecurityFunctionTableAByNameA(Name);
if (!table)
return SEC_E_SECPKG_NOT_FOUND;
2014-06-07 01:20:34 +04:00
if (!table->AcceptSecurityContext)
{
WLog_WARN(TAG, "[%s]: Security module does not provide an implementation", __FUNCTION__);
return SEC_E_UNSUPPORTED_FUNCTION;
}
2019-11-06 17:24:51 +03:00
status =
table->AcceptSecurityContext(phCredential, phContext, pInput, fContextReq, TargetDataRep,
phNewContext, pOutput, pfContextAttr, ptsTimeStamp);
2015-07-07 18:17:29 +03:00
if (IsSecurityStatusError(status))
2015-07-07 18:17:29 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "AcceptSecurityContext status %s [0x%08" PRIX32 "]",
2017-05-11 19:51:45 +03:00
GetSecurityStatusString(status), status);
2015-07-07 18:17:29 +03:00
}
return status;
}
static SECURITY_STATUS SEC_ENTRY winpr_ApplyControlToken(PCtxtHandle phContext,
2019-11-06 17:24:51 +03:00
PSecBufferDesc pInput)
{
char* Name = NULL;
SECURITY_STATUS status;
2021-08-02 13:13:34 +03:00
const SecurityFunctionTableA* table;
2019-11-06 17:24:51 +03:00
Name = (char*)sspi_SecureHandleGetUpperPointer(phContext);
if (!Name)
return SEC_E_SECPKG_NOT_FOUND;
table = sspi_GetSecurityFunctionTableAByNameA(Name);
if (!table)
return SEC_E_SECPKG_NOT_FOUND;
if (!table->ApplyControlToken)
{
WLog_WARN(TAG, "[%s]: Security module does not provide an implementation", __FUNCTION__);
return SEC_E_UNSUPPORTED_FUNCTION;
}
status = table->ApplyControlToken(phContext, pInput);
if (IsSecurityStatusError(status))
2015-07-07 18:17:29 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "ApplyControlToken status %s [0x%08" PRIX32 "]",
2017-05-11 19:51:45 +03:00
GetSecurityStatusString(status), status);
2015-07-07 18:17:29 +03:00
}
return status;
}
static SECURITY_STATUS SEC_ENTRY winpr_CompleteAuthToken(PCtxtHandle phContext,
2019-11-06 17:24:51 +03:00
PSecBufferDesc pToken)
{
char* Name = NULL;
SECURITY_STATUS status;
2021-08-02 13:13:34 +03:00
const SecurityFunctionTableA* table;
2019-11-06 17:24:51 +03:00
Name = (char*)sspi_SecureHandleGetUpperPointer(phContext);
if (!Name)
return SEC_E_SECPKG_NOT_FOUND;
table = sspi_GetSecurityFunctionTableAByNameA(Name);
if (!table)
return SEC_E_SECPKG_NOT_FOUND;
if (!table->CompleteAuthToken)
{
WLog_WARN(TAG, "[%s]: Security module does not provide an implementation", __FUNCTION__);
return SEC_E_UNSUPPORTED_FUNCTION;
}
status = table->CompleteAuthToken(phContext, pToken);
if (IsSecurityStatusError(status))
2015-07-07 18:17:29 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "CompleteAuthToken status %s [0x%08" PRIX32 "]",
2017-05-11 19:51:45 +03:00
GetSecurityStatusString(status), status);
2015-07-07 18:17:29 +03:00
}
return status;
}
static SECURITY_STATUS SEC_ENTRY winpr_DeleteSecurityContext(PCtxtHandle phContext)
{
char* Name = NULL;
SECURITY_STATUS status;
2021-08-02 13:13:34 +03:00
const SecurityFunctionTableA* table;
2019-11-06 17:24:51 +03:00
Name = (char*)sspi_SecureHandleGetUpperPointer(phContext);
if (!Name)
return SEC_E_SECPKG_NOT_FOUND;
table = sspi_GetSecurityFunctionTableAByNameA(Name);
if (!table)
return SEC_E_SECPKG_NOT_FOUND;
2014-06-07 01:20:34 +04:00
if (!table->DeleteSecurityContext)
{
WLog_WARN(TAG, "[%s]: Security module does not provide an implementation", __FUNCTION__);
return SEC_E_UNSUPPORTED_FUNCTION;
}
status = table->DeleteSecurityContext(phContext);
if (IsSecurityStatusError(status))
2015-07-07 18:17:29 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "DeleteSecurityContext status %s [0x%08" PRIX32 "]",
2017-05-11 19:51:45 +03:00
GetSecurityStatusString(status), status);
2015-07-07 18:17:29 +03:00
}
return status;
}
static SECURITY_STATUS SEC_ENTRY winpr_FreeContextBuffer(void* pvContextBuffer)
{
if (!pvContextBuffer)
return SEC_E_INVALID_HANDLE;
sspi_ContextBufferFree(pvContextBuffer);
return SEC_E_OK;
}
static SECURITY_STATUS SEC_ENTRY winpr_ImpersonateSecurityContext(PCtxtHandle phContext)
{
SEC_CHAR* Name;
SECURITY_STATUS status;
2021-08-26 12:23:32 +03:00
const SecurityFunctionTableW* table;
2019-11-06 17:24:51 +03:00
Name = (SEC_CHAR*)sspi_SecureHandleGetUpperPointer(phContext);
if (!Name)
return SEC_E_SECPKG_NOT_FOUND;
table = sspi_GetSecurityFunctionTableWByNameA(Name);
if (!table)
return SEC_E_SECPKG_NOT_FOUND;
if (!table->ImpersonateSecurityContext)
{
WLog_WARN(TAG, "[%s]: Security module does not provide an implementation", __FUNCTION__);
return SEC_E_UNSUPPORTED_FUNCTION;
}
status = table->ImpersonateSecurityContext(phContext);
if (IsSecurityStatusError(status))
2015-07-07 18:17:29 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "ImpersonateSecurityContext status %s [0x%08" PRIX32 "]",
2017-05-11 19:51:45 +03:00
GetSecurityStatusString(status), status);
2015-07-07 18:17:29 +03:00
}
return status;
}
2019-11-06 17:24:51 +03:00
static SECURITY_STATUS SEC_ENTRY winpr_InitializeSecurityContextW(
PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR* pszTargetName, ULONG fContextReq,
ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2,
PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry)
{
SEC_CHAR* Name;
SECURITY_STATUS status;
2021-08-26 12:23:32 +03:00
const SecurityFunctionTableW* table;
2019-11-06 17:24:51 +03:00
Name = (SEC_CHAR*)sspi_SecureHandleGetUpperPointer(phCredential);
if (!Name)
return SEC_E_SECPKG_NOT_FOUND;
table = sspi_GetSecurityFunctionTableWByNameA(Name);
if (!table)
return SEC_E_SECPKG_NOT_FOUND;
2014-06-07 01:20:34 +04:00
if (!table->InitializeSecurityContextW)
{
WLog_WARN(TAG, "[%s]: Security module does not provide an implementation", __FUNCTION__);
return SEC_E_UNSUPPORTED_FUNCTION;
}
2019-11-06 17:24:51 +03:00
status = table->InitializeSecurityContextW(phCredential, phContext, pszTargetName, fContextReq,
Reserved1, TargetDataRep, pInput, Reserved2,
phNewContext, pOutput, pfContextAttr, ptsExpiry);
2015-07-07 18:17:29 +03:00
if (IsSecurityStatusError(status))
2015-07-07 18:17:29 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "InitializeSecurityContextW status %s [0x%08" PRIX32 "]",
2017-05-11 19:51:45 +03:00
GetSecurityStatusString(status), status);
2015-07-07 18:17:29 +03:00
}
return status;
}
2019-11-06 17:24:51 +03:00
static SECURITY_STATUS SEC_ENTRY winpr_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)
{
SEC_CHAR* Name;
SECURITY_STATUS status;
2021-08-02 13:13:34 +03:00
const SecurityFunctionTableA* table;
2019-11-06 17:24:51 +03:00
Name = (SEC_CHAR*)sspi_SecureHandleGetUpperPointer(phCredential);
if (!Name)
return SEC_E_SECPKG_NOT_FOUND;
table = sspi_GetSecurityFunctionTableAByNameA(Name);
if (!table)
return SEC_E_SECPKG_NOT_FOUND;
2014-06-07 01:20:34 +04:00
if (!table->InitializeSecurityContextA)
{
WLog_WARN(TAG, "[%s]: Security module does not provide an implementation", __FUNCTION__);
return SEC_E_UNSUPPORTED_FUNCTION;
}
2019-11-06 17:24:51 +03:00
status = table->InitializeSecurityContextA(phCredential, phContext, pszTargetName, fContextReq,
Reserved1, TargetDataRep, pInput, Reserved2,
phNewContext, pOutput, pfContextAttr, ptsExpiry);
2015-07-07 18:17:29 +03:00
if (IsSecurityStatusError(status))
2015-07-07 18:17:29 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "InitializeSecurityContextA status %s [0x%08" PRIX32 "]",
2017-05-11 19:51:45 +03:00
GetSecurityStatusString(status), status);
2015-07-07 18:17:29 +03:00
}
return status;
}
static SECURITY_STATUS SEC_ENTRY winpr_QueryContextAttributesW(PCtxtHandle phContext,
2019-11-06 17:24:51 +03:00
ULONG ulAttribute, void* pBuffer)
{
SEC_CHAR* Name;
SECURITY_STATUS status;
2021-08-26 12:23:32 +03:00
const SecurityFunctionTableW* table;
2019-11-06 17:24:51 +03:00
Name = (SEC_CHAR*)sspi_SecureHandleGetUpperPointer(phContext);
if (!Name)
return SEC_E_SECPKG_NOT_FOUND;
table = sspi_GetSecurityFunctionTableWByNameA(Name);
if (!table)
return SEC_E_SECPKG_NOT_FOUND;
2014-06-07 01:20:34 +04:00
if (!table->QueryContextAttributesW)
{
WLog_WARN(TAG, "[%s]: Security module does not provide an implementation", __FUNCTION__);
return SEC_E_UNSUPPORTED_FUNCTION;
}
status = table->QueryContextAttributesW(phContext, ulAttribute, pBuffer);
if (IsSecurityStatusError(status))
2015-07-07 18:17:29 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "QueryContextAttributesW status %s [0x%08" PRIX32 "]",
2017-05-11 19:51:45 +03:00
GetSecurityStatusString(status), status);
2015-07-07 18:17:29 +03:00
}
return status;
}
static SECURITY_STATUS SEC_ENTRY winpr_QueryContextAttributesA(PCtxtHandle phContext,
2019-11-06 17:24:51 +03:00
ULONG ulAttribute, void* pBuffer)
{
SEC_CHAR* Name;
SECURITY_STATUS status;
2021-08-02 13:13:34 +03:00
const SecurityFunctionTableA* table;
2019-11-06 17:24:51 +03:00
Name = (SEC_CHAR*)sspi_SecureHandleGetUpperPointer(phContext);
if (!Name)
return SEC_E_SECPKG_NOT_FOUND;
table = sspi_GetSecurityFunctionTableAByNameA(Name);
if (!table)
return SEC_E_SECPKG_NOT_FOUND;
2014-06-07 01:20:34 +04:00
if (!table->QueryContextAttributesA)
{
WLog_WARN(TAG, "[%s]: Security module does not provide an implementation", __FUNCTION__);
return SEC_E_UNSUPPORTED_FUNCTION;
}
status = table->QueryContextAttributesA(phContext, ulAttribute, pBuffer);
if (IsSecurityStatusError(status))
2015-07-07 18:17:29 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "QueryContextAttributesA status %s [0x%08" PRIX32 "]",
2017-05-11 19:51:45 +03:00
GetSecurityStatusString(status), status);
2015-07-07 18:17:29 +03:00
}
return status;
}
static SECURITY_STATUS SEC_ENTRY winpr_QuerySecurityContextToken(PCtxtHandle phContext,
2019-11-06 17:24:51 +03:00
HANDLE* phToken)
{
2014-06-10 22:38:17 +04:00
SEC_CHAR* Name;
SECURITY_STATUS status;
2021-08-26 12:23:32 +03:00
const SecurityFunctionTableW* table;
2019-11-06 17:24:51 +03:00
Name = (SEC_CHAR*)sspi_SecureHandleGetUpperPointer(phContext);
2014-06-10 22:38:17 +04:00
if (!Name)
return SEC_E_SECPKG_NOT_FOUND;
table = sspi_GetSecurityFunctionTableWByNameA(Name);
if (!table)
return SEC_E_SECPKG_NOT_FOUND;
if (!table->QuerySecurityContextToken)
{
WLog_WARN(TAG, "[%s]: Security module does not provide an implementation", __FUNCTION__);
2014-06-10 22:38:17 +04:00
return SEC_E_UNSUPPORTED_FUNCTION;
}
2014-06-10 22:38:17 +04:00
status = table->QuerySecurityContextToken(phContext, phToken);
if (IsSecurityStatusError(status))
2015-07-07 18:17:29 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "QuerySecurityContextToken status %s [0x%08" PRIX32 "]",
2017-05-11 19:51:45 +03:00
GetSecurityStatusString(status), status);
2015-07-07 18:17:29 +03:00
}
2014-06-10 22:38:17 +04:00
return status;
}
static SECURITY_STATUS SEC_ENTRY winpr_SetContextAttributesW(PCtxtHandle phContext,
2019-11-06 17:24:51 +03:00
ULONG ulAttribute, void* pBuffer,
ULONG cbBuffer)
{
2014-06-10 22:38:17 +04:00
SEC_CHAR* Name;
SECURITY_STATUS status;
2021-08-26 12:23:32 +03:00
const SecurityFunctionTableW* table;
2019-11-06 17:24:51 +03:00
Name = (SEC_CHAR*)sspi_SecureHandleGetUpperPointer(phContext);
2014-06-10 22:38:17 +04:00
if (!Name)
return SEC_E_SECPKG_NOT_FOUND;
table = sspi_GetSecurityFunctionTableWByNameA(Name);
if (!table)
return SEC_E_SECPKG_NOT_FOUND;
if (!table->SetContextAttributesW)
{
WLog_WARN(TAG, "[%s]: Security module does not provide an implementation", __FUNCTION__);
2014-06-10 22:38:17 +04:00
return SEC_E_UNSUPPORTED_FUNCTION;
}
2014-06-10 22:38:17 +04:00
status = table->SetContextAttributesW(phContext, ulAttribute, pBuffer, cbBuffer);
if (IsSecurityStatusError(status))
2015-07-07 18:17:29 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "SetContextAttributesW status %s [0x%08" PRIX32 "]",
2017-05-11 19:51:45 +03:00
GetSecurityStatusString(status), status);
2015-07-07 18:17:29 +03:00
}
2014-06-10 22:38:17 +04:00
return status;
}
static SECURITY_STATUS SEC_ENTRY winpr_SetContextAttributesA(PCtxtHandle phContext,
2019-11-06 17:24:51 +03:00
ULONG ulAttribute, void* pBuffer,
ULONG cbBuffer)
{
2014-06-10 22:38:17 +04:00
char* Name;
SECURITY_STATUS status;
2021-08-02 13:13:34 +03:00
const SecurityFunctionTableA* table;
2019-11-06 17:24:51 +03:00
Name = (char*)sspi_SecureHandleGetUpperPointer(phContext);
2014-06-10 22:38:17 +04:00
if (!Name)
return SEC_E_SECPKG_NOT_FOUND;
table = sspi_GetSecurityFunctionTableAByNameA(Name);
if (!table)
return SEC_E_SECPKG_NOT_FOUND;
if (!table->SetContextAttributesA)
{
WLog_WARN(TAG, "[%s]: Security module does not provide an implementation", __FUNCTION__);
2014-06-10 22:38:17 +04:00
return SEC_E_UNSUPPORTED_FUNCTION;
}
2014-06-10 22:38:17 +04:00
status = table->SetContextAttributesA(phContext, ulAttribute, pBuffer, cbBuffer);
if (IsSecurityStatusError(status))
2015-07-07 18:17:29 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "SetContextAttributesA status %s [0x%08" PRIX32 "]",
2017-05-11 19:51:45 +03:00
GetSecurityStatusString(status), status);
2015-07-07 18:17:29 +03:00
}
2014-06-10 22:38:17 +04:00
return status;
}
static SECURITY_STATUS SEC_ENTRY winpr_RevertSecurityContext(PCtxtHandle phContext)
{
2014-06-10 22:38:17 +04:00
SEC_CHAR* Name;
SECURITY_STATUS status;
2021-08-26 12:23:32 +03:00
const SecurityFunctionTableW* table;
2019-11-06 17:24:51 +03:00
Name = (SEC_CHAR*)sspi_SecureHandleGetUpperPointer(phContext);
2014-06-10 22:38:17 +04:00
if (!Name)
return SEC_E_SECPKG_NOT_FOUND;
table = sspi_GetSecurityFunctionTableWByNameA(Name);
if (!table)
return SEC_E_SECPKG_NOT_FOUND;
if (!table->RevertSecurityContext)
{
WLog_WARN(TAG, "[%s]: Security module does not provide an implementation", __FUNCTION__);
2014-06-10 22:38:17 +04:00
return SEC_E_UNSUPPORTED_FUNCTION;
}
2014-06-10 22:38:17 +04:00
status = table->RevertSecurityContext(phContext);
if (IsSecurityStatusError(status))
2015-07-07 18:17:29 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "RevertSecurityContext status %s [0x%08" PRIX32 "]",
2017-05-11 19:51:45 +03:00
GetSecurityStatusString(status), status);
2015-07-07 18:17:29 +03:00
}
2014-06-10 22:38:17 +04:00
return status;
}
/* Message Support */
static SECURITY_STATUS SEC_ENTRY winpr_DecryptMessage(PCtxtHandle phContext,
2019-11-06 17:24:51 +03:00
PSecBufferDesc pMessage, ULONG MessageSeqNo,
PULONG pfQOP)
{
char* Name;
SECURITY_STATUS status;
2021-08-02 13:13:34 +03:00
const SecurityFunctionTableA* table;
2019-11-06 17:24:51 +03:00
Name = (char*)sspi_SecureHandleGetUpperPointer(phContext);
if (!Name)
return SEC_E_SECPKG_NOT_FOUND;
table = sspi_GetSecurityFunctionTableAByNameA(Name);
if (!table)
return SEC_E_SECPKG_NOT_FOUND;
2014-06-07 01:20:34 +04:00
if (!table->DecryptMessage)
{
WLog_WARN(TAG, "[%s]: Security module does not provide an implementation", __FUNCTION__);
return SEC_E_UNSUPPORTED_FUNCTION;
}
status = table->DecryptMessage(phContext, pMessage, MessageSeqNo, pfQOP);
if (IsSecurityStatusError(status))
2015-07-07 18:17:29 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "DecryptMessage status %s [0x%08" PRIX32 "]",
2017-05-11 19:51:45 +03:00
GetSecurityStatusString(status), status);
2015-07-07 18:17:29 +03:00
}
return status;
}
static SECURITY_STATUS SEC_ENTRY winpr_EncryptMessage(PCtxtHandle phContext, ULONG fQOP,
2019-11-06 17:24:51 +03:00
PSecBufferDesc pMessage, ULONG MessageSeqNo)
{
char* Name;
SECURITY_STATUS status;
2021-08-02 13:13:34 +03:00
const SecurityFunctionTableA* table;
2019-11-06 17:24:51 +03:00
Name = (char*)sspi_SecureHandleGetUpperPointer(phContext);
if (!Name)
return SEC_E_SECPKG_NOT_FOUND;
table = sspi_GetSecurityFunctionTableAByNameA(Name);
if (!table)
return SEC_E_SECPKG_NOT_FOUND;
2014-06-07 01:20:34 +04:00
if (!table->EncryptMessage)
{
WLog_WARN(TAG, "[%s]: Security module does not provide an implementation", __FUNCTION__);
return SEC_E_UNSUPPORTED_FUNCTION;
}
status = table->EncryptMessage(phContext, fQOP, pMessage, MessageSeqNo);
2015-07-07 18:17:29 +03:00
if (status != SEC_E_OK)
{
2019-11-06 17:24:51 +03:00
WLog_ERR(TAG, "EncryptMessage status %s [0x%08" PRIX32 "]", GetSecurityStatusString(status),
status);
2015-07-07 18:17:29 +03:00
}
return status;
}
static SECURITY_STATUS SEC_ENTRY winpr_MakeSignature(PCtxtHandle phContext, ULONG fQOP,
2019-11-06 17:24:51 +03:00
PSecBufferDesc pMessage, ULONG MessageSeqNo)
{
char* Name;
SECURITY_STATUS status;
2021-08-02 13:13:34 +03:00
const SecurityFunctionTableA* table;
2019-11-06 17:24:51 +03:00
Name = (char*)sspi_SecureHandleGetUpperPointer(phContext);
if (!Name)
return SEC_E_SECPKG_NOT_FOUND;
table = sspi_GetSecurityFunctionTableAByNameA(Name);
if (!table)
return SEC_E_SECPKG_NOT_FOUND;
2014-06-07 01:20:34 +04:00
if (!table->MakeSignature)
{
WLog_WARN(TAG, "[%s]: Security module does not provide an implementation", __FUNCTION__);
return SEC_E_UNSUPPORTED_FUNCTION;
}
status = table->MakeSignature(phContext, fQOP, pMessage, MessageSeqNo);
if (IsSecurityStatusError(status))
2015-07-07 18:17:29 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "MakeSignature status %s [0x%08" PRIX32 "]", GetSecurityStatusString(status),
status);
2015-07-07 18:17:29 +03:00
}
return status;
}
static SECURITY_STATUS SEC_ENTRY winpr_VerifySignature(PCtxtHandle phContext,
2019-11-06 17:24:51 +03:00
PSecBufferDesc pMessage, ULONG MessageSeqNo,
PULONG pfQOP)
{
char* Name;
SECURITY_STATUS status;
2021-08-02 13:13:34 +03:00
const SecurityFunctionTableA* table;
2019-11-06 17:24:51 +03:00
Name = (char*)sspi_SecureHandleGetUpperPointer(phContext);
if (!Name)
return SEC_E_SECPKG_NOT_FOUND;
table = sspi_GetSecurityFunctionTableAByNameA(Name);
if (!table)
return SEC_E_SECPKG_NOT_FOUND;
2014-06-07 01:20:34 +04:00
if (!table->VerifySignature)
{
WLog_WARN(TAG, "[%s]: Security module does not provide an implementation", __FUNCTION__);
return SEC_E_UNSUPPORTED_FUNCTION;
}
status = table->VerifySignature(phContext, pMessage, MessageSeqNo, pfQOP);
if (IsSecurityStatusError(status))
2015-07-07 18:17:29 +03:00
{
2019-11-06 17:24:51 +03:00
WLog_WARN(TAG, "VerifySignature status %s [0x%08" PRIX32 "]",
2017-05-11 19:51:45 +03:00
GetSecurityStatusString(status), status);
2015-07-07 18:17:29 +03:00
}
return status;
}
2019-11-06 17:24:51 +03:00
static SecurityFunctionTableA winpr_SecurityFunctionTableA = {
1, /* dwVersion */
winpr_EnumerateSecurityPackagesA, /* EnumerateSecurityPackages */
winpr_QueryCredentialsAttributesA, /* QueryCredentialsAttributes */
2019-11-06 17:24:51 +03:00
winpr_AcquireCredentialsHandleA, /* AcquireCredentialsHandle */
winpr_FreeCredentialsHandle, /* FreeCredentialsHandle */
NULL, /* Reserved2 */
winpr_InitializeSecurityContextA, /* InitializeSecurityContext */
winpr_AcceptSecurityContext, /* AcceptSecurityContext */
winpr_CompleteAuthToken, /* CompleteAuthToken */
winpr_DeleteSecurityContext, /* DeleteSecurityContext */
winpr_ApplyControlToken, /* ApplyControlToken */
winpr_QueryContextAttributesA, /* QueryContextAttributes */
winpr_ImpersonateSecurityContext, /* ImpersonateSecurityContext */
winpr_RevertSecurityContext, /* RevertSecurityContext */
winpr_MakeSignature, /* MakeSignature */
winpr_VerifySignature, /* VerifySignature */
winpr_FreeContextBuffer, /* FreeContextBuffer */
winpr_QuerySecurityPackageInfoA, /* QuerySecurityPackageInfo */
NULL, /* Reserved3 */
NULL, /* Reserved4 */
winpr_ExportSecurityContext, /* ExportSecurityContext */
winpr_ImportSecurityContextA, /* ImportSecurityContext */
NULL, /* AddCredentials */
NULL, /* Reserved8 */
winpr_QuerySecurityContextToken, /* QuerySecurityContextToken */
winpr_EncryptMessage, /* EncryptMessage */
winpr_DecryptMessage, /* DecryptMessage */
winpr_SetContextAttributesA, /* SetContextAttributes */
};
2019-11-06 17:24:51 +03:00
static SecurityFunctionTableW winpr_SecurityFunctionTableW = {
1, /* dwVersion */
winpr_EnumerateSecurityPackagesW, /* EnumerateSecurityPackages */
winpr_QueryCredentialsAttributesW, /* QueryCredentialsAttributes */
2019-11-06 17:24:51 +03:00
winpr_AcquireCredentialsHandleW, /* AcquireCredentialsHandle */
winpr_FreeCredentialsHandle, /* FreeCredentialsHandle */
NULL, /* Reserved2 */
winpr_InitializeSecurityContextW, /* InitializeSecurityContext */
winpr_AcceptSecurityContext, /* AcceptSecurityContext */
winpr_CompleteAuthToken, /* CompleteAuthToken */
winpr_DeleteSecurityContext, /* DeleteSecurityContext */
winpr_ApplyControlToken, /* ApplyControlToken */
winpr_QueryContextAttributesW, /* QueryContextAttributes */
winpr_ImpersonateSecurityContext, /* ImpersonateSecurityContext */
winpr_RevertSecurityContext, /* RevertSecurityContext */
winpr_MakeSignature, /* MakeSignature */
winpr_VerifySignature, /* VerifySignature */
winpr_FreeContextBuffer, /* FreeContextBuffer */
winpr_QuerySecurityPackageInfoW, /* QuerySecurityPackageInfo */
NULL, /* Reserved3 */
NULL, /* Reserved4 */
winpr_ExportSecurityContext, /* ExportSecurityContext */
winpr_ImportSecurityContextW, /* ImportSecurityContext */
NULL, /* AddCredentials */
NULL, /* Reserved8 */
winpr_QuerySecurityContextToken, /* QuerySecurityContextToken */
winpr_EncryptMessage, /* EncryptMessage */
winpr_DecryptMessage, /* DecryptMessage */
winpr_SetContextAttributesW, /* SetContextAttributes */
};