Merge pull request #10549 from hardening/rcg2
core, channels: client-side remote credential guard
This commit is contained in:
commit
7ef9345743
38
channels/rdpear/CMakeLists.txt
Normal file
38
channels/rdpear/CMakeLists.txt
Normal file
@ -0,0 +1,38 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2023 David Fort <contact@hardening-consulting.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.
|
||||
|
||||
if (NOT IOS AND NOT WIN32 AND NOT ANDROID)
|
||||
find_package(KRB5)
|
||||
|
||||
if(KRB5_FOUND)
|
||||
define_channel("rdpear")
|
||||
|
||||
include_directories(common)
|
||||
|
||||
if(WITH_CLIENT_CHANNELS OR WITH_SERVER_CHANNELS)
|
||||
add_subdirectory(common)
|
||||
endif()
|
||||
|
||||
if(WITH_CLIENT_CHANNELS)
|
||||
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
||||
|
||||
#if(WITH_SERVER_CHANNELS)
|
||||
# add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
#endif()
|
||||
endif()
|
||||
endif()
|
13
channels/rdpear/ChannelOptions.cmake
Normal file
13
channels/rdpear/ChannelOptions.cmake
Normal file
@ -0,0 +1,13 @@
|
||||
|
||||
set(OPTION_DEFAULT OFF)
|
||||
set(OPTION_CLIENT_DEFAULT ON)
|
||||
set(OPTION_SERVER_DEFAULT ON)
|
||||
|
||||
define_channel_options(NAME "rdpear" TYPE "dynamic"
|
||||
DESCRIPTION "Authentication redirection Virtual Channel Extension"
|
||||
SPECIFICATIONS "[MS-RDPEAR]"
|
||||
DEFAULT ${OPTION_DEFAULT})
|
||||
|
||||
define_channel_client_options(${OPTION_CLIENT_DEFAULT})
|
||||
define_channel_server_options(${OPTION_SERVER_DEFAULT})
|
||||
|
31
channels/rdpear/client/CMakeLists.txt
Normal file
31
channels/rdpear/client/CMakeLists.txt
Normal file
@ -0,0 +1,31 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2023 David Fort <contact@hardening-consulting.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.
|
||||
|
||||
define_channel_client("rdpear")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
rdpear_main.c
|
||||
)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS
|
||||
winpr
|
||||
freerdp
|
||||
rdpear-common
|
||||
)
|
||||
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
|
||||
|
965
channels/rdpear/client/rdpear_main.c
Normal file
965
channels/rdpear/client/rdpear_main.c
Normal file
@ -0,0 +1,965 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Authentication redirection virtual channel
|
||||
*
|
||||
* Copyright 2023 David Fort <contact@hardening-consulting.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.
|
||||
*/
|
||||
#include <krb5/krb5.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <winpr/assert.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/wlog.h>
|
||||
#include <winpr/print.h>
|
||||
#include <winpr/asn1.h>
|
||||
#include <winpr/sspi.h>
|
||||
#include <winpr/collections.h>
|
||||
|
||||
#include <rdpear-common/ndr.h>
|
||||
#include <rdpear-common/rdpear_common.h>
|
||||
#include <rdpear-common/rdpear_asn1.h>
|
||||
|
||||
#include <freerdp/config.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/client/channels.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
#include <freerdp/channels/rdpear.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("rdpear.client")
|
||||
|
||||
/* defined in libkrb5 */
|
||||
krb5_error_code encode_krb5_authenticator(const krb5_authenticator* rep, krb5_data** code_out);
|
||||
krb5_error_code encode_krb5_ap_rep(const krb5_ap_rep* rep, krb5_data** code_out);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GENERIC_DYNVC_PLUGIN base;
|
||||
rdpContext* rdp_context;
|
||||
krb5_context krbContext;
|
||||
} RDPEAR_PLUGIN;
|
||||
|
||||
const BYTE payloadHeader[16] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
static krb5_error_code RPC_ENCRYPTION_KEY_to_keyblock(krb5_context ctx,
|
||||
const KERB_RPC_ENCRYPTION_KEY* key,
|
||||
krb5_keyblock** pkeyblock)
|
||||
{
|
||||
WINPR_ASSERT(ctx);
|
||||
WINPR_ASSERT(key);
|
||||
WINPR_ASSERT(pkeyblock);
|
||||
|
||||
if (!key->reserved3.length)
|
||||
return KRB5KDC_ERR_NULL_KEY;
|
||||
|
||||
krb5_error_code rv =
|
||||
krb5_init_keyblock(ctx, (krb5_enctype)key->reserved2, key->reserved3.length, pkeyblock);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
krb5_keyblock* keyblock = *pkeyblock;
|
||||
memcpy(keyblock->contents, key->reserved3.value, key->reserved3.length);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static krb5_error_code kerb_do_checksum(krb5_context ctx, const KERB_RPC_ENCRYPTION_KEY* key,
|
||||
krb5_keyusage kusage, krb5_cksumtype cksumtype,
|
||||
const KERB_ASN1_DATA* plain, krb5_checksum* out)
|
||||
{
|
||||
WINPR_ASSERT(ctx);
|
||||
WINPR_ASSERT(key);
|
||||
WINPR_ASSERT(plain);
|
||||
WINPR_ASSERT(out);
|
||||
|
||||
krb5_keyblock* keyblock = NULL;
|
||||
krb5_data data = { 0 };
|
||||
|
||||
krb5_error_code rv = RPC_ENCRYPTION_KEY_to_keyblock(ctx, key, &keyblock);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
data.data = (char*)plain->Asn1Buffer;
|
||||
data.length = plain->Asn1BufferHints.count;
|
||||
|
||||
rv = krb5_c_make_checksum(ctx, cksumtype, keyblock, kusage, &data, out);
|
||||
|
||||
krb5_free_keyblock(ctx, keyblock);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static krb5_error_code kerb_do_encrypt(krb5_context ctx, const KERB_RPC_ENCRYPTION_KEY* key,
|
||||
krb5_keyusage kusage, const KERB_ASN1_DATA* plain,
|
||||
krb5_data* out)
|
||||
{
|
||||
WINPR_ASSERT(ctx);
|
||||
WINPR_ASSERT(key);
|
||||
WINPR_ASSERT(plain);
|
||||
WINPR_ASSERT(out);
|
||||
|
||||
krb5_keyblock* keyblock = NULL;
|
||||
krb5_data data = { 0 };
|
||||
krb5_enc_data enc = { 0 };
|
||||
size_t elen = 0;
|
||||
|
||||
krb5_error_code rv = RPC_ENCRYPTION_KEY_to_keyblock(ctx, key, &keyblock);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
data.data = (char*)plain->Asn1Buffer;
|
||||
data.length = plain->Asn1BufferHints.count;
|
||||
|
||||
rv = krb5_c_encrypt_length(ctx, keyblock->enctype, data.length, &elen);
|
||||
if (rv)
|
||||
goto out;
|
||||
if (!elen)
|
||||
{
|
||||
rv = KRB5_PARSE_MALFORMED;
|
||||
goto out;
|
||||
}
|
||||
enc.ciphertext.length = elen;
|
||||
enc.ciphertext.data = malloc(elen);
|
||||
if (!enc.ciphertext.data)
|
||||
{
|
||||
rv = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rv = krb5_c_encrypt(ctx, keyblock, kusage, NULL, &data, &enc);
|
||||
|
||||
out->data = enc.ciphertext.data;
|
||||
out->length = enc.ciphertext.length;
|
||||
out:
|
||||
krb5_free_keyblock(ctx, keyblock);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static krb5_error_code kerb_do_decrypt(krb5_context ctx, const KERB_RPC_ENCRYPTION_KEY* key,
|
||||
krb5_keyusage kusage, const krb5_data* cipher,
|
||||
KERB_ASN1_DATA* plain)
|
||||
{
|
||||
WINPR_ASSERT(ctx);
|
||||
WINPR_ASSERT(key);
|
||||
WINPR_ASSERT(cipher);
|
||||
WINPR_ASSERT(cipher->length);
|
||||
WINPR_ASSERT(plain);
|
||||
|
||||
krb5_keyblock* keyblock = NULL;
|
||||
krb5_data data = { 0 };
|
||||
krb5_enc_data enc = { 0 };
|
||||
|
||||
krb5_error_code rv = RPC_ENCRYPTION_KEY_to_keyblock(ctx, key, &keyblock);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
enc.kvno = KRB5_PVNO;
|
||||
enc.enctype = (krb5_enctype)key->reserved2;
|
||||
enc.ciphertext.length = cipher->length;
|
||||
enc.ciphertext.data = cipher->data;
|
||||
|
||||
data.length = cipher->length;
|
||||
data.data = (char*)malloc(cipher->length);
|
||||
if (!data.data)
|
||||
{
|
||||
rv = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rv = krb5_c_decrypt(ctx, keyblock, kusage, NULL, &enc, &data);
|
||||
|
||||
plain->Asn1Buffer = (BYTE*)data.data;
|
||||
plain->Asn1BufferHints.count = data.length;
|
||||
out:
|
||||
krb5_free_keyblock(ctx, keyblock);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static BOOL rdpear_send_payload(RDPEAR_PLUGIN* rdpear, IWTSVirtualChannelCallback* pChannelCallback,
|
||||
RdpEarPackageType packageType, wStream* payload)
|
||||
{
|
||||
GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
|
||||
BOOL ret = FALSE;
|
||||
wStream* finalStream = NULL;
|
||||
SecBuffer cryptedBuffer = { 0 };
|
||||
wStream* unencodedContent = rdpear_encodePayload(packageType, payload);
|
||||
if (!unencodedContent)
|
||||
goto out;
|
||||
|
||||
size_t unencodedLen = Stream_GetPosition(unencodedContent);
|
||||
SecBuffer inBuffer = { unencodedLen, SECBUFFER_DATA, Stream_Buffer(unencodedContent) };
|
||||
|
||||
if (!freerdp_nla_encrypt(rdpear->rdp_context, &inBuffer, &cryptedBuffer))
|
||||
goto out;
|
||||
|
||||
finalStream = Stream_New(NULL, 200);
|
||||
if (!finalStream)
|
||||
goto out;
|
||||
Stream_Write_UINT32(finalStream, 0x4EACC3C8); /* ProtocolMagic (4 bytes) */
|
||||
Stream_Write_UINT32(finalStream, cryptedBuffer.cbBuffer); /* Length (4 bytes) */
|
||||
Stream_Write_UINT32(finalStream, 0x00000000); /* Version (4 bytes) */
|
||||
Stream_Write_UINT32(finalStream, 0x00000000); /* Reserved (4 bytes) */
|
||||
Stream_Write_UINT64(finalStream, 0); /* TsPkgContext (8 bytes) */
|
||||
|
||||
/* payload */
|
||||
if (!Stream_EnsureRemainingCapacity(finalStream, cryptedBuffer.cbBuffer))
|
||||
goto out;
|
||||
|
||||
Stream_Write(finalStream, cryptedBuffer.pvBuffer, cryptedBuffer.cbBuffer);
|
||||
|
||||
UINT status = callback->channel->Write(callback->channel, Stream_GetPosition(finalStream),
|
||||
Stream_Buffer(finalStream), NULL);
|
||||
ret = (status == CHANNEL_RC_OK);
|
||||
if (!ret)
|
||||
WLog_DBG(TAG, "rdpear_send_payload=0x%x", status);
|
||||
out:
|
||||
sspi_SecBufferFree(&cryptedBuffer);
|
||||
Stream_Free(unencodedContent, TRUE);
|
||||
Stream_Free(finalStream, TRUE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BOOL rdpear_prepare_response(NdrContext* rcontext, UINT16 callId, UINT32 status,
|
||||
NdrContext** pwcontext, wStream* retStream)
|
||||
{
|
||||
WINPR_ASSERT(rcontext);
|
||||
WINPR_ASSERT(pwcontext);
|
||||
|
||||
BOOL ret = FALSE;
|
||||
*pwcontext = NULL;
|
||||
NdrContext* wcontext = ndr_context_copy(rcontext);
|
||||
if (!wcontext)
|
||||
return FALSE;
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(retStream, sizeof(payloadHeader)))
|
||||
goto out;
|
||||
|
||||
Stream_Write(retStream, payloadHeader, sizeof(payloadHeader));
|
||||
|
||||
if (!ndr_write_header(wcontext, retStream) || !ndr_start_constructed(wcontext, retStream) ||
|
||||
!ndr_write_pickle(wcontext, retStream) || /* pickle header */
|
||||
!ndr_write_uint16(wcontext, retStream, callId) || /* callId */
|
||||
!ndr_write_uint16(wcontext, retStream, 0x0000) || /* align padding */
|
||||
!ndr_write_uint32(wcontext, retStream, status) || /* status */
|
||||
!ndr_write_uint16(wcontext, retStream, callId) || /* callId */
|
||||
!ndr_write_uint16(wcontext, retStream, 0x0000)) /* align padding */
|
||||
goto out;
|
||||
|
||||
*pwcontext = wcontext;
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BOOL rdpear_kerb_version(RDPEAR_PLUGIN* rdpear, IWTSVirtualChannelCallback* pChannelCallback,
|
||||
NdrContext* rcontext, wStream* s, UINT32* pstatus, UINT32* pversion)
|
||||
{
|
||||
*pstatus = ERROR_INVALID_DATA;
|
||||
|
||||
if (!ndr_read_uint32(rcontext, s, pversion))
|
||||
return TRUE;
|
||||
|
||||
WLog_DBG(TAG, "-> KerbNegotiateVersion(v=0x%x)", *pversion);
|
||||
*pstatus = 0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdpear_kerb_ComputeTgsChecksum(RDPEAR_PLUGIN* rdpear,
|
||||
IWTSVirtualChannelCallback* pChannelCallback,
|
||||
NdrContext* rcontext, wStream* s, UINT32* pstatus,
|
||||
KERB_ASN1_DATA* resp)
|
||||
{
|
||||
ComputeTgsChecksumReq req = { 0 };
|
||||
krb5_checksum checksum = { 0 };
|
||||
wStream* asn1Payload = NULL;
|
||||
|
||||
*pstatus = ERROR_INVALID_DATA;
|
||||
WLog_DBG(TAG, "-> ComputeTgsChecksum");
|
||||
|
||||
if (!ndr_read_ComputeTgsChecksumReq(rcontext, s, NULL, &req) ||
|
||||
!ndr_treat_deferred_read(rcontext, s))
|
||||
goto out;
|
||||
// ComputeTgsChecksumReq_dump(WLog_Get(""), WLOG_DEBUG, &req);
|
||||
|
||||
krb5_error_code rv =
|
||||
kerb_do_checksum(rdpear->krbContext, req.Key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
|
||||
(krb5_cksumtype)req.ChecksumType, req.requestBody, &checksum);
|
||||
if (rv)
|
||||
goto out;
|
||||
|
||||
asn1Payload = rdpear_enc_Checksum(req.ChecksumType, &checksum);
|
||||
if (!asn1Payload)
|
||||
goto out;
|
||||
|
||||
resp->Pdu = 8;
|
||||
resp->Asn1Buffer = Stream_Buffer(asn1Payload);
|
||||
resp->Asn1BufferHints.count = Stream_GetPosition(asn1Payload);
|
||||
*pstatus = 0;
|
||||
|
||||
out:
|
||||
ndr_destroy_ComputeTgsChecksumReq(rcontext, NULL, &req);
|
||||
krb5_free_checksum_contents(rdpear->krbContext, &checksum);
|
||||
Stream_Free(asn1Payload, FALSE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdpear_kerb_BuildEncryptedAuthData(RDPEAR_PLUGIN* rdpear,
|
||||
IWTSVirtualChannelCallback* pChannelCallback,
|
||||
NdrContext* rcontext, wStream* s, UINT32* pstatus,
|
||||
KERB_ASN1_DATA* asn1)
|
||||
{
|
||||
BuildEncryptedAuthDataReq req = { 0 };
|
||||
krb5_data encrypted = { 0 };
|
||||
wStream* asn1Payload = NULL;
|
||||
krb5_error_code rv = 0;
|
||||
|
||||
*pstatus = ERROR_INVALID_DATA;
|
||||
WLog_DBG(TAG, "-> BuildEncryptedAuthData");
|
||||
|
||||
if (!ndr_read_BuildEncryptedAuthDataReq(rcontext, s, NULL, &req) ||
|
||||
!ndr_treat_deferred_read(rcontext, s))
|
||||
goto out;
|
||||
|
||||
rv = kerb_do_encrypt(rdpear->krbContext, req.Key, (krb5_keyusage)req.KeyUsage,
|
||||
req.PlainAuthData, &encrypted);
|
||||
if (rv)
|
||||
goto out;
|
||||
|
||||
/* do the encoding */
|
||||
asn1Payload = rdpear_enc_EncryptedData(req.Key->reserved2, &encrypted);
|
||||
if (!asn1Payload)
|
||||
goto out;
|
||||
|
||||
// WLog_DBG(TAG, "rdpear_kerb_BuildEncryptedAuthData resp=");
|
||||
// winpr_HexDump(TAG, WLOG_DEBUG, Stream_Buffer(asn1Payload), Stream_GetPosition(asn1Payload));
|
||||
asn1->Pdu = 6;
|
||||
asn1->Asn1Buffer = Stream_Buffer(asn1Payload);
|
||||
asn1->Asn1BufferHints.count = Stream_GetPosition(asn1Payload);
|
||||
*pstatus = 0;
|
||||
|
||||
out:
|
||||
krb5_free_data_contents(rdpear->krbContext, &encrypted);
|
||||
ndr_destroy_BuildEncryptedAuthDataReq(rcontext, NULL, &req);
|
||||
Stream_Free(asn1Payload, FALSE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static char* KERB_RPC_UNICODESTR_to_charptr(const RPC_UNICODE_STRING* src)
|
||||
{
|
||||
WINPR_ASSERT(src);
|
||||
return ConvertWCharNToUtf8Alloc(src->Buffer, src->strLength, NULL);
|
||||
}
|
||||
|
||||
static BOOL extractAuthData(const KERB_ASN1_DATA* src, krb5_authdata* authData, BOOL* haveData)
|
||||
{
|
||||
WinPrAsn1Decoder dec, dec2, dec3;
|
||||
WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, src->Asn1Buffer, src->Asn1BufferHints.count);
|
||||
BOOL error = FALSE;
|
||||
WinPrAsn1_INTEGER adType;
|
||||
WinPrAsn1_OctetString os;
|
||||
|
||||
*haveData = FALSE;
|
||||
if (!WinPrAsn1DecReadSequence(&dec, &dec2))
|
||||
return FALSE;
|
||||
|
||||
wStream subStream = WinPrAsn1DecGetStream(&dec2);
|
||||
if (!Stream_GetRemainingLength(&subStream))
|
||||
return TRUE;
|
||||
|
||||
if (!WinPrAsn1DecReadSequence(&dec2, &dec3))
|
||||
return FALSE;
|
||||
|
||||
if (!WinPrAsn1DecReadContextualInteger(&dec3, 0, &error, &adType) ||
|
||||
!WinPrAsn1DecReadContextualOctetString(&dec3, 1, &error, &os, FALSE))
|
||||
return FALSE;
|
||||
|
||||
authData->ad_type = adType;
|
||||
authData->length = os.len;
|
||||
authData->contents = os.data;
|
||||
*haveData = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL extractChecksum(const KERB_ASN1_DATA* src, krb5_checksum* dst)
|
||||
{
|
||||
WinPrAsn1Decoder dec, dec2;
|
||||
WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, src->Asn1Buffer, src->Asn1BufferHints.count);
|
||||
BOOL error = FALSE;
|
||||
WinPrAsn1_OctetString os;
|
||||
|
||||
if (!WinPrAsn1DecReadSequence(&dec, &dec2))
|
||||
return FALSE;
|
||||
|
||||
WinPrAsn1_INTEGER cksumtype;
|
||||
if (!WinPrAsn1DecReadContextualInteger(&dec2, 0, &error, &cksumtype) ||
|
||||
!WinPrAsn1DecReadContextualOctetString(&dec2, 1, &error, &os, FALSE))
|
||||
return FALSE;
|
||||
|
||||
dst->checksum_type = cksumtype;
|
||||
dst->length = os.len;
|
||||
dst->contents = os.data;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#define FILETIME_TO_UNIX_OFFSET_S 11644473600LL
|
||||
|
||||
static LONGLONG krb5_time_to_FILETIME(const krb5_timestamp* ts, krb5_int32 usec)
|
||||
{
|
||||
WINPR_ASSERT(ts);
|
||||
return (((*ts + FILETIME_TO_UNIX_OFFSET_S) * (1000LL * 1000LL) + usec) * 10LL);
|
||||
}
|
||||
|
||||
static void krb5_free_principal_contents(krb5_context ctx, krb5_principal principal)
|
||||
{
|
||||
WINPR_ASSERT(principal);
|
||||
krb5_free_data_contents(ctx, &principal->realm);
|
||||
krb5_free_data(ctx, principal->data);
|
||||
}
|
||||
|
||||
static BOOL rdpear_kerb_CreateApReqAuthenticator(RDPEAR_PLUGIN* rdpear,
|
||||
IWTSVirtualChannelCallback* pChannelCallback,
|
||||
NdrContext* rcontext, wStream* s, UINT32* pstatus,
|
||||
CreateApReqAuthenticatorResp* resp)
|
||||
{
|
||||
krb5_error_code rv = 0;
|
||||
wStream* asn1EncodedAuth = NULL;
|
||||
CreateApReqAuthenticatorReq req = { 0 };
|
||||
krb5_data authenticator = { 0 };
|
||||
krb5_data* der = NULL;
|
||||
krb5_keyblock* subkey = NULL;
|
||||
krb5_principal_data client = { 0 };
|
||||
|
||||
*pstatus = ERROR_INVALID_DATA;
|
||||
WLog_DBG(TAG, "-> CreateApReqAuthenticator");
|
||||
|
||||
if (!ndr_read_CreateApReqAuthenticatorReq(rcontext, s, NULL, &req) ||
|
||||
!ndr_treat_deferred_read(rcontext, s))
|
||||
goto out;
|
||||
|
||||
krb5_authdata authdata;
|
||||
krb5_authdata* authDataPtr[2] = { &authdata, NULL };
|
||||
BOOL haveData;
|
||||
|
||||
if (!extractAuthData(req.AuthData, &authdata, &haveData))
|
||||
{
|
||||
WLog_ERR(TAG, "error retrieving auth data");
|
||||
winpr_HexDump(TAG, WLOG_DEBUG, req.AuthData->Asn1Buffer,
|
||||
req.AuthData->Asn1BufferHints.count);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (req.SkewTime->QuadPart)
|
||||
{
|
||||
WLog_ERR(TAG, "!!!!! should handle SkewTime !!!!!");
|
||||
}
|
||||
|
||||
if (req.SubKey)
|
||||
{
|
||||
rv = RPC_ENCRYPTION_KEY_to_keyblock(rdpear->krbContext, req.SubKey, &subkey);
|
||||
if (rv)
|
||||
{
|
||||
WLog_ERR(TAG, "error importing subkey");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
krb5_authenticator authent = { .checksum = NULL,
|
||||
.subkey = NULL,
|
||||
.seq_number = req.SequenceNumber,
|
||||
.authorization_data = haveData ? authDataPtr : NULL };
|
||||
|
||||
client.type = req.ClientName->NameType;
|
||||
client.length = (krb5_int32)req.ClientName->nameHints.count;
|
||||
client.data = calloc(client.length, sizeof(krb5_data));
|
||||
if (!client.data)
|
||||
goto out;
|
||||
|
||||
for (int i = 0; i < client.length; i++)
|
||||
{
|
||||
client.data[i].data = KERB_RPC_UNICODESTR_to_charptr(&req.ClientName->Names[i]);
|
||||
if (!client.data[i].data)
|
||||
goto out;
|
||||
client.data[i].length = strlen(client.data[i].data);
|
||||
}
|
||||
client.realm.data = KERB_RPC_UNICODESTR_to_charptr(req.ClientRealm);
|
||||
if (!client.realm.data)
|
||||
goto out;
|
||||
client.realm.length = strlen(client.realm.data);
|
||||
authent.client = &client;
|
||||
|
||||
krb5_checksum checksum;
|
||||
krb5_checksum* pchecksum = NULL;
|
||||
if (req.GssChecksum)
|
||||
{
|
||||
if (!extractChecksum(req.GssChecksum, &checksum))
|
||||
{
|
||||
WLog_ERR(TAG, "Error getting the checksum");
|
||||
goto out;
|
||||
}
|
||||
pchecksum = &checksum;
|
||||
}
|
||||
authent.checksum = pchecksum;
|
||||
|
||||
krb5_us_timeofday(rdpear->krbContext, &authent.ctime, &authent.cusec);
|
||||
|
||||
rv = encode_krb5_authenticator(&authent, &der);
|
||||
if (rv)
|
||||
{
|
||||
WLog_ERR(TAG, "error encoding authenticator");
|
||||
goto out;
|
||||
}
|
||||
|
||||
KERB_ASN1_DATA plain_authent = { .Pdu = 0,
|
||||
.Asn1Buffer = (BYTE*)der->data,
|
||||
.Asn1BufferHints = { .count = der->length } };
|
||||
|
||||
rv = kerb_do_encrypt(rdpear->krbContext, req.EncryptionKey, (krb5_keyusage)req.KeyUsage,
|
||||
&plain_authent, &authenticator);
|
||||
if (rv)
|
||||
{
|
||||
WLog_ERR(TAG, "error encrypting authenticator");
|
||||
goto out;
|
||||
}
|
||||
|
||||
asn1EncodedAuth = rdpear_enc_EncryptedData(req.EncryptionKey->reserved2, &authenticator);
|
||||
if (!asn1EncodedAuth)
|
||||
{
|
||||
WLog_ERR(TAG, "error encoding to ASN1");
|
||||
rv = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
// WLog_DBG(TAG, "authenticator=");
|
||||
// winpr_HexDump(TAG, WLOG_DEBUG, Stream_Buffer(asn1EncodedAuth),
|
||||
// Stream_GetPosition(asn1EncodedAuth));
|
||||
|
||||
resp->Authenticator.Asn1BufferHints.count = Stream_GetPosition(asn1EncodedAuth);
|
||||
resp->Authenticator.Asn1Buffer = Stream_Buffer(asn1EncodedAuth);
|
||||
resp->AuthenticatorTime.QuadPart = krb5_time_to_FILETIME(&authent.ctime, authent.cusec);
|
||||
*pstatus = 0;
|
||||
|
||||
out:
|
||||
resp->Authenticator.Pdu = 6;
|
||||
resp->KerbProtocolError = rv;
|
||||
krb5_free_principal_contents(rdpear->krbContext, &client);
|
||||
krb5_free_data(rdpear->krbContext, der);
|
||||
krb5_free_data_contents(rdpear->krbContext, &authenticator);
|
||||
if (subkey)
|
||||
krb5_free_keyblock(rdpear->krbContext, subkey);
|
||||
ndr_destroy_CreateApReqAuthenticatorReq(rcontext, NULL, &req);
|
||||
Stream_Free(asn1EncodedAuth, FALSE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdpear_findEncryptedData(const KERB_ASN1_DATA* src, int* penctype, krb5_data* data)
|
||||
{
|
||||
WinPrAsn1Decoder dec, dec2;
|
||||
WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, src->Asn1Buffer, src->Asn1BufferHints.count);
|
||||
BOOL error = FALSE;
|
||||
WinPrAsn1_INTEGER encType;
|
||||
WinPrAsn1_OctetString os;
|
||||
|
||||
if (!WinPrAsn1DecReadSequence(&dec, &dec2) ||
|
||||
!WinPrAsn1DecReadContextualInteger(&dec2, 0, &error, &encType) ||
|
||||
!WinPrAsn1DecReadContextualOctetString(&dec2, 2, &error, &os, FALSE))
|
||||
return FALSE;
|
||||
|
||||
data->data = (char*)os.data;
|
||||
data->length = os.len;
|
||||
*penctype = encType;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdpear_kerb_UnpackKdcReplyBody(RDPEAR_PLUGIN* rdpear,
|
||||
IWTSVirtualChannelCallback* pChannelCallback,
|
||||
NdrContext* rcontext, wStream* s, UINT32* pstatus,
|
||||
UnpackKdcReplyBodyResp* resp)
|
||||
{
|
||||
UnpackKdcReplyBodyReq req = { 0 };
|
||||
|
||||
*pstatus = ERROR_INVALID_DATA;
|
||||
|
||||
if (!ndr_read_UnpackKdcReplyBodyReq(rcontext, s, NULL, &req) ||
|
||||
!ndr_treat_deferred_read(rcontext, s))
|
||||
goto out;
|
||||
|
||||
if (req.StrengthenKey)
|
||||
{
|
||||
WLog_ERR(TAG, "StrengthenKey not supported yet");
|
||||
goto out;
|
||||
}
|
||||
|
||||
WLog_DBG(TAG, "-> UnpackKdcReplyBody: KeyUsage=0x%x PDU=0x%x", req.KeyUsage, req.Pdu);
|
||||
// WLog_DBG(TAG, "encryptedPayload=");
|
||||
// winpr_HexDump(TAG, WLOG_DEBUG, req.EncryptedData->Asn1Buffer,
|
||||
// req.EncryptedData->Asn1BufferHints.count);
|
||||
|
||||
krb5_data asn1Data;
|
||||
int encType;
|
||||
if (!rdpear_findEncryptedData(req.EncryptedData, &encType, &asn1Data) || !asn1Data.length)
|
||||
goto out;
|
||||
|
||||
resp->KerbProtocolError = kerb_do_decrypt(
|
||||
rdpear->krbContext, req.Key, (krb5_keyusage)req.KeyUsage, &asn1Data, &resp->ReplyBody);
|
||||
resp->ReplyBody.Pdu = req.Pdu;
|
||||
|
||||
*pstatus = 0;
|
||||
|
||||
out:
|
||||
ndr_destroy_UnpackKdcReplyBodyReq(rcontext, NULL, &req);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdpear_kerb_DecryptApReply(RDPEAR_PLUGIN* rdpear,
|
||||
IWTSVirtualChannelCallback* pChannelCallback,
|
||||
NdrContext* rcontext, wStream* s, UINT32* pstatus,
|
||||
KERB_ASN1_DATA* resp)
|
||||
{
|
||||
DecryptApReplyReq req = { 0 };
|
||||
|
||||
*pstatus = ERROR_INVALID_DATA;
|
||||
if (!ndr_read_DecryptApReplyReq(rcontext, s, NULL, &req) ||
|
||||
!ndr_treat_deferred_read(rcontext, s))
|
||||
goto out;
|
||||
|
||||
WLog_DBG(TAG, "-> DecryptApReply");
|
||||
// winpr_HexDump(TAG, WLOG_DEBUG, req.EncryptedReply->Asn1Buffer,
|
||||
// req.EncryptedReply->Asn1BufferHints.count);
|
||||
|
||||
krb5_data asn1Data;
|
||||
int encType;
|
||||
if (!rdpear_findEncryptedData(req.EncryptedReply, &encType, &asn1Data) || !asn1Data.length)
|
||||
goto out;
|
||||
|
||||
resp->Pdu = 0x31;
|
||||
krb5_error_code rv =
|
||||
kerb_do_decrypt(rdpear->krbContext, req.Key, KRB5_KEYUSAGE_AP_REP_ENCPART, &asn1Data, resp);
|
||||
if (rv != 0)
|
||||
{
|
||||
WLog_ERR(TAG, "error decrypting");
|
||||
goto out;
|
||||
}
|
||||
|
||||
// WLog_DBG(TAG, "response=");
|
||||
// winpr_HexDump(TAG, WLOG_DEBUG, resp->Asn1Buffer, resp->Asn1BufferHints.count);
|
||||
*pstatus = 0;
|
||||
out:
|
||||
ndr_destroy_DecryptApReplyReq(rcontext, NULL, &req);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdpear_kerb_PackApReply(RDPEAR_PLUGIN* rdpear,
|
||||
IWTSVirtualChannelCallback* pChannelCallback,
|
||||
NdrContext* rcontext, wStream* s, UINT32* pstatus,
|
||||
PackApReplyResp* resp)
|
||||
{
|
||||
PackApReplyReq req = { 0 };
|
||||
krb5_data asn1Data = { 0 };
|
||||
krb5_data* out = NULL;
|
||||
|
||||
WLog_DBG(TAG, "-> PackApReply");
|
||||
*pstatus = ERROR_INVALID_DATA;
|
||||
if (!ndr_read_PackApReplyReq(rcontext, s, NULL, &req) || !ndr_treat_deferred_read(rcontext, s))
|
||||
goto out;
|
||||
|
||||
krb5_error_code rv = kerb_do_encrypt(rdpear->krbContext, req.SessionKey,
|
||||
KRB5_KEYUSAGE_AP_REP_ENCPART, req.ReplyBody, &asn1Data);
|
||||
if (rv)
|
||||
goto out;
|
||||
|
||||
krb5_ap_rep reply;
|
||||
reply.enc_part.kvno = KRB5_PVNO;
|
||||
reply.enc_part.enctype = (krb5_enctype)req.SessionKey->reserved2;
|
||||
reply.enc_part.ciphertext.length = asn1Data.length;
|
||||
reply.enc_part.ciphertext.data = asn1Data.data;
|
||||
|
||||
rv = encode_krb5_ap_rep(&reply, &out);
|
||||
if (rv)
|
||||
goto out;
|
||||
|
||||
resp->PackedReply = (BYTE*)out->data;
|
||||
resp->PackedReplyHints.count = out->length;
|
||||
*pstatus = 0;
|
||||
out:
|
||||
free(out);
|
||||
krb5_free_data_contents(rdpear->krbContext, &asn1Data);
|
||||
ndr_destroy_PackApReplyReq(rcontext, NULL, &req);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static UINT rdpear_decode_payload(RDPEAR_PLUGIN* rdpear,
|
||||
IWTSVirtualChannelCallback* pChannelCallback,
|
||||
RdpEarPackageType packageType, wStream* s)
|
||||
{
|
||||
UINT ret = ERROR_INVALID_DATA;
|
||||
NdrContext* context = NULL;
|
||||
NdrContext* wcontext = NULL;
|
||||
UINT32 status = 0;
|
||||
|
||||
UINT32 uint32Resp = 0;
|
||||
KERB_ASN1_DATA asn1Data = { 0 };
|
||||
CreateApReqAuthenticatorResp createApReqAuthenticatorResp = { 0 };
|
||||
UnpackKdcReplyBodyResp unpackKdcReplyBodyResp = { 0 };
|
||||
PackApReplyResp packApReplyResp = { 0 };
|
||||
|
||||
void* resp = NULL;
|
||||
NdrMessageType respDescr = NULL;
|
||||
|
||||
wStream* respStream = Stream_New(NULL, 500);
|
||||
if (!respStream)
|
||||
goto out;
|
||||
|
||||
Stream_Seek(s, 16); /* skip first 16 bytes */
|
||||
|
||||
wStream commandStream;
|
||||
UINT16 callId, callId2;
|
||||
|
||||
context = ndr_read_header(s);
|
||||
if (!context || !ndr_read_constructed(context, s, &commandStream) ||
|
||||
!ndr_read_pickle(context, &commandStream) ||
|
||||
!ndr_read_uint16(context, &commandStream, &callId) ||
|
||||
!ndr_read_uint16(context, &commandStream, &callId2) || (callId != callId2))
|
||||
goto out;
|
||||
|
||||
ret = CHANNEL_RC_NOT_OPEN;
|
||||
switch (callId)
|
||||
{
|
||||
case RemoteCallKerbNegotiateVersion:
|
||||
resp = &uint32Resp;
|
||||
respDescr = ndr_uint32_descr();
|
||||
|
||||
if (rdpear_kerb_version(rdpear, pChannelCallback, context, &commandStream, &status,
|
||||
&uint32Resp))
|
||||
ret = CHANNEL_RC_OK;
|
||||
break;
|
||||
case RemoteCallKerbCreateApReqAuthenticator:
|
||||
resp = &createApReqAuthenticatorResp;
|
||||
respDescr = ndr_CreateApReqAuthenticatorResp_descr();
|
||||
|
||||
if (rdpear_kerb_CreateApReqAuthenticator(rdpear, pChannelCallback, context,
|
||||
&commandStream, &status,
|
||||
&createApReqAuthenticatorResp))
|
||||
ret = CHANNEL_RC_OK;
|
||||
break;
|
||||
case RemoteCallKerbDecryptApReply:
|
||||
resp = &asn1Data;
|
||||
respDescr = ndr_KERB_ASN1_DATA_descr();
|
||||
|
||||
if (rdpear_kerb_DecryptApReply(rdpear, pChannelCallback, context, &commandStream,
|
||||
&status, &asn1Data))
|
||||
ret = CHANNEL_RC_OK;
|
||||
break;
|
||||
case RemoteCallKerbComputeTgsChecksum:
|
||||
resp = &asn1Data;
|
||||
respDescr = ndr_KERB_ASN1_DATA_descr();
|
||||
|
||||
if (rdpear_kerb_ComputeTgsChecksum(rdpear, pChannelCallback, context, &commandStream,
|
||||
&status, &asn1Data))
|
||||
ret = CHANNEL_RC_OK;
|
||||
break;
|
||||
case RemoteCallKerbBuildEncryptedAuthData:
|
||||
resp = &asn1Data;
|
||||
respDescr = ndr_KERB_ASN1_DATA_descr();
|
||||
|
||||
if (rdpear_kerb_BuildEncryptedAuthData(rdpear, pChannelCallback, context,
|
||||
&commandStream, &status, &asn1Data))
|
||||
ret = CHANNEL_RC_OK;
|
||||
break;
|
||||
case RemoteCallKerbUnpackKdcReplyBody:
|
||||
resp = &unpackKdcReplyBodyResp;
|
||||
respDescr = ndr_UnpackKdcReplyBodyResp_descr();
|
||||
|
||||
if (rdpear_kerb_UnpackKdcReplyBody(rdpear, pChannelCallback, context, &commandStream,
|
||||
&status, &unpackKdcReplyBodyResp))
|
||||
ret = CHANNEL_RC_OK;
|
||||
break;
|
||||
case RemoteCallKerbPackApReply:
|
||||
resp = &packApReplyResp;
|
||||
respDescr = ndr_PackApReplyResp_descr();
|
||||
|
||||
if (rdpear_kerb_PackApReply(rdpear, pChannelCallback, context, &commandStream, &status,
|
||||
&packApReplyResp))
|
||||
ret = CHANNEL_RC_OK;
|
||||
break;
|
||||
|
||||
case RemoteCallNtlmNegotiateVersion:
|
||||
WLog_ERR(TAG, "don't wanna support NTLM");
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_DBG(TAG, "Unhandled callId=0x%x", callId);
|
||||
winpr_HexDump(TAG, WLOG_DEBUG, Stream_PointerAs(&commandStream, BYTE),
|
||||
Stream_GetRemainingLength(&commandStream));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!rdpear_prepare_response(context, callId, status, &wcontext, respStream))
|
||||
goto out;
|
||||
|
||||
if (resp && respDescr)
|
||||
{
|
||||
WINPR_ASSERT(respDescr->writeFn);
|
||||
|
||||
BOOL r = respDescr->writeFn(wcontext, respStream, NULL, resp) &&
|
||||
ndr_treat_deferred_write(wcontext, respStream);
|
||||
|
||||
if (respDescr->destroyFn)
|
||||
respDescr->destroyFn(wcontext, NULL, resp);
|
||||
|
||||
if (!r)
|
||||
{
|
||||
WLog_DBG(TAG, "!writeFn || !ndr_treat_deferred_write");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ndr_end_constructed(wcontext, respStream) ||
|
||||
!rdpear_send_payload(rdpear, pChannelCallback, RDPEAR_PACKAGE_KERBEROS, respStream))
|
||||
{
|
||||
WLog_DBG(TAG, "rdpear_send_payload !!!!!!!!");
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
if (context)
|
||||
ndr_context_destroy(&context);
|
||||
|
||||
if (wcontext)
|
||||
ndr_context_destroy(&wcontext);
|
||||
|
||||
if (respStream)
|
||||
Stream_Free(respStream, TRUE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static UINT rdpear_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* s)
|
||||
{
|
||||
GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
|
||||
WINPR_ASSERT(callback);
|
||||
UINT ret = ERROR_INVALID_DATA;
|
||||
|
||||
// winpr_HexDump(TAG, WLOG_DEBUG, Stream_PointerAs(s, BYTE), Stream_GetRemainingLength(s));
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 24))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
UINT32 protocolMagic, Length, Version;
|
||||
Stream_Read_UINT32(s, protocolMagic);
|
||||
if (protocolMagic != 0x4EACC3C8)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, Length);
|
||||
|
||||
Stream_Read_UINT32(s, Version);
|
||||
if (Version != 0x00000000)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Seek(s, 4); /* Reserved (4 bytes) */
|
||||
Stream_Seek(s, 8); /* TsPkgContext (8 bytes) */
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, Length))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
SecBuffer inBuffer = { Length, SECBUFFER_TOKEN, Stream_PointerAs(s, void) };
|
||||
SecBuffer decrypted = { 0 };
|
||||
|
||||
RDPEAR_PLUGIN* rdpear = (RDPEAR_PLUGIN*)callback->plugin;
|
||||
WINPR_ASSERT(rdpear);
|
||||
if (!freerdp_nla_decrypt(rdpear->rdp_context, &inBuffer, &decrypted))
|
||||
goto out;
|
||||
|
||||
WinPrAsn1Decoder dec, dec2;
|
||||
wStream decodedStream;
|
||||
Stream_StaticInit(&decodedStream, decrypted.pvBuffer, decrypted.cbBuffer);
|
||||
WinPrAsn1Decoder_Init(&dec, WINPR_ASN1_DER, &decodedStream);
|
||||
|
||||
if (!WinPrAsn1DecReadSequence(&dec, &dec2))
|
||||
goto out;
|
||||
|
||||
WinPrAsn1_OctetString packageName;
|
||||
WinPrAsn1_OctetString payload;
|
||||
BOOL error;
|
||||
if (!WinPrAsn1DecReadContextualOctetString(&dec2, 1, &error, &packageName, FALSE))
|
||||
goto out;
|
||||
|
||||
RdpEarPackageType packageType = rdpear_packageType_from_name(&packageName);
|
||||
|
||||
if (!WinPrAsn1DecReadContextualOctetString(&dec2, 2, &error, &payload, FALSE))
|
||||
goto out;
|
||||
|
||||
wStream payloadStream;
|
||||
Stream_StaticInit(&payloadStream, payload.data, payload.len);
|
||||
|
||||
ret = rdpear_decode_payload(rdpear, pChannelCallback, packageType, &payloadStream);
|
||||
out:
|
||||
sspi_SecBufferFree(&decrypted);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpear_on_open(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
{
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpear_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
return error;
|
||||
}
|
||||
|
||||
static void terminate_plugin_cb(GENERIC_DYNVC_PLUGIN* base)
|
||||
{
|
||||
WINPR_ASSERT(base);
|
||||
|
||||
RDPEAR_PLUGIN* rdpear = (RDPEAR_PLUGIN*)base;
|
||||
krb5_free_context(rdpear->krbContext);
|
||||
}
|
||||
|
||||
static UINT init_plugin_cb(GENERIC_DYNVC_PLUGIN* base, rdpContext* rcontext, rdpSettings* settings)
|
||||
{
|
||||
WINPR_ASSERT(base);
|
||||
|
||||
RDPEAR_PLUGIN* rdpear = (RDPEAR_PLUGIN*)base;
|
||||
rdpear->rdp_context = rcontext;
|
||||
if (krb5_init_context(&rdpear->krbContext))
|
||||
return CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static const IWTSVirtualChannelCallback rdpear_callbacks = { rdpear_on_data_received,
|
||||
rdpear_on_open, rdpear_on_close };
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT rdpear_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
|
||||
{
|
||||
return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, RDPEAR_DVC_CHANNEL_NAME,
|
||||
sizeof(RDPEAR_PLUGIN), sizeof(GENERIC_CHANNEL_CALLBACK),
|
||||
&rdpear_callbacks, init_plugin_cb, terminate_plugin_cb);
|
||||
}
|
43
channels/rdpear/common/CMakeLists.txt
Normal file
43
channels/rdpear/common/CMakeLists.txt
Normal file
@ -0,0 +1,43 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2024 David Fort <contact@hardening-consulting.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.
|
||||
|
||||
add_library(rdpear-common INTERFACE)
|
||||
add_library(rdpear-common-obj OBJECT
|
||||
ndr.c
|
||||
rdpear_asn1.c
|
||||
rdpear_common.c
|
||||
rdpear-common/ndr.h
|
||||
rdpear-common/rdpear_asn1.h
|
||||
rdpear-common/rdpear_common.h
|
||||
)
|
||||
|
||||
target_include_directories(rdpear-common
|
||||
INTERFACE ${KRB5_INCLUDE_DIRS})
|
||||
target_include_directories(rdpear-common-obj
|
||||
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${KRB5_INCLUDE_DIRS})
|
||||
|
||||
target_link_directories(rdpear-common INTERFACE ${KRB5_LIBRARY_DIRS})
|
||||
target_link_libraries(rdpear-common INTERFACE
|
||||
${KRB5_LIBRARIES}
|
||||
$<TARGET_OBJECTS:rdpear-common-obj>
|
||||
)
|
||||
|
||||
channel_install(rdpear-common ${FREERDP_ADDIN_PATH} "FreeRDPTargets")
|
||||
|
||||
if (BUILD_TESTING)
|
||||
add_subdirectory(test)
|
||||
endif()
|
994
channels/rdpear/common/ndr.c
Normal file
994
channels/rdpear/common/ndr.c
Normal file
@ -0,0 +1,994 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Authentication redirection virtual channel
|
||||
*
|
||||
* Copyright 2024 David Fort <contact@hardening-consulting.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.
|
||||
*/
|
||||
#include <winpr/assert.h>
|
||||
#include <winpr/collections.h>
|
||||
#include <winpr/wlog.h>
|
||||
|
||||
#include <freerdp/log.h>
|
||||
|
||||
#include <rdpear-common/ndr.h>
|
||||
|
||||
#define TAG FREERDP_TAG("ndr")
|
||||
|
||||
#define NDR_MAX_CONSTRUCTS 16
|
||||
#define NDR_MAX_DEFERRED 50
|
||||
|
||||
/** @brief */
|
||||
struct NdrContext_s
|
||||
{
|
||||
BYTE version;
|
||||
BOOL bigEndianDrep;
|
||||
size_t alignBytes;
|
||||
|
||||
int currentLevel;
|
||||
size_t indentLevels[16];
|
||||
|
||||
int constructLevel;
|
||||
size_t constructs[NDR_MAX_CONSTRUCTS];
|
||||
|
||||
wHashTable* refPointers;
|
||||
size_t ndeferred;
|
||||
NdrDeferredEntry deferred[NDR_MAX_DEFERRED];
|
||||
|
||||
UINT32 refIdCounter;
|
||||
};
|
||||
|
||||
NdrContext* ndr_context_new(BOOL bigEndianDrep, BYTE version)
|
||||
{
|
||||
NdrContext* ret = calloc(1, sizeof(*ret));
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
ret->version = version;
|
||||
ret->bigEndianDrep = bigEndianDrep;
|
||||
ret->alignBytes = 4;
|
||||
ret->refPointers = HashTable_New(FALSE);
|
||||
if (!ret->refPointers)
|
||||
{
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ndr_context_reset(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ndr_context_reset(NdrContext* context)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
context->currentLevel = 0;
|
||||
context->constructLevel = -1;
|
||||
memset(context->indentLevels, 0, sizeof(context->indentLevels));
|
||||
|
||||
if (context->refPointers)
|
||||
HashTable_Clear(context->refPointers);
|
||||
context->ndeferred = 0;
|
||||
context->refIdCounter = 0x20000;
|
||||
}
|
||||
|
||||
NdrContext* ndr_context_copy(const NdrContext* src)
|
||||
{
|
||||
WINPR_ASSERT(src);
|
||||
|
||||
NdrContext* ret = calloc(1, sizeof(*ret));
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
*ret = *src;
|
||||
|
||||
ret->refPointers = HashTable_New(FALSE);
|
||||
if (!ret->refPointers)
|
||||
{
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ndr_context_reset(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ndr_context_destroy(NdrContext** pcontext)
|
||||
{
|
||||
WINPR_ASSERT(pcontext);
|
||||
|
||||
NdrContext* context = *pcontext;
|
||||
if (context)
|
||||
{
|
||||
HashTable_Free(context->refPointers);
|
||||
free(context);
|
||||
}
|
||||
*pcontext = NULL;
|
||||
}
|
||||
|
||||
void ndr_context_bytes_read(NdrContext* context, size_t len)
|
||||
{
|
||||
context->indentLevels[context->currentLevel] += len;
|
||||
}
|
||||
|
||||
void ndr_context_bytes_written(NdrContext* context, size_t len)
|
||||
{
|
||||
ndr_context_bytes_read(context, len);
|
||||
}
|
||||
|
||||
NdrContext* ndr_read_header(wStream* s)
|
||||
{
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
|
||||
return NULL;
|
||||
|
||||
BYTE version, drep;
|
||||
Stream_Read_UINT8(s, version);
|
||||
Stream_Read_UINT8(s, drep);
|
||||
|
||||
UINT16 headerLen;
|
||||
Stream_Read_UINT16(s, headerLen);
|
||||
|
||||
if (headerLen < 4 || !Stream_CheckAndLogRequiredLength(TAG, s, headerLen - 4))
|
||||
return NULL;
|
||||
|
||||
/* skip filler */
|
||||
Stream_Seek(s, headerLen - 4);
|
||||
|
||||
return ndr_context_new((drep != 0x10), version);
|
||||
}
|
||||
|
||||
BOOL ndr_write_header(NdrContext* context, wStream* s)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, 8))
|
||||
return FALSE;
|
||||
|
||||
Stream_Write_UINT8(s, context->version);
|
||||
Stream_Write_UINT8(s, context->bigEndianDrep ? 0x00 : 0x10);
|
||||
Stream_Write_UINT16(s, 0x8); /* header len */
|
||||
|
||||
BYTE filler[] = { 0xcc, 0xcc, 0xcc, 0xcc };
|
||||
Stream_Write(s, filler, sizeof(filler));
|
||||
return TRUE;
|
||||
}
|
||||
BOOL ndr_skip_bytes(NdrContext* context, wStream* s, size_t nbytes)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, nbytes))
|
||||
return FALSE;
|
||||
|
||||
context->indentLevels[context->currentLevel] += nbytes;
|
||||
Stream_Seek(s, nbytes);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_read_align(NdrContext* context, wStream* s, size_t sz)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
size_t rest = context->indentLevels[context->currentLevel] % sz;
|
||||
if (rest)
|
||||
{
|
||||
size_t padding = (sz - rest);
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, padding))
|
||||
return FALSE;
|
||||
|
||||
Stream_Seek(s, padding);
|
||||
context->indentLevels[context->currentLevel] += padding;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_write_align(NdrContext* context, wStream* s, size_t sz)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
size_t rest = context->indentLevels[context->currentLevel] % sz;
|
||||
if (rest)
|
||||
{
|
||||
size_t padding = (sz - rest);
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, padding))
|
||||
return FALSE;
|
||||
|
||||
Stream_Zero(s, padding);
|
||||
context->indentLevels[context->currentLevel] += padding;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_read_pickle(NdrContext* context, wStream* s)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
UINT32 v;
|
||||
|
||||
/* NDR format label */
|
||||
if (!ndr_read_uint32(context, s, &v) || v != 0x20000)
|
||||
return FALSE;
|
||||
|
||||
return ndr_read_uint32(context, s, &v); // padding
|
||||
}
|
||||
|
||||
BOOL ndr_write_pickle(NdrContext* context, wStream* s)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
/* NDR format label */
|
||||
if (!ndr_write_uint32(context, s, 0x20000))
|
||||
return FALSE;
|
||||
|
||||
ndr_write_uint32(context, s, 0); /* padding */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_read_constructed(NdrContext* context, wStream* s, wStream* target)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
UINT32 len;
|
||||
|
||||
/* len */
|
||||
if (!ndr_read_uint32(context, s, &len))
|
||||
return FALSE;
|
||||
|
||||
/* padding */
|
||||
if (!ndr_skip_bytes(context, s, 4))
|
||||
return FALSE;
|
||||
|
||||
/* payload */
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, len))
|
||||
return FALSE;
|
||||
|
||||
Stream_StaticInit(target, Stream_PointerAs(s, BYTE), len);
|
||||
Stream_Seek(s, len);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_start_constructed(NdrContext* context, wStream* s)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
if (!Stream_EnsureCapacity(s, 8))
|
||||
return FALSE;
|
||||
|
||||
if (context->constructLevel == NDR_MAX_CONSTRUCTS)
|
||||
return FALSE;
|
||||
|
||||
context->constructLevel++;
|
||||
context->constructs[context->constructLevel] = Stream_GetPosition(s);
|
||||
|
||||
Stream_Zero(s, 8);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_end_constructed(NdrContext* context, wStream* s)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(context->constructLevel >= 0);
|
||||
|
||||
size_t offset = context->constructs[context->constructLevel];
|
||||
|
||||
wStream staticS;
|
||||
Stream_StaticInit(&staticS, Stream_Buffer(s) + offset, 4);
|
||||
|
||||
/* len */
|
||||
size_t len = Stream_GetPosition(s) - (offset + 8);
|
||||
if (!ndr_write_uint32(context, &staticS, len))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static size_t ndr_hintsCount(NdrMessageType msgType, const void* hints)
|
||||
{
|
||||
WINPR_ASSERT(msgType);
|
||||
|
||||
switch (msgType->arity)
|
||||
{
|
||||
case NDR_ARITY_SIMPLE:
|
||||
return 1;
|
||||
case NDR_ARITY_ARRAYOF:
|
||||
WINPR_ASSERT(hints);
|
||||
return ((const NdrArrayHints*)hints)->count;
|
||||
case NDR_ARITY_VARYING_ARRAYOF:
|
||||
WINPR_ASSERT(hints);
|
||||
return ((const NdrVaryingArrayHints*)hints)->maxLength;
|
||||
default:
|
||||
WINPR_ASSERT(0 && "unknown arity");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Stream_Write_UINT64_BE(wStream* _s, UINT64 _v)
|
||||
{
|
||||
WINPR_ASSERT(FALSE && "implement Stream_Write_UINT64_BE()");
|
||||
}
|
||||
|
||||
BOOL ndr_read_uint8(NdrContext* context, wStream* s, BYTE* v)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
|
||||
return FALSE;
|
||||
|
||||
Stream_Read_UINT8(s, *v);
|
||||
|
||||
ndr_context_bytes_read(context, 1);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_read_uint8_(NdrContext* context, wStream* s, const void* hints, void* v)
|
||||
{
|
||||
return ndr_read_uint8(context, s, (BYTE*)v);
|
||||
}
|
||||
|
||||
BOOL ndr_write_uint8(NdrContext* context, wStream* s, BYTE v)
|
||||
{
|
||||
if (!Stream_EnsureRemainingCapacity(s, 1))
|
||||
return FALSE;
|
||||
|
||||
Stream_Write_UINT8(s, v);
|
||||
ndr_context_bytes_written(context, 1);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_write_uint8_(NdrContext* context, wStream* s, const void* hints, const void* v)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(v);
|
||||
|
||||
return ndr_write_uint8(context, s, *(const BYTE*)v);
|
||||
}
|
||||
|
||||
const static NdrMessageDescr uint8_descr = { NDR_ARITY_SIMPLE, 1, ndr_read_uint8_,
|
||||
ndr_write_uint8_, NULL, NULL };
|
||||
|
||||
NdrMessageType ndr_uint8_descr(void)
|
||||
{
|
||||
return &uint8_descr;
|
||||
}
|
||||
|
||||
#define SIMPLE_TYPE_IMPL(UPPERTYPE, LOWERTYPE) \
|
||||
BOOL ndr_read_##LOWERTYPE(NdrContext* context, wStream* s, UPPERTYPE* v) \
|
||||
{ \
|
||||
WINPR_ASSERT(context); \
|
||||
\
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(UPPERTYPE))) \
|
||||
return FALSE; \
|
||||
\
|
||||
if (!ndr_read_align(context, s, sizeof(UPPERTYPE))) \
|
||||
return FALSE; \
|
||||
\
|
||||
if (context->bigEndianDrep) \
|
||||
Stream_Read_##UPPERTYPE##_BE(s, *v); \
|
||||
else \
|
||||
Stream_Read_##UPPERTYPE(s, *v); \
|
||||
\
|
||||
ndr_context_bytes_read(context, sizeof(UPPERTYPE)); \
|
||||
return TRUE; \
|
||||
} \
|
||||
\
|
||||
BOOL ndr_read_##LOWERTYPE##_(NdrContext* context, wStream* s, const void* hints, void* v) \
|
||||
{ \
|
||||
return ndr_read_##LOWERTYPE(context, s, (UPPERTYPE*)v); \
|
||||
} \
|
||||
\
|
||||
BOOL ndr_write_##LOWERTYPE(NdrContext* context, wStream* s, UPPERTYPE v) \
|
||||
{ \
|
||||
if (!ndr_write_align(context, s, sizeof(UPPERTYPE)) || \
|
||||
!Stream_EnsureRemainingCapacity(s, sizeof(UPPERTYPE))) \
|
||||
return FALSE; \
|
||||
\
|
||||
if (context->bigEndianDrep) \
|
||||
Stream_Write_##UPPERTYPE##_BE(s, v); \
|
||||
else \
|
||||
Stream_Write_##UPPERTYPE(s, v); \
|
||||
\
|
||||
ndr_context_bytes_written(context, sizeof(UPPERTYPE)); \
|
||||
return TRUE; \
|
||||
} \
|
||||
\
|
||||
BOOL ndr_write_##LOWERTYPE##_(NdrContext* context, wStream* s, const void* hints, \
|
||||
const void* v) \
|
||||
{ \
|
||||
WINPR_ASSERT(context); \
|
||||
WINPR_ASSERT(s); \
|
||||
WINPR_ASSERT(v); \
|
||||
\
|
||||
return ndr_write_##LOWERTYPE(context, s, *(const UPPERTYPE*)v); \
|
||||
} \
|
||||
\
|
||||
const NdrMessageDescr ndr_##LOWERTYPE##_descr_s = { \
|
||||
NDR_ARITY_SIMPLE, sizeof(UPPERTYPE), ndr_read_##LOWERTYPE##_, \
|
||||
ndr_write_##LOWERTYPE##_, (NDR_DESTROY_FN)NULL, (NDR_DUMP_FN)NULL \
|
||||
}; \
|
||||
\
|
||||
NdrMessageType ndr_##LOWERTYPE##_descr(void) \
|
||||
{ \
|
||||
return &ndr_##LOWERTYPE##_descr_s; \
|
||||
}
|
||||
|
||||
SIMPLE_TYPE_IMPL(UINT32, uint32)
|
||||
SIMPLE_TYPE_IMPL(UINT16, uint16)
|
||||
SIMPLE_TYPE_IMPL(UINT64, uint64)
|
||||
|
||||
#define ARRAY_OF_TYPE_IMPL(TYPE, UPPERTYPE) \
|
||||
BOOL ndr_read_##TYPE##Array(NdrContext* context, wStream* s, const void* hints, void* v) \
|
||||
{ \
|
||||
WINPR_ASSERT(context); \
|
||||
WINPR_ASSERT(s); \
|
||||
WINPR_ASSERT(hints); \
|
||||
return ndr_read_uconformant_array(context, s, (NdrArrayHints*)hints, ndr_##TYPE##_descr(), \
|
||||
(void*)v); \
|
||||
} \
|
||||
\
|
||||
BOOL ndr_write_##TYPE##Array(NdrContext* context, wStream* s, const void* hints, \
|
||||
const void* v) \
|
||||
{ \
|
||||
WINPR_ASSERT(context); \
|
||||
WINPR_ASSERT(s); \
|
||||
WINPR_ASSERT(hints); \
|
||||
NdrArrayHints* ahints = (NdrArrayHints*)hints; \
|
||||
return ndr_write_uconformant_array(context, s, ahints->count, ndr_##TYPE##_descr(), \
|
||||
(const void*)v); \
|
||||
} \
|
||||
void ndr_destroy_##TYPE##Array(NdrContext* context, const void* hints, void* obj) \
|
||||
{ \
|
||||
WINPR_ASSERT(context); \
|
||||
WINPR_ASSERT(obj); \
|
||||
WINPR_ASSERT(hints); \
|
||||
NdrArrayHints* ahints = (NdrArrayHints*)hints; \
|
||||
NdrMessageType descr = ndr_##TYPE##_descr(); \
|
||||
if (descr->destroyFn) \
|
||||
{ \
|
||||
UPPERTYPE* ptr = (UPPERTYPE*)obj; \
|
||||
for (UINT32 i = 0; i < ahints->count; i++, ptr++) \
|
||||
descr->destroyFn(context, NULL, ptr); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
const NdrMessageDescr ndr_##TYPE##Array_descr_s = { \
|
||||
NDR_ARITY_ARRAYOF, sizeof(UPPERTYPE), ndr_read_##TYPE##Array, \
|
||||
ndr_write_##TYPE##Array, ndr_destroy_##TYPE##Array, (NDR_DUMP_FN)NULL \
|
||||
}; \
|
||||
\
|
||||
NdrMessageType ndr_##TYPE##Array_descr(void) \
|
||||
{ \
|
||||
return &ndr_##TYPE##Array_descr_s; \
|
||||
} \
|
||||
\
|
||||
BOOL ndr_read_##TYPE##VaryingArray(NdrContext* context, wStream* s, const void* hints, \
|
||||
void* v) \
|
||||
{ \
|
||||
WINPR_ASSERT(context); \
|
||||
WINPR_ASSERT(s); \
|
||||
WINPR_ASSERT(hints); \
|
||||
return ndr_read_uconformant_varying_array(context, s, (NdrVaryingArrayHints*)hints, \
|
||||
ndr_##TYPE##_descr(), v); \
|
||||
} \
|
||||
BOOL ndr_write_##TYPE##VaryingArray(NdrContext* context, wStream* s, const void* hints, \
|
||||
const void* v) \
|
||||
{ \
|
||||
WINPR_ASSERT(context); \
|
||||
WINPR_ASSERT(s); \
|
||||
WINPR_ASSERT(hints); \
|
||||
return ndr_write_uconformant_varying_array(context, s, (NdrVaryingArrayHints*)hints, \
|
||||
ndr_##TYPE##_descr(), v); \
|
||||
} \
|
||||
\
|
||||
const NdrMessageDescr ndr_##TYPE##VaryingArray_descr_s = { \
|
||||
NDR_ARITY_VARYING_ARRAYOF, sizeof(UPPERTYPE), ndr_read_##TYPE##VaryingArray, \
|
||||
ndr_write_##TYPE##VaryingArray, (NDR_DESTROY_FN)NULL, (NDR_DUMP_FN)NULL \
|
||||
}; \
|
||||
\
|
||||
NdrMessageType ndr_##TYPE##VaryingArray_descr(void) \
|
||||
{ \
|
||||
return &ndr_##TYPE##VaryingArray_descr_s; \
|
||||
}
|
||||
|
||||
ARRAY_OF_TYPE_IMPL(uint8, BYTE)
|
||||
ARRAY_OF_TYPE_IMPL(uint16, UINT16)
|
||||
|
||||
BOOL ndr_read_wchar(NdrContext* context, wStream* s, WCHAR* ptr)
|
||||
{
|
||||
return ndr_read_uint16(context, s, (UINT16*)ptr);
|
||||
}
|
||||
|
||||
BOOL ndr_read_uconformant_varying_array(NdrContext* context, wStream* s,
|
||||
const NdrVaryingArrayHints* hints, NdrMessageType itemType,
|
||||
void* ptarget)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(hints);
|
||||
WINPR_ASSERT(itemType);
|
||||
WINPR_ASSERT(ptarget);
|
||||
|
||||
UINT32 maxCount, offset, length;
|
||||
|
||||
if (!ndr_read_uint32(context, s, &maxCount) || !ndr_read_uint32(context, s, &offset) ||
|
||||
!ndr_read_uint32(context, s, &length))
|
||||
return FALSE;
|
||||
|
||||
if ((length * itemType->itemSize) < hints->length)
|
||||
return FALSE;
|
||||
|
||||
if ((maxCount * itemType->itemSize) < hints->maxLength)
|
||||
return FALSE;
|
||||
|
||||
BYTE* target = (BYTE*)ptarget;
|
||||
for (UINT32 i = 0; i < length; i++, target += itemType->itemSize)
|
||||
{
|
||||
if (!itemType->readFn(context, s, NULL, target))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return ndr_read_align(context, s, 4);
|
||||
}
|
||||
|
||||
BOOL ndr_write_uconformant_varying_array(NdrContext* context, wStream* s,
|
||||
const NdrVaryingArrayHints* hints, NdrMessageType itemType,
|
||||
const void* psrc)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(hints);
|
||||
WINPR_ASSERT(itemType);
|
||||
WINPR_ASSERT(psrc);
|
||||
|
||||
if (!ndr_write_uint32(context, s, hints->maxLength) || !ndr_write_uint32(context, s, 0) ||
|
||||
!ndr_write_uint32(context, s, hints->length))
|
||||
return FALSE;
|
||||
|
||||
const BYTE* src = (const BYTE*)psrc;
|
||||
for (UINT32 i = 0; i < hints->length; i++, src += itemType->itemSize)
|
||||
{
|
||||
if (!itemType->writeFn(context, s, NULL, src))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_read_uconformant_array(NdrContext* context, wStream* s, const NdrArrayHints* hints,
|
||||
NdrMessageType itemType, void* vtarget)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(itemType);
|
||||
WINPR_ASSERT(vtarget);
|
||||
|
||||
UINT32 count;
|
||||
|
||||
if (!ndr_read_uint32(context, s, &count))
|
||||
return FALSE;
|
||||
|
||||
if ((count * itemType->itemSize < hints->count))
|
||||
return FALSE;
|
||||
|
||||
BYTE* target = (BYTE*)vtarget;
|
||||
for (UINT32 i = 0; i < count; i++, target += itemType->itemSize)
|
||||
{
|
||||
if (!itemType->readFn(context, s, NULL, target))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return ndr_read_align(context, s, /*context->alignBytes*/ 4);
|
||||
}
|
||||
|
||||
BOOL ndr_write_uconformant_array(NdrContext* context, wStream* s, UINT32 len,
|
||||
NdrMessageType itemType, const BYTE* ptr)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(itemType);
|
||||
WINPR_ASSERT(ptr);
|
||||
|
||||
size_t toWrite = len * itemType->itemSize;
|
||||
size_t padding = (4 - (toWrite % 4)) % 4;
|
||||
if (!ndr_write_uint32(context, s, len) || !Stream_EnsureRemainingCapacity(s, toWrite + padding))
|
||||
return FALSE;
|
||||
|
||||
for (UINT32 i = 0; i < len; i++, ptr += itemType->itemSize)
|
||||
{
|
||||
if (!itemType->writeFn(context, s, NULL, ptr))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (padding)
|
||||
{
|
||||
Stream_Zero(s, padding);
|
||||
ndr_context_bytes_written(context, padding);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_struct_read_fromDescr(NdrContext* context, wStream* s, const NdrStructDescr* descr,
|
||||
void* target)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(descr);
|
||||
WINPR_ASSERT(target);
|
||||
|
||||
#define NDR_MAX_STRUCT_DEFERRED 16
|
||||
NdrDeferredEntry deferreds[NDR_MAX_STRUCT_DEFERRED] = { 0 };
|
||||
size_t ndeferred = 0;
|
||||
|
||||
for (size_t i = 0; i < descr->nfields; i++)
|
||||
{
|
||||
const NdrFieldStruct* field = &descr->fields[i];
|
||||
BYTE* ptr = target;
|
||||
ptr += field->structOffset;
|
||||
void* hints = NULL;
|
||||
|
||||
if (field->hintsField >= 0)
|
||||
{
|
||||
/* computes the address of the hints field if any */
|
||||
WINPR_ASSERT(field->hintsField < descr->nfields);
|
||||
const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
|
||||
|
||||
hints = (BYTE*)target + hintsField->structOffset;
|
||||
}
|
||||
|
||||
switch (field->pointerType)
|
||||
{
|
||||
case NDR_NOT_POINTER:
|
||||
if (!field->typeDescr->readFn(context, s, hints, ptr))
|
||||
{
|
||||
WLog_ERR(TAG, "error when reading %s.%s", descr->name, field->name);
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
case NDR_POINTER:
|
||||
case NDR_POINTER_NON_NULL:
|
||||
{
|
||||
NdrDeferredEntry* deferred = &deferreds[ndeferred];
|
||||
if (ndeferred >= NDR_MAX_STRUCT_DEFERRED)
|
||||
{
|
||||
WLog_ERR(TAG, "too many deferred when calling ndr_read_struct_fromDescr for %s",
|
||||
descr->name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
deferred->name = field->name;
|
||||
deferred->hints = hints;
|
||||
deferred->target = ptr;
|
||||
deferred->msg = field->typeDescr;
|
||||
if (!ndr_read_refpointer(context, s, &deferred->ptrId))
|
||||
{
|
||||
WLog_ERR(TAG, "error when reading %s.%s", descr->name, field->name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!deferred->ptrId && field->pointerType == NDR_POINTER_NON_NULL)
|
||||
{
|
||||
WLog_ERR(TAG, "%s.%s can't be null", descr->name, field->name);
|
||||
return FALSE;
|
||||
}
|
||||
ndeferred++;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
WLog_ERR(TAG, "%s.%s unknown pointer type 0x%x", descr->name, field->name,
|
||||
field->pointerType);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return ndr_push_deferreds(context, deferreds, ndeferred);
|
||||
}
|
||||
|
||||
BOOL ndr_struct_write_fromDescr(NdrContext* context, wStream* s, const NdrStructDescr* descr,
|
||||
const void* src)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(descr);
|
||||
WINPR_ASSERT(src);
|
||||
|
||||
NdrDeferredEntry deferreds[NDR_MAX_STRUCT_DEFERRED] = { 0 };
|
||||
size_t ndeferred = 0;
|
||||
|
||||
for (size_t i = 0; i < descr->nfields; i++)
|
||||
{
|
||||
const NdrFieldStruct* field = &descr->fields[i];
|
||||
BYTE* ptr = (BYTE*)src + field->structOffset;
|
||||
|
||||
void* hints = NULL;
|
||||
|
||||
if (field->hintsField >= 0)
|
||||
{
|
||||
/* computes the address of the hints field if any */
|
||||
WINPR_ASSERT(field->hintsField < descr->nfields);
|
||||
const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
|
||||
|
||||
hints = (BYTE*)src + hintsField->structOffset;
|
||||
}
|
||||
|
||||
switch (field->pointerType)
|
||||
{
|
||||
case NDR_POINTER:
|
||||
case NDR_POINTER_NON_NULL:
|
||||
{
|
||||
ndr_refid ptrId = NDR_PTR_NULL;
|
||||
BOOL isNew;
|
||||
ptr = *(void**)ptr;
|
||||
|
||||
if (!ptr && field->pointerType == NDR_POINTER_NON_NULL)
|
||||
{
|
||||
WLog_ERR(TAG, "%s.%s can't be null", descr->name, field->name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!ndr_context_allocatePtr(context, ptr, &ptrId, &isNew))
|
||||
return FALSE;
|
||||
|
||||
if (isNew)
|
||||
{
|
||||
NdrDeferredEntry* deferred = &deferreds[ndeferred];
|
||||
if (ndeferred >= NDR_MAX_STRUCT_DEFERRED)
|
||||
{
|
||||
WLog_ERR(TAG,
|
||||
"too many deferred when calling ndr_read_struct_fromDescr for %s",
|
||||
descr->name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
deferred->name = field->name;
|
||||
deferred->hints = hints;
|
||||
deferred->target = ptr;
|
||||
deferred->msg = field->typeDescr;
|
||||
ndeferred++;
|
||||
}
|
||||
|
||||
if (!ndr_write_uint32(context, s, ptrId))
|
||||
return FALSE;
|
||||
break;
|
||||
}
|
||||
case NDR_NOT_POINTER:
|
||||
if (!field->typeDescr->writeFn(context, s, hints, ptr))
|
||||
{
|
||||
WLog_ERR(TAG, "error when writing %s.%s", descr->name, field->name);
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ndr_push_deferreds(context, deferreds, ndeferred);
|
||||
}
|
||||
|
||||
void ndr_struct_dump_fromDescr(wLog* logger, UINT32 lvl, size_t identLevel,
|
||||
const NdrStructDescr* descr, const void* obj)
|
||||
{
|
||||
char tabArray[30 + 1];
|
||||
size_t ntabs = (identLevel <= 30) ? identLevel : 30;
|
||||
|
||||
memset(tabArray, '\t', ntabs);
|
||||
tabArray[ntabs] = 0;
|
||||
|
||||
WLog_Print(logger, lvl, "%s%s", tabArray, descr->name);
|
||||
for (size_t i = 0; i < descr->nfields; i++)
|
||||
{
|
||||
const NdrFieldStruct* field = &descr->fields[i];
|
||||
BYTE* ptr = (BYTE*)obj + field->structOffset;
|
||||
|
||||
switch (field->pointerType)
|
||||
{
|
||||
case NDR_POINTER:
|
||||
case NDR_POINTER_NON_NULL:
|
||||
ptr = *(void**)ptr;
|
||||
break;
|
||||
case NDR_NOT_POINTER:
|
||||
break;
|
||||
default:
|
||||
WLog_ERR(TAG, "invalid field->pointerType");
|
||||
break;
|
||||
}
|
||||
|
||||
WLog_Print(logger, lvl, "%s*%s:", tabArray, field->name);
|
||||
if (field->typeDescr->dumpFn)
|
||||
field->typeDescr->dumpFn(logger, lvl, identLevel + 1, ptr);
|
||||
else
|
||||
WLog_Print(logger, lvl, "%s\t<no dump function>", tabArray);
|
||||
}
|
||||
}
|
||||
|
||||
void ndr_struct_destroy(NdrContext* context, const NdrStructDescr* descr, void* pptr)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(descr);
|
||||
WINPR_ASSERT(pptr);
|
||||
|
||||
for (size_t i = 0; i < descr->nfields; i++)
|
||||
{
|
||||
const NdrFieldStruct* field = &descr->fields[i];
|
||||
void* ptr = (BYTE*)pptr + field->structOffset;
|
||||
void* hints = NULL;
|
||||
|
||||
if (field->hintsField >= 0)
|
||||
{
|
||||
/* computes the address of the hints field if any */
|
||||
WINPR_ASSERT(field->hintsField < descr->nfields);
|
||||
const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
|
||||
|
||||
hints = (BYTE*)pptr + hintsField->structOffset;
|
||||
}
|
||||
|
||||
if (field->pointerType != NDR_NOT_POINTER)
|
||||
ptr = *(void**)ptr;
|
||||
|
||||
if (ptr && field->typeDescr->destroyFn)
|
||||
field->typeDescr->destroyFn(context, hints, ptr);
|
||||
|
||||
if (field->pointerType != NDR_NOT_POINTER)
|
||||
free(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
ndr_refid ndr_pointer_refid(const void* ptr)
|
||||
{
|
||||
return (ndr_refid)((ULONG_PTR)ptr);
|
||||
}
|
||||
|
||||
BOOL ndr_read_refpointer(NdrContext* context, wStream* s, ndr_refid* refId)
|
||||
{
|
||||
return ndr_read_uint32(context, s, refId);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const void* needle;
|
||||
ndr_refid* presult;
|
||||
} FindValueArgs;
|
||||
|
||||
BOOL findValueRefFn(const void* key, void* value, void* parg)
|
||||
{
|
||||
WINPR_ASSERT(parg);
|
||||
|
||||
FindValueArgs* args = (FindValueArgs*)parg;
|
||||
if (args->needle == value)
|
||||
{
|
||||
*args->presult = (ndr_refid)(UINT_PTR)key;
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_context_allocatePtr(NdrContext* context, const void* ptr, ndr_refid* prefId, BOOL* pnewPtr)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
FindValueArgs findArgs = { ptr, prefId };
|
||||
if (!HashTable_Foreach(context->refPointers, findValueRefFn, &findArgs))
|
||||
{
|
||||
*pnewPtr = FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
*pnewPtr = TRUE;
|
||||
*prefId = context->refIdCounter + 4;
|
||||
if (!HashTable_Insert(context->refPointers, (void*)(UINT_PTR)(*prefId), ptr))
|
||||
return FALSE;
|
||||
|
||||
context->refIdCounter += 4;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_read_pointedMessageEx(NdrContext* context, wStream* s, ndr_refid ptrId,
|
||||
NdrMessageType descr, void* hints, void** target)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(descr);
|
||||
WINPR_ASSERT(target);
|
||||
|
||||
*target = NULL;
|
||||
if (!ptrId)
|
||||
return TRUE;
|
||||
|
||||
void* ret = HashTable_GetItemValue(context->refPointers, (void*)(UINT_PTR)ptrId);
|
||||
if (!ret)
|
||||
{
|
||||
size_t itemCount = ndr_hintsCount(descr, hints);
|
||||
ret = calloc(itemCount, descr->itemSize);
|
||||
if (!ret)
|
||||
return FALSE;
|
||||
|
||||
if (!descr->readFn(context, s, hints, ret) ||
|
||||
!HashTable_Insert(context->refPointers, (void*)(UINT_PTR)ptrId, ret))
|
||||
{
|
||||
if (descr->destroyFn)
|
||||
descr->destroyFn(context, hints, ret);
|
||||
free(ret);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
*target = ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_push_deferreds(NdrContext* context, NdrDeferredEntry* deferreds, size_t ndeferred)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(deferreds);
|
||||
|
||||
if (!ndeferred)
|
||||
return TRUE;
|
||||
|
||||
if (context->ndeferred + ndeferred > NDR_MAX_DEFERRED)
|
||||
{
|
||||
WLog_ERR(TAG, "too many deferred");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (size_t i = ndeferred; i > 0; i--, context->ndeferred++)
|
||||
{
|
||||
context->deferred[context->ndeferred] = deferreds[i - 1];
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_treat_deferred_read(NdrContext* context, wStream* s)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
while (context->ndeferred)
|
||||
{
|
||||
NdrDeferredEntry current = context->deferred[context->ndeferred - 1];
|
||||
context->ndeferred--;
|
||||
|
||||
WLog_VRB(TAG, "treating read deferred 0x%x for %s", current.ptrId, current.name);
|
||||
if (!ndr_read_pointedMessageEx(context, s, current.ptrId, current.msg, current.hints,
|
||||
(void**)current.target))
|
||||
{
|
||||
WLog_ERR(TAG, "error parsing deferred %s", current.name);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_treat_deferred_write(NdrContext* context, wStream* s)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
while (context->ndeferred)
|
||||
{
|
||||
NdrDeferredEntry current = context->deferred[context->ndeferred - 1];
|
||||
context->ndeferred--;
|
||||
|
||||
WLog_VRB(TAG, "treating write deferred for %s", current.name);
|
||||
if (!current.msg->writeFn(context, s, current.hints, current.target))
|
||||
{
|
||||
WLog_ERR(TAG, "error writing deferred %s", current.name);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
192
channels/rdpear/common/rdpear-common/ndr.h
Normal file
192
channels/rdpear/common/rdpear-common/ndr.h
Normal file
@ -0,0 +1,192 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Authentication redirection virtual channel
|
||||
*
|
||||
* Copyright 2024 David Fort <contact@hardening-consulting.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.
|
||||
*/
|
||||
|
||||
#ifndef CHANNELS_RDPEAR_NDR_H_
|
||||
#define CHANNELS_RDPEAR_NDR_H_
|
||||
|
||||
#include <winpr/stream.h>
|
||||
#include <freerdp/api.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
typedef struct NdrContext_s NdrContext;
|
||||
|
||||
typedef UINT32 ndr_refid;
|
||||
#define NDR_PTR_NULL (0UL)
|
||||
|
||||
typedef BOOL (*NDR_READER_FN)(NdrContext* context, wStream* s, const void* hints, void* target);
|
||||
typedef BOOL (*NDR_WRITER_FN)(NdrContext* context, wStream* s, const void* hints,
|
||||
const void* obj);
|
||||
typedef void (*NDR_DESTROY_FN)(NdrContext* context, const void* hints, void* obj);
|
||||
typedef void (*NDR_DUMP_FN)(wLog* logger, UINT32 lvl, size_t indentLevel, const void* obj);
|
||||
|
||||
/** @brief arity of a message */
|
||||
typedef enum
|
||||
{
|
||||
NDR_ARITY_SIMPLE,
|
||||
NDR_ARITY_ARRAYOF,
|
||||
NDR_ARITY_VARYING_ARRAYOF,
|
||||
} NdrTypeArity;
|
||||
|
||||
/** @brief message descriptor */
|
||||
typedef struct
|
||||
{
|
||||
NdrTypeArity arity;
|
||||
size_t itemSize;
|
||||
NDR_READER_FN readFn;
|
||||
NDR_WRITER_FN writeFn;
|
||||
NDR_DESTROY_FN destroyFn;
|
||||
NDR_DUMP_FN dumpFn;
|
||||
} NdrMessageDescr;
|
||||
|
||||
typedef const NdrMessageDescr* NdrMessageType;
|
||||
|
||||
/** @brief pointer or not and if null is accepted */
|
||||
typedef enum
|
||||
{
|
||||
NDR_NOT_POINTER,
|
||||
NDR_POINTER_NON_NULL,
|
||||
NDR_POINTER
|
||||
} NdrPointerType;
|
||||
|
||||
/** @brief descriptor of a field in a structure */
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
size_t structOffset;
|
||||
NdrPointerType pointerType;
|
||||
ssize_t hintsField;
|
||||
NdrMessageType typeDescr;
|
||||
} NdrFieldStruct;
|
||||
|
||||
/** @brief structure descriptor */
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
size_t nfields;
|
||||
const NdrFieldStruct* fields;
|
||||
} NdrStructDescr;
|
||||
|
||||
/** @brief a deferred pointer */
|
||||
typedef struct
|
||||
{
|
||||
ndr_refid ptrId;
|
||||
const char* name;
|
||||
void* hints;
|
||||
void* target;
|
||||
NdrMessageType msg;
|
||||
} NdrDeferredEntry;
|
||||
|
||||
NdrContext* ndr_context_new(BOOL bigEndianDrep, BYTE version);
|
||||
void ndr_context_destroy(NdrContext** pcontext);
|
||||
|
||||
void ndr_context_reset(NdrContext* context);
|
||||
NdrContext* ndr_context_copy(const NdrContext* src);
|
||||
|
||||
NdrContext* ndr_read_header(wStream* s);
|
||||
BOOL ndr_write_header(NdrContext* context, wStream* s);
|
||||
|
||||
BOOL ndr_write_uint8(NdrContext* context, wStream* s, BYTE v);
|
||||
BOOL ndr_write_uint8_(NdrContext* context, wStream* s, const void* hints, const void* v);
|
||||
NdrMessageType ndr_uint8_descr(void);
|
||||
|
||||
#define NDR_SIMPLE_TYPE_DECL(LOWER, UPPER) \
|
||||
BOOL ndr_read_##LOWER(NdrContext* context, wStream* s, UPPER* v); \
|
||||
BOOL ndr_write_##LOWER(NdrContext* context, wStream* s, UPPER v); \
|
||||
BOOL ndr_write_##LOWER##_(NdrContext* context, wStream* s, const void* hints, const void* v); \
|
||||
extern const NdrMessageDescr ndr_##LOWER##_descr_s; \
|
||||
NdrMessageType ndr_##LOWER##_descr(void)
|
||||
|
||||
NDR_SIMPLE_TYPE_DECL(uint16, UINT16);
|
||||
NDR_SIMPLE_TYPE_DECL(uint32, UINT32);
|
||||
NDR_SIMPLE_TYPE_DECL(uint64, UINT64);
|
||||
|
||||
extern const NdrMessageDescr ndr_uint8Array_descr_s;
|
||||
NdrMessageType ndr_uint8Array_descr(void);
|
||||
NdrMessageType ndr_uint16Array_descr(void);
|
||||
NdrMessageType ndr_uint16VaryingArray_descr(void);
|
||||
|
||||
BOOL ndr_skip_bytes(NdrContext* context, wStream* s, size_t nbytes);
|
||||
BOOL ndr_read_align(NdrContext* context, wStream* s, size_t sz);
|
||||
BOOL ndr_write_align(NdrContext* context, wStream* s, size_t sz);
|
||||
|
||||
BOOL ndr_read_pickle(NdrContext* context, wStream* s);
|
||||
BOOL ndr_write_pickle(NdrContext* context, wStream* s);
|
||||
|
||||
BOOL ndr_read_constructed(NdrContext* context, wStream* s, wStream* target);
|
||||
BOOL ndr_write_constructed(NdrContext* context, wStream* s, wStream* payload);
|
||||
|
||||
BOOL ndr_start_constructed(NdrContext* context, wStream* s);
|
||||
BOOL ndr_end_constructed(NdrContext* context, wStream* s);
|
||||
|
||||
BOOL ndr_read_wchar(NdrContext* context, wStream* s, WCHAR* ptr);
|
||||
|
||||
/** @brief hints for a varying conformant array */
|
||||
typedef struct
|
||||
{
|
||||
UINT32 length;
|
||||
UINT32 maxLength;
|
||||
} NdrVaryingArrayHints;
|
||||
|
||||
BOOL ndr_read_uconformant_varying_array(NdrContext* context, wStream* s,
|
||||
const NdrVaryingArrayHints* hints,
|
||||
NdrMessageType itemType, void* ptarget);
|
||||
BOOL ndr_write_uconformant_varying_array(NdrContext* context, wStream* s,
|
||||
const NdrVaryingArrayHints* hints,
|
||||
NdrMessageType itemType, const void* src);
|
||||
|
||||
/** @brief hints for a conformant array */
|
||||
typedef struct
|
||||
{
|
||||
UINT32 count;
|
||||
} NdrArrayHints;
|
||||
|
||||
BOOL ndr_read_uconformant_array(NdrContext* context, wStream* s, const NdrArrayHints* hints,
|
||||
NdrMessageType itemType, void* vtarget);
|
||||
BOOL ndr_write_uconformant_array(NdrContext* context, wStream* s, UINT32 len,
|
||||
NdrMessageType itemType, const BYTE* ptr);
|
||||
|
||||
BOOL ndr_struct_read_fromDescr(NdrContext* context, wStream* s, const NdrStructDescr* descr,
|
||||
void* target);
|
||||
BOOL ndr_struct_write_fromDescr(NdrContext* context, wStream* s, const NdrStructDescr* descr,
|
||||
const void* src);
|
||||
void ndr_struct_dump_fromDescr(wLog* logger, UINT32 lvl, size_t identLevel,
|
||||
const NdrStructDescr* descr, const void* obj);
|
||||
void ndr_struct_destroy(NdrContext* context, const NdrStructDescr* descr, void* pptr);
|
||||
|
||||
ndr_refid ndr_pointer_refid(const void* ptr);
|
||||
BOOL ndr_read_refpointer(NdrContext* context, wStream* s, UINT32* refId);
|
||||
BOOL ndr_context_allocatePtr(NdrContext* context, const void* ptr, ndr_refid* prefId,
|
||||
BOOL* pnewPtr);
|
||||
|
||||
BOOL ndr_read_pointedMessageEx(NdrContext* context, wStream* s, ndr_refid ptrId,
|
||||
NdrMessageType descr, void* hints, void** target);
|
||||
|
||||
BOOL ndr_push_deferreds(NdrContext* context, NdrDeferredEntry* deferreds, size_t ndeferred);
|
||||
BOOL ndr_treat_deferred_read(NdrContext* context, wStream* s);
|
||||
BOOL ndr_treat_deferred_write(NdrContext* context, wStream* s);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CHANNELS_RDPEAR_NDR_H_ */
|
31
channels/rdpear/common/rdpear-common/rdpear_asn1.h
Normal file
31
channels/rdpear/common/rdpear-common/rdpear_asn1.h
Normal file
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* ASN1 routines for RDPEAR
|
||||
*
|
||||
* Copyright 2024 David Fort <contact@hardening-consulting.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.
|
||||
*/
|
||||
|
||||
#ifndef RPDEAR_RDPEAR_ASN1_H__
|
||||
#define RPDEAR_RDPEAR_ASN1_H__
|
||||
|
||||
#include <krb5.h>
|
||||
|
||||
#include <winpr/stream.h>
|
||||
|
||||
wStream* rdpear_enc_Checksum(UINT32 cksumtype, krb5_checksum* csum);
|
||||
|
||||
wStream* rdpear_enc_EncryptedData(UINT32 encType, krb5_data* payload);
|
||||
|
||||
#endif /* RPDEAR_RDPEAR_ASN1_H__ */
|
244
channels/rdpear/common/rdpear-common/rdpear_common.h
Normal file
244
channels/rdpear/common/rdpear-common/rdpear_common.h
Normal file
@ -0,0 +1,244 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
*
|
||||
* Copyright 2023 David Fort <contact@hardening-consulting.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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_CHANNEL_RDPEAR_COMMON_H
|
||||
#define FREERDP_CHANNEL_RDPEAR_COMMON_H
|
||||
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/asn1.h>
|
||||
#include <winpr/wlog.h>
|
||||
#include <winpr/sspi.h>
|
||||
|
||||
#include <freerdp/api.h>
|
||||
|
||||
#include <rdpear-common/ndr.h>
|
||||
|
||||
typedef enum
|
||||
{
|
||||
RDPEAR_PACKAGE_KERBEROS,
|
||||
RDPEAR_PACKAGE_NTLM,
|
||||
RDPEAR_PACKAGE_UNKNOWN
|
||||
} RdpEarPackageType;
|
||||
|
||||
/* RDPEAR 2.2.1.1 */
|
||||
typedef enum
|
||||
{
|
||||
// Start Kerberos remote calls
|
||||
RemoteCallKerbMinimum = 0x100,
|
||||
RemoteCallKerbNegotiateVersion = 0x100,
|
||||
RemoteCallKerbBuildAsReqAuthenticator,
|
||||
RemoteCallKerbVerifyServiceTicket,
|
||||
RemoteCallKerbCreateApReqAuthenticator,
|
||||
RemoteCallKerbDecryptApReply,
|
||||
RemoteCallKerbUnpackKdcReplyBody,
|
||||
RemoteCallKerbComputeTgsChecksum,
|
||||
RemoteCallKerbBuildEncryptedAuthData,
|
||||
RemoteCallKerbPackApReply,
|
||||
RemoteCallKerbHashS4UPreauth,
|
||||
RemoteCallKerbSignS4UPreauthData,
|
||||
RemoteCallKerbVerifyChecksum,
|
||||
RemoteCallKerbReserved1,
|
||||
RemoteCallKerbReserved2,
|
||||
RemoteCallKerbReserved3,
|
||||
RemoteCallKerbReserved4,
|
||||
RemoteCallKerbReserved5,
|
||||
RemoteCallKerbReserved6,
|
||||
RemoteCallKerbReserved7,
|
||||
RemoteCallKerbDecryptPacCredentials,
|
||||
RemoteCallKerbCreateECDHKeyAgreement,
|
||||
RemoteCallKerbCreateDHKeyAgreement,
|
||||
RemoteCallKerbDestroyKeyAgreement,
|
||||
RemoteCallKerbKeyAgreementGenerateNonce,
|
||||
RemoteCallKerbFinalizeKeyAgreement,
|
||||
RemoteCallKerbMaximum = 0x1ff,
|
||||
// End Kerberos remote calls
|
||||
|
||||
// Start NTLM remote calls
|
||||
RemoteCallNtlmMinimum = 0x200,
|
||||
RemoteCallNtlmNegotiateVersion = 0x200,
|
||||
RemoteCallNtlmLm20GetNtlm3ChallengeResponse,
|
||||
RemoteCallNtlmCalculateNtResponse,
|
||||
RemoteCallNtlmCalculateUserSessionKeyNt,
|
||||
RemoteCallNtlmCompareCredentials,
|
||||
RemoteCallNtlmMaximum = 0x2ff,
|
||||
// End NTLM remote calls
|
||||
} RemoteGuardCallId;
|
||||
|
||||
FREERDP_LOCAL RdpEarPackageType rdpear_packageType_from_name(WinPrAsn1_OctetString* package);
|
||||
FREERDP_LOCAL wStream* rdpear_encodePayload(RdpEarPackageType packageType, wStream* payload);
|
||||
|
||||
#define RDPEAR_COMMON_MESSAGE_DECL(V) \
|
||||
FREERDP_LOCAL BOOL ndr_read_##V(NdrContext* context, wStream* s, const void* hints, V* obj); \
|
||||
FREERDP_LOCAL BOOL ndr_write_##V(NdrContext* context, wStream* s, const void* hints, \
|
||||
const V* obj); \
|
||||
FREERDP_LOCAL void ndr_destroy_##V(NdrContext* context, const void* hints, V* obj); \
|
||||
FREERDP_LOCAL void ndr_dump_##V(wLog* logger, UINT32 lvl, size_t indentLevel, const V* obj); \
|
||||
FREERDP_LOCAL NdrMessageType ndr_##V##_descr()
|
||||
|
||||
/** @brief 2.2.1.2.2 KERB_RPC_OCTET_STRING */
|
||||
typedef struct
|
||||
{
|
||||
UINT32 length;
|
||||
BYTE* value;
|
||||
} KERB_RPC_OCTET_STRING;
|
||||
|
||||
RDPEAR_COMMON_MESSAGE_DECL(KERB_RPC_OCTET_STRING);
|
||||
|
||||
/** @brief 2.2.1.2.1 KERB_ASN1_DATA */
|
||||
typedef struct
|
||||
{
|
||||
UINT32 Pdu;
|
||||
NdrArrayHints Asn1BufferHints;
|
||||
BYTE* Asn1Buffer;
|
||||
} KERB_ASN1_DATA;
|
||||
|
||||
RDPEAR_COMMON_MESSAGE_DECL(KERB_ASN1_DATA);
|
||||
|
||||
/** @brief 2.3.10 RPC_UNICODE_STRING (MS-DTYP) */
|
||||
typedef struct
|
||||
{
|
||||
NdrVaryingArrayHints lenHints;
|
||||
UINT32 strLength;
|
||||
WCHAR* Buffer;
|
||||
} RPC_UNICODE_STRING;
|
||||
|
||||
RDPEAR_COMMON_MESSAGE_DECL(RPC_UNICODE_STRING);
|
||||
|
||||
/** @brief 2.2.1.2.3 KERB_RPC_INTERNAL_NAME */
|
||||
typedef struct
|
||||
{
|
||||
UINT16 NameType;
|
||||
NdrArrayHints nameHints;
|
||||
RPC_UNICODE_STRING* Names;
|
||||
} KERB_RPC_INTERNAL_NAME;
|
||||
|
||||
FREERDP_LOCAL BOOL ndr_read_KERB_RPC_INTERNAL_NAME(NdrContext* context, wStream* s,
|
||||
const void* hints, KERB_RPC_INTERNAL_NAME* res);
|
||||
FREERDP_LOCAL BOOL ndr_write_KERB_RPC_INTERNAL_NAME(NdrContext* context, wStream* s,
|
||||
const void* hints,
|
||||
const KERB_RPC_INTERNAL_NAME* res);
|
||||
FREERDP_LOCAL void ndr_dump_KERB_RPC_INTERNAL_NAME(wLog* logger, UINT32 lvl, size_t indentLevel,
|
||||
const KERB_RPC_INTERNAL_NAME* obj);
|
||||
FREERDP_LOCAL void ndr_destroy_KERB_RPC_INTERNAL_NAME(NdrContext* context, const void* hints,
|
||||
KERB_RPC_INTERNAL_NAME* obj);
|
||||
|
||||
/** @brief 2.2.1.2.8 KERB_RPC_ENCRYPTION_KEY */
|
||||
typedef struct
|
||||
{
|
||||
UINT32 reserved1;
|
||||
UINT32 reserved2;
|
||||
KERB_RPC_OCTET_STRING reserved3;
|
||||
} KERB_RPC_ENCRYPTION_KEY;
|
||||
|
||||
RDPEAR_COMMON_MESSAGE_DECL(KERB_RPC_ENCRYPTION_KEY);
|
||||
|
||||
/** @brief 2.2.2.1.8 BuildEncryptedAuthData */
|
||||
typedef struct
|
||||
{
|
||||
UINT32 KeyUsage;
|
||||
KERB_RPC_ENCRYPTION_KEY* Key;
|
||||
KERB_ASN1_DATA* PlainAuthData;
|
||||
} BuildEncryptedAuthDataReq;
|
||||
|
||||
RDPEAR_COMMON_MESSAGE_DECL(BuildEncryptedAuthDataReq);
|
||||
|
||||
/** @brief 2.2.2.1.7 ComputeTgsChecksum */
|
||||
typedef struct
|
||||
{
|
||||
KERB_ASN1_DATA* requestBody;
|
||||
KERB_RPC_ENCRYPTION_KEY* Key;
|
||||
UINT32 ChecksumType;
|
||||
} ComputeTgsChecksumReq;
|
||||
|
||||
RDPEAR_COMMON_MESSAGE_DECL(ComputeTgsChecksumReq);
|
||||
|
||||
/** @brief 2.2.2.1.4 CreateApReqAuthenticator */
|
||||
typedef struct
|
||||
{
|
||||
KERB_RPC_ENCRYPTION_KEY* EncryptionKey;
|
||||
ULONG SequenceNumber;
|
||||
KERB_RPC_INTERNAL_NAME* ClientName;
|
||||
RPC_UNICODE_STRING* ClientRealm;
|
||||
PLARGE_INTEGER SkewTime;
|
||||
KERB_RPC_ENCRYPTION_KEY* SubKey; // optional
|
||||
KERB_ASN1_DATA* AuthData; // optional
|
||||
KERB_ASN1_DATA* GssChecksum; // optional
|
||||
ULONG KeyUsage;
|
||||
} CreateApReqAuthenticatorReq;
|
||||
|
||||
RDPEAR_COMMON_MESSAGE_DECL(CreateApReqAuthenticatorReq);
|
||||
|
||||
/** @brief 2.2.2.1.4 CreateApReqAuthenticator */
|
||||
typedef struct
|
||||
{
|
||||
LARGE_INTEGER AuthenticatorTime;
|
||||
KERB_ASN1_DATA Authenticator;
|
||||
LONG KerbProtocolError;
|
||||
} CreateApReqAuthenticatorResp;
|
||||
|
||||
FREERDP_LOCAL NdrMessageType ndr_CreateApReqAuthenticatorResp_descr();
|
||||
|
||||
/** @brief 2.2.2.1.6 UnpackKdcReplyBody */
|
||||
typedef struct
|
||||
{
|
||||
KERB_ASN1_DATA* EncryptedData;
|
||||
KERB_RPC_ENCRYPTION_KEY* Key;
|
||||
KERB_RPC_ENCRYPTION_KEY* StrengthenKey;
|
||||
ULONG Pdu;
|
||||
ULONG KeyUsage;
|
||||
} UnpackKdcReplyBodyReq;
|
||||
|
||||
RDPEAR_COMMON_MESSAGE_DECL(UnpackKdcReplyBodyReq);
|
||||
|
||||
/** @brief 2.2.2.1.6 UnpackKdcReplyBody */
|
||||
typedef struct
|
||||
{
|
||||
LONG KerbProtocolError;
|
||||
KERB_ASN1_DATA ReplyBody;
|
||||
} UnpackKdcReplyBodyResp;
|
||||
|
||||
FREERDP_LOCAL NdrMessageType ndr_UnpackKdcReplyBodyResp_descr();
|
||||
|
||||
typedef struct
|
||||
{
|
||||
KERB_ASN1_DATA* EncryptedReply;
|
||||
KERB_RPC_ENCRYPTION_KEY* Key;
|
||||
} DecryptApReplyReq;
|
||||
|
||||
RDPEAR_COMMON_MESSAGE_DECL(DecryptApReplyReq);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
KERB_ASN1_DATA* Reply;
|
||||
KERB_ASN1_DATA* ReplyBody;
|
||||
KERB_RPC_ENCRYPTION_KEY* SessionKey;
|
||||
} PackApReplyReq;
|
||||
|
||||
RDPEAR_COMMON_MESSAGE_DECL(PackApReplyReq);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
NdrArrayHints PackedReplyHints;
|
||||
BYTE* PackedReply;
|
||||
} PackApReplyResp;
|
||||
|
||||
FREERDP_LOCAL NdrMessageType ndr_PackApReplyResp_descr();
|
||||
|
||||
#undef RDPEAR_COMMON_MESSAGE_DECL
|
||||
|
||||
#endif /* FREERDP_CHANNEL_RDPEAR_COMMON_H */
|
104
channels/rdpear/common/rdpear_asn1.c
Normal file
104
channels/rdpear/common/rdpear_asn1.c
Normal file
@ -0,0 +1,104 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* ASN1 routines for RDPEAR
|
||||
*
|
||||
* Copyright 2024 David Fort <contact@hardening-consulting.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.
|
||||
*/
|
||||
|
||||
#include <rdpear-common/rdpear_asn1.h>
|
||||
#include <winpr/asn1.h>
|
||||
|
||||
wStream* rdpear_enc_Checksum(UINT32 cksumtype, krb5_checksum* csum)
|
||||
{
|
||||
/**
|
||||
* Checksum ::= SEQUENCE {
|
||||
* cksumtype [0] Int32,
|
||||
* checksum [1] OCTET STRING
|
||||
* }
|
||||
*/
|
||||
wStream* ret = NULL;
|
||||
WinPrAsn1Encoder* enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER);
|
||||
if (!enc)
|
||||
return NULL;
|
||||
|
||||
if (!WinPrAsn1EncSeqContainer(enc))
|
||||
goto out;
|
||||
|
||||
if (!WinPrAsn1EncContextualInteger(enc, 0, (WinPrAsn1_INTEGER)cksumtype))
|
||||
goto out;
|
||||
|
||||
WinPrAsn1_OctetString octets;
|
||||
octets.data = (BYTE*)csum->contents;
|
||||
octets.len = csum->length;
|
||||
if (!WinPrAsn1EncContextualOctetString(enc, 1, &octets) || !WinPrAsn1EncEndContainer(enc))
|
||||
goto out;
|
||||
|
||||
ret = Stream_New(NULL, 1024);
|
||||
if (!ret)
|
||||
goto out;
|
||||
|
||||
if (!WinPrAsn1EncToStream(enc, ret))
|
||||
{
|
||||
Stream_Free(ret, TRUE);
|
||||
ret = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
WinPrAsn1Encoder_Free(&enc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
wStream* rdpear_enc_EncryptedData(UINT32 encType, krb5_data* payload)
|
||||
{
|
||||
/**
|
||||
* EncryptedData ::= SEQUENCE {
|
||||
* etype [0] Int32 -- EncryptionType --,
|
||||
* kvno [1] UInt32 OPTIONAL,
|
||||
* cipher [2] OCTET STRING -- ciphertext
|
||||
* }
|
||||
*/
|
||||
wStream* ret = NULL;
|
||||
WinPrAsn1Encoder* enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER);
|
||||
if (!enc)
|
||||
return NULL;
|
||||
|
||||
if (!WinPrAsn1EncSeqContainer(enc))
|
||||
goto out;
|
||||
|
||||
if (!WinPrAsn1EncContextualInteger(enc, 0, (WinPrAsn1_INTEGER)encType))
|
||||
goto out;
|
||||
|
||||
WinPrAsn1_OctetString octets;
|
||||
octets.data = (BYTE*)payload->data;
|
||||
octets.len = payload->length;
|
||||
if (!WinPrAsn1EncContextualOctetString(enc, 2, &octets) || !WinPrAsn1EncEndContainer(enc))
|
||||
goto out;
|
||||
|
||||
ret = Stream_New(NULL, 1024);
|
||||
if (!ret)
|
||||
goto out;
|
||||
|
||||
if (!WinPrAsn1EncToStream(enc, ret))
|
||||
{
|
||||
Stream_Free(ret, TRUE);
|
||||
ret = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
WinPrAsn1Encoder_Free(&enc);
|
||||
return ret;
|
||||
}
|
488
channels/rdpear/common/rdpear_common.c
Normal file
488
channels/rdpear/common/rdpear_common.c
Normal file
@ -0,0 +1,488 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
*
|
||||
* Copyright 2023 David Fort <contact@hardening-consulting.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.
|
||||
*/
|
||||
|
||||
#include <rdpear-common/rdpear_common.h>
|
||||
#include <rdpear-common/rdpear_asn1.h>
|
||||
#include <rdpear-common/ndr.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <winpr/print.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("rdpear")
|
||||
|
||||
const char kerberosPackageName[] = {
|
||||
'K', 0, 'e', 0, 'r', 0, 'b', 0, 'e', 0, 'r', 0, 'o', 0, 's', 0
|
||||
};
|
||||
const char ntlmPackageName[] = { 'N', 0, 'T', 0, 'L', 0, 'M', 0 };
|
||||
|
||||
RdpEarPackageType rdpear_packageType_from_name(WinPrAsn1_OctetString* package)
|
||||
{
|
||||
if (package->len == sizeof(kerberosPackageName) &&
|
||||
memcmp(package->data, kerberosPackageName, package->len) == 0)
|
||||
return RDPEAR_PACKAGE_KERBEROS;
|
||||
|
||||
if (package->len == sizeof(ntlmPackageName) &&
|
||||
memcmp(package->data, ntlmPackageName, package->len) == 0)
|
||||
return RDPEAR_PACKAGE_NTLM;
|
||||
|
||||
return RDPEAR_PACKAGE_UNKNOWN;
|
||||
}
|
||||
|
||||
wStream* rdpear_encodePayload(RdpEarPackageType packageType, wStream* payload)
|
||||
{
|
||||
wStream* ret = NULL;
|
||||
WinPrAsn1Encoder* enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER);
|
||||
if (!enc)
|
||||
return NULL;
|
||||
|
||||
/* TSRemoteGuardInnerPacket ::= SEQUENCE { */
|
||||
if (!WinPrAsn1EncSeqContainer(enc))
|
||||
goto out;
|
||||
|
||||
/* packageName [1] OCTET STRING */
|
||||
WinPrAsn1_OctetString packageOctetString;
|
||||
switch (packageType)
|
||||
{
|
||||
case RDPEAR_PACKAGE_KERBEROS:
|
||||
packageOctetString.data = (BYTE*)kerberosPackageName;
|
||||
packageOctetString.len = sizeof(kerberosPackageName);
|
||||
break;
|
||||
case RDPEAR_PACKAGE_NTLM:
|
||||
packageOctetString.data = (BYTE*)ntlmPackageName;
|
||||
packageOctetString.len = sizeof(ntlmPackageName);
|
||||
break;
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!WinPrAsn1EncContextualOctetString(enc, 1, &packageOctetString))
|
||||
goto out;
|
||||
|
||||
/* buffer [2] OCTET STRING*/
|
||||
WinPrAsn1_OctetString payloadOctetString = { Stream_GetPosition(payload),
|
||||
Stream_Buffer(payload) };
|
||||
if (!WinPrAsn1EncContextualOctetString(enc, 2, &payloadOctetString))
|
||||
goto out;
|
||||
|
||||
/* } */
|
||||
if (!WinPrAsn1EncEndContainer(enc))
|
||||
goto out;
|
||||
|
||||
ret = Stream_New(NULL, 100);
|
||||
if (!ret)
|
||||
goto out;
|
||||
|
||||
if (!WinPrAsn1EncToStream(enc, ret))
|
||||
{
|
||||
Stream_Free(ret, TRUE);
|
||||
ret = NULL;
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
WinPrAsn1Encoder_Free(&enc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define RDPEAR_SIMPLE_MESSAGE_TYPE(V) \
|
||||
BOOL ndr_read_##V(NdrContext* context, wStream* s, const void* hints, V* obj) \
|
||||
{ \
|
||||
return ndr_struct_read_fromDescr(context, s, &V##_struct, obj); \
|
||||
} \
|
||||
BOOL ndr_write_##V(NdrContext* context, wStream* s, const void* hints, const V* obj) \
|
||||
{ \
|
||||
return ndr_struct_write_fromDescr(context, s, &V##_struct, obj); \
|
||||
} \
|
||||
void ndr_destroy_##V(NdrContext* context, const void* hints, V* obj) \
|
||||
{ \
|
||||
ndr_struct_destroy(context, &V##_struct, obj); \
|
||||
} \
|
||||
void ndr_dump_##V(wLog* logger, UINT32 lvl, size_t indentLevel, const V* obj) \
|
||||
{ \
|
||||
ndr_struct_dump_fromDescr(logger, lvl, indentLevel, &V##_struct, obj); \
|
||||
} \
|
||||
\
|
||||
NdrMessageDescr ndr_##V##_descr_s = { \
|
||||
NDR_ARITY_SIMPLE, \
|
||||
sizeof(V), \
|
||||
(NDR_READER_FN)ndr_read_##V, \
|
||||
(NDR_WRITER_FN)ndr_write_##V, \
|
||||
(NDR_DESTROY_FN)ndr_destroy_##V, \
|
||||
(NDR_DUMP_FN)ndr_dump_##V, \
|
||||
}; \
|
||||
\
|
||||
NdrMessageType ndr_##V##_descr(void) \
|
||||
{ \
|
||||
return &ndr_##V##_descr_s; \
|
||||
}
|
||||
|
||||
static const NdrFieldStruct KERB_RPC_OCTET_STRING_fields[] = {
|
||||
{ "Length", offsetof(KERB_RPC_OCTET_STRING, length), NDR_NOT_POINTER, -1, &ndr_uint32_descr_s },
|
||||
{ "value", offsetof(KERB_RPC_OCTET_STRING, value), NDR_POINTER_NON_NULL, 0,
|
||||
&ndr_uint8Array_descr_s }
|
||||
};
|
||||
static const NdrStructDescr KERB_RPC_OCTET_STRING_struct = { "KERB_RPC_OCTET_STRING", 2,
|
||||
KERB_RPC_OCTET_STRING_fields };
|
||||
|
||||
RDPEAR_SIMPLE_MESSAGE_TYPE(KERB_RPC_OCTET_STRING)
|
||||
|
||||
/* ============================= KERB_ASN1_DATA ============================== */
|
||||
|
||||
static const NdrFieldStruct KERB_ASN1_DATA_fields[] = {
|
||||
{ "Pdu", offsetof(KERB_ASN1_DATA, Pdu), NDR_NOT_POINTER, -1, &ndr_uint32_descr_s },
|
||||
{ "Count", offsetof(KERB_ASN1_DATA, Asn1BufferHints.count), NDR_NOT_POINTER, -1,
|
||||
&ndr_uint32_descr_s },
|
||||
{ "Asn1Buffer", offsetof(KERB_ASN1_DATA, Asn1Buffer), NDR_POINTER_NON_NULL, 1,
|
||||
&ndr_uint8Array_descr_s }
|
||||
};
|
||||
static const NdrStructDescr KERB_ASN1_DATA_struct = { "KERB_ASN1_DATA",
|
||||
ARRAYSIZE(KERB_ASN1_DATA_fields),
|
||||
KERB_ASN1_DATA_fields };
|
||||
|
||||
RDPEAR_SIMPLE_MESSAGE_TYPE(KERB_ASN1_DATA)
|
||||
|
||||
/* ============================ RPC_UNICODE_STRING ========================== */
|
||||
|
||||
BOOL ndr_read_RPC_UNICODE_STRING(NdrContext* context, wStream* s, const void* hints,
|
||||
RPC_UNICODE_STRING* res)
|
||||
{
|
||||
NdrDeferredEntry bufferDesc = { NDR_PTR_NULL, "RPC_UNICODE_STRING.Buffer", &res->lenHints,
|
||||
&res->Buffer, ndr_uint16VaryingArray_descr() };
|
||||
UINT16 Length, MaximumLength;
|
||||
|
||||
if (!ndr_read_uint16(context, s, &Length) || !ndr_read_uint16(context, s, &MaximumLength) ||
|
||||
!ndr_read_refpointer(context, s, &bufferDesc.ptrId) || Length > MaximumLength)
|
||||
return FALSE;
|
||||
|
||||
res->lenHints.length = Length;
|
||||
res->lenHints.maxLength = MaximumLength;
|
||||
res->strLength = Length / 2;
|
||||
|
||||
return ndr_push_deferreds(context, &bufferDesc, 1);
|
||||
}
|
||||
|
||||
#if 0
|
||||
BOOL ndr_write_RPC_UNICODE_STRING(NdrContext* context, wStream* s, const void* hints,
|
||||
const RPC_UNICODE_STRING* res)
|
||||
{
|
||||
return ndr_write_uint32(context, s, res->lenHints.length) &&
|
||||
ndr_write_uint32(context, s, res->lenHints.maxLength) /*&&
|
||||
ndr_write_BYTE_ptr(context, s, (BYTE*)res->Buffer, res->Length)*/
|
||||
;
|
||||
}
|
||||
#endif
|
||||
|
||||
void ndr_dump_RPC_UNICODE_STRING(wLog* logger, UINT32 lvl, size_t indentLevel,
|
||||
const RPC_UNICODE_STRING* obj)
|
||||
{
|
||||
WLog_Print(logger, lvl, "\tLength=%d MaximumLength=%d", obj->lenHints.length,
|
||||
obj->lenHints.maxLength);
|
||||
winpr_HexLogDump(logger, lvl, obj->Buffer, obj->lenHints.length);
|
||||
}
|
||||
|
||||
void ndr_destroy_RPC_UNICODE_STRING(NdrContext* context, const void* hints, RPC_UNICODE_STRING* obj)
|
||||
{
|
||||
if (!obj)
|
||||
return;
|
||||
free(obj->Buffer);
|
||||
obj->Buffer = NULL;
|
||||
}
|
||||
|
||||
static const NdrMessageDescr RPC_UNICODE_STRING_descr_ = {
|
||||
NDR_ARITY_SIMPLE,
|
||||
sizeof(RPC_UNICODE_STRING),
|
||||
(NDR_READER_FN)ndr_read_RPC_UNICODE_STRING,
|
||||
(NDR_WRITER_FN) /*ndr_write_RPC_UNICODE_STRING*/ NULL,
|
||||
(NDR_DESTROY_FN)ndr_destroy_RPC_UNICODE_STRING,
|
||||
(NDR_DUMP_FN)ndr_dump_RPC_UNICODE_STRING
|
||||
};
|
||||
|
||||
NdrMessageType RPC_UNICODE_STRING_descr()
|
||||
{
|
||||
return &RPC_UNICODE_STRING_descr_;
|
||||
}
|
||||
|
||||
/* ========================= RPC_UNICODE_STRING_Array ======================== */
|
||||
|
||||
static BOOL ndr_read_RPC_UNICODE_STRING_Array(NdrContext* context, wStream* s, const void* hints,
|
||||
void* v)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(hints);
|
||||
return ndr_read_uconformant_array(context, s, hints, RPC_UNICODE_STRING_descr(), v);
|
||||
}
|
||||
|
||||
static BOOL ndr_write_RPC_UNICODE_STRING_Array(NdrContext* context, wStream* s, const void* ghints,
|
||||
const void* v)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(ghints);
|
||||
|
||||
const NdrArrayHints* hints = (const NdrArrayHints*)ghints;
|
||||
|
||||
return ndr_write_uconformant_array(context, s, hints->count, RPC_UNICODE_STRING_descr(), v);
|
||||
}
|
||||
|
||||
static const NdrMessageDescr RPC_UNICODE_STRING_Array_descr_ = { NDR_ARITY_ARRAYOF,
|
||||
sizeof(RPC_UNICODE_STRING),
|
||||
ndr_read_RPC_UNICODE_STRING_Array,
|
||||
ndr_write_RPC_UNICODE_STRING_Array,
|
||||
(NDR_DESTROY_FN)NULL,
|
||||
(NDR_DUMP_FN)NULL };
|
||||
|
||||
NdrMessageType RPC_UNICODE_STRING_Array_descr()
|
||||
{
|
||||
return &RPC_UNICODE_STRING_Array_descr_;
|
||||
}
|
||||
|
||||
/* ========================== KERB_RPC_INTERNAL_NAME ======================== */
|
||||
|
||||
BOOL ndr_read_KERB_RPC_INTERNAL_NAME(NdrContext* context, wStream* s, const void* hints,
|
||||
KERB_RPC_INTERNAL_NAME* res)
|
||||
{
|
||||
NdrDeferredEntry names = { NDR_PTR_NULL, "KERB_RPC_INTERNAL_NAME.Names", &res->nameHints,
|
||||
&res->Names, RPC_UNICODE_STRING_Array_descr() };
|
||||
|
||||
UINT16 nameCount;
|
||||
if (!ndr_read_uint16(context, s, &res->NameType) || !ndr_read_uint16(context, s, &nameCount))
|
||||
return FALSE;
|
||||
|
||||
res->nameHints.count = nameCount;
|
||||
|
||||
return ndr_read_refpointer(context, s, &names.ptrId) && ndr_push_deferreds(context, &names, 1);
|
||||
}
|
||||
|
||||
BOOL ndr_write_KERB_RPC_INTERNAL_NAME(NdrContext* context, wStream* s, const void* hints,
|
||||
const KERB_RPC_INTERNAL_NAME* res)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void ndr_dump_KERB_RPC_INTERNAL_NAME(wLog* logger, UINT32 lvl, size_t indentLevel,
|
||||
const KERB_RPC_INTERNAL_NAME* obj)
|
||||
{
|
||||
}
|
||||
|
||||
void ndr_destroy_KERB_RPC_INTERNAL_NAME(NdrContext* context, const void* hints,
|
||||
KERB_RPC_INTERNAL_NAME* obj)
|
||||
{
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < obj->nameHints.count; i++)
|
||||
ndr_destroy_RPC_UNICODE_STRING(context, NULL, &obj->Names[i]);
|
||||
|
||||
free(obj->Names);
|
||||
obj->Names = NULL;
|
||||
}
|
||||
|
||||
NdrMessageDescr KERB_RPC_INTERNAL_NAME_descr_ = {
|
||||
NDR_ARITY_SIMPLE,
|
||||
sizeof(KERB_RPC_INTERNAL_NAME),
|
||||
(NDR_READER_FN)ndr_read_KERB_RPC_INTERNAL_NAME,
|
||||
(NDR_WRITER_FN)NULL,
|
||||
(NDR_DESTROY_FN)ndr_destroy_KERB_RPC_INTERNAL_NAME,
|
||||
(NDR_DUMP_FN)ndr_dump_KERB_RPC_INTERNAL_NAME
|
||||
};
|
||||
|
||||
NdrMessageType KERB_RPC_INTERNAL_NAME_descr()
|
||||
{
|
||||
return &KERB_RPC_INTERNAL_NAME_descr_;
|
||||
}
|
||||
|
||||
/* ========================== KERB_RPC_ENCRYPTION_KEY ======================== */
|
||||
|
||||
static const NdrFieldStruct KERB_RPC_ENCRYPTION_KEY_fields[] = {
|
||||
{ "reserved1", offsetof(KERB_RPC_ENCRYPTION_KEY, reserved1), NDR_NOT_POINTER, -1,
|
||||
&ndr_uint32_descr_s },
|
||||
{ "reserved2", offsetof(KERB_RPC_ENCRYPTION_KEY, reserved2), NDR_NOT_POINTER, -1,
|
||||
&ndr_uint32_descr_s },
|
||||
{ "reserved3", offsetof(KERB_RPC_ENCRYPTION_KEY, reserved3), NDR_NOT_POINTER, -1,
|
||||
&ndr_KERB_RPC_OCTET_STRING_descr_s }
|
||||
};
|
||||
static const NdrStructDescr KERB_RPC_ENCRYPTION_KEY_struct = {
|
||||
"KERB_RPC_ENCRYPTION_KEY", ARRAYSIZE(KERB_RPC_ENCRYPTION_KEY_fields),
|
||||
KERB_RPC_ENCRYPTION_KEY_fields
|
||||
};
|
||||
|
||||
RDPEAR_SIMPLE_MESSAGE_TYPE(KERB_RPC_ENCRYPTION_KEY)
|
||||
|
||||
/* ========================== BuildEncryptedAuthDataReq ======================== */
|
||||
|
||||
static const NdrFieldStruct BuildEncryptedAuthDataReq_fields[] = {
|
||||
{ "KeyUsage", offsetof(BuildEncryptedAuthDataReq, KeyUsage), NDR_NOT_POINTER, -1,
|
||||
&ndr_uint32_descr_s },
|
||||
{ "key", offsetof(BuildEncryptedAuthDataReq, Key), NDR_POINTER_NON_NULL, -1,
|
||||
&ndr_KERB_RPC_ENCRYPTION_KEY_descr_s },
|
||||
{ "plainAuthData", offsetof(BuildEncryptedAuthDataReq, PlainAuthData), NDR_POINTER_NON_NULL, -1,
|
||||
&ndr_KERB_ASN1_DATA_descr_s }
|
||||
};
|
||||
static const NdrStructDescr BuildEncryptedAuthDataReq_struct = {
|
||||
"BuildEncryptedAuthDataReq", ARRAYSIZE(BuildEncryptedAuthDataReq_fields),
|
||||
BuildEncryptedAuthDataReq_fields
|
||||
};
|
||||
|
||||
RDPEAR_SIMPLE_MESSAGE_TYPE(BuildEncryptedAuthDataReq)
|
||||
|
||||
/* ========================== ComputeTgsChecksumReq ======================== */
|
||||
|
||||
static const NdrFieldStruct ComputeTgsChecksumReq_fields[] = {
|
||||
{ "requestBody", offsetof(ComputeTgsChecksumReq, requestBody), NDR_POINTER_NON_NULL, -1,
|
||||
&ndr_KERB_ASN1_DATA_descr_s },
|
||||
{ "key", offsetof(ComputeTgsChecksumReq, Key), NDR_POINTER_NON_NULL, -1,
|
||||
&ndr_KERB_RPC_ENCRYPTION_KEY_descr_s },
|
||||
{ "ChecksumType", offsetof(ComputeTgsChecksumReq, ChecksumType), NDR_NOT_POINTER, -1,
|
||||
&ndr_uint32_descr_s }
|
||||
};
|
||||
static const NdrStructDescr ComputeTgsChecksumReq_struct = {
|
||||
"ComputeTgsChecksumReq", ARRAYSIZE(ComputeTgsChecksumReq_fields), ComputeTgsChecksumReq_fields
|
||||
};
|
||||
|
||||
RDPEAR_SIMPLE_MESSAGE_TYPE(ComputeTgsChecksumReq)
|
||||
|
||||
/* ========================== CreateApReqAuthenticatorReq ======================== */
|
||||
|
||||
static const NdrFieldStruct CreateApReqAuthenticatorReq_fields[] = {
|
||||
{ "EncryptionKey", offsetof(CreateApReqAuthenticatorReq, EncryptionKey), NDR_POINTER_NON_NULL,
|
||||
-1, &ndr_KERB_RPC_ENCRYPTION_KEY_descr_s },
|
||||
{ "SequenceNumber", offsetof(CreateApReqAuthenticatorReq, SequenceNumber), NDR_NOT_POINTER, -1,
|
||||
&ndr_uint32_descr_s },
|
||||
{ "ClientName", offsetof(CreateApReqAuthenticatorReq, ClientName), NDR_POINTER_NON_NULL, -1,
|
||||
&KERB_RPC_INTERNAL_NAME_descr_ },
|
||||
{ "ClientRealm", offsetof(CreateApReqAuthenticatorReq, ClientRealm), NDR_POINTER_NON_NULL, -1,
|
||||
&RPC_UNICODE_STRING_descr_ },
|
||||
{ "SkewTime", offsetof(CreateApReqAuthenticatorReq, SkewTime), NDR_POINTER_NON_NULL, -1,
|
||||
&ndr_uint64_descr_s },
|
||||
{ "SubKey", offsetof(CreateApReqAuthenticatorReq, SubKey), NDR_POINTER, -1,
|
||||
&ndr_KERB_RPC_ENCRYPTION_KEY_descr_s },
|
||||
{ "AuthData", offsetof(CreateApReqAuthenticatorReq, AuthData), NDR_POINTER_NON_NULL, -1,
|
||||
&ndr_KERB_ASN1_DATA_descr_s },
|
||||
{ "GssChecksum", offsetof(CreateApReqAuthenticatorReq, GssChecksum), NDR_POINTER, -1,
|
||||
&ndr_KERB_ASN1_DATA_descr_s },
|
||||
{ "KeyUsage", offsetof(CreateApReqAuthenticatorReq, KeyUsage), NDR_NOT_POINTER, -1,
|
||||
&ndr_uint32_descr_s },
|
||||
};
|
||||
static const NdrStructDescr CreateApReqAuthenticatorReq_struct = {
|
||||
"CreateApReqAuthenticatorReq", ARRAYSIZE(CreateApReqAuthenticatorReq_fields),
|
||||
CreateApReqAuthenticatorReq_fields
|
||||
};
|
||||
|
||||
RDPEAR_SIMPLE_MESSAGE_TYPE(CreateApReqAuthenticatorReq)
|
||||
|
||||
/* ========================== CreateApReqAuthenticatorResp ======================== */
|
||||
|
||||
static const NdrFieldStruct CreateApReqAuthenticatorResp_fields[] = {
|
||||
{ "AuthenticatorTime", offsetof(CreateApReqAuthenticatorResp, AuthenticatorTime),
|
||||
NDR_NOT_POINTER, -1, &ndr_uint64_descr_s },
|
||||
{ "Authenticator", offsetof(CreateApReqAuthenticatorResp, Authenticator), NDR_NOT_POINTER, -1,
|
||||
&ndr_KERB_ASN1_DATA_descr_s },
|
||||
{ "KerbProtocolError", offsetof(CreateApReqAuthenticatorResp, KerbProtocolError),
|
||||
NDR_NOT_POINTER, -1, &ndr_uint32_descr_s },
|
||||
};
|
||||
|
||||
static const NdrStructDescr CreateApReqAuthenticatorResp_struct = {
|
||||
"CreateApReqAuthenticatorResp", ARRAYSIZE(CreateApReqAuthenticatorResp_fields),
|
||||
CreateApReqAuthenticatorResp_fields
|
||||
};
|
||||
|
||||
RDPEAR_SIMPLE_MESSAGE_TYPE(CreateApReqAuthenticatorResp)
|
||||
|
||||
/* ========================== UnpackKdcReplyBodyReq ======================== */
|
||||
|
||||
static const NdrFieldStruct UnpackKdcReplyBodyReq_fields[] = {
|
||||
{ "EncryptedData", offsetof(UnpackKdcReplyBodyReq, EncryptedData), NDR_POINTER_NON_NULL, -1,
|
||||
&ndr_KERB_ASN1_DATA_descr_s },
|
||||
{ "Key", offsetof(UnpackKdcReplyBodyReq, Key), NDR_POINTER_NON_NULL, -1,
|
||||
&ndr_KERB_RPC_ENCRYPTION_KEY_descr_s },
|
||||
{ "StrenghtenKey", offsetof(UnpackKdcReplyBodyReq, StrengthenKey), NDR_POINTER, -1,
|
||||
&ndr_KERB_RPC_ENCRYPTION_KEY_descr_s },
|
||||
{ "Pdu", offsetof(UnpackKdcReplyBodyReq, Pdu), NDR_NOT_POINTER, -1, &ndr_uint32_descr_s },
|
||||
{ "KeyUsage", offsetof(UnpackKdcReplyBodyReq, KeyUsage), NDR_NOT_POINTER, -1,
|
||||
&ndr_uint32_descr_s },
|
||||
};
|
||||
|
||||
static const NdrStructDescr UnpackKdcReplyBodyReq_struct = {
|
||||
"UnpackKdcReplyBodyReq", ARRAYSIZE(UnpackKdcReplyBodyReq_fields), UnpackKdcReplyBodyReq_fields
|
||||
};
|
||||
|
||||
RDPEAR_SIMPLE_MESSAGE_TYPE(UnpackKdcReplyBodyReq)
|
||||
|
||||
/* ========================== UnpackKdcReplyBodyResp ======================== */
|
||||
|
||||
static const NdrFieldStruct UnpackKdcReplyBodyResp_fields[] = {
|
||||
{ "KerbProtocolError", offsetof(UnpackKdcReplyBodyResp, KerbProtocolError), NDR_NOT_POINTER, -1,
|
||||
&ndr_uint32_descr_s },
|
||||
{ "ReplyBody", offsetof(UnpackKdcReplyBodyResp, ReplyBody), NDR_NOT_POINTER, -1,
|
||||
&ndr_KERB_ASN1_DATA_descr_s }
|
||||
};
|
||||
|
||||
static const NdrStructDescr UnpackKdcReplyBodyResp_struct = {
|
||||
"UnpackKdcReplyBodyResp", ARRAYSIZE(UnpackKdcReplyBodyResp_fields),
|
||||
UnpackKdcReplyBodyResp_fields
|
||||
};
|
||||
|
||||
RDPEAR_SIMPLE_MESSAGE_TYPE(UnpackKdcReplyBodyResp)
|
||||
|
||||
/* ========================== DecryptApReplyReq ======================== */
|
||||
|
||||
static const NdrFieldStruct DecryptApReplyReq_fields[] = {
|
||||
{ "EncryptedReply", offsetof(DecryptApReplyReq, EncryptedReply), NDR_POINTER_NON_NULL, -1,
|
||||
&ndr_KERB_ASN1_DATA_descr_s },
|
||||
{ "Key", offsetof(DecryptApReplyReq, Key), NDR_POINTER_NON_NULL, -1,
|
||||
&ndr_KERB_RPC_ENCRYPTION_KEY_descr_s }
|
||||
};
|
||||
|
||||
static const NdrStructDescr DecryptApReplyReq_struct = { "DecryptApReplyReq",
|
||||
ARRAYSIZE(DecryptApReplyReq_fields),
|
||||
DecryptApReplyReq_fields };
|
||||
|
||||
RDPEAR_SIMPLE_MESSAGE_TYPE(DecryptApReplyReq)
|
||||
|
||||
/* ========================== PackApReplyReq ======================== */
|
||||
|
||||
static const NdrFieldStruct PackApReplyReq_fields[] = {
|
||||
{ "Reply", offsetof(PackApReplyReq, Reply), NDR_POINTER_NON_NULL, -1,
|
||||
&ndr_KERB_ASN1_DATA_descr_s },
|
||||
{ "ReplyBody", offsetof(PackApReplyReq, ReplyBody), NDR_POINTER_NON_NULL, -1,
|
||||
&ndr_KERB_ASN1_DATA_descr_s },
|
||||
{ "SessionKey", offsetof(PackApReplyReq, SessionKey), NDR_POINTER_NON_NULL, -1,
|
||||
&ndr_KERB_RPC_ENCRYPTION_KEY_descr_s }
|
||||
};
|
||||
|
||||
static const NdrStructDescr PackApReplyReq_struct = { "PackApReplyReq",
|
||||
ARRAYSIZE(PackApReplyReq_fields),
|
||||
PackApReplyReq_fields };
|
||||
|
||||
RDPEAR_SIMPLE_MESSAGE_TYPE(PackApReplyReq)
|
||||
|
||||
/* ========================== PackApReplyResp ======================== */
|
||||
|
||||
static const NdrFieldStruct PackApReplyResp_fields[] = {
|
||||
{ "PackedReplySize", offsetof(PackApReplyResp, PackedReplyHints), NDR_NOT_POINTER, -1,
|
||||
&ndr_uint32_descr_s },
|
||||
{ "PackedReply", offsetof(PackApReplyResp, PackedReply), NDR_POINTER_NON_NULL, 0,
|
||||
&ndr_uint8Array_descr_s },
|
||||
};
|
||||
|
||||
static const NdrStructDescr PackApReplyResp_struct = { "PackApReplyResp",
|
||||
ARRAYSIZE(PackApReplyResp_fields),
|
||||
PackApReplyResp_fields };
|
||||
|
||||
RDPEAR_SIMPLE_MESSAGE_TYPE(PackApReplyResp)
|
33
channels/rdpear/common/test/CMakeLists.txt
Normal file
33
channels/rdpear/common/test/CMakeLists.txt
Normal file
@ -0,0 +1,33 @@
|
||||
|
||||
set(MODULE_NAME "TestRdpear")
|
||||
set(MODULE_PREFIX "TEST_RDPEAR")
|
||||
|
||||
set(TEST_RDPEAR_DRIVER TestRdpear.c)
|
||||
|
||||
set(TEST_RDPEAR_TESTS
|
||||
TestNdr.c
|
||||
TestNdrEar.c
|
||||
)
|
||||
|
||||
create_test_sourcelist(TEST_RDPEAR_SRCS
|
||||
TestRdpear.c
|
||||
${TEST_RDPEAR_TESTS}
|
||||
)
|
||||
|
||||
add_executable(${MODULE_NAME} ${TEST_RDPEAR_SRCS})
|
||||
|
||||
add_definitions(-DTESTING_OUTPUT_DIRECTORY="${PROJECT_BINARY_DIR}")
|
||||
add_definitions(-DTESTING_SRC_DIRECTORY="${PROJECT_SOURCE_DIR}")
|
||||
|
||||
target_link_libraries(${MODULE_NAME} freerdp winpr freerdp-client)
|
||||
|
||||
|
||||
set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
|
||||
|
||||
foreach(test ${${MODULE_PREFIX}_TESTS})
|
||||
get_filename_component(TestName ${test} NAME_WE)
|
||||
add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
|
||||
endforeach()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "FreeRDP/Rdpear/Test")
|
||||
|
36
channels/rdpear/common/test/TestNdr.c
Normal file
36
channels/rdpear/common/test/TestNdr.c
Normal file
@ -0,0 +1,36 @@
|
||||
#include <rdpear-common/ndr.h>
|
||||
|
||||
int TestNdr(int argc, char* argv[])
|
||||
{
|
||||
int retCode = -2;
|
||||
NdrContext* context = ndr_context_new(FALSE, 1);
|
||||
if (!context)
|
||||
return -1;
|
||||
|
||||
BYTE payload[] = {
|
||||
// == conformant array ==
|
||||
0x02, 0x00, 0x00, 0x00, // (nitems)
|
||||
0x30, 0x00, // content
|
||||
0x00, 0x00 // (padding)
|
||||
};
|
||||
wStream staticS;
|
||||
wStream* s = Stream_StaticInit(&staticS, payload, sizeof(payload));
|
||||
|
||||
BYTE* target = NULL;
|
||||
NdrArrayHints hints = { 2 };
|
||||
NdrDeferredEntry e = { 0x020028, "arrayContent", &hints, &target, ndr_uint8Array_descr() };
|
||||
|
||||
if (!ndr_push_deferreds(context, &e, 1))
|
||||
goto out;
|
||||
|
||||
if (!ndr_treat_deferred_read(context, s))
|
||||
goto out;
|
||||
|
||||
NdrMessageType descr = ndr_uint8Array_descr();
|
||||
descr->destroyFn(context, &hints, target);
|
||||
free(target);
|
||||
retCode = 0;
|
||||
out:
|
||||
ndr_context_destroy(&context);
|
||||
return retCode;
|
||||
}
|
357
channels/rdpear/common/test/TestNdrEar.c
Normal file
357
channels/rdpear/common/test/TestNdrEar.c
Normal file
@ -0,0 +1,357 @@
|
||||
#include <winpr/print.h>
|
||||
|
||||
#include <rdpear-common/ndr.h>
|
||||
#include <rdpear-common/rdpear_common.h>
|
||||
|
||||
BYTE* parseHexBlock(const char* str, size_t* plen)
|
||||
{
|
||||
WINPR_ASSERT(str);
|
||||
WINPR_ASSERT(plen);
|
||||
|
||||
BYTE* ret = malloc(strlen(str) / 2);
|
||||
BYTE* dest = ret;
|
||||
const char* ptr = str;
|
||||
BYTE tmp = 0;
|
||||
size_t nchars = 0;
|
||||
size_t len = 0;
|
||||
|
||||
for (; *ptr; ptr++)
|
||||
{
|
||||
switch (*ptr)
|
||||
{
|
||||
case ' ':
|
||||
case '\n':
|
||||
case '\t':
|
||||
if (nchars)
|
||||
{
|
||||
WLog_ERR("", "error parsing hex block, unpaired char");
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
tmp = tmp * 16 + (*ptr - '0');
|
||||
nchars++;
|
||||
if (nchars == 2)
|
||||
{
|
||||
*dest = tmp;
|
||||
dest++;
|
||||
len++;
|
||||
tmp = 0;
|
||||
nchars = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
case 'b':
|
||||
case 'c':
|
||||
case 'd':
|
||||
case 'e':
|
||||
case 'f':
|
||||
tmp = tmp * 16 + (10 + *ptr - 'a');
|
||||
nchars++;
|
||||
if (nchars == 2)
|
||||
{
|
||||
*dest = tmp;
|
||||
dest++;
|
||||
len++;
|
||||
tmp = 0;
|
||||
nchars = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'A':
|
||||
case 'B':
|
||||
case 'C':
|
||||
case 'D':
|
||||
case 'E':
|
||||
case 'F':
|
||||
tmp = tmp * 16 + (10 + *ptr - 'A');
|
||||
nchars++;
|
||||
if (nchars == 2)
|
||||
{
|
||||
*dest = tmp;
|
||||
dest++;
|
||||
len++;
|
||||
tmp = 0;
|
||||
nchars = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
WLog_ERR("", "invalid char in hex block");
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
*plen = len;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int TestNdrEarWrite(int argc, char* argv[])
|
||||
{
|
||||
wStream* s = NULL;
|
||||
|
||||
BYTE buffer[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
|
||||
KERB_ASN1_DATA asn1;
|
||||
asn1.Pdu = 7;
|
||||
asn1.Asn1BufferHints.count = 16;
|
||||
asn1.Asn1Buffer = buffer;
|
||||
|
||||
s = Stream_New(NULL, 100);
|
||||
if (!s)
|
||||
return -1;
|
||||
|
||||
NdrContext* context = ndr_context_new(FALSE, 1);
|
||||
if (!context)
|
||||
return -1;
|
||||
|
||||
if (!ndr_write_KERB_ASN1_DATA(context, s, NULL, &asn1))
|
||||
return -2;
|
||||
if (!ndr_treat_deferred_write(context, s))
|
||||
return -3;
|
||||
|
||||
// winpr_HexDump("", WLOG_DEBUG, Stream_Buffer(s), Stream_GetPosition(s));
|
||||
ndr_context_destroy(&context);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TestNdrEarRead(int argc, char* argv[])
|
||||
{
|
||||
int retCode = -2;
|
||||
|
||||
/* ====================================================================== */
|
||||
NdrContext* context = ndr_context_new(FALSE, 1);
|
||||
if (!context)
|
||||
return -1;
|
||||
|
||||
wStream staticS;
|
||||
wStream* s;
|
||||
|
||||
#if 0
|
||||
BYTE payload[] = {
|
||||
0x00, 0x00, 0x00, 0x00, // (PduType)
|
||||
0x02, 0x00, 0x00, 0x00, // (Length)
|
||||
0x28, 0x00, 0x02, 0x00, // (Asn1Buffer)
|
||||
|
||||
// == conformant array ==
|
||||
0x02, 0x00, 0x00, 0x00, // (nitems)
|
||||
0x30, 0x00, // content
|
||||
0x00, 0x00 // (padding)
|
||||
};
|
||||
s = Stream_StaticInit(&staticS, payload, sizeof(payload));
|
||||
|
||||
KERB_ASN1_DATA asn1 = { 0 };
|
||||
if (!ndr_read_KERB_ASN1_DATA(context, s, NULL, &asn1) || !ndr_treat_deferred_read(context, s) ||
|
||||
asn1.Asn1BufferHints.count != 2 || *asn1.Asn1Buffer != 0x30)
|
||||
goto out;
|
||||
KERB_ASN1_DATA_destroy(context, &asn1);
|
||||
ndr_context_reset(context);
|
||||
|
||||
/* ====================================================================== */
|
||||
BYTE payload2[] = {
|
||||
// ------------ a RPC_UNICODE_STRING: Administrateur -------------------------
|
||||
0x1c, 0x00, // (Length)
|
||||
0x1e, 0x00, // (MaximumLength)
|
||||
0x1c, 0x00, 0x02, 0x00, // (Buffer ptr)
|
||||
// == conformant array ==
|
||||
0x0f, 0x00, 0x00, 0x00, // (maximum count)
|
||||
0x00, 0x00, 0x00, 0x00, // (offset)
|
||||
0x0e, 0x00, 0x00, 0x00, // (length)
|
||||
|
||||
0x48, 0x00, 0x41, 0x00, 0x52, 0x00, 0x44, 0x00, 0x45, 0x00, 0x4e, 0x00, 0x49, 0x00, 0x4e,
|
||||
0x00, 0x47, 0x00, 0x33, 0x00, 0x2e, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x00, 0x00,
|
||||
|
||||
0x00, 0x00
|
||||
};
|
||||
retCode = -3;
|
||||
|
||||
s = Stream_StaticInit(&staticS, payload2, sizeof(payload2));
|
||||
RPC_UNICODE_STRING unicode = { 0 };
|
||||
if (!ndr_read_RPC_UNICODE_STRING(context, s, NULL, &unicode) || !ndr_treat_deferred_read(context, s))
|
||||
goto out;
|
||||
RPC_UNICODE_STRING_destroy(context, &unicode);
|
||||
ndr_context_reset(context);
|
||||
|
||||
/* ====================================================================== */
|
||||
BYTE payload3[] = {
|
||||
// ------------ an KERB_RPC_INTERNAL_NAME: HARDENING3.COM -------------------------
|
||||
0x01, 0x00, // (NameType)
|
||||
0x01, 0x00, // (NameCount)
|
||||
0x10, 0x00, 0x02, 0x00, // (Names)
|
||||
// == conformant array ==
|
||||
0x01, 0x00, 0x00, 0x00, // (nitems)
|
||||
|
||||
// = RPC_UNICODE_STRING =
|
||||
0x1c, 0x00, // (Length)
|
||||
0x1e, 0x00, // (MaximumLength)
|
||||
0x14, 0x00, 0x02, 0x00, /// (Buffer ptr)
|
||||
// == Uni-dimensional Conformant-varying Array ==
|
||||
0x0f, 0x00, 0x00, 0x00, // (maximum count)
|
||||
0x00, 0x00, 0x00, 0x00, // (offset)
|
||||
0x0e, 0x00, 0x00, 0x00, // (length)
|
||||
0x41, 0x00, 0x64, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x69, 0x00, 0x73, 0x00, 0x74,
|
||||
0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x75, 0x00, 0x72, 0x00, 0x00, 0x00,
|
||||
|
||||
0x00, 0x00
|
||||
};
|
||||
KERB_RPC_INTERNAL_NAME intName = { 0 };
|
||||
retCode = -4;
|
||||
s = Stream_StaticInit(&staticS, payload3, sizeof(payload3));
|
||||
if (!ndr_read_KERB_RPC_INTERNAL_NAME(context, s, NULL, &intName) || !ndr_treat_deferred_read(context, s))
|
||||
goto out;
|
||||
KERB_RPC_INTERNAL_NAME_destroy(context, &intName);
|
||||
ndr_context_reset(context);
|
||||
#endif
|
||||
|
||||
/* ====================================================================== */
|
||||
#if 0
|
||||
BYTE payload4[] = {
|
||||
0x03, 0x01, 0x03, 0x01, // unionId / unionId
|
||||
0x04, 0x00, 0x02, 0x00, // (EncryptionKey ptr)
|
||||
0xf8, 0xca, 0x95, 0x11, // (SequenceNumber)
|
||||
0x0c, 0x00, 0x02, 0x00, // (ClientName ptr)
|
||||
0x18, 0x00, 0x02, 0x00, // (ClientRealm ptr)
|
||||
0x20, 0x00, 0x02, 0x00, // (SkewTime ptr)
|
||||
0x00, 0x00, 0x00, 0x00, // (SubKey ptr)
|
||||
0x24, 0x00, 0x02, 0x00, // (AuthData ptr)
|
||||
0x2c, 0x00, 0x02, 0x00, // (GssChecksum ptr)
|
||||
0x07, 0x00, 0x00, 0x00, // (KeyUsage)
|
||||
|
||||
// === EncryptionKey ===
|
||||
0x40, 0xe9, 0x12, 0xdf, // reserved1
|
||||
0x12, 0x00, 0x00, 0x00, // reserved2
|
||||
// KERB_RPC_OCTET_STRING
|
||||
0x4c, 0x00, 0x00, 0x00, // (length)
|
||||
0x08, 0x00, 0x02, 0x00, // (value ptr)
|
||||
// == conformant array ==
|
||||
0x4c, 0x00, 0x00, 0x00, // (length)
|
||||
0xc4, 0x41, 0xee, 0x34,
|
||||
0x82, 0x2b, 0x29, 0x61, 0xe2, 0x96, 0xb5, 0x75, 0x61, 0x2d, 0xbf, 0x86, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x08, 0x60, 0x2e,
|
||||
0x30, 0x3e, 0xfe, 0x56, 0x11, 0xf0, 0x31, 0xf2, 0xd6, 0x2e, 0x3d, 0x33, 0xfe, 0xce, 0x56, 0x12,
|
||||
0xbf, 0xb2, 0xe5, 0x86, 0x29, 0x8d, 0x29, 0x74, 0x1f, 0x8a, 0xf9, 0xb9, 0x8c, 0xd4, 0x86, 0x3a,
|
||||
0x21, 0x92, 0xb2, 0x07, 0x95, 0x4b, 0xea, 0xee,
|
||||
|
||||
//=== ClientName - KERB_RPC_INTERNAL_NAME ===
|
||||
0x01, 0x00, // (NameType)
|
||||
0x01, 0x00, // (NameCount)
|
||||
0x10, 0x00, 0x02, 0x00, // (Names)
|
||||
|
||||
0x01, 0x00, 0x00, 0x00, // (nitems)
|
||||
|
||||
// = RPC_UNICODE_STRING =
|
||||
0x1c, 0x00, // (Length)
|
||||
0x1e, 0x00, // (MaximumLength)
|
||||
0x14, 0x00, 0x02, 0x00, //(Buffer ptr)
|
||||
// == Uni-dimensional Conformant-varying Array ==
|
||||
0x0f, 0x00, 0x00, 0x00, // (maximum count)
|
||||
0x00, 0x00, 0x00, 0x00, // (offset)
|
||||
0x0e, 0x00, 0x00, 0x00, // (length)
|
||||
0x41, 0x00, 0x64, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x69, 0x00, 0x73, 0x00, 0x74, 0x00,
|
||||
0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x75, 0x00, 0x72, 0x00,
|
||||
|
||||
// === ClientRealm - RPC_UNICODE_STRING ===
|
||||
0x1c, 0x00, // (Length)
|
||||
0x1e, 0x00, // (MaximumLength)
|
||||
0x1c, 0x00, 0x02, 0x00, // (Buffer ptr)
|
||||
// == Uni-dimensional conformant varying array ==
|
||||
0x0f, 0x00, 0x00, 0x00, // (maximum count)
|
||||
0x00, 0x00, 0x00, 0x00, // (offset)
|
||||
0x0e, 0x00, 0x00, 0x00, // (length)
|
||||
0x48, 0x00, 0x41, 0x00, 0x52, 0x00, 0x44, 0x00, 0x45, 0x00, 0x4e, 0x00, 0x49, 0x00, 0x4e, 0x00,
|
||||
0x47, 0x00, 0x33, 0x00, 0x2e, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x4d, 0x00,
|
||||
|
||||
0x00, 0x00, 0x00, 0x00, // padding
|
||||
|
||||
// == SkewTime ==
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// === AuthData - KERB_ASN1_DATA ==
|
||||
0x00, 0x00, 0x00, 0x00, // (PduType)
|
||||
0x02, 0x00, 0x00, 0x00, // (Length)
|
||||
0x28, 0x00, 0x02, 0x00, // (Asn1Buffer)
|
||||
// == conformant array ==
|
||||
0x02, 0x00, 0x00, 0x00, // (nitems)
|
||||
0x30, 0x00,
|
||||
0x00, 0x00, // (padding)
|
||||
|
||||
// === GssChecksum - KERB_ASN1_DATA ===
|
||||
0x08, 0x00, 0x00, 0x00, // (PduType)
|
||||
0x1b, 0x00, 0x00, 0x00, // (Length)
|
||||
0x30, 0x00, 0x02, 0x00, // (Asn1Buffer)
|
||||
// == conformant array ==
|
||||
0x1b, 0x00, 0x00, 0x00, // (length)
|
||||
0x30, 0x19,
|
||||
0xa0, 0x03,
|
||||
0x02, 0x01, 0x07,
|
||||
0xa1, 0x12,
|
||||
0x04, 0x10, 0xb9, 0x4f, 0xcd, 0xae, 0xd9, 0xa8, 0xff, 0x49, 0x69, 0x5a, 0xd1,
|
||||
0x1d, 0x38, 0x49, 0xb6, 0x92, 0x00
|
||||
};
|
||||
size_t sizeofPayload4 = sizeof(payload4);
|
||||
#endif
|
||||
#if 1
|
||||
size_t sizeofPayload4;
|
||||
BYTE* payload4 = parseHexBlock("03 01 03 01 \
|
||||
04 00 02 00 38 9e ef 6b 0c 00 02 00 18 00 02 00 \
|
||||
20 00 02 00 00 00 00 00 24 00 02 00 2c 00 02 00 \
|
||||
07 00 00 00 13 8a a5 a8 12 00 00 00 20 00 00 00 \
|
||||
08 00 02 00 20 00 00 00 c9 03 42 a8 17 8f d9 c4 \
|
||||
9b d2 c4 6e 73 64 98 7b 90 f5 9a 28 77 8e ca de \
|
||||
29 2e a3 8d 8a 56 36 d5 01 00 01 00 10 00 02 00 \
|
||||
01 00 00 00 1c 00 1e 00 14 00 02 00 0f 00 00 00 \
|
||||
00 00 00 00 0e 00 00 00 41 00 64 00 6d 00 69 00 \
|
||||
6e 00 69 00 73 00 74 00 72 00 61 00 74 00 65 00 \
|
||||
75 00 72 00 1c 00 1e 00 1c 00 02 00 0f 00 00 00 \
|
||||
00 00 00 00 0e 00 00 00 48 00 41 00 52 00 44 00 \
|
||||
45 00 4e 00 49 00 4e 00 47 00 33 00 2e 00 43 00 \
|
||||
4f 00 4d 00 00 00 00 00 00 00 00 00 00 00 00 00 \
|
||||
02 00 00 00 28 00 02 00 02 00 00 00 30 00 00 00 \
|
||||
08 00 00 00 1b 00 00 00 30 00 02 00 1b 00 00 00 \
|
||||
30 19 a0 03 02 01 07 a1 12 04 10 e4 aa ff 2b 93 \
|
||||
97 4c f2 5c 0b 49 85 72 92 94 54 00",
|
||||
&sizeofPayload4);
|
||||
|
||||
if (!payload4)
|
||||
goto out;
|
||||
#endif
|
||||
|
||||
CreateApReqAuthenticatorReq createApReqAuthenticatorReq = { 0 };
|
||||
s = Stream_StaticInit(&staticS, payload4, sizeofPayload4);
|
||||
if (!ndr_skip_bytes(context, s, 4) || /* skip union id */
|
||||
!ndr_read_CreateApReqAuthenticatorReq(context, s, NULL, &createApReqAuthenticatorReq) ||
|
||||
!ndr_treat_deferred_read(context, s) || createApReqAuthenticatorReq.KeyUsage != 7 ||
|
||||
!createApReqAuthenticatorReq.EncryptionKey || createApReqAuthenticatorReq.SubKey ||
|
||||
!createApReqAuthenticatorReq.ClientName ||
|
||||
createApReqAuthenticatorReq.ClientName->nameHints.count != 1 ||
|
||||
!createApReqAuthenticatorReq.ClientRealm || !createApReqAuthenticatorReq.AuthData ||
|
||||
createApReqAuthenticatorReq.AuthData->Asn1BufferHints.count != 2 ||
|
||||
!createApReqAuthenticatorReq.SkewTime ||
|
||||
createApReqAuthenticatorReq.SkewTime->QuadPart != 0)
|
||||
goto out;
|
||||
ndr_destroy_CreateApReqAuthenticatorReq(context, NULL, &createApReqAuthenticatorReq);
|
||||
ndr_context_reset(context);
|
||||
|
||||
/* ============ successful end of test =============== */
|
||||
retCode = 0;
|
||||
out:
|
||||
free(payload4);
|
||||
ndr_context_destroy(&context);
|
||||
return retCode;
|
||||
}
|
||||
|
||||
int TestNdrEar(int argc, char* argv[])
|
||||
{
|
||||
return TestNdrEarRead(argc, argv);
|
||||
}
|
@ -40,6 +40,7 @@
|
||||
#include <freerdp/channels/drdynvc.h>
|
||||
#include <freerdp/channels/cliprdr.h>
|
||||
#include <freerdp/channels/encomsp.h>
|
||||
#include <freerdp/channels/rdpear.h>
|
||||
#include <freerdp/channels/rdp2tcp.h>
|
||||
#include <freerdp/channels/remdesk.h>
|
||||
#include <freerdp/channels/rdpsnd.h>
|
||||
@ -4403,6 +4404,13 @@ static int freerdp_client_settings_parse_command_line_arguments_int(
|
||||
if (!freerdp_settings_set_bool(settings, FreeRDP_RestrictedAdminModeRequired, enable))
|
||||
return fail_at(arg, COMMAND_LINE_ERROR);
|
||||
}
|
||||
CommandLineSwitchCase(arg, "remoteGuard")
|
||||
{
|
||||
if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteCredentialGuard, TRUE))
|
||||
return fail_at(arg, COMMAND_LINE_ERROR);
|
||||
if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, TRUE))
|
||||
return fail_at(arg, COMMAND_LINE_ERROR);
|
||||
}
|
||||
CommandLineSwitchCase(arg, "pth")
|
||||
{
|
||||
if (!freerdp_settings_set_bool(settings, FreeRDP_ConsoleSession, TRUE))
|
||||
@ -5772,6 +5780,7 @@ BOOL freerdp_client_load_addins(rdpChannels* channels, rdpSettings* settings)
|
||||
{ FreeRDP_SupportDisplayControl, DISP_CHANNEL_NAME, NULL },
|
||||
{ FreeRDP_SupportGeometryTracking, GEOMETRY_CHANNEL_NAME, NULL },
|
||||
{ FreeRDP_SupportVideoOptimized, VIDEO_CHANNEL_NAME, NULL },
|
||||
{ FreeRDP_RemoteCredentialGuard, RDPEAR_CHANNEL_NAME, NULL },
|
||||
};
|
||||
|
||||
ChannelToLoad staticChannels[] = {
|
||||
|
@ -393,6 +393,8 @@ static const COMMAND_LINE_ARGUMENT_A global_cmd_args[] = {
|
||||
"connecting to a buggy server" },
|
||||
{ "restricted-admin", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, "restrictedAdmin",
|
||||
"Restricted admin mode" },
|
||||
{ "remoteGuard", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, "remoteGuard",
|
||||
"Remote guard credentials" },
|
||||
{ "rfx", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, "RemoteFX" },
|
||||
{ "rfx-mode", COMMAND_LINE_VALUE_REQUIRED, "[image|video]", NULL, NULL, -1, NULL,
|
||||
"RemoteFX mode" },
|
||||
|
@ -77,7 +77,7 @@ endfunction()
|
||||
|
||||
function(GET_KRB5_BY_CONFIG KRB5_CONFIG)
|
||||
if (NOT KRB5_CONFIG)
|
||||
find_file(KRB5_CONFIG
|
||||
find_program(KRB5_CONFIG
|
||||
NAMES
|
||||
"krb5-config"
|
||||
"krb5-config.mit"
|
||||
@ -89,6 +89,9 @@ function(GET_KRB5_BY_CONFIG KRB5_CONFIG)
|
||||
REQUIRED
|
||||
)
|
||||
message("autodetected krb5-config at ${KRB5_CONFIG}")
|
||||
if (NOT KRB5_CONFIG)
|
||||
return()
|
||||
endif()
|
||||
else()
|
||||
message("using krb5-config ${KRB5_CONFIG} provided by KRB5_ROOT_CONFIG")
|
||||
endif()
|
||||
@ -195,7 +198,7 @@ elseif(KRB5_ANY_FOUND)
|
||||
GET_KRB5_VENDOR(ANY_VENDOR)
|
||||
PROVIDES_KRB5(NAME "${ANY_VENDOR}")
|
||||
else()
|
||||
GET_KRB5_BY_CONFIG(${KRB5_ROOT_CONFIG})
|
||||
GET_KRB5_BY_CONFIG("${KRB5_ROOT_CONFIG}")
|
||||
endif()
|
||||
|
||||
#message("using KRB5_FOUND ${KRB5_FOUND} ")
|
||||
|
39
include/freerdp/channels/rdpear.h
Normal file
39
include/freerdp/channels/rdpear.h
Normal file
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Authentication redirection channel
|
||||
*
|
||||
* Copyright 2023 David Fort <contact@hardening-consulting.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.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_CHANNEL_RDPEAR_H
|
||||
#define FREERDP_CHANNEL_RDPEAR_H
|
||||
|
||||
#include <freerdp/api.h>
|
||||
#include <freerdp/dvc.h>
|
||||
#include <freerdp/types.h>
|
||||
|
||||
#define RDPEAR_CHANNEL_NAME "rdpear"
|
||||
#define RDPEAR_DVC_CHANNEL_NAME "Microsoft::Windows::RDS::AuthRedirection"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FREERDP_CHANNEL_RDPEAR_H */
|
@ -23,6 +23,7 @@
|
||||
#define FREERDP_H
|
||||
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/sspi.h>
|
||||
|
||||
#include <freerdp/api.h>
|
||||
#include <freerdp/types.h>
|
||||
@ -538,10 +539,10 @@ owned by rdpRdp */
|
||||
It is used to get the username/password. The reason
|
||||
argument tells why it was called. */
|
||||
ALIGN64 pChooseSmartcard
|
||||
ChooseSmartcard; /* (offset 70)
|
||||
Callback for choosing a smartcard for logon.
|
||||
Used when multiple smartcards are available. Returns an index into a list
|
||||
of SmartcardCertInfo pointers */
|
||||
ChooseSmartcard; /* (offset 70)
|
||||
Callback for choosing a smartcard for logon.
|
||||
Used when multiple smartcards are available. Returns an index into a list
|
||||
of SmartcardCertInfo pointers */
|
||||
ALIGN64 pGetAccessToken GetAccessToken; /* (offset 71)
|
||||
Callback for obtaining an access token
|
||||
for \b AccessTokenType authentication */
|
||||
@ -660,6 +661,40 @@ owned by rdpRdp */
|
||||
|
||||
FREERDP_API UINT32 freerdp_get_nla_sspi_error(rdpContext* context);
|
||||
|
||||
/** Encrypts the provided buffer using the NLA's GSSAPI context
|
||||
*
|
||||
* \param context the RDP context
|
||||
* \param inBuffer the SecBuffer buffer to encrypt
|
||||
* \param outBuffer a SecBuffer to hold the encrypted content
|
||||
* \returns if the operation completed successfully
|
||||
* \since version 3.9.0
|
||||
*/
|
||||
FREERDP_API BOOL freerdp_nla_encrypt(rdpContext* context, const SecBuffer* inBuffer,
|
||||
SecBuffer* outBuffer);
|
||||
|
||||
/** Decrypts the provided buffer using the NLA's GSSAPI context
|
||||
*
|
||||
* \param context the RDP context
|
||||
* \param inBuffer the SecBuffer buffer to decrypt
|
||||
* \param outBuffer a SecBuffer to hold the decrypted content
|
||||
* \returns if the operation completed successfully
|
||||
* \since version 3.9.0
|
||||
*/
|
||||
FREERDP_API BOOL freerdp_nla_decrypt(rdpContext* context, const SecBuffer* inBuffer,
|
||||
SecBuffer* outBuffer);
|
||||
|
||||
/** Calls QueryContextAttributes on the SSPI context associated with the NLA part of
|
||||
* the RDP context
|
||||
*
|
||||
* \param context the RDP context
|
||||
* \param ulAttr the attribute
|
||||
* \param pBuffer an opaque pointer depending on ulAttr
|
||||
* \returns a SECURITY_STATUS indicating if the operation completed successfully
|
||||
* \since version 3.9.0
|
||||
*/
|
||||
FREERDP_API SECURITY_STATUS freerdp_nla_QueryContextAttributes(rdpContext* context,
|
||||
DWORD ulAttr, PVOID pBuffer);
|
||||
|
||||
FREERDP_API void clearChannelError(rdpContext* context);
|
||||
FREERDP_API HANDLE getChannelErrorEventHandle(rdpContext* context);
|
||||
FREERDP_API UINT getChannelError(rdpContext* context);
|
||||
|
@ -402,6 +402,7 @@ BOOL rdp_client_connect(rdpRdp* rdp)
|
||||
nego_set_preconnection_blob(rdp->nego, settings->PreconnectionBlob);
|
||||
nego_set_negotiation_enabled(rdp->nego, settings->NegotiateSecurityLayer);
|
||||
nego_set_restricted_admin_mode_required(rdp->nego, settings->RestrictedAdminModeRequired);
|
||||
nego_set_RCG_required(rdp->nego, settings->RemoteCredentialGuard);
|
||||
nego_set_gateway_enabled(rdp->nego, settings->GatewayEnabled);
|
||||
nego_set_gateway_bypass_local(rdp->nego, settings->GatewayBypassLocal);
|
||||
nego_enable_rdp(rdp->nego, settings->RdpSecurity);
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <winpr/assert.h>
|
||||
#include <winpr/library.h>
|
||||
#include <winpr/registry.h>
|
||||
#include <winpr/sspi.h>
|
||||
|
||||
#include <freerdp/log.h>
|
||||
|
||||
@ -694,6 +695,17 @@ UINT32 credssp_auth_sspi_error(rdpCredsspAuth* auth)
|
||||
return (UINT32)auth->sspi_error;
|
||||
}
|
||||
|
||||
void credssp_auth_tableAndContext(rdpCredsspAuth* auth, SecurityFunctionTable** ptable,
|
||||
CtxtHandle* pcontext)
|
||||
{
|
||||
WINPR_ASSERT(auth);
|
||||
WINPR_ASSERT(ptable);
|
||||
WINPR_ASSERT(pcontext);
|
||||
|
||||
*ptable = auth->table;
|
||||
*pcontext = auth->context;
|
||||
}
|
||||
|
||||
void credssp_auth_free(rdpCredsspAuth* auth)
|
||||
{
|
||||
SEC_WINPR_KERBEROS_SETTINGS* krb_settings = NULL;
|
||||
|
@ -30,6 +30,7 @@ typedef struct rdp_credssp_auth rdpCredsspAuth;
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <winpr/tchar.h>
|
||||
#include <winpr/sspi.h>
|
||||
#include <winpr/secapi.h>
|
||||
|
||||
FREERDP_LOCAL void credssp_auth_free(rdpCredsspAuth* auth);
|
||||
|
||||
@ -62,4 +63,8 @@ FREERDP_LOCAL const char* credssp_auth_pkg_name(rdpCredsspAuth* auth);
|
||||
FREERDP_LOCAL size_t credssp_auth_trailer_size(rdpCredsspAuth* auth);
|
||||
FREERDP_LOCAL UINT32 credssp_auth_sspi_error(rdpCredsspAuth* auth);
|
||||
|
||||
FREERDP_LOCAL void credssp_auth_tableAndContext(rdpCredsspAuth* auth,
|
||||
SecurityFunctionTable** ptable,
|
||||
CtxtHandle* pcontext);
|
||||
|
||||
#endif /* FREERDP_LIB_CORE_CREDSSP_AUTH_H */
|
||||
|
@ -1224,6 +1224,32 @@ UINT32 freerdp_get_nla_sspi_error(rdpContext* context)
|
||||
return nla_get_sspi_error(nla);
|
||||
}
|
||||
|
||||
BOOL freerdp_nla_encrypt(rdpContext* context, const SecBuffer* inBuffer, SecBuffer* outBuffer)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(context->rdp);
|
||||
|
||||
rdpNla* nla = context->rdp->nla;
|
||||
return nla_encrypt(nla, inBuffer, outBuffer);
|
||||
}
|
||||
|
||||
BOOL freerdp_nla_decrypt(rdpContext* context, const SecBuffer* inBuffer, SecBuffer* outBuffer)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(context->rdp);
|
||||
|
||||
rdpNla* nla = context->rdp->nla;
|
||||
return nla_decrypt(nla, inBuffer, outBuffer);
|
||||
}
|
||||
|
||||
SECURITY_STATUS freerdp_nla_QueryContextAttributes(rdpContext* context, DWORD ulAttr, PVOID pBuffer)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(context->rdp);
|
||||
|
||||
return nla_QueryContextAttributes(context->rdp->nla, ulAttr, pBuffer);
|
||||
}
|
||||
|
||||
HANDLE getChannelErrorEventHandle(rdpContext* context)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
@ -1405,6 +1405,78 @@ static BOOL nla_read_ts_credentials(rdpNla* nla, SecBuffer* data)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BOOL nla_write_KERB_TICKET_LOGON(wStream* s, const KERB_TICKET_LOGON* ticket)
|
||||
{
|
||||
WINPR_ASSERT(ticket);
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, (4ULL * 4) + 16ULL + ticket->ServiceTicketLength +
|
||||
ticket->TicketGrantingTicketLength))
|
||||
return FALSE;
|
||||
|
||||
Stream_Write_UINT32(s, KerbTicketLogon);
|
||||
Stream_Write_UINT32(s, ticket->Flags);
|
||||
Stream_Write_UINT32(s, ticket->ServiceTicketLength);
|
||||
Stream_Write_UINT32(s, ticket->TicketGrantingTicketLength);
|
||||
|
||||
Stream_Write_UINT64(s, 0x20); /* offset of TGS in the packet */
|
||||
Stream_Write_UINT64(s, 0x20 + ticket->ServiceTicketLength); /* offset of TGT in packet */
|
||||
|
||||
Stream_Write(s, ticket->ServiceTicket, ticket->ServiceTicketLength);
|
||||
Stream_Write(s, ticket->TicketGrantingTicket, ticket->TicketGrantingTicketLength);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL nla_get_KERB_TICKET_LOGON(rdpNla* nla, KERB_TICKET_LOGON* logonTicket)
|
||||
{
|
||||
WINPR_ASSERT(nla);
|
||||
WINPR_ASSERT(logonTicket);
|
||||
|
||||
SecurityFunctionTable* table;
|
||||
CtxtHandle context;
|
||||
credssp_auth_tableAndContext(nla->auth, &table, &context);
|
||||
return table->QueryContextAttributes(&context, SECPKG_CRED_ATTR_TICKET_LOGON, logonTicket) ==
|
||||
SEC_E_OK;
|
||||
}
|
||||
|
||||
static BOOL nla_write_TSRemoteGuardKerbCred(rdpNla* nla, WinPrAsn1Encoder* enc)
|
||||
{
|
||||
BOOL ret = FALSE;
|
||||
wStream* s = NULL;
|
||||
char kerberos[] = { 'K', '\0', 'e', '\0', 'r', '\0', 'b', '\0',
|
||||
'e', '\0', 'r', '\0', 'o', '\0', 's', '\0' };
|
||||
WinPrAsn1_OctetString packageName = { sizeof(kerberos), (BYTE*)kerberos };
|
||||
WinPrAsn1_OctetString credBuffer;
|
||||
KERB_TICKET_LOGON logonTicket;
|
||||
|
||||
logonTicket.ServiceTicket = NULL;
|
||||
logonTicket.TicketGrantingTicket = NULL;
|
||||
|
||||
/* packageName [0] OCTET STRING */
|
||||
if (!WinPrAsn1EncContextualOctetString(enc, 0, &packageName))
|
||||
goto out;
|
||||
|
||||
/* credBuffer [1] OCTET STRING */
|
||||
if (!nla_get_KERB_TICKET_LOGON(nla, &logonTicket))
|
||||
goto out;
|
||||
|
||||
s = Stream_New(NULL, 2000);
|
||||
if (!s)
|
||||
goto out;
|
||||
|
||||
if (!nla_write_KERB_TICKET_LOGON(s, &logonTicket))
|
||||
goto out;
|
||||
|
||||
credBuffer.len = Stream_GetPosition(s);
|
||||
credBuffer.data = Stream_Buffer(s);
|
||||
ret = WinPrAsn1EncContextualOctetString(enc, 1, &credBuffer) != 0;
|
||||
|
||||
out:
|
||||
free(logonTicket.ServiceTicket);
|
||||
free(logonTicket.TicketGrantingTicket);
|
||||
Stream_Free(s, TRUE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode TSCredentials structure.
|
||||
* @param nla A pointer to the NLA to use
|
||||
@ -1547,6 +1619,29 @@ static BOOL nla_encode_ts_credentials(rdpNla* nla)
|
||||
break;
|
||||
}
|
||||
case TSCREDS_REMOTEGUARD:
|
||||
/* TSRemoteGuardCreds */
|
||||
if (!WinPrAsn1EncSeqContainer(enc))
|
||||
goto out;
|
||||
|
||||
/* logonCred [0] TSRemoteGuardPackageCred, */
|
||||
if (!WinPrAsn1EncContextualSeqContainer(enc, 0))
|
||||
goto out;
|
||||
|
||||
if (!nla_write_TSRemoteGuardKerbCred(nla, enc) || !WinPrAsn1EncEndContainer(enc))
|
||||
goto out;
|
||||
|
||||
/* supplementalCreds [1] SEQUENCE OF TSRemoteGuardPackageCred OPTIONAL,
|
||||
*
|
||||
* no NTLM supplemental creds for now
|
||||
*
|
||||
*/
|
||||
if (!WinPrAsn1EncContextualSeqContainer(enc, 1) || !WinPrAsn1EncEndContainer(enc))
|
||||
goto out;
|
||||
|
||||
/* End TSRemoteGuardCreds */
|
||||
if (!WinPrAsn1EncEndContainer(enc))
|
||||
goto out;
|
||||
break;
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
@ -2107,3 +2202,30 @@ UINT32 nla_get_sspi_error(rdpNla* nla)
|
||||
WINPR_ASSERT(nla);
|
||||
return credssp_auth_sspi_error(nla->auth);
|
||||
}
|
||||
|
||||
BOOL nla_encrypt(rdpNla* nla, const SecBuffer* inBuffer, SecBuffer* outBuffer)
|
||||
{
|
||||
WINPR_ASSERT(nla);
|
||||
WINPR_ASSERT(inBuffer);
|
||||
WINPR_ASSERT(outBuffer);
|
||||
return credssp_auth_encrypt(nla->auth, inBuffer, outBuffer, NULL, nla->sendSeqNum++);
|
||||
}
|
||||
|
||||
BOOL nla_decrypt(rdpNla* nla, const SecBuffer* inBuffer, SecBuffer* outBuffer)
|
||||
{
|
||||
WINPR_ASSERT(nla);
|
||||
WINPR_ASSERT(inBuffer);
|
||||
WINPR_ASSERT(outBuffer);
|
||||
return credssp_auth_decrypt(nla->auth, inBuffer, outBuffer, nla->recvSeqNum++);
|
||||
}
|
||||
|
||||
SECURITY_STATUS nla_QueryContextAttributes(rdpNla* nla, DWORD ulAttr, PVOID pBuffer)
|
||||
{
|
||||
WINPR_ASSERT(nla);
|
||||
|
||||
SecurityFunctionTable* table;
|
||||
CtxtHandle context;
|
||||
credssp_auth_tableAndContext(nla->auth, &table, &context);
|
||||
|
||||
return table->QueryContextAttributes(&context, ulAttr, pBuffer);
|
||||
}
|
||||
|
@ -75,5 +75,8 @@ WINPR_ATTR_MALLOC(nla_free, 1)
|
||||
FREERDP_LOCAL rdpNla* nla_new(rdpContext* context, rdpTransport* transport);
|
||||
|
||||
FREERDP_LOCAL void nla_set_early_user_auth(rdpNla* nla, BOOL earlyUserAuth);
|
||||
FREERDP_LOCAL BOOL nla_encrypt(rdpNla* nla, const SecBuffer* inBuffer, SecBuffer* outBuffer);
|
||||
FREERDP_LOCAL BOOL nla_decrypt(rdpNla* nla, const SecBuffer* inBuffer, SecBuffer* outBuffer);
|
||||
FREERDP_LOCAL SECURITY_STATUS nla_QueryContextAttributes(rdpNla* nla, DWORD ulAttr, PVOID pBuffer);
|
||||
|
||||
#endif /* FREERDP_LIB_CORE_NLA_H */
|
||||
|
@ -1332,6 +1332,8 @@ extern "C"
|
||||
#define SECPKG_ATTR_AUTH_NTLM_MIC 1106
|
||||
#define SECPKG_ATTR_AUTH_NTLM_MIC_VALUE 1107
|
||||
|
||||
#define SECPKG_CRED_ATTR_TICKET_LOGON 1200
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char User[256 + 1];
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1000,7 +1000,7 @@ size_t WinPrAsn1DecReadBoolean(WinPrAsn1Decoder* dec, WinPrAsn1_BOOL* target)
|
||||
ret = readTagAndLen(dec, &dec->source, &tag, &len);
|
||||
if (!ret || tag != ER_TAG_BOOLEAN)
|
||||
return 0;
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, &dec->source, len) || len != 1)
|
||||
if (Stream_GetRemainingLength(&dec->source) < len || len != 1)
|
||||
return 0;
|
||||
|
||||
Stream_Read_UINT8(&dec->source, v);
|
||||
@ -1020,7 +1020,7 @@ static size_t WinPrAsn1DecReadIntegerLike(WinPrAsn1Decoder* dec, WinPrAsn1_tag e
|
||||
size_t ret = readTagAndLen(dec, &dec->source, &tag, &len);
|
||||
if (!ret || (tag != expectedTag))
|
||||
return 0;
|
||||
if (len == 0 || !Stream_CheckAndLogRequiredLength(TAG, &dec->source, len) || (len > 4))
|
||||
if (len == 0 || Stream_GetRemainingLength(&dec->source) < len || (len > 4))
|
||||
return 0;
|
||||
|
||||
WinPrAsn1_INTEGER val = 0;
|
||||
|
@ -6,6 +6,8 @@ static const BYTE badBoolContent[] = { 0x01, 0x04, 0xFF };
|
||||
|
||||
static const BYTE integerContent[] = { 0x02, 0x01, 0x02 };
|
||||
static const BYTE badIntegerContent[] = { 0x02, 0x04, 0x02 };
|
||||
static const BYTE positiveIntegerContent[] = { 0x02, 0x02, 0x00, 0xff };
|
||||
static const BYTE negativeIntegerContent[] = { 0x02, 0x01, 0xff };
|
||||
|
||||
static const BYTE seqContent[] = { 0x30, 0x22, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x1B, 0x44,
|
||||
0x69, 0x67, 0x69, 0x74, 0x61, 0x6C, 0x20, 0x53, 0x69, 0x67,
|
||||
@ -46,6 +48,16 @@ int TestASN1Read(int argc, char* argv[])
|
||||
if (!WinPrAsn1DecReadInteger(&decoder, &integerV))
|
||||
return -1;
|
||||
|
||||
Stream_StaticConstInit(&staticS, positiveIntegerContent, sizeof(positiveIntegerContent));
|
||||
WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
|
||||
if (!WinPrAsn1DecReadInteger(&decoder, &integerV) && integerV != 0xff)
|
||||
return -1;
|
||||
|
||||
Stream_StaticConstInit(&staticS, negativeIntegerContent, sizeof(negativeIntegerContent));
|
||||
WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
|
||||
if (!WinPrAsn1DecReadInteger(&decoder, &integerV) && integerV != -1)
|
||||
return -1;
|
||||
|
||||
Stream_StaticConstInit(&staticS, badIntegerContent, sizeof(badIntegerContent));
|
||||
WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
|
||||
if (WinPrAsn1DecReadInteger(&decoder, &integerV))
|
||||
|
Loading…
Reference in New Issue
Block a user