Merge pull request #10549 from hardening/rcg2

core, channels: client-side remote credential guard
This commit is contained in:
akallabeth 2024-09-13 08:24:14 +02:00 committed by GitHub
commit 7ef9345743
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 4302 additions and 264 deletions

View 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()

View 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})

View 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")

View 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);
}

View 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()

View 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;
}

View 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_ */

View 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__ */

View 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 */

View 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;
}

View 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)

View 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")

View 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;
}

View 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);
}

View File

@ -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[] = {

View File

@ -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" },

View File

@ -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} ")

View 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 */

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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 */

View File

@ -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);

View File

@ -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);
}

View File

@ -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 */

View File

@ -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

View File

@ -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;

View File

@ -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))