/****************************************************************************/ // acpcapi.cpp // // Capabilities Coordinator API functions. // // Copyright(c) Microsoft, PictureTel 1992-1996 // (C) 1997-1999 Microsoft Corp. /****************************************************************************/ #include #pragma hdrstop #define TRC_FILE "acpcapi" #include /****************************************************************************/ /* API FUNCTION: CPC_Init */ /* */ /* Initializes the Capabilities Coordinator. */ /****************************************************************************/ void RDPCALL SHCLASS CPC_Init(void) { DC_BEGIN_FN("CPC_Init"); /************************************************************************/ /* This initializes all the global data for this component */ /************************************************************************/ #define DC_INIT_DATA #include #undef DC_INIT_DATA // Set up pointer to presized memory buffer and set initial size value // since the presized buffer is uninitialized. cpcLocalCombinedCaps = (PTS_COMBINED_CAPABILITIES)cpcLocalCaps; cpcLocalCombinedCaps->numberCapabilities = 0; DC_END_FN(); } /****************************************************************************/ /* API FUNCTION: CPC_Term */ /* */ /* Terminates the Capabilities Coordinator. */ /****************************************************************************/ void RDPCALL SHCLASS CPC_Term(void) { unsigned i; DC_BEGIN_FN("CPC_Term"); /************************************************************************/ /* Free capabilities for each party */ /************************************************************************/ for (i = 0; i < SC_DEF_MAX_PARTIES; i++) { TRC_NRM((TB, "Free data for party %d", i)); if (cpcRemoteCombinedCaps[i] != NULL) COM_Free(cpcRemoteCombinedCaps[i]); } DC_END_FN(); } /****************************************************************************/ /* API FUNCTION: CPC_RegisterCapabilities */ /* */ /* Called at initialisation time by each component that has capabilities */ /* which need to be negotiated across the share. This is used to register */ /* all capabilities. */ /* */ /* PARAMETERS: */ /* pCapabilities - pointer to a structure containing the capabilities ID */ /* and any number of capability fields. */ /* The values used in these fields should be non-zero. A */ /* zero in any capability field is used to indicate that */ /* the capability is either unknown or undefined by the */ /* remote. */ /* */ /* sizeOfCaps - the size of the total capabilities. The limit on the */ /* total size of all secondary capabilities is 300 bytes, */ /* but this is imposed locally and could be increased */ /* without harming interoperability. */ /****************************************************************************/ void RDPCALL SHCLASS CPC_RegisterCapabilities( PTS_CAPABILITYHEADER pCapabilities, UINT16 sizeOfCaps) { unsigned i; PTS_CAPABILITYHEADER pNextCaps; DC_BEGIN_FN("CPC_RegisterCapabilities"); TRC_NRM((TB, "Registering capabilities ID %hd, size %hd", pCapabilities->capabilitySetType, sizeOfCaps)); #ifdef DC_DEBUG /************************************************************************/ /* Check that CPC_GetCombinedCapabilities has not already been called. */ /************************************************************************/ if (cpcLocalCombinedCapsQueried) { TRC_ERR((TB, "CPC_GetCombinedCapabilities has already been called")); } #endif /************************************************************************/ /* Register capabilities (if any) */ /************************************************************************/ if (sizeOfCaps != 0) { pCapabilities->lengthCapability = sizeOfCaps; // Search for the end of the capabilities structure. pNextCaps = (PTS_CAPABILITYHEADER)&(cpcLocalCombinedCaps->data[0]); for (i = 0; i < cpcLocalCombinedCaps->numberCapabilities; i++) pNextCaps = (PTS_CAPABILITYHEADER)((PBYTE)pNextCaps + pNextCaps->lengthCapability); // Check that we have enough room in our combined capabilities // structure to add the new capabilities. if (((PBYTE)pNextCaps - (PBYTE)cpcLocalCombinedCaps + pCapabilities->lengthCapability) <= CPC_MAX_LOCAL_CAPS_SIZE) { // Copy across the new capabilities into our combined capabilities // structure. memcpy(pNextCaps, pCapabilities, pCapabilities->lengthCapability); // Update the number of capabilities in our combined capabilities // structure. cpcLocalCombinedCaps->numberCapabilities++; TRC_DBG((TB, "Added %d bytes to capabilities for ID %d", pCapabilities->lengthCapability, pCapabilities->capabilitySetType)); } else { // We do not have enough room to add the capabilities so return. // Any system communicating with us will think we do not support // these capabilities. The size of the capabilities structure // can be increased (it is not limited as part of the protocol). // The value of CPC_MAX_LOCAL_CAPS_SIZE should be increased. TRC_ERR((TB,"Out of combined capabilities space ID %d; size %d", pCapabilities->capabilitySetType, pCapabilities->lengthCapability)); } } DC_END_FN(); } /****************************************************************************/ /* API FUNCTION: CPC_EnumerateCapabilities */ /* */ /* Enumerates the capabilities for each node in the share (not including */ /* local). */ /* */ /* PARAMETERS: */ /* capabilitiesID - the ID of the capabilities (group structure) to be */ /* enumerated. */ /* UserData - Private caller data to be passed to each call of the enum */ /* func. */ /* pCapsEnumerateFN - function to be called for each person in the share */ /* with the persons capabilities structure. */ /****************************************************************************/ void RDPCALL SHCLASS CPC_EnumerateCapabilities( unsigned capabilitiesID, UINT_PTR UserData, PCAPSENUMERATEFN pCapsEnumerateFN) { LOCALPERSONID localID; unsigned i; BOOL foundCapabilities; PTS_CAPABILITYHEADER pCaps; TS_CAPABILITYHEADER emptyCaps; DC_BEGIN_FN("CPC_EnumerateCapabilities"); /************************************************************************/ /* Search for the capabilities ID within the remote party's section of */ /* the combined capabilities structure. */ /************************************************************************/ for (localID = SC_DEF_MAX_PARTIES - 1; localID >= 1; localID--) { if (cpcRemoteCombinedCaps[localID-1] != NULL) { pCaps = (PTS_CAPABILITYHEADER) &(cpcRemoteCombinedCaps[localID-1]->data[0]); for (i = 0, foundCapabilities = FALSE; i < cpcRemoteCombinedCaps[localID-1]->numberCapabilities; i++) { if (pCaps->capabilitySetType == capabilitiesID) { /********************************************************/ /* We have found the capabilities structure requested. */ /* Make the call to the enumeration callback function. */ /********************************************************/ foundCapabilities = TRUE; (this->*pCapsEnumerateFN)(localID, UserData, pCaps); /********************************************************/ /* Go onto the next person. */ /********************************************************/ break; } pCaps = (PTS_CAPABILITYHEADER)((PBYTE)pCaps + pCaps->lengthCapability); } if (!foundCapabilities) { /************************************************************/ /* We did not find the requested capability structure for */ /* this party so we must return an empty one. */ /************************************************************/ emptyCaps.capabilitySetType = (UINT16)capabilitiesID; emptyCaps.lengthCapability = 0; /************************************************************/ /* Call the enumeration function callback with the empty */ /* capabilities for this personID. */ /************************************************************/ (this->*pCapsEnumerateFN)(localID, UserData, &emptyCaps); } } } DC_END_FN(); } /****************************************************************************/ /* API FUNCTION: CPC_GetCombinedCapabilities */ /* */ /* Called by the Share Controller (SC). Returns a pointers to structures */ /* containing the combined capabilities of all the registered capabilities. */ /* */ /* Note that this relies on the initialisation order of the components. */ /* The CPC must be initialized before any components with capabilities. */ /* Any components with capabilities must register them at initialisation */ /* time. The SC must be initialized after any components with */ /* capabilities. */ /* */ /* PARAMETERS: */ /* localID - local ID of the person we are interested in. */ /* pSizeOfCaps - pointer to variable to be filled in with the size of the */ /* combined capabilities structure returned as ppCaps. */ /* */ /* ppCaps - pointer to variable to be filled in with the pointer to the */ /* combined capabilities structure containing capabilities passed to */ /* CPC_RegisterCapabilities. */ /****************************************************************************/ void RDPCALL SHCLASS CPC_GetCombinedCapabilities( LOCALPERSONID localID, PUINT pSizeOfCaps, PTS_COMBINED_CAPABILITIES *ppCaps) { unsigned i; PTS_CAPABILITYHEADER pNextCaps; PTS_COMBINED_CAPABILITIES pCaps; unsigned numCaps; DC_BEGIN_FN("CPC_GetCombinedCapabilities"); /************************************************************************/ /* Try to find the requested capabilitiesID for this person. */ /* */ /* If the localID refers to the local system then search the combined */ /* capabilities structure (ie all capabilities registered with */ /* CPC_RegisterCapabilities). Otherwise search the structure we */ /* received from the remote person. */ /************************************************************************/ if (localID == SC_LOCAL_PERSON_ID) { pCaps = cpcLocalCombinedCaps; numCaps = cpcLocalCombinedCaps->numberCapabilities; #ifdef DC_DEBUG /************************************************************************/ /* Set our flag we use to check that CPC_Register is not called after */ /* this function has been called. */ /************************************************************************/ cpcLocalCombinedCapsQueried = TRUE; #endif } else { if (cpcRemoteCombinedCaps[localID - 1] != NULL) { pCaps = cpcRemoteCombinedCaps[localID - 1]; numCaps = cpcRemoteCombinedCaps[localID - 1]->numberCapabilities; } else { TRC_ERR((TB, "Capabilities pointer is NULL")); *pSizeOfCaps = 0; *ppCaps = NULL; DC_QUIT; } } /************************************************************************/ /* Search for the end of the capabilities structure for the local */ /* party. */ /************************************************************************/ TRC_DBG((TB, "Caps:")); pNextCaps = (PTS_CAPABILITYHEADER)&(pCaps->data[0]); for (i = 0; i < numCaps; i++) { TRC_DBG((TB, "caps size %hd", pNextCaps->lengthCapability)); TRC_DBG((TB, "caps ID %hd", pNextCaps->capabilitySetType)); pNextCaps = (PTS_CAPABILITYHEADER)( (PBYTE)pNextCaps + pNextCaps->lengthCapability ); } *pSizeOfCaps = (unsigned)((PBYTE)pNextCaps - (PBYTE)pCaps); *ppCaps = pCaps; TRC_NRM((TB, "Total size %d", *pSizeOfCaps)); DC_EXIT_POINT: DC_END_FN(); } /****************************************************************************/ /* CPC_SetCombinedCapabilities(..) */ /* */ /* Used by a shadow stack to initialize the combined capabilities to the */ /* values negotiated so far by its predecessors. */ /****************************************************************************/ void RDPCALL SHCLASS CPC_SetCombinedCapabilities( UINT cbSizeOfCaps, PTS_COMBINED_CAPABILITIES pCaps) { unsigned i; PTS_CAPABILITYHEADER pNextCaps; DC_BEGIN_FN("CPC_SetCombinedCapabilities"); /************************************************************************/ /* Replace the existing capability set with the new values */ /************************************************************************/ cpcLocalCombinedCaps->numberCapabilities = 0; pNextCaps = (PTS_CAPABILITYHEADER)&(pCaps->data[0]); TRC_NRM((TB, "Caps:")); for (i = 0; i < pCaps->numberCapabilities; i++) { CPC_RegisterCapabilities(pNextCaps, pNextCaps->lengthCapability); pNextCaps = (PTS_CAPABILITYHEADER)( (PBYTE)pNextCaps + pNextCaps->lengthCapability ); } TRC_ALT((TB, "Capability bytes accepted: %ld / %ld", (unsigned)((PBYTE)pNextCaps - (PBYTE)cpcLocalCombinedCaps), cbSizeOfCaps)); DC_END_FN(); } /****************************************************************************/ /* API FUNCTION: CPC_PartyJoiningShare() */ /* */ /* Capabilities Coordinator function called when a new party is joining the */ /* share. */ /* */ /* Note that the capabilities data is still in wire format */ /* (Intel byte order) when CPC_PartyJoiningShare is called. */ /* */ /* PARAMETERS: */ /* */ /* personID - local person ID of remote person joining the share. */ /* */ /* oldShareSize - the number of the parties which were in the share (ie */ /* excludes the joining party). */ /* */ /* sizeOfCapsData - size of the data pointed to by pCapsData. */ /* */ /* pCapsData - pointer to capabilities (returned by the person's */ /* CPC_GetCombinedCapabilities) data for NET_EV_PERSON_ADDs. For the other */ /* events this is NULL. */ /* */ /* RETURNS: TRUE if the party can join the share. */ /* FALSE if the party can NOT join the share. */ /****************************************************************************/ BOOL RDPCALL SHCLASS CPC_PartyJoiningShare( LOCALPERSONID personID, unsigned oldShareSize, unsigned sizeOfCapsData, PVOID pCapsData) { PTS_COMBINED_CAPABILITIES pCombinedCaps; PBYTE pCaps; PBYTE pSavedCaps; BOOL rc = TRUE; int i; UINT32 sizeOfCaps = FIELDOFFSET(TS_COMBINED_CAPABILITIES, data); UINT32 work; DC_BEGIN_FN("CPC_PartyJoiningShare"); DC_IGNORE_PARAMETER(oldShareSize) /************************************************************************/ /* Allow ourself to be added to the share, but do nothing else. */ /************************************************************************/ if (personID == SC_LOCAL_PERSON_ID) { TRC_DBG((TB, "Ignore adding self to share")); DC_QUIT; } /************************************************************************/ /* Calculate actual space required to save capabilities */ /************************************************************************/ // First we check if actually can deref the numberCapabilities member. // We should have enough bytes till up to the data meber. if(sizeOfCapsData < FIELDOFFSET(TS_COMBINED_CAPABILITIES, data)){ TRC_ERR((TB, "Buffer too small to fit a combined caps structure: %d", sizeOfCapsData)); WDW_LogAndDisconnect(m_pTSWd, TRUE, Log_RDP_CapabilitySetTooSmall, (PBYTE)pCapsData, sizeOfCapsData); rc = FALSE; DC_QUIT; } pCombinedCaps = (PTS_COMBINED_CAPABILITIES)pCapsData; pCaps = (PBYTE)pCombinedCaps->data; for (i = 0; i < pCombinedCaps->numberCapabilities; i++) { // here we check if we still have left TS_CAPABILITYHEADER length worth of data // we can't just deref the length member without checking that we actually have // enough buffer for a TS_CAPABILITYHEADER if ((PBYTE)pCaps + sizeof(TS_CAPABILITYHEADER) > (PBYTE)pCapsData + sizeOfCapsData) { TRC_ERR((TB, "Not enough space left for a capability header: %d", sizeOfCapsData-(pCaps-(PBYTE)pCapsData) )); WDW_LogAndDisconnect(m_pTSWd, TRUE, Log_RDP_CapabilitySetTooSmall, (PBYTE)pCapsData, sizeOfCapsData); rc = FALSE; DC_QUIT; } work = (UINT32)(((PTS_CAPABILITYHEADER)pCaps)->lengthCapability); /********************************************************************/ /* Reject capability sets whose length is too small to contain any */ /* data */ /********************************************************************/ if (work <= sizeof(TS_CAPABILITYHEADER)) { TRC_ERR((TB, "Capability set too small: %d", work)); WDW_LogAndDisconnect(m_pTSWd, TRUE, Log_RDP_CapabilitySetTooSmall, (PBYTE)pCapsData, sizeOfCapsData); rc = FALSE; DC_QUIT; } /********************************************************************/ /* Reject capability sets whose length would overrun the end of the */ /* packet. */ /********************************************************************/ if ((pCaps+work> (PBYTE)pCapsData + sizeOfCapsData) || (pCaps+work < (PBYTE)pCaps)) { TRC_ERR((TB, "Capability set too large: %d", work)); WDW_LogAndDisconnect(m_pTSWd, TRUE, Log_RDP_CapabilitySetTooLarge, (PBYTE)pCapsData, sizeOfCapsData); rc = FALSE; DC_QUIT; } pCaps += work; work = (UINT32)DC_ROUND_UP_4(work); sizeOfCaps += work; } TRC_NRM((TB, "Caps size: passed %d, actual %d", sizeOfCapsData, sizeOfCaps)); /************************************************************************/ /* Allocate the space for this person's capabilities. */ /************************************************************************/ cpcRemoteCombinedCaps[personID - 1] = (PTS_COMBINED_CAPABILITIES)COM_Malloc(sizeOfCaps); if (cpcRemoteCombinedCaps[personID - 1] == NULL) { /********************************************************************/ /* This party cannot join the share. */ /********************************************************************/ TRC_NRM((TB, "Failed to get %d bytes for personID %d caps", sizeOfCapsData, personID)); rc = FALSE; DC_QUIT; } TRC_DBG((TB, "Allocated %d bytes for personID %d caps", sizeOfCapsData, personID)); /************************************************************************/ /* Initialize the memory to zero. Otherwise we can get little gaps of */ /* garbage - which can be interpreted as valid capabilities - when */ /* copying non-end-padded capability entries from the remote party's */ /* data. */ /************************************************************************/ memset(cpcRemoteCombinedCaps[personID-1], 0, sizeOfCaps); /************************************************************************/ /* Copy the combined capabilities data. */ /************************************************************************/ /************************************************************************/ /* Copy the combined capabilities header */ /************************************************************************/ memcpy( cpcRemoteCombinedCaps[personID-1], pCapsData, FIELDOFFSET(TS_COMBINED_CAPABILITIES, data)); /************************************************************************/ /* Loop through capabilities, copying them to 4-byte aligned positions */ /************************************************************************/ pSavedCaps = (PBYTE)(cpcRemoteCombinedCaps[personID-1]->data); pCaps = (PBYTE)((PBYTE)pCapsData + FIELDOFFSET(TS_COMBINED_CAPABILITIES, data)); for (i = 0; i < pCombinedCaps->numberCapabilities; i++) { work = (UINT32)(((PTS_CAPABILITYHEADER)pCaps)->lengthCapability); memcpy( pSavedCaps, pCaps, work); pCaps += work; work = (UINT32)DC_ROUND_UP_4(work); ((PTS_CAPABILITYHEADER)pSavedCaps)->lengthCapability = (UINT16)work; pSavedCaps += work; } DC_EXIT_POINT: DC_END_FN(); return rc; } /****************************************************************************/ /* API FUNCTION: CPC_PartyLeftShare() */ /* */ /* Capabilities Coordinator function called when a party has left the */ /* share. */ /* */ /* PARAMETERS: */ /* personID - local person ID of remote person leaving the share. */ /* */ /* newShareSize - the number of the parties now in the share (ie excludes */ /* the leaving party). */ /****************************************************************************/ void RDPCALL SHCLASS CPC_PartyLeftShare(LOCALPERSONID locPersonID, unsigned newShareSize) { DC_BEGIN_FN("CPC_PartyLeftShare"); DC_IGNORE_PARAMETER(newShareSize) /************************************************************************/ /* If this is ourself leaving the share do nothing. */ /************************************************************************/ if (locPersonID != SC_LOCAL_PERSON_ID) { // Free this party's capabilities from the array and mark the entry // as invalid. if (cpcRemoteCombinedCaps[locPersonID - 1] != NULL) { COM_Free((PVOID)cpcRemoteCombinedCaps[locPersonID-1]); cpcRemoteCombinedCaps[locPersonID - 1] = NULL; } } DC_END_FN(); } /****************************************************************************/ /* FUNCTION: CPCGetCapabilities */ /* */ /* Returns the capabilities for one person. */ /* */ /* PARAMETERS: */ /* localID - local ID of person (0 == local person) */ /* capabilitiesID - the ID of the capabilities (group structure) to be */ /* queried. */ /* */ /* RETURNS: */ /* Pointer to a structure containing the capabilities ID, the size of the */ /* capabilities, and any number of capability fields. */ /* */ /* If the person has no capabilities with capabilitiesID, a NULL pointer is */ /* returned. */ /****************************************************************************/ PTS_CAPABILITYHEADER RDPCALL SHCLASS CPCGetCapabilities( LOCALPERSONID localID, unsigned capabilitiesID) { unsigned i; unsigned numCaps; PTS_CAPABILITYHEADER pCaps; PTS_CAPABILITYHEADER rc = NULL; DC_BEGIN_FN("CPCGetCapabilities"); /************************************************************************/ /* Try to find the requested capabilitiesID for this person. */ /* */ /* If the localID refers to the local system then search the combined */ /* capabilities structure (ie all capabilities registered with */ /* CPC_RegisterCapabilities). Otherwise search the structure we */ /* received from the remote person. */ /************************************************************************/ if (localID == 0) { pCaps = (PTS_CAPABILITYHEADER)&(cpcLocalCombinedCaps->data[0]); numCaps = cpcLocalCombinedCaps->numberCapabilities; } else { if (cpcRemoteCombinedCaps[localID-1] == NULL) { TRC_ERR((TB, "Capabilities pointer is NULL")); DC_QUIT; } pCaps = (PTS_CAPABILITYHEADER) &(cpcRemoteCombinedCaps[localID-1]->data[0]); numCaps = cpcRemoteCombinedCaps[localID-1]->numberCapabilities; } for (i = 0; i < numCaps; i++) { if (pCaps->capabilitySetType == capabilitiesID) { /****************************************************************/ /* We have found the capabilities structure requested. Return */ /* the address of the capabilities. */ /****************************************************************/ TRC_DBG((TB, "Found %d bytes of caps ID %d localID %d", pCaps->lengthCapability, capabilitiesID, localID)); rc = pCaps; break; } pCaps = (PTS_CAPABILITYHEADER)( (PBYTE)pCaps + pCaps->lengthCapability ); } if (rc == NULL) { TRC_NRM((TB, " local ID = %u : No caps found for ID %d", (unsigned)localID, capabilitiesID)); } DC_EXIT_POINT: DC_END_FN(); return rc; }