diff --git a/src/sniffer.c b/src/sniffer.c index dc02b6aea..7f6dc552b 100644 --- a/src/sniffer.c +++ b/src/sniffer.c @@ -244,7 +244,11 @@ static const char* const msgTable[] = "Secure Renegotiation Not Supported", /* 76 */ - "Get Session Stats Failure" + "Get Session Stats Failure", + "Reassembly Buffer Size Exceeded", + "Dropping Lost Fragment", + "Dropping Partial Record", + "Clear ACK Fault" }; @@ -316,6 +320,10 @@ typedef struct Flags { byte clientHello; /* processed client hello yet, for SSLv2 */ byte finCount; /* get both FINs before removing */ byte fatalError; /* fatal error state */ + byte cliAckFault; /* client acked unseen data from server */ + byte srvAckFault; /* server acked unseen data from client */ + byte cliSkipPartial; /* client skips partial data to catch up */ + byte srvSkipPartial; /* server skips partial data to catch up */ } Flags; @@ -346,6 +354,8 @@ typedef struct SnifferSession { time_t lastUsed; /* last used ticks */ PacketBuffer* cliReassemblyList; /* client out of order packets */ PacketBuffer* srvReassemblyList; /* server out of order packets */ + word32 cliReassemblyMemory; /* client packet memory used */ + word32 srvReassemblyMemory; /* server packet memory used */ struct SnifferSession* next; /* for hash table list */ byte* ticketID; /* mac ID of session ticket */ } SnifferSession; @@ -366,7 +376,14 @@ static wolfSSL_Mutex RecoveryMutex; /* for stats */ static int RecoveryEnabled = 0; /* global switch */ static int MaxRecoveryMemory = -1; /* per session max recovery memory */ static word32 MissedDataSessions = 0; /* # of sessions with missed data */ -static word32 ReassemblyMemory = 0; /* total reassembly memory in use */ + + +static void UpdateMissedDataSessions(void) +{ + LockMutex(&RecoveryMutex); + MissedDataSessions += 1; + UnLockMutex(&RecoveryMutex); +} /* Initialize overall Sniffer */ @@ -537,6 +554,10 @@ static void InitFlags(Flags* flags) flags->clientHello = 0; flags->finCount = 0; flags->fatalError = 0; + flags->cliAckFault = 0; + flags->srvAckFault = 0; + flags->cliSkipPartial = 0; + flags->srvSkipPartial = 0; } @@ -567,6 +588,8 @@ static void InitSession(SnifferSession* session) session->lastUsed = 0; session->cliReassemblyList = 0; session->srvReassemblyList = 0; + session->cliReassemblyMemory = 0; + session->srvReassemblyMemory = 0; session->next = 0; session->ticketID = 0; @@ -2333,18 +2356,26 @@ static int AddToReassembly(byte from, word32 seq, const byte* sslFrame, PacketBuffer* curr = *front; PacketBuffer* prev = curr; + word32* reassemblyMemory = (from == WOLFSSL_SERVER_END) ? + &session->cliReassemblyMemory : &session->srvReassemblyMemory; word32 startSeq = seq; word32 added; int bytesLeft = sslBytes; /* could be overlapping fragment */ /* if list is empty add full frame to front */ if (!curr) { + if (MaxRecoveryMemory != -1 && + (int)(*reassemblyMemory + sslBytes) > MaxRecoveryMemory) { + SetError(REASSEMBLY_MAX_STR, error, session, FATAL_ERROR_STATE); + return -1; + } add = CreateBuffer(&seq, seq + sslBytes - 1, sslFrame, &bytesLeft); if (add == NULL) { SetError(MEMORY_STR, error, session, FATAL_ERROR_STATE); return -1; } *front = add; + *reassemblyMemory += sslBytes; return 1; } @@ -2355,6 +2386,11 @@ static int AddToReassembly(byte from, word32 seq, const byte* sslFrame, if (end >= curr->begin) end = curr->begin - 1; + if (MaxRecoveryMemory -1 && + (int)(*reassemblyMemory + sslBytes) > MaxRecoveryMemory) { + SetError(REASSEMBLY_MAX_STR, error, session, FATAL_ERROR_STATE); + return -1; + } add = CreateBuffer(&seq, end, sslFrame, &bytesLeft); if (add == NULL) { SetError(MEMORY_STR, error, session, FATAL_ERROR_STATE); @@ -2362,6 +2398,7 @@ static int AddToReassembly(byte from, word32 seq, const byte* sslFrame, } add->next = curr; *front = add; + *reassemblyMemory += sslBytes; } /* while we have bytes left, try to find a gap to fill */ @@ -2391,6 +2428,11 @@ static int AddToReassembly(byte from, word32 seq, const byte* sslFrame, if (added == 0) continue; + if (MaxRecoveryMemory != -1 && + (int)(*reassemblyMemory + added) > MaxRecoveryMemory) { + SetError(REASSEMBLY_MAX_STR, error, session, FATAL_ERROR_STATE); + return -1; + } add = CreateBuffer(&seq, seq + added - 1, &sslFrame[seq - startSeq], &bytesLeft); if (add == NULL) { @@ -2399,6 +2441,7 @@ static int AddToReassembly(byte from, word32 seq, const byte* sslFrame, } add->next = prev->next; prev->next = add; + *reassemblyMemory += added; } return 1; } @@ -2432,6 +2475,9 @@ static int AdjustSequence(TcpInfo* tcpInfo, SnifferSession* session, &session->cliExpected : &session->srvExpected; PacketBuffer* reassemblyList = (session->flags.side == WOLFSSL_SERVER_END) ? session->cliReassemblyList : session->srvReassemblyList; + byte skipPartial = (session->flags.side == WOLFSSL_SERVER_END) ? + session->flags.srvSkipPartial : + session->flags.cliSkipPartial; /* handle rollover of sequence */ if (tcpInfo->sequence < seqStart) @@ -2477,17 +2523,33 @@ static int AdjustSequence(TcpInfo* tcpInfo, SnifferSession* session, } else if (real > *expected) { Trace(OUT_OF_ORDER_STR); - if (*sslBytes > 0) - return AddToReassembly(session->flags.side, real, *sslFrame, - *sslBytes, session, error); + if (*sslBytes > 0) { + int addResult = AddToReassembly(session->flags.side, real, + *sslFrame, *sslBytes, session, error); + if (skipPartial) { + *sslBytes = 0; + return 0; + } + else + return addResult; + } else if (tcpInfo->fin) return AddFinCapture(session, real); } - else { + else if (*sslBytes > 0) { + if (skipPartial) { + AddToReassembly(session->flags.side, real, + *sslFrame, *sslBytes, session, error); + *expected += *sslBytes; + *sslBytes = 0; + if (tcpInfo->fin) + *expected += 1; + return 0; + } /* The following conditional block is duplicated above. It is the * same action but for a different setup case. If changing this * block be sure to also update the block above. */ - if (reassemblyList) { + else if (reassemblyList) { word32 newEnd = *expected + *sslBytes; if (newEnd > reassemblyList->begin) { @@ -2516,6 +2578,110 @@ static int AdjustSequence(TcpInfo* tcpInfo, SnifferSession* session, } +static int FindNextRecordInAssembly(SnifferSession* session, + const byte** sslFrame, int* sslBytes, + const byte** end, char* error) +{ + PacketBuffer** front = (session->flags.side == WOLFSSL_SERVER_END) ? + &session->cliReassemblyList : + &session->srvReassemblyList; + PacketBuffer* curr = *front; + PacketBuffer* prev = NULL; + byte* skipPartial = (session->flags.side == WOLFSSL_SERVER_END) ? + &session->flags.srvSkipPartial : + &session->flags.cliSkipPartial; + word32* reassemblyMemory = (session->flags.side == WOLFSSL_SERVER_END) ? + &session->cliReassemblyMemory : + &session->srvReassemblyMemory; + SSL* ssl = (session->flags.side == WOLFSSL_SERVER_END) ? + session->sslServer : + session->sslClient; + ProtocolVersion pv = ssl->version; + word32* expected = (session->flags.side == WOLFSSL_SERVER_END) ? + &session->cliExpected : + &session->srvExpected; + + while (curr != NULL) { + *expected = curr->end + 1; + + if (curr->data[0] == application_data && + curr->data[1] == pv.major && + curr->data[2] == pv.minor) { + + if (ssl->buffers.inputBuffer.length > 0) + Trace(DROPPING_PARTIAL_RECORD); + + *sslBytes = curr->end - curr->begin + 1; + if ( (word32)*sslBytes > ssl->buffers.inputBuffer.bufferSize) { + if (GrowInputBuffer(ssl, *sslBytes, 0) < 0) { + SetError(MEMORY_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + } + + XMEMCPY(ssl->buffers.inputBuffer.buffer, curr->data, *sslBytes); + + *front = curr->next; + *reassemblyMemory -= *sslBytes; + FreePacketBuffer(curr); + + ssl->buffers.inputBuffer.length = *sslBytes; + *sslFrame = ssl->buffers.inputBuffer.buffer; + *end = *sslFrame + *sslBytes; + *skipPartial = 0; + + return 0; + } + else if (ssl->specs.cipher_type == block) { + if (ssl->specs.bulk_cipher_algorithm == wolfssl_aes) + wc_AesSetIV(ssl->decrypt.aes, + curr->data + curr->end - curr->begin + - ssl->specs.block_size + 1); + else if (ssl->specs.bulk_cipher_algorithm == wolfssl_triple_des) + wc_Des3_SetIV(ssl->decrypt.des3, + curr->data + curr->end - curr->begin + - ssl->specs.block_size + 1); + } + + Trace(DROPPING_LOST_FRAG_STR); + prev = curr; + curr = curr->next; + *reassemblyMemory -= (prev->end - prev->begin + 1); + FreePacketBuffer(prev); + } + + *front = curr; + + return 0; +} + + +static int FixSequence(TcpInfo* tcpInfo, SnifferSession* session) +{ + word32* expected = (session->flags.side == WOLFSSL_SERVER_END) ? + &session->srvExpected : &session->cliExpected; + PacketBuffer* list = (session->flags.side == WOLFSSL_SERVER_END) ? + session->srvReassemblyList : + session->cliReassemblyList; + byte* skipPartial = (session->flags.side != WOLFSSL_SERVER_END) ? + &session->flags.srvSkipPartial : + &session->flags.cliSkipPartial; + + *skipPartial = 1; + if (list != NULL) + *expected = list->begin; + else { + word32 seqStart = (session->flags.side == WOLFSSL_SERVER_END) ? + session->srvSeqStart : session->cliSeqStart; + word32 real = tcpInfo->ackNumber - seqStart; + + *expected = real; + } + + return 1; +} + + /* Check latest ack number for missing packets return 0 ok, <0 on error */ static int CheckAck(TcpInfo* tcpInfo, SnifferSession* session) @@ -2547,7 +2713,10 @@ static int CheckSequence(IpInfo* ipInfo, TcpInfo* tcpInfo, const byte** sslFrame, char* error) { int actualLen; - + byte* ackFault = (session->flags.side == WOLFSSL_SERVER_END) ? + &session->flags.cliAckFault : + &session->flags.srvAckFault; + /* init SEQ from server to client */ if (tcpInfo->syn && tcpInfo->ack) { session->srvSeqStart = tcpInfo->sequence; @@ -2564,10 +2733,26 @@ static int CheckSequence(IpInfo* ipInfo, TcpInfo* tcpInfo, TraceSequence(tcpInfo->sequence, *sslBytes); if (CheckAck(tcpInfo, session) < 0) { - SetError(ACK_MISSED_STR, error, session, FATAL_ERROR_STATE); - return -1; + if (!RecoveryEnabled) { + UpdateMissedDataSessions(); + SetError(ACK_MISSED_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + else { + SetError(ACK_MISSED_STR, error, session, 0); + if (*ackFault == 0) { + *ackFault = 1; + UpdateMissedDataSessions(); + } + return FixSequence(tcpInfo, session); + } } + if (*ackFault) { + Trace(CLEAR_ACK_FAULT); + *ackFault = 0; + } + return AdjustSequence(tcpInfo, session, sslBytes, sslFrame, error); } @@ -2581,6 +2766,9 @@ static int CheckPreRecord(IpInfo* ipInfo, TcpInfo* tcpInfo, word32 length; SSL* ssl = ((*session)->flags.side == WOLFSSL_SERVER_END) ? (*session)->sslServer : (*session)->sslClient; + byte skipPartial = ((*session)->flags.side == WOLFSSL_SERVER_END) ? + (*session)->flags.srvSkipPartial : + (*session)->flags.cliSkipPartial; /* remove SnifferSession on 2nd FIN or RST */ if (tcpInfo->fin || tcpInfo->rst) { /* flag FIN and RST */ @@ -2601,13 +2789,22 @@ static int CheckPreRecord(IpInfo* ipInfo, TcpInfo* tcpInfo, return -1; } + if (skipPartial) { + if (FindNextRecordInAssembly(*session, + sslFrame, sslBytes, end, error) < 0) { + return -1; + } + } + if (*sslBytes == 0) { Trace(NO_DATA_STR); return 1; } /* if current partial data, add to end of partial */ - if ( (length = ssl->buffers.inputBuffer.length) ) { + /* if skipping, the data is alread at the end of partial */ + if ( !skipPartial && + (length = ssl->buffers.inputBuffer.length) ) { Trace(PARTIAL_ADD_STR); if ( (*sslBytes + length) > ssl->buffers.inputBuffer.bufferSize) { @@ -2671,6 +2868,8 @@ static int HaveMoreInput(SnifferSession* session, const byte** sslFrame, &session->sslClient->buffers.inputBuffer.bufferSize; SSL* ssl = (session->flags.side == WOLFSSL_SERVER_END) ? session->sslServer : session->sslClient; + word32* reassemblyMemory = (session->flags.side == WOLFSSL_SERVER_END) ? + &session->cliReassemblyMemory : &session->srvReassemblyMemory; while (*front && ((*front)->begin == *expected) ) { word32 room = *bufferSize - *length; @@ -2694,6 +2893,8 @@ static int HaveMoreInput(SnifferSession* session, const byte** sslFrame, /* remove used packet */ *front = (*front)->next; + + *reassemblyMemory -= packetLen; FreePacketBuffer(del); moreInput = 1; @@ -3023,14 +3224,28 @@ int ssl_GetSessionStats(unsigned int* active, unsigned int* total, { int ret; - LockMutex(&RecoveryMutex); - - if (missedData) + if (missedData) { + LockMutex(&RecoveryMutex); *missedData = MissedDataSessions; - if (reassemblyMem) - *reassemblyMem = ReassemblyMemory; + UnLockMutex(&RecoveryMutex); + } - UnLockMutex(&RecoveryMutex); + if (reassemblyMem) { + SnifferSession* session; + int i; + + *reassemblyMem = 0; + LockMutex(&SessionMutex); + for (i = 0; i < HASH_SIZE; i++) { + session = SessionTable[i]; + while (session) { + *reassemblyMem += session->cliReassemblyMemory; + *reassemblyMem += session->srvReassemblyMemory; + session = session->next; + } + } + UnLockMutex(&SessionMutex); + } ret = wolfSSL_get_session_stats(active, total, peak, maxSessions); diff --git a/sslSniffer/sslSnifferTest/snifftest.c b/sslSniffer/sslSnifferTest/snifftest.c index 155a14954..0a21e3958 100755 --- a/sslSniffer/sslSnifferTest/snifftest.c +++ b/sslSniffer/sslSnifferTest/snifftest.c @@ -143,6 +143,7 @@ int main(int argc, char** argv) ssl_InitSniffer(); /* dll load on Windows */ #endif ssl_Trace("./tracefile.txt", err); + ssl_EnableRecovery(1, -1, err); if (argc == 1) { /* normal case, user chooses device and port */ diff --git a/wolfssl/sniffer_error.h b/wolfssl/sniffer_error.h index e459ec858..53acf6a10 100644 --- a/wolfssl/sniffer_error.h +++ b/wolfssl/sniffer_error.h @@ -110,6 +110,10 @@ #define NO_SECURE_RENEGOTIATION 75 #define BAD_SESSION_STATS 76 +#define REASSEMBLY_MAX_STR 77 +#define DROPPING_LOST_FRAG_STR 78 +#define DROPPING_PARTIAL_RECORD 79 +#define CLEAR_ACK_FAULT 80 /* !!!! also add to msgTable in sniffer.c and .rc file !!!! */ diff --git a/wolfssl/sniffer_error.rc b/wolfssl/sniffer_error.rc index 8b942b257..40bfac84a 100644 --- a/wolfssl/sniffer_error.rc +++ b/wolfssl/sniffer_error.rc @@ -92,5 +92,9 @@ STRINGTABLE 75, "Secure Renegotiation Not Supported" 76, "Get Session Stats Failure" + 77, "Reassembly Buffer Size Exceeded" + 78, "Dropping Lost Fragment" + 79, "Dropping Partial Record" + 80, "Clear ACK Fault" }