You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2624 lines
105 KiB
2624 lines
105 KiB
/****************************************************************************/
|
|
// slint.cpp
|
|
//
|
|
// RDP client Security Layer functions.
|
|
//
|
|
// Copyright (C) 1997-2000 Microsoft Corporation
|
|
/****************************************************************************/
|
|
|
|
#include <adcg.h>
|
|
extern "C" {
|
|
#define TRC_GROUP TRC_GROUP_SECURITY
|
|
#define TRC_FILE "aslint"
|
|
#include <atrcapi.h>
|
|
#include "licecert.h"
|
|
#include "regapi.h"
|
|
}
|
|
|
|
#ifndef OS_WINCE
|
|
#include <hydrix.h>
|
|
#endif
|
|
|
|
//
|
|
// Autoreconnect security related
|
|
//
|
|
#include <md5.h>
|
|
#include <hmac.h>
|
|
|
|
|
|
#include "autil.h"
|
|
#include "cd.h"
|
|
#include "sl.h"
|
|
#include "nl.h"
|
|
#include "wui.h"
|
|
#include "aco.h"
|
|
#include "clicense.h"
|
|
#include "clx.h"
|
|
|
|
//
|
|
// Instrumentation
|
|
//
|
|
DWORD g_dwSLDbgStatus = 0;
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: SL_OnInitialized */
|
|
/* */
|
|
/* Purpose: Called by NL when its initialization is complete */
|
|
/* */
|
|
/* Operation: Called in the Receive context */
|
|
/****************************************************************************/
|
|
void DCCALLBACK CSL::SL_OnInitialized()
|
|
{
|
|
DC_BEGIN_FN("SL_OnInitialized");
|
|
|
|
SL_DBG_SETINFO(SL_DBG_ONINIT_CALLED);
|
|
|
|
SL_CHECK_STATE(SL_EVENT_ON_INITIALIZED);
|
|
|
|
//
|
|
// Sets state here to prevent a race that
|
|
// causes problems when disconnection/connecting quickly
|
|
// change made to fix webctrl problem.
|
|
//
|
|
SL_SET_STATE(SL_STATE_INITIALIZED);
|
|
|
|
TRC_NRM((TB, _T("Initialized")));
|
|
_SL.callbacks.onInitialized(_pCo);
|
|
|
|
SL_DBG_SETINFO(SL_DBG_ONINIT_DONE1);
|
|
|
|
DC_EXIT_POINT:
|
|
SL_DBG_SETINFO(SL_DBG_ONINIT_DONE2);
|
|
|
|
DC_END_FN();
|
|
} /* SL_OnInitialized */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: SL_OnTerminating */
|
|
/* */
|
|
/* Purpose: Called by NL before terminating */
|
|
/* */
|
|
/* Operation: This function is called on the NL's receive thread to allow */
|
|
/* resources to be freed prior to termination. */
|
|
/****************************************************************************/
|
|
|
|
//TEMP instrumentation to help track down races
|
|
DCUINT g_slDbgStateOnTerminating = -1;
|
|
void DCCALLBACK CSL::SL_OnTerminating()
|
|
{
|
|
DC_BEGIN_FN("SL_OnTerminating");
|
|
|
|
SL_DBG_SETINFO(SL_DBG_ONTERM_CALLED);
|
|
g_slDbgStateOnTerminating = _SL.state;
|
|
|
|
SL_CHECK_STATE(SL_EVENT_ON_TERMINATING);
|
|
TRC_NRM((TB, _T("Terminating")));
|
|
|
|
SLFreeConnectResources();
|
|
SLFreeInitResources();
|
|
|
|
/************************************************************************/
|
|
/* No need to clear SL data - it will be reinitialied to 0 when */
|
|
/* SL_Init() is called. However, reset 'initialized' flag here in case */
|
|
/* a late SL_ call comes in. */
|
|
/************************************************************************/
|
|
|
|
// Call the Core's callback.
|
|
_SL.callbacks.onTerminating(_pCo);
|
|
SL_SET_STATE(SL_STATE_TERMINATED);
|
|
|
|
SL_DBG_SETINFO(SL_DBG_ONTERM_DONE1);
|
|
|
|
DC_EXIT_POINT:
|
|
SL_DBG_SETINFO(SL_DBG_ONTERM_DONE2);
|
|
|
|
DC_END_FN();
|
|
} /* SL_OnTerminating */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: SL_OnConnected */
|
|
/* */
|
|
/* Purpose: Called by NL when its connection to the Server is complete */
|
|
/* */
|
|
/* Params: channelID - ID of T.Share broadcast channel */
|
|
/* pUserData - user data from Server */
|
|
/* userDataLength - length of user data */
|
|
/* */
|
|
/* Operation: Called in the Receive context */
|
|
/****************************************************************************/
|
|
DCVOID DCCALLBACK CSL::SL_OnConnected(DCUINT channelID,
|
|
PDCVOID pUserData,
|
|
DCUINT userDataLength,
|
|
DCUINT32 serverVersion)
|
|
{
|
|
PRNS_UD_SC_SEC1 pSecUD;
|
|
PDCUINT8 pSecUDEnd;
|
|
DCUINT32 encMethod;
|
|
CERT_TYPE CertType = CERT_TYPE_INVALID;
|
|
BOOL fDisconnect;
|
|
|
|
DC_BEGIN_FN("SL_OnConnected");
|
|
|
|
SL_CHECK_STATE(SL_EVENT_ON_CONNECTED);
|
|
|
|
/************************************************************************/
|
|
/* Save channel ID */
|
|
/************************************************************************/
|
|
TRC_NRM((TB, _T("Share channel %x"), channelID));
|
|
_SL.channelID = channelID;
|
|
|
|
TRC_NRM((TB, _T("Server version %x"), serverVersion));
|
|
_SL.serverVersion = serverVersion;
|
|
|
|
/************************************************************************/
|
|
/* Check for user data. */
|
|
/************************************************************************/
|
|
if ((NULL == pUserData) || (0 == userDataLength))
|
|
{
|
|
TRC_ERR((TB, _T("No user data (pUserData:%p length:%u)"),
|
|
pUserData,
|
|
userDataLength));
|
|
|
|
_pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, this,
|
|
CD_NOTIFICATION_FUNC(CSL, SLSetReasonAndDisconnect),
|
|
(ULONG_PTR) SL_ERR_NOSECURITYUSERDATA);
|
|
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* userDataLength has already been verified to sit within the received */
|
|
/* packet in nccb.cpp!NC_OnMCSChannelJoinConfirm. (This was a retail */
|
|
/* check.) Thus, the allocation size is bounded by network-speed. */
|
|
/* The allocation is capped at thesize of a receive-packet buffer. */
|
|
/************************************************************************/
|
|
TRC_ASSERT((userDataLength <= MCS_MAX_RCVPKT_LENGTH),
|
|
(TB, _T("UserData expected to be smaller than a packet size (sanity check)")));
|
|
|
|
/************************************************************************/
|
|
/* There is some user data. Allocate space for a copy, because it is */
|
|
/* not valid after this function returns, and SL needs it beyond that */
|
|
/* time. */
|
|
/************************************************************************/
|
|
TRC_NRM((TB, _T("Got %u bytes of user data"), userDataLength));
|
|
TRC_DATA_NRM("User data", pUserData, userDataLength);
|
|
|
|
_SL.pSCUserData = (PDCUINT8)UT_Malloc(_pUt, userDataLength);
|
|
if (_SL.pSCUserData == NULL)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to alloc %u bytes for user data"),
|
|
userDataLength));
|
|
|
|
_pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, this,
|
|
CD_NOTIFICATION_FUNC(CSL, SLSetReasonAndDisconnect),
|
|
(ULONG_PTR) SL_ERR_NOMEMFORRECVUD);
|
|
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Save the userdata, to be passed to the Core later. */
|
|
/************************************************************************/
|
|
memcpy(_SL.pSCUserData, pUserData, userDataLength);
|
|
_SL.SCUserDataLength = userDataLength;
|
|
|
|
/************************************************************************/
|
|
/* Loop through each piece of user data: - if it's security data, save */
|
|
/* the encryption type */
|
|
/************************************************************************/
|
|
pSecUD = (PRNS_UD_SC_SEC1)
|
|
_pUt->UT_ParseUserData(
|
|
(PRNS_UD_HEADER)pUserData,
|
|
userDataLength,
|
|
RNS_UD_SC_SEC_ID);
|
|
if (pSecUD == NULL)
|
|
{
|
|
/********************************************************************/
|
|
/* There is no SECURITY data in UserData, so disconnect. */
|
|
/********************************************************************/
|
|
TRC_ERR((TB, _T("No SECURITY user data")));
|
|
|
|
_pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, this,
|
|
CD_NOTIFICATION_FUNC(CSL, SLSetReasonAndDisconnect),
|
|
(ULONG_PTR) SL_ERR_NOSECURITYUSERDATA);
|
|
DC_QUIT;
|
|
}
|
|
pSecUDEnd = (PDCUINT8)pSecUD + pSecUD->header.length;
|
|
|
|
fDisconnect = FALSE;
|
|
if ((PDCUINT8)pSecUD + sizeof(RNS_UD_SC_SEC) > pSecUDEnd)
|
|
{
|
|
fDisconnect = TRUE;
|
|
}
|
|
else if (0 != ((PRNS_UD_SC_SEC)pSecUD)->encryptionLevel)
|
|
{
|
|
/********************************************************************/
|
|
/* There should be at least enough data for: */
|
|
/* a) PRNS_UD_SC_SEC1 struct */
|
|
/* b) RANDOM_KEY_LENGTH */
|
|
/* c) pSecUD->serverCertLen (Certificate length) */
|
|
/********************************************************************/
|
|
if (((PDCUINT8)(&pSecUD->serverCertLen) + sizeof(pSecUD->serverCertLen) > pSecUDEnd) ||
|
|
(((PDCUINT8)pSecUD +
|
|
sizeof(RNS_UD_SC_SEC1) +
|
|
RANDOM_KEY_LENGTH +
|
|
pSecUD->serverCertLen) > pSecUDEnd))
|
|
{
|
|
fDisconnect = TRUE;
|
|
}
|
|
}
|
|
|
|
if (fDisconnect)
|
|
{
|
|
TRC_ABORT((TB, _T("Invalid SECURITY user data")));
|
|
|
|
_pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, this,
|
|
CD_NOTIFICATION_FUNC(CSL, SLSetReasonAndDisconnect),
|
|
(ULONG_PTR) SL_ERR_NOSECURITYUSERDATA);
|
|
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* remember the server's encryption level and encryption method. */
|
|
/************************************************************************/
|
|
_SL.encryptionLevel = pSecUD->encryptionLevel;
|
|
_SL.encryptionMethodSelected =
|
|
encMethod = pSecUD->encryptionMethod;
|
|
|
|
// If FIPS GP is set, disconnect if encryptionMethod is not FIPS
|
|
if (_SL.encryptionMethodsSupported == SM_FIPS_ENCRYPTION_FLAG) {
|
|
if (encMethod != SM_FIPS_ENCRYPTION_FLAG) {
|
|
TRC_ERR((TB, _T("Invalid encryption method received, %u"), encMethod ));
|
|
|
|
_pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, this,
|
|
CD_NOTIFICATION_FUNC(CSL, SLSetReasonAndDisconnect),
|
|
SL_ERR_INVALIDENCMETHOD);
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
|
|
if( !encMethod ) {
|
|
TRC_NRM((TB, _T("No encryption for this session")));
|
|
_SL.encrypting = FALSE;
|
|
}
|
|
else {
|
|
PDCUINT8 pData;
|
|
|
|
if( (encMethod != SM_40BIT_ENCRYPTION_FLAG) &&
|
|
(encMethod != SM_56BIT_ENCRYPTION_FLAG) &&
|
|
(encMethod != SM_128BIT_ENCRYPTION_FLAG) &&
|
|
(encMethod != SM_FIPS_ENCRYPTION_FLAG) ) {
|
|
|
|
TRC_ERR((TB, _T("Invalid encryption method received, %u"), encMethod ));
|
|
|
|
_pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, this,
|
|
CD_NOTIFICATION_FUNC(CSL, SLSetReasonAndDisconnect),
|
|
SL_ERR_INVALIDENCMETHOD);
|
|
DC_QUIT;
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* gather the client random and verify the server certificate sent. */
|
|
/********************************************************************/
|
|
if( pSecUD->serverRandomLen != RANDOM_KEY_LENGTH ) {
|
|
|
|
TRC_ERR((TB, _T("Invalid server random received, %u"), encMethod ));
|
|
|
|
_pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, this,
|
|
CD_NOTIFICATION_FUNC(CSL, SLSetReasonAndDisconnect),
|
|
SL_ERR_INVALIDSRVRAND);
|
|
DC_QUIT;
|
|
}
|
|
|
|
pData = (PDCUINT8)pSecUD + sizeof(RNS_UD_SC_SEC1);
|
|
memcpy(_SL.keyPair.serverRandom, pData, RANDOM_KEY_LENGTH);
|
|
|
|
pData += RANDOM_KEY_LENGTH;
|
|
|
|
/********************************************************************/
|
|
/* validate the server certificate. */
|
|
/********************************************************************/
|
|
if (!SLValidateServerCert( pData, pSecUD->serverCertLen, &CertType))
|
|
{
|
|
TRC_ERR( ( TB, _T("Invalid server certificate received, %u"),
|
|
encMethod ) );
|
|
|
|
_pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, this,
|
|
CD_NOTIFICATION_FUNC(CSL, SLSetReasonAndDisconnect),
|
|
SL_ERR_INVALIDSRVCERT);
|
|
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (_SL.encryptionMethodSelected == SM_FIPS_ENCRYPTION_FLAG) {
|
|
if (!TSCAPI_Init(&_SL.SLCapiData) || !TSCAPI_Enable(&(_SL.SLCapiData))) {
|
|
TRC_ERR( ( TB, _T("Init CAPI failed")));
|
|
_pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, this,
|
|
CD_NOTIFICATION_FUNC(CSL, SLSetReasonAndDisconnect),
|
|
SL_ERR_INITFIPSFAILED);
|
|
DC_QUIT;
|
|
}
|
|
else {
|
|
TRC_ERR( ( TB, _T("Init CAPI succeed")));
|
|
}
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* generate client random key */
|
|
/********************************************************************/
|
|
if (_SL.encryptionMethodSelected == SM_FIPS_ENCRYPTION_FLAG) {
|
|
if (!TSCAPI_GenerateRandomNumber(
|
|
&(_SL.SLCapiData),
|
|
(PDCUINT8)_SL.keyPair.clientRandom,
|
|
sizeof(_SL.keyPair.clientRandom) ) ) {
|
|
TRC_ERR((TB, _T("Failed create client random") ));
|
|
|
|
_pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, this,
|
|
CD_NOTIFICATION_FUNC(CSL, SLSetReasonAndDisconnect),
|
|
SL_ERR_GENSRVRANDFAILED);
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
else {
|
|
if (!TSRNG_GenerateRandomBits(
|
|
(PDCUINT8)_SL.keyPair.clientRandom,
|
|
sizeof(_SL.keyPair.clientRandom) ) ) {
|
|
TRC_ERR((TB, _T("Failed create client random") ));
|
|
|
|
_pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, this,
|
|
CD_NOTIFICATION_FUNC(CSL, SLSetReasonAndDisconnect),
|
|
SL_ERR_GENSRVRANDFAILED);
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
if (_SL.encryptionMethodSelected == SM_FIPS_ENCRYPTION_FLAG) {
|
|
TSCAPI_MakeSessionKeys(&(_SL.SLCapiData), &(_SL.keyPair), NULL);
|
|
}
|
|
else {
|
|
/********************************************************************/
|
|
/* make security session keys */
|
|
/* */
|
|
/* Note : Server encryption key should be same as the client */
|
|
/* decryption key and vice versa. So the encryption/decryption */
|
|
/* parameters passed below are reverse order that of the server */
|
|
/* call. */
|
|
/********************************************************************/
|
|
if (!MakeSessionKeys(
|
|
&_SL.keyPair,
|
|
_SL.startDecryptKey,
|
|
&_SL.rc4DecryptKey,
|
|
_SL.startEncryptKey,
|
|
&_SL.rc4EncryptKey,
|
|
_SL.macSaltKey,
|
|
_SL.encryptionMethodSelected,
|
|
&_SL.keyLength,
|
|
_SL.encryptionLevel)) {
|
|
TRC_ERR((TB, _T("MakeSessionKeys failed") ));
|
|
|
|
_pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, this,
|
|
CD_NOTIFICATION_FUNC(CSL, SLSetReasonAndDisconnect),
|
|
SL_ERR_MKSESSKEYFAILED);
|
|
|
|
DC_QUIT;
|
|
}
|
|
|
|
TRC_ASSERT((_SL.keyLength == (DCUINT32)
|
|
((_SL.encryptionMethodSelected ==
|
|
SM_128BIT_ENCRYPTION_FLAG) ?
|
|
MAX_SESSION_KEY_SIZE :
|
|
(MAX_SESSION_KEY_SIZE/2))),
|
|
(TB, _T("Invalid key length")));
|
|
|
|
/********************************************************************/
|
|
/* use startKey as currentKey for the first time */
|
|
/********************************************************************/
|
|
memcpy(_SL.currentEncryptKey, _SL.startEncryptKey,
|
|
sizeof(_SL.currentEncryptKey));
|
|
memcpy(_SL.currentDecryptKey, _SL.startDecryptKey,
|
|
sizeof(_SL.currentEncryptKey));
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* reset encryption and decryption count. */
|
|
/********************************************************************/
|
|
_SL.encryptCount = 0;
|
|
_SL.decryptCount = 0;
|
|
_SL.totalEncryptCount = 0;
|
|
_SL.totalDecryptCount = 0;
|
|
|
|
_SL.encrypting = TRUE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Build and send security packet. Note that */
|
|
/* SLSendSecurityPacket will call onConnected/onDisconnected if: */
|
|
/* - the connection process completes successfully, OR */
|
|
/* - the connection process fails. */
|
|
/* */
|
|
/* Therefore SLSetReasonAndDisconnect is not called here on failure as */
|
|
/* that would cause SLSetReasonAndDisconnect to be called twice. */
|
|
/************************************************************************/
|
|
SL_SET_STATE(SL_STATE_SL_CONNECTING);
|
|
|
|
if( CERT_TYPE_PROPRIETORY == CertType )
|
|
{
|
|
SLSendSecurityPacket(
|
|
_SL.pServerCert->PublicKeyData.pBlob,
|
|
_SL.pServerCert->PublicKeyData.wBlobLen );
|
|
}
|
|
else if( CERT_TYPE_X509 == CertType )
|
|
{
|
|
SLSendSecurityPacket(
|
|
_SL.pbServerPubKey,
|
|
_SL.cbServerPubKey );
|
|
}
|
|
else if (!_SL.encrypting)
|
|
{
|
|
SLSendSecurityPacket( NULL, 0 );
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB, _T("Unexpected CertType %d"), CertType));
|
|
}
|
|
|
|
TRC_NRM((TB, _T("Security packets sent to the server")));
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
} /* SL_OnConnected */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: SLValidateServerCert */
|
|
/* */
|
|
/* Purpose: Validate the terminal server certificate */
|
|
/* */
|
|
/* Returns: TRUE if the certificate is validated successfully or FALSE */
|
|
/* FALSE otherwise. */
|
|
/* */
|
|
/* Params: pbCert - The server certificate. */
|
|
/* cbCert - size of the server certificate. */
|
|
/* */
|
|
/* Operation: Called by SL_OnConnected. */
|
|
/****************************************************************************/
|
|
DCBOOL DCINTERNAL CSL::SLValidateServerCert( PDCUINT8 pbCert,
|
|
DCUINT32 cbCert,
|
|
CERT_TYPE * pCertType )
|
|
{
|
|
DWORD
|
|
dwCertVersion;
|
|
DCBOOL
|
|
fResult = TRUE;
|
|
|
|
DC_BEGIN_FN( "SLValidateServerCert" );
|
|
|
|
/********************************************************************/
|
|
/* Make sure the packet is long enough for the version number. */
|
|
/********************************************************************/
|
|
if (cbCert < sizeof(DWORD))
|
|
{
|
|
TRC_ABORT( ( TB, _T("Invalid certificate version")));
|
|
fResult = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
memcpy( &dwCertVersion, pbCert, sizeof( DWORD ) );
|
|
|
|
if( CERT_CHAIN_VERSION_2 > GET_CERTIFICATE_VERSION( dwCertVersion ) )
|
|
{
|
|
//
|
|
// decode and validate proprietory certificate.
|
|
//
|
|
*pCertType = CERT_TYPE_PROPRIETORY;
|
|
|
|
_SL.pbCertificate = (PDCUINT8)UT_Malloc(_pUt, ( DCUINT )cbCert );
|
|
if( NULL == _SL.pbCertificate )
|
|
{
|
|
TRC_ERR( ( TB, _T("Failed to allocate %u bytes for server certificate"),
|
|
cbCert ) );
|
|
fResult = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
_SL.pServerCert = (PHydra_Server_Cert)UT_Malloc(_pUt, sizeof( Hydra_Server_Cert ) );
|
|
if( NULL == _SL.pServerCert )
|
|
{
|
|
TRC_ERR( ( TB, _T("Failed to allocate server certificate data structure") ) );
|
|
fResult = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
memcpy( _SL.pbCertificate, pbCert, cbCert );
|
|
_SL.cbCertificate = (unsigned)cbCert;
|
|
|
|
if( !UnpackServerCert( _SL.pbCertificate, _SL.cbCertificate, _SL.pServerCert ) )
|
|
{
|
|
TRC_ERR( ( TB, _T("Failed to unpack server certificate\n") ) ) ;
|
|
fResult = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* validate the server certificate. */
|
|
/********************************************************************/
|
|
if( !ValidateServerCert( _SL.pServerCert ) )
|
|
{
|
|
TRC_ERR( ( TB, _T("Invalid server certificate received\n") ) );
|
|
fResult = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
if( _pUi->UI_GetNotifyTSPublicKey() )
|
|
{
|
|
fResult = (BOOL) SendMessage(_pUi->_UI.hWndCntrl,
|
|
WM_TS_RECEIVEDPUBLICKEY,
|
|
(WPARAM)_SL.pServerCert->PublicKeyData.wBlobLen,
|
|
(LPARAM)_SL.pServerCert->PublicKeyData.pBlob
|
|
);
|
|
}
|
|
}
|
|
else if( MAX_CERT_CHAIN_VERSION >= GET_CERTIFICATE_VERSION( dwCertVersion ) )
|
|
{
|
|
LICENSE_STATUS
|
|
Status;
|
|
DWORD
|
|
fDates = CERT_DATE_DONT_VALIDATE;
|
|
|
|
//
|
|
// decode X509 certificate and extract public key
|
|
//
|
|
|
|
*pCertType = CERT_TYPE_X509;
|
|
|
|
Status = VerifyCertChain( pbCert, cbCert, NULL, &_SL.cbServerPubKey, &fDates );
|
|
|
|
if( LICENSE_STATUS_INSUFFICIENT_BUFFER == Status )
|
|
{
|
|
_SL.pbServerPubKey = (PDCUINT8)UT_Malloc( _pUt, ( DCUINT )_SL.cbServerPubKey );
|
|
}
|
|
else if( LICENSE_STATUS_OK != Status )
|
|
{
|
|
TRC_ERR( ( TB, _T("Failed to verify server certificate: %u\n"), Status ) ) ;
|
|
fResult = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
if( NULL == _SL.pbServerPubKey )
|
|
{
|
|
TRC_ERR( ( TB, _T("Failed to allocate %u bytes for server public key\n"),
|
|
_SL.cbServerPubKey ) ) ;
|
|
fResult = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
Status = VerifyCertChain( pbCert, cbCert, _SL.pbServerPubKey, &_SL.cbServerPubKey, &fDates );
|
|
|
|
if( LICENSE_STATUS_OK != Status )
|
|
{
|
|
TRC_ERR( ( TB, _T("Failed to verify server certificate: %d\n"), Status ) ) ;
|
|
fResult = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
if( _pUi->UI_GetNotifyTSPublicKey() )
|
|
{
|
|
fResult = (BOOL) SendMessage(_pUi->_UI.hWndCntrl,
|
|
WM_TS_RECEIVEDPUBLICKEY,
|
|
(WPARAM)_SL.cbServerPubKey,
|
|
(LPARAM)_SL.pbServerPubKey
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// don't know how to decode this version of certificate
|
|
//
|
|
TRC_ERR( ( TB, _T("Invalid certificate version: %d\n"),
|
|
GET_CERTIFICATE_VERSION( dwCertVersion ) ) ) ;
|
|
|
|
fResult = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
|
|
if( FALSE == fResult )
|
|
{
|
|
//
|
|
// free resources if failed.
|
|
//
|
|
if( CERT_TYPE_PROPRIETORY == *pCertType )
|
|
{
|
|
if( _SL.pServerCert )
|
|
{
|
|
UT_Free( _pUt, _SL.pServerCert );
|
|
_SL.pServerCert = NULL;
|
|
}
|
|
|
|
if( _SL.pbCertificate )
|
|
{
|
|
UT_Free( _pUt, _SL.pbCertificate );
|
|
_SL.pbCertificate = NULL;
|
|
_SL.cbCertificate = 0;
|
|
}
|
|
}
|
|
else if( CERT_TYPE_X509 == *pCertType )
|
|
{
|
|
if( _SL.pbServerPubKey )
|
|
{
|
|
UT_Free( _pUt, _SL.pbServerPubKey );
|
|
_SL.pbServerPubKey = NULL;
|
|
_SL.cbServerPubKey = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
DC_END_FN();
|
|
return( fResult );
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: SL_OnDisconnected */
|
|
/* */
|
|
/* Purpose: Called by NL when its connection to the Server is disconnected*/
|
|
/* */
|
|
/* Operation: Called in the Receive context */
|
|
/****************************************************************************/
|
|
void DCCALLBACK CSL::SL_OnDisconnected(unsigned reason)
|
|
{
|
|
DC_BEGIN_FN("SL_OnDisconnected");
|
|
|
|
SL_DBG_SETINFO(SL_DBG_ONDISC_CALLED);
|
|
|
|
SL_CHECK_STATE(SL_EVENT_ON_DISCONNECTED);
|
|
TRC_ASSERT((0 != reason), (TB, _T("Disconnect reason from NL is 0")));
|
|
|
|
// Free the connection resources and set the state to initialized.
|
|
SLFreeConnectResources();
|
|
SL_SET_STATE(SL_STATE_INITIALIZED);
|
|
|
|
// Decide if we want to over-ride the disconnect reason code.
|
|
if (_SL.disconnectErrorCode != 0)
|
|
{
|
|
TRC_ALT((TB, _T("Over-riding disconnection error code (%u->%u)"),
|
|
reason,
|
|
_SL.disconnectErrorCode));
|
|
|
|
// Over-ride the error code and set the global variable to 0.
|
|
reason = _SL.disconnectErrorCode;
|
|
_SL.disconnectErrorCode = 0;
|
|
}
|
|
|
|
// Tell the Core.
|
|
TRC_NRM((TB, _T("Disconnect reason:%u"), reason));
|
|
_SL.callbacks.onDisconnected(_pCo, reason);
|
|
|
|
SL_DBG_SETINFO(SL_DBG_ONDISC_DONE1);
|
|
|
|
DC_EXIT_POINT:
|
|
|
|
SL_DBG_SETINFO(SL_DBG_ONDISC_DONE2);
|
|
|
|
DC_END_FN();
|
|
} /* SL_OnDisconnected */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: SL_OnPacketReceived */
|
|
/* */
|
|
/* Purpose: Called by NL when a packet is received from the Server */
|
|
/* */
|
|
/* Params: pData - packet received */
|
|
/* dataLen - length of packet received */
|
|
/* flags - security flags (always 0) */
|
|
/* channelID - channel ID on which data was sent */
|
|
/* priority - priority on which packet was received */
|
|
/* */
|
|
/* Operation: Two types of packet might be received: */
|
|
/* - security packet (during the security exchange sequence) */
|
|
/* - data packet (otherwise). */
|
|
/* The code recognises a security packet as one which is received*/
|
|
/* - before the encryption type has been negotiated */
|
|
/* - AND before the session is connected. */
|
|
/* Other packets are data packets. */
|
|
/* */
|
|
/* Called in the Receive context */
|
|
/****************************************************************************/
|
|
HRESULT DCCALLBACK CSL::SL_OnPacketReceived(
|
|
PDCUINT8 pData,
|
|
DCUINT dataLen,
|
|
DCUINT flags,
|
|
DCUINT channelID,
|
|
DCUINT priority)
|
|
{
|
|
DCBOOL dataPacket;
|
|
HRESULT hrc = S_OK;
|
|
DCBOOL rc;
|
|
|
|
DC_BEGIN_FN("SL_OnPacketReceived");
|
|
|
|
/************************************************************************/
|
|
/* The packet should be at least large enough to hold Security Header */
|
|
/************************************************************************/
|
|
if (dataLen < sizeof(RNS_SECURITY_HEADER))
|
|
{
|
|
TRC_ABORT((TB, _T("SL packet too small for RNS_SECURITY_HEADER: %u"), dataLen));
|
|
|
|
//
|
|
// It is necessary to immediately abort the connection
|
|
// as we may be under attack here and we should stop processing
|
|
// any additional data
|
|
//
|
|
SL_DropLinkImmediate(SL_ERR_INVALIDPACKETFORMAT);
|
|
|
|
/************************************************************************/
|
|
/* Do not process the rest of this packet! */
|
|
/************************************************************************/
|
|
hrc = E_ABORT;
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Read the flags from the packet. */
|
|
/************************************************************************/
|
|
flags = (DCUINT)((PRNS_SECURITY_HEADER)pData)->flags;
|
|
|
|
/************************************************************************/
|
|
/* First of all, determine what type of packet this is */
|
|
/************************************************************************/
|
|
TRC_NRM((TB, _T("Encrypting? %s, state %s, flags %#lx: channel %x"),
|
|
_SL.encrypting ? "Y" : "N",
|
|
slState[_SL.state],
|
|
((PRNS_SECURITY_HEADER)pData)->flags, channelID ));
|
|
|
|
TRC_DATA_DBG("Pkt from NL", pData, dataLen);
|
|
|
|
/************************************************************************/
|
|
/* If no encryption is in force, assume this is a data packet unless */
|
|
/* we're still negotiating the encryption exchange */
|
|
/************************************************************************/
|
|
if (_SL.encrypting) {
|
|
// If encryption is in force, the encryption header tells us
|
|
// whether this is a security or data packet.
|
|
dataPacket =
|
|
(((PRNS_SECURITY_HEADER)pData)->flags & RNS_SEC_NONDATA_PKT) ?
|
|
FALSE : TRUE;
|
|
}
|
|
else {
|
|
dataPacket = (_SL.state == SL_STATE_CONNECTED);
|
|
}
|
|
|
|
// Handle the packet.
|
|
if (dataPacket) {
|
|
TRC_DBG((TB, _T("Data packet")));
|
|
hrc = SLReceivedDataPacket(pData, dataLen, flags, channelID, priority);
|
|
}
|
|
else {
|
|
// Non-data packets always have a security header, even when
|
|
// encryption is not in effect. Use this to determine what type of
|
|
// packet this is.
|
|
if (((PRNS_SECURITY_HEADER)pData)->flags & RNS_SEC_EXCHANGE_PKT) {
|
|
TRC_NRM((TB, _T("Security packet")));
|
|
SLReceivedSecPacket(pData, dataLen, flags, channelID, priority);
|
|
}
|
|
else if (((PRNS_SECURITY_HEADER)pData)->flags & RNS_SEC_LICENSE_PKT) {
|
|
#ifdef USE_LICENSE
|
|
TRC_NRM((TB, _T("Licensing packet")));
|
|
SLReceivedLicPacket(pData, dataLen, flags, channelID, priority);
|
|
#else /* USE_LICENSE */
|
|
TRC_ABORT((TB,_T("Licensing not yet implemented")));
|
|
#endif /* USE_LICENSE */
|
|
}
|
|
else {
|
|
TRC_NRM((TB, _T("Server redirection packet")));
|
|
// The redirection packet is encrypted
|
|
if (((PRNS_SECURITY_HEADER)pData)->flags & RDP_SEC_REDIRECTION_PKT3) {
|
|
rc = SLDecryptRedirectionPacket(&pData, &dataLen);
|
|
if (!rc) {
|
|
SL_DropLinkImmediate(SL_ERR_INVALIDPACKETFORMAT);
|
|
hrc = E_ABORT;
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* The packet should be at least large enough to hold the */
|
|
/* RDP_SERVER_REDIRECTION_PACKET packet, since we cast to that type */
|
|
/* below. */
|
|
/************************************************************************/
|
|
if (dataLen < sizeof(RDP_SERVER_REDIRECTION_PACKET))
|
|
{
|
|
TRC_ABORT((TB, _T("SL packet too small for RDP_SERVER_REDIRECTION_PACKET: %u"), dataLen));
|
|
|
|
//
|
|
// It is necessary to immediately abort the connection
|
|
// as we may be under attack here and we should stop processing
|
|
// any additional data
|
|
//
|
|
SL_DropLinkImmediate(SL_ERR_INVALIDPACKETFORMAT);
|
|
|
|
/************************************************************************/
|
|
/* Do not process the rest of this packet! */
|
|
/************************************************************************/
|
|
hrc = E_ABORT;
|
|
DC_QUIT;
|
|
}
|
|
|
|
_pCo->CO_OnServerRedirectionPacket(
|
|
(RDP_SERVER_REDIRECTION_PACKET UNALIGNED *)pData, dataLen);
|
|
}
|
|
}
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return(hrc);
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// SL_OnFastPathOutputReceived
|
|
//
|
|
// Special-case data reception path for fast-path output packets.
|
|
/****************************************************************************/
|
|
HRESULT DCAPI CSL::SL_OnFastPathOutputReceived(
|
|
BYTE FAR *pData,
|
|
unsigned DataLen,
|
|
BOOL bEncrypted,
|
|
BOOL fSecureChecksum)
|
|
{
|
|
HRESULT hrc = S_OK;
|
|
unsigned HeaderLen, padlen;
|
|
|
|
DC_BEGIN_FN("SL_OnFastPathOutputReceived");
|
|
|
|
if (_SL.encrypting && _SL.encryptionLevel >= 2) {
|
|
BOOL rc;
|
|
|
|
if (!bEncrypted) {
|
|
//
|
|
// It is necessary to immediately abort the connection
|
|
// as we may be under attack here and we should stop processing
|
|
// any additional data
|
|
//
|
|
TRC_ERR((TB, _T("unencrypted data received in encrypted stream")));
|
|
SL_DropLinkImmediate(SL_ERR_DECRYPTFAILED);
|
|
DC_QUIT;
|
|
}
|
|
|
|
// The encryption MAC signature is in the first 8 bytes of the
|
|
// packet after the header byte and size.
|
|
|
|
if (_SL.decryptCount == UPDATE_SESSION_KEY_COUNT) {
|
|
rc = TRUE;
|
|
// Don't need to update the session key if using FIPS
|
|
if (_SL.encryptionMethodSelected != SM_FIPS_ENCRYPTION_FLAG) {
|
|
rc = UpdateSessionKey(
|
|
_SL.startDecryptKey,
|
|
_SL.currentDecryptKey,
|
|
_SL.encryptionMethodSelected,
|
|
_SL.keyLength,
|
|
&_SL.rc4DecryptKey,
|
|
_SL.encryptionLevel);
|
|
}
|
|
if (rc) {
|
|
// Reset counter.
|
|
_SL.decryptCount = 0;
|
|
}
|
|
else {
|
|
TRC_ERR((TB, _T("SL failed to update session key")));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
TRC_ASSERT((_SL.decryptCount < UPDATE_SESSION_KEY_COUNT),
|
|
(TB, _T("Invalid decrypt count")));
|
|
|
|
if (_SL.encryptionMethodSelected == SM_FIPS_ENCRYPTION_FLAG) {
|
|
HeaderLen = sizeof(RNS_SECURITY_HEADER2) - sizeof(RNS_SECURITY_HEADER);
|
|
}
|
|
else {
|
|
HeaderLen = DATA_SIGNATURE_SIZE;
|
|
}
|
|
|
|
// There needs to be enough data at least for the data signature and header.
|
|
if (DataLen < HeaderLen)
|
|
{
|
|
TRC_ABORT((TB, _T("Not enough data in PDU for DATA_SIGNATURE_SIZE: %u"), DataLen));
|
|
|
|
//
|
|
// It is necessary to immediately abort the connection
|
|
// as we may be under attack here and we should stop processing
|
|
// any additional data
|
|
//
|
|
SL_DropLinkImmediate(SL_ERR_INVALIDPACKETFORMAT);
|
|
|
|
hrc = E_ABORT;
|
|
DC_QUIT;
|
|
}
|
|
pData += HeaderLen;
|
|
DataLen -= HeaderLen;
|
|
if (_SL.encryptionMethodSelected == SM_FIPS_ENCRYPTION_FLAG) {
|
|
padlen = *((TSUINT8 *)(pData - MAX_SIGN_SIZE - sizeof(TSUINT8)));
|
|
}
|
|
|
|
if (SL_GetEncSafeChecksumSC() != (fSecureChecksum != 0)) {
|
|
TRC_ERR((TB,_T("SC safechecksum: 0x%x mismatch protocol:0x%x"),
|
|
SL_GetEncSafeChecksumSC(),
|
|
fSecureChecksum));
|
|
}
|
|
|
|
if (_SL.encryptionMethodSelected == SM_FIPS_ENCRYPTION_FLAG) {
|
|
rc = TSCAPI_DecryptData(
|
|
&(_SL.SLCapiData),
|
|
pData,
|
|
DataLen,
|
|
padlen,
|
|
pData - MAX_SIGN_SIZE,
|
|
_SL.totalDecryptCount);
|
|
DataLen -= padlen;
|
|
}
|
|
else {
|
|
rc = DecryptData(
|
|
_SL.encryptionLevel,
|
|
_SL.currentDecryptKey,
|
|
&_SL.rc4DecryptKey,
|
|
_SL.keyLength,
|
|
pData,
|
|
DataLen,
|
|
_SL.macSaltKey,
|
|
pData - DATA_SIGNATURE_SIZE,
|
|
fSecureChecksum,
|
|
_SL.totalDecryptCount);
|
|
}
|
|
if (rc) {
|
|
// Successfully decrypted a packet, increment the decryption
|
|
// counter.
|
|
_SL.decryptCount++;
|
|
_SL.totalDecryptCount++;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// It is necessary to immediately abort the connection
|
|
// as we may be under attack here and we should stop processing
|
|
// any additional data
|
|
//
|
|
TRC_ERR((TB, _T("SL failed to decrypt data")));
|
|
SL_DropLinkImmediate(SL_ERR_DECRYPTFAILED);
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
// For TS4 server, the default is only client to server encryption
|
|
// so if encryptionlevel is 1 or less, then we accept unencrypted
|
|
// data as default.
|
|
_pCo->CO_OnFastPathOutputReceived(pData, DataLen);
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return(hrc);
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: SL_OnBufferAvailable */
|
|
/* */
|
|
/* Purpose: Called by NL when the network is ready to send again after */
|
|
/* being busy for a period */
|
|
/* */
|
|
/* Operation: Called in the Receive context */
|
|
/****************************************************************************/
|
|
void DCCALLBACK CSL::SL_OnBufferAvailable()
|
|
{
|
|
DC_BEGIN_FN("SL_OnBufferAvailable");
|
|
|
|
SL_CHECK_STATE(SL_EVENT_ON_BUFFERAVAILABLE);
|
|
|
|
// Tell the Core.
|
|
TRC_NRM((TB, _T("Tell the Core ready to send")));
|
|
_SL.callbacks.onBufferAvailable(_pCo);
|
|
|
|
// No state change.
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: SLSendSecInfoPacket */
|
|
/* */
|
|
/* Purpose: Build and send a security information packet to the Server. */
|
|
/****************************************************************************/
|
|
void DCINTERNAL CSL::SLSendSecInfoPacket()
|
|
{
|
|
RNS_INFO_PACKET InfoPkt;
|
|
PDCWCHAR pszW;
|
|
LPVOID p;
|
|
DCUINT32 flags;
|
|
UINT cb, cc;
|
|
BYTE Salt[UT_SALT_LENGTH];
|
|
|
|
DC_BEGIN_FN("SLSendSecInfoPacket");
|
|
|
|
/********************************************************************/
|
|
/* set InfoPkt contents */
|
|
/********************************************************************/
|
|
|
|
// Cached Logon information is always Unicode, even on Win16. The
|
|
// information is only obtained from the server, so it never has to
|
|
// be manipulated on the client side.
|
|
// Auto logon information does have to be displayed on the client side
|
|
// so in that case the Win16 client should send the information as
|
|
// ANSI, and a code page the server can use to convert it. Win32
|
|
// clients will always just send it Unicode
|
|
|
|
flags = 0;
|
|
|
|
// This flag indicates the client will only send encrypted
|
|
// packets to the server. Pre XP client sends unencrypted VC packets
|
|
// from client to server.
|
|
flags |= RNS_INFO_FORCE_ENCRYPTED_CS_PDU;
|
|
|
|
if (_pUi->UI_GetMouse())
|
|
flags |= RNS_INFO_MOUSE;
|
|
|
|
if (_pUi->UI_GetDisableCtrlAltDel())
|
|
flags |= RNS_INFO_DISABLECTRLALTDEL;
|
|
|
|
if (_pUi->UI_GetEnableWindowsKey())
|
|
flags |= RNS_INFO_ENABLEWINDOWSKEY;
|
|
|
|
if (_pUi->UI_GetDoubleClickDetect())
|
|
flags |= RNS_INFO_DOUBLECLICKDETECT;
|
|
|
|
if (_pUi->UI_GetAutoLogon())
|
|
flags |= RNS_INFO_AUTOLOGON;
|
|
|
|
if (_pUi->UI_GetMaximizeShell())
|
|
flags |= RNS_INFO_MAXIMIZESHELL;
|
|
|
|
if (_pClx->CLX_Loaded())
|
|
flags |= RNS_INFO_LOGONNOTIFY;
|
|
|
|
// Advertise TS5 new 64K compression handling to the server.
|
|
if (_pUi->UI_GetCompress())
|
|
flags |= RNS_INFO_COMPRESSION |
|
|
(PACKET_COMPR_TYPE_64K << RNS_INFO_COMPR_TYPE_SHIFT);
|
|
|
|
if (_pUi->UI_GetAudioRedirectionMode() == UTREG_UI_AUDIO_MODE_PLAY_ON_SERVER)
|
|
{
|
|
//Add protocol flag for audio plays on server here.
|
|
flags |= RNS_INFO_REMOTECONSOLEAUDIO;
|
|
}
|
|
|
|
{
|
|
SecureZeroMemory(&InfoPkt, sizeof(InfoPkt));
|
|
|
|
//
|
|
// In the UNICODE packet the CodePage field was unused so we
|
|
//
|
|
//
|
|
#ifndef OS_WINCE
|
|
InfoPkt.CodePage = (TSUINT32)PtrToUlong(CicSubstGetKeyboardLayout(NULL));
|
|
#else
|
|
InfoPkt.CodePage = (TSUINT32)PtrToUlong(GetKeyboardLayout(0));
|
|
#endif
|
|
TRC_NRM((TB,_T("Passing up keyboard layout in CodePage field: 0x%x"),
|
|
InfoPkt.CodePage));
|
|
InfoPkt.flags = (DCUINT32)(flags | (DCUINT32)RNS_INFO_UNICODE);
|
|
pszW = (PDCWCHAR)&InfoPkt.Domain[0];
|
|
|
|
// fill in the Unicode buffer
|
|
|
|
_pUi->UI_GetDomain((PDCUINT8)pszW, sizeof(InfoPkt.Domain));
|
|
cc = wcslen(pszW);
|
|
InfoPkt.cbDomain = (DCUINT16)(cc * sizeof(DCWCHAR));
|
|
#ifdef UNICODE
|
|
#define UNICODE_FORMAT_STRING _T("%s")
|
|
#else
|
|
#define UNICODE_FORMAT_STRING "%S"
|
|
#endif
|
|
TRC_NRM((TB, _T("Domain: ") UNICODE_FORMAT_STRING, pszW));
|
|
pszW += cc + 1;
|
|
|
|
// Be kind to old servers (see above)
|
|
if (_SL.serverVersion < RNS_DNS_USERNAME_UD_VERSION) {
|
|
cb = TS_MAX_USERNAME_LENGTH_OLD - 4;
|
|
} else {
|
|
cb = sizeof(InfoPkt.Domain);
|
|
}
|
|
|
|
if (_pUi->UI_GetUseRedirectionUserName()) {
|
|
_pUi->UI_GetRedirectionUserName((PDCUINT8)pszW, cb);
|
|
} else {
|
|
_pUi->UI_GetUserName((PDCUINT8)pszW, cb);
|
|
}
|
|
|
|
cc = wcslen(pszW);
|
|
InfoPkt.cbUserName = (DCUINT16)(cc * sizeof(DCWCHAR));
|
|
TRC_NRM((TB, _T("Username: ") UNICODE_FORMAT_STRING, pszW));
|
|
pszW += cc + 1;
|
|
|
|
//
|
|
// Only pass up the password if it was specified (i.e AutoLogon)
|
|
//
|
|
if (_pUi->UI_GetAutoLogon()) {
|
|
|
|
_pUi->UI_GetPassword((PDCUINT8)pszW, sizeof(InfoPkt.Password));
|
|
_pUi->UI_GetSalt(Salt, sizeof(Salt));
|
|
|
|
if (!EncryptDecryptLocalData50((LPBYTE)pszW, sizeof(InfoPkt.Password),
|
|
Salt, sizeof(Salt)))
|
|
{
|
|
TRC_ERR((TB, _T("Failed to decrypt Password")));
|
|
}
|
|
cc = wcslen(pszW);
|
|
InfoPkt.cbPassword = (DCUINT16)(cc * sizeof(DCWCHAR));
|
|
pszW += cc + 1;
|
|
|
|
}
|
|
else {
|
|
InfoPkt.cbPassword = 0;
|
|
//Trailing NULL is still needed even though there is nothing else
|
|
pszW += 1;
|
|
}
|
|
|
|
|
|
_pUi->UI_GetAlternateShell((PDCUINT8)pszW, sizeof(InfoPkt.AlternateShell));
|
|
cc = wcslen(pszW);
|
|
InfoPkt.cbAlternateShell = (DCUINT16)(cc * sizeof(DCWCHAR));
|
|
TRC_NRM((TB, _T("AlternateShell: ") UNICODE_FORMAT_STRING, pszW));
|
|
pszW += cc + 1;
|
|
|
|
_pUi->UI_GetWorkingDir((PDCUINT8)pszW, sizeof(InfoPkt.WorkingDir));
|
|
cc = wcslen(pszW);
|
|
InfoPkt.cbWorkingDir = (DCUINT16)(cc * sizeof(DCWCHAR));
|
|
TRC_NRM((TB, _T("WorkingDir: ") UNICODE_FORMAT_STRING, pszW));
|
|
pszW += cc + 1;
|
|
|
|
// computer address
|
|
SLGetComputerAddressW((PDCUINT8)pszW);
|
|
// cc is in characters, not in bytes. since we are storing wchar,
|
|
// we need to divide by 2.
|
|
cc = (sizeof(InfoPkt.ExtraInfo.clientAddressFamily) +
|
|
sizeof(InfoPkt.ExtraInfo.cbClientAddress) +
|
|
*((PDCUINT16_UA)((PDCUINT8)pszW + sizeof(InfoPkt.ExtraInfo.clientAddressFamily)))) / 2;
|
|
pszW += cc;
|
|
|
|
// client directory name
|
|
_pUt->UT_GetClientDirW((PDCUINT8)pszW);
|
|
// cc is in characters, not in bytes. since we are storing wchar,
|
|
// we need to divide by 2.
|
|
cc = (sizeof(InfoPkt.ExtraInfo.cbClientDir) +
|
|
*((PDCUINT16_UA)((PDCUINT8)pszW))) / 2;
|
|
pszW += cc;
|
|
|
|
//client time zone information
|
|
{
|
|
UNALIGNED RDP_TIME_ZONE_INFORMATION * prdptz =(RDP_TIME_ZONE_INFORMATION *)pszW;
|
|
|
|
//for win32 get real time zone information
|
|
TIME_ZONE_INFORMATION tzi;
|
|
|
|
GetTimeZoneInformation(&tzi);
|
|
|
|
prdptz->Bias = tzi.Bias;
|
|
prdptz->StandardBias = tzi.StandardBias;
|
|
prdptz->DaylightBias = tzi.DaylightBias;
|
|
memcpy(&prdptz->StandardName,&tzi.StandardName,sizeof(prdptz->StandardName));
|
|
memcpy(&prdptz->DaylightName,&tzi.DaylightName,sizeof(prdptz->DaylightName));
|
|
|
|
prdptz->StandardDate.wYear = tzi.StandardDate.wYear ;
|
|
prdptz->StandardDate.wMonth = tzi.StandardDate.wMonth ;
|
|
prdptz->StandardDate.wDayOfWeek = tzi.StandardDate.wDayOfWeek ;
|
|
prdptz->StandardDate.wDay = tzi.StandardDate.wDay ;
|
|
prdptz->StandardDate.wHour = tzi.StandardDate.wHour ;
|
|
prdptz->StandardDate.wMinute = tzi.StandardDate.wMinute ;
|
|
prdptz->StandardDate.wSecond = tzi.StandardDate.wSecond ;
|
|
prdptz->StandardDate.wMilliseconds = tzi.StandardDate.wMilliseconds;
|
|
|
|
prdptz->DaylightDate.wYear = tzi.DaylightDate.wYear ;
|
|
prdptz->DaylightDate.wMonth = tzi.DaylightDate.wMonth ;
|
|
prdptz->DaylightDate.wDayOfWeek = tzi.DaylightDate.wDayOfWeek ;
|
|
prdptz->DaylightDate.wDay = tzi.DaylightDate.wDay ;
|
|
prdptz->DaylightDate.wHour = tzi.DaylightDate.wHour ;
|
|
prdptz->DaylightDate.wMinute = tzi.DaylightDate.wMinute ;
|
|
prdptz->DaylightDate.wSecond = tzi.DaylightDate.wSecond ;
|
|
prdptz->DaylightDate.wMilliseconds = tzi.DaylightDate.wMilliseconds;
|
|
|
|
// divide by 2 !!!
|
|
pszW += sizeof(RDP_TIME_ZONE_INFORMATION) / 2;
|
|
}
|
|
|
|
// get the sessionid on which we're running
|
|
_pUi->UI_GetLocalSessionId((PDCUINT32)pszW);
|
|
// cc is in characters, not in bytes. since we are storing wchar,
|
|
// we need to divide by 2.
|
|
cc = (sizeof(InfoPkt.ExtraInfo.clientSessionId)) / 2;
|
|
pszW += cc;
|
|
|
|
//
|
|
// Send up the features to disable list.
|
|
//
|
|
DWORD dwPerformanceFlags = _pUi->UI_GetPerformanceFlags();
|
|
memcpy(pszW, &dwPerformanceFlags, sizeof(DWORD));
|
|
cc = sizeof(DWORD)/sizeof(WCHAR);
|
|
pszW += cc;
|
|
|
|
//
|
|
// Potentially send the autoreconnect packet
|
|
//
|
|
BOOL fAddedAutoReconnectInfo = FALSE;
|
|
if (_pUi->UI_GetEnableAutoReconnect()) {
|
|
DCUINT16 cbAutoReconnectLen =
|
|
(DCUINT16)_pUi->UI_GetAutoReconnectCookieLen();
|
|
PBYTE pAutoReconnectCookie = _pUi->UI_GetAutoReconnectCookie();
|
|
|
|
TRC_ASSERT(cbAutoReconnectLen <= TS_MAX_AUTORECONNECT_LEN,
|
|
(TB,_T("Reconnect packet len too big: %d"),
|
|
cbAutoReconnectLen));
|
|
|
|
if (cbAutoReconnectLen && pAutoReconnectCookie)
|
|
{
|
|
PARC_SC_PRIVATE_PACKET pArcSCPkt;
|
|
ARC_CS_PRIVATE_PACKET ArcCSPkt;
|
|
pArcSCPkt = (PARC_SC_PRIVATE_PACKET)pAutoReconnectCookie;
|
|
char hmacVerifier[TS_ARC_VERIFIER_LEN];
|
|
|
|
TRC_ASSERT(sizeof(hmacVerifier) ==
|
|
sizeof(ArcCSPkt.SecurityVerifier),
|
|
(TB,_T("HMAC verifier size doesn't match pkt format")));
|
|
|
|
|
|
memset(&hmacVerifier, 0, sizeof(hmacVerifier));
|
|
memset(&ArcCSPkt, 0, sizeof(ArcCSPkt));
|
|
|
|
#ifdef INSTRUMENT_ARC
|
|
LPDWORD pdwArcBits = (LPDWORD)pArcSCPkt->ArcRandomBits;
|
|
KdPrint(("ARC-Client:Sending arc for LID:%d"
|
|
"- ARC: 0x%x,0x%x,0x%x,0x%x\n",
|
|
ArcCSPkt.LogonId,
|
|
pdwArcBits[0],pdwArcBits[1],
|
|
pdwArcBits[2],pdwArcBits[3]));
|
|
#endif
|
|
|
|
|
|
if (SLComputeHMACVerifier(pArcSCPkt->ArcRandomBits,
|
|
sizeof(pArcSCPkt->ArcRandomBits),
|
|
_SL.keyPair.clientRandom,
|
|
RANDOM_KEY_LENGTH,
|
|
(PBYTE)&hmacVerifier,
|
|
sizeof(hmacVerifier))) {
|
|
|
|
ArcCSPkt.cbLen = sizeof(ArcCSPkt);
|
|
ArcCSPkt.LogonId = pArcSCPkt->LogonId;
|
|
ArcCSPkt.Version = 1;
|
|
memcpy(ArcCSPkt.SecurityVerifier,
|
|
hmacVerifier,
|
|
sizeof(hmacVerifier));
|
|
|
|
#ifdef INSTRUMENT_ARC
|
|
LPDWORD pdwHMACbits = (LPDWORD)hmacVerifier;
|
|
KdPrint(("ARC-Client:Sending HMAC for SID:%d -"
|
|
"HMAC: 0x%x,0x%x,0x%x,0x%x\n",
|
|
ArcCSPkt.LogonId,
|
|
pdwHMACbits[0],pdwHMACbits[1],
|
|
pdwHMACbits[2],pdwHMACbits[3]));
|
|
#endif
|
|
|
|
//
|
|
// Send the HMAC verifier in the ARC C->S packet
|
|
//
|
|
DCUINT16 cbArcCSPkt = sizeof(ArcCSPkt);
|
|
memset(pszW, 0, TS_MAX_AUTORECONNECT_LEN);
|
|
memcpy(pszW, &cbArcCSPkt, sizeof(DCUINT16));
|
|
pszW += sizeof(DCUINT16) / 2;
|
|
memcpy(pszW, &ArcCSPkt, cbArcCSPkt);
|
|
pszW += cbArcCSPkt / 2;
|
|
|
|
//
|
|
// For security clear the data
|
|
//
|
|
memset(&hmacVerifier, 0, sizeof(hmacVerifier));
|
|
memset(&ArcCSPkt, 0, sizeof(ArcCSPkt));
|
|
|
|
fAddedAutoReconnectInfo = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
#ifdef INSTRUMENT_ARC
|
|
KdPrint(("ARC-Client: Not sending any autoreconnect info\n"));
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// If we're not going to add ARC info just add a zero length
|
|
// and update the pointer
|
|
//
|
|
if (!fAddedAutoReconnectInfo) {
|
|
DCUINT16 cbZeroLen = 0;
|
|
// 0 length autoreconnect info
|
|
memcpy(pszW, &cbZeroLen, sizeof(DCUINT16));
|
|
pszW += sizeof(DCUINT16) / 2;
|
|
}
|
|
|
|
// Set up the pointer and size for the decouple call
|
|
p = &InfoPkt;
|
|
cb = (UINT) (((BYTE*)pszW) - ((BYTE *)p));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Decouple to the send context before sending the security packet */
|
|
/************************************************************************/
|
|
_pCd->CD_DecoupleNotification(CD_SND_COMPONENT,
|
|
this,
|
|
CD_NOTIFICATION_FUNC(CSL,SL_SendSecInfoPacket),
|
|
p,
|
|
cb);
|
|
|
|
// Erase the clear text password from stack variable
|
|
SecureZeroMemory(&InfoPkt, sizeof(InfoPkt));
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: SLSendSecurityPacket */
|
|
/* */
|
|
/* Purpose: 1. Build and send a security packet to the Server. */
|
|
/* 2. Build and send security info packet also. */
|
|
/* 3. call the Core's OnConnected callback. */
|
|
/* 3. move the state to SL_STATE_LICENSING. */
|
|
/* */
|
|
/* Returns: TRUE - if all of the above setps are completed successfully. */
|
|
/* FALSE - otherwise. */
|
|
/* */
|
|
/* Operation: Called in the Receive context */
|
|
/****************************************************************************/
|
|
DCBOOL DCINTERNAL CSL::SLSendSecurityPacket(PDCUINT8 serverPublicKey,
|
|
DCUINT32 serverPublicKeyLen)
|
|
{
|
|
DCBOOL rc = FALSE;
|
|
DCUINT32 secPktLength;
|
|
PRNS_SECURITY_PACKET pSecPkt = NULL;
|
|
|
|
DC_BEGIN_FN("SLSendSecurityPacket");
|
|
|
|
/************************************************************************/
|
|
/* send a security packet if we are encrypting. */
|
|
/************************************************************************/
|
|
if (_SL.encrypting) {
|
|
/********************************************************************/
|
|
/* ALLOCATE a maximum possible encrypted data size buffer on the */
|
|
/* stack. */
|
|
/********************************************************************/
|
|
DCUINT8 encClientRandom[512];
|
|
DCUINT32 encClientRandomLen;
|
|
|
|
encClientRandomLen = sizeof(encClientRandom);
|
|
if( !EncryptClientRandom(
|
|
serverPublicKey,
|
|
serverPublicKeyLen,
|
|
(PDCUINT8)&_SL.keyPair.clientRandom,
|
|
sizeof(_SL.keyPair.clientRandom),
|
|
(PDCUINT8)&encClientRandom,
|
|
&encClientRandomLen) ) {
|
|
TRC_ERR((TB, _T("Failed to encrypt client random") ));
|
|
|
|
|
|
_pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT,
|
|
this,
|
|
CD_NOTIFICATION_FUNC(CSL,SLSetReasonAndDisconnect),
|
|
SL_ERR_ENCCLNTRANDFAILED);
|
|
|
|
DC_QUIT;
|
|
}
|
|
|
|
TRC_ASSERT((encClientRandomLen <= sizeof(encClientRandom) ),
|
|
(TB, _T("Invalid encClientRandomLen")));
|
|
|
|
/********************************************************************/
|
|
/* Allocate space for security exchange packet */
|
|
/********************************************************************/
|
|
secPktLength = (DCUINT)
|
|
(sizeof(RNS_SECURITY_PACKET) + encClientRandomLen);
|
|
|
|
pSecPkt = (PRNS_SECURITY_PACKET)UT_Malloc( _pUt, (DCUINT)secPktLength);
|
|
if (pSecPkt == NULL)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to allocate %u bytes for security packet"),
|
|
secPktLength));
|
|
|
|
_pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT,
|
|
this,
|
|
CD_NOTIFICATION_FUNC(CSL,SLSetReasonAndDisconnect),
|
|
SL_ERR_NOMEMFORSECPACKET);
|
|
|
|
DC_QUIT;
|
|
}
|
|
|
|
/*********************************************************************/
|
|
/* Build security packet */
|
|
/* SECURITY: We tell the server that we know how to accept an */
|
|
/* encrypted licensing-data packet. */
|
|
/*********************************************************************/
|
|
TRC_NRM((TB, _T("Build security packet")));
|
|
pSecPkt->flags = RNS_SEC_EXCHANGE_PKT | RDP_SEC_LICENSE_ENCRYPT_SC;
|
|
pSecPkt->length = encClientRandomLen;
|
|
|
|
TRC_NRM((TB, _T("Copy %lu bytes of client security info"),
|
|
sizeof(encClientRandom)));
|
|
|
|
DC_MEMCPY(
|
|
(PDCVOID)(pSecPkt + 1),
|
|
(PDCVOID)encClientRandom,
|
|
(DCUINT)encClientRandomLen);
|
|
|
|
/********************************************************************/
|
|
/* Decouple to the send context before sending the security packet */
|
|
/********************************************************************/
|
|
_pCd->CD_DecoupleNotification(CD_SND_COMPONENT,
|
|
this,
|
|
CD_NOTIFICATION_FUNC(CSL,SL_SendSecurityPacket),
|
|
pSecPkt,
|
|
(DCUINT)secPktLength);
|
|
|
|
/********************************************************************/
|
|
/* we finished sending the security packet. Send Info packet now. */
|
|
/********************************************************************/
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* move the state to connected state. */
|
|
/************************************************************************/
|
|
_pUi->UI_SetChannelID(_SL.channelID);
|
|
SLSendSecInfoPacket();
|
|
|
|
/************************************************************************/
|
|
/* We've finished - tell the core */
|
|
/************************************************************************/
|
|
TRC_NRM((TB, _T("Security exchange complete")));
|
|
|
|
#ifdef USE_LICENSE
|
|
/************************************************************************/
|
|
/* Finished security exchange - wait for licensing */
|
|
/************************************************************************/
|
|
SL_SET_STATE(SL_STATE_LICENSING);
|
|
|
|
//
|
|
// Decouple to the UI thread, it will take care of
|
|
// stopping the connection timers and starting the
|
|
// licensing timer (in a thread safe way).
|
|
//
|
|
_pCd->CD_DecoupleSimpleNotification(CD_UI_COMPONENT,
|
|
_pUi,
|
|
CD_NOTIFICATION_FUNC(CUI,
|
|
UI_OnSecurityExchangeComplete),
|
|
NULL);
|
|
|
|
if(LICENSE_OK!= _pLic->CLicenseInit(&_SL.hLicenseHandle))
|
|
{
|
|
TRC_ERR((TB, _T("Failed to init License Manager")));
|
|
DC_QUIT;
|
|
}
|
|
#else /* USE_LICENSE */
|
|
/************************************************************************/
|
|
/* We've finished - tell the core */
|
|
/************************************************************************/
|
|
SL_SET_STATE(SL_STATE_CONNECTED);
|
|
|
|
_pCd->CD_DecoupleSimpleNotification(CD_UI_COMPONENT,
|
|
_pUi,
|
|
CD_NOTIFICATION_FUNC(CUI,
|
|
UI_OnSecurityExchangeComplete),
|
|
NULL);
|
|
|
|
_SL.callbacks.onConnected(_pCo, _SL.channelID,
|
|
_SL.pSCUserData,
|
|
_SL.SCUserDataLength,
|
|
_SL.serverVersion);
|
|
#endif /* USE_LICENSE */
|
|
|
|
/************************************************************************/
|
|
/* Hurrah! Everything has worked */
|
|
/************************************************************************/
|
|
rc = TRUE;
|
|
|
|
DC_EXIT_POINT:
|
|
|
|
/************************************************************************/
|
|
/* Free the security packet (if allocated) */
|
|
/************************************************************************/
|
|
if (pSecPkt != NULL)
|
|
{
|
|
TRC_NRM((TB, _T("Free the security packet")));
|
|
UT_Free( _pUt, pSecPkt);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Return to caller */
|
|
/************************************************************************/
|
|
DC_END_FN();
|
|
return(rc);
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: SLReceivedDataPacket */
|
|
/* */
|
|
/* Purpose: Handle incoming data packets */
|
|
/* */
|
|
/* Operation: Called in the Receive context */
|
|
/****************************************************************************/
|
|
HRESULT DCINTERNAL CSL::SLReceivedDataPacket(PDCUINT8 pData,
|
|
DCUINT dataLen,
|
|
DCUINT flags,
|
|
DCUINT channelID,
|
|
DCUINT priority)
|
|
{
|
|
HRESULT hrc = S_OK;
|
|
PRNS_SECURITY_HEADER pSecHdr;
|
|
PDCUINT8 pCoreData;
|
|
DCUINT coreDataLen;
|
|
#ifdef DC_LOOPBACK
|
|
PDCUINT8 pString;
|
|
DCUINT8 lbRetString[SL_LB_RETURN_STRING_SIZE] =
|
|
SL_LB_RETURN_STRING;
|
|
#endif
|
|
|
|
DC_BEGIN_FN("SLReceivedDataPacket");
|
|
|
|
SL_CHECK_STATE(SL_EVENT_ON_RECEIVED_DATA_PACKET);
|
|
|
|
/************************************************************************/
|
|
/* Decrypt the packet if necessary */
|
|
/************************************************************************/
|
|
if (_SL.encrypting)
|
|
{
|
|
/************************************************************************/
|
|
/* There needs to be at least enough data for RNS_SECURITY_HEADER */
|
|
/************************************************************************/
|
|
if (dataLen < sizeof(RNS_SECURITY_HEADER))
|
|
{
|
|
TRC_ABORT((TB, _T("No RNS_SECURITY_HEADER in encrypted packet (size=%u)"), dataLen));
|
|
|
|
//
|
|
// It is necessary to immediately abort the connection
|
|
// as we may be under attack here and we should stop processing
|
|
// any additional data
|
|
//
|
|
SL_DropLinkImmediate(SL_ERR_DECRYPTFAILED);
|
|
|
|
hrc = E_ABORT;
|
|
DC_QUIT;
|
|
}
|
|
|
|
pSecHdr = (PRNS_SECURITY_HEADER)pData;
|
|
if (pSecHdr->flags & RNS_SEC_ENCRYPT)
|
|
{
|
|
if (!SL_DecryptHelper(pData, &dataLen))
|
|
{
|
|
TRC_ERR((TB, _T("SL failed to decompress data")));
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (_SL.encryptionMethodSelected == SM_FIPS_ENCRYPTION_FLAG) {
|
|
coreDataLen = dataLen - sizeof(RNS_SECURITY_HEADER2);
|
|
pCoreData = (PDCUINT8)pSecHdr + sizeof(RNS_SECURITY_HEADER2);
|
|
}
|
|
else {
|
|
coreDataLen = dataLen - sizeof(RNS_SECURITY_HEADER1);
|
|
pCoreData = (PDCUINT8)pSecHdr + sizeof(RNS_SECURITY_HEADER1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/****************************************************************/
|
|
/* This packet not encrypted, although encryption is supported */
|
|
/****************************************************************/
|
|
if (_SL.encryptionLevel <= 1) {
|
|
// For TS4 server, the default is only client to server encryption
|
|
// so if encryptionlevel is 1 or less, then we accept unencrypted
|
|
// data as default.
|
|
coreDataLen = dataLen - sizeof(RNS_SECURITY_HEADER);
|
|
pCoreData = (PDCUINT8)(pSecHdr) + sizeof(RNS_SECURITY_HEADER);
|
|
TRC_DBG((TB, _T("Unencrypted packet at %p (%u)"),
|
|
pCoreData, coreDataLen));
|
|
}
|
|
else {
|
|
|
|
//
|
|
// It is necessary to immediately abort the connection
|
|
// as we may be under attack here and we should stop processing
|
|
// any additional data
|
|
//
|
|
TRC_ERR((TB, _T("unencrypted data received in encrypted stream")));
|
|
SL_DropLinkImmediate(SL_ERR_DECRYPTFAILED);
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* Decryption OK or packet not encrypted - set up stuff to pass to */
|
|
/* Core */
|
|
/********************************************************************/
|
|
flags = (DCUINT)pSecHdr->flags;
|
|
}
|
|
else
|
|
{
|
|
/********************************************************************/
|
|
/* Not encrypting - set up data pointer and length */
|
|
/********************************************************************/
|
|
pCoreData = pData;
|
|
coreDataLen = dataLen;
|
|
flags &= ~RNS_SEC_ENCRYPT;
|
|
TRC_DBG((TB, _T("Never-encrypted packet at %p (%u)"),
|
|
pCoreData, coreDataLen));
|
|
}
|
|
|
|
{
|
|
if (channelID == _SL.channelID)
|
|
{
|
|
/****************************************************************/
|
|
/* Pass the packet to the Core */
|
|
/****************************************************************/
|
|
TRC_NRM((TB, _T("Packet received on Share channel %x - pass to CO"),
|
|
channelID));
|
|
_SL.callbacks.onPacketReceived(_pCo, pCoreData,
|
|
coreDataLen,
|
|
flags,
|
|
channelID,
|
|
priority);
|
|
}
|
|
else
|
|
{
|
|
/****************************************************************/
|
|
/* Pass packet to virtual channel handler */
|
|
/****************************************************************/
|
|
TRC_NRM((TB, _T("Packet received on channel %x"),
|
|
channelID));
|
|
|
|
_pChan->ChannelOnPacketReceived(pCoreData,
|
|
coreDataLen,
|
|
flags,
|
|
channelID,
|
|
priority);
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* No state change */
|
|
/************************************************************************/
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return(hrc);
|
|
} /* SLReceivedDataPacket */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: SLDecryptRedirectionPacket */
|
|
/* */
|
|
/* Purpose: Decrypt Redirection packet from server */
|
|
/****************************************************************************/
|
|
DCBOOL DCINTERNAL CSL::SLDecryptRedirectionPacket(PDCUINT8 *ppData,
|
|
DCUINT *pdataLen)
|
|
{
|
|
// *ppData returns the decrypted data
|
|
PRNS_SECURITY_HEADER1 pSecHdr;
|
|
PRNS_SECURITY_HEADER2 pSecHdr2;
|
|
PDCUINT8 pCoreData;
|
|
DCUINT coreDataLen;
|
|
BOOL rc = FALSE;
|
|
|
|
DC_BEGIN_FN("SLDecryptRedirectionPacket");
|
|
|
|
SL_CHECK_STATE(SL_EVENT_ON_RECEIVED_DATA_PACKET);
|
|
|
|
/************************************************************************/
|
|
/* Decrypt the packet if necessary */
|
|
/************************************************************************/
|
|
if (_SL.encrypting) {
|
|
pSecHdr = (PRNS_SECURITY_HEADER1)*ppData;
|
|
|
|
if (_SL.encryptionMethodSelected == SM_FIPS_ENCRYPTION_FLAG) {
|
|
// *pdataLen must be larger than sizeof(RNS_SECURITY_HEADER2)
|
|
if (*pdataLen <= sizeof(RNS_SECURITY_HEADER2)) {
|
|
SL_DropLinkImmediate(SL_ERR_DECRYPTFAILED);
|
|
TRC_ERR((TB, _T("SL security header not large enough")));
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
pSecHdr2 = (PRNS_SECURITY_HEADER2)*ppData;
|
|
coreDataLen = *pdataLen - sizeof(RNS_SECURITY_HEADER2);
|
|
pCoreData = (PDCUINT8)pSecHdr2 + sizeof(RNS_SECURITY_HEADER2);
|
|
|
|
TRC_DBG((TB, _T("Encrypted packet at %p (%u), sign %p (%u)"),
|
|
pCoreData, coreDataLen, pSecHdr2,
|
|
sizeof(RNS_SECURITY_HEADER2)));
|
|
}
|
|
else {
|
|
// Bug 679216 - must check there is enough data before reading
|
|
// *pdataLen must be larger than sizeof(RNS_SECURITY_HEADER1)
|
|
if (*pdataLen <= sizeof(RNS_SECURITY_HEADER1)) {
|
|
SL_DropLinkImmediate(SL_ERR_DECRYPTFAILED);
|
|
TRC_ERR((TB, _T("SL security header not large enough")));
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
pSecHdr = (PRNS_SECURITY_HEADER1)*ppData;
|
|
coreDataLen = *pdataLen - sizeof(RNS_SECURITY_HEADER1);
|
|
pCoreData = (PDCUINT8)pSecHdr + sizeof(RNS_SECURITY_HEADER1);
|
|
|
|
TRC_DBG((TB, _T("Encrypted packet at %p (%u), sign %p (%u)"),
|
|
pCoreData, coreDataLen, pSecHdr,
|
|
sizeof(RNS_SECURITY_HEADER1)));
|
|
}
|
|
|
|
TRC_DATA_DBG("Data buffer before decryption", pCoreData, coreDataLen);
|
|
|
|
TRC_NRM((TB, _T("Update Decrypt Session Key Count , %d"),
|
|
_SL.decryptCount));
|
|
|
|
/****************************************************************/
|
|
/* Decrypt the packet */
|
|
/****************************************************************/
|
|
if( _SL.decryptCount == UPDATE_SESSION_KEY_COUNT ) {
|
|
rc = TRUE;
|
|
// Don't need to update the session key if using FIPS
|
|
if (_SL.encryptionMethodSelected != SM_FIPS_ENCRYPTION_FLAG) {
|
|
rc = UpdateSessionKey(
|
|
_SL.startDecryptKey,
|
|
_SL.currentDecryptKey,
|
|
_SL.encryptionMethodSelected,
|
|
_SL.keyLength,
|
|
&_SL.rc4DecryptKey,
|
|
_SL.encryptionLevel );
|
|
}
|
|
if( !rc ) {
|
|
TRC_ERR((TB, _T("SL failed to update session key")));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************/
|
|
/* reset counter. */
|
|
/************************************************************/
|
|
_SL.decryptCount = 0;
|
|
}
|
|
|
|
if (SL_GetEncSafeChecksumSC() !=
|
|
((pSecHdr->flags & RDP_SEC_SECURE_CHECKSUM) != 0)) {
|
|
TRC_ERR((TB,_T("SC safechecksum: 0x%x mismatch protocol:0x%x"),
|
|
SL_GetEncSafeChecksumSC(),
|
|
(pSecHdr->flags & RDP_SEC_SECURE_CHECKSUM)));
|
|
}
|
|
|
|
if (_SL.encryptionMethodSelected == SM_FIPS_ENCRYPTION_FLAG) {
|
|
rc = TSCAPI_DecryptData(
|
|
&(_SL.SLCapiData),
|
|
pCoreData,
|
|
coreDataLen,
|
|
pSecHdr2->padlen,
|
|
(PDCUINT8)pSecHdr2->dataSignature,
|
|
_SL.totalDecryptCount);
|
|
*pdataLen -= pSecHdr2->padlen;
|
|
}
|
|
else {
|
|
rc = DecryptData(
|
|
_SL.encryptionLevel,
|
|
_SL.currentDecryptKey,
|
|
&_SL.rc4DecryptKey,
|
|
_SL.keyLength,
|
|
pCoreData,
|
|
coreDataLen,
|
|
_SL.macSaltKey,
|
|
(PDCUINT8)((PRNS_SECURITY_HEADER1)pSecHdr)->dataSignature,
|
|
(pSecHdr->flags & RDP_SEC_SECURE_CHECKSUM),
|
|
_SL.totalDecryptCount
|
|
);
|
|
}
|
|
*ppData = pCoreData;
|
|
if( !rc ) {
|
|
//
|
|
// It is necessary to immediately abort the connection
|
|
// as we may be under attack here and we should stop processing
|
|
// any additional data
|
|
//
|
|
SL_DropLinkImmediate(SL_ERR_DECRYPTFAILED);
|
|
TRC_ERR((TB, _T("SL failed to decrypt data")));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/****************************************************************/
|
|
/* successfully decrypted a packet, increment the decrption */
|
|
/* counter. */
|
|
/****************************************************************/
|
|
_SL.decryptCount++;
|
|
_SL.totalDecryptCount++;
|
|
|
|
TRC_DBG((TB, _T("Data decrypted")));
|
|
TRC_DATA_DBG("Data buffer after decryption", pCoreData, coreDataLen);
|
|
}
|
|
else {
|
|
TRC_ABORT((TB,_T("Should not get here unless decrypt state is wrong")));
|
|
// Should add disconnect here later
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
return rc;
|
|
DC_END_FN();
|
|
} /* SLDecryptRedirectionPacket */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: SLReceivedSecPacket */
|
|
/* */
|
|
/* Purpose: Handle incoming security packet */
|
|
/* */
|
|
/* Operation: Called in the Receive context */
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CSL::SLReceivedSecPacket(PDCUINT8 pData,
|
|
DCUINT dataLen,
|
|
DCUINT flags,
|
|
DCUINT channelID,
|
|
DCUINT priority)
|
|
{
|
|
BOOL bAssert;
|
|
|
|
DC_BEGIN_FN("SLReceivedSecPacket");
|
|
|
|
SL_CHECK_STATE(SL_EVENT_ON_RECEIVED_SEC_PACKET);
|
|
|
|
DC_IGNORE_PARAMETER(pData);
|
|
DC_IGNORE_PARAMETER(dataLen);
|
|
DC_IGNORE_PARAMETER(flags);
|
|
DC_IGNORE_PARAMETER(channelID);
|
|
DC_IGNORE_PARAMETER(priority);
|
|
|
|
bAssert = FALSE;
|
|
TRC_ASSERT((bAssert),
|
|
(TB, _T("SLReceivedSecPacket - ")
|
|
_T("WE DON'T EXPECT SECURITY PACKET FROM SERVER")));
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
} /* SLReceivedSecPacket */
|
|
|
|
/****************************************************************************/
|
|
/* Name: SLInitSecurity */
|
|
/* */
|
|
/* Purpose: Initialize security interface */
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CSL::SLInitSecurity(DCVOID)
|
|
{
|
|
DCBOOL intRC = FALSE;
|
|
DWORD FipsPolicy = 0;
|
|
DCBOOL rc = FALSE;
|
|
HKEY hKey;
|
|
DWORD KeyType, cbSize;
|
|
|
|
DC_BEGIN_FN("SLInitSecurity");
|
|
|
|
_SL.encryptionEnabled = TRUE;
|
|
|
|
// Read GP Fips setting
|
|
rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
TS_FIPS_POLICY,
|
|
0,
|
|
KEY_READ,
|
|
&hKey);
|
|
|
|
if (ERROR_SUCCESS == rc) {
|
|
cbSize = sizeof(FipsPolicy);
|
|
rc = RegQueryValueEx(hKey,
|
|
FIPS_ALGORITH_POLICY,
|
|
0,
|
|
&KeyType,
|
|
(LPBYTE)&FipsPolicy,
|
|
&cbSize);
|
|
if (ERROR_SUCCESS != rc) {
|
|
FipsPolicy = 0;
|
|
}
|
|
RegCloseKey(hKey);
|
|
}
|
|
TRC_ERR((TB, _T("GP setting for FIPS is %d"), FipsPolicy));
|
|
|
|
// If GP FIPS policy is enabled, only do FIPS
|
|
if (FipsPolicy == 1) {
|
|
_SL.encryptionMethodsSupported = SM_FIPS_ENCRYPTION_FLAG;
|
|
_pUi->UI_SetfUseFIPS(TRUE);
|
|
}
|
|
else {
|
|
_SL.encryptionMethodsSupported =
|
|
SM_40BIT_ENCRYPTION_FLAG |
|
|
SM_56BIT_ENCRYPTION_FLAG |
|
|
SM_128BIT_ENCRYPTION_FLAG |
|
|
SM_FIPS_ENCRYPTION_FLAG;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* The server certificate and public key. */
|
|
/************************************************************************/
|
|
_SL.pbCertificate = NULL;
|
|
_SL.cbCertificate = 0;
|
|
_SL.pServerCert = NULL;
|
|
_SL.pbServerPubKey = NULL;
|
|
_SL.cbServerPubKey = 0;
|
|
|
|
_SL.SLCapiData.hDecKey = NULL;
|
|
_SL.SLCapiData.hEncKey = NULL;
|
|
_SL.SLCapiData.hProv = NULL;
|
|
_SL.SLCapiData.hSignKey = NULL;
|
|
|
|
/************************************************************************/
|
|
/* Initialization complete */
|
|
/************************************************************************/
|
|
intRC = TRUE;
|
|
|
|
DC_EXIT_POINT:
|
|
/************************************************************************/
|
|
/* If anything failed, release resources */
|
|
/************************************************************************/
|
|
if (!intRC)
|
|
{
|
|
TRC_NRM((TB, _T("Clean up")));
|
|
SLFreeInitResources();
|
|
}
|
|
|
|
DC_END_FN();
|
|
} /* SLInitSecurity */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: SLInitCSUserData */
|
|
/* */
|
|
/* Purpose: Initialize Core to Server security user data */
|
|
/* */
|
|
/* Operation: Called during initialization, after security API has been */
|
|
/* initialized successfully, to build the user data which is */
|
|
/* passed to NL_Connect(). */
|
|
/****************************************************************************/
|
|
DCVOID DCAPI CSL::SLInitCSUserData(DCVOID)
|
|
{
|
|
DC_BEGIN_FN("SLInitCSUserData");
|
|
|
|
/************************************************************************/
|
|
/* Calculate the size of user data required (& trace the packages). */
|
|
/* Initial size is "size of RNS_UD_CS_SEC" */
|
|
/************************************************************************/
|
|
_SL.CSUserDataLength = sizeof(RNS_UD_CS_SEC);
|
|
|
|
/************************************************************************/
|
|
/* Allocate space for all user data */
|
|
/************************************************************************/
|
|
_SL.pCSUserData = (PDCUINT8)UT_Malloc( _pUt, _SL.CSUserDataLength);
|
|
if (_SL.pCSUserData == NULL)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to alloc %u bytes for user data"),
|
|
_SL.CSUserDataLength));
|
|
_pUi->UI_FatalError(DC_ERR_OUTOFMEMORY);
|
|
TRC_ABORT((TB,_T("returned from UI_FatalError")));
|
|
}
|
|
TRC_NRM((TB, _T("Allocated %u bytes for user data"), _SL.CSUserDataLength));
|
|
|
|
/************************************************************************/
|
|
/* Build security user data */
|
|
/************************************************************************/
|
|
TRC_NRM((TB, _T("Build security user data")));
|
|
|
|
((PRNS_UD_CS_SEC)_SL.pCSUserData)->header.type = RNS_UD_CS_SEC_ID;
|
|
((PRNS_UD_CS_SEC)_SL.pCSUserData)->header.length =
|
|
(DCUINT16)_SL.CSUserDataLength;
|
|
((PRNS_UD_CS_SEC)_SL.pCSUserData)->encryptionMethods =
|
|
_SL.encryptionMethodsSupported;
|
|
((PRNS_UD_CS_SEC)_SL.pCSUserData)->extEncryptionMethods = 0;
|
|
|
|
|
|
//
|
|
// for backward compatibility, we need to set the encryptionMethods field to
|
|
// zero for Frnech Locale system. However set the required encryption level
|
|
// in the new field extEncryptionMethods.
|
|
//
|
|
|
|
if( FindIsFrenchSystem() ) {
|
|
((PRNS_UD_CS_SEC)_SL.pCSUserData)->encryptionMethods = 0;
|
|
((PRNS_UD_CS_SEC)_SL.pCSUserData)->extEncryptionMethods =
|
|
_SL.encryptionMethodsSupported;
|
|
}
|
|
|
|
TRC_DATA_NRM("Built user data", _SL.pCSUserData, _SL.CSUserDataLength);
|
|
|
|
DC_EXIT_POINT:
|
|
|
|
/************************************************************************/
|
|
/* Return to caller */
|
|
/************************************************************************/
|
|
DC_END_FN();
|
|
} /* SLInitCSUserData */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: SLFreeConnectResources */
|
|
/* */
|
|
/* Purpose: Release resources acquired during connection processing */
|
|
/* */
|
|
/* Operation: Called by both SL_OnDisconnected and SL_OnTerminating to free */
|
|
/* resources */
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CSL::SLFreeConnectResources(DCVOID)
|
|
{
|
|
DC_BEGIN_FN("SLFreeConnectResources");
|
|
|
|
/************************************************************************/
|
|
/* Free SC user data if any */
|
|
/************************************************************************/
|
|
if (_SL.pSCUserData != NULL)
|
|
{
|
|
TRC_NRM((TB, _T("Free user data")));
|
|
UT_Free( _pUt, _SL.pSCUserData);
|
|
_SL.pSCUserData = NULL;
|
|
_SL.SCUserDataLength = 0;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Free the server certificate and public key */
|
|
/************************************************************************/
|
|
if( _SL.pServerCert )
|
|
{
|
|
UT_Free( _pUt, _SL.pServerCert );
|
|
_SL.pServerCert = NULL;
|
|
}
|
|
if( _SL.pbCertificate )
|
|
{
|
|
UT_Free( _pUt, _SL.pbCertificate );
|
|
_SL.pbCertificate = NULL;
|
|
_SL.cbCertificate = 0;
|
|
}
|
|
if( _SL.pbServerPubKey )
|
|
{
|
|
UT_Free( _pUt, _SL.pbServerPubKey );
|
|
_SL.pbServerPubKey = NULL;
|
|
_SL.cbServerPubKey = 0;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Clear global data */
|
|
/************************************************************************/
|
|
_SL.decryptFailed = FALSE;
|
|
|
|
DC_END_FN();
|
|
} /* SLFreeConnectResources */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: SLFreeInitResources */
|
|
/* */
|
|
/* Purpose: Release resources acquired during initialization */
|
|
/* */
|
|
/* Operation: Called by SLTerminating to free resources */
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CSL::SLFreeInitResources(DCVOID)
|
|
{
|
|
DC_BEGIN_FN("SLFreeInitResources");
|
|
|
|
/************************************************************************/
|
|
/* Free CS user data if any */
|
|
/************************************************************************/
|
|
if (_SL.pCSUserData != NULL)
|
|
{
|
|
TRC_NRM((TB, _T("Free CS user data")));
|
|
UT_Free( _pUt, _SL.pCSUserData);
|
|
_SL.pCSUserData = NULL;
|
|
_SL.CSUserDataLength = 0;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Free CS User Data. */
|
|
/************************************************************************/
|
|
if (_SL.pCSUserData != NULL)
|
|
{
|
|
TRC_NRM((TB, _T("Free CS User Data")));
|
|
UT_Free( _pUt, _SL.pCSUserData);
|
|
_SL.pCSUserData = NULL;
|
|
_SL.CSUserDataLength = 0;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* No need to clear SL data - it will be reinitialized to 0 when */
|
|
/* SL_Init() is called. */
|
|
/************************************************************************/
|
|
|
|
DC_END_FN();
|
|
} /* SLFreeInitResources */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: SLIssueDisconnectedCallback */
|
|
/* */
|
|
/* Purpose: Issues a onDisconnected callback. This function is called */
|
|
/* when an SL connection error occurs before the lower layers */
|
|
/* are connected. */
|
|
/* */
|
|
/* Params: IN reason - reason to pass on the callback. */
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CSL::SLIssueDisconnectedCallback(ULONG_PTR reason)
|
|
{
|
|
DC_BEGIN_FN("SLIssueDisconnectedCallback");
|
|
|
|
/************************************************************************/
|
|
/* Just issue a onDisconnected callback with the specified reason. */
|
|
/************************************************************************/
|
|
SL_OnDisconnected(SL_MAKE_DISCONNECT_ERR(reason));
|
|
|
|
DC_END_FN();
|
|
} /* SLIssueDisconnectedCallback */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: SLSetReasonAndDisconnect */
|
|
/* */
|
|
/* Purpose: Set the disconnection reason and then call NL_Disconnect. This */
|
|
/* function is always called in the sender context. */
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CSL::SLSetReasonAndDisconnect(ULONG_PTR reason)
|
|
{
|
|
DC_BEGIN_FN("SLSetReasonAndDisconnect");
|
|
|
|
TRC_NRM((TB, _T("Setting disconnect error code from %u->%u"),
|
|
_SL.disconnectErrorCode,
|
|
SL_MAKE_DISCONNECT_ERR(reason)));
|
|
|
|
// Check that the disconnectErrorCode has not already been set and then
|
|
// set it.
|
|
if (0 != _SL.disconnectErrorCode)
|
|
{
|
|
TRC_ERR((TB, _T("Disconnect error code has already been set! Was %u"),
|
|
_SL.disconnectErrorCode));
|
|
}
|
|
|
|
_SL.disconnectErrorCode = SL_MAKE_DISCONNECT_ERR(reason);
|
|
|
|
// Finally begin the disconnect processing.
|
|
SL_Disconnect();
|
|
|
|
DC_END_FN();
|
|
} /* SLSetReasonAndDisconnect */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: SL_DecryptHelper */
|
|
/* */
|
|
/* Purpose: Increments decryptcount, does decryption. */
|
|
/* */
|
|
/* Returns: TRUE if the certificate is validated successfully or FALSE */
|
|
/* FALSE otherwise. */
|
|
/****************************************************************************/
|
|
DCBOOL DCINTERNAL CSL::SL_DecryptHelper(
|
|
PDCUINT8 pData,
|
|
DCUINT *pdataLen)
|
|
{
|
|
PRNS_SECURITY_HEADER pSecHdr;
|
|
PRNS_SECURITY_HEADER2 pSecHdr2;
|
|
PDCUINT8 pCoreData;
|
|
DCUINT coreDataLen;
|
|
DCBOOL rc;
|
|
|
|
DC_BEGIN_FN("SL_DecryptHelper");
|
|
|
|
// Bug 679214 - must check there is enough data before reading
|
|
if (*pdataLen < sizeof(RNS_SECURITY_HEADER) ||
|
|
*pdataLen < sizeof(RNS_SECURITY_HEADER1)) {
|
|
SL_DropLinkImmediate(SL_ERR_DECRYPTFAILED);
|
|
TRC_ERR((TB, _T("SL security header not large enough")));
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
pSecHdr = (PRNS_SECURITY_HEADER)pData;
|
|
TRC_ASSERT((pSecHdr->flags & RNS_SEC_ENCRYPT),
|
|
(TB, _T("SL_DecryptHelper should only be called on encrypted data")));
|
|
|
|
if (_SL.encryptionMethodSelected == SM_FIPS_ENCRYPTION_FLAG) {
|
|
// Bug 679214 - must check there is enough data before reading
|
|
if (*pdataLen < sizeof(RNS_SECURITY_HEADER2)) {
|
|
SL_DropLinkImmediate(SL_ERR_DECRYPTFAILED);
|
|
TRC_ERR((TB, _T("SL security header not large enough")));
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
pSecHdr2 = (PRNS_SECURITY_HEADER2)pData;
|
|
pCoreData = (PDCUINT8)pData + sizeof(RNS_SECURITY_HEADER2);
|
|
coreDataLen = *pdataLen - sizeof(RNS_SECURITY_HEADER2);
|
|
|
|
TRC_DBG((TB, _T("Encrypted packet at %p (%u), sign %p (%u)"),
|
|
pCoreData, coreDataLen, pData, sizeof(RNS_SECURITY_HEADER2)));
|
|
}
|
|
else {
|
|
pCoreData = (PDCUINT8)pData + sizeof(RNS_SECURITY_HEADER1);
|
|
coreDataLen = *pdataLen - sizeof(RNS_SECURITY_HEADER1);
|
|
|
|
TRC_DBG((TB, _T("Encrypted packet at %p (%u), sign %p (%u)"),
|
|
pCoreData, coreDataLen, pData, sizeof(RNS_SECURITY_HEADER1)));
|
|
}
|
|
|
|
TRC_NRM((TB, _T("Update Decrypt Session Key Count , %d"),
|
|
_SL.decryptCount));
|
|
|
|
/****************************************************************/
|
|
/* Decrypt the packet */
|
|
/****************************************************************/
|
|
if( _SL.decryptCount == UPDATE_SESSION_KEY_COUNT ) {
|
|
rc = TRUE;
|
|
// Don't need to update the session key if using FIPS
|
|
if (_SL.encryptionMethodSelected != SM_FIPS_ENCRYPTION_FLAG) {
|
|
rc = UpdateSessionKey(
|
|
_SL.startDecryptKey,
|
|
_SL.currentDecryptKey,
|
|
_SL.encryptionMethodSelected,
|
|
_SL.keyLength,
|
|
&_SL.rc4DecryptKey,
|
|
_SL.encryptionLevel );
|
|
}
|
|
if( !rc ) {
|
|
TRC_ERR((TB, _T("SL failed to update session key")));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************/
|
|
/* reset counter. */
|
|
/************************************************************/
|
|
_SL.decryptCount = 0;
|
|
}
|
|
|
|
TRC_ASSERT((_SL.decryptCount < UPDATE_SESSION_KEY_COUNT),
|
|
(TB, _T("Invalid decrypt count")));
|
|
|
|
TRC_DATA_DBG("Data buffer before decryption", pCoreData, coreDataLen);
|
|
|
|
if (SL_GetEncSafeChecksumSC() !=
|
|
((pSecHdr->flags & RDP_SEC_SECURE_CHECKSUM) != 0)) {
|
|
TRC_ERR((TB,_T("SC safechecksum: 0x%x mismatch protocol:0x%x"),
|
|
SL_GetEncSafeChecksumSC(),
|
|
(pSecHdr->flags & RDP_SEC_SECURE_CHECKSUM)));
|
|
}
|
|
|
|
if (_SL.encryptionMethodSelected == SM_FIPS_ENCRYPTION_FLAG) {
|
|
rc = TSCAPI_DecryptData(
|
|
&(_SL.SLCapiData),
|
|
pCoreData,
|
|
coreDataLen,
|
|
pSecHdr2->padlen,
|
|
(PDCUINT8)pSecHdr2->dataSignature,
|
|
_SL.totalDecryptCount);
|
|
*pdataLen -= pSecHdr2->padlen;
|
|
}
|
|
else {
|
|
rc = DecryptData(
|
|
_SL.encryptionLevel,
|
|
_SL.currentDecryptKey,
|
|
&_SL.rc4DecryptKey,
|
|
_SL.keyLength,
|
|
pCoreData,
|
|
coreDataLen,
|
|
_SL.macSaltKey,
|
|
(PDCUINT8)((PRNS_SECURITY_HEADER1)pSecHdr)->dataSignature,
|
|
(pSecHdr->flags & RDP_SEC_SECURE_CHECKSUM),
|
|
_SL.totalDecryptCount);
|
|
}
|
|
if( !rc ) {
|
|
|
|
//
|
|
// It is necessary to immediately abort the connection
|
|
// as we may be under attack here and we should stop processing
|
|
// any additional data
|
|
//
|
|
SL_DropLinkImmediate(SL_ERR_DECRYPTFAILED);
|
|
|
|
TRC_ERR((TB, _T("SL failed to decrypt data")));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/****************************************************************/
|
|
/* successfully decrypted a packet, increment the decrption */
|
|
/* counter. */
|
|
/****************************************************************/
|
|
_SL.decryptCount++;
|
|
_SL.totalDecryptCount++;
|
|
|
|
TRC_DBG((TB, _T("Data decrypted")));
|
|
TRC_DATA_DBG("Data buffer after decryption", pCoreData, coreDataLen);
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
|
|
#ifdef USE_LICENSE
|
|
|
|
/****************************************************************************/
|
|
/* Name: SLReceivedLicPacket */
|
|
/* */
|
|
/* Purpose: Handle incoming license packet */
|
|
/* */
|
|
/* Operation: Called in the Receive context */
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CSL::SLReceivedLicPacket(
|
|
PDCUINT8 pData,
|
|
DCUINT dataLen,
|
|
DCUINT flags,
|
|
DCUINT channelID,
|
|
DCUINT priority)
|
|
{
|
|
DC_BEGIN_FN("SLReceivedLicPacket");
|
|
|
|
SL_CHECK_STATE(SL_EVENT_ON_RECEIVED_LIC_PACKET);
|
|
|
|
DC_IGNORE_PARAMETER(flags);
|
|
DC_IGNORE_PARAMETER(channelID);
|
|
DC_IGNORE_PARAMETER(priority);
|
|
|
|
//
|
|
// We will decrypt the S->C licensing data packet if encryption is
|
|
// on AND if the server encrypted this particular packet.
|
|
//
|
|
if (_SL.encrypting &&
|
|
(((PRNS_SECURITY_HEADER_UA)pData)->flags & RNS_SEC_ENCRYPT))
|
|
{
|
|
if (!SL_DecryptHelper(pData, &dataLen))
|
|
{
|
|
TRC_ERR((TB, _T("SL failed to decompress data")));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
// Decouple to Sender thread.
|
|
_pCd->CD_DecoupleSyncDataNotification(CD_SND_COMPONENT, this,
|
|
CD_NOTIFICATION_FUNC(CSL,SLLicenseData), pData, dataLen);
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
} /* SLReceivedLicPacket */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: SLLicenseData */
|
|
/* */
|
|
/* Purpose: Handle incoming license packets on the Send thread */
|
|
/* */
|
|
/* Params: pData - pointer to incoming license data */
|
|
/* dataLen - length of data */
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CSL::SLLicenseData(PDCVOID pData, DCUINT dataLen)
|
|
{
|
|
int licenseResult;
|
|
int nSecHeader;
|
|
PDCUINT8 pbInput = NULL;
|
|
UINT32 uiExtendedErrorInfo = TS_ERRINFO_NOERROR;
|
|
|
|
DC_BEGIN_FN("SLLicenseData");
|
|
|
|
if (_SL.encryptionMethodSelected == SM_FIPS_ENCRYPTION_FLAG) {
|
|
nSecHeader = sizeof(RNS_SECURITY_HEADER2);
|
|
}
|
|
else {
|
|
nSecHeader = (((PRNS_SECURITY_HEADER_UA)pData)->flags & RNS_SEC_ENCRYPT) ?
|
|
sizeof(RNS_SECURITY_HEADER1) : sizeof(RNS_SECURITY_HEADER);
|
|
}
|
|
pbInput = (PDCUINT8)pData;
|
|
dataLen -= nSecHeader;
|
|
pbInput += nSecHeader;
|
|
|
|
if(((PRNS_SECURITY_HEADER_UA)pData)->flags & RDP_SEC_LICENSE_ENCRYPT_CS)
|
|
{
|
|
TRC_NRM((TB,_T("Server specified encrypt licensing packets")));
|
|
_pLic->SetEncryptLicensingPackets(TRUE);
|
|
}
|
|
else
|
|
{
|
|
_pLic->SetEncryptLicensingPackets(FALSE);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Call licensing callout */
|
|
/************************************************************************/
|
|
licenseResult = _pLic->CLicenseData(_SL.hLicenseHandle,
|
|
pbInput,
|
|
(DWORD)dataLen,
|
|
&uiExtendedErrorInfo);
|
|
|
|
//
|
|
// Licensing is enforced, proceed with the connection only if the licensing
|
|
// protocol has completed successfully.
|
|
//
|
|
TRC_ASSERT( ( ( licenseResult == LICENSE_OK ) ||
|
|
( licenseResult == LICENSE_CONTINUE ) ||
|
|
( licenseResult == LICENSE_ERROR ) ),
|
|
( TB,_T("Invalid license result %d"), licenseResult ) );
|
|
|
|
/************************************************************************/
|
|
/* If everything is finished, tell the Core */
|
|
/************************************************************************/
|
|
if ( licenseResult == LICENSE_OK )
|
|
{
|
|
TRC_NRM((TB, _T("License negotiation complete")));
|
|
|
|
//
|
|
// stop the licensing timer
|
|
//
|
|
|
|
//
|
|
// Decouple to the UI thread, it will take care of
|
|
// stopping the licensing timer.
|
|
// (in a thread safe way).
|
|
//
|
|
_pCd->CD_DecoupleSimpleNotification(CD_UI_COMPONENT,
|
|
_pUi,
|
|
CD_NOTIFICATION_FUNC(CUI,
|
|
UI_OnLicensingComplete),
|
|
NULL);
|
|
|
|
SL_SET_STATE(SL_STATE_CONNECTED);
|
|
|
|
TRC_NRM((TB, _T("Terminate License Manager")));
|
|
|
|
_pLic->CLicenseTerm(_SL.hLicenseHandle);
|
|
_SL.hLicenseHandle = NULL;
|
|
|
|
_SL.callbacks.onConnected(_pCo, _SL.channelID,
|
|
_SL.pSCUserData,
|
|
_SL.SCUserDataLength,
|
|
_SL.serverVersion);
|
|
}
|
|
else if( LICENSE_CONTINUE != licenseResult )
|
|
{
|
|
TRC_ERR((TB, _T("License negotiation failed: %d"), licenseResult));
|
|
|
|
SL_SET_STATE(SL_STATE_DISCONNECTING);
|
|
|
|
_pCd->CD_DecoupleSimpleNotification(
|
|
CD_UI_COMPONENT,
|
|
_pUi,
|
|
CD_NOTIFICATION_FUNC(CUI,
|
|
UI_SetDisconnectReason),
|
|
UI_MAKE_DISCONNECT_ERR(
|
|
UI_ERR_LICENSING_NEGOTIATION_FAIL));
|
|
|
|
|
|
_pCd->CD_DecoupleSimpleNotification(
|
|
CD_UI_COMPONENT,
|
|
_pUi,
|
|
CD_NOTIFICATION_FUNC(CUI,
|
|
UI_SetServerErrorInfo),
|
|
uiExtendedErrorInfo);
|
|
|
|
SL_Disconnect();
|
|
}
|
|
|
|
DC_END_FN();
|
|
} /* SLLicenseData */
|
|
#endif //USE_LICENSE
|
|
|
|
|
|
#ifdef OS_WINCE
|
|
#define CLIENTADDRESS_LENGTH 30
|
|
#endif
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: SLGetComputerAddressW */
|
|
/* */
|
|
/* Purpose: Retrieves the computer address. */
|
|
/****************************************************************************/
|
|
DCBOOL DCINTERNAL CSL::SLGetComputerAddressW(PDCUINT8 szBuff)
|
|
{
|
|
BOOL rc = FALSE;
|
|
|
|
DC_BEGIN_FN("UT_GetComputerAddressW");
|
|
|
|
// initialize client address family and client address length
|
|
*((PDCUINT16_UA)szBuff) = 0;
|
|
*((PDCUINT16_UA)(szBuff+sizeof(DCUINT16))) = 0;
|
|
|
|
if (_pUi->UI_GetTDSocket() != INVALID_SOCKET) {
|
|
int sockLen;
|
|
SOCKADDR sockName;
|
|
char *pszaddr;
|
|
UINT addrlength;
|
|
USHORT pstrW[CLIENTADDRESS_LENGTH + 2];
|
|
|
|
sockLen = sizeof(sockName);
|
|
|
|
if (getsockname(_pUi->UI_GetTDSocket(), &sockName, &sockLen) == 0) {
|
|
// client address family
|
|
*((PDCUINT16_UA)szBuff) = sockName.sa_family;
|
|
szBuff += sizeof(DCUINT16);
|
|
|
|
pszaddr = inet_ntoa(((PSOCKADDR_IN)&sockName)->sin_addr);
|
|
addrlength = strlen(pszaddr) + 1;
|
|
|
|
// client address length
|
|
*((PDCUINT16_UA)szBuff) = (USHORT) (addrlength * 2);
|
|
szBuff += sizeof(DCUINT16);
|
|
|
|
// client address
|
|
#ifdef OS_WIN32
|
|
{
|
|
ULONG ulRetVal;
|
|
|
|
ulRetVal = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
|
|
pszaddr, -1, pstrW, CLIENTADDRESS_LENGTH + 2);
|
|
pstrW[ulRetVal] = 0;
|
|
memcpy(szBuff, pstrW, (ulRetVal + 1) * 2);
|
|
}
|
|
#else // !OS_WIN32
|
|
mbstowcs(pstrW, pszaddr, addrlength);
|
|
memcpy(szBuff, pstrW, addrlength * 2);
|
|
#endif // OS_WIN32
|
|
|
|
rc = TRUE;
|
|
}
|
|
}
|
|
|
|
DC_END_FN()
|
|
return rc;
|
|
}
|
|
|
|
//
|
|
// SLComputeHMACVerifier
|
|
// Compute the HMAC verifier from the random
|
|
// and the cookie
|
|
//
|
|
BOOL
|
|
CSL::SLComputeHMACVerifier(
|
|
PBYTE pCookie, //IN - the shared secret
|
|
LONG cbCookieLen, //IN - the shared secret len
|
|
PBYTE pRandom, //IN - the session random
|
|
LONG cbRandomLen, //IN - the session random len
|
|
PBYTE pVerifier, //OUT- the verifier
|
|
LONG cbVerifierLen //IN - the verifier buffer length
|
|
)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
DC_BEGIN_FN("SLComputeHMACVerifier");
|
|
|
|
TRC_ASSERT(cbVerifierLen >= MD5DIGESTLEN,
|
|
(TB,_T("cbVerifierLen too short!")));
|
|
|
|
if (!(pCookie &&
|
|
cbCookieLen &&
|
|
pRandom &&
|
|
cbRandomLen &&
|
|
pVerifier &&
|
|
cbVerifierLen)) {
|
|
|
|
TRC_ERR((TB,_T("Invalid param(s) bailing on HMAC")));
|
|
DC_QUIT;
|
|
|
|
}
|
|
HMACMD5_CTX hmacctx;
|
|
HMACMD5Init(&hmacctx, pCookie, cbCookieLen);
|
|
|
|
HMACMD5Update(&hmacctx, pRandom, cbRandomLen);
|
|
HMACMD5Final(&hmacctx, pVerifier);
|
|
|
|
fRet = TRUE;
|
|
|
|
DC_END_FN();
|
|
|
|
DC_EXIT_POINT:
|
|
return fRet;
|
|
}
|
|
|