Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1692 lines
74 KiB

/****************************************************************************/
/* asmapi.c */
/* */
/* Security Manager API */
/* */
/* Copyright (C) 1997-1999 Microsoft Corporation */
/****************************************************************************/
#include <precomp.h>
#pragma hdrstop
#define TRC_FILE "asmapi"
#define pTRCWd (pRealSMHandle->pWDHandle)
#include <adcg.h>
#include <aprot.h>
#include <acomapi.h>
#include <nwdwapi.h>
#include <anmapi.h>
#include <asmint.h>
#include <slicense.h>
#include <regapi.h>
#define DC_INCLUDE_DATA
#include <asmdata.c>
#undef DC_INCLUDE_DATA
/****************************************************************************/
/* Name: SM_GetDataSize */
/* */
/* Purpose: Returns size of per-instance SM data required */
/* */
/* Returns: size of data required */
/* */
/* Operation: SM stores per-instance data in a piece of memory allocated */
/* by WDW. This function returns the size of the data required. */
/* A pointer to this data (the 'SM Handle') is passed into all */
/* subsequent SM functions. */
/****************************************************************************/
unsigned RDPCALL SM_GetDataSize(void)
{
DC_BEGIN_FN("SM_GetDataSize");
DC_END_FN();
return(sizeof(SM_HANDLE_DATA) + NM_GetDataSize());
} /* SM_GetDataSize */
/****************************************************************************/
/* Name: SM_GetEncryptionMethods */
/* */
/* Purpose: Return the security settings supported by this server for use */
/* in shadowing operations. The shadow target server dictates */
/* the final selected method & level. */
/* */
/* Params: pSMHandle - SM handle */
/****************************************************************************/
VOID RDPCALL SM_GetEncryptionMethods(PVOID pSMHandle, PRNS_UD_CS_SEC pSecurityData)
{
PSM_HANDLE_DATA pRealSMHandle = (PSM_HANDLE_DATA)pSMHandle;
ULONG ulMethods ;
DC_BEGIN_FN("SM_SM_GetEncryptionMethods");
// Allow a FIPS shadow client to remote control servers with lesser
// encryption strength
ulMethods = pRealSMHandle->encryptionMethodsSupported;
if (ulMethods & SM_FIPS_ENCRYPTION_FLAG) {
ulMethods |= (SM_128BIT_ENCRYPTION_FLAG | SM_40BIT_ENCRYPTION_FLAG | SM_56BIT_ENCRYPTION_FLAG);
TRC_ALT((TB, "Allow FIPS client to shadow a lower security target: %lx",
ulMethods));
}
else {
// Allow a 128-bit shadow client to remote control servers with lesser
// encryption strength
if (ulMethods & SM_128BIT_ENCRYPTION_FLAG) {
ulMethods |= (SM_40BIT_ENCRYPTION_FLAG | SM_56BIT_ENCRYPTION_FLAG);
TRC_ALT((TB, "Allow 128-bit client to shadow a lower security target: %lx",
ulMethods));
}
}
if( !pRealSMHandle->frenchClient ) {
pSecurityData->encryptionMethods = ulMethods;
pSecurityData->extEncryptionMethods = 0;
}
else {
pSecurityData->encryptionMethods = 0;
pSecurityData->extEncryptionMethods = ulMethods;
}
DC_END_FN();
return;
}
/****************************************************************************/
/* Name: SM_GetDefaultSecuritySettings */
/* */
/* Purpose: Returns the security settings supported by this server */
/* for shadowing operations. */
/* */
/* Params: pClientSecurityData - GCC user data identical to a standard */
/* conference connection. */
/****************************************************************************/
NTSTATUS RDPCALL SM_GetDefaultSecuritySettings(PRNS_UD_CS_SEC pClientSecurityData)
{
pClientSecurityData->header.type = RNS_UD_CS_SEC_ID;
pClientSecurityData->header.length = sizeof(*pClientSecurityData);
pClientSecurityData->encryptionMethods =
SM_40BIT_ENCRYPTION_FLAG |
SM_56BIT_ENCRYPTION_FLAG |
SM_128BIT_ENCRYPTION_FLAG |
SM_FIPS_ENCRYPTION_FLAG;
pClientSecurityData->extEncryptionMethods = 0;
return STATUS_SUCCESS;
}
/****************************************************************************/
/* Name: SM_Init */
/* */
/* Purpose: Initialize SM */
/* */
/* Params: pSMHandle - SM handle */
/* pWDHandle - WD Handle (to be passed back on WDW_SMCallback) */
/****************************************************************************/
NTSTATUS RDPCALL SM_Init(PVOID pSMHandle,
PTSHARE_WD pWDHandle,
BOOLEAN bOldShadow)
{
BOOL rc;
NTSTATUS status = STATUS_UNSUCCESSFUL;
unsigned i;
unsigned regRc;
PSM_HANDLE_DATA pRealSMHandle = (PSM_HANDLE_DATA)pSMHandle;
INT32 regValue;
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING UnicodeString;
static UINT32 keyInfoBuffer[16];
ULONG keyInfoLength;
HANDLE RegistryKeyHandle;
PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
DC_BEGIN_FN("SM_Init");
SM_CHECK_STATE(SM_EVT_INIT);
/************************************************************************/
/* Store the WDW Handle before we do anything else, as we can't trace */
/* until we do so. */
/************************************************************************/
pRealSMHandle->pWDHandle = pWDHandle;
pRealSMHandle->bForwardDataToSC = FALSE;
/************************************************************************/
/* Get default DONTDISPLAYLASTUSERNAME setting */
/************************************************************************/
pWDHandle->fDontDisplayLastUserName = FALSE;
RtlInitUnicodeString(&UnicodeString, W2K_GROUP_POLICY_WINLOGON_KEY);
InitializeObjectAttributes(&ObjectAttributes,
&UnicodeString,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
status = ZwOpenKey(&RegistryKeyHandle, GENERIC_READ, &ObjectAttributes);
if (NT_SUCCESS(status)) {
RtlInitUnicodeString(&UnicodeString, WIN_DONTDISPLAYLASTUSERNAME);
KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION)keyInfoBuffer;
status = ZwQueryValueKey(RegistryKeyHandle,
&UnicodeString,
KeyValuePartialInformation,
KeyValueInformation,
sizeof(keyInfoBuffer),
&keyInfoLength);
// For W2K the value should be a DWORD.
if ((NT_SUCCESS(status)) &&
(KeyValueInformation->Type == REG_DWORD) &&
(KeyValueInformation->DataLength >= sizeof(DWORD))) {
pWDHandle->fDontDisplayLastUserName =
(BOOLEAN)(*(PDWORD)(KeyValueInformation->Data) == 1);
}
ZwClose(RegistryKeyHandle);
}
// Starting with W2K the place where the DontDislpayLastUserName policy
// is store has moved to another key (W2K_GROUP_POLICY_WINLOGON_KEY). But
// we still have to look at the old key in case we could not read the
// value in the post W2K key (the policy is not definde). We want to follow
// the winlogon behavior in the console.
// In case there is a value set in the new policy key we will use that
// value. In case there isn't one we look in the old place. As I said this
// is what winlogon does.
//
if (!NT_SUCCESS(status)) {
RtlInitUnicodeString(&UnicodeString, WINLOGON_KEY);
InitializeObjectAttributes(&ObjectAttributes,
&UnicodeString,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
status = ZwOpenKey(&RegistryKeyHandle, GENERIC_READ, &ObjectAttributes);
if (NT_SUCCESS(status)) {
RtlInitUnicodeString(&UnicodeString, WIN_DONTDISPLAYLASTUSERNAME);
KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION)keyInfoBuffer;
status = ZwQueryValueKey(RegistryKeyHandle,
&UnicodeString,
KeyValuePartialInformation,
KeyValueInformation,
sizeof(keyInfoBuffer),
&keyInfoLength);
if (NT_SUCCESS(status)) {
pWDHandle->fDontDisplayLastUserName =
(BOOLEAN)(KeyValueInformation->Data[0] == '1');
}
ZwClose(RegistryKeyHandle);
}
}
/************************************************************************/
/* We don't run without encryption in a retail build */
/************************************************************************/
TRC_NRM((TB, "encryption level is %d", pRealSMHandle->encryptionLevel));
if (pRealSMHandle->encryptionLevel < 1)
{
TRC_ALT((TB, "Forcing encryption back to level 1!"));
pRealSMHandle->encryptionLevel = 1;
}
#ifdef INSTRUM_TRACK_DISCARDED
pRealSMHandle->nDiscardNonVCPDUWhenDead = 0;
pRealSMHandle->nDiscardPDUBadState = 0;
pRealSMHandle->nDiscardVCDataWhenDead = 0;
#endif
if (pRealSMHandle->encryptionLevel == 0) {
TRC_ALT((TB, "Not encrypting"));
pRealSMHandle->encryptionMethodsSupported = 0;
pRealSMHandle->encrypting = FALSE;
pRealSMHandle->encryptDisplayData = FALSE;
pRealSMHandle->encryptingLicToClient = FALSE;
pRealSMHandle->encryptionMethodSelected = 0;
pRealSMHandle->frenchClient = FALSE;
pRealSMHandle->recvdClientRandom = FALSE;
pRealSMHandle->bSessionKeysMade = FALSE;
pRealSMHandle->encryptHeaderLen = 0;
pRealSMHandle->encryptHeaderLenIfForceEncrypt = sizeof(RNS_SECURITY_HEADER1);
}
else {
pRealSMHandle->encrypting = TRUE;
pRealSMHandle->frenchClient = FALSE;
TRC_NRM((TB, "Encrypting"));
/********************************************************************/
/* encrypt the display data if encryptionLevel is 2 (or above). */
/********************************************************************/
if (pRealSMHandle->encryptionLevel == 1) {
pRealSMHandle->encryptDisplayData = FALSE;
pRealSMHandle->encryptHeaderLen = sizeof(RNS_SECURITY_HEADER);
pRealSMHandle->encryptHeaderLenIfForceEncrypt = sizeof(RNS_SECURITY_HEADER1);
TRC_NRM((TB, "Displaydata not encrypted"));
}
else {
pRealSMHandle->encryptDisplayData = TRUE;
pRealSMHandle->encryptHeaderLen = sizeof(RNS_SECURITY_HEADER1);
}
/********************************************************************/
/* for down level compatibility, support both 40bit, 56bit */
/* and 128bit default. */
/********************************************************************/
pRealSMHandle->encryptionMethodsSupported =
SM_128BIT_ENCRYPTION_FLAG |
SM_56BIT_ENCRYPTION_FLAG |
SM_40BIT_ENCRYPTION_FLAG |
SM_FIPS_ENCRYPTION_FLAG;
/********************************************************************/
/* encrypt 128bit and above if encryptionLevel is 3 */
/********************************************************************/
if (pRealSMHandle->encryptionLevel == 3) {
pRealSMHandle->encryptionMethodsSupported =
SM_128BIT_ENCRYPTION_FLAG | SM_FIPS_ENCRYPTION_FLAG;
}
/********************************************************************/
/* encrypt in FIPS only if encryption level is 4 or above. */
/********************************************************************/
if (pRealSMHandle->encryptionLevel >= 4) {
pRealSMHandle->encryptionMethodsSupported = SM_FIPS_ENCRYPTION_FLAG;
}
TRC_NRM((TB, "Encryption methods supported %08lx: Level %ld\n",
pRealSMHandle->encryptionMethodsSupported,
pRealSMHandle->encryptionLevel));
/********************************************************************/
/* initally set the encryption method selected as */
/* SM_56BIT_ENCRYPTION_FLAG, later it will be set according to the */
/* client support. */
/********************************************************************/
pRealSMHandle->encryptionMethodSelected = SM_56BIT_ENCRYPTION_FLAG;
/********************************************************************/
/* misc init. */
/********************************************************************/
pRealSMHandle->recvdClientRandom = FALSE;
pRealSMHandle->bSessionKeysMade = FALSE;
}
/************************************************************************/
/* We do not know the certificate type used in the key exchange till */
/* after the exchange has taken place. */
/************************************************************************/
pRealSMHandle->CertType = CERT_TYPE_INVALID;
#ifdef USE_LICENSE
/************************************************************************/
/* Initialize the Server license manager */
/************************************************************************/
pRealSMHandle->pLicenseHandle = SLicenseInit();
if (!pRealSMHandle->pLicenseHandle)
{
TRC_ERR((TB, "Failed to initialize License Manager"));
DC_QUIT;
}
pWDHandle->pSLicenseHandle = pRealSMHandle->pLicenseHandle;
#endif
/************************************************************************/
/* Initialize the console buffer stuff */
/************************************************************************/
InitializeListHead(&pRealSMHandle->consoleBufferList);
pRealSMHandle->consoleBufferCount = 0;
/************************************************************************/
/* Finally, initialize the Network Manager */
/************************************************************************/
rc = NM_Init(pRealSMHandle->pWDHandle->pNMInfo,
pSMHandle,
pWDHandle,
pWDHandle->hDomainKernel);
if (!rc)
{
TRC_ERR((TB, "Failed to init NM"));
DC_QUIT;
}
/************************************************************************/
/* Update the state */
/************************************************************************/
SM_SET_STATE(SM_STATE_INITIALIZED);
/************************************************************************/
/* All worked */
/************************************************************************/
status = STATUS_SUCCESS;
DC_EXIT_POINT:
/************************************************************************/
/* If anything failed, clean up. Must be done after calling */
/* FreeContextBuffer as this clears the function table. */
/************************************************************************/
if (!NT_SUCCESS(status))
{
TRC_ERR((TB, "Something failed - clean up"));
SMFreeInitResources(pRealSMHandle);
}
DC_END_FN();
return(status);
} /* SM_Init */
/****************************************************************************/
/* Name: SM_Term */
/****************************************************************************/
void RDPCALL SM_Term(PVOID pSMHandle)
{
PSM_HANDLE_DATA pRealSMHandle = (PSM_HANDLE_DATA)pSMHandle;
DC_BEGIN_FN("SM_Term");
/************************************************************************/
/* SM_Term is called from WD_Close. This can be called on the */
/* listening stack (and maybe in other situations) where SM_Init has */
/* not been called. Check for state != SM_STATE_STARTED before going */
/* any further. */
/* */
/* AND DON'T TRACE IF SM_INIT HASN'T BEEN CALLED. */
/************************************************************************/
if (pRealSMHandle->state == SM_STATE_STARTED)
{
DC_QUIT;
}
/************************************************************************/
/* Free connection resources */
/************************************************************************/
TRC_NRM((TB, "Free connection resources"));
SMFreeConnectResources(pRealSMHandle);
/************************************************************************/
/* Free initialization resources */
/************************************************************************/
TRC_NRM((TB, "Free initialization resources"));
SMFreeInitResources(pRealSMHandle);
#ifdef USE_LICENSE
/************************************************************************/
/* Terminate License Manager */
/************************************************************************/
SLicenseTerm(pRealSMHandle->pLicenseHandle);
#endif
/************************************************************************/
/* Terminate Network Manager */
/************************************************************************/
NM_Term(pRealSMHandle->pWDHandle->pNMInfo);
/************************************************************************/
/* Terminate FIPS */
/************************************************************************/
TSFIPS_Term(&(pRealSMHandle->FIPSData));
/************************************************************************/
/* Free the console buffers */
/************************************************************************/
while (!IsListEmpty(&pRealSMHandle->consoleBufferList)) {
PSM_CONSOLE_BUFFER pBlock;
pBlock = CONTAINING_RECORD(pRealSMHandle->consoleBufferList.Flink, SM_CONSOLE_BUFFER, links);
RemoveEntryList(&pBlock->links);
COM_Free(pBlock);
};
/************************************************************************/
/* Update the state */
/************************************************************************/
SM_SET_STATE(SM_STATE_STARTED);
DC_EXIT_POINT:
DC_END_FN();
} /* SM_Term */
/****************************************************************************/
/* Name: SM_Connect */
/* */
/* Purpose: Accept or Reject an incoming Client */
/* */
/* Returns: TRUE - Connect started OK */
/* FALSE - Connect failed to start */
/* */
/* Params: ppSMHandle - handle for subsequent SM calls on behalf of */
/* this Client */
/* pUserDataIn - SM user data received from Client */
/* pNetUserData - Net user data received from Client */
/* bOldShadow - indicates this is a B3 shadow request */
/* */
/* Operation: Note that this function completes asynchronously. The caller */
/* must wait for an SM_CB_CONNECTED or SM_CB_DISCONNECTED */
/* callback to find out whether the Connect succeeded or failed. */
/****************************************************************************/
NTSTATUS RDPCALL SM_Connect(PVOID pSMHandle,
PRNS_UD_CS_SEC pUserDataIn,
PRNS_UD_CS_NET pNetUserData,
BOOLEAN bOldShadow)
{
NTSTATUS status = STATUS_SUCCESS;
BOOL rc = FALSE;
PSM_HANDLE_DATA pRealSMHandle = (PSM_HANDLE_DATA)pSMHandle;
UINT32 encMethodPicked = 0;
DC_BEGIN_FN("SM_Connect");
SM_CHECK_STATE(SM_EVT_CONNECT);
pRealSMHandle->pUserData = NULL;
/************************************************************************/
/* pick a matching encryption method. */
/************************************************************************/
TRC_ALT((TB, "Client supports encryption: %lx",
pUserDataIn->encryptionMethods));
TRC_NRM((TB, "Server supports encryption: %lx",
pRealSMHandle->encryptionMethodsSupported));
/************************************************************************/
/* if the server does not require any encryption .. */
/************************************************************************/
if( pRealSMHandle->encryptionMethodsSupported == 0 ) {
encMethodPicked = 0;
goto DC_ENC_PICKED;
}
//
// French Client (old and new) set the encryptionMethods value 0.
//
if (pUserDataIn->encryptionMethods == 0) {
pRealSMHandle->frenchClient = TRUE;
//
// check to see the request is from a new client, if so
// use the new field to set the appropriate encryption level.
//
if( pUserDataIn->header.length >= sizeof(RNS_UD_CS_SEC) ) {
pUserDataIn->encryptionMethods = pUserDataIn->extEncryptionMethods;
}
else {
//
// force old client to use 40-bit encryption.
//
pUserDataIn->encryptionMethods = SM_40BIT_ENCRYPTION_FLAG;
}
/************************************************************************/
/* pick a matching encryption method. */
/************************************************************************/
TRC_ALT((TB, "French Client supports encryption: %lx",
pUserDataIn->encryptionMethods));
}
/************************************************************************/
/* if the client only supports FIPS */
/************************************************************************/
if (pUserDataIn->encryptionMethods == SM_FIPS_ENCRYPTION_FLAG) {
/********************************************************************/
/* if the server supports FIPS .... */
/********************************************************************/
if (pRealSMHandle->encryptionMethodsSupported & SM_FIPS_ENCRYPTION_FLAG) {
encMethodPicked = SM_FIPS_ENCRYPTION_FLAG;
goto DC_ENC_PICKED;
}
}
/********************************************************************/
/* if the server only supports FIPS .... */
/********************************************************************/
if (pRealSMHandle->encryptionMethodsSupported == SM_FIPS_ENCRYPTION_FLAG) {
/************************************************************************/
/* if the client supports FIPS */
/************************************************************************/
if (pUserDataIn->encryptionMethods & SM_FIPS_ENCRYPTION_FLAG) {
encMethodPicked = SM_FIPS_ENCRYPTION_FLAG;
goto DC_ENC_PICKED;
}
}
/************************************************************************/
/* if the client supports 128 bit */
/************************************************************************/
if (pUserDataIn->encryptionMethods & SM_128BIT_ENCRYPTION_FLAG) {
/********************************************************************/
/* if the server supports 128bit .... */
/********************************************************************/
if (pRealSMHandle->encryptionMethodsSupported &
SM_128BIT_ENCRYPTION_FLAG) {
encMethodPicked = SM_128BIT_ENCRYPTION_FLAG;
goto DC_ENC_PICKED;
}
}
/************************************************************************/
/* if the client supports 56 bit */
/************************************************************************/
if( pUserDataIn->encryptionMethods & SM_56BIT_ENCRYPTION_FLAG ) {
/********************************************************************/
/* if the server supports 56bit ... */
/********************************************************************/
if( pRealSMHandle->encryptionMethodsSupported &
SM_56BIT_ENCRYPTION_FLAG ) {
encMethodPicked = SM_56BIT_ENCRYPTION_FLAG;
goto DC_ENC_PICKED;
}
}
/************************************************************************/
/* if the client supports 40 bit */
/************************************************************************/
if( pUserDataIn->encryptionMethods & SM_40BIT_ENCRYPTION_FLAG ) {
/********************************************************************/
/* if the server supports 40bit ... */
/********************************************************************/
if( pRealSMHandle->encryptionMethodsSupported &
SM_40BIT_ENCRYPTION_FLAG ) {
encMethodPicked = SM_40BIT_ENCRYPTION_FLAG;
goto DC_ENC_PICKED;
}
}
/************************************************************************/
/* if we are here, we did not find a match */
/************************************************************************/
TRC_ERR((TB, "Failed to match encryption package: %lx",
pUserDataIn->encryptionMethods));
status = STATUS_ENCRYPTION_FAILED;
/****************************************************************/
/* Log an error and disconnect the Client */
/****************************************************************/
if (pRealSMHandle->pWDHandle->StackClass == Stack_Primary) {
WDW_LogAndDisconnect(
pRealSMHandle->pWDHandle, TRUE,
Log_RDP_ENC_EncPkgMismatch,
NULL,
0);
}
DC_QUIT;
DC_ENC_PICKED:
TRC_ALT((TB, "Encryption Method=%d, Level=%ld, Display=%ld",
encMethodPicked,
pRealSMHandle->encryptionLevel,
pRealSMHandle->encryptDisplayData));
/************************************************************************/
/* remember the encryption method that we picked. */
/************************************************************************/
pRealSMHandle->encryptionMethodSelected = encMethodPicked;
// For FIPS, do initialization
// Even the enc method is not FIPS, we need to do initialization since we might
// shadow a FIPS later
if (TSFIPS_Init(&(pRealSMHandle->FIPSData))) {
TRC_NRM((TB, "Init Fips succeed\n"));
}
else {
TRC_ERR((TB, "Init Fips Failed\n"));
// This is only a fatal failure if FIPS was selected as the encryption
// method. If we chose something other than FIPS, then we should
// proceed, although shadowing a FIPS session later should fail.
//
if (SM_FIPS_ENCRYPTION_FLAG == encMethodPicked)
{
status = STATUS_ENCRYPTION_FAILED;
DC_QUIT;
}
}
/************************************************************************/
/* Build user data to return to Client */
/************************************************************************/
pRealSMHandle->pUserData =
(PRNS_UD_SC_SEC)COM_Malloc(sizeof(RNS_UD_SC_SEC));
if (pRealSMHandle->pUserData == NULL)
{
TRC_ERR((TB, "Failed to allocated %d bytes for user data",
sizeof(RNS_UD_SC_SEC)));
status = STATUS_NO_MEMORY;
DC_QUIT;
}
pRealSMHandle->pUserData->header.type = RNS_UD_SC_SEC_ID;
pRealSMHandle->pUserData->header.length = sizeof(RNS_UD_SC_SEC);
pRealSMHandle->pUserData->encryptionMethod =
(bOldShadow ? 0xffffffff : encMethodPicked);
pRealSMHandle->pUserData->encryptionLevel =
pRealSMHandle->encryptionLevel;
/************************************************************************/
/* Call Network Manager */
/************************************************************************/
SM_SET_STATE(SM_STATE_NM_CONNECTING);
TRC_NRM((TB, "Connect to Network Manager"));
rc = NM_Connect(pRealSMHandle->pWDHandle->pNMInfo, pNetUserData);
if (rc != TRUE)
{
status = STATUS_CANCELLED;
DC_QUIT;
}
// Shadow/passthru stacks start out with no encryption. If the target end
// determines that the client server supports encryption, an encrypted
// context will be set up by rdpwsx.
if (pRealSMHandle->pWDHandle->StackClass != Stack_Primary) {
pRealSMHandle->pWDHandle->connected = TRUE;
pRealSMHandle->encrypting = FALSE;
pRealSMHandle->encryptDisplayData = FALSE;
pRealSMHandle->encryptHeaderLen = 0;
pRealSMHandle->encryptHeaderLenIfForceEncrypt = 0;
SM_SET_STATE(SM_STATE_CONNECTED);
SM_SET_STATE(SM_STATE_SC_REGISTERED);
SM_Dead(pRealSMHandle, FALSE);
TRC_ALT((TB, "%s stack: Suspending encryption during key exchange",
pRealSMHandle->pWDHandle->StackClass == Stack_Shadow ?
"Shadow" : "Passthru"));
}
DC_EXIT_POINT:
/************************************************************************/
/* If anything failed, release resources */
/************************************************************************/
if (status != STATUS_SUCCESS)
{
TRC_ERR((TB, "Failed - free connect resources"));
SMFreeConnectResources(pRealSMHandle);
}
DC_END_FN();
return status;
} /* SM_Connect */
/****************************************************************************/
/* Name: SM_Disconnect */
/* */
/* Purpose: Disconnect from a Client */
/* */
/* Returns: TRUE - Disconnect started OK */
/* FALSE - Disconnect failed */
/* */
/* Params: pSMHandle - SM handle */
/* */
/* Operation: Detach the user from the domain. */
/* Note that this function completes asynchronously. The caller */
/* must wait for an SM_CB_DISCONNECTED callback to find whether */
/* the Disconnect succeeded or failed. */
/****************************************************************************/
BOOL RDPCALL SM_Disconnect(PVOID pSMHandle)
{
BOOL rc = FALSE;
PSM_HANDLE_DATA pRealSMHandle = (PSM_HANDLE_DATA)pSMHandle;
DC_BEGIN_FN("SM_Disconnect");
if (SM_CHECK_STATE_Q(SM_EVT_DISCONNECT)) {
// Call Network Layer.
SM_SET_STATE(SM_STATE_DISCONNECTING);
rc = NM_Disconnect(pRealSMHandle->pWDHandle->pNMInfo);
}
DC_END_FN();
return rc;
}
void SM_BreakConnectionWorker(PTSHARE_WD pTSWd, PVOID pParam)
{
NTSTATUS status;
ICA_CHANNEL_COMMAND Command;
PSM_HANDLE_DATA pRealSMHandle = (PSM_HANDLE_DATA)pTSWd->pSmInfo;
DC_BEGIN_FN("SM_BreakConnectionWorker");
Command.Header.Command = ICA_COMMAND_BROKEN_CONNECTION;
Command.BrokenConnection.Reason = Broken_Unexpected;
Command.BrokenConnection.Source = BrokenSource_Server;
status = IcaChannelInput(pTSWd->pContext, Channel_Command, 0, NULL, (BYTE *)&Command, sizeof(Command));
if (!NT_SUCCESS(status)) {
TRC_NRM((TB, "Can't send ICA_COMMAND_BROKEN_CONNECTION, error code 0x%08x", status));
}
DC_END_FN();
}
/****************************************************************************/
/* Name: SM_AllocBuffer */
/* */
/* Purpose: Allocate a buffer */
/* */
/* Returns: TRUE - buffer allocated OK */
/* FALSE - failed to allocate buffer */
/* */
/* Params: pSMHandle - SM Handle */
/* ppBuffer - pointer to packet (returned) */
/* bufferLen - length of buffer */
/* fForceEncrypt - Always encrypt or not */
/* Used only if encryptDisplayData is FALSE */
/* i.e., encryptionLevel is less than 2 */
/****************************************************************************/
NTSTATUS __fastcall SM_AllocBuffer(PVOID pSMHandle,
PPVOID ppBuffer,
UINT32 bufferLen,
BOOLEAN fWait,
BOOLEAN fForceEncrypt)
{
NTSTATUS status;
PSM_HANDLE_DATA pRealSMHandle = (PSM_HANDLE_DATA)pSMHandle;
PRNS_SECURITY_HEADER2 pSecHeader2;
UINT32 newBufferLen, padLen;
DC_BEGIN_FN("SM_AllocBuffer");
if (SM_CHECK_STATE_Q(SM_EVT_ALLOCBUFFER)) {
if (pRealSMHandle->pWDHandle->StackClass != Stack_Console) {
// If FIPS, adjust the BufferLen to the whole FIPS_BLOCK_LEN size
if (pRealSMHandle->encryptionMethodSelected == SM_FIPS_ENCRYPTION_FLAG) {
newBufferLen = TSFIPS_AdjustDataLen(bufferLen);
padLen = newBufferLen - bufferLen;
bufferLen = newBufferLen;
}
// Add enough space for a security header. We always send at least
// a short security header.
if (pRealSMHandle->encryptDisplayData) {
bufferLen += pRealSMHandle->encryptHeaderLen;
}
else {
if (!fForceEncrypt) {
bufferLen += pRealSMHandle->encryptHeaderLen;
}
else {
bufferLen += pRealSMHandle->encryptHeaderLenIfForceEncrypt;
}
}
status = NM_AllocBuffer(pRealSMHandle->pWDHandle->pNMInfo, ppBuffer,
bufferLen, fWait);
if ( STATUS_SUCCESS == status ) {
TRC_NRM((TB, "Alloc buffer size %d at %p", bufferLen,
*ppBuffer));
// If FIPS, fill in padSize in Header
if (pRealSMHandle->encryptionMethodSelected == SM_FIPS_ENCRYPTION_FLAG) {
pSecHeader2 = (PRNS_SECURITY_HEADER2)(*ppBuffer);
pSecHeader2->padlen = (TSUINT8)padLen;
}
// Adjust return pointer for security header.
if (pRealSMHandle->encryptDisplayData) {
*ppBuffer = (PVOID)((PBYTE)(*ppBuffer) +
pRealSMHandle->encryptHeaderLen);
}
else {
if (!fForceEncrypt) {
*ppBuffer = (PVOID)((PBYTE)(*ppBuffer) +
pRealSMHandle->encryptHeaderLen);
}
else {
*ppBuffer = (PVOID)((PBYTE)(*ppBuffer) +
pRealSMHandle->encryptHeaderLenIfForceEncrypt);
}
}
}
else
{
if( status == STATUS_IO_TIMEOUT && TRUE == fWait ) {
//
// WARNING : Disconnect client on first allocation failure, different
// result ranging from TS client AV to immediate lock up on
// re-connect if this scheme is changed disconnect on 2nd
// try, 3rd try ...
//
TRC_NRM((TB, "Failed to alloc buffer size %d, disconnecting client", bufferLen));
// 254514 STRESS: TS: Tdtcp!TdRawWrite needs synchronization with the write complete routine
// Calling the following function will hold the connection lock in tdtcp TDSyncWrite to wait
// for all pending IRP to finish. This will cause deadlock in the system.
//WDW_LogAndDisconnect(pRealSMHandle->pWDHandle, FALSE, Log_RDP_AllocOutBuf, NULL, 0);
// return STATUS_IO_TIMEOUT back to caller,
// looks like we use this return code.
if (!pRealSMHandle->bDisconnectWorkerSent) {
status = IcaQueueWorkItem(pRealSMHandle->pWDHandle->pContext,
SM_BreakConnectionWorker,
0,
ICALOCK_DRIVER);
if (!NT_SUCCESS(status)) {
TRC_NRM((TB, "IcaQueueWorkItem failed, error code 0x%08x", status));
} else {
pRealSMHandle->bDisconnectWorkerSent = TRUE;
}
}
status = STATUS_IO_TIMEOUT;
}
else {
// NM_AllocBuffer will have traced appropriately if the alloc
// failed, no need for TRC_ERR here.
TRC_NRM((TB, "Failed to alloc buffer size %d", bufferLen));
}
}
}
else {
PSM_CONSOLE_BUFFER pBlock;
// For a console stack, just alloc a suitable block - we're not
// actually going to send it!
TRC_NRM((TB, "console stack requesting %d bytes", bufferLen));
*ppBuffer = NULL;
if (!IsListEmpty(&pRealSMHandle->consoleBufferList)) {
pBlock = CONTAINING_RECORD(pRealSMHandle->consoleBufferList.Flink, SM_CONSOLE_BUFFER, links);
do {
// we could improve this by taking the smaller of the suitable blocks
// it can be also faster if we order the list
if (pBlock->length >= bufferLen) {
RemoveEntryList(&pBlock->links);
pRealSMHandle->consoleBufferCount -= 1;
*ppBuffer = pBlock->buffer;
break;
}
pBlock = CONTAINING_RECORD(pBlock->links.Flink, SM_CONSOLE_BUFFER, links);
} while(pBlock != (PSM_CONSOLE_BUFFER)(&pRealSMHandle->consoleBufferList));
}
if (*ppBuffer == NULL) {
// allocate a new block
pBlock = COM_Malloc(sizeof(SM_CONSOLE_BUFFER) + bufferLen);
if (pBlock != NULL) {
pBlock->buffer = (PVOID)((PBYTE)pBlock + sizeof(SM_CONSOLE_BUFFER));
pBlock->length = bufferLen;
*ppBuffer = pBlock->buffer;
} else {
*ppBuffer = NULL;
}
}
if (*ppBuffer != NULL) {
TRC_NRM((TB, "and returning buffer at %p", *ppBuffer));
status = STATUS_SUCCESS;
}
else {
TRC_ERR((TB, "failed to alloc buffer"));
status = STATUS_NO_MEMORY;
}
}
}
else {
status = STATUS_UNSUCCESSFUL; // right error code?
}
DC_END_FN();
return status;
} /* SM_AllocBuffer */
/****************************************************************************/
/* Name: SM_FreeBuffer */
/* */
/* Purpose: Free a buffer */
/* */
/* Params: pSMHandle - SM Handle */
/* pBuffer - buffer to free */
/* fForceEncrypt - Always encrypt or not */
/* Used only if encryptDisplayData is FALSE */
/* i.e., encryptionLevel is less than 2 */
/****************************************************************************/
void __fastcall SM_FreeBuffer(PVOID pSMHandle, PVOID pBuffer, BOOLEAN fForceEncrypt)
{
PSM_HANDLE_DATA pRealSMHandle = (PSM_HANDLE_DATA)pSMHandle;
DC_BEGIN_FN("SM_FreeBuffer");
// Note that, unlike SM_AllocBuffer, we don't check the state table here,
// since if we were able to allocate the buffer, we should always be
// able to free it. Otherwise we may end up leaking buffers during
// warn states.
if (pRealSMHandle->pWDHandle->StackClass != Stack_Console) {
// Adjust for security header.
if (pRealSMHandle->encryptDisplayData) {
pBuffer = (PVOID)((PBYTE)pBuffer -
pRealSMHandle->encryptHeaderLen);
}
else {
if (!fForceEncrypt) {
pBuffer = (PVOID)((PBYTE)pBuffer -
pRealSMHandle->encryptHeaderLen);
}
else {
pBuffer = (PVOID)((PBYTE)pBuffer -
pRealSMHandle->encryptHeaderLenIfForceEncrypt);
}
}
TRC_NRM((TB, "Free buffer at %p", pBuffer));
NM_FreeBuffer(pRealSMHandle->pWDHandle->pNMInfo, pBuffer);
}
else {
PSM_CONSOLE_BUFFER pBlock;
pBlock = (PSM_CONSOLE_BUFFER)((PBYTE)pBuffer - sizeof(SM_CONSOLE_BUFFER));
// For console session, insert it in the list of freed blocks.
TRC_NRM((TB, "console stack freeing buffer at %p", pBuffer));
// Since this block was freshly used,
// insert it at the beginning of the list.
InsertHeadList(&pRealSMHandle->consoleBufferList, &pBlock->links);
if (pRealSMHandle->consoleBufferCount >= 2) {
PVOID listEntry;
// Free a buffer since we have enough buffers.
// Remove and free the last one (tail of the list),
// assuming it's the less used.
listEntry = RemoveTailList(&pRealSMHandle->consoleBufferList);
pBlock = CONTAINING_RECORD(listEntry, SM_CONSOLE_BUFFER, links);
COM_Free(pBlock);
} else {
pRealSMHandle->consoleBufferCount += 1;
}
}
DC_END_FN();
} /* SM_FreeBuffer */
/****************************************************************************/
// SM_SendData
//
// Sends a network buffer. Note that upper layers assume that, if the send
// fails, the buffer will get deallocated. Returns FALSE on failure.
//
// Params: pSMHandle - SM Handle
// pData - data to send
// dataLen - length if data to send
// priority - priority to use
// channelID - channel ID (ignored in this version)
// bFastPathOutput - whether pData contains fast-path output
// flags - the flag that should be put in RNS_SECURITY_HEADER.flags
// fForceEncrypt - Always encrypt or not
// Used only if encryptDisplayData is FALSE
// i.e., encryptionLevel is less than 2
/****************************************************************************/
BOOL __fastcall SM_SendData(
PVOID pSMHandle,
PVOID pData,
UINT32 dataLen,
UINT32 priority,
UINT32 channelID,
BOOL bFastPathOutput,
UINT16 flags,
BOOLEAN fForceEncrypt)
{
BOOL rc = FALSE;
PRNS_SECURITY_HEADER pSecHeader;
PRNS_SECURITY_HEADER2 pSecHeader2;
PSM_HANDLE_DATA pRealSMHandle = (PSM_HANDLE_DATA)pSMHandle;
UINT32 sendLen;
BOOL fUseSafeChecksum = FALSE;
UINT32 encryptHeaderLen = 0;
DC_BEGIN_FN("SM_SendData");
if (SM_CHECK_STATE_Q(SM_EVT_SENDDATA)) {
if (pRealSMHandle->pWDHandle->StackClass != Stack_Console) {
// Send the packet unchanged if we're not encrypting at all.
if ((!pRealSMHandle->encrypting) && (!fForceEncrypt)) {
TRC_DATA_DBG("Send never-encrypted data", pData, dataLen);
rc = NM_SendData(pRealSMHandle->pWDHandle->pNMInfo,
(BYTE *)pData, dataLen, priority, channelID,
bFastPathOutput | NM_NO_SECURITY_HEADER);
DC_QUIT;
}
// Get interesting pointers and lengths.
if ((!pRealSMHandle->encryptDisplayData) && !fForceEncrypt) {
// S->C is not encrypted
encryptHeaderLen = pRealSMHandle->encryptHeaderLen;
sendLen = dataLen + encryptHeaderLen;
}
else {
if (pRealSMHandle->encryptDisplayData) {
// S->C is encrypted
encryptHeaderLen = pRealSMHandle->encryptHeaderLen;
}
else {
// S->C is not encrypted, but we want to encrypt this packet
encryptHeaderLen = pRealSMHandle->encryptHeaderLenIfForceEncrypt;
}
if (pRealSMHandle->encryptionMethodSelected == SM_FIPS_ENCRYPTION_FLAG) {
pSecHeader2 = (PRNS_SECURITY_HEADER2)((PBYTE)pData - encryptHeaderLen);
pSecHeader2->padlen = (TSUINT8)TSFIPS_AdjustDataLen(dataLen) - dataLen;
sendLen = dataLen + encryptHeaderLen + pSecHeader2->padlen;
pSecHeader2->length = sizeof(RNS_SECURITY_HEADER2);
pSecHeader2->version = TSFIPS_VERSION1;
}
else {
sendLen = dataLen + encryptHeaderLen;
}
}
pSecHeader = (PRNS_SECURITY_HEADER)((PBYTE)pData - encryptHeaderLen);
// Encrypt display data if we are asked to do so.
if ((!pRealSMHandle->encryptDisplayData) && !fForceEncrypt) {
pSecHeader->flags = 0;
// We are implicitly not setting TS_OUTPUT_FASTPATH_ENCRYPTED
// bit in bFastPathOutput before passing to NM_SendData.
TRC_DBG((TB, "Data not encrypted"));
}
else {
// Check to see if we need to update the session key.
if (pRealSMHandle->encryptCount == UPDATE_SESSION_KEY_COUNT) {
rc = TRUE;
// Don't need to update the session key if using FIPS
if (pRealSMHandle->encryptionMethodSelected != SM_FIPS_ENCRYPTION_FLAG) {
rc = UpdateSessionKey(
pRealSMHandle->startEncryptKey,
pRealSMHandle->currentEncryptKey,
pRealSMHandle->encryptionMethodSelected,
pRealSMHandle->keyLength,
&pRealSMHandle->rc4EncryptKey,
pRealSMHandle->encryptionLevel);
}
if (rc) {
// Reset counter.
pRealSMHandle->encryptCount = 0;
}
else {
TRC_ERR((TB, "SM failed to update session key"));
WDW_LogAndDisconnect(
pRealSMHandle->pWDHandle, TRUE,
Log_RDP_ENC_UpdateSessionKeyFailed,
NULL,
0);
DC_QUIT;
}
}
TRC_DATA_DBG("Data buffer before encryption", pData, dataLen);
//
// Disable the safe checksumming in the shadow pipe as there
// is currently no way to reliably do caps negotiation in the
// pipe.
//
// This is not an issue because we don't have fastpath in
// the shadow pipe and as a result of this we're not vulnerable
// to the checksum frequency analysis security vulnerability.
//
//
fUseSafeChecksum = pRealSMHandle->useSafeChecksumMethod &&
(pRealSMHandle->pWDHandle->StackClass == Stack_Primary);
if (pRealSMHandle->encryptionMethodSelected == SM_FIPS_ENCRYPTION_FLAG) {
rc = TSFIPS_EncryptData(
&(pRealSMHandle->FIPSData),
pData,
dataLen + pSecHeader2->padlen,
pSecHeader2->padlen,
pSecHeader2->dataSignature,
pRealSMHandle->totalEncryptCount);
}
else {
rc = EncryptData(
pRealSMHandle->encryptionLevel,
pRealSMHandle->currentEncryptKey,
&pRealSMHandle->rc4EncryptKey,
pRealSMHandle->keyLength,
pData,
dataLen,
pRealSMHandle->macSaltKey,
((PRNS_SECURITY_HEADER1)pSecHeader)->dataSignature,
fUseSafeChecksum,
pRealSMHandle->totalEncryptCount);
}
if (rc) {
TRC_DBG((TB, "Data encrypted"));
TRC_DATA_DBG("Data buffer after encryption", pData,
dataLen);
// Successfully encrypted a packet, increment the
// encryption counter and set the header.
pRealSMHandle->encryptCount++;
pRealSMHandle->totalEncryptCount++;
TRC_ASSERT(((flags == RNS_SEC_ENCRYPT) ||
(flags == RDP_SEC_REDIRECTION_PKT3) ||
(flags == (RNS_SEC_ENCRYPT | RNS_SEC_LICENSE_PKT | RDP_SEC_LICENSE_ENCRYPT_CS))),
(TB,"SM_SendData shouldn't get this flag %d", flags));
pSecHeader->flags = flags;
if (fUseSafeChecksum) {
bFastPathOutput |= TS_OUTPUT_FASTPATH_SECURE_CHECKSUM;
pSecHeader->flags |= RDP_SEC_SECURE_CHECKSUM;
}
bFastPathOutput |= TS_OUTPUT_FASTPATH_ENCRYPTED;
}
else {
// If we failed, the in-place encryption probably
// destroyed part or all of the data. The stream is
// now corrupted, we need to disconnect.
TRC_ERR((TB, "SM failed to encrypt data"));
WDW_LogAndDisconnect(
pRealSMHandle->pWDHandle, TRUE,
Log_RDP_ENC_EncryptFailed,
NULL,
0);
DC_QUIT;
}
}
// Send it!
rc = NM_SendData(pRealSMHandle->pWDHandle->pNMInfo, (BYTE *)pSecHeader,
sendLen, priority, channelID, bFastPathOutput);
}
else {
// For console session, simply claim to have sent it.
TRC_NRM((TB, "console stack sending buffer at %p", pData));
rc = TRUE;
// Note that we will have to free it.
SM_FreeBuffer(pSMHandle, pData, fForceEncrypt);
}
}
else {
// Bad state - we need to free the data.
TRC_ALT((TB,"Freeing buffer on bad state"));
SM_FreeBuffer(pSMHandle, pData, fForceEncrypt);
}
DC_EXIT_POINT:
DC_END_FN();
return rc;
} /* SM_SendData */
/****************************************************************************/
/* Name: SM_Dead */
/****************************************************************************/
void RDPCALL SM_Dead(PVOID pSMHandle, BOOL dead)
{
PSM_HANDLE_DATA pRealSMHandle = (PSM_HANDLE_DATA)pSMHandle;
DC_BEGIN_FN("SM_Dead");
pRealSMHandle->dead = (BOOLEAN)dead;
NM_Dead(pRealSMHandle->pWDHandle->pNMInfo, dead);
if (dead)
{
/********************************************************************/
/* SM_Dead(TRUE) can be called in any state to kill SM. Don't */
/* check the existing state - simply set the new state to */
/* DISCONNECTING. */
/********************************************************************/
TRC_ALT((TB, "SM Dead - change state to DISCONNECTING"));
SM_SET_STATE(SM_STATE_DISCONNECTING);
}
else
{
/********************************************************************/
/* SM_Dead(FALSE) is called on (re)connect. The SM state will be */
/* - SC_REGISTERED on connect */
/* - SM_DISCONNECTING on reconnect */
/* In both cases, set the state to SC_REGISTERED */
/********************************************************************/
SM_CHECK_STATE(SM_EVT_ALIVE);
TRC_ALT((TB, "SM Alive - change state to SC_REGISTERED"));
SM_SET_STATE(SM_STATE_SC_REGISTERED);
pRealSMHandle->bDisconnectWorkerSent = FALSE;
}
DC_EXIT_POINT:
DC_END_FN();
} /* SM_Dead */
#ifdef USE_LICENSE
/****************************************************************************/
/* Name: SM_LicenseOK */
/****************************************************************************/
void RDPCALL SM_LicenseOK(PVOID pSMHandle)
{
PSM_HANDLE_DATA pRealSMHandle = (PSM_HANDLE_DATA)pSMHandle;
DC_BEGIN_FN("SM_LicenseOK");
TRC_NRM((TB, "Licensing Done"));
SM_SET_STATE(SM_STATE_CONNECTED);
DC_END_FN();
} /* SM_LicenseOK */
#endif
/****************************************************************************/
/* Name: SM_GetSecurityData */
/* */
/* Purpose: Retrieve security data. This will be the encrypted client */
/* random for a Primary or Shadow connection. For passthru */
/* stacks the shadow server random and certificate is returned. */
/* */
/* Params: pSMHandle - SM handle */
/* INOUT PSD_IOCTL - pointer to received IOCtl */
/****************************************************************************/
NTSTATUS RDPCALL SM_GetSecurityData(PVOID pSMHandle, PSD_IOCTL pSdIoctl)
{
NTSTATUS status;
PSM_HANDLE_DATA pRealSMHandle = (PSM_HANDLE_DATA)pSMHandle;
PSECURITYTIMEOUT pSecurityTimeout = (PSECURITYTIMEOUT)
pSdIoctl->InputBuffer;
DC_BEGIN_FN("SM_GetSecurityData");
// Wait for the connected indication from SM.
TRC_ERR((TB, "About to wait for security data"));
if (pSdIoctl->InputBufferLength == sizeof(SECURITYTIMEOUT)) {
status = WDW_WaitForConnectionEvent(pRealSMHandle->pWDHandle,
pRealSMHandle->pWDHandle->pSecEvent,
pSecurityTimeout->ulTimeout);
}
else {
status = STATUS_INVALID_PARAMETER;
TRC_ERR((TB, "Bogus timeout value: %ld", pSecurityTimeout->ulTimeout));
DC_QUIT;
}
TRC_DBG((TB, "Back from wait for security data"));
if (status != STATUS_SUCCESS) {
TRC_ERR((TB, "SM connected indication timed out: (%lx), msec=%ld",
status, pSecurityTimeout->ulTimeout));
status = STATUS_IO_TIMEOUT;
DC_QUIT;
}
/********************************************************************/
/* check to see the given buffer is sufficient. */
/********************************************************************/
if ((pSdIoctl->OutputBuffer == NULL) ||
(pSdIoctl->OutputBufferLength <=
pRealSMHandle->encClientRandomLen)) {
status = STATUS_BUFFER_TOO_SMALL;
DC_QUIT;
}
/********************************************************************/
/* check to see security info is available. */
/********************************************************************/
TRC_ASSERT((pRealSMHandle->encryptionMethodSelected != 0),
(TB,"SM_GetSecurityData is called when encryption is not ON"));
if (pRealSMHandle->encryptionMethodSelected == 0) {
status = STATUS_UNSUCCESSFUL;
DC_QUIT;
}
TRC_ASSERT((pRealSMHandle->recvdClientRandom == TRUE),
(TB,"SM_GetSecurityData issued before the client random received"));
if (pRealSMHandle->recvdClientRandom == FALSE) {
status = STATUS_UNSUCCESSFUL;
DC_QUIT;
}
if (pRealSMHandle->pEncClientRandom == NULL) {
status = STATUS_UNSUCCESSFUL;
DC_QUIT;
}
TRC_ASSERT((pRealSMHandle->encClientRandomLen != 0 ),
(TB,"SM_GetSecurityData invalid pEncClientRandom buffer"));
if (pRealSMHandle->encClientRandomLen == 0) {
status = STATUS_UNSUCCESSFUL;
DC_QUIT;
}
/********************************************************************/
/* copy return data. */
/********************************************************************/
memcpy(
pSdIoctl->OutputBuffer,
pRealSMHandle->pEncClientRandom,
pRealSMHandle->encClientRandomLen);
/********************************************************************/
/* set returned buffer size. */
/********************************************************************/
pSdIoctl->BytesReturned = pRealSMHandle->encClientRandomLen;
/********************************************************************/
/* Free up the client pEncClientRandom Buffer, we don't need this */
/* any more. */
/********************************************************************/
COM_Free(pRealSMHandle->pEncClientRandom);
pRealSMHandle->pEncClientRandom = NULL;
pRealSMHandle->encClientRandomLen = 0;
/************************************************************************/
/* All worked OK */
/************************************************************************/
status = STATUS_SUCCESS;
DC_EXIT_POINT:
DC_END_FN();
return (status);
}
/****************************************************************************/
/* Name: SM_SetSecurityData */
/* */
/* Purpose: Set security data and compute session key. */
/* */
/* Params: pSMHandle - SM Handle */
/* pSecinfo - pointer to random key pair */
/****************************************************************************/
NTSTATUS RDPCALL SM_SetSecurityData(PVOID pSMHandle, PSECINFO pSecInfo)
{
BOOL rc;
NTSTATUS status;
PSM_HANDLE_DATA pRealSMHandle = (PSM_HANDLE_DATA)pSMHandle;
DC_BEGIN_FN("SM_SetSecurityData");
/************************************************************************/
/* use the given client and server random key pairs and arrive at */
/* session keys. */
/************************************************************************/
if ((pRealSMHandle->pWDHandle->StackClass == Stack_Primary) ||
(pRealSMHandle->pWDHandle->StackClass == Stack_Shadow)) {
if (pRealSMHandle->encryptionMethodSelected == SM_FIPS_ENCRYPTION_FLAG) {
rc = TSFIPS_MakeSessionKeys(&(pRealSMHandle->FIPSData), (LPRANDOM_KEYS_PAIR)&pSecInfo->KeyPair, NULL, FALSE);
}
else {
rc = MakeSessionKeys(
(LPRANDOM_KEYS_PAIR)&pSecInfo->KeyPair,
pRealSMHandle->startEncryptKey,
&pRealSMHandle->rc4EncryptKey,
pRealSMHandle->startDecryptKey,
&pRealSMHandle->rc4DecryptKey,
pRealSMHandle->macSaltKey,
pRealSMHandle->encryptionMethodSelected,
&pRealSMHandle->keyLength,
pRealSMHandle->encryptionLevel );
}
}
// Passthru stack looks like a client
else {
if (pRealSMHandle->encryptionMethodSelected == SM_FIPS_ENCRYPTION_FLAG) {
rc = TSFIPS_MakeSessionKeys(&(pRealSMHandle->FIPSData), (LPRANDOM_KEYS_PAIR)&pSecInfo->KeyPair, NULL, TRUE);
}
else {
rc = MakeSessionKeys(
(LPRANDOM_KEYS_PAIR)&pSecInfo->KeyPair,
pRealSMHandle->startDecryptKey,
&pRealSMHandle->rc4DecryptKey,
pRealSMHandle->startEncryptKey,
&pRealSMHandle->rc4EncryptKey,
pRealSMHandle->macSaltKey,
pRealSMHandle->encryptionMethodSelected,
&pRealSMHandle->keyLength,
pRealSMHandle->encryptionLevel );
}
}
if (!rc) {
TRC_ERR((TB, "Could not create session keys!"));
status = STATUS_UNSUCCESSFUL;
DC_QUIT;
}
/************************************************************************/
/* validate the key length returned. */
/************************************************************************/
if (pRealSMHandle->encryptionMethodSelected != SM_FIPS_ENCRYPTION_FLAG) {
if (pRealSMHandle->encryptionMethodSelected == SM_128BIT_ENCRYPTION_FLAG) {
TRC_ASSERT((pRealSMHandle->keyLength == MAX_SESSION_KEY_SIZE),
(TB,"Invalid session key length"));
}
else {
TRC_ASSERT((pRealSMHandle->keyLength == (MAX_SESSION_KEY_SIZE / 2)),
(TB,"Invalid session key length"));
}
/************************************************************************/
/* copy start session key to current. */
/************************************************************************/
memcpy(
pRealSMHandle->currentEncryptKey,
pRealSMHandle->startEncryptKey,
MAX_SESSION_KEY_SIZE);
memcpy(
pRealSMHandle->currentDecryptKey,
pRealSMHandle->startDecryptKey,
MAX_SESSION_KEY_SIZE);
}
pRealSMHandle->encryptCount = 0;
pRealSMHandle->decryptCount = 0;
pRealSMHandle->totalDecryptCount = 0;
pRealSMHandle->totalEncryptCount = 0;
pRealSMHandle->bSessionKeysMade = TRUE;
// Whenever we change the state of encrypting, we need to make sure
// we get the right header size for buffer allocations. If encrypting
// if FALSE, the header size is zero.
pRealSMHandle->encrypting = TRUE;
if (pRealSMHandle->encryptionLevel == 1) {
pRealSMHandle->encryptHeaderLen = sizeof(RNS_SECURITY_HEADER);
pRealSMHandle->encryptDisplayData = FALSE;
if (pRealSMHandle->encryptionMethodSelected == SM_FIPS_ENCRYPTION_FLAG)
pRealSMHandle->encryptHeaderLenIfForceEncrypt = sizeof(RNS_SECURITY_HEADER2);
else
pRealSMHandle->encryptHeaderLenIfForceEncrypt = sizeof(RNS_SECURITY_HEADER1);
}
else if (pRealSMHandle->encryptionLevel >= 2) {
if (pRealSMHandle->encryptionMethodSelected == SM_FIPS_ENCRYPTION_FLAG)
pRealSMHandle->encryptHeaderLen = sizeof(RNS_SECURITY_HEADER2);
else
pRealSMHandle->encryptHeaderLen = sizeof(RNS_SECURITY_HEADER1);
pRealSMHandle->encryptDisplayData = TRUE;
}
TRC_ALT((TB, "%s stack -> encryption %s: level=%ld, method=%ld, display=%ld",
pRealSMHandle->pWDHandle->StackClass == Stack_Primary ? "Primary" :
(pRealSMHandle->pWDHandle->StackClass == Stack_Shadow ? "Shadow" :
"PassThru"),
rc ? "ON" : "OFF",
pRealSMHandle->encryptionLevel,
pRealSMHandle->encryptionMethodSelected,
pRealSMHandle->encryptDisplayData));
/************************************************************************/
/* Remember the certificate type that was used for the key exchange */
/************************************************************************/
pRealSMHandle->CertType = pSecInfo->CertType;
/************************************************************************/
/* All worked OK */
/************************************************************************/
status = STATUS_SUCCESS;
DC_EXIT_POINT:
DC_END_FN();
return (status);
}
/****************************************************************************/
/* Name: SM_SetEncryptionParams */
/* */
/* Purpose: Updates the encryption level and method. Used to set */
/* negotiated encryption level for passthru stack. */
/* */
/* Params: pSMHandle - SM Handle */
/* ulLevel - negotiated encryption level */
/* ulMethod - negotiated encryption method */
/****************************************************************************/
NTSTATUS RDPCALL SM_SetEncryptionParams(
PVOID pSMHandle,
ULONG ulLevel,
ULONG ulMethod)
{
PSM_HANDLE_DATA pRealSMHandle = (PSM_HANDLE_DATA)pSMHandle;
DC_BEGIN_FN("SM_SetEncryptionParams");
// The passthru stack is simulating a client for the target server. As
// such, if we negotiated client input encryption, we've got to turn on
// output encryption for this stack since it conveys all client input
// to the target server.
if (ulLevel == 1) {
ulLevel = 2;
TRC_ALT((TB, "Passthru stack switching to duplex encryption."));
}
pRealSMHandle->encryptionLevel = ulLevel;
pRealSMHandle->encryptionMethodSelected = ulMethod;
DC_END_FN();
return STATUS_SUCCESS;
}
/****************************************************************************/
/* Name: SM_IsSecurityExchangeCompleted */
/* */
/* Purpose: Ask SM if the security exchange protocol has already been */
/* completed. */
/* */
/* Returns: TRUE if the protocol has already been completed or FALSE */
/* otherwise. */
/* */
/* Params: pSMHandle - SM Handle */
/* pCertType - The type of certificate that is used in the */
/* security exchange. */
/****************************************************************************/
BOOL RDPCALL SM_IsSecurityExchangeCompleted(
PVOID pSMHandle,
CERT_TYPE *pCertType)
{
PSM_HANDLE_DATA pRealSMHandle = (PSM_HANDLE_DATA)pSMHandle;
DC_BEGIN_FN("SM_IsSecurityExchangeCompleted");
*pCertType = pRealSMHandle->CertType;
DC_END_FN();
return pRealSMHandle->encrypting;
}
#ifdef DC_DEBUG
/****************************************************************************/
// SMCheckState
//
// Debug-only embodiment of the SM_CHECK_STATE logic.
/****************************************************************************/
BOOL RDPCALL SMCheckState(PSM_HANDLE_DATA pRealSMHandle, unsigned event)
{
BOOL rc;
DC_BEGIN_FN("SMCheckState");
if (smStateTable[event][pRealSMHandle->state] == SM_TABLE_OK) {
rc = TRUE;
}
else {
rc = FALSE;
if (smStateTable[event][pRealSMHandle->state] == SM_TABLE_WARN) {
TRC_ALT((TB, "Unusual event %s in state %s",
smEventName[event], smStateName[pRealSMHandle->state]));
}
else {
TRC_ABORT((TB, "Invalid event %s in state %s",
smEventName[event], smStateName[pRealSMHandle->state]));
}
}
DC_END_FN();
return rc;
}
#endif
/****************************************************************************/
/* Name: SM_SetSafeChecksumMethod
/*
/* Purpose: Sets flag to use safe checksum style
/*
/* Params: pSMHandle - SM Handle
// fEncryptChecksummedData -
/****************************************************************************/
NTSTATUS RDPCALL SM_SetSafeChecksumMethod(
PVOID pSMHandle,
BOOLEAN fSafeChecksumMethod
)
{
PSM_HANDLE_DATA pRealSMHandle = (PSM_HANDLE_DATA)pSMHandle;
DC_BEGIN_FN("SM_SetSafeChecksumMethod");
pRealSMHandle->useSafeChecksumMethod = fSafeChecksumMethod;
DC_END_FN();
return STATUS_SUCCESS;
}