wolfSSL 3.11.1 for TLS1.3 beta
Fork of wolfSSL by
Diff: src/sniffer.c
- Revision:
- 13:80fb167dafdf
- Parent:
- 11:cee25a834751
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sniffer.c Tue May 30 06:16:19 2017 +0000 @@ -0,0 +1,3602 @@ +/* sniffer.c + * + * Copyright (C) 2006-2016 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + + +#ifdef HAVE_CONFIG_H + #include <config.h> +#endif + +#include <wolfssl/wolfcrypt/settings.h> + +#ifndef WOLFCRYPT_ONLY +#ifdef WOLFSSL_SNIFFER + +#include <assert.h> +#include <time.h> + +#ifndef _WIN32 + #include <arpa/inet.h> +#endif + +#ifdef _WIN32 + #define SNPRINTF _snprintf +#else + #define SNPRINTF snprintf +#endif + +#include <wolfssl/openssl/ssl.h> +#include <wolfssl/internal.h> +#include <wolfssl/error-ssl.h> +#include <wolfssl/sniffer.h> +#include <wolfssl/sniffer_error.h> +#ifdef NO_INLINE + #include <wolfssl/wolfcrypt/misc.h> +#else + #define WOLFSSL_MISC_INCLUDED + #include <wolfcrypt/src/misc.c> +#endif + + +#ifndef WOLFSSL_SNIFFER_TIMEOUT + #define WOLFSSL_SNIFFER_TIMEOUT 900 + /* Cache unclosed Sessions for 15 minutes since last used */ +#endif + +/* Misc constants */ +enum { + MAX_SERVER_ADDRESS = 128, /* maximum server address length */ + MAX_SERVER_NAME = 128, /* maximum server name length */ + MAX_ERROR_LEN = 80, /* maximum error length */ + ETHER_IF_ADDR_LEN = 6, /* ethernet interface address length */ + LOCAL_IF_ADDR_LEN = 4, /* localhost interface address length, !windows */ + TCP_PROTO = 6, /* TCP_PROTOCOL */ + IP_HDR_SZ = 20, /* IP header length, min */ + TCP_HDR_SZ = 20, /* TCP header length, min */ + IPV4 = 4, /* IP version 4 */ + TCP_PROTOCOL = 6, /* TCP Protocol id */ + TRACE_MSG_SZ = 80, /* Trace Message buffer size */ + HASH_SIZE = 499, /* Session Hash Table Rows */ + PSEUDO_HDR_SZ = 12, /* TCP Pseudo Header size in bytes */ + FATAL_ERROR_STATE = 1, /* SnifferSession fatal error state */ + TICKET_HINT_LEN = 4, /* Session Ticket Hint length */ + EXT_TYPE_SZ = 2, /* Extension length */ + MAX_INPUT_SZ = MAX_RECORD_SIZE + COMP_EXTRA + MAX_MSG_EXTRA + + MTU_EXTRA, /* Max input sz of reassembly */ + EXT_MASTER_SECRET = 0x17, /* Extended Master Secret Extension ID */ + TICKET_EXT_ID = 0x23 /* Session Ticket Extension ID */ +}; + + +#ifdef _WIN32 + +static HMODULE dllModule; /* for error string resources */ + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + static int didInit = 0; + + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + if (didInit == 0) { + dllModule = hModule; + ssl_InitSniffer(); + didInit = 1; + } + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + break; + case DLL_PROCESS_DETACH: + if (didInit) { + ssl_FreeSniffer(); + didInit = 0; + } + break; + } + return TRUE; +} + +#endif /* _WIN32 */ + + +static int TraceOn = 0; /* Trace is off by default */ +static FILE* TraceFile = 0; + + +/* windows uses .rc table for this */ +#ifndef _WIN32 + +static const char* const msgTable[] = +{ + /* 1 */ + "Out of Memory", + "New SSL Sniffer Server Registered", + "Checking IP Header", + "SSL Sniffer Server Not Registered", + "Checking TCP Header", + + /* 6 */ + "SSL Sniffer Server Port Not Registered", + "RSA Private Decrypt Error", + "RSA Private Decode Error", + "Set Cipher Spec Error", + "Server Hello Input Malformed", + + /* 11 */ + "Couldn't Resume Session Error", + "Server Did Resumption", + "Client Hello Input Malformed", + "Client Trying to Resume", + "Handshake Input Malformed", + + /* 16 */ + "Got Hello Verify msg", + "Got Server Hello msg", + "Got Cert Request msg", + "Got Server Key Exchange msg", + "Got Cert msg", + + /* 21 */ + "Got Server Hello Done msg", + "Got Finished msg", + "Got Client Hello msg", + "Got Client Key Exchange msg", + "Got Cert Verify msg", + + /* 26 */ + "Got Unknown Handshake msg", + "New SSL Sniffer Session created", + "Couldn't create new SSL", + "Got a Packet to decode", + "No data present", + + /* 31 */ + "Session Not Found", + "Got an Old Client Hello msg", + "Old Client Hello Input Malformed", + "Old Client Hello OK", + "Bad Old Client Hello", + + /* 36 */ + "Bad Record Header", + "Record Header Input Malformed", + "Got a HandShake msg", + "Bad HandShake msg", + "Got a Change Cipher Spec msg", + + /* 41 */ + "Got Application Data msg", + "Bad Application Data", + "Got an Alert msg", + "Another msg to Process", + "Removing Session From Table", + + /* 46 */ + "Bad Key File", + "Wrong IP Version", + "Wrong Protocol type", + "Packet Short for header processing", + "Got Unknown Record Type", + + /* 51 */ + "Can't Open Trace File", + "Session in Fatal Error State", + "Partial SSL record received", + "Buffer Error, malformed input", + "Added to Partial Input", + + /* 56 */ + "Received a Duplicate Packet", + "Received an Out of Order Packet", + "Received an Overlap Duplicate Packet", + "Received an Overlap Reassembly Begin Duplicate Packet", + "Received an Overlap Reassembly End Duplicate Packet", + + /* 61 */ + "Missed the Client Hello Entirely", + "Got Hello Request msg", + "Got Session Ticket msg", + "Bad Input", + "Bad Decrypt Type", + + /* 66 */ + "Bad Finished Message Processing", + "Bad Compression Type", + "Bad DeriveKeys Error", + "Saw ACK for Missing Packet Error", + "Bad Decrypt Operation", + + /* 71 */ + "Decrypt Keys Not Set Up", + "Late Key Load Error", + "Got Certificate Status msg", + "RSA Key Missing Error", + "Secure Renegotiation Not Supported", + + /* 76 */ + "Get Session Stats Failure", + "Reassembly Buffer Size Exceeded", + "Dropping Lost Fragment", + "Dropping Partial Record", + "Clear ACK Fault", + + /* 81 */ + "Bad Decrypt Size", + "Extended Master Secret Hash Error" +}; + + +/* *nix version uses table above */ +static void GetError(int idx, char* str) +{ + XSTRNCPY(str, msgTable[idx - 1], MAX_ERROR_LEN); +} + + +#else /* _WIN32 */ + + +/* Windows version uses .rc table */ +static void GetError(int idx, char* buffer) +{ + if (!LoadStringA(dllModule, idx, buffer, MAX_ERROR_LEN)) + buffer[0] = 0; +} + + +#endif /* _WIN32 */ + + +/* Packet Buffer for reassembly list and ready list */ +typedef struct PacketBuffer { + word32 begin; /* relative sequence begin */ + word32 end; /* relative sequence end */ + byte* data; /* actual data */ + struct PacketBuffer* next; /* next on reassembly list or ready list */ +} PacketBuffer; + + +#ifdef HAVE_SNI + +/* NamedKey maps a SNI name to a specific private key */ +typedef struct NamedKey { + char name[MAX_SERVER_NAME]; /* server DNS name */ + word32 nameSz; /* size of server DNS name */ + byte* key; /* DER private key */ + word32 keySz; /* size of DER private key */ + struct NamedKey* next; /* for list */ +} NamedKey; + +#endif + + +/* Sniffer Server holds info for each server/port monitored */ +typedef struct SnifferServer { + SSL_CTX* ctx; /* SSL context */ + char address[MAX_SERVER_ADDRESS]; /* passed in server address */ + word32 server; /* netowrk order address */ + int port; /* server port */ +#ifdef HAVE_SNI + NamedKey* namedKeys; /* mapping of names and keys */ + wolfSSL_Mutex namedKeysMutex; /* mutex for namedKey list */ +#endif + struct SnifferServer* next; /* for list */ +} SnifferServer; + + +/* Session Flags */ +typedef struct Flags { + byte side; /* which end is current packet headed */ + byte serverCipherOn; /* indicates whether cipher is active */ + byte clientCipherOn; /* indicates whether cipher is active */ + byte resuming; /* did this session come from resumption */ + byte cached; /* have we cached this session yet */ + 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 */ +#ifdef HAVE_EXTENDED_MASTER + byte expectEms; /* expect extended master secret */ +#endif +} Flags; + + +/* Out of Order FIN caputre */ +typedef struct FinCaputre { + word32 cliFinSeq; /* client relative sequence FIN 0 is no */ + word32 srvFinSeq; /* server relative sequence FIN, 0 is no */ + byte cliCounted; /* did we count yet, detects duplicates */ + byte srvCounted; /* did we count yet, detects duplicates */ +} FinCaputre; + + +typedef struct HsHashes { +#ifndef NO_OLD_TLS +#ifndef NO_SHA + Sha hashSha; +#endif +#ifndef NO_MD5 + Md5 hashMd5; +#endif +#endif +#ifndef NO_SHA256 + Sha256 hashSha256; +#endif +#ifdef WOLFSSL_SHA384 + Sha384 hashSha384; +#endif +} HsHashes; + + +/* Sniffer Session holds info for each client/server SSL/TLS session */ +typedef struct SnifferSession { + SnifferServer* context; /* server context */ + SSL* sslServer; /* SSL server side decode */ + SSL* sslClient; /* SSL client side decode */ + word32 server; /* server address in network byte order */ + word32 client; /* client address in network byte order */ + word16 srvPort; /* server port */ + word16 cliPort; /* client port */ + word32 cliSeqStart; /* client start sequence */ + word32 srvSeqStart; /* server start sequence */ + word32 cliExpected; /* client expected sequence (relative) */ + word32 srvExpected; /* server expected sequence (relative) */ + FinCaputre finCaputre; /* retain out of order FIN s */ + Flags flags; /* session flags */ + 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 */ +#ifdef HAVE_EXTENDED_MASTER + HsHashes* hash; +#endif +} SnifferSession; + + +/* Sniffer Server List and mutex */ +static SnifferServer* ServerList = 0; +static wolfSSL_Mutex ServerListMutex; + + +/* Session Hash Table, mutex, and count */ +static SnifferSession* SessionTable[HASH_SIZE]; +static wolfSSL_Mutex SessionMutex; +static int SessionCount = 0; + +/* Recovery of missed data switches and stats */ +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 void UpdateMissedDataSessions(void) +{ + wc_LockMutex(&RecoveryMutex); + MissedDataSessions += 1; + wc_UnLockMutex(&RecoveryMutex); +} + + +/* Initialize overall Sniffer */ +void ssl_InitSniffer(void) +{ + wolfSSL_Init(); + wc_InitMutex(&ServerListMutex); + wc_InitMutex(&SessionMutex); + wc_InitMutex(&RecoveryMutex); +} + + +#ifdef HAVE_SNI + +/* Free Named Key and the zero out the private key it holds */ +static void FreeNamedKey(NamedKey* in) +{ + if (in) { + if (in->key) { + ForceZero(in->key, in->keySz); + free(in->key); + } + free(in); + } +} + + +static void FreeNamedKeyList(NamedKey* in) +{ + NamedKey* next; + + while (in) { + next = in->next; + FreeNamedKey(in); + in = next; + } +} + +#endif + + +/* Free Sniffer Server's resources/self */ +static void FreeSnifferServer(SnifferServer* srv) +{ + if (srv) { +#ifdef HAVE_SNI + wc_LockMutex(&srv->namedKeysMutex); + FreeNamedKeyList(srv->namedKeys); + wc_UnLockMutex(&srv->namedKeysMutex); + wc_FreeMutex(&srv->namedKeysMutex); +#endif + SSL_CTX_free(srv->ctx); + } + free(srv); +} + + +/* free PacketBuffer's resources/self */ +static void FreePacketBuffer(PacketBuffer* del) +{ + if (del) { + free(del->data); + free(del); + } +} + + +/* remove PacketBuffer List */ +static void FreePacketList(PacketBuffer* in) +{ + if (in) { + PacketBuffer* del; + PacketBuffer* packet = in; + + while (packet) { + del = packet; + packet = packet->next; + FreePacketBuffer(del); + } + } +} + + +/* Free Sniffer Session's resources/self */ +static void FreeSnifferSession(SnifferSession* session) +{ + if (session) { + SSL_free(session->sslClient); + SSL_free(session->sslServer); + + FreePacketList(session->cliReassemblyList); + FreePacketList(session->srvReassemblyList); + + free(session->ticketID); +#ifdef HAVE_EXTENDED_MASTER + free(session->hash); +#endif + } + free(session); +} + + +/* Free overall Sniffer */ +void ssl_FreeSniffer(void) +{ + SnifferServer* srv; + SnifferServer* removeServer; + SnifferSession* session; + SnifferSession* removeSession; + int i; + + wc_LockMutex(&ServerListMutex); + wc_LockMutex(&SessionMutex); + + srv = ServerList; + while (srv) { + removeServer = srv; + srv = srv->next; + FreeSnifferServer(removeServer); + } + + for (i = 0; i < HASH_SIZE; i++) { + session = SessionTable[i]; + while (session) { + removeSession = session; + session = session->next; + FreeSnifferSession(removeSession); + } + } + + wc_UnLockMutex(&SessionMutex); + wc_UnLockMutex(&ServerListMutex); + + wc_FreeMutex(&RecoveryMutex); + wc_FreeMutex(&SessionMutex); + wc_FreeMutex(&ServerListMutex); + + if (TraceFile) { + TraceOn = 0; + fclose(TraceFile); + TraceFile = NULL; + } + + wolfSSL_Cleanup(); +} + + +#ifdef HAVE_EXTENDED_MASTER + +static int HashInit(HsHashes* hash) +{ + int ret = 0; + + XMEMSET(hash, 0, sizeof(HsHashes)); + +#ifndef NO_OLD_TLS +#ifndef NO_SHA + if (ret == 0) + ret = wc_InitSha(&hash->hashSha); +#endif +#ifndef NO_MD5 + if (ret == 0) + wc_InitMd5(&hash->hashMd5); +#endif +#endif +#ifndef NO_SHA256 + if (ret == 0) + ret = wc_InitSha256(&hash->hashSha256); +#endif +#ifdef WOLFSSL_SHA384 + if (ret == 0) + ret = wc_InitSha384(&hash->hashSha384); +#endif + + return ret; +} + + +static int HashUpdate(HsHashes* hash, const byte* input, int sz) +{ + int ret = 0; + + input -= HANDSHAKE_HEADER_SZ; + sz += HANDSHAKE_HEADER_SZ; + +#ifndef NO_OLD_TLS +#ifndef NO_SHA + if (ret == 0) + ret = wc_ShaUpdate(&hash->hashSha, input, sz); +#endif +#ifndef NO_MD5 + if (ret == 0) + wc_Md5Update(&hash->hashMd5, input, sz); +#endif +#endif +#ifndef NO_SHA256 + if (ret == 0) + ret = wc_Sha256Update(&hash->hashSha256, input, sz); +#endif +#ifdef WOLFSSL_SHA384 + if (ret == 0) + ret = wc_Sha384Update(&hash->hashSha384, input, sz); +#endif + + return ret; +} + + +static int HashCopy(HS_Hashes* d, HsHashes* s) +{ +#ifndef NO_OLD_TLS +#ifndef NO_SHA + XMEMCPY(&d->hashSha, &s->hashSha, sizeof(Sha)); +#endif +#ifndef NO_MD5 + XMEMCPY(&d->hashMd5, &s->hashMd5, sizeof(Md5)); +#endif +#endif + +#ifndef NO_SHA256 + XMEMCPY(&d->hashSha256, &s->hashSha256, sizeof(Sha256)); +#endif +#ifdef WOLFSSL_SHA384 + XMEMCPY(&d->hashSha384, &s->hashSha384, sizeof(Sha384)); +#endif + + return 0; +} + +#endif + + +/* Initialize a SnifferServer */ +static void InitSnifferServer(SnifferServer* sniffer) +{ + sniffer->ctx = 0; + XMEMSET(sniffer->address, 0, MAX_SERVER_ADDRESS); + sniffer->server = 0; + sniffer->port = 0; +#ifdef HAVE_SNI + sniffer->namedKeys = 0; + wc_InitMutex(&sniffer->namedKeysMutex); +#endif + sniffer->next = 0; +} + + +/* Initialize session flags */ +static void InitFlags(Flags* flags) +{ + flags->side = 0; + flags->serverCipherOn = 0; + flags->clientCipherOn = 0; + flags->resuming = 0; + flags->cached = 0; + flags->clientHello = 0; + flags->finCount = 0; + flags->fatalError = 0; + flags->cliAckFault = 0; + flags->srvAckFault = 0; + flags->cliSkipPartial = 0; + flags->srvSkipPartial = 0; +#ifdef HAVE_EXTENDED_MASTER + flags->expectEms = 0; +#endif +} + + +/* Initialize FIN Capture */ +static void InitFinCapture(FinCaputre* cap) +{ + cap->cliFinSeq = 0; + cap->srvFinSeq = 0; + cap->cliCounted = 0; + cap->srvCounted = 0; +} + + +/* Initialize a Sniffer Session */ +static void InitSession(SnifferSession* session) +{ + session->context = 0; + session->sslServer = 0; + session->sslClient = 0; + session->server = 0; + session->client = 0; + session->srvPort = 0; + session->cliPort = 0; + session->cliSeqStart = 0; + session->srvSeqStart = 0; + session->cliExpected = 0; + session->srvExpected = 0; + session->lastUsed = 0; + session->cliReassemblyList = 0; + session->srvReassemblyList = 0; + session->cliReassemblyMemory = 0; + session->srvReassemblyMemory = 0; + session->next = 0; + session->ticketID = 0; + + InitFlags(&session->flags); + InitFinCapture(&session->finCaputre); +#ifdef HAVE_EXTENDED_MASTER + session->hash = 0; +#endif +} + + +/* IP Info from IP Header */ +typedef struct IpInfo { + int length; /* length of this header */ + int total; /* total length of fragment */ + word32 src; /* network order source address */ + word32 dst; /* network order destination address */ +} IpInfo; + + +/* TCP Info from TCP Header */ +typedef struct TcpInfo { + int srcPort; /* source port */ + int dstPort; /* source port */ + int length; /* length of this header */ + word32 sequence; /* sequence number */ + word32 ackNumber; /* ack number */ + byte fin; /* FIN set */ + byte rst; /* RST set */ + byte syn; /* SYN set */ + byte ack; /* ACK set */ +} TcpInfo; + + +/* Tcp Pseudo Header for Checksum calculation */ +typedef struct TcpPseudoHdr { + word32 src; /* source address */ + word32 dst; /* destination address */ + byte rsv; /* reserved, always 0 */ + byte protocol; /* IP protocol */ + word16 length; /* tcp header length + data length (doesn't include */ + /* pseudo header length) network order */ +} TcpPseudoHdr; + + +/* Password Setting Callback */ +static int SetPassword(char* passwd, int sz, int rw, void* userdata) +{ + (void)rw; + XSTRNCPY(passwd, (const char*)userdata, sz); + return (int)XSTRLEN((const char*)userdata); +} + + +/* Ethernet Header */ +typedef struct EthernetHdr { + byte dst[ETHER_IF_ADDR_LEN]; /* destination host address */ + byte src[ETHER_IF_ADDR_LEN]; /* source host address */ + word16 type; /* IP, ARP, etc */ +} EthernetHdr; + + +/* IP Header */ +typedef struct IpHdr { + byte ver_hl; /* version/header length */ + byte tos; /* type of service */ + word16 length; /* total length */ + word16 id; /* identification */ + word16 offset; /* fragment offset field */ + byte ttl; /* time to live */ + byte protocol; /* protocol */ + word16 sum; /* checksum */ + word32 src; /* source address */ + word32 dst; /* destination address */ +} IpHdr; + + +#define IP_HL(ip) ( (((ip)->ver_hl) & 0x0f) * 4) +#define IP_V(ip) ( ((ip)->ver_hl) >> 4) + +/* TCP Header */ +typedef struct TcpHdr { + word16 srcPort; /* source port */ + word16 dstPort; /* destination port */ + word32 sequence; /* sequence number */ + word32 ack; /* acknoledgment number */ + byte offset; /* data offset, reserved */ + byte flags; /* option flags */ + word16 window; /* window */ + word16 sum; /* checksum */ + word16 urgent; /* urgent pointer */ +} TcpHdr; + +#define TCP_LEN(tcp) ( (((tcp)->offset & 0xf0) >> 4) * 4) +#define TCP_FIN 0x01 +#define TCP_SYN 0x02 +#define TCP_RST 0x04 +#define TCP_ACK 0x10 + + + + + +/* Use platform specific GetError to write to tracfile if tracing */ +static void Trace(int idx) +{ + if (TraceOn) { + char myBuffer[MAX_ERROR_LEN]; + GetError(idx, myBuffer); + fprintf(TraceFile, "\t%s\n", myBuffer); +#ifdef DEBUG_SNIFFER + fprintf(stderr, "\t%s\n", myBuffer); +#endif + } +} + + +/* Show TimeStamp for beginning of packet Trace */ +static void TraceHeader(void) +{ + if (TraceOn) { + time_t ticks = time(NULL); + fprintf(TraceFile, "\n%s", ctime(&ticks)); + } +} + + +/* Show Set Server info for Trace */ +static void TraceSetServer(const char* srv, int port, const char* keyFile) +{ + if (TraceOn) { + fprintf(TraceFile, "\tTrying to install a new Sniffer Server with\n"); + fprintf(TraceFile, "\tserver: %s, port: %d, keyFile: %s\n", srv, port, + keyFile); + } +} + + +#ifdef HAVE_SNI + +/* Show Set Named Server info for Trace */ +static void TraceSetNamedServer(const char* name, + const char* srv, int port, const char* keyFile) +{ + if (TraceOn) { + fprintf(TraceFile, "\tTrying to install a new Sniffer Server with\n"); + fprintf(TraceFile, "\tname: %s, server: %s, port: %d, keyFile: %s\n", + name, srv, port, keyFile); + } +} + +#endif + + +/* Trace got packet number */ +static void TracePacket(void) +{ + if (TraceOn) { + static word32 packetNumber = 0; + fprintf(TraceFile, "\tGot a Packet to decode, packet %u\n", + ++packetNumber); + } +} + + +/* Convert network byte order address into human readable */ +static char* IpToS(word32 addr, char* str) +{ + byte* p = (byte*)&addr; + + SNPRINTF(str, TRACE_MSG_SZ, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + + return str; +} + + +/* Show destination and source address from Ip Hdr for packet Trace */ +static void TraceIP(IpHdr* iphdr) +{ + if (TraceOn) { + char src[TRACE_MSG_SZ]; + char dst[TRACE_MSG_SZ]; + fprintf(TraceFile, "\tdst:%s src:%s\n", IpToS(iphdr->dst, dst), + IpToS(iphdr->src, src)); + } +} + + +/* Show destination and source port from Tcp Hdr for packet Trace */ +static void TraceTcp(TcpHdr* tcphdr) +{ + if (TraceOn) { + fprintf(TraceFile, "\tdstPort:%u srcPort:%u\n", ntohs(tcphdr->dstPort), + ntohs(tcphdr->srcPort)); + } +} + + +/* Show sequence and payload length for Trace */ +static void TraceSequence(word32 seq, int len) +{ + if (TraceOn) { + fprintf(TraceFile, "\tSequence:%u, payload length:%d\n", seq, len); + } +} + + +/* Show sequence and payload length for Trace */ +static void TraceAck(word32 ack, word32 expected) +{ + if (TraceOn) { + fprintf(TraceFile, "\tAck:%u Expected:%u\n", ack, expected); + } +} + + +/* Show relative expected and relative received sequences */ +static void TraceRelativeSequence(word32 expected, word32 got) +{ + if (TraceOn) { + fprintf(TraceFile, "\tExpected sequence:%u, received sequence:%u\n", + expected, got); + } +} + + +/* Show server sequence startup from SYN */ +static void TraceServerSyn(word32 seq) +{ + if (TraceOn) { + fprintf(TraceFile, "\tServer SYN, Sequence Start:%u\n", seq); + } +} + + +/* Show client sequence startup from SYN */ +static void TraceClientSyn(word32 seq) +{ + if (TraceOn) { + fprintf(TraceFile, "\tClient SYN, Sequence Start:%u\n", seq); + } +} + + +/* Show client FIN capture */ +static void TraceClientFin(word32 finSeq, word32 relSeq) +{ + if (TraceOn) { + fprintf(TraceFile, "\tClient FIN capture:%u, current SEQ:%u\n", + finSeq, relSeq); + } +} + + +/* Show server FIN capture */ +static void TraceServerFin(word32 finSeq, word32 relSeq) +{ + if (TraceOn) { + fprintf(TraceFile, "\tServer FIN capture:%u, current SEQ:%u\n", + finSeq, relSeq); + } +} + + +/* Show number of SSL data bytes decoded, could be 0 (ok) */ +static void TraceGotData(int bytes) +{ + if (TraceOn) { + fprintf(TraceFile, "\t%d bytes of SSL App data processed\n", bytes); + } +} + + +/* Show bytes added to old SSL App data */ +static void TraceAddedData(int newBytes, int existingBytes) +{ + if (TraceOn) { + fprintf(TraceFile, + "\t%d bytes added to %d existing bytes in User Buffer\n", + newBytes, existingBytes); + } +} + + +/* Show Stale Session */ +static void TraceStaleSession(void) +{ + if (TraceOn) { + fprintf(TraceFile, "\tFound a stale session\n"); + } +} + + +/* Show Finding Stale Sessions */ +static void TraceFindingStale(void) +{ + if (TraceOn) { + fprintf(TraceFile, "\tTrying to find Stale Sessions\n"); + } +} + + +/* Show Removed Session */ +static void TraceRemovedSession(void) +{ + if (TraceOn) { + fprintf(TraceFile, "\tRemoved it\n"); + } +} + + +/* Set user error string */ +static void SetError(int idx, char* error, SnifferSession* session, int fatal) +{ + GetError(idx, error); + Trace(idx); + if (session && fatal == FATAL_ERROR_STATE) + session->flags.fatalError = 1; +} + + +/* See if this IPV4 network order address has been registered */ +/* return 1 is true, 0 is false */ +static int IsServerRegistered(word32 addr) +{ + int ret = 0; /* false */ + SnifferServer* sniffer; + + wc_LockMutex(&ServerListMutex); + + sniffer = ServerList; + while (sniffer) { + if (sniffer->server == addr) { + ret = 1; + break; + } + sniffer = sniffer->next; + } + + wc_UnLockMutex(&ServerListMutex); + + return ret; +} + + +/* See if this port has been registered to watch */ +/* return 1 is true, 0 is false */ +static int IsPortRegistered(word32 port) +{ + int ret = 0; /* false */ + SnifferServer* sniffer; + + wc_LockMutex(&ServerListMutex); + + sniffer = ServerList; + while (sniffer) { + if (sniffer->port == (int)port) { + ret = 1; + break; + } + sniffer = sniffer->next; + } + + wc_UnLockMutex(&ServerListMutex); + + return ret; +} + + +/* Get SnifferServer from IP and Port */ +static SnifferServer* GetSnifferServer(IpInfo* ipInfo, TcpInfo* tcpInfo) +{ + SnifferServer* sniffer; + + wc_LockMutex(&ServerListMutex); + + sniffer = ServerList; + while (sniffer) { + if (sniffer->port == tcpInfo->srcPort && sniffer->server == ipInfo->src) + break; + if (sniffer->port == tcpInfo->dstPort && sniffer->server == ipInfo->dst) + break; + sniffer = sniffer->next; + } + + wc_UnLockMutex(&ServerListMutex); + + return sniffer; +} + + +/* Hash the Session Info, return hash row */ +static word32 SessionHash(IpInfo* ipInfo, TcpInfo* tcpInfo) +{ + word32 hash = ipInfo->src * ipInfo->dst; + hash *= tcpInfo->srcPort * tcpInfo->dstPort; + + return hash % HASH_SIZE; +} + + +/* Get Exisiting SnifferSession from IP and Port */ +static SnifferSession* GetSnifferSession(IpInfo* ipInfo, TcpInfo* tcpInfo) +{ + SnifferSession* session; + time_t currTime = time(NULL); + word32 row = SessionHash(ipInfo, tcpInfo); + + assert(row <= HASH_SIZE); + + wc_LockMutex(&SessionMutex); + + session = SessionTable[row]; + while (session) { + if (session->server == ipInfo->src && session->client == ipInfo->dst && + session->srvPort == tcpInfo->srcPort && + session->cliPort == tcpInfo->dstPort) + break; + if (session->client == ipInfo->src && session->server == ipInfo->dst && + session->cliPort == tcpInfo->srcPort && + session->srvPort == tcpInfo->dstPort) + break; + + session = session->next; + } + + if (session) + session->lastUsed= currTime; /* keep session alive, remove stale will */ + /* leave alone */ + wc_UnLockMutex(&SessionMutex); + + /* determine side */ + if (session) { + if (ipInfo->dst == session->context->server && + tcpInfo->dstPort == session->context->port) + session->flags.side = WOLFSSL_SERVER_END; + else + session->flags.side = WOLFSSL_CLIENT_END; + } + + return session; +} + + +#ifdef HAVE_SNI + +static int LoadKeyFile(byte** keyBuf, word32* keyBufSz, + const char* keyFile, int typeKey, + const char* password) +{ + byte* loadBuf; + long fileSz = 0; + XFILE file; + int ret; + + if (keyBuf == NULL || keyBufSz == NULL || keyFile == NULL) { + return -1; + } + + file = XFOPEN(keyFile, "rb"); + if (file == XBADFILE) return -1; + XFSEEK(file, 0, XSEEK_END); + fileSz = XFTELL(file); + XREWIND(file); + + loadBuf = (byte*)malloc(fileSz); + if (loadBuf == NULL) { + XFCLOSE(file); + return -1; + } + + ret = (int)XFREAD(loadBuf, 1, fileSz, file); + XFCLOSE(file); + + if (ret != fileSz) { + free(loadBuf); + return -1; + } + + if (typeKey == SSL_FILETYPE_PEM) { + byte* saveBuf = (byte*)malloc(fileSz); + int saveBufSz = 0; + + ret = -1; + if (saveBuf != NULL) { + saveBufSz = wolfSSL_KeyPemToDer(loadBuf, (int)fileSz, + saveBuf, (int)fileSz, password); + if (saveBufSz < 0) { + saveBufSz = 0; + free(saveBuf); + saveBuf = NULL; + } + else + ret = 0; + } + + ForceZero(loadBuf, (word32)fileSz); + free(loadBuf); + + if (saveBuf) { + *keyBuf = saveBuf; + *keyBufSz = (word32)saveBufSz; + } + } + else { + *keyBuf = loadBuf; + *keyBufSz = (word32)fileSz; + } + + if (ret < 0) { + return -1; + } + + return ret; +} + +#endif + + +static int SetNamedPrivateKey(const char* name, const char* address, int port, + const char* keyFile, int typeKey, const char* password, char* error) +{ + SnifferServer* sniffer; + int ret; + int type = (typeKey == FILETYPE_PEM) ? SSL_FILETYPE_PEM : + SSL_FILETYPE_ASN1; + int isNew = 0; + word32 serverIp; + +#ifdef HAVE_SNI + NamedKey* namedKey = NULL; +#endif + + (void)name; +#ifdef HAVE_SNI + if (name != NULL) { + namedKey = (NamedKey*)malloc(sizeof(NamedKey)); + if (namedKey == NULL) { + SetError(MEMORY_STR, error, NULL, 0); + return -1; + } + XMEMSET(namedKey, 0, sizeof(NamedKey)); + + namedKey->nameSz = (word32)XSTRLEN(name); + XSTRNCPY(namedKey->name, name, sizeof(namedKey->name)); + if (namedKey->nameSz >= sizeof(namedKey->name)) { + namedKey->nameSz = sizeof(namedKey->name) - 1; + namedKey->name[namedKey->nameSz] = '\0'; + } + + ret = LoadKeyFile(&namedKey->key, &namedKey->keySz, + keyFile, type, password); + if (ret < 0) { + SetError(KEY_FILE_STR, error, NULL, 0); + FreeNamedKey(namedKey); + return -1; + } + } +#endif + + serverIp = inet_addr(address); + sniffer = ServerList; + while (sniffer != NULL && + (sniffer->server != serverIp || sniffer->port != port)) { + sniffer = sniffer->next; + } + + if (sniffer == NULL) { + isNew = 1; + sniffer = (SnifferServer*)malloc(sizeof(SnifferServer)); + if (sniffer == NULL) { + SetError(MEMORY_STR, error, NULL, 0); +#ifdef HAVE_SNI + FreeNamedKey(namedKey); +#endif + return -1; + } + InitSnifferServer(sniffer); + + XSTRNCPY(sniffer->address, address, MAX_SERVER_ADDRESS-1); + sniffer->address[MAX_SERVER_ADDRESS-1] = '\0'; + sniffer->server = serverIp; + sniffer->port = port; + + sniffer->ctx = SSL_CTX_new(TLSv1_client_method()); + if (!sniffer->ctx) { + SetError(MEMORY_STR, error, NULL, 0); +#ifdef HAVE_SNI + FreeNamedKey(namedKey); +#endif + FreeSnifferServer(sniffer); + return -1; + } + } + + if (name == NULL) { + if (password) { + SSL_CTX_set_default_passwd_cb(sniffer->ctx, SetPassword); + SSL_CTX_set_default_passwd_cb_userdata( + sniffer->ctx, (void*)password); + } + ret = SSL_CTX_use_PrivateKey_file(sniffer->ctx, keyFile, type); + if (ret != SSL_SUCCESS) { + SetError(KEY_FILE_STR, error, NULL, 0); + if (isNew) + FreeSnifferServer(sniffer); + return -1; + } + } +#ifdef HAVE_SNI + else { + wc_LockMutex(&sniffer->namedKeysMutex); + namedKey->next = sniffer->namedKeys; + sniffer->namedKeys = namedKey; + wc_UnLockMutex(&sniffer->namedKeysMutex); + } +#endif + + if (isNew) { + sniffer->next = ServerList; + ServerList = sniffer; + } + + return 0; +} + + +#ifdef HAVE_SNI + +/* Sets the private key for a specific name, server and port */ +/* returns 0 on success, -1 on error */ +int ssl_SetNamedPrivateKey(const char* name, + const char* address, int port, + const char* keyFile, int typeKey, + const char* password, char* error) +{ + int ret; + + TraceHeader(); + TraceSetNamedServer(name, address, port, keyFile); + + wc_LockMutex(&ServerListMutex); + ret = SetNamedPrivateKey(name, address, port, keyFile, + typeKey, password, error); + wc_UnLockMutex(&ServerListMutex); + + if (ret == 0) + Trace(NEW_SERVER_STR); + + return ret; +} + +#endif + + +/* Sets the private key for a specific server and port */ +/* returns 0 on success, -1 on error */ +int ssl_SetPrivateKey(const char* address, int port, const char* keyFile, + int typeKey, const char* password, char* error) +{ + int ret; + + TraceHeader(); + TraceSetServer(address, port, keyFile); + + wc_LockMutex(&ServerListMutex); + ret = SetNamedPrivateKey(NULL, address, port, keyFile, + typeKey, password, error); + wc_UnLockMutex(&ServerListMutex); + + if (ret == 0) + Trace(NEW_SERVER_STR); + + return ret; +} + + +/* Check IP Header for IPV4, TCP, and a registered server address */ +/* returns 0 on success, -1 on error */ +static int CheckIpHdr(IpHdr* iphdr, IpInfo* info, int length, char* error) +{ + int version = IP_V(iphdr); + + TraceIP(iphdr); + Trace(IP_CHECK_STR); + + if (version != IPV4) { + SetError(BAD_IPVER_STR, error, NULL, 0); + return -1; + } + + if (iphdr->protocol != TCP_PROTOCOL) { + SetError(BAD_PROTO_STR, error, NULL, 0); + return -1; + } + + if (!IsServerRegistered(iphdr->src) && !IsServerRegistered(iphdr->dst)) { + SetError(SERVER_NOT_REG_STR, error, NULL, 0); + return -1; + } + + info->length = IP_HL(iphdr); + info->total = ntohs(iphdr->length); + info->src = iphdr->src; + info->dst = iphdr->dst; + + if (info->total == 0) + info->total = length; /* reassembled may be off */ + + return 0; +} + + +/* Check TCP Header for a registered port */ +/* returns 0 on success, -1 on error */ +static int CheckTcpHdr(TcpHdr* tcphdr, TcpInfo* info, char* error) +{ + TraceTcp(tcphdr); + Trace(TCP_CHECK_STR); + info->srcPort = ntohs(tcphdr->srcPort); + info->dstPort = ntohs(tcphdr->dstPort); + info->length = TCP_LEN(tcphdr); + info->sequence = ntohl(tcphdr->sequence); + info->fin = tcphdr->flags & TCP_FIN; + info->rst = tcphdr->flags & TCP_RST; + info->syn = tcphdr->flags & TCP_SYN; + info->ack = tcphdr->flags & TCP_ACK; + if (info->ack) + info->ackNumber = ntohl(tcphdr->ack); + + if (!IsPortRegistered(info->srcPort) && !IsPortRegistered(info->dstPort)) { + SetError(SERVER_PORT_NOT_REG_STR, error, NULL, 0); + return -1; + } + + return 0; +} + + +/* Decode Record Layer Header */ +static int GetRecordHeader(const byte* input, RecordLayerHeader* rh, int* size) +{ + XMEMCPY(rh, input, RECORD_HEADER_SZ); + *size = (rh->length[0] << 8) | rh->length[1]; + + if (*size > (MAX_RECORD_SIZE + COMP_EXTRA + MAX_MSG_EXTRA)) + return LENGTH_ERROR; + + return 0; +} + + +/* Process Client Key Exchange, RSA only */ +static int ProcessClientKeyExchange(const byte* input, int* sslBytes, + SnifferSession* session, char* error) +{ + word32 idx = 0; + RsaKey key; + int ret; + + if (session->sslServer->buffers.key == NULL || + session->sslServer->buffers.key->buffer == NULL || + session->sslServer->buffers.key->length == 0) { + + SetError(RSA_KEY_MISSING_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + ret = wc_InitRsaKey(&key, 0); + if (ret == 0) + ret = wc_RsaPrivateKeyDecode(session->sslServer->buffers.key->buffer, + &idx, &key, session->sslServer->buffers.key->length); + if (ret == 0) { + int length = wc_RsaEncryptSize(&key); + + if (IsTLS(session->sslServer)) + input += 2; /* tls pre length */ + + if (length > *sslBytes) { + SetError(PARTIAL_INPUT_STR, error, session, FATAL_ERROR_STATE); + wc_FreeRsaKey(&key); + return -1; + } + #ifdef WC_RSA_BLINDING + ret = wc_RsaSetRNG(&key, session->sslServer->rng); + if (ret != 0) { + SetError(RSA_DECRYPT_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + #endif + ret = wc_RsaPrivateDecrypt(input, length, + session->sslServer->arrays->preMasterSecret,SECRET_LEN, &key); + + if (ret != SECRET_LEN) { + SetError(RSA_DECRYPT_STR, error, session, FATAL_ERROR_STATE); + wc_FreeRsaKey(&key); + return -1; + } + session->sslServer->arrays->preMasterSz = SECRET_LEN; + + /* store for client side as well */ + XMEMCPY(session->sslClient->arrays->preMasterSecret, + session->sslServer->arrays->preMasterSecret, SECRET_LEN); + session->sslClient->arrays->preMasterSz = SECRET_LEN; + + #ifdef SHOW_SECRETS + { + int i; + printf("pre master secret: "); + for (i = 0; i < SECRET_LEN; i++) + printf("%02x", session->sslServer->arrays->preMasterSecret[i]); + printf("\n"); + } + #endif + } + else { + SetError(RSA_DECODE_STR, error, session, FATAL_ERROR_STATE); + wc_FreeRsaKey(&key); + return -1; + } + + if (SetCipherSpecs(session->sslServer) != 0) { + SetError(BAD_CIPHER_SPEC_STR, error, session, FATAL_ERROR_STATE); + wc_FreeRsaKey(&key); + return -1; + } + + if (SetCipherSpecs(session->sslClient) != 0) { + SetError(BAD_CIPHER_SPEC_STR, error, session, FATAL_ERROR_STATE); + wc_FreeRsaKey(&key); + return -1; + } + + ret = MakeMasterSecret(session->sslServer); + ret += MakeMasterSecret(session->sslClient); + ret += SetKeysSide(session->sslServer, ENCRYPT_AND_DECRYPT_SIDE); + ret += SetKeysSide(session->sslClient, ENCRYPT_AND_DECRYPT_SIDE); + + if (ret != 0) { + SetError(BAD_DERIVE_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + +#ifdef SHOW_SECRETS + { + int i; + printf("server master secret: "); + for (i = 0; i < SECRET_LEN; i++) + printf("%02x", session->sslServer->arrays->masterSecret[i]); + printf("\n"); + + printf("client master secret: "); + for (i = 0; i < SECRET_LEN; i++) + printf("%02x", session->sslClient->arrays->masterSecret[i]); + printf("\n"); + + printf("server suite = %d\n", session->sslServer->options.cipherSuite); + printf("client suite = %d\n", session->sslClient->options.cipherSuite); + } +#endif + + wc_FreeRsaKey(&key); + return ret; +} + + +/* Process Session Ticket */ +static int ProcessSessionTicket(const byte* input, int* sslBytes, + SnifferSession* session, char* error) +{ + word16 len; + + /* make sure can read through hint and len */ + if (TICKET_HINT_LEN + LENGTH_SZ > *sslBytes) { + SetError(BAD_INPUT_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + + input += TICKET_HINT_LEN; /* skip over hint */ + *sslBytes -= TICKET_HINT_LEN; + + len = (word16)((input[0] << 8) | input[1]); + input += LENGTH_SZ; + *sslBytes -= LENGTH_SZ; + + /* make sure can read through ticket */ + if (len > *sslBytes || len < ID_LEN) { + SetError(BAD_INPUT_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + + /* store session with macID as sessionID */ + session->sslServer->options.haveSessionId = 1; + XMEMCPY(session->sslServer->arrays->sessionID, input + len - ID_LEN,ID_LEN); + + return 0; +} + + +/* Process Server Hello */ +static int ProcessServerHello(int msgSz, const byte* input, int* sslBytes, + SnifferSession* session, char* error) +{ + ProtocolVersion pv; + byte b; + int toRead = VERSION_SZ + RAN_LEN + ENUM_LEN; + int doResume = 0; + int initialBytes = *sslBytes; + + (void)msgSz; + (void)initialBytes; + + /* make sure we didn't miss ClientHello */ + if (session->flags.clientHello == 0) { + SetError(MISSED_CLIENT_HELLO_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + + /* make sure can read through session len */ + if (toRead > *sslBytes) { + SetError(SERVER_HELLO_INPUT_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + + XMEMCPY(&pv, input, VERSION_SZ); + input += VERSION_SZ; + *sslBytes -= VERSION_SZ; + + session->sslServer->version = pv; + session->sslClient->version = pv; + + XMEMCPY(session->sslServer->arrays->serverRandom, input, RAN_LEN); + XMEMCPY(session->sslClient->arrays->serverRandom, input, RAN_LEN); + input += RAN_LEN; + *sslBytes -= RAN_LEN; + + b = *input++; + *sslBytes -= 1; + + /* make sure can read through compression */ + if ( (b + SUITE_LEN + ENUM_LEN) > *sslBytes) { + SetError(SERVER_HELLO_INPUT_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + if (b) { + XMEMCPY(session->sslServer->arrays->sessionID, input, ID_LEN); + session->sslServer->options.haveSessionId = 1; + } + input += b; + *sslBytes -= b; + + /* cipher suite */ + b = *input++; /* first byte, ECC or not */ + session->sslServer->options.cipherSuite0 = b; + session->sslClient->options.cipherSuite0 = b; + b = *input++; + session->sslServer->options.cipherSuite = b; + session->sslClient->options.cipherSuite = b; + *sslBytes -= SUITE_LEN; + + /* compression */ + b = *input++; + *sslBytes -= ENUM_LEN; + + if (b) { + SetError(BAD_COMPRESSION_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + +#ifdef HAVE_EXTENDED_MASTER + /* extensions */ + if ((initialBytes - *sslBytes) < msgSz) { + word16 len; + + /* skip extensions until extended master secret */ + /* make sure can read len */ + if (SUITE_LEN > *sslBytes) { + SetError(SERVER_HELLO_INPUT_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + len = (word16)((input[0] << 8) | input[1]); + input += SUITE_LEN; + *sslBytes -= SUITE_LEN; + /* make sure can read through all extensions */ + if (len > *sslBytes) { + SetError(SERVER_HELLO_INPUT_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + + while (len >= EXT_TYPE_SZ + LENGTH_SZ) { + byte extType[EXT_TYPE_SZ]; + word16 extLen; + + extType[0] = input[0]; + extType[1] = input[1]; + input += EXT_TYPE_SZ; + *sslBytes -= EXT_TYPE_SZ; + + extLen = (word16)((input[0] << 8) | input[1]); + input += LENGTH_SZ; + *sslBytes -= LENGTH_SZ; + + /* make sure can read through individual extension */ + if (extLen > *sslBytes) { + SetError(SERVER_HELLO_INPUT_STR, error, session, + FATAL_ERROR_STATE); + return -1; + } + + if (extType[0] == 0x00 && extType[1] == EXT_MASTER_SECRET) { + session->flags.expectEms = 1; + } + + input += extLen; + *sslBytes -= extLen; + len -= extLen + EXT_TYPE_SZ + LENGTH_SZ; + } + } + + if (!session->flags.expectEms) { + free(session->hash); + session->hash = NULL; + } +#endif + + if (session->sslServer->options.haveSessionId && + XMEMCMP(session->sslServer->arrays->sessionID, + session->sslClient->arrays->sessionID, ID_LEN) == 0) + doResume = 1; + else if (session->sslClient->options.haveSessionId == 0 && + session->sslServer->options.haveSessionId == 0 && + session->ticketID) + doResume = 1; + + if (session->ticketID && doResume) { + /* use ticketID to retrieve from session, prefer over sessionID */ + XMEMCPY(session->sslServer->arrays->sessionID,session->ticketID,ID_LEN); + session->sslServer->options.haveSessionId = 1; /* may not have + actual sessionID */ + } + + if (doResume ) { + int ret = 0; + SSL_SESSION* resume = GetSession(session->sslServer, + session->sslServer->arrays->masterSecret, 0); + if (resume == NULL) { + SetError(BAD_SESSION_RESUME_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + /* make sure client has master secret too */ + XMEMCPY(session->sslClient->arrays->masterSecret, + session->sslServer->arrays->masterSecret, SECRET_LEN); + session->flags.resuming = 1; + + Trace(SERVER_DID_RESUMPTION_STR); + if (SetCipherSpecs(session->sslServer) != 0) { + SetError(BAD_CIPHER_SPEC_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + + if (SetCipherSpecs(session->sslClient) != 0) { + SetError(BAD_CIPHER_SPEC_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + + if (session->sslServer->options.tls) { + ret = DeriveTlsKeys(session->sslServer); + ret += DeriveTlsKeys(session->sslClient); + } + else { + ret = DeriveKeys(session->sslServer); + ret += DeriveKeys(session->sslClient); + } + ret += SetKeysSide(session->sslServer, ENCRYPT_AND_DECRYPT_SIDE); + ret += SetKeysSide(session->sslClient, ENCRYPT_AND_DECRYPT_SIDE); + + if (ret != 0) { + SetError(BAD_DERIVE_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + } +#ifdef SHOW_SECRETS + { + int i; + printf("cipher suite = 0x%02x\n", + session->sslServer->options.cipherSuite); + printf("server random: "); + for (i = 0; i < RAN_LEN; i++) + printf("%02x", session->sslServer->arrays->serverRandom[i]); + printf("\n"); + } +#endif + return 0; +} + + +/* Process normal Client Hello */ +static int ProcessClientHello(const byte* input, int* sslBytes, + SnifferSession* session, char* error) +{ + byte bLen; + word16 len; + int toRead = VERSION_SZ + RAN_LEN + ENUM_LEN; + +#ifdef HAVE_SNI + { + byte name[MAX_SERVER_NAME]; + word32 nameSz = sizeof(name); + int ret; + + ret = wolfSSL_SNI_GetFromBuffer( + input - HANDSHAKE_HEADER_SZ - RECORD_HEADER_SZ, + *sslBytes + HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ, + WOLFSSL_SNI_HOST_NAME, name, &nameSz); + + if (ret == SSL_SUCCESS) { + NamedKey* namedKey; + + if (nameSz >= sizeof(name)) + nameSz = sizeof(name) - 1; + name[nameSz] = 0; + wc_LockMutex(&session->context->namedKeysMutex); + namedKey = session->context->namedKeys; + while (namedKey != NULL) { + if (nameSz == namedKey->nameSz && + XSTRNCMP((char*)name, namedKey->name, nameSz) == 0) { + if (wolfSSL_use_PrivateKey_buffer(session->sslServer, + namedKey->key, namedKey->keySz, + SSL_FILETYPE_ASN1) != SSL_SUCCESS) { + wc_UnLockMutex(&session->context->namedKeysMutex); + SetError(CLIENT_HELLO_LATE_KEY_STR, error, session, + FATAL_ERROR_STATE); + return -1; + } + break; + } + else + namedKey = namedKey->next; + } + wc_UnLockMutex(&session->context->namedKeysMutex); + } + } +#endif + + session->flags.clientHello = 1; /* don't process again */ + + /* make sure can read up to session len */ + if (toRead > *sslBytes) { + SetError(CLIENT_HELLO_INPUT_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + + /* skip, get negotiated one from server hello */ + input += VERSION_SZ; + *sslBytes -= VERSION_SZ; + + XMEMCPY(session->sslServer->arrays->clientRandom, input, RAN_LEN); + XMEMCPY(session->sslClient->arrays->clientRandom, input, RAN_LEN); + + input += RAN_LEN; + *sslBytes -= RAN_LEN; + + /* store session in case trying to resume */ + bLen = *input++; + *sslBytes -= ENUM_LEN; + if (bLen) { + if (ID_LEN > *sslBytes) { + SetError(CLIENT_HELLO_INPUT_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + Trace(CLIENT_RESUME_TRY_STR); + XMEMCPY(session->sslClient->arrays->sessionID, input, ID_LEN); + session->sslClient->options.haveSessionId = 1; + } +#ifdef SHOW_SECRETS + { + int i; + printf("client random: "); + for (i = 0; i < RAN_LEN; i++) + printf("%02x", session->sslServer->arrays->clientRandom[i]); + printf("\n"); + } +#endif + + input += bLen; + *sslBytes -= bLen; + + /* skip cipher suites */ + /* make sure can read len */ + if (SUITE_LEN > *sslBytes) { + SetError(CLIENT_HELLO_INPUT_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + len = (word16)((input[0] << 8) | input[1]); + input += SUITE_LEN; + *sslBytes -= SUITE_LEN; + /* make sure can read suites + comp len */ + if (len + ENUM_LEN > *sslBytes) { + SetError(CLIENT_HELLO_INPUT_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + input += len; + *sslBytes -= len; + + /* skip compression */ + bLen = *input++; + *sslBytes -= ENUM_LEN; + /* make sure can read len */ + if (bLen > *sslBytes) { + SetError(CLIENT_HELLO_INPUT_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + input += bLen; + *sslBytes -= bLen; + + if (*sslBytes == 0) { + /* no extensions */ + return 0; + } + + /* skip extensions until session ticket */ + /* make sure can read len */ + if (SUITE_LEN > *sslBytes) { + SetError(CLIENT_HELLO_INPUT_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + len = (word16)((input[0] << 8) | input[1]); + input += SUITE_LEN; + *sslBytes -= SUITE_LEN; + /* make sure can read through all extensions */ + if (len > *sslBytes) { + SetError(CLIENT_HELLO_INPUT_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + + while (len >= EXT_TYPE_SZ + LENGTH_SZ) { + byte extType[EXT_TYPE_SZ]; + word16 extLen; + + extType[0] = input[0]; + extType[1] = input[1]; + input += EXT_TYPE_SZ; + *sslBytes -= EXT_TYPE_SZ; + + extLen = (word16)((input[0] << 8) | input[1]); + input += LENGTH_SZ; + *sslBytes -= LENGTH_SZ; + + /* make sure can read through individual extension */ + if (extLen > *sslBytes) { + SetError(CLIENT_HELLO_INPUT_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + + if (extType[0] == 0x00 && extType[1] == TICKET_EXT_ID) { + + /* make sure can read through ticket if there is a non blank one */ + if (extLen && extLen < ID_LEN) { + SetError(CLIENT_HELLO_INPUT_STR, error, session, + FATAL_ERROR_STATE); + return -1; + } + + if (extLen) { + if (session->ticketID == 0) { + session->ticketID = (byte*)malloc(ID_LEN); + if (session->ticketID == 0) { + SetError(MEMORY_STR, error, session, + FATAL_ERROR_STATE); + return -1; + } + } + XMEMCPY(session->ticketID, input + extLen - ID_LEN, ID_LEN); + } + } + + input += extLen; + *sslBytes -= extLen; + len -= extLen + EXT_TYPE_SZ + LENGTH_SZ; + } + + return 0; +} + + +/* Process Finished */ +static int ProcessFinished(const byte* input, int size, int* sslBytes, + SnifferSession* session, char* error) +{ + SSL* ssl; + word32 inOutIdx = 0; + int ret; + + if (session->flags.side == WOLFSSL_SERVER_END) + ssl = session->sslServer; + else + ssl = session->sslClient; + + ret = DoFinished(ssl, input, &inOutIdx, (word32) size, (word32) *sslBytes, + SNIFF); + *sslBytes -= (int)inOutIdx; + + if (ret < 0) { + SetError(BAD_FINISHED_MSG, error, session, FATAL_ERROR_STATE); + return ret; + } + + if (ret == 0 && session->flags.cached == 0) { + if (session->sslServer->options.haveSessionId) { + WOLFSSL_SESSION* sess = GetSession(session->sslServer, NULL, 0); + if (sess == NULL) + AddSession(session->sslServer); /* don't re add */ + session->flags.cached = 1; + } + } + + /* If receiving a finished message from one side, free the resources + * from the other side's tracker. */ + if (session->flags.side == WOLFSSL_SERVER_END) + FreeHandshakeResources(session->sslClient); + else + FreeHandshakeResources(session->sslServer); + + return ret; +} + + +/* Process HandShake input */ +static int DoHandShake(const byte* input, int* sslBytes, + SnifferSession* session, char* error) +{ + byte type; + int size; + int ret = 0; + int startBytes; + + if (*sslBytes < HANDSHAKE_HEADER_SZ) { + SetError(HANDSHAKE_INPUT_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + type = input[0]; + size = (input[1] << 16) | (input[2] << 8) | input[3]; + + input += HANDSHAKE_HEADER_SZ; + *sslBytes -= HANDSHAKE_HEADER_SZ; + startBytes = *sslBytes; + + if (*sslBytes < size) { + SetError(HANDSHAKE_INPUT_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + + /* A session's arrays are released when the handshake is completed. */ + if (session->sslServer->arrays == NULL && + session->sslClient->arrays == NULL) { + + SetError(NO_SECURE_RENEGOTIATION, error, session, FATAL_ERROR_STATE); + return -1; + } + +#ifdef HAVE_EXTENDED_MASTER + if (session->hash) { + if (HashUpdate(session->hash, input, size) != 0) { + SetError(EXTENDED_MASTER_HASH_STR, error, + session, FATAL_ERROR_STATE); + return -1; + } + } +#endif + + switch (type) { + case hello_verify_request: + Trace(GOT_HELLO_VERIFY_STR); + break; + case hello_request: + Trace(GOT_HELLO_REQUEST_STR); + break; + case session_ticket: + Trace(GOT_SESSION_TICKET_STR); + ret = ProcessSessionTicket(input, sslBytes, session, error); + break; + case server_hello: + Trace(GOT_SERVER_HELLO_STR); + ret = ProcessServerHello(size, input, sslBytes, session, error); + break; + case certificate_request: + Trace(GOT_CERT_REQ_STR); + break; + case server_key_exchange: + Trace(GOT_SERVER_KEY_EX_STR); + /* can't know temp key passively */ + SetError(BAD_CIPHER_SPEC_STR, error, session, FATAL_ERROR_STATE); + ret = -1; + break; + case certificate: + Trace(GOT_CERT_STR); + break; + case server_hello_done: + Trace(GOT_SERVER_HELLO_DONE_STR); + break; + case finished: + Trace(GOT_FINISHED_STR); + ret = ProcessFinished(input, size, sslBytes, session, error); + break; + case client_hello: + Trace(GOT_CLIENT_HELLO_STR); + ret = ProcessClientHello(input, sslBytes, session, error); + break; + case client_key_exchange: + Trace(GOT_CLIENT_KEY_EX_STR); +#ifdef HAVE_EXTENDED_MASTER + if (session->flags.expectEms && session->hash != NULL) { + if (HashCopy(session->sslServer->hsHashes, + session->hash) == 0 && + HashCopy(session->sslClient->hsHashes, + session->hash) == 0) { + + session->sslServer->options.haveEMS = 1; + session->sslClient->options.haveEMS = 1; + } + else { + SetError(EXTENDED_MASTER_HASH_STR, error, + session, FATAL_ERROR_STATE); + ret = -1; + } + XMEMSET(session->hash, 0, sizeof(HsHashes)); + free(session->hash); + session->hash = NULL; + } + else { + session->sslServer->options.haveEMS = 0; + session->sslClient->options.haveEMS = 0; + } +#endif + if (ret == 0) + ret = ProcessClientKeyExchange(input, sslBytes, session, error); + break; + case certificate_verify: + Trace(GOT_CERT_VER_STR); + break; + case certificate_status: + Trace(GOT_CERT_STATUS_STR); + break; + default: + SetError(GOT_UNKNOWN_HANDSHAKE_STR, error, session, 0); + return -1; + } + + *sslBytes = startBytes - size; /* actual bytes of full process */ + + return ret; +} + + +/* Decrypt input into plain output, 0 on success */ +static int Decrypt(SSL* ssl, byte* output, const byte* input, word32 sz) +{ + int ret = 0; + + (void)output; + (void)input; + (void)sz; + + switch (ssl->specs.bulk_cipher_algorithm) { + #ifdef BUILD_ARC4 + case wolfssl_rc4: + wc_Arc4Process(ssl->decrypt.arc4, output, input, sz); + break; + #endif + + #ifdef BUILD_DES3 + case wolfssl_triple_des: + ret = wc_Des3_CbcDecrypt(ssl->decrypt.des3, output, input, sz); + break; + #endif + + #ifdef BUILD_AES + case wolfssl_aes: + ret = wc_AesCbcDecrypt(ssl->decrypt.aes, output, input, sz); + break; + #endif + + #ifdef HAVE_HC128 + case wolfssl_hc128: + wc_Hc128_Process(ssl->decrypt.hc128, output, input, sz); + break; + #endif + + #ifdef BUILD_RABBIT + case wolfssl_rabbit: + wc_RabbitProcess(ssl->decrypt.rabbit, output, input, sz); + break; + #endif + + #ifdef HAVE_CAMELLIA + case wolfssl_camellia: + wc_CamelliaCbcDecrypt(ssl->decrypt.cam, output, input, sz); + break; + #endif + + #ifdef HAVE_IDEA + case wolfssl_idea: + wc_IdeaCbcDecrypt(ssl->decrypt.idea, output, input, sz); + break; + #endif + + #ifdef HAVE_AESGCM + case wolfssl_aes_gcm: + if (sz >= (word32)(AESGCM_EXP_IV_SZ + ssl->specs.aead_mac_size)) + { + /* scratch buffer, sniffer ignores auth tag*/ + byte authTag[WOLFSSL_MIN_AUTH_TAG_SZ]; + + byte nonce[AESGCM_NONCE_SZ]; + XMEMCPY(nonce, ssl->keys.aead_dec_imp_IV, AESGCM_IMP_IV_SZ); + XMEMCPY(nonce + AESGCM_IMP_IV_SZ, input, AESGCM_EXP_IV_SZ); + + if (wc_AesGcmEncrypt(ssl->decrypt.aes, + output, + input + AESGCM_EXP_IV_SZ, + sz - AESGCM_EXP_IV_SZ - ssl->specs.aead_mac_size, + nonce, AESGCM_NONCE_SZ, + authTag, sizeof(authTag), + NULL, 0) < 0) { + Trace(BAD_DECRYPT); + ret = -1; + } + ForceZero(nonce, AESGCM_NONCE_SZ); + } + else { + Trace(BAD_DECRYPT_SIZE); + ret = -1; + } + break; + #endif + + default: + Trace(BAD_DECRYPT_TYPE); + ret = -1; + break; + } + + return ret; +} + + +/* Decrypt input message into output, adjust output steam if needed */ +static const byte* DecryptMessage(SSL* ssl, const byte* input, word32 sz, + byte* output, int* error, int* advance) +{ + int ivExtra = 0; + + int ret = Decrypt(ssl, output, input, sz); + if (ret != 0) { + *error = ret; + return NULL; + } + ssl->keys.encryptSz = sz; + if (ssl->options.tls1_1 && ssl->specs.cipher_type == block) { + output += ssl->specs.block_size; /* go past TLSv1.1 IV */ + ivExtra = ssl->specs.block_size; + *advance = ssl->specs.block_size; + } + + if (ssl->specs.cipher_type == aead) { + *advance = ssl->specs.aead_mac_size; + ssl->keys.padSz = ssl->specs.aead_mac_size; + } + else + ssl->keys.padSz = ssl->specs.hash_size; + + if (ssl->specs.cipher_type == block) + ssl->keys.padSz += *(output + sz - ivExtra - 1) + 1; + + return output; +} + + +/* remove session from table, use rowHint if no info (means we have a lock) */ +static void RemoveSession(SnifferSession* session, IpInfo* ipInfo, + TcpInfo* tcpInfo, word32 rowHint) +{ + SnifferSession* previous = 0; + SnifferSession* current; + word32 row = rowHint; + int haveLock = 0; + + if (ipInfo && tcpInfo) + row = SessionHash(ipInfo, tcpInfo); + else + haveLock = 1; + + assert(row <= HASH_SIZE); + Trace(REMOVE_SESSION_STR); + + if (!haveLock) + wc_LockMutex(&SessionMutex); + + current = SessionTable[row]; + + while (current) { + if (current == session) { + if (previous) + previous->next = current->next; + else + SessionTable[row] = current->next; + FreeSnifferSession(session); + TraceRemovedSession(); + break; + } + previous = current; + current = current->next; + } + + if (!haveLock) + wc_UnLockMutex(&SessionMutex); +} + + +/* Remove stale sessions from the Session Table, have a lock */ +static void RemoveStaleSessions(void) +{ + word32 i; + SnifferSession* session; + + for (i = 0; i < HASH_SIZE; i++) { + session = SessionTable[i]; + while (session) { + SnifferSession* next = session->next; + if (time(NULL) >= session->lastUsed + WOLFSSL_SNIFFER_TIMEOUT) { + TraceStaleSession(); + RemoveSession(session, NULL, NULL, i); + } + session = next; + } + } +} + + +/* Create a new Sniffer Session */ +static SnifferSession* CreateSession(IpInfo* ipInfo, TcpInfo* tcpInfo, + char* error) +{ + SnifferSession* session = 0; + int row; + + Trace(NEW_SESSION_STR); + /* create a new one */ + session = (SnifferSession*)malloc(sizeof(SnifferSession)); + if (session == NULL) { + SetError(MEMORY_STR, error, NULL, 0); + return 0; + } + InitSession(session); +#ifdef HAVE_EXTENDED_MASTER + { + HsHashes* newHash = (HsHashes*)malloc(sizeof(HsHashes)); + if (newHash == NULL) { + SetError(MEMORY_STR, error, NULL, 0); + free(session); + return 0; + } + if (HashInit(newHash) != 0) { + SetError(EXTENDED_MASTER_HASH_STR, error, NULL, 0); + free(session); + return 0; + } + session->hash = newHash; + } +#endif + session->server = ipInfo->dst; + session->client = ipInfo->src; + session->srvPort = (word16)tcpInfo->dstPort; + session->cliPort = (word16)tcpInfo->srcPort; + session->cliSeqStart = tcpInfo->sequence; + session->cliExpected = 1; /* relative */ + session->lastUsed= time(NULL); + + session->context = GetSnifferServer(ipInfo, tcpInfo); + if (session->context == NULL) { + SetError(SERVER_NOT_REG_STR, error, NULL, 0); + free(session); + return 0; + } + + session->sslServer = SSL_new(session->context->ctx); + if (session->sslServer == NULL) { + SetError(BAD_NEW_SSL_STR, error, session, FATAL_ERROR_STATE); + free(session); + return 0; + } + session->sslClient = SSL_new(session->context->ctx); + if (session->sslClient == NULL) { + SSL_free(session->sslServer); + session->sslServer = 0; + + SetError(BAD_NEW_SSL_STR, error, session, FATAL_ERROR_STATE); + free(session); + return 0; + } + /* put server back into server mode */ + session->sslServer->options.side = WOLFSSL_SERVER_END; + + row = SessionHash(ipInfo, tcpInfo); + + /* add it to the session table */ + wc_LockMutex(&SessionMutex); + + session->next = SessionTable[row]; + SessionTable[row] = session; + + SessionCount++; + + if ( (SessionCount % HASH_SIZE) == 0) { + TraceFindingStale(); + RemoveStaleSessions(); + } + + wc_UnLockMutex(&SessionMutex); + + /* determine headed side */ + if (ipInfo->dst == session->context->server && + tcpInfo->dstPort == session->context->port) + session->flags.side = WOLFSSL_SERVER_END; + else + session->flags.side = WOLFSSL_CLIENT_END; + + return session; +} + + +#ifdef OLD_HELLO_ALLOWED + +/* Process Old Client Hello Input */ +static int DoOldHello(SnifferSession* session, const byte* sslFrame, + int* rhSize, int* sslBytes, char* error) +{ + const byte* input = sslFrame; + byte b0, b1; + word32 idx = 0; + int ret; + + Trace(GOT_OLD_CLIENT_HELLO_STR); + session->flags.clientHello = 1; /* don't process again */ + b0 = *input++; + b1 = *input++; + *sslBytes -= 2; + *rhSize = ((b0 & 0x7f) << 8) | b1; + + if (*rhSize > *sslBytes) { + SetError(OLD_CLIENT_INPUT_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + + ret = ProcessOldClientHello(session->sslServer, input, &idx, *sslBytes, + (word16)*rhSize); + if (ret < 0 && ret != MATCH_SUITE_ERROR) { + SetError(BAD_OLD_CLIENT_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + + Trace(OLD_CLIENT_OK_STR); + XMEMCPY(session->sslClient->arrays->clientRandom, + session->sslServer->arrays->clientRandom, RAN_LEN); + + *sslBytes -= *rhSize; + return 0; +} + +#endif /* OLD_HELLO_ALLOWED */ + + +#if 0 +/* Calculate the TCP checksum, see RFC 1071 */ +/* return 0 for success, -1 on error */ +/* can be called from decode() with + TcpChecksum(&ipInfo, &tcpInfo, sslBytes, packet + ipInfo.length); + could also add a 64bit version if type available and using this +*/ +int TcpChecksum(IpInfo* ipInfo, TcpInfo* tcpInfo, int dataLen, + const byte* packet) +{ + TcpPseudoHdr pseudo; + int count = PSEUDO_HDR_SZ; + const word16* data = (word16*)&pseudo; + word32 sum = 0; + word16 checksum; + + pseudo.src = ipInfo->src; + pseudo.dst = ipInfo->dst; + pseudo.rsv = 0; + pseudo.protocol = TCP_PROTO; + pseudo.length = htons(tcpInfo->length + dataLen); + + /* pseudo header sum */ + while (count >= 2) { + sum += *data++; + count -= 2; + } + + count = tcpInfo->length + dataLen; + data = (word16*)packet; + + /* main sum */ + while (count > 1) { + sum += *data++; + count -=2; + } + + /* get left-over, if any */ + packet = (byte*)data; + if (count > 0) { + sum += *packet; + } + + /* fold 32bit sum into 16 bits */ + while (sum >> 16) + sum = (sum & 0xffff) + (sum >> 16); + + checksum = (word16)~sum; + /* checksum should now equal 0, since included already calcd checksum */ + /* field, but tcp checksum offloading could negate calculation */ + if (checksum == 0) + return 0; + return -1; +} +#endif + + +/* Check IP and TCP headers, set payload */ +/* returns 0 on success, -1 on error */ +static int CheckHeaders(IpInfo* ipInfo, TcpInfo* tcpInfo, const byte* packet, + int length, const byte** sslFrame, int* sslBytes, char* error) +{ + TraceHeader(); + TracePacket(); + + /* ip header */ + if (length < IP_HDR_SZ) { + SetError(PACKET_HDR_SHORT_STR, error, NULL, 0); + return -1; + } + if (CheckIpHdr((IpHdr*)packet, ipInfo, length, error) != 0) + return -1; + + /* tcp header */ + if (length < (ipInfo->length + TCP_HDR_SZ)) { + SetError(PACKET_HDR_SHORT_STR, error, NULL, 0); + return -1; + } + if (CheckTcpHdr((TcpHdr*)(packet + ipInfo->length), tcpInfo, error) != 0) + return -1; + + /* setup */ + *sslFrame = packet + ipInfo->length + tcpInfo->length; + if (*sslFrame > packet + length) { + SetError(PACKET_HDR_SHORT_STR, error, NULL, 0); + return -1; + } + *sslBytes = (int)(packet + length - *sslFrame); + + return 0; +} + + +/* Create or Find existing session */ +/* returns 0 on success (continue), -1 on error, 1 on success (end) */ +static int CheckSession(IpInfo* ipInfo, TcpInfo* tcpInfo, int sslBytes, + SnifferSession** session, char* error) +{ + /* create a new SnifferSession on client SYN */ + if (tcpInfo->syn && !tcpInfo->ack) { + TraceClientSyn(tcpInfo->sequence); + *session = CreateSession(ipInfo, tcpInfo, error); + if (*session == NULL) { + *session = GetSnifferSession(ipInfo, tcpInfo); + /* already had existing, so OK */ + if (*session) + return 1; + + SetError(MEMORY_STR, error, NULL, 0); + return -1; + } + return 1; + } + /* get existing sniffer session */ + else { + *session = GetSnifferSession(ipInfo, tcpInfo); + if (*session == NULL) { + /* don't worry about extraneous RST or duplicate FINs */ + if (tcpInfo->fin || tcpInfo->rst) + return 1; + /* don't worry about duplicate ACKs either */ + if (sslBytes == 0 && tcpInfo->ack) + return 1; + + SetError(BAD_SESSION_STR, error, NULL, 0); + return -1; + } + } + return 0; +} + + +/* Create a Packet Buffer from *begin - end, adjust new *begin and bytesLeft */ +static PacketBuffer* CreateBuffer(word32* begin, word32 end, const byte* data, + int* bytesLeft) +{ + PacketBuffer* pb; + + int added = end - *begin + 1; + assert(*begin <= end); + + pb = (PacketBuffer*)malloc(sizeof(PacketBuffer)); + if (pb == NULL) return NULL; + + pb->next = 0; + pb->begin = *begin; + pb->end = end; + pb->data = (byte*)malloc(added); + + if (pb->data == NULL) { + free(pb); + return NULL; + } + XMEMCPY(pb->data, data, added); + + *bytesLeft -= added; + *begin = pb->end + 1; + + return pb; +} + + +/* Add sslFrame to Reassembly List */ +/* returns 1 (end) on success, -1, on error */ +static int AddToReassembly(byte from, word32 seq, const byte* sslFrame, + int sslBytes, SnifferSession* session, char* error) +{ + PacketBuffer* add; + PacketBuffer** front = (from == WOLFSSL_SERVER_END) ? + &session->cliReassemblyList: &session->srvReassemblyList; + 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; + } + + /* add to front if before current front, up to next->begin */ + if (seq < curr->begin) { + word32 end = seq + sslBytes - 1; + + 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); + return -1; + } + add->next = curr; + *front = add; + *reassemblyMemory += sslBytes; + } + + /* while we have bytes left, try to find a gap to fill */ + while (bytesLeft > 0) { + /* get previous packet in list */ + while (curr && (seq >= curr->begin)) { + prev = curr; + curr = curr->next; + } + + /* don't add duplicate data */ + if (prev->end >= seq) { + if ( (seq + bytesLeft - 1) <= prev->end) + return 1; + seq = prev->end + 1; + bytesLeft = startSeq + sslBytes - seq; + } + + if (!curr) + /* we're at the end */ + added = bytesLeft; + else + /* we're in between two frames */ + added = min((word32)bytesLeft, curr->begin - seq); + + /* data already there */ + 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) { + SetError(MEMORY_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + add->next = prev->next; + prev->next = add; + *reassemblyMemory += added; + } + return 1; +} + + +/* Add out of order FIN capture */ +/* returns 1 for success (end) */ +static int AddFinCapture(SnifferSession* session, word32 sequence) +{ + if (session->flags.side == WOLFSSL_SERVER_END) { + if (session->finCaputre.cliCounted == 0) + session->finCaputre.cliFinSeq = sequence; + } + else { + if (session->finCaputre.srvCounted == 0) + session->finCaputre.srvFinSeq = sequence; + } + return 1; +} + + +/* Adjust incoming sequence based on side */ +/* returns 0 on success (continue), -1 on error, 1 on success (end) */ +static int AdjustSequence(TcpInfo* tcpInfo, SnifferSession* session, + int* sslBytes, const byte** sslFrame, char* error) +{ + word32 seqStart = (session->flags.side == WOLFSSL_SERVER_END) ? + session->cliSeqStart :session->srvSeqStart; + word32 real = tcpInfo->sequence - seqStart; + word32* expected = (session->flags.side == WOLFSSL_SERVER_END) ? + &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) + real = 0xffffffffU - seqStart + tcpInfo->sequence; + + TraceRelativeSequence(*expected, real); + + if (real < *expected) { + Trace(DUPLICATE_STR); + if (real + *sslBytes > *expected) { + int overlap = *expected - real; + Trace(OVERLAP_DUPLICATE_STR); + + /* adjust to expected, remove duplicate */ + *sslFrame += overlap; + *sslBytes -= overlap; + + /* The following conditional block is duplicated below. It is the + * same action but for a different setup case. If changing this + * block be sure to also update the block below. */ + if (reassemblyList) { + word32 newEnd = *expected + *sslBytes; + + if (newEnd > reassemblyList->begin) { + Trace(OVERLAP_REASSEMBLY_BEGIN_STR); + + /* remove bytes already on reassembly list */ + *sslBytes -= newEnd - reassemblyList->begin; + } + if (newEnd > reassemblyList->end) { + Trace(OVERLAP_REASSEMBLY_END_STR); + + /* may be past reassembly list end (could have more on list) + so try to add what's past the front->end */ + AddToReassembly(session->flags.side, reassemblyList->end +1, + *sslFrame + reassemblyList->end - *expected + 1, + newEnd - reassemblyList->end, session, error); + } + } + } + else + return 1; + } + else if (real > *expected) { + Trace(OUT_OF_ORDER_STR); + 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 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. */ + else if (reassemblyList) { + word32 newEnd = *expected + *sslBytes; + + if (newEnd > reassemblyList->begin) { + Trace(OVERLAP_REASSEMBLY_BEGIN_STR); + + /* remove bytes already on reassembly list */ + *sslBytes -= newEnd - reassemblyList->begin; + } + if (newEnd > reassemblyList->end) { + Trace(OVERLAP_REASSEMBLY_END_STR); + + /* may be past reassembly list end (could have more on list) + so try to add what's past the front->end */ + AddToReassembly(session->flags.side, reassemblyList->end +1, + *sslFrame + reassemblyList->end - *expected + 1, + newEnd - reassemblyList->end, session, error); + } + } + } + /* got expected sequence */ + *expected += *sslBytes; + if (tcpInfo->fin) + *expected += 1; + + return 0; +} + + +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) { +#ifdef BUILD_AES + wc_AesSetIV(ssl->decrypt.aes, + curr->data + curr->end - curr->begin + - ssl->specs.block_size + 1); +#endif + } + else if (ssl->specs.bulk_cipher_algorithm == wolfssl_triple_des) { +#ifdef BUILD_DES3 + wc_Des3_SetIV(ssl->decrypt.des3, + curr->data + curr->end - curr->begin + - ssl->specs.block_size + 1); +#endif + } + } + + 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) +{ + if (tcpInfo->ack) { + word32 seqStart = (session->flags.side == WOLFSSL_SERVER_END) ? + session->srvSeqStart :session->cliSeqStart; + word32 real = tcpInfo->ackNumber - seqStart; + word32 expected = (session->flags.side == WOLFSSL_SERVER_END) ? + session->srvExpected : session->cliExpected; + + /* handle rollover of sequence */ + if (tcpInfo->ackNumber < seqStart) + real = 0xffffffffU - seqStart + tcpInfo->ackNumber; + + TraceAck(real, expected); + + if (real > expected) + return -1; /* we missed a packet, ACKing data we never saw */ + } + return 0; +} + + +/* Check TCP Sequence status */ +/* returns 0 on success (continue), -1 on error, 1 on success (end) */ +static int CheckSequence(IpInfo* ipInfo, TcpInfo* tcpInfo, + SnifferSession* session, int* sslBytes, + 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; + session->srvExpected = 1; + TraceServerSyn(tcpInfo->sequence); + return 1; + } + + /* adjust potential ethernet trailer */ + actualLen = ipInfo->total - ipInfo->length - tcpInfo->length; + if (*sslBytes > actualLen) { + *sslBytes = actualLen; + } + + TraceSequence(tcpInfo->sequence, *sslBytes); + if (CheckAck(tcpInfo, session) < 0) { + 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); +} + + +/* Check Status before record processing */ +/* returns 0 on success (continue), -1 on error, 1 on success (end) */ +static int CheckPreRecord(IpInfo* ipInfo, TcpInfo* tcpInfo, + const byte** sslFrame, SnifferSession** session, + int* sslBytes, const byte** end, char* error) +{ + 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 */ + if (tcpInfo->fin) + (*session)->flags.finCount += 1; + else if (tcpInfo->rst) + (*session)->flags.finCount += 2; + + if ((*session)->flags.finCount >= 2) { + RemoveSession(*session, ipInfo, tcpInfo, 0); + *session = NULL; + return 1; + } + } + + if ((*session)->flags.fatalError == FATAL_ERROR_STATE) { + SetError(FATAL_ERROR_STR, error, NULL, 0); + 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 skipping, the data is already at the end of partial */ + if ( !skipPartial && + (length = ssl->buffers.inputBuffer.length) ) { + Trace(PARTIAL_ADD_STR); + + if ( (*sslBytes + length) > ssl->buffers.inputBuffer.bufferSize) { + if (GrowInputBuffer(ssl, *sslBytes, length) < 0) { + SetError(MEMORY_STR, error, *session, FATAL_ERROR_STATE); + return -1; + } + } + XMEMCPY(&ssl->buffers.inputBuffer.buffer[length], *sslFrame, *sslBytes); + *sslBytes += length; + ssl->buffers.inputBuffer.length = *sslBytes; + *sslFrame = ssl->buffers.inputBuffer.buffer; + *end = *sslFrame + *sslBytes; + } + + if ((*session)->flags.clientHello == 0 && **sslFrame != handshake) { + /* Sanity check the packet for an old style client hello. */ + int rhSize = (((*sslFrame)[0] & 0x7f) << 8) | ((*sslFrame)[1]); + + if ((rhSize <= (*sslBytes - 2)) && + (*sslFrame)[2] == OLD_HELLO_ID && (*sslFrame)[3] == SSLv3_MAJOR) { +#ifdef OLD_HELLO_ALLOWED + int ret = DoOldHello(*session, *sslFrame, &rhSize, sslBytes, error); + if (ret < 0) + return -1; /* error already set */ + if (*sslBytes <= 0) + return 1; +#endif + } + else { +#ifdef STARTTLS_ALLOWED + return 1; +#endif + } + } + + return 0; +} + + +/* See if input on the reassembly list is ready for consuming */ +/* returns 1 for TRUE, 0 for FALSE */ +static int HaveMoreInput(SnifferSession* session, const byte** sslFrame, + int* sslBytes, const byte** end, char* error) +{ + /* sequence and reassembly based on from, not to */ + int moreInput = 0; + PacketBuffer** front = (session->flags.side == WOLFSSL_SERVER_END) ? + &session->cliReassemblyList : &session->srvReassemblyList; + word32* expected = (session->flags.side == WOLFSSL_SERVER_END) ? + &session->cliExpected : &session->srvExpected; + /* buffer is on receiving end */ + word32* length = (session->flags.side == WOLFSSL_SERVER_END) ? + &session->sslServer->buffers.inputBuffer.length : + &session->sslClient->buffers.inputBuffer.length; + byte** myBuffer = (session->flags.side == WOLFSSL_SERVER_END) ? + &session->sslServer->buffers.inputBuffer.buffer : + &session->sslClient->buffers.inputBuffer.buffer; + word32* bufferSize = (session->flags.side == WOLFSSL_SERVER_END) ? + &session->sslServer->buffers.inputBuffer.bufferSize : + &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; + word32 packetLen = (*front)->end - (*front)->begin + 1; + + if (packetLen > room && *bufferSize < MAX_INPUT_SZ) { + if (GrowInputBuffer(ssl, packetLen, *length) < 0) { + SetError(MEMORY_STR, error, session, FATAL_ERROR_STATE); + return 0; + } + room = *bufferSize - *length; /* bufferSize is now bigger */ + } + + if (packetLen <= room) { + PacketBuffer* del = *front; + byte* buf = *myBuffer; + + XMEMCPY(&buf[*length], (*front)->data, packetLen); + *length += packetLen; + *expected += packetLen; + + /* remove used packet */ + *front = (*front)->next; + + *reassemblyMemory -= packetLen; + FreePacketBuffer(del); + + moreInput = 1; + } + else + break; + } + if (moreInput) { + *sslFrame = *myBuffer; + *sslBytes = *length; + *end = *myBuffer + *length; + } + return moreInput; +} + + + +/* Process Message(s) from sslFrame */ +/* return Number of bytes on success, 0 for no data yet, and -1 on error */ +static int ProcessMessage(const byte* sslFrame, SnifferSession* session, + int sslBytes, byte** data, const byte* end, + char* error) +{ + const byte* sslBegin = sslFrame; + const byte* recordEnd; /* end of record indicator */ + const byte* inRecordEnd; /* indicator from input stream not decrypt */ + RecordLayerHeader rh; + int rhSize = 0; + int ret; + int errCode = 0; + int decoded = 0; /* bytes stored for user in data */ + int notEnough; /* notEnough bytes yet flag */ + int decrypted = 0; /* was current msg decrypted */ + SSL* ssl = (session->flags.side == WOLFSSL_SERVER_END) ? + session->sslServer : session->sslClient; +doMessage: + notEnough = 0; + if (sslBytes < 0) { + SetError(PACKET_HDR_SHORT_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + if (sslBytes >= RECORD_HEADER_SZ) { + if (GetRecordHeader(sslFrame, &rh, &rhSize) != 0) { + SetError(BAD_RECORD_HDR_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + } + else + notEnough = 1; + + if (notEnough || rhSize > (sslBytes - RECORD_HEADER_SZ)) { + /* don't have enough input yet to process full SSL record */ + Trace(PARTIAL_INPUT_STR); + + /* store partial if not there already or we advanced */ + if (ssl->buffers.inputBuffer.length == 0 || sslBegin != sslFrame) { + if (sslBytes > (int)ssl->buffers.inputBuffer.bufferSize) { + if (GrowInputBuffer(ssl, sslBytes, 0) < 0) { + SetError(MEMORY_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + } + XMEMMOVE(ssl->buffers.inputBuffer.buffer, sslFrame, sslBytes); + ssl->buffers.inputBuffer.length = sslBytes; + } + if (HaveMoreInput(session, &sslFrame, &sslBytes, &end, error)) + goto doMessage; + return decoded; + } + sslFrame += RECORD_HEADER_SZ; + sslBytes -= RECORD_HEADER_SZ; + recordEnd = sslFrame + rhSize; /* may have more than one record */ + inRecordEnd = recordEnd; + + /* decrypt if needed */ + if ((session->flags.side == WOLFSSL_SERVER_END && + session->flags.serverCipherOn) + || (session->flags.side == WOLFSSL_CLIENT_END && + session->flags.clientCipherOn)) { + int ivAdvance = 0; /* TLSv1.1 advance amount */ + if (ssl->decrypt.setup != 1) { + SetError(DECRYPT_KEYS_NOT_SETUP, error, session, FATAL_ERROR_STATE); + return -1; + } + if (CheckAvailableSize(ssl, rhSize) < 0) { + SetError(MEMORY_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + sslFrame = DecryptMessage(ssl, sslFrame, rhSize, + ssl->buffers.outputBuffer.buffer, &errCode, + &ivAdvance); + recordEnd = sslFrame - ivAdvance + rhSize; /* sslFrame moved so + should recordEnd */ + decrypted = 1; + if (errCode != 0) { + SetError(BAD_DECRYPT, error, session, FATAL_ERROR_STATE); + return -1; + } + } + +doPart: + + switch ((enum ContentType)rh.type) { + case handshake: + { + int startIdx = sslBytes; + int used; + + Trace(GOT_HANDSHAKE_STR); + ret = DoHandShake(sslFrame, &sslBytes, session, error); + if (ret != 0) { + if (session->flags.fatalError == 0) + SetError(BAD_HANDSHAKE_STR, error, session, + FATAL_ERROR_STATE); + return -1; + } + + /* DoHandShake now fully decrements sslBytes to remaining */ + used = startIdx - sslBytes; + sslFrame += used; + if (decrypted) + sslFrame += ssl->keys.padSz; + } + break; + case change_cipher_spec: + if (session->flags.side == WOLFSSL_SERVER_END) + session->flags.serverCipherOn = 1; + else + session->flags.clientCipherOn = 1; + Trace(GOT_CHANGE_CIPHER_STR); + ssl->options.handShakeState = HANDSHAKE_DONE; + ssl->options.handShakeDone = 1; + + sslFrame += 1; + sslBytes -= 1; + + break; + case application_data: + Trace(GOT_APP_DATA_STR); + { + word32 inOutIdx = 0; + + ret = DoApplicationData(ssl, (byte*)sslFrame, &inOutIdx); + if (ret == 0) { + ret = ssl->buffers.clearOutputBuffer.length; + TraceGotData(ret); + if (ret) { /* may be blank message */ + byte* tmpData; /* don't leak on realloc free */ + /* add an extra byte at end of allocation in case user + * wants to null terminate plaintext */ + tmpData = (byte*)realloc(*data, decoded + ret + 1); + if (tmpData == NULL) { + ForceZero(*data, decoded); + free(*data); + *data = NULL; + SetError(MEMORY_STR, error, session, + FATAL_ERROR_STATE); + return -1; + } + *data = tmpData; + XMEMCPY(*data + decoded, + ssl->buffers.clearOutputBuffer.buffer, ret); + TraceAddedData(ret, decoded); + decoded += ret; + ssl->buffers.clearOutputBuffer.length = 0; + } + } + else { + SetError(BAD_APP_DATA_STR, error,session,FATAL_ERROR_STATE); + return -1; + } + if (ssl->buffers.outputBuffer.dynamicFlag) + ShrinkOutputBuffer(ssl); + + sslFrame += inOutIdx; + sslBytes -= inOutIdx; + } + break; + case alert: + Trace(GOT_ALERT_STR); + sslFrame += rhSize; + sslBytes -= rhSize; + break; + case no_type: + default: + SetError(GOT_UNKNOWN_RECORD_STR, error, session, FATAL_ERROR_STATE); + return -1; + } + + /* do we have another msg in record ? */ + if (sslFrame < recordEnd) { + Trace(ANOTHER_MSG_STR); + goto doPart; + } + + /* back to input stream instead of potential decrypt buffer */ + recordEnd = inRecordEnd; + + /* do we have more records ? */ + if (recordEnd < end) { + Trace(ANOTHER_MSG_STR); + sslFrame = recordEnd; + sslBytes = (int)(end - recordEnd); + goto doMessage; + } + + /* clear used input */ + ssl->buffers.inputBuffer.length = 0; + + /* could have more input ready now */ + if (HaveMoreInput(session, &sslFrame, &sslBytes, &end, error)) + goto doMessage; + + if (ssl->buffers.inputBuffer.dynamicFlag) + ShrinkInputBuffer(ssl, NO_FORCED_FREE); + + return decoded; +} + + +/* See if we need to process any pending FIN captures */ +static void CheckFinCapture(IpInfo* ipInfo, TcpInfo* tcpInfo, + SnifferSession* session) +{ + if (session->finCaputre.cliFinSeq && session->finCaputre.cliFinSeq <= + session->cliExpected) { + if (session->finCaputre.cliCounted == 0) { + session->flags.finCount += 1; + session->finCaputre.cliCounted = 1; + TraceClientFin(session->finCaputre.cliFinSeq, session->cliExpected); + } + } + + if (session->finCaputre.srvFinSeq && session->finCaputre.srvFinSeq <= + session->srvExpected) { + if (session->finCaputre.srvCounted == 0) { + session->flags.finCount += 1; + session->finCaputre.srvCounted = 1; + TraceServerFin(session->finCaputre.srvFinSeq, session->srvExpected); + } + } + + if (session->flags.finCount >= 2) + RemoveSession(session, ipInfo, tcpInfo, 0); +} + + +/* If session is in fatal error state free resources now + return true if removed, 0 otherwise */ +static int RemoveFatalSession(IpInfo* ipInfo, TcpInfo* tcpInfo, + SnifferSession* session, char* error) +{ + if (session && session->flags.fatalError == FATAL_ERROR_STATE) { + RemoveSession(session, ipInfo, tcpInfo, 0); + SetError(FATAL_ERROR_STR, error, NULL, 0); + return 1; + } + return 0; +} + + +/* Passes in an IP/TCP packet for decoding (ethernet/localhost frame) removed */ +/* returns Number of bytes on success, 0 for no data yet, and -1 on error */ +int ssl_DecodePacket(const byte* packet, int length, byte** data, char* error) +{ + TcpInfo tcpInfo; + IpInfo ipInfo; + const byte* sslFrame; + const byte* end = packet + length; + int sslBytes; /* ssl bytes unconsumed */ + int ret; + SnifferSession* session = 0; + + if (CheckHeaders(&ipInfo, &tcpInfo, packet, length, &sslFrame, &sslBytes, + error) != 0) + return -1; + + ret = CheckSession(&ipInfo, &tcpInfo, sslBytes, &session, error); + if (RemoveFatalSession(&ipInfo, &tcpInfo, session, error)) return -1; + else if (ret == -1) return -1; + else if (ret == 1) return 0; /* done for now */ + + ret = CheckSequence(&ipInfo, &tcpInfo, session, &sslBytes, &sslFrame,error); + if (RemoveFatalSession(&ipInfo, &tcpInfo, session, error)) return -1; + else if (ret == -1) return -1; + else if (ret == 1) return 0; /* done for now */ + + ret = CheckPreRecord(&ipInfo, &tcpInfo, &sslFrame, &session, &sslBytes, + &end, error); + if (RemoveFatalSession(&ipInfo, &tcpInfo, session, error)) return -1; + else if (ret == -1) return -1; + else if (ret == 1) return 0; /* done for now */ + + ret = ProcessMessage(sslFrame, session, sslBytes, data, end, error); + if (RemoveFatalSession(&ipInfo, &tcpInfo, session, error)) return -1; + CheckFinCapture(&ipInfo, &tcpInfo, session); + return ret; +} + + +/* Deallocator for the decoded data buffer. */ +/* returns 0 on success, -1 on error */ +int ssl_FreeDecodeBuffer(byte** data, char* error) +{ + return ssl_FreeZeroDecodeBuffer(data, 0, error); +} + + +/* Deallocator for the decoded data buffer, zeros out buffer. */ +/* returns 0 on success, -1 on error */ +int ssl_FreeZeroDecodeBuffer(byte** data, int sz, char* error) +{ + (void)error; + + if (sz < 0) { + return -1; + } + + if (data != NULL) { + ForceZero(*data, (word32)sz); + free(*data); + *data = NULL; + } + + return 0; +} + + +/* Enables (if traceFile)/ Disables debug tracing */ +/* returns 0 on success, -1 on error */ +int ssl_Trace(const char* traceFile, char* error) +{ + if (traceFile) { + TraceFile = fopen(traceFile, "a"); + if (!TraceFile) { + SetError(BAD_TRACE_FILE_STR, error, NULL, 0); + return -1; + } + TraceOn = 1; + } + else + TraceOn = 0; + + return 0; +} + + +/* Enables/Disables Recovery of missed data if later packets allow + * maxMemory is number of bytes to use for reassembly buffering per session, + * -1 means unlimited + * returns 0 on success, -1 on error */ +int ssl_EnableRecovery(int onOff, int maxMemory, char* error) +{ + (void)error; + + RecoveryEnabled = onOff; + if (onOff) + MaxRecoveryMemory = maxMemory; + + return 0; +} + + + +int ssl_GetSessionStats(unsigned int* active, unsigned int* total, + unsigned int* peak, unsigned int* maxSessions, + unsigned int* missedData, unsigned int* reassemblyMem, + char* error) +{ + int ret; + + if (missedData) { + wc_LockMutex(&RecoveryMutex); + *missedData = MissedDataSessions; + wc_UnLockMutex(&RecoveryMutex); + } + + if (reassemblyMem) { + SnifferSession* session; + int i; + + *reassemblyMem = 0; + wc_LockMutex(&SessionMutex); + for (i = 0; i < HASH_SIZE; i++) { + session = SessionTable[i]; + while (session) { + *reassemblyMem += session->cliReassemblyMemory; + *reassemblyMem += session->srvReassemblyMemory; + session = session->next; + } + } + wc_UnLockMutex(&SessionMutex); + } + + ret = wolfSSL_get_session_stats(active, total, peak, maxSessions); + + if (ret == SSL_SUCCESS) + return 0; + else { + SetError(BAD_SESSION_STATS, error, NULL, 0); + return -1; + } +} + + + +#endif /* WOLFSSL_SNIFFER */ +#endif /* WOLFCRYPT_ONLY */ +