/* sniffer.c * * Copyright (C) 2006-2015 wolfSSL Inc. * * This file is part of wolfSSL. (formerly known as CyaSSL) * * 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-1301, USA */ #ifdef HAVE_CONFIG_H #include #endif #include #ifndef WOLFCRYPT_ONLY #ifdef WOLFSSL_SNIFFER #include #include #ifndef _WIN32 #include #endif #ifdef _WIN32 #define SNPRINTF _snprintf #else #define SNPRINTF snprintf #endif #include #include #include #include #include #ifdef NO_INLINE #include #else #include #endif #ifndef WOLFSSL_HAVE_MIN #define WOLFSSL_HAVE_MIN static INLINE word32 min(word32 a, word32 b) { return a > b ? b : a; } #endif /* WOLFSSL_HAVE_MIN */ #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 legnth, min */ TCP_HDR_SZ = 20, /* TCP header legnth, 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 */ 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" }; /* *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 */ } 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; /* 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 */ } 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) { LockMutex(&RecoveryMutex); MissedDataSessions += 1; UnLockMutex(&RecoveryMutex); } /* Initialize overall Sniffer */ void ssl_InitSniffer(void) { wolfSSL_Init(); InitMutex(&ServerListMutex); InitMutex(&SessionMutex); 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 LockMutex(&srv->namedKeysMutex); FreeNamedKeyList(srv->namedKeys); UnLockMutex(&srv->namedKeysMutex); 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); } free(session); } /* Free overall Sniffer */ void ssl_FreeSniffer(void) { SnifferServer* srv; SnifferServer* removeServer; SnifferSession* session; SnifferSession* removeSession; int i; LockMutex(&ServerListMutex); 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); } } UnLockMutex(&SessionMutex); UnLockMutex(&ServerListMutex); FreeMutex(&RecoveryMutex); FreeMutex(&SessionMutex); FreeMutex(&ServerListMutex); if (TraceFile) { TraceOn = 0; fclose(TraceFile); TraceFile = NULL; } wolfSSL_Cleanup(); } /* 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; 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; } /* 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); } /* 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 legnth; /* 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 exisiting 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; LockMutex(&ServerListMutex); sniffer = ServerList; while (sniffer) { if (sniffer->server == addr) { ret = 1; break; } sniffer = sniffer->next; } 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; LockMutex(&ServerListMutex); sniffer = ServerList; while (sniffer) { if (sniffer->port == (int)port) { ret = 1; break; } sniffer = sniffer->next; } UnLockMutex(&ServerListMutex); return ret; } /* Get SnifferServer from IP and Port */ static SnifferServer* GetSnifferServer(IpInfo* ipInfo, TcpInfo* tcpInfo) { SnifferServer* sniffer; 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; } 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); 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 */ 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; byte* saveBuf; long fileSz = 0; int saveBufSz; 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, fileSz, 1, file); XFCLOSE(file); if (typeKey == SSL_FILETYPE_PEM) { saveBuf = (byte*)malloc(fileSz); saveBufSz = wolfSSL_KeyPemToDer(loadBuf, (int)fileSz, saveBuf, (int)fileSz, password); free(loadBuf); *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 { LockMutex(&sniffer->namedKeysMutex); namedKey->next = sniffer->namedKeys; sniffer->namedKeys = namedKey; 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); LockMutex(&ServerListMutex); ret = SetNamedPrivateKey(name, address, port, keyFile, typeKey, password, error); 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); LockMutex(&ServerListMutex); ret = SetNamedPrivateKey(NULL, address, port, keyFile, typeKey, password, error); 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.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; } 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(const byte* input, int* sslBytes, SnifferSession* session, char* error) { ProtocolVersion pv; byte b; int toRead = VERSION_SZ + RAN_LEN + ENUM_LEN; int doResume = 0; /* 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; } 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); 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; 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) { UnLockMutex(&session->context->namedKeysMutex); SetError(CLIENT_HELLO_LATE_KEY_STR, error, session, FATAL_ERROR_STATE); return -1; } break; } else namedKey = namedKey->next; } 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); 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; } 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(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); 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; 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)(AEAD_EXP_IV_SZ + ssl->specs.aead_mac_size)) { byte nonce[AEAD_NONCE_SZ]; XMEMCPY(nonce, ssl->keys.aead_dec_imp_IV, AEAD_IMP_IV_SZ); XMEMCPY(nonce + AEAD_IMP_IV_SZ, input, AEAD_EXP_IV_SZ); if (wc_AesGcmEncrypt(ssl->decrypt.aes, output, input + AEAD_EXP_IV_SZ, sz - AEAD_EXP_IV_SZ - ssl->specs.aead_mac_size, nonce, AEAD_NONCE_SZ, NULL, 0, NULL, 0) < 0) { Trace(BAD_DECRYPT); ret = -1; } ForceZero(nonce, AEAD_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) 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) 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); 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 */ LockMutex(&SessionMutex); session->next = SessionTable[row]; SessionTable[row] = session; SessionCount++; if ( (SessionCount % HASH_SIZE) == 0) { TraceFindingStale(); RemoveStaleSessions(); } 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.legnth = 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 exisiting, 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) 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) { 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 alread 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; /* indictor 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) { 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) { (void)error; if (data != NULL) { 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) { LockMutex(&RecoveryMutex); *missedData = MissedDataSessions; 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); if (ret == SSL_SUCCESS) return 0; else { SetError(BAD_SESSION_STATS, error, NULL, 0); return -1; } } #endif /* WOLFSSL_SNIFFER */ #endif /* WOLFCRYPT_ONLY */