]> The Tcpdump Group git mirrors - tcpdump/commitdiff
Do more bounds checking.
authorGuy Harris <[email protected]>
Wed, 16 Dec 2015 03:12:00 +0000 (19:12 -0800)
committerGuy Harris <[email protected]>
Wed, 16 Dec 2015 03:12:00 +0000 (19:12 -0800)
Step through the SCTP packet with the bp variable, and keep a packet
data remaining variable and use that for bounds checking.  For each
chunk, keep a chunk data remaining variable and use that as well.  If
there's not enough room for a full item, that gets reported as an error.

While we're at it, fix an error printout.

print-sctp.c

index 49d1cbba789a14b15496f707a083f1f5e2be70a1..b092d770878ab86e43722ebd01bdabfe6fea652b 100644 (file)
@@ -493,43 +493,35 @@ void sctp_print(netdissect_options *ndo,
                 const u_char *bp2,       /* beginning of enclosing */
                 u_int sctpPacketLength)  /* ip packet */
 {
+  u_int sctpPacketLengthRemaining;
   const struct sctpHeader *sctpPktHdr;
   const struct ip *ip;
   const struct ip6_hdr *ip6;
-  const void *endPacketPtr;
   u_short sourcePort, destPort;
   int chunkCount;
   const struct sctpChunkDesc *chunkDescPtr;
-  const void *nextChunk;
   const char *sep;
   int isforces = 0;
 
-
-  sctpPktHdr = (const struct sctpHeader*) bp;
-  endPacketPtr = (const u_char*)sctpPktHdr+sctpPacketLength;
-
-  if( (u_long) endPacketPtr > (u_long) ndo->ndo_snapend)
-    endPacketPtr = (const void *) ndo->ndo_snapend;
-  ip = (const struct ip *)bp2;
-  if (IP_V(ip) == 6)
-    ip6 = (const struct ip6_hdr *)bp2;
-  else
-    ip6 = NULL;
-  ND_TCHECK(*sctpPktHdr);
-
   if (sctpPacketLength < sizeof(struct sctpHeader))
     {
       ND_PRINT((ndo, "truncated-sctp - %ld bytes missing!",
-                  (long)sctpPacketLength-sizeof(struct sctpHeader)));
+                  (long)(sizeof(struct sctpHeader) - sctpPacketLength)));
       return;
     }
-
-  /*    sctpPacketLength -= sizeof(struct sctpHeader);  packet length  */
-  /*                         is now only as long as the payload  */
+  sctpPktHdr = (const struct sctpHeader*) bp;
+  ND_TCHECK(*sctpPktHdr);
+  sctpPacketLengthRemaining = sctpPacketLength;
 
   sourcePort = EXTRACT_16BITS(&sctpPktHdr->source);
   destPort = EXTRACT_16BITS(&sctpPktHdr->destination);
 
+  ip = (const struct ip *)bp2;
+  if (IP_V(ip) == 6)
+    ip6 = (const struct ip6_hdr *)bp2;
+  else
+    ip6 = NULL;
+
   if (ip6) {
     ND_PRINT((ndo, "%s.%d > %s.%d: sctp",
       ip6addr_string(ndo, &ip6->ip6_src),
@@ -554,40 +546,48 @@ void sctp_print(netdissect_options *ndo,
          isforces = 1;
   }
 
+  bp += sizeof(struct sctpHeader);
+  sctpPacketLengthRemaining -= sizeof(struct sctpHeader);
+
   if (ndo->ndo_vflag >= 2)
     sep = "\n\t";
   else
     sep = " (";
   /* cycle through all chunks, printing information on each one */
-  for (chunkCount = 0,
-        chunkDescPtr = (const struct sctpChunkDesc *)
-           ((const u_char*) sctpPktHdr + sizeof(struct sctpHeader));
-       chunkDescPtr != NULL &&
-        ( (const void *)
-           ((const u_char *) chunkDescPtr + sizeof(struct sctpChunkDesc))
-          <= endPacketPtr);
-
-       chunkDescPtr = (const struct sctpChunkDesc *) nextChunk, chunkCount++)
+  for (chunkCount = 0, chunkDescPtr = (const struct sctpChunkDesc *)bp;
+      sctpPacketLengthRemaining != 0;
+      chunkCount++)
     {
-      uint16_t chunkLength;
-      const u_char *chunkEnd;
+      uint16_t chunkLength, chunkLengthRemaining;
       uint16_t align;
 
+      chunkDescPtr = (const struct sctpChunkDesc *)bp;
+      if (sctpPacketLengthRemaining < sizeof(*chunkDescPtr)) {
+        ND_PRINT((ndo, "%s%d) [chunk descriptor cut off at end of packet]", sep, chunkCount+1));
+        break;
+      }
       ND_TCHECK(*chunkDescPtr);
       chunkLength = EXTRACT_16BITS(&chunkDescPtr->chunkLength);
       if (chunkLength < sizeof(*chunkDescPtr)) {
-        ND_PRINT((ndo, "%s%d) [Bad chunk length %u]", sep, chunkCount+1, chunkLength));
+        ND_PRINT((ndo, "%s%d) [Bad chunk length %u, < size of chunk descriptor]", sep, chunkCount+1, chunkLength));
         break;
       }
+      chunkLengthRemaining = chunkLength;
 
-      ND_TCHECK2(*((const uint8_t *)chunkDescPtr), chunkLength);
-      chunkEnd = ((const u_char*)chunkDescPtr + chunkLength);
-
-      align=chunkLength % 4;
+      align = chunkLength % 4;
       if (align != 0)
        align = 4 - align;
 
-      nextChunk = (const void *) (chunkEnd + align);
+      if (sctpPacketLengthRemaining < align) {
+        ND_PRINT((ndo, "%s%d) [Bad chunk length %u, > remaining data in packet]", sep, chunkCount+1, chunkLength));
+        break;
+      }
+
+      ND_TCHECK2(*bp, chunkLength);
+
+      bp += sizeof(*chunkDescPtr);
+      sctpPacketLengthRemaining -= sizeof(*chunkDescPtr);
+      chunkLengthRemaining -= sizeof(*chunkDescPtr);
 
       ND_PRINT((ndo, "%s%d) ", sep, chunkCount+1));
       ND_PRINT((ndo, "[%s] ", tok2str(sctp_chunkid_str, "Unknown chunk type: 0x%x",
@@ -598,7 +598,6 @@ void sctp_print(netdissect_options *ndo,
          {
            const struct sctpDataPart *dataHdrPtr;
            uint32_t ppid;
-           const u_char *payloadPtr;
            u_int payload_size;
 
            if ((chunkDescPtr->chunkFlg & SCTP_DATA_UNORDERED)
@@ -623,7 +622,11 @@ void sctp_print(netdissect_options *ndo,
                 == SCTP_DATA_LAST_FRAG) )
              ND_PRINT((ndo, " "));
 
-           dataHdrPtr=(const struct sctpDataPart*)(chunkDescPtr+1);
+           if (chunkLengthRemaining < sizeof(*dataHdrPtr)) {
+               ND_PRINT((ndo, "bogus chunk length %u]", chunkLength));
+               return;
+           }
+           dataHdrPtr=(const struct sctpDataPart*)bp;
 
            ppid = EXTRACT_32BITS(&dataHdrPtr->payloadtype);
            ND_PRINT((ndo, "[TSN: %u] ", EXTRACT_32BITS(&dataHdrPtr->TSN)));
@@ -638,70 +641,92 @@ void sctp_print(netdissect_options *ndo,
                    (ppid == SCTP_PPID_FORCES_LP);
            }
 
-           payloadPtr = (const u_char *) (dataHdrPtr + 1);
-           if (EXTRACT_16BITS(&chunkDescPtr->chunkLength) <
-                   sizeof(struct sctpDataPart) + sizeof(struct sctpChunkDesc) + 1) {
-               ND_PRINT((ndo, "bogus chunk length %u]", EXTRACT_16BITS(&chunkDescPtr->chunkLength)));
+           bp += sizeof(*dataHdrPtr);
+           sctpPacketLengthRemaining -= sizeof(*dataHdrPtr);
+           chunkLengthRemaining -= sizeof(*dataHdrPtr);
+           payload_size = chunkLengthRemaining;
+           if (payload_size == 0) {
+               ND_PRINT((ndo, "bogus chunk length %u]", chunkLength));
                return;
            }
 
-           payload_size = EXTRACT_16BITS(&chunkDescPtr->chunkLength) -
-               (sizeof(struct sctpDataPart) + sizeof(struct sctpChunkDesc));
-
            if (isforces) {
-               forces_print(ndo, payloadPtr, payload_size);
+               forces_print(ndo, bp, payload_size);
            } else if (ndo->ndo_vflag >= 2) {   /* if verbose output is specified */
                                        /* at the command line */
                switch (ppid) {
                case SCTP_PPID_M3UA :
-                       m3ua_print(ndo, payloadPtr, payload_size);
+                       m3ua_print(ndo, bp, payload_size);
                        break;
                default:
                        ND_PRINT((ndo, "[Payload"));
                        if (!ndo->ndo_suppress_default_print) {
                                ND_PRINT((ndo, ":"));
-                               ND_DEFAULTPRINT(payloadPtr, payload_size);
+                               ND_DEFAULTPRINT(bp, payload_size);
                        }
                        ND_PRINT((ndo, "]"));
                        break;
                }
            }
+           bp += payload_size;
+           sctpPacketLengthRemaining -= payload_size;
+           chunkLengthRemaining -= payload_size;
            break;
          }
        case SCTP_INITIATION :
          {
            const struct sctpInitiation *init;
 
-           init=(const struct sctpInitiation*)(chunkDescPtr+1);
+           if (chunkLengthRemaining < sizeof(*init)) {
+               ND_PRINT((ndo, "bogus chunk length %u]", chunkLength));
+               return;
+           }
+           init=(const struct sctpInitiation*)bp;
            ND_PRINT((ndo, "[init tag: %u] ", EXTRACT_32BITS(&init->initTag)));
            ND_PRINT((ndo, "[rwnd: %u] ", EXTRACT_32BITS(&init->rcvWindowCredit)));
            ND_PRINT((ndo, "[OS: %u] ", EXTRACT_16BITS(&init->NumPreopenStreams)));
            ND_PRINT((ndo, "[MIS: %u] ", EXTRACT_16BITS(&init->MaxInboundStreams)));
            ND_PRINT((ndo, "[init TSN: %u] ", EXTRACT_32BITS(&init->initialTSN)));
+           bp += sizeof(*init);
+           sctpPacketLengthRemaining -= sizeof(*init);
+           chunkLengthRemaining -= sizeof(*init);
 
 #if(0) /* ALC you can add code for optional params here */
-           if( (init+1) < chunkEnd )
+           if( chunkLengthRemaining != 0 )
              ND_PRINT((ndo, " @@@@@ UNFINISHED @@@@@@%s\n",
                     "Optional params present, but not printed."));
 #endif
+            bp += chunkLengthRemaining;
+           sctpPacketLengthRemaining -= chunkLengthRemaining;
+            chunkLengthRemaining = 0;
            break;
          }
        case SCTP_INITIATION_ACK :
          {
            const struct sctpInitiation *init;
 
-           init=(const struct sctpInitiation*)(chunkDescPtr+1);
+           if (chunkLengthRemaining < sizeof(*init)) {
+               ND_PRINT((ndo, "bogus chunk length %u]", chunkLength));
+               return;
+           }
+           init=(const struct sctpInitiation*)bp;
            ND_PRINT((ndo, "[init tag: %u] ", EXTRACT_32BITS(&init->initTag)));
            ND_PRINT((ndo, "[rwnd: %u] ", EXTRACT_32BITS(&init->rcvWindowCredit)));
            ND_PRINT((ndo, "[OS: %u] ", EXTRACT_16BITS(&init->NumPreopenStreams)));
            ND_PRINT((ndo, "[MIS: %u] ", EXTRACT_16BITS(&init->MaxInboundStreams)));
            ND_PRINT((ndo, "[init TSN: %u] ", EXTRACT_32BITS(&init->initialTSN)));
+            bp += sizeof(*init);
+            sctpPacketLengthRemaining -= sizeof(*init);
+            chunkLengthRemaining -= sizeof(*init);
 
 #if(0) /* ALC you can add code for optional params here */
-           if( (init+1) < chunkEnd )
+           if( chunkLengthRemaining != 0 )
              ND_PRINT((ndo, " @@@@@ UNFINISHED @@@@@@%s\n",
                     "Optional params present, but not printed."));
 #endif
+            bp += chunkLengthRemaining;
+           sctpPacketLengthRemaining -= chunkLengthRemaining;
+            chunkLengthRemaining = 0;
            break;
          }
        case SCTP_SELECTIVE_ACK:
@@ -711,38 +736,77 @@ void sctp_print(netdissect_options *ndo,
            int fragNo, tsnNo;
            const u_char *dupTSN;
 
-           sack=(const struct sctpSelectiveAck*)(chunkDescPtr+1);
+           if (chunkLengthRemaining < sizeof(*sack)) {
+             ND_PRINT((ndo, "bogus chunk length %u]", chunkLength));
+             return;
+           }
+           sack=(const struct sctpSelectiveAck*)bp;
            ND_PRINT((ndo, "[cum ack %u] ", EXTRACT_32BITS(&sack->highestConseqTSN)));
            ND_PRINT((ndo, "[a_rwnd %u] ", EXTRACT_32BITS(&sack->updatedRwnd)));
            ND_PRINT((ndo, "[#gap acks %u] ", EXTRACT_16BITS(&sack->numberOfdesc)));
            ND_PRINT((ndo, "[#dup tsns %u] ", EXTRACT_16BITS(&sack->numDupTsns)));
+            bp += sizeof(*sack);
+           sctpPacketLengthRemaining -= sizeof(*sack);
+            chunkLengthRemaining -= sizeof(*sack);
 
 
            /* print gaps */
-           for (frag = ( (const struct sctpSelectiveFrag *)
-                         ((const struct sctpSelectiveAck *) sack+1)),
-                  fragNo=0;
-                (const void *)frag < nextChunk && fragNo < EXTRACT_16BITS(&sack->numberOfdesc);
-                frag++, fragNo++)
+           for (fragNo=0;
+                chunkLengthRemaining != 0 && fragNo < EXTRACT_16BITS(&sack->numberOfdesc);
+                bp += sizeof(*frag), sctpPacketLengthRemaining -= sizeof(*frag), chunkLengthRemaining -= sizeof(*frag), fragNo++) {
+             if (chunkLengthRemaining < sizeof(*frag)) {
+               ND_PRINT((ndo, "bogus chunk length %u]", chunkLength));
+               return;
+             }
+             frag = (const struct sctpSelectiveFrag *)bp;
              ND_PRINT((ndo, "\n\t\t[gap ack block #%d: start = %u, end = %u] ",
                     fragNo+1,
                     EXTRACT_32BITS(&sack->highestConseqTSN) + EXTRACT_16BITS(&frag->fragmentStart),
                     EXTRACT_32BITS(&sack->highestConseqTSN) + EXTRACT_16BITS(&frag->fragmentEnd)));
-
+           }
 
            /* print duplicate TSNs */
-           for (dupTSN = (const u_char *)frag, tsnNo=0;
-                (const void *) dupTSN < nextChunk && tsnNo<EXTRACT_16BITS(&sack->numDupTsns);
-                dupTSN += 4, tsnNo++)
+           for (tsnNo=0;
+                chunkLengthRemaining != 0 && tsnNo<EXTRACT_16BITS(&sack->numDupTsns);
+                bp += 4, sctpPacketLengthRemaining -= 4, chunkLengthRemaining -= 4, tsnNo++) {
+             if (chunkLengthRemaining < 4) {
+               ND_PRINT((ndo, "bogus chunk length %u]", chunkLength));
+               return;
+             }
+              dupTSN = (const u_char *)bp;
              ND_PRINT((ndo, "\n\t\t[dup TSN #%u: %u] ", tsnNo+1,
-                 EXTRACT_32BITS(dupTSN)));
-
+               EXTRACT_32BITS(dupTSN)));
+           }
+           break;
+         }
+       default :
+         {
+            bp += chunkLengthRemaining;
+            sctpPacketLengthRemaining -= chunkLengthRemaining;
+            chunkLengthRemaining = 0;
            break;
          }
        }
 
-       if (ndo->ndo_vflag < 2)
-         sep = ", (";
+      /*
+       * Any extra stuff at the end of the chunk?
+       * XXX - report this?
+       */
+      bp += chunkLengthRemaining;
+      sctpPacketLengthRemaining -= chunkLengthRemaining;
+
+      if (ndo->ndo_vflag < 2)
+        sep = ", (";
+
+      if (align != 0) {
+       /*
+        * Fail if the alignment padding isn't in the captured data.
+        * Otherwise, skip it.
+        */
+       ND_TCHECK2(*bp, align);
+       bp += align;
+       sctpPacketLengthRemaining -= align;
+      }
     }
     return;