00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #ifdef HAVE_CONFIG_H
00024 #include "config.h"
00025 #endif
00026
00027 #include <sys/types.h>
00028 #ifndef WIN32
00029 #include <sys/socket.h>
00030 #include <netinet/in.h>
00031 #include <arpa/inet.h>
00032 #endif
00033 #include <stdlib.h>
00034 #include <string.h>
00035 #ifdef HAVE_STRINGS_H
00036 #include <strings.h>
00037 #endif
00038
00039 #include "spp_conversation.h"
00040 #include "spp_portscan2.h"
00041
00042 #include "generators.h"
00043 #include "decode.h"
00044 #include "plugbase.h"
00045 #include "debug.h"
00046 #include "util.h"
00047 #include "parser.h"
00048 #include "mstring.h"
00049 #include "log.h"
00050 #include "detect.h"
00051 #include "event_queue.h"
00052
00053 #define CONV_TIMEOUT 120
00054 #define CONV_DEFAULT_MAX 65335
00055
00056 #define OPT_TIMEOUT "timeout"
00057 #define OPT_MAX_CONV "max_conversations"
00058 #define OPT_ALLOWED_PROTOS "allowed_ip_protocols"
00059 #define OPT_ALERT_BAD_PROTO "alert_odd_protocols"
00060
00061
00062
00063
00064
00065
00066
00067 #define PACKET_FORWARD(a) (*((unsigned int*)&a->iph->ip_dst) > *((unsigned int*)&a->iph->ip_src))
00068
00069 #define TRUE 1
00070 #define FALSE 0
00071
00072
00073
00074
00075 #ifdef DEBUG
00076 #ifdef INLINE
00077 #undef INLINE
00078 #endif
00079 #define INLINE
00080 #else
00081 #ifndef INLINE
00082 #define INLINE inline
00083 #endif
00084 #endif
00085
00086
00087 ConversationData conv_data;
00088
00089
00090
00091 static void ConvInit(u_char* args);
00092 static void ParseConvArgs(u_char* args);
00093 static void ConvFunc(Packet* p, void *);
00094 static int ConvCompareFunc(ubi_trItemPtr ItemPtr, ubi_trNodePtr NodePtr);
00095 static int PruneConvCache(u_int32_t now, int tokill, StateRecord *keeper);
00096 static StateRecord* ConvGetSession(Packet* p);
00097 static INLINE void FillStateRecord(StateRecord *s, Packet *p);
00098 static INLINE void FillConvStats(StateRecord *s, Packet *p);
00099
00100
00101
00102 void SetupConv(void)
00103 {
00104 RegisterPreprocessor("conversation", ConvInit);
00105 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,
00106 "Preprocessor: Registering Session\n"););
00107 }
00108
00109 static void ParseConvArgs(u_char* args)
00110 {
00111 char **toks;
00112 char **stoks;
00113 char *index;
00114 int num_toks, s_toks;
00115 int num;
00116
00117 conv_data.timeout = CONV_TIMEOUT;
00118 conv_data.max_convs = CONV_DEFAULT_MAX;
00119
00120 if(args == NULL || *args == '\0')
00121 {
00122 return;
00123 }
00124
00125
00126 toks = mSplit(args, ",", 31, &num_toks, '\\');
00127
00128
00129
00130
00131 for(num = 0; num < num_toks; num++)
00132 {
00133 index = toks[num];
00134
00135 while(index && isspace((int)*(index)))
00136 {
00137 index++;
00138 }
00139
00140 if(!strncasecmp(OPT_TIMEOUT, index, strlen(OPT_TIMEOUT)))
00141 {
00142 stoks = mSplit(index, " ", 4, &s_toks, 0);
00143 if(s_toks < 2)
00144 {
00145 FatalError("ERROR %s(%d) => No timeout argument to "
00146 "conversation\n", file_name, file_line);
00147 }
00148
00149 conv_data.timeout = atoi(stoks[1]);
00150 mSplitFree(&stoks, s_toks);
00151 }
00152 else if(!strncasecmp(OPT_MAX_CONV, index,
00153 strlen(OPT_MAX_CONV)))
00154 {
00155 stoks = mSplit(index, " ", 4, &s_toks, 0);
00156 if(s_toks < 2)
00157 {
00158 FatalError("ERROR %s(%d) => No max_conversations argument "
00159 "to conversation\n", file_name, file_line);
00160 }
00161
00162 conv_data.max_convs = atoi(stoks[1]);
00163 mSplitFree(&stoks, s_toks);
00164 }
00165 else if(!strncasecmp(OPT_ALLOWED_PROTOS, index,
00166 strlen(OPT_ALLOWED_PROTOS)))
00167 {
00168
00169
00170
00171
00172
00173
00174
00175 char **ports;
00176 int num_ports;
00177 char *port;
00178 int j = 0;
00179 u_int32_t portnum;
00180
00181 for(j = 0;j<256;j++)
00182 {
00183 conv_data.allowed_ip_protocols[j] = 0;
00184 }
00185
00186 ports = mSplit(index, " ", 40, &num_ports, 0);
00187
00188 if(num_ports < 2)
00189 {
00190 FatalError("ERROR %s(%d) => No ip_proto list "
00191 "to conversation\n", file_name, file_line);
00192 }
00193
00194 j = 1;
00195
00196 while(j < num_ports)
00197 {
00198 port = ports[j];
00199
00200 if(isdigit((int)port[0]))
00201 {
00202 portnum = atoi(port);
00203
00204 if(portnum > 255)
00205 {
00206 FatalError("ERROR %s(%d) => Bad ip_proto list to "
00207 "conversation\n", file_name, file_line);
00208 }
00209
00210 conv_data.allowed_ip_protocols[portnum] = 1;
00211 }
00212 else if(!strncasecmp(port, "all", 3))
00213 {
00214 memset(&conv_data.allowed_ip_protocols, 1, 256);
00215 }
00216 else
00217 {
00218 FatalError("ERROR %s(%d) => Bad ip_proto list to "
00219 "conversation\n", file_name, file_line);
00220 }
00221
00222 j++;
00223 }
00224 mSplitFree(&ports, num_ports);
00225 }
00226 else if(!strncasecmp(OPT_ALERT_BAD_PROTO, index,
00227 strlen(OPT_ALERT_BAD_PROTO)))
00228 {
00229 conv_data.alert_odd_protocols = 1;
00230 }
00231 else
00232 {
00233 FatalError("ERROR %s(%d) => Unknown argument to spp_conversation "
00234 "preprocessor: \"%s\"\n",
00235 file_name, file_line, index);
00236 }
00237 }
00238
00239 mSplitFree(&toks, num_toks);
00240 }
00241
00242
00243
00244
00245 void ConvInit(u_char* args)
00246 {
00247 int i;
00248 int printall = 1;
00249 char buf[STD_BUF+1];
00250
00251 memset(&conv_data, 0, sizeof(ConversationData));
00252 conv_data.keepstats = 0;
00253 conv_data.alert_odd_protocols = 0;
00254
00255
00256 memset(&conv_data.allowed_ip_protocols, 1, 256);
00257
00258 ParseConvArgs(args);
00259
00260 if(mempool_init(&conv_data.state_records,
00261 conv_data.max_convs, sizeof(StateRecord)))
00262 {
00263 FatalError("ERROR: can't initialize state records\n");
00264 }
00265
00266 conv_data.cachePtr = &conv_data.cache;
00267
00268 ubi_trInitTree(conv_data.cachePtr,
00269 ConvCompareFunc,
00270 0);
00271
00272 AddFuncToPreprocList(ConvFunc);
00273
00274
00275 LogMessage("Conversation Config:\n");
00276 LogMessage(" KeepStats: %d\n", conv_data.keepstats);
00277 LogMessage(" Conv Count: %d\n", conv_data.max_convs);
00278 LogMessage(" Timeout : %d\n", conv_data.timeout);
00279 LogMessage(" Alert Odd?: %d\n", conv_data.alert_odd_protocols);
00280
00281 memset(buf, 0, STD_BUF+1);
00282 snprintf(buf, STD_BUF, " Allowed IP Protocols: ");
00283
00284 for(i=0;i<256;i++)
00285 {
00286 if(!conv_data.allowed_ip_protocols[i])
00287 {
00288 printall = 0;
00289 break;
00290 }
00291 }
00292
00293 if(printall)
00294 {
00295 sfsnprintfappend(buf, STD_BUF, " All\n");
00296 }
00297 else
00298 {
00299 for(i=0;i<256;i++)
00300 {
00301 if(conv_data.allowed_ip_protocols[i])
00302 {
00303 sfsnprintfappend(buf, STD_BUF, "%d ", i);
00304 }
00305 }
00306 }
00307 LogMessage("%s\n", buf);
00308
00309 conv_data.isInitialized = 1;
00310 }
00311
00312
00313
00314
00315 void ConvFunc(Packet* p, void *context)
00316 {
00317 StateRecord* srecord;
00318
00319 if(!(p->preprocessors & PP_CONVERSATION))
00320 {
00321 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,
00322 "Ignoring preprocessor conversation\n"););
00323 return;
00324 }
00325
00326 if (p->packet_flags & PKT_REBUILT_STREAM)
00327 {
00328 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,
00329 "Ignoring Rebuilt Stream\n"););
00330 return;
00331 }
00332
00333 if (p->iph == NULL)
00334 {
00335 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,
00336 "ignoring non-ip traffic\n"));
00337 return;
00338 }
00339
00340
00341 if(conv_data.allowed_ip_protocols[p->iph->ip_proto] != 1)
00342 {
00343 if(conv_data.alert_odd_protocols == 1)
00344 {
00345 SnortEventqAdd(GENERATOR_SPP_CONV, CONV_BAD_IP_PROTOCOL,
00346 1, 0, 5, CONV_BAD_IP_PROTOCOL_STR, 0);
00347 }
00348
00349 return;
00350 }
00351
00352 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,
00353 "_____________________________\n"
00354 "%s:%u->",
00355 inet_ntoa(p->iph->ip_src), p->sp);
00356 DebugMessage(DEBUG_CONVERSATION, "%s:%u\n",
00357 inet_ntoa(p->iph->ip_dst), p->dp););
00358
00359
00360
00361 srecord = ConvGetSession(p);
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374 if(srecord == NULL)
00375 {
00376 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,
00377 "State table is full! -- %u\n",
00378 ubi_trCount(conv_data.cachePtr)););
00379
00380
00381 PruneConvCache(p->pkth->ts.tv_sec, 5, NULL);
00382
00383 srecord = ConvGetSession(p);
00384
00385
00386 if(srecord == NULL)
00387 {
00388 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,
00389 "Can't allocate even after a free\n"););
00390
00391 return;
00392 }
00393 }
00394
00395 srecord->last_time.tv_sec = p->pkth->ts.tv_sec;
00396 srecord->last_time.tv_usec = p->pkth->ts.tv_usec;
00397
00398 if(conv_data.keepstats)
00399 {
00400 FillConvStats(srecord, p);
00401 }
00402
00403
00404
00405
00406
00407
00408
00409 if(conv_data.watch_scans && (!(srecord->conv_flags & CONV_MULIPACKETS)))
00410 {
00411
00412 psWatch(p);
00413 }
00414
00415
00416 if(p->pkth->ts.tv_sec >= (conv_data.prune_time.tv_sec + conv_data.timeout))
00417 {
00418 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION, "Prune time quanta exceeded, pruning "
00419 "conversation cache\n"););
00420 PruneConvCache(p->pkth->ts.tv_sec, 0, NULL);
00421 conv_data.prune_time.tv_sec = p->pkth->ts.tv_sec;
00422 }
00423
00424 }
00425
00426
00427
00428
00429
00430
00431
00432
00433 StateRecord* ConvAlloc(unsigned long cur_time)
00434 {
00435 MemBucket* bp;
00436 StateRecord *sr;
00437 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION, "Getting free state\n"););
00438
00439 bp = mempool_alloc(&conv_data.state_records);
00440
00441 if(bp == NULL)
00442 {
00443
00444 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,
00445 "State Table is full! used: %u max: %u\n",
00446 conv_data.max_convs););
00447 return NULL;
00448 }
00449
00450
00451 sr = bp->data;
00452 sr->bucket = bp;
00453
00454 return sr;
00455 }
00456
00457 void ConvDelete(StateRecord *sr)
00458 {
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469 MemBucket *mb;
00470
00471 mb = sr->bucket;
00472
00473 ubi_sptRemove(conv_data.cachePtr, (ubi_btNodePtr) sr);
00474 mempool_free(&conv_data.state_records, mb);
00475 }
00476
00477
00478
00479
00480
00481
00482
00483
00484 static INLINE void FillConvStats(StateRecord *s, Packet *p)
00485 {
00486 if(PACKET_FORWARD(p))
00487 {
00488 s->bytes_sent += p->caplen;
00489 s->dsize_sent += p->dsize;
00490 s->pkts_sent++;
00491 }
00492 else
00493 {
00494 s->bytes_recv += p->caplen;
00495 s->dsize_recv += p->dsize;
00496 s->pkts_recv++;
00497 }
00498 }
00499
00500
00501
00502
00503
00504 static INLINE void FillStateRecord(StateRecord* s, Packet* p)
00505 {
00506
00507
00508
00509
00510
00511 s->ip_proto = p->iph->ip_proto;
00512
00513 if(PACKET_FORWARD(p))
00514 {
00515 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,
00516 "going forward!\n"););
00517 s->sip = p->iph->ip_src.s_addr;
00518 s->dip = p->iph->ip_dst.s_addr;
00519 s->sport = p->sp;
00520 s->dport = p->dp;
00521 }
00522 else
00523 {
00524 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,
00525 "going switcheroo ninja style!\n"););
00526 s->sip = p->iph->ip_dst.s_addr;
00527 s->dip = p->iph->ip_src.s_addr;
00528 s->sport = p->dp;
00529 s->dport = p->sp;
00530 }
00531
00532 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,
00533 "s->sip: %X, s->dip: %X "
00534 "s->sport: %d s->dport: %d s->ip_proto: %d\n",
00535 s->sip, s->dip, s->sport,s->dport,s->ip_proto););
00536
00537 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,
00538 "p->iph->ip_src.s_addr: %X, p->iph->ip_dst.s_addr: %X "
00539 "p->sp: %d p->dp: %d p->iph->ip_proto: %d\n",
00540 (u_int32_t) p->iph->ip_src.s_addr,
00541 (u_int32_t) p->iph->ip_dst.s_addr,
00542 p->sp,p->dp,p->iph->ip_proto););
00543
00544
00545 }
00546
00547
00548
00549
00550
00551
00552
00553 static StateRecord* ConvGetSession(Packet* p)
00554 {
00555
00556
00557 StateRecord tmp;
00558 MemBucket *mb = NULL;
00559 StateRecord *ret = NULL;
00560
00561
00562 bzero(&tmp, sizeof(StateRecord));
00563
00564
00565 FillStateRecord(&tmp, p);
00566 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,
00567 "tmp.sip: %X, tmp.dip: %X "
00568 "tmp.sport: %d tmp.dport: %d tmp.ip_proto: %d\n",
00569 tmp.sip, tmp.dip, tmp.sport,tmp.dport,tmp.ip_proto););
00570
00571
00572
00573 ret = (StateRecord *) ubi_sptFind(conv_data.cachePtr,
00574 (ubi_btItemPtr) &tmp);
00575
00576 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,
00577 "ret from the sptFind is %p\n", ret););
00578
00579 if(ret == NULL)
00580 {
00581 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,
00582 "Conversation not found... allocating a new one\n"););
00583
00584 mb = mempool_alloc(&conv_data.state_records);
00585
00586 if(mb == NULL)
00587 {
00588
00589
00590
00591 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,
00592 "mempool is out of state records\n"););
00593
00594 return NULL;
00595 }
00596 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,
00597 "allocated: %p\n", mb->data););
00598
00599
00600 ret = (StateRecord *) mb->data;
00601 ret->bucket = mb;
00602
00603 FillStateRecord(ret, p);
00604
00605 if(ubi_sptInsert(conv_data.cachePtr,
00606 (ubi_btNodePtr) ret,
00607 (ubi_btNodePtr) ret, NULL) == ubi_trFALSE)
00608 {
00609 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,
00610 "insertion into splay tree failed for ret==%p\n", ret););
00611 return NULL;
00612 }
00613 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,
00614 "insertion into splay tree succeed for ret==%p\n", ret););
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625 if(PACKET_FORWARD(p))
00626 {
00627 ret->conv_flags |= CONV_FORWARD;
00628 }
00629 else
00630 {
00631 ret->conv_flags |= CONV_REVERSED;
00632 }
00633
00634 }
00635 else
00636 {
00637 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,
00638 "Conversation found @ %p\n", ret););
00639
00640 ret->conv_flags |= CONV_MULIPACKETS;
00641 }
00642
00643 return ret;
00644 }
00645
00646
00647
00648
00649 static int ConvCompareFunc(ubi_trItemPtr ItemPtr, ubi_trNodePtr NodePtr)
00650 {
00651 StateRecord *A = (StateRecord *) ItemPtr;
00652 StateRecord *B = (StateRecord *) NodePtr;
00653 #ifdef DEBUG
00654
00655 #define IPLEN 256
00656 char sip[IPLEN];
00657
00658 strncpy(sip, inet_ntoa(*(struct in_addr *) &A->sip), IPLEN);
00659 DebugMessage(DEBUG_PORTSCAN2,"A %d %s:%d -> %s:%d\n",
00660 A->ip_proto,
00661 sip,
00662 A->sport,
00663 inet_ntoa(*(struct in_addr *) &A->dip),
00664 A->dport);
00665
00666 strncpy(sip, inet_ntoa(*(struct in_addr *) &B->sip), IPLEN);
00667 DebugMessage(DEBUG_PORTSCAN2,"B %d %s:%d -> %s:%d\n",
00668 B->ip_proto,
00669 sip,
00670 B->sport,
00671 inet_ntoa(*(struct in_addr *) &B->dip),
00672 B->dport);
00673
00674 #undef IPLEN
00675 #endif
00676
00677
00678
00679 if(A->sip > B->sip)
00680 {
00681 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,"returning 1\n"););
00682 return 1;
00683 }
00684
00685 if(A->sip < B->sip)
00686 {
00687 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,"returning -1\n"););
00688 return -1;
00689 }
00690
00691
00692 if(A->dip > B->dip)
00693 {
00694 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,"returning 1\n"););
00695 return 1;
00696 }
00697
00698 if(A->dip < B->dip)
00699 {
00700 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,"returning -1\n"););
00701 return -1;
00702 }
00703
00704
00705 if(A->sport > B->sport)
00706 {
00707 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,"returning 1\n"););
00708 return 1;
00709 }
00710
00711
00712 if(A->sport < B->sport)
00713 {
00714 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,"returning -1, count: %u\n",
00715 ubi_trCount(conv_data.cachePtr)
00716 ););
00717 return -1;
00718 }
00719
00720 if(A->dport > B->dport) return 1;
00721 if(A->dport < B->dport) return -1;
00722
00723
00724
00725
00726
00727
00728 if(A->ip_proto > B->ip_proto) return 1;
00729 if(A->ip_proto < B->ip_proto) return -1;
00730
00731 #ifdef DEBUG
00732 DebugMessage(DEBUG_CONVERSATION, "returning 0 for session equalness\n");
00733
00734 DebugMessage(DEBUG_CONVERSATION,
00735 "A->sip: %u B->sip: %u A->sport: %d"
00736 "B->dport: %d A->ip_proto %d B->ip_proto: %d\n",
00737 A->sip, B->sip, A->sport, B->sport, A->ip_proto, B->ip_proto
00738 );
00739 #endif
00740
00741 return 0;
00742 }
00743
00744 static int PruneConvCache(u_int32_t now, int tokill, StateRecord *keeper)
00745 {
00746 StateRecord *idx;
00747 u_int32_t pruned = 0;
00748
00749 if(ubi_trCount(conv_data.cachePtr) <= 1)
00750 {
00751 return 0;
00752 }
00753
00754
00755 if(tokill == 0)
00756 {
00757 idx = (StateRecord *) ubi_btFirst((ubi_btNodePtr)conv_data.cachePtr->root);
00758
00759 if(idx == NULL)
00760 {
00761 return 0;
00762 }
00763
00764 do
00765 {
00766 if(idx == keeper)
00767 {
00768 idx = (StateRecord *) ubi_btNext((ubi_btNodePtr)idx);
00769 continue;
00770 }
00771
00772 if((idx->last_time.tv_sec+conv_data.timeout) < now)
00773 {
00774 StateRecord *savidx = idx;
00775
00776 if(ubi_trCount(conv_data.cachePtr) > 1)
00777 {
00778 idx = (StateRecord *) ubi_btNext((ubi_btNodePtr)idx);
00779 DEBUG_WRAP(DebugMessage(DEBUG_CONVERSATION,
00780 "pruning stale conversation\n"););
00781 ConvDelete(savidx);
00782 pruned++;
00783 }
00784 else
00785 {
00786 ConvDelete(savidx);
00787 pruned++;
00788 return pruned;
00789 }
00790 }
00791 else
00792 {
00793 if(idx != NULL && ubi_trCount(conv_data.cachePtr))
00794 {
00795 idx = (StateRecord *) ubi_btNext((ubi_btNodePtr)idx);
00796 }
00797 else
00798 {
00799 return pruned;
00800 }
00801 }
00802 } while(idx != NULL);
00803
00804 return pruned;
00805 }
00806 else
00807 {
00808 while(tokill-- && ubi_trCount(conv_data.cachePtr) > 1)
00809 {
00810 idx = (StateRecord *) ubi_btLeafNode((ubi_btNodePtr)conv_data.cachePtr);
00811 if(idx != keeper)
00812 ConvDelete(idx);
00813 }
00814 #ifdef DEBUG
00815 if(tokill > 0)
00816 {
00817 DebugMessage(DEBUG_STREAM, "Emptied out the conversation cache"
00818 "completely tokill: %d\n",
00819 tokill);
00820 }
00821 #endif
00822
00823 return 0;
00824 }
00825
00826 return 0;
00827 }
00828