/****************************************************************************/ /* asmapi.c */ /* */ /* Security Manager API */ /* */ /* Copyright (C) 1997-1999 Microsoft Corporation */ /****************************************************************************/ #include #pragma hdrstop #define TRC_FILE "asmapi" #define pTRCWd (pRealSMHandle->pWDHandle) #include #include #include #include #include #include #include #include #define DC_INCLUDE_DATA #include #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; }