/*++ Copyright (C) Microsoft Corporation, 1991 - 1999 Module Name: tower.c Abstract: This file contains encoding/decoding for the tower representation of the binding information that DCE runtime uses. TowerConstruct/TowerExplode will be called by the Runtime EpResolveBinding on the client side, TowerExplode will be called by the Endpoint Mapper and in addition the name service may call TowerExplode, TowerConstruct. Author: Bharat Shah (barats) 3-23-92 KamenM 05/02/2002 Major security and cleanup changes Revision History: --*/ #include #include #include #include // // TowerVerify() defines. // // Note: DECnet Protocol uses 6 Floors, all others 5. // #define MAX_FLOOR_COUNT 6 #define MIN_FLOOR_COUNT 3 #ifndef UNALIGNED #error UNALIGNED not defined by sysinc.h or its includes and is needed. #endif #pragma pack(1) /* Some Lrpc specific stuff */ #define NP_TRANSPORTID_LRPC 0x10 #define NP_TOWERS_LRPC 0x04 // "ncalrpc" including the NULL terminator const int LrpcProtocolSequenceLength = 8; RPC_STATUS Floor0or1ToId( IN PFLOOR_0OR1 Floor, OUT PGENERIC_ID Id ) /*++ Routine Description: This function extracts Xfer Syntax or If info from a a DCE tower Floor 0 or 1 encoding Arguments: Floor - A pointer to Floor0 or Floor 1 encoding Id - on output, the transfer syntax or interface id Return Value: EP_S_CANT_PERFORM_OP - The Encoding for the floor [0 or 1] is incorrect. N.B. Caller must have verified that the whole floor is present (i.e. we can't walk off the end of the buffer) --*/ { if (Floor->FloorId != UUID_ENCODING) { return (EP_S_CANT_PERFORM_OP); } RpcpMemoryCopy((char PAPI *)&Id->Uuid, (char PAPI *) &Floor->Uuid, sizeof(UUID)); Id->MajorVersion = Floor->MajorVersion; Id->MinorVersion = Floor->MinorVersion; return(RPC_S_OK); } RPC_STATUS CopyIdToFloor( OUT PFLOOR_0OR1 Floor, IN PGENERIC_ID Id ) /*++ Routine Description: This function constructs FLOOR 0 or 1 from the given IF-id or Transfer Syntax Id. Arguments: Floor - Pointer to Floor 0 or 1 structure that will be constructed Id - Pointer to If-id or Xfer Syntax Id. Return Value: RPC_S_OK --*/ { Floor->FloorId = UUID_ENCODING; Floor->ProtocolIdByteCount = sizeof(Floor->Uuid) + sizeof(Floor->FloorId) + sizeof(Floor->MajorVersion); Floor->AddressByteCount = sizeof(Floor->MinorVersion); RpcpMemoryCopy((char PAPI *) &Floor->Uuid, (char PAPI *) &Id->Uuid, sizeof(UUID)); Floor->MajorVersion = Id->MajorVersion; Floor->MinorVersion = Id->MinorVersion; return(RPC_S_OK); } BYTE * VerifyFloorLHSAndRHS ( IN BYTE *FloorStart, IN BYTE *UpperBound, IN ULONG FloorNum ) /*++ Routine Description: Verifies that the left hand side (LHS) byte count and the right hand side (RHS) byte count in the given floor are accurate with respect to the floor start and size (i.e. jumping from the LHS we go to the right hand side, and from there, to the end of the floor without walking off the end). Arguments: FloorStart - the start of the current floor. UpperBound - pointer to first invalid byte after the buffer. FloorNum - the number of the floor. Used for debugging only Return Value: The position of the next floor, or NULL if the floor fails the check. --*/ { USHORT LHSBytes, RHSBytes; if (FloorStart + 2 >= UpperBound) return NULL; LHSBytes = *(unsigned short UNALIGNED *)FloorStart; if (FloorStart + (ULONG)LHSBytes + 4 >= UpperBound) // the LHSBytes counter itself is 2 bytes, and we { // must provide at least 2 bytes for the RHSBytes #ifdef DEBUGRPC PrintToDebugger("RPC: TowerVerify(): LHS of Tower floor %d " "greater than equal to FloorSize\n", FloorNum); #endif // DEBUGRPC return NULL; } FloorStart += LHSBytes + 2; RHSBytes = *(unsigned short UNALIGNED *)FloorStart; if (FloorStart + (ULONG)RHSBytes + 2 > UpperBound) // the RHSBytes counter itself is 2 bytes { #ifdef DEBUGRPC PrintToDebugger("RPC: TowerVerify(): RHS of Tower floor %d " "greater than equal to FloorSize\n", FloorNum); #endif // DEBUGRPC return NULL; } return FloorStart + RHSBytes + 2; } RPC_STATUS ExtractStringFromUntrustedPacket ( IN BYTE *UpperBound, IN ULONG ClaimedStringLength, IN char *StringStart, OUT char **ExtractedString ) /*++ Routine Description: Extracts a string from untrusted packet. Verifies string boundaries, reasonable length (at least one character and no more than 1000), and null termination. Arguments: UpperBound - the first invalid byte after the end of the buffer. ClaimedStringLength - the string length that the packet claims. StringStart - the alleged start of the string - untrusted. ExtractedString - the extracted string. Must be freed with delete. Undefined on failure. Return Value: RPC_S_OK, RPC_S_OUT_OF_MEMORY or EP_S_CANT_PERFORM_OP --*/ { char *NewString; if ((ClaimedStringLength < 2) || (ClaimedStringLength > 1000)) return EP_S_CANT_PERFORM_OP; if (StringStart + ClaimedStringLength > (char *)UpperBound) return EP_S_CANT_PERFORM_OP; // the claimed string length includes the terminating NULL if (StringStart[ClaimedStringLength - 1] != '\0') return EP_S_CANT_PERFORM_OP; NewString = new char [ClaimedStringLength]; if (NewString == NULL) return RPC_S_OUT_OF_MEMORY; RpcpMemoryCopy(NewString, StringStart, ClaimedStringLength); *ExtractedString = NewString; return RPC_S_OK; } RPC_STATUS LrpcTowerConstruct( IN char PAPI * Endpoint, OUT unsigned short PAPI UNALIGNED * Floors, OUT unsigned long PAPI UNALIGNED * ByteCount, OUT unsigned char PAPI * UNALIGNED PAPI * Tower ) { unsigned long TowerSize; FLOOR_234 UNALIGNED *Floor; int EndpointLength; *Floors = NP_TOWERS_LRPC; if ((Endpoint == NULL) || (*Endpoint == '\0')) { EndpointLength = 0; } else { EndpointLength = RpcpStringLengthA(Endpoint) + 1; } if (EndpointLength == 0) TowerSize = 2; else TowerSize = EndpointLength; TowerSize += sizeof(FLOOR_234) - 2; *ByteCount = TowerSize; if ((*Tower = (unsigned char *)I_RpcAllocate(TowerSize)) == NULL) { return (RPC_S_OUT_OF_MEMORY); } Floor = (PFLOOR_234) *Tower; Floor->ProtocolIdByteCount = 1; Floor->FloorId = (unsigned char)(NP_TRANSPORTID_LRPC & 0xFF); if (EndpointLength) { Floor->AddressByteCount = (unsigned short)EndpointLength; RpcpMemoryCopy((char PAPI *)&Floor->Data[0], Endpoint, EndpointLength); } else { Floor->AddressByteCount = 2; Floor->Data[0] = 0; } return(RPC_S_OK); } RPC_STATUS LrpcTowerExplode( IN PFLOOR_234 Floor34, IN BYTE *UpperBound, IN ULONG RemainingFloors, OUT char ** Protseq, OPTIONAL OUT char ** Endpoint, OPTIONAL OUT char ** NetworkAddress OPTIONAL ) { BYTE *CurrentPosition; BYTE *NextFloor; RPC_STATUS RpcStatus; ULONG EndpointLength; // Initialize all our OUT pointers so we don't end up deleting uninitialized memory if (Protseq) *Protseq = NULL; if (Endpoint) *Endpoint = NULL; // we expect only one floor more if (RemainingFloors != 1) return EP_S_CANT_PERFORM_OP; if (Protseq != NULL) { *Protseq = new char[LrpcProtocolSequenceLength]; if (*Protseq == NULL) { return(RPC_S_OUT_OF_MEMORY); } RpcpMemoryCopy(*Protseq, "ncalrpc", LrpcProtocolSequenceLength); } if (Endpoint == NULL) { return (RPC_S_OK); } CurrentPosition = (BYTE *)Floor34; // an endpoint must be at least two chars long (null terminator included). // Since FLOOR_234 has space for 2 chars, we need to subtract one if (CurrentPosition + sizeof(FLOOR_234) >= UpperBound) return EP_S_CANT_PERFORM_OP; NextFloor = VerifyFloorLHSAndRHS (CurrentPosition, UpperBound, 3 // FloorNum ); if (NextFloor == NULL) { RpcStatus = EP_S_CANT_PERFORM_OP; goto AbortAndExit; } EndpointLength = Floor34->AddressByteCount; RpcStatus = ExtractStringFromUntrustedPacket (UpperBound, EndpointLength, (char *)&Floor34->Data[0], Endpoint ); if (RpcStatus == RPC_S_OK) return(RPC_S_OK); else { // fall through to cleanup call below } AbortAndExit: if (Protseq && *Protseq) delete [] *Protseq; if (Endpoint && *Endpoint) delete [] *Endpoint; return RpcStatus; } RPC_STATUS GetProtseqAndEndpointFromFloor3( IN PFLOOR_234 Floor, IN BYTE *UpperBound, IN ULONG RemainingFloors, OUT char **Protseq, OPTIONAL OUT char **Endpoint, OPTIONAL OUT char **NWAddress OPTIONAL ) { /*++ Routine Description: This function extracts the Protocol Sequence and Endpoint info from a "Lower Tower" representation Arguments: Floor - Pointer to Floor2 structure. UpperBound - the first invalid byte after the end of the buffer. RemainingFloors - the number of floors remaining. Protseq - A pointer that will contain Protocol seq on return The memory will be allocated by this routine and caller will have to free this memory using delete. Undefined on failure. Endpoint - A pointer that will contain Endpoint on return The memory will be allocated by this routine and caller will have to free this memory using delete. Undefined on failure. NWAddress - A pointer that will contain Endpoint on return The memory will be allocated by this routine and caller will have to free this memory using delete. Undefined on failure. Return Value: RPC_S_OK RPC_S_OUT_OF_MEMORY - There is no memory to return Protseq or Endpoint string EP_S_CANT_PERFORM_OP - Lower Tower Encoding is incorrect. --*/ USHORT ProtocolType; RPC_STATUS Status; BYTE *CurrentPosition; PFLOOR_234 Floor34; CurrentPosition = (BYTE *)Floor; if (CurrentPosition + sizeof(FLOOR_234) >= UpperBound) { return EP_S_CANT_PERFORM_OP; } Floor34 = (PFLOOR_234)VerifyFloorLHSAndRHS(CurrentPosition, UpperBound, 2 // FloorNum ); if (Floor34 == NULL) return EP_S_CANT_PERFORM_OP; ProtocolType = Floor->FloorId; if (NWAddress != 0) { *NWAddress = 0; } switch(ProtocolType) { case CONN_LRPC: Status = LrpcTowerExplode(Floor34, UpperBound, RemainingFloors - 1, Protseq, Endpoint, NWAddress); break; case CONNECTIONFUL: case CONNECTIONLESS: Status = OsfTowerExplode((BYTE *)Floor34, UpperBound, RemainingFloors - 1, Protseq, Endpoint, NWAddress); break; default: Status = EP_S_CANT_PERFORM_OP; } return(Status); } RPC_STATUS TowerExplode( IN twr_p_t Tower, OUT RPC_IF_ID *Ifid, OPTIONAL OUT RPC_TRANSFER_SYNTAX *XferId, OPTIONAL OUT char **Protseq, OPTIONAL OUT char **Endpoint, OPTIONAL OUT char **NWAddress OPTIONAL ) /*++ Routine Description: This function converts a DCE tower representation of various binding information to binding info that is suitable to MS runtime. Specically it returns Ifid, Xferid, Protseq and Endpoint information encoded in the tower. Arguments: Tower - Tower encoding. Ifid - A pointer to Ifid Xferid - A pointer to Xferid Protseq - A pointer to pointer returning Protseq Endpoint- A pointer to pointer returning Endpoint Return Value: RPC_S_OK EP_S_CANT_PERFORM_OP - Error while parsing the Tower encoding RPC_S_OUT_OF_MEMORY - There is no memory to return Protseq and Endpoint strings. --*/ { PFLOOR_0OR1 Floor00; PFLOOR_0OR1 Floor01; PFLOOR_234 Floor234; unsigned short FloorCount; RPC_STATUS RpcStatus; BYTE *UpperBound, *LowerBound; BYTE *CurrentPosition; if (Tower == NULL) { RpcStatus = EP_S_CANT_PERFORM_OP; goto CleanupAndExit; } // // LowerBound points to Floor1 // UpperBound points to the byte after the last Floor // LowerBound = Tower->tower_octet_string + 2; // FloorCount is 2 bytes UpperBound = Tower->tower_octet_string + Tower->tower_length; // make sure we at least have the floor count if (UpperBound <= LowerBound) { RpcStatus = EP_S_CANT_PERFORM_OP; goto CleanupAndExit; } // go to the floor count CurrentPosition = Tower->tower_octet_string; // the tower octet string immediately follows the tower_length in the IDL // so we know it is 4 bytes aligned. No need for unaligned here. FloorCount = *((USHORT *)CurrentPosition); if ( FloorCount > MAX_FLOOR_COUNT || FloorCount < MIN_FLOOR_COUNT) { #ifdef DEBUGRPC PrintToDebugger("RPC: TowerVerify(): Too many/few floors - %d\n", FloorCount); #endif // DEBUGRPC RpcStatus = EP_S_CANT_PERFORM_OP; goto CleanupAndExit; } // skip the floor count and go to the first floor CurrentPosition += 2; // floor count is 2 bytes if (CurrentPosition + sizeof(FLOOR_0OR1) >= UpperBound) { RpcStatus = EP_S_CANT_PERFORM_OP; goto CleanupAndExit; } Floor01 = (PFLOOR_0OR1)VerifyFloorLHSAndRHS(CurrentPosition, UpperBound, 0 // FloorNum ); if (Floor01 == NULL) { RpcStatus = EP_S_CANT_PERFORM_OP; goto CleanupAndExit; } Floor00 = (PFLOOR_0OR1) CurrentPosition; //Process Floor 0 Interface Spec. if (Ifid != NULL) { RpcStatus = Floor0or1ToId(Floor00, (PGENERIC_ID) Ifid); if (RpcStatus != RPC_S_OK) { RpcStatus = EP_S_CANT_PERFORM_OP; goto CleanupAndExit; } } CurrentPosition = (BYTE *)Floor01; if (CurrentPosition + sizeof(FLOOR_0OR1) >= UpperBound) { RpcStatus = EP_S_CANT_PERFORM_OP; goto CleanupAndExit; } Floor234 = (PFLOOR_234)VerifyFloorLHSAndRHS(CurrentPosition, UpperBound, 1 // FloorNum ); if (Floor234 == NULL) { RpcStatus = EP_S_CANT_PERFORM_OP; goto CleanupAndExit; } //Now we point to and process Floor 1 Transfer Syntax Spec. if (XferId != NULL) { RpcStatus = Floor0or1ToId(Floor01, (PGENERIC_ID) XferId); if (RpcStatus != RPC_S_OK) { RpcStatus = EP_S_CANT_PERFORM_OP; goto CleanupAndExit; } } // Now Floor234 points to Floor 2. RpcProtocol [Connect-Datagram] // we already did 2 floors - subtract them from the count of floors RpcStatus = GetProtseqAndEndpointFromFloor3(Floor234, UpperBound, FloorCount - 2, Protseq, Endpoint, NWAddress); // fall through with the status CleanupAndExit: #if DBG if (RpcStatus != RPC_S_OK) { DbgPrint("RPCRT4: %X: TowerExplode: Failed to decrypt tower: %X\n", GetCurrentProcessId(), RpcStatus); } #endif // DBG return RpcStatus; } RPC_STATUS TowerConstruct( IN RPC_IF_ID PAPI * Ifid, IN RPC_TRANSFER_SYNTAX PAPI * Xferid, IN char PAPI * RpcProtocolSequence, IN char PAPI * Endpoint, OPTIONAL IN char PAPI * NWAddress, OPTIONAL OUT twr_t PAPI * PAPI * Tower ) /*++ Routine Description: This function constructs a DCE tower representation from Protseq, Endpoint, XferId and IfId Arguments: Ifid - A pointer to Ifid Xferid - A pointer to Xferid Protseq - A pointer to Protseq Endpoint- A pointer to Endpoint Tower - The constructed tower returmed - The memory is allocated by the routine and caller will have to free it. Return Value: RPC_S_OK EP_S_CANT_PERFORM_OP - Error while parsing the Tower encoding RPC_S_OUT_OF_MEMORY - There is no memory to return the constructed Tower. --*/ { unsigned short Numfloors, PAPI *FloorCnt; twr_t PAPI * Twr; PFLOOR_0OR1 Floor; PFLOOR_234 Floor234, Floor234_1; RPC_STATUS Status; unsigned long TowerLen, ByteCount; char PAPI * UpperTower; unsigned short ProtocolType; if ( RpcpStringCompareA(RpcProtocolSequence, "ncalrpc") == 0 ) { ProtocolType = CONN_LRPC; Status = LrpcTowerConstruct(Endpoint, &Numfloors, &ByteCount, (unsigned char **)&UpperTower); } else { if ( (RpcProtocolSequence[0] == 'n') && (RpcProtocolSequence[1] == 'c') && (RpcProtocolSequence[2] == 'a') && (RpcProtocolSequence[3] == 'c') && (RpcProtocolSequence[4] == 'n') && (RpcProtocolSequence[5] == '_')) { ProtocolType = CONNECTIONFUL; } else if ( (RpcProtocolSequence[0] == 'n') && (RpcProtocolSequence[1] == 'c') && (RpcProtocolSequence[2] == 'a') && (RpcProtocolSequence[3] == 'd') && (RpcProtocolSequence[4] == 'g') && (RpcProtocolSequence[5] == '_')) { ProtocolType = CONNECTIONLESS; } else { return(RPC_S_INVALID_RPC_PROTSEQ); } Status = OsfTowerConstruct( RpcProtocolSequence, Endpoint, NWAddress, &Numfloors, &ByteCount, (unsigned char **)&UpperTower ); } if (Status != RPC_S_OK) { return (Status); } TowerLen = 2 + ByteCount; TowerLen += 2 * sizeof(FLOOR_0OR1) + sizeof(FLOOR_2) ; if ( (*Tower = Twr = (twr_t *)I_RpcAllocate((unsigned int)TowerLen+4)) == NULL) { I_RpcFree(UpperTower); return(RPC_S_OUT_OF_MEMORY); } Twr->tower_length = TowerLen; FloorCnt = (unsigned short PAPI *)&Twr->tower_octet_string; *FloorCnt = Numfloors; Floor = (PFLOOR_0OR1)(FloorCnt+1); //Floor 0 - IfUuid and IfVersion CopyIdToFloor(Floor, (PGENERIC_ID)Ifid); Floor++; //Floor 1 - XferUuid and XferVersion CopyIdToFloor(Floor, (PGENERIC_ID)Xferid); //Floor 2 //ProtocolId = CONNECTIONFUL/CONNECTIONLESS/LRPC and Address = 0(ushort) Floor234 = (PFLOOR_234) (Floor + 1); Floor234->ProtocolIdByteCount = 1; Floor234->FloorId = (byte) ProtocolType; Floor234->Data[0] = 0x0; Floor234->Data[1] = 0x0; Floor234->AddressByteCount = 2; //Floor 3,4,5.. use the tower encoded by the Transports Floor234_1 = NEXTFLOOR(PFLOOR_234, Floor234); RpcpMemoryCopy((char PAPI *)Floor234_1, (char PAPI *)UpperTower, (size_t)ByteCount); I_RpcFree(UpperTower); return(RPC_S_OK); } #pragma pack()