"Fossies" - the Fresh Open Source Software Archive

Member "portsentry-2.0b1/portsentry.c" (8 Apr 2002, 30426 Bytes) of package /linux/privat/old/portsentry-2.0b1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "portsentry.c" see the Fossies "Dox" file reference documentation.

    1 /************************************************************************/
    2 /*                                                                      */
    3 /* Psionic PortSentry                                                                                                       */
    4 /*                                                                      */
    5 /* Created: 10-12-1997                                                  */
    6 /* Modified: 03-27-2002                                                 */
    7 /*                                                                      */
    8 /* Send all changes/modifications/bugfixes to sentrysupport@psionic.com */
    9 /*                                                                      */
   10 /*                                                                      */
   11 /* This software is Copyright(c) 1997-2002 Psionic Technologies, Inc.   */
   12 /*                                                                      */
   13 /* Disclaimer:                                                          */
   14 /*                                                                      */
   15 /* All software distributed by Psionic Technologies is distributed          */
   16 /* AS IS and carries NO WARRANTY or GUARANTEE OF ANY KIND. End users of */
   17 /* the software acknowledge that they will not hold Psionic Technologies*/
   18 /* liable for failure or non-function of the software product. YOU ARE  */
   19 /* USING THIS PRODUCT AT YOUR OWN RISK.                                                                 */
   20 /*                                                                      */
   21 /* Licensing restrictions apply. Commercial re-sell is prohibited under */
   22 /* certain conditions. See the license that came with this package or   */
   23 /* visit http://www.psionic.com for more information.                                   */
   24 /*                                                                      */
   25 /* $Id: portsentry.c,v 1.53 2002/04/08 16:48:15 crowland Exp crowland $ */
   26 /************************************************************************/
   27 
   28 #include "portsentry.h"
   29 #include "portsentry_io.h"
   30 #include "portsentry_util.h"
   31 
   32 /* Globals */
   33 pcap_t *gblPcapSocketPtr;
   34 pcap_t *gblHandlePtr;
   35 bpf_u_int32 gblAddressPcapPtr;
   36 bpf_u_int32 gblNetmaskPcapPtr;
   37 int gblDataLink;
   38 int gblLinkSize;
   39 struct in_addr gblSrc, gblDst;
   40 char gblAttackerIP[IPMAXBUF], gblTargetIP[IPMAXBUF];
   41 
   42 struct {
   43     char gblScanDetectHost[MAXSTATE][IPMAXBUF];
   44     char gblKillRoute[MAXBUF];
   45     char gblKillHostsDeny[MAXBUF];
   46     char gblKillRunCmd[MAXBUF];
   47     char gblBlockedFile[MAXBUF];
   48     char gblHistoryFile[MAXBUF];
   49     char gblIgnoreFile[MAXBUF];
   50     char gblInterface[MAXBUF];
   51     char gblInterfaceAddr[MAXBUF];
   52     int gblScanDetectCount;
   53     int gblTriggerCount;
   54 } gblConfig;
   55 
   56 
   57 struct {
   58     int gblBlockTCP;
   59     int gblBlockUDP;
   60     int gblRunCmdFirst;
   61     int gblResolveHost;
   62     int gblIPChecks;
   63     int gblTCPChecks;
   64     int gblUDPChecks;
   65     int gblICMPChecks;
   66 } gblFlags;
   67 
   68 
   69 /* Here we go */
   70 int main (int argc, char **argv)
   71 {
   72 
   73 
   74 if (argc != 1)
   75     {
   76         Usage ();
   77         ExitNow (ERROR);
   78     }
   79 else if ((geteuid ()) && (getuid ()) != 0)
   80     {
   81         printf ("You need to be root to run this.\n");
   82         ExitNow (ERROR);
   83     }
   84 else if (CheckConfig () != TRUE)
   85     {
   86       Log ("adminalert: ERROR: Configuration files are missing/corrupted. Shutting down.\n");
   87       printf ("ERROR: Configuration files are missing/corrupted.\n");
   88       printf ("ERROR: Check your syslog for a more detailed error message.\n");
   89       printf ("ERROR: PortSentry is shutting down!\n");
   90       ExitNow (ERROR);
   91     }
   92 else if (InitConfig () != TRUE)
   93     {
   94       Log ("adminalert: ERROR: Your config file is corrupted/missing mandatory option! Shutting down.\n");
   95       printf ("ERROR: Your config file is corrupted/missing mandatory option!\n");
   96       printf ("ERROR: Check your syslog for a more detailed error message.\n");
   97       printf ("ERROR: PortSentry is shutting down!\n");
   98       ExitNow (ERROR);
   99     }
  100 else if (InitStats () != TRUE)
  101     {
  102       Log ("adminalert: ERROR: Couldn't initialize statistics. Shutting down.\n");
  103       printf ("ERROR: Couldn't initialize statistics. Shutting down.\n");
  104       ExitNow (ERROR);
  105     }
  106 #ifndef NODAEMON
  107 else if (DaemonSeed () == ERROR)
  108     {
  109       Log ("adminalert: ERROR: could not go into daemon mode. Shutting down.\n");
  110       printf ("ERROR: could not go into daemon mode. Shutting down.\n");
  111       ExitNow (ERROR);
  112     }
  113 #endif
  114 else if (InitInterface (gblConfig.gblInterface) != TRUE)
  115     {
  116       Log ("adminalert: ERROR: Couldn't initialize interface. Shutting down.\n");
  117       printf ("ERROR: Couldn't initialize interface. Shutting down.\n");
  118       ExitNow (ERROR);
  119     }
  120 else if(InitFilter() != TRUE)
  121     {
  122       Log ("adminalert: ERROR: Couldn't initialize BPF filter. Shutting down.\n");
  123       printf ("ERROR: Couldn't initialize BPF filter. Shutting down.\n");
  124       ExitNow (ERROR);
  125     }
  126 
  127 Log("adminalert: PortSentry is initialized and monitoring.");
  128 
  129 
  130 /* Grab multiple packets */
  131 pcap_loop(gblHandlePtr, -1, PktEngine, NULL);
  132 
  133 /* Close interface */
  134 pcap_close(gblHandlePtr);
  135 
  136 exit(0);
  137 }
  138 
  139 
  140 /****************************************************************/
  141 /* Reads generic config options into global variables           */
  142 /****************************************************************/
  143 int
  144 InitConfig (void)
  145 {
  146   FILE *input;
  147   char configToken[MAXBUF];
  148 
  149   gblFlags.gblBlockTCP = CheckFlag ("BLOCK_TCP");
  150   gblFlags.gblBlockUDP = CheckFlag ("BLOCK_UDP");
  151   gblFlags.gblResolveHost = CheckFlag ("RESOLVE_HOST");
  152   gblFlags.gblIPChecks = CheckFlag("CHECK_IP");
  153   gblFlags.gblTCPChecks = CheckFlag("CHECK_TCP");
  154   gblFlags.gblUDPChecks = CheckFlag("CHECK_UDP");
  155   gblFlags.gblICMPChecks = CheckFlag("CHECK_ICMP");
  156 
  157   memset (gblConfig.gblKillRoute, '\0', MAXBUF);
  158   memset (gblConfig.gblKillHostsDeny, '\0', MAXBUF);
  159   memset (gblConfig.gblKillRunCmd, '\0', MAXBUF);
  160 
  161   if ((ConfigTokenRetrieve ("SCAN_TRIGGER", configToken)) == FALSE)
  162     {
  163       Log ("adminalert: ERROR: Could not read SCAN_TRIGGER option from config file. Disabling SCAN DETECTION TRIGGER");
  164       gblConfig.gblTriggerCount = 0;
  165     }
  166   else
  167     {
  168 #ifdef DEBUG
  169       Log ("debug: InitConfig: retrieved SCAN_TRIGGER option: %s \n",
  170        configToken);
  171 #endif
  172       gblConfig.gblTriggerCount = atoi (configToken);
  173     }
  174 
  175   if ((ConfigTokenRetrieve ("KILL_ROUTE", gblConfig.gblKillRoute)) == TRUE)
  176     {
  177 #ifdef DEBUG
  178       Log ("debug: InitConfig: retrieved KILL_ROUTE option: %s \n",
  179        gblConfig.gblKillRoute);
  180 #endif
  181     }
  182   else
  183     {
  184 #ifdef DEBUG
  185       Log ("debug: InitConfig: KILL_ROUTE option NOT FOUND.\n");
  186 #endif
  187     }
  188 
  189   if ((ConfigTokenRetrieve ("KILL_HOSTS_DENY", gblConfig.gblKillHostsDeny)) == TRUE)
  190     {
  191 #ifdef DEBUG
  192       Log ("debug: InitConfig: retrieved KILL_HOSTS_DENY option: %s \n",
  193        gblConfig.gblKillHostsDeny);
  194 #endif
  195     }
  196   else
  197     {
  198 #ifdef DEBUG
  199       Log ("debug: InitConfig: KILL_HOSTS_DENY option NOT FOUND.\n");
  200 #endif
  201     }
  202 
  203   if ((ConfigTokenRetrieve ("KILL_RUN_CMD", gblConfig.gblKillRunCmd)) == TRUE)
  204     {
  205 #ifdef DEBUG
  206       Log ("debug: InitConfig: retrieved KILL_RUN_CMD option: %s \n",
  207        gblConfig.gblKillRunCmd);
  208 #endif
  209     /* Check the order we should run the KILL_RUN_CMD */
  210     /* Default is to run the command after blocking */
  211     gblFlags.gblRunCmdFirst = CheckFlag ("KILL_RUN_CMD_FIRST");
  212     }
  213   else
  214     {
  215 #ifdef DEBUG
  216       Log ("debug: InitConfig: KILL_RUN_CMD option NOT FOUND.\n");
  217 #endif
  218     }
  219 
  220   if ((ConfigTokenRetrieve ("BLOCKED_FILE", gblConfig.gblBlockedFile)) == TRUE)
  221     {
  222 #ifdef DEBUG
  223       Log ("debug: InitConfig: retrieved BLOCKED_FILE option: %s \n",
  224        gblConfig.gblBlockedFile);
  225       Log ("debug: CheckConfig: Removing old block file: %s \n",
  226        gblConfig.gblBlockedFile);
  227 #endif
  228 
  229         if ((input = fopen (gblConfig.gblBlockedFile, "w")) == NULL)
  230             {
  231         Log ("adminalert: ERROR: Cannot delete blocked file on startup: %s.\n",
  232          gblConfig.gblBlockedFile);
  233         return (FALSE);
  234             }
  235         else
  236             fclose(input);
  237    }
  238   else
  239     {
  240       Log ("adminalert: ERROR: Cannot retrieve BLOCKED_FILE option! Aborting\n");
  241       return (FALSE);
  242     }
  243 
  244 
  245   if ((ConfigTokenRetrieve ("HISTORY_FILE", gblConfig.gblHistoryFile)) == TRUE)
  246     {
  247 #ifdef DEBUG
  248       Log ("debug: InitConfig: retrieved HISTORY_FILE option: %s \n",
  249        gblConfig.gblHistoryFile);
  250 #endif
  251     }
  252   else
  253     {
  254       Log ("adminalert: ERROR: Cannot retrieve HISTORY_FILE option! Aborting\n");
  255       return (FALSE);
  256     }
  257 
  258 
  259   if ((ConfigTokenRetrieve ("IGNORE_FILE", gblConfig.gblIgnoreFile)) == TRUE)
  260     {
  261 #ifdef DEBUG
  262       Log ("debug: InitConfig: retrieved IGNORE_FILE option: %s \n",
  263        gblConfig.gblIgnoreFile);
  264 #endif
  265     }
  266   else
  267     {
  268       Log ("adminalert: ERROR: Cannot retrieve IGNORE_FILE option! Aborting\n");
  269       return (FALSE);
  270     }
  271 
  272   if ((ConfigTokenRetrieve ("INTERFACE", gblConfig.gblInterface)) == TRUE)
  273     {
  274 #ifdef DEBUG
  275       Log ("debug: InitConfig: retrieved INTERFACE option: %s \n",
  276        gblConfig.gblInterface);
  277 #endif
  278     }
  279   else
  280     {
  281       Log ("adminalert: ERROR: Cannot retrieve INTERFACE option! Aborting\n");
  282       return (FALSE);
  283     }
  284 
  285   if ((ConfigTokenRetrieve ("INTERFACE_ADDRESS", gblConfig.gblInterfaceAddr)) == TRUE)
  286     {
  287 #ifdef DEBUG
  288       Log ("debug: InitConfig: retrieved INTERFACE_ADDRESS option: %s \n",
  289        gblConfig.gblInterface);
  290 #endif
  291     }
  292   else
  293     {
  294       Log ("adminalert: ERROR: Cannot retrieve INTERFACE_ADDRESS option! Aborting\n");
  295       return (FALSE);
  296     }
  297 
  298   return (TRUE);
  299 }
  300 
  301 
  302 /* Initialize statistic counters */
  303 int InitStats(void)
  304 {
  305     gblStats.gblFrameCount=0;
  306     gblStats.gblIPPackCount=0;
  307     gblStats.gblIcmpPackCount=0;
  308     gblStats.gblTcpPackCount=0;
  309     gblStats.gblUdpPackCount=0;
  310 
  311 return(TRUE);
  312 }
  313 
  314 
  315 /* Initialize the interface */
  316 int InitInterface(char *interface)
  317 {
  318 
  319 char *interfacePtr;
  320 char pcapError[PCAP_ERRBUF_SIZE];
  321 /* XXX lookupnet vars
  322 struct in_addr addr;
  323 char *addressPtr, *netmaskPtr;
  324 */
  325 
  326 /* Initialize Interface */
  327 if(strcmp(interface,"auto") == 0)
  328     interfacePtr = pcap_lookupdev(pcapError);
  329 else
  330     interfacePtr = interface;
  331 
  332 if(interfacePtr == NULL)
  333 {
  334     Log("adminalert: ERROR: Error looking up interface: %s\n", pcapError);
  335     return(ERROR);
  336 }
  337 
  338 #ifdef DEBUG
  339       Log ("debug: InitInterface: Opening interface: %s \n",interfacePtr);
  340 #endif
  341 
  342 /* Initialize address/netmask */
  343 if(pcap_lookupnet(interfacePtr, &gblAddressPcapPtr, &gblNetmaskPcapPtr, pcapError) == ERROR)
  344 {
  345     Log("adminalert: ERROR: Error looking up network: %s\n", pcapError);
  346     return(ERROR);
  347 }
  348 
  349 /* XXX Broken. Can't determine Interface IP */
  350 /*addr.s_addr = gblAddressPcapPtr;
  351 addressPtr = (char *)inet_ntoa(addr);
  352 addr.s_addr = gblNetmaskPcapPtr;
  353 netmaskPtr = (char *)inet_ntoa(addr);
  354 
  355 if(addressPtr == NULL)
  356 {
  357     Log("adminalert: ERROR: Can't translate network address for parsing.\n");
  358     return(ERROR);
  359 }
  360 */
  361 
  362 Log("adminalert: Monitoring interface %s and address: %s\n", interfacePtr, gblConfig.gblInterfaceAddr);
  363 
  364 /* Open device */
  365 if((gblHandlePtr = pcap_open_live(interfacePtr, MAXBUF, 0, 0, pcapError)) == NULL)
  366 {
  367     Log("adminalert: ERROR: Can't open device %s for monitoring: %s\n", interfacePtr, pcapError);
  368     return(ERROR);
  369 }
  370 
  371 #ifdef DEBUG
  372       Log ("debug: InitInterface: Interface %s is now live. \n",interfacePtr);
  373 #endif
  374 
  375 
  376 /* Check media type function here */
  377 gblDataLink = pcap_datalink(gblHandlePtr);
  378 switch(gblDataLink)
  379     {
  380     case DLT_EN10MB:
  381         gblLinkSize = LINK_ETHERSIZE;
  382         break;
  383     default:
  384         Log("adminalert: ERROR: Datalink type is not supported yet.");
  385         return(ERROR);
  386     }
  387 
  388 return(TRUE);
  389 }
  390 
  391 
  392 
  393 /* Initialize BPF */
  394 int InitFilter(void)
  395 {
  396 char tcpPorts[MAXBUF], udpPorts[MAXBUF], filterString[MAXBUF * 2], finalFilter[MAXBUF * 2];
  397 struct bpf_program BPF;
  398 
  399 
  400 Log ("adminalert: Initializing PortSentry BPF filters.\n");
  401 
  402 /* XXX */
  403 /* Need to do:
  404  Bypass ports in use already
  405  Add unused ports to list  with 'or' separator
  406  add ICMP
  407  put in the config retrieve in the main init function.
  408 */
  409 
  410  if ((ConfigTokenRetrieve ("TCP_PORTS", tcpPorts)) == FALSE)
  411       Log ("adminalert: No TCP_PORTS defined in config file. Continuing.");
  412  else
  413       Log ("adminalert: Monitoring TCP ports: %s\n", tcpPorts);
  414 
  415  if ((ConfigTokenRetrieve ("UDP_PORTS", udpPorts)) == FALSE)
  416       Log ("adminalert: No UDP_PORTS defined in config file. Continuing.");
  417  else
  418       Log ("adminalert: Monitoring UDP ports: %s\n", udpPorts);
  419 
  420 
  421  if ((strlen(tcpPorts) == 0) && (strlen(udpPorts) == 0))
  422     {
  423       Log("adminalert: No TCP or UDP ports defined in config file. Aborting.");
  424       return(ERROR);
  425     }
  426  else
  427     {
  428     SafeStrncpy(finalFilter, "not src host ", 14);
  429     strncat(finalFilter, gblConfig.gblInterfaceAddr, IPMAXBUF);
  430     if(strlen(tcpPorts) != 0)
  431        {
  432         strncat(finalFilter, " and tcp dst port ", MAXBUF);
  433         SubstString(" or ", ",", tcpPorts, filterString);
  434         strncat(finalFilter, filterString, ((MAXBUF * 2)- strlen(finalFilter)));
  435        }
  436     if(strlen(udpPorts) != 0)
  437        {
  438         /* Straigtens out filter ordering here */
  439         if(strlen(tcpPorts) != 0)
  440             strncat(finalFilter, " or udp dst port ", ((MAXBUF * 2) - strlen(finalFilter)));
  441         else
  442             strncat(finalFilter, " and udp dst port ", ((MAXBUF * 2) - strlen(finalFilter)));
  443 
  444         SubstString(" or ", ",", udpPorts, filterString);
  445         strncat(finalFilter, filterString, ((MAXBUF * 2) - strlen(finalFilter)));
  446        }
  447     }
  448 
  449 
  450 #ifdef DEBUG
  451       Log ("debug: InitFilter: Applying filter: \"%s\" \n", finalFilter);
  452 #endif
  453 
  454 /* Set Filters function here */
  455 if (pcap_compile(gblHandlePtr, &BPF, finalFilter, 0, gblNetmaskPcapPtr) == -1)
  456     {
  457             Log ("adminalert: ERROR: Could not apply BPF filter: %s", pcap_geterr(gblHandlePtr));
  458         return(ERROR);
  459     }
  460 else if(pcap_setfilter(gblHandlePtr, &BPF) == -1)
  461     {
  462       Log ("adminalert: ERROR: Could not apply BPF filter: %s", pcap_geterr(gblHandlePtr));
  463         return(ERROR);
  464     }
  465 
  466 
  467 #ifdef DEBUG
  468       Log ("debug: InitFilter: Filter set.\n", finalFilter);
  469 #endif
  470 
  471 return(TRUE);
  472 }
  473 
  474 
  475 void PktEngine(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
  476 {
  477 
  478 const struct ether_header *ethernet;
  479 const struct ip *ip;
  480 const struct tcphdr *tcp;
  481 const struct udphdr *udp;
  482 const struct icmp *icmp;
  483 int dataoffset = 0;
  484 int result = FALSE;
  485 
  486 
  487 gblStats.gblFrameCount++;
  488 
  489 #ifdef DEBUG
  490       Log ("debug: PktEngine: Frame counter: %d\n",gblStats.gblFrameCount);
  491       Log ("debug: PktEngine: IP counter: %d\n",gblStats.gblIPPackCount);
  492       Log ("debug: PktEngine: TCP counter: %d\n",gblStats.gblTcpPackCount);
  493       Log ("debug: PktEngine: UDP counter: %d\n",gblStats.gblUdpPackCount);
  494       Log ("debug: PktEngine: ICMP counter: %d\n",gblStats.gblIcmpPackCount);
  495 #endif
  496 
  497 /* shuts up compiler about uninitialized ip struct warning in switch() */
  498 ip = NULL;
  499 
  500 
  501 /* XXX Re-evaluate this switch statement if you apply BPF first */
  502 /* the ethertype check is probably not necessary with ip,tcp,udp,icmp options with BPF */
  503 switch(gblDataLink)
  504     {
  505         case DLT_EN10MB:
  506             ethernet = (struct ether_header *) (packet);
  507             if (ntohs(ethernet -> ether_type) == ETHERTYPE_IP)
  508                 {
  509 #ifdef DEBUG
  510       Log ("debug: PktEngine: Found ETHERTYPE_IP\n");
  511 #endif
  512                     gblStats.gblIPPackCount++;
  513                     ip = (struct ip *) (packet + gblLinkSize);
  514                     gblSrc.s_addr = ip->ip_src.s_addr;
  515                     gblDst.s_addr = ip->ip_dst.s_addr;
  516                     if((dataoffset = CheckIP(ip)))
  517                         {
  518                             SafeStrncpy (gblAttackerIP, (char *) inet_ntoa (gblSrc), IPMAXBUF);
  519                             SafeStrncpy (gblTargetIP, (char *) inet_ntoa (gblDst), IPMAXBUF);
  520                             break;
  521                         }
  522                     else
  523                         {
  524                             Log("attackalert: An illegal IP packet type was detected and discarded.\n");
  525                             break;
  526                         }
  527 
  528                 }
  529             /* Found other ETHERTYPE (probably an ARP) */
  530             else
  531                 break;
  532     }
  533 
  534 if(dataoffset)
  535     {
  536       /* check if we should ignore this IP */
  537     /* XXX This should read in at Init and placed into a list */
  538       result = NeverBlock (gblAttackerIP, gblConfig.gblIgnoreFile);
  539         if (result == ERROR)
  540                 Log ("attackalert: ERROR: cannot open ignore file. Analyzing attack anyway.\n");
  541         else if(result == TRUE) /* Ignore this IP */
  542             return;
  543 
  544     /* check if this target is already blocked */
  545     if (IsBlocked (gblAttackerIP, gblConfig.gblBlockedFile) == TRUE)
  546         {
  547             Log ("attackalert: Host: %s is already blocked - Ignoring", gblAttackerIP);
  548             return;
  549         }
  550 
  551     /* check if they've visited before */
  552     if (!CheckStateEngine (gblAttackerIP))
  553         return;
  554 
  555     switch(ip->ip_p)
  556         {
  557             case IPPROTO_TCP:
  558                 tcp = (struct tcphdr *) (packet + gblLinkSize + dataoffset);
  559                 gblStats.gblTcpPackCount++;
  560                 if (!CheckTCP(ip, tcp))
  561                     Log("adminalert: ERROR: Error checking TCP packet.\n");
  562                 break;
  563             case IPPROTO_UDP:
  564                 gblStats.gblUdpPackCount++;
  565                 udp = (struct udphdr *) (packet + gblLinkSize + dataoffset);
  566                 if (!CheckUDP(ip, udp))
  567                     Log("adminalert: ERROR: Error checking UDP packet.\n");
  568                 break;
  569             case IPPROTO_ICMP:
  570                 gblStats.gblIcmpPackCount++;
  571                 icmp = (struct icmp *) (packet + gblLinkSize + dataoffset);
  572                 if (!CheckICMP(ip, icmp))
  573                     Log("adminalert: ERROR: Error checking ICMP packet.\n");
  574                 break;
  575         }
  576     }
  577 
  578 }
  579 
  580 int CheckTCP(const struct ip *ip, const struct tcphdr *tcp)
  581 {
  582 char resolvedHost[DNSMAXBUF];
  583 char *scanType;
  584 int dstPort = 0, srcPort = 0;
  585 
  586 
  587 /* Set destination/src port  */
  588 dstPort = ntohs(tcp->th_dport);
  589 srcPort = ntohs(tcp->th_sport);
  590 
  591 if(SmartVerify(dstPort, "TCP") == TRUE)
  592     {
  593 #ifdef DEBUG
  594         Log("debug: CheckTCP: SmartVerify indicates port is in use. Ignoring connection\n");
  595 #endif DEBUG
  596         return(TRUE);
  597     }
  598 
  599     /* By-Bye */
  600 if (Dispose(gblAttackerIP, dstPort, "TCP") != TRUE)
  601     Log ("attackalert: ERROR: Could not block host %s!", gblAttackerIP);
  602 
  603 /* Ok we've already blocked this guy or ignored the actions */
  604 /* Since those are the time critical areas we'll now do the optional */
  605 /* resolution for logging */
  606 if (gblFlags.gblResolveHost)
  607     {
  608         if(CleanAndResolve(resolvedHost, gblAttackerIP) != TRUE)
  609             {
  610                 Log ("attackalert: ERROR: Error resolving host. \
  611                     resolving disabled for this host.\n");
  612                 snprintf (resolvedHost, DNSMAXBUF, "%s", gblAttackerIP);
  613             }
  614     }
  615 else
  616     snprintf (resolvedHost, DNSMAXBUF, "%s", gblAttackerIP);
  617 
  618 /* XXX Put in flags to null out for unused bit attack vulnerability or simply report?? */
  619 /* Also this false alarms with Explicit Congestion Notification aware kernels */
  620 /*
  621 if (tcp->th_flags & (TCP_UNUSED1|TCP_UNUSED2))
  622     Log("attackalert: TCP Unused bits set. Detection evasion technique in use!\n");
  623 */
  624 
  625 if(tcp->th_flags == 0)
  626     {
  627         Log("attackalert: TCP NULL scan from host %s/%s to TCP port: %d from TCP port: %d", resolvedHost, \
  628                 gblAttackerIP, dstPort, srcPort);
  629         scanType="NULL";
  630     }
  631 else if (tcp->th_flags == (TH_FIN|TH_URG|TH_PUSH|TH_ACK|TH_SYN|TH_RST))
  632     {
  633         Log("attackalert: TCP XMAS-FULL scan from host %s/%s to TCP port: %d from TCP port: %d", resolvedHost, \
  634                 gblAttackerIP, dstPort, srcPort);
  635         scanType="XMAS-FULL";
  636     }
  637 else if (tcp->th_flags == (TH_FIN|TH_URG|TH_PUSH))
  638     {
  639         Log("attackalert: TCP XMAS scan from host %s/%s to TCP port: %d from TCP port: %d", resolvedHost, \
  640                 gblAttackerIP, dstPort, srcPort);
  641         scanType="XMAS";
  642     }
  643 else if (tcp->th_flags == TH_SYN)
  644     {
  645         Log("attackalert: TCP SYN scan from host %s/%s to TCP port: %d from TCP port: %d", resolvedHost, \
  646                 gblAttackerIP, dstPort, srcPort);
  647         scanType="SYN";
  648     }
  649 else if (tcp->th_flags == TH_FIN)
  650     {
  651         Log("attackalert: TCP FIN scan from host %s/%s to TCP port: %d from TCP port: %d", resolvedHost, \
  652                 gblAttackerIP, dstPort, srcPort);
  653         scanType="FIN";
  654     }
  655 else
  656     {
  657     Log("attackalert: Unknown/Illegal scan type: TCP Packet Flags: FIN %d SYN: %d RST: %d PUSH: %d ACK: %d URG: %d \
  658 UNUSED1: %d UNUSED2: %d scan from host %s/%s to TCP port: %d from TCP port: %d", \
  659   ((tcp->th_flags & TH_FIN) >> 0), \
  660     ((tcp->th_flags & TH_SYN) >> 1), \
  661   ((tcp->th_flags & TH_RST) >> 2), \
  662   ((tcp->th_flags & TH_PUSH) >> 3), \
  663   ((tcp->th_flags & TH_ACK) >> 4), \
  664   ((tcp->th_flags & TH_URG) >> 5), \
  665   ((tcp->th_flags & TCP_UNUSED1) >> 6), \
  666   ((tcp->th_flags & TCP_UNUSED2) >> 7), \
  667     resolvedHost, gblAttackerIP, dstPort, srcPort);
  668     scanType="UNKNOWN";
  669     }
  670 
  671 if (WriteBlocked (gblAttackerIP, resolvedHost, "TCP", scanType, dstPort, srcPort, \
  672         gblConfig.gblBlockedFile, gblConfig.gblHistoryFile) != TRUE)
  673             Log("attackalert: ERROR: Could not write out to history/blocked file.");
  674 
  675 
  676 #ifdef DEBUG
  677     Log("debug: CheckTCP: src: %s\n", gblAttackerIP);
  678     Log("debug: CheckTCP: dst: %s\n", gblTargetIP);
  679     Log("debug: CheckTCP: src port: %d\n", ntohs(tcp->th_sport));
  680     Log("debug: CheckTCP: dst port: %d\n", ntohs(tcp->th_dport));
  681     Log("debug: CheckTCP: FIN: %d\n", (tcp->th_flags & TH_FIN) >> 0);
  682     Log("debug: CheckTCP: SYN: %d\n", (tcp->th_flags & TH_SYN) >> 1);
  683     Log("debug: CheckTCP: RST: %d\n", (tcp->th_flags & TH_RST) >> 2);
  684     Log("debug: CheckTCP: PSH: %d\n", (tcp->th_flags & TH_PUSH) >> 3);
  685     Log("debug: CheckTCP: ACK: %d\n", (tcp->th_flags & TH_ACK) >> 4);
  686     Log("debug: CheckTCP: URG: %d\n", (tcp->th_flags & TH_URG) >> 5);
  687     Log("debug: CheckTCP: UNUSED1: %d\n", (tcp->th_flags & TCP_UNUSED1) >> 6);
  688     Log("debug: CheckTCP: UNUSED2: %d\n", (tcp->th_flags & TCP_UNUSED2) >> 7);
  689     Log("debug: CheckTCP: total flags: %d\n", tcp->th_flags);
  690 #endif
  691 
  692 return(TRUE);
  693 }
  694 
  695 
  696 int CheckUDP(const struct ip *ip, const struct udphdr *udp)
  697 {
  698 char resolvedHost[DNSMAXBUF];
  699 int dstPort = 0, srcPort = 0;
  700 
  701 
  702 /* Set destination/src port  */
  703 dstPort = ntohs(udp->uh_dport);
  704 srcPort = ntohs(udp->uh_sport);
  705 
  706 if((SmartVerify(dstPort, "UDP")) == TRUE)
  707     {
  708 #ifdef DEBUG
  709         Log("debug: CheckUDP: SmartVerify indicates port is in use. Ignoring connection\n");
  710 #endif DEBUG
  711         return(TRUE);
  712     }
  713 
  714     /* By-Bye */
  715 if (Dispose(gblAttackerIP, dstPort, "UDP") != TRUE)
  716     Log ("attackalert: ERROR: Could not block host %s!", gblAttackerIP);
  717 
  718 /* Ok we've already blocked this guy or ignored the actions */
  719 /* Since those are the time critical areas we'll now do the optional */
  720 /* resolution for logging */
  721 if (gblFlags.gblResolveHost)
  722     {
  723         if(CleanAndResolve(resolvedHost, gblAttackerIP) != TRUE)
  724             {
  725                 Log ("attackalert: ERROR: Error resolving host. \
  726                     resolving disabled for this host.\n");
  727                 snprintf (resolvedHost, DNSMAXBUF, "%s", gblAttackerIP);
  728             }
  729     }
  730 else
  731     snprintf (resolvedHost, DNSMAXBUF, "%s", gblAttackerIP);
  732 
  733 Log("attackalert: UDP scan from host %s/%s to UDP port: %d from UDP port: %d", resolvedHost, \
  734 gblAttackerIP, dstPort, srcPort);
  735 if (WriteBlocked (gblAttackerIP, resolvedHost, "UDP", "UDP", dstPort, srcPort, \
  736     gblConfig.gblBlockedFile, gblConfig.gblHistoryFile) != TRUE)
  737     Log("attackalert: ERROR: Could not write out to history/blocked file.");
  738 
  739 #ifdef DEBUG
  740     Log("debug: CheckUDP: src: %s\n", gblAttackerIP);
  741     Log("debug: CheckUDP: dst: %s\n", gblTargetIP);
  742     Log("debug: CheckUDP: src port: %d\n", ntohs(udp->uh_sport));
  743     Log("debug: CheckUDP: dst port: %d\n", ntohs(udp->uh_dport));
  744 #endif
  745 
  746 return(TRUE);
  747 }
  748 
  749 
  750 
  751 /* XXX Not used yet */
  752 int CheckICMP(const struct ip *ip, const struct icmp *icmp)
  753 {
  754 struct in_addr saddr;
  755 
  756 saddr.s_addr = ip->ip_src.s_addr;
  757 
  758     Log("attackalert: ICMP scan from host %s\n", gblAttackerIP);
  759     printf("ICMP Scan from host %s\n", gblAttackerIP);
  760 
  761 return(TRUE);
  762 }
  763 
  764 
  765 
  766 int CheckIP(const struct ip *ip)
  767 {
  768 int dataoffset = 0;
  769 
  770     /* This should never be less than 5 */
  771     if (ip->ip_hl < 5)
  772         {
  773             Log ("attackalert: Illegal IP header length detected in IP packet - length: %d from (possible) host: %s\n",
  774             ip->ip_hl, inet_ntoa (gblSrc));
  775             Log("attackalert: This could be an attempt to bypass detection or attack the system.\n");
  776         }
  777     else
  778         {
  779 #ifdef DEBUG
  780             Log("debug: PktEngine: Found a good IP packet. Type: %d hlen: %d\n", ip->ip_p, ip->ip_hl);
  781 #endif
  782             if((dataoffset = (ip->ip_hl * 4)) > 20)
  783                 {
  784                     Log ("attackalert: An IP packet with options set was found - length: %d from host: %s\n",
  785                     ip->ip_hl, inet_ntoa (gblSrc));
  786                     Log("attackalert: This could be an attempt to bypass detection or attack the system.\n");
  787                 }
  788         }
  789 
  790 /* XXX Put additional checks in here if anomaly flag is set */
  791 return(dataoffset);
  792 }
  793 
  794 
  795 int
  796 SmartVerify (int port, char *mode)
  797 {
  798   int testSockfd;
  799 
  800 /* Ok here is where we "Smart-Verify" the socket. If the port was previously */
  801 /* unbound, but now appears to have someone there, then we will skip responding */
  802 /* to this inbound packet. This a basic "stateful" inspection of the */
  803 /* the connection */
  804 
  805   if (mode == "TCP")
  806     {
  807     if ((testSockfd = OpenTCPSocket ()) == ERROR)
  808         {
  809           Log ("adminalert: ERROR: could not open TCP socket to SmartVerify.\n");
  810             return (FALSE);
  811         }
  812     }
  813     else if (mode == "UDP")
  814     {
  815     if ((testSockfd = OpenUDPSocket ()) == ERROR)
  816         {
  817         Log ("adminalert: ERROR: could not open UDP socket to SmartVerify.\n");
  818             return (FALSE);
  819         }
  820     }
  821     else
  822     {
  823             Log ("adminalert: ERROR: Illegal socket mode passed to SmartVerify.\n");
  824         return (FALSE);
  825     }
  826 
  827 
  828   if (BindSocket (testSockfd, port) == ERROR)
  829         {
  830 #ifdef DEBUG
  831             Log ("debug: SmartVerify: SmartVerify Port In Use: %d", port);
  832 #endif
  833             close (testSockfd);
  834             return (TRUE);
  835         }
  836 
  837   close (testSockfd);
  838   return (FALSE);
  839 }
  840 
  841 
  842 void
  843 Usage (void)
  844 {
  845   printf ("Psionic PortSentry - Port Scan Detector.\n");
  846   printf ("Copyright 1997-2002 Psionic Technologies, Inc. http://www.psionic.com\n");
  847   printf ("Licensing restrictions apply. COMMERCIAL RESALE PROHIBITED WITHOUT LICENSING.\n");
  848   printf ("See documentation for more information. Questions and comments: <sentrysupport@psionic.com>\n");
  849   printf ("Version: %s\n\n", VERSION);
  850   printf ("usage: portsentry\n\n");
  851   printf ("*** PLEASE READ THE DOCS BEFORE USING *** \n\n");
  852 }
  853 
  854 /* our cheesy state engine to monitor who has connected here before */
  855 int
  856 CheckStateEngine (char *target)
  857 {
  858   int count = 0, scanDetectTrigger = TRUE;
  859   int gotOne = 0;
  860 
  861 /* This is the rather basic scan state engine. It maintains     */
  862 /* an array of past hosts who triggered a connection on a port */
  863 /* when a new host arrives it is compared against the array */
  864 /* if it is found in the array it increments a state counter by */
  865 /* one and checks the remainder of the array. It does this until */
  866 /* the end is reached or the trigger value has been exceeded */
  867 /* This would probably be better as a linked list/hash table, */
  868 /* but for the number of hosts we are tracking this is just as good. */
  869 /* This will probably change in the future */
  870 
  871   gotOne = 1;           /* our flag counter if we get a match */
  872   scanDetectTrigger = TRUE; /* set to TRUE until set otherwise */
  873 
  874   if (gblConfig.gblTriggerCount > 0)
  875     {
  876       for (count = 0; count < MAXSTATE; count++)
  877     {
  878       /* if the array has the IP address then increment the gotOne counter and */
  879       /* check the trigger value. If it is exceeded break out of the loop and */
  880       /* set the detecttrigger to TRUE */
  881       if (strcmp (gblConfig.gblScanDetectHost[count], target) == 0 )
  882         {
  883           /* compare the number of matches to the configured trigger value */
  884           /* if we've exceeded we can stop this noise */
  885           if (++gotOne >= gblConfig.gblTriggerCount)
  886         {
  887           scanDetectTrigger = TRUE;
  888 #ifdef DEBUG
  889           Log ("debug: CheckStateEngine: host: %s has exceeded trigger value: %d\n",
  890              gblConfig.gblScanDetectHost[count], gblConfig.gblTriggerCount);
  891 #endif
  892           break;
  893         }
  894         }
  895       else
  896         scanDetectTrigger = FALSE;
  897     }
  898 
  899       /* now add the fresh meat into the state engine */
  900       /* if our array is still less than MAXSTATE large add it to the end */
  901       if (gblConfig.gblScanDetectCount < MAXSTATE)
  902     {
  903       SafeStrncpy (gblConfig.gblScanDetectHost[gblConfig.gblScanDetectCount], target,
  904                IPMAXBUF);
  905       gblConfig.gblScanDetectCount++;
  906     }
  907       else
  908     {
  909       /* otherwise tack it to the beginning and start overwriting older ones */
  910       gblConfig.gblScanDetectCount = 0;
  911       SafeStrncpy (gblConfig.gblScanDetectHost[gblConfig.gblScanDetectCount], target,
  912                IPMAXBUF);
  913       gblConfig.gblScanDetectCount++;
  914     }
  915 
  916 #ifdef DEBUG
  917       for (count = 0; count < MAXSTATE; count++)
  918     Log ("debug: CheckStateEngine: state engine host: %s -> position: %d Detected: %d\n",
  919        gblConfig.gblScanDetectHost[count], count, scanDetectTrigger);
  920 #endif
  921       /* end catch to set state if gblConfigTriggerCount == 0 */
  922       if (gotOne >= gblConfig.gblTriggerCount)
  923     scanDetectTrigger = TRUE;
  924     }
  925 
  926 
  927   if (gblConfig.gblTriggerCount > MAXSTATE)
  928     {
  929       Log ("securityalert: WARNING: Trigger value %d is larger than state engine capacity of %d.\n",
  930     gblConfig.gblTriggerCount);
  931       Log ("Adjust the value lower or recompile with a larger state engine value.\n",
  932      MAXSTATE);
  933       Log ("securityalert: Blocking host anyway because of invalid trigger value");
  934       scanDetectTrigger = TRUE;
  935     }
  936   return (scanDetectTrigger);
  937 }
  938 
  939 
  940 
  941 
  942 /* kill the connection depending on config option */
  943 int
  944 Dispose (char *target, int port, char *mode)
  945 {
  946   int status = TRUE;
  947     int responseFlags = 0;
  948 
  949 if (mode == "TCP")
  950     responseFlags = gblFlags.gblBlockTCP;
  951 else if (mode == "UDP")
  952     responseFlags = gblFlags.gblBlockUDP;
  953 else
  954     {
  955    Log ("attackalert: An unknown response type was passed to the Dispose function.\n");
  956    return(ERROR);
  957     }
  958 
  959 #ifdef DEBUG
  960   Log ("debug: Dispose: disposing of host %s on port %d with mode %s and option: %d",
  961        target, port, mode, responseFlags);
  962   Log ("debug: Dispose: killRunCmd: %s", gblConfig.gblKillRunCmd);
  963   Log ("debug: Dispose: gblRunCmdFirst: %d", gblFlags.gblRunCmdFirst);
  964   Log ("debug: Dispose: killHostsDeny: %s", gblConfig.gblKillHostsDeny);
  965   Log ("debug: Dispose: killRoute: %s  %d", gblConfig.gblKillRoute,
  966        strlen (gblConfig.gblKillRoute));
  967 #endif
  968 
  969     /* Should we ignore active response? */
  970     if (responseFlags == 1)
  971         {
  972             /* run external command first, hosts.deny second, dead route last */
  973             if (gblFlags.gblRunCmdFirst)
  974                 {
  975                     if (strlen (gblConfig.gblKillRunCmd) > 0)
  976                         if (KillRunCmd (target, port, gblConfig.gblKillRunCmd, mode) != TRUE)
  977                             status = FALSE;
  978                     if (strlen (gblConfig.gblKillHostsDeny) > 0)
  979                         if (KillHostsDeny (target, port, gblConfig.gblKillHostsDeny, mode) != TRUE)
  980                             status = FALSE;
  981                     if (strlen (gblConfig.gblKillRoute) > 0)
  982                         if (KillRoute (target, port, gblConfig.gblKillRoute, mode) != TRUE)
  983                             status = FALSE;
  984                 }
  985             /* run hosts.deny first, dead route second, external command last */
  986             else
  987                 {
  988                     if (strlen (gblConfig.gblKillHostsDeny) > 0)
  989                         if (KillHostsDeny (target, port, gblConfig.gblKillHostsDeny, mode) != TRUE)
  990                         status = FALSE;
  991                     if (strlen (gblConfig.gblKillRoute) > 0)
  992                         if (KillRoute (target, port, gblConfig.gblKillRoute, mode) != TRUE)
  993                         status = FALSE;
  994                     if (strlen (gblConfig.gblKillRunCmd) > 0)
  995                         if (KillRunCmd (target, port, gblConfig.gblKillRunCmd, mode) != TRUE)
  996                         status = FALSE;
  997                 }
  998         }
  999     else if (responseFlags == 2)
 1000         {
 1001             /* run external command only */
 1002             if (strlen (gblConfig.gblKillRunCmd) > 0)
 1003                 if (KillRunCmd (target, port, gblConfig.gblKillRunCmd, mode) != TRUE)
 1004                 status = FALSE;
 1005         }
 1006     else
 1007         Log ("attackalert: Ignoring response per configuration file setting.");
 1008 
 1009 return (status);
 1010 }
 1011 
 1012