summaryrefslogtreecommitdiff
path: root/print-sctp.c
diff options
context:
space:
mode:
authorGuy Harris <guy@alum.mit.edu>2015-12-15 19:12:00 -0800
committerGuy Harris <guy@alum.mit.edu>2015-12-15 19:12:00 -0800
commit354638fa02452bf7aa2a9a28160227bc5f18151f (patch)
treed0e02ca7aa1ae32c801913ee1191ba61ed80810b /print-sctp.c
parent67d45a4b057134735c2cf3dd64c1f8d8bc522a9e (diff)
downloadtcpdump-354638fa02452bf7aa2a9a28160227bc5f18151f.tar.gz
Do more bounds checking.
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.
Diffstat (limited to 'print-sctp.c')
-rw-r--r--print-sctp.c196
1 files changed, 130 insertions, 66 deletions
diff --git a/print-sctp.c b/print-sctp.c
index 49d1cbba..b092d770 100644
--- a/print-sctp.c
+++ b/print-sctp.c
@@ -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;