Browse Source

Check incoming RTCP SDES

Filip Klembara 4 years ago
parent
commit
c43e82b8cb
2 changed files with 74 additions and 0 deletions
  1. 70 0
      include/rtc/rtp.hpp
  2. 4 0
      src/peerconnection.cpp

+ 70 - 0
include/rtc/rtp.hpp

@@ -326,6 +326,9 @@ public:
     inline SSRC ssrc() const { return ntohl(_ssrc); }
     inline void setSSRC(SSRC ssrc) { _ssrc = htonl(ssrc); }
 
+    /// Get item at given index
+    /// @note All items with index < `num` must be valid, otherwise this function has undefined behaviour (use `safelyCountChunkSize` to check if chunk is valid)
+    /// @param num Index of item to return
     inline RTCP_SDES_ITEM *getItem(int num) {
         auto base = &_items;
         while (num-- > 0) {
@@ -335,6 +338,39 @@ public:
         return reinterpret_cast<RTCP_SDES_ITEM *>(base);
     }
 
+    long safelyCountChunkSize(unsigned int maxChunkSize) {
+        if (maxChunkSize < RTCP_SDES_CHUNK::size({})) {
+            // chunk is truncated
+            return -1;
+        } else {
+            unsigned int size = sizeof(SSRC);
+            unsigned int i = 0;
+            // We can always access first 4 bytes of first item (in case of no items there will be 4 null bytes)
+            auto item = getItem(i);
+            std::vector<uint8_t> textsLength{};
+            while (item->type != 0) {
+                if (size + RTCP_SDES_ITEM::size(0) > maxChunkSize) {
+                    // item is too short
+                    return -1;
+                }
+                auto itemLength = item->length();
+                if (size + RTCP_SDES_ITEM::size(itemLength) >= maxChunkSize) {
+                    // item is too large (it can't be equal to chunk size because after item there must be 1-4 null bytes as padding)
+                    return -1;
+                }
+                textsLength.push_back(itemLength);
+                // safely to access next item
+                item = getItem(++i);
+            }
+            auto realSize = RTCP_SDES_CHUNK::size(textsLength);
+            if (realSize > maxChunkSize) {
+                // Chunk is too large
+                return -1;
+            }
+            return realSize;
+        }
+    }
+
     [[nodiscard]] static unsigned int size(std::vector<uint8_t> textLengths) {
         unsigned int itemsSize = 0;
         for (auto length: textLengths) {
@@ -345,6 +381,8 @@ public:
         return words * 4;
     }
 
+    /// Get size of chunk
+    /// @note All  items must be valid, otherwise this function has undefined behaviour (use `safelyCountChunkSize` to check if chunk is valid)
     [[nodiscard]] unsigned int getSize() {
         std::vector<uint8_t> textLengths{};
         unsigned int i = 0;
@@ -374,8 +412,37 @@ public:
         header.prepareHeader(202, chunkCount, length);
     }
 
+    bool isValid() {
+        auto chunksSize = header.lengthInBytes() - sizeof(header);
+        if (chunksSize == 0) {
+            return true;
+        } else {
+            // there is at least one chunk
+            unsigned int i = 0;
+            unsigned int size = 0;
+            while (size < chunksSize) {
+                if (chunksSize < size + RTCP_SDES_CHUNK::size({})) {
+                    // chunk is truncated
+                    return false;
+                }
+                auto chunk = getChunk(i);
+                auto chunkSize = chunk->safelyCountChunkSize(chunksSize - size);
+                if (chunksSize < 0) {
+                    // chunk is invalid
+                    return false;
+                }
+                size += chunkSize;
+            }
+            return size == chunksSize;
+        }
+    }
 
+    /// Returns number of chunks in this packet
+    /// @note Returns 0 if packet is invalid
     inline unsigned int chunksCount() {
+        if (!isValid()) {
+            return 0;
+        }
         uint16_t chunksSize = 4 * (header.length() + 1) - sizeof(header);
         unsigned int size = 0;
         unsigned int i = 0;
@@ -385,6 +452,9 @@ public:
         return i;
     }
 
+    /// Get chunk at given index
+    /// @note All chunks (and their items) with index < `num` must be valid, otherwise this function has undefined behaviour (use `isValid` to check if chunk is valid)
+    /// @param num Index of chunk to return
     inline RTCP_SDES_CHUNK *getChunk(int num) {
         auto base = &_chunks;
         while (num-- > 0) {

+ 4 - 0
src/peerconnection.cpp

@@ -694,6 +694,10 @@ void PeerConnection::forwardMedia(message_ptr message) {
 					ssrcs.insert(rtcpsr->getReportBlock(i)->getSSRC());
             } else if (header->payloadType() == 202) {
                 auto sdes = reinterpret_cast<RTCP_SDES *>(header);
+                if (!sdes->isValid()) {
+                    PLOG_WARNING << "RTCP SDES packet is invalid";
+                    continue;
+                }
                 for (unsigned int i = 0; i < sdes->chunksCount(); i++) {
                     auto chunk = sdes->getChunk(i);
                     ssrcs.insert(chunk->ssrc());