Source for file port_scan.php

Documentation is available at port_scan.php

  1. #!/usr/bin/php
  2. <?php
  3. /**
  4.  * /opt/nac/bin/portscan
  5.  *
  6.  * Long description for file:
  7.  * Learn open ports from equipments allowed
  8.  * Currently it has 3 modes of operation:
  9.  * Normal: Without parameters
  10.  *    Will get ips from devices allowed in the network and will scan them
  11.  * Scannow: With the parameter "scannow"
  12.  *    Will get ips which have the scannow value set to 1 no matter if
  13.  *    they are allowed in the network or not
  14.  * Manual:
  15.  *    Every parameter passed through the command line will be taken
  16.  *    as an ip to be scanned. No to be used in combination with the
  17.  *    "scannow" parameter
  18.  *
  19.  * Important: You have to define first networks to scan in the
  20.  *    nac_netsallowed table
  21.  *
  22.  * PHP version 5
  23.  *
  24.  * LICENSE: This program is free software; you can redistribute it and/or
  25.  * modify it under the terms of the GNU General Public License as published
  26.  * by the Free Software Foundation.
  27.  *
  28.  * @package            FreeNAC
  29.  * @author            Héctor Ortiz (FreeNAC Core Team)
  30.  * @copyright            2006 FreeNAC
  31.  * @license            http://www.gnu.org/copyleft/gpl.html   GNU Public License Version 2
  32.  * @version            SVN: $Id$
  33.  * @link                http://www.freenac.net
  34.  *
  35.  */
  36.  
  37. require_once "funcs.inc.php";
  38. $output=TRUE;
  39.  
  40. $logger->setDebugLevel(0);
  41. $logger->setLogToStdOut(false);
  42.  
  43. #Compatibility with old vars
  44. if (!$conf->scan_directory && $conf->nmap_scan_directory)
  45.    $scan_directory=$conf->nmap_scan_directory;
  46. else 
  47.    $scan_directory=$conf->scan_directory;
  48.  
  49. if (!$conf->what_units_time && $conf->nmap_what_units_time)
  50.    $what_units_time=$conf->nmap_what_units_time;
  51. else 
  52.    $what_units_time=$conf->what_units_time;
  53.  
  54. if (!$conf->time_threshold && $conf->nmap_time_threshold)
  55.    $time_threshold=$conf->nmap_time_threshold;
  56. else
  57.    $time_threshold=$conf->time_threshold;
  58.  
  59. if (!$conf->which_nmap && $conf->nmap_path)
  60.    $which_nmap=$conf->nmap_path;
  61. else
  62.    $which_nmap=$conf->which_nmap;
  63.  
  64. //If we get up to this point, life is good
  65. $queries['number']=0;
  66. $queries['messages']=0;
  67. $flagscannow=0;
  68. for ($i=0;$i<$argc;$i++)
  69. {
  70.    if ($argv[$i]=="--scannow")
  71.       $flagscannow++;
  72.    else if ($argv[$i]=="--verbose")
  73.       $logger->setDebugLevel(1);      
  74. }  
  75. if ($flagscannow)
  76.    $output=FALSE;
  77.  
  78. message("Doing port_scan to sytems... Please wait...\n");
  79. $logger->debug("port_scan started");
  80. $file_timestamp=date('Y-m-d H:i:s');
  81. $file_timestamp=str_replace(' ','-',$file_timestamp);
  82.  
  83. $scan_results=$scan_directory."/scan-$file_timestamp.xml";       //Scan file
  84. $logger->debug("Scan file: $scan_results");        //Parameters from port_scan.inc
  85. $logger->debug("Nmap flags: ".$conf->nmap_flags);
  86. if (($what_units_time<0)||($what_units_time>6)||($what_units_time==2))
  87.    $string=$time_threshold." hours";
  88. else if ($what_units_time==0)
  89.    $string=$time_threshold." seconds";
  90. else if ($what_units_time==1)
  91.    $string=$time_threshold." minutes";
  92. else if ($what_units_time==3)
  93.    $string=$time_threshold." days";
  94. else if ($what_units_time==4)
  95.    $string=$time_threshold." weeks";
  96. else if ($what_units_time==5)
  97.    $string=$time_threshold." months";
  98. else if ($what_units_time==6)
  99.    $string=$time_threshold." years";
  100. $logger->debug("Last_seen threshold: $string");
  101. if ($argc==1)                //Running mode
  102.    $logger->debug("Running mode: Normal");
  103. else if ($flagscannow)
  104.    $logger->debug("Running mode: Scannow");
  105. else $logger->debug("Running mode: Manual")
  106. $list=scan($scan_results,$conf->nmap_flags);        //Scan network with those flags    
  107. if is_array($list|| (count($list== 0) )
  108.    check_and_abort("No info retrieved from the scan"0);
  109. $var=parse_scanfile($scan_results,$list);        //Parse the xml file
  110. if ($var['equipments']>0)            
  111.    do_inventory($var);                //Check against database    
  112. else
  113.    $var['equipments']=0;
  114.  
  115. syscall("rm $scan_results");
  116. message("port_scan finished normally. ".$var['equipments']." hosts scanned\n");
  117. $logger->debug("port_scan finished normally. ".$var['equipments']." hosts scanned\n");
  118. if ($flagscannow)
  119.    log2db('info',"port_scan finished normally. ".$var['equipments']." hosts scanned");
  120.  
  121. function check_requirements()  //Checks for required functions and tables structure
  122. {
  123.    global $conf,$what_units_time$scan_directory$time_threshold$which_nmap;
  124.    $functions=get_defined_functions();
  125.    $functions=$functions['user']//A little bit of paranoia :)
  126.    if (!in_array('logit',$functions))
  127.       check_and_abort("Function logit not defined in funcs.inc\n",0);
  128.    if (!in_array('db_connect',$functions))
  129.       check_and_abort("Function db_connect not defined in funcs.inc\n",0);
  130.    if (!in_array('syscall',$functions))
  131.       check_and_abort("Function syscall not defined in funcs.inc\n",0);
  132.    if (!in_array('normalise_mac',$functions))
  133.       check_and_abort("Function normalise_mac not defined in funcs.inc\n",0);
  134.    if (!in_array('log2db',$functions))
  135.       check_and_abort("Function log2db not defined in funcs.inc\n",0);
  136.    if (!$which_nmap)
  137.       check_and_abort("Var \$which_nmap not defined in port_scan.inc\n",0);
  138.    if (!$scan_directory)
  139.       check_and_abort("Var \$scan_directory not defined in port_scan.inc\n",0);
  140.    if (!$scan_directory)
  141.         check_and_abort("Required directory $scan_directory doesn't exist\n",0);
  142.    if (!$time_threshold)
  143.       check_and_abort("Var \$time_threshold not defined in port_scan.inc\n",0);
  144.    if (!$what_units_time)
  145.      check_and_abort("Var \$what_units_time not defined in port_scan.inc\n",0);
  146.    $query="describe systems;";
  147.    $res=execute_query($query);
  148.    check_and_abort("Please make sure you have properly installed FreeNAC\n",$res);
  149.    $query="describe nac_hostscanned;";
  150.    $res=execute_query($query);
  151.    check_and_abort("Please make sure you have properly followed the doc file README.port_scan\n",$res);
  152.    $query="describe nac_openports;";
  153.    $res=execute_query($query);
  154.    check_and_abort("Please make sure you have properly followed the doc file README.port_scan\n",$res);
  155.    $query="describe subnets;";
  156.    $res=execute_query($query);
  157.    check_and_abort("Please make sure you have properly followed the doc file README.port_scan\n",$res);
  158.    $query="describe services;";
  159.    $res=execute_query($query);
  160.    check_and_abort("Please make sure you have properly followed the doc file README.port_scan\n",$res);
  161.    $query="describe protocols;";
  162.    $res=execute_query($query);
  163.    check_and_abort("Please make sure you have properly followed the doc file README.port_scan\n",$res);
  164.    $tmp=syscall($which_nmap." --version | grep -i nmap");
  165.    $nmap_string NULL;
  166.    $nmap_version NULL;
  167.    if $tmp )
  168.    {
  169.       $tmp=explode(" ",$tmp);
  170.       $nmap_string=$tmp[0];
  171.       $nmap_version=$tmp[2];
  172.    }
  173.    if (isset($nmap_string)&&(strcasecmp($nmap_string,"nmap")!=0))
  174.       check_and_abort("Nmap seems not to be installed in your system\n",0);
  175.    #if (isset($nmap_version)&&($nmap_version<4.11))
  176.    if (isset($nmap_version)&&($nmap_version<4.10))
  177.       check_and_abort("You need a newer version of nmap\n",0);
  178.    if (!$conf->nmap_flags)
  179.      check_and_abort("Var \$nmap_flags not defined in port_scan.inc\n",0);
  180. }
  181.  
  182. function message($string)
  183. {
  184.    global $output,$logger,$output_to_syslog;
  185.    if (($output===TRUE)&&(!$logger->getDebugLevel()))
  186.    {
  187.       if ($output_to_syslog===TRUE)
  188.       {
  189.          $logger->logit($string);
  190.          //log2db('info',$string);
  191.       }   
  192.       else
  193.       {
  194.          $logger->setLogToStdOut();
  195.          $logger->logit($string);
  196.          $logger->setLogToStdOut(false);
  197.       }
  198.    }
  199. }
  200.  
  201. function validate($string)
  202. {
  203.    rtrim($string,' ');
  204.    if (get_magic_quotes_gpc()) {
  205.       $value=stripslashes($string);
  206.    }
  207.    if (!is_numeric($string)) {
  208.       $stringmysql_real_escape_string($string);
  209.    }
  210.    return $string;
  211. }
  212.  
  213. function do_something($query)            //Let's do something with our structure
  214. {
  215.    global $logger;
  216.    if isset($query['number']) )
  217.       $queries=$query['number'];           //How many queries we have?
  218.    else
  219.       $queries 0;
  220.    if isset($query['messages']) )
  221.       $messages=$query['messages'];           //How many messages?
  222.    else
  223.       $messages 0;
  224.    for ($i=0;$i<$queries;$i++)
  225.    {
  226.       if isset($query['query'][$i]) )
  227.          execute_query($query['query'][$i]);       //Execute the queries
  228.    }
  229.    for ($i=0;$i<$messages;$i++)
  230.       if isset($query['message'][$i]) )
  231.         $logger->debug($query['message'][$i]);       //And display the messages
  232. }
  233.  
  234. function update_queries($mesg,$what)
  235. {
  236.    global $queries//Here we hold messages and queries
  237.    if ($what=='q')
  238.    {
  239.       if isset($queries['query'][$queries['number']]) )
  240.          $queries['query'][$queries['number']]=$mesg//This is a query
  241.       if isset($queries['number']) )
  242.          $queries['number'0;
  243.       $queries['number']++;    //Count queries
  244.    }
  245.    else if ($what=='m')
  246.    {
  247.       if isset($queries['message'][$queries['messages']]) )
  248.          $queries['message'][$queries['messages']]=$mesg//This is a message
  249.       if isset($queries['messages']) )
  250.          $queries['messages'0;
  251.       $queries['messages']++;    //Count messages
  252.    }
  253. }
  254.  
  255. function do_inventory($data_from_xml)
  256. {
  257.    global $queries;
  258.    for ($i=0;$i<$data_from_xml['equipments'];$i++)    //How many hosts scanned
  259.    {
  260.        if isset($data_from_xml[$i]['ip']) )
  261.           $ip=$data_from_xml[$i]['ip'];        //Get ip of one host
  262.        else
  263.           $ip 0;
  264.        if isset($data_from_xml[$i]['sid']) )
  265.           $id=$data_from_xml[$i]['sid'];
  266.        else
  267.           $id 0;
  268.        $query=sprintf("select * from nac_hostscanned where sid='%s';",mysql_real_escape_string($id));
  269.        $res=execute_query($query);
  270.        if ($res)
  271.        {
  272.           if (mysql_num_rows($res==0)
  273.              add_entry($data_from_xml[$i]);//Host not found in database 
  274.           else
  275.              check_existent($data_from_xml[$i])//Host in database, let's see if something has changed
  276.        }
  277.    }
  278.    do_something($queries);             //Do something with the structure
  279. }
  280.  
  281. function check_existent($data)     //This function will check info concerning one host scanned against its info in the database
  282. {
  283.    global $logger$queries;
  284.    $timestamp=date('Y-m-d H:i:s');
  285.    if ((!isset($data))||(!is_array($data)))
  286.       check_and_abort("There was a problem parsing the XML file. Make sure you have the right version of PHP and libXML in your system",0);
  287.    if isset($data['ip']) )
  288.       $ip=$data['ip'];   
  289.    else
  290.       $ip 0;
  291.    if isset($data['ports']) )
  292.       $ports=$data['ports'];              //Number of open ports this time
  293.    else
  294.       $ports 0;
  295.    if isset($data['hostname']) )
  296.       $hostname=strtolower($data['hostname']);
  297.    else
  298.       $hostname '';
  299.    if isset($data['os']) )
  300.       $os=$data['os'];                   //OS system this time
  301.    else 
  302.       $os '';
  303.    if isset($data['sid']) )
  304.       $id=$data['sid'];
  305.    else
  306.       $id 0;
  307.    $query=sprintf("select * from nac_hostscanned where sid='%s';",mysql_real_escape_string($id));
  308.    $res=execute_query($query);
  309.    if ($res)
  310.    {
  311.       $result=mysql_fetch_array($resMYSQL_ASSOC);
  312.       $db_ports=mysql_num_rows($res);
  313.       $db_ip=$result['ip'];            //Same IP from last time?
  314.       $db_hostname=strtolower($result['hostname']);        //Same hostname from last time?
  315.       $db_os=$result['os'];            //Same OS from last time?
  316.       $db_timestamp=$result['timestamp'];      //If it changed, since when?
  317.       $host_changed=$os_changed=$mac_changed=0//To control if we need to update its record in the database
  318.       if (!empty($hostname)&&!empty($db_hostname)&&(strcasecmp($hostname,$db_hostname)!=0))       //Info about its hostname
  319.       {
  320.          if ((strcasecmp($db_hostname,'NULL')==0)&&(strcasecmp($hostname,'NULL')!=0))
  321.          
  322.             update_queries("Host $ip has its hostname resolved now. $ip is $hostname\n",'m');
  323.             $host_changed++;
  324.          }
  325.          else  if ((strcasecmp($db_hostname,'unknown')==0)&&(strcasecmp($hostname,'NULL')!=0))
  326.          {
  327.             update_queries("Host $ip has its hostname resolved now. $ip is $hostname\n",'m');
  328.             $host_changed++;
  329.          }
  330.          else if ((strcasecmp($db_hostname,'NULL')!=0)&&(strcasecmp($hostname,'NULL')==0))
  331.          {
  332.             update_queries("Unable to resolve $ip this time, old hostname $db_hostname preserved\n",'m');
  333.             $mac=$db_mac;
  334.          }
  335.          else
  336.          
  337.             update_queries("Old hostname $db_hostname no longer valid. Renamed to $hostname\n",'m');
  338.             $host_changed++;
  339.          }
  340.       }
  341.       if (!empty($os)&&!empty($db_os)&&(strcasecmp($os,$db_os)!=0))        //Info about its OS
  342.       {
  343.          if ((strcasecmp($db_os,'NULL')==0)&&(strcasecmp($os,'NULL')!=0))
  344.          {
  345.             update_queries("OS from $ip now determined. $ip is using $os\n",'m');
  346.             $os_changed++;
  347.          }
  348.          else if ((strcasecmp($db_os'Firewalled')==0&& (strcasecmp($db_os$os)!=0))
  349.          {
  350.             update_queries("OS from $ip now determined. $ip is using $os\n",'m');
  351.             $os_changed++;
  352.          }
  353.          else if ((strcasecmp($db_os,'NULL')!=0)&&(strcasecmp($os,'NULL')==0))
  354.          {
  355.             //update_queries("No OS info yet for $ip this time\n",'m');
  356.             $os=$db_os;
  357.          }
  358.          else
  359.          {
  360.             if (strcasecmp($db_os,'Unreachable')!=0)
  361.                update_queries("$ip has changed its OS since $db_timestamp. Now is using $os\n",'m');
  362.             $os_changed++;
  363.          }
  364.       }
  365.       $changes=$host_changed+$os_changed+$mac_changed;
  366.       if($changes>0)
  367.       {
  368.          $query=sprintf("update nac_hostscanned set hostname='%s',os='%s',timestamp='%s' where sid='%d' and ip='%s';",$hostname,$os,$timestamp,$id,$ip);
  369.          update_queries($query,'q');
  370.       }
  371.       $query=sprintf("select o.banner as banner,o.timestamp as timestamp, p.name as protocol, s.port as port from nac_openports o inner join services s on o.service=s.id inner join protocols p on s.protocol=p.protocol and o.sid='%s';",mysql_real_escape_string($id));
  372.       $res1=execute_query($query);
  373.       if ($res1)                //Let's check info about ports
  374.       {
  375.          $db_ports=mysql_num_rows($res1);
  376.          $counter=0
  377.          $db_tcp=0;
  378.          $db_udp=0;
  379.          while ($result=mysql_fetch_array($res1MYSQL_ASSOC))
  380.          {
  381.             if (strcasecmp($result['protocol'],'tcp')==0)
  382.             {
  383.                $db_tmp_port_tcp[$db_tcp]['port']=$result['port'];
  384.                $db_tmp_port_tcp[$db_tcp]['timestamp']=$result['timestamp'];
  385.                $db_tmp_port_tcp[$db_tcp]['banner']=$result['banner'];
  386.                $db_tcp++;
  387.             }
  388.             else if (strcasecmp($result['protocol'],'udp')==0)
  389.             {
  390.                $db_tmp_port_udp[$db_udp]['port']=$result['port'];
  391.                $db_tmp_port_udp[$db_udp]['timestamp']=$result['timestamp'];
  392.                $db_tmp_port_udp[$db_udp]['banner']=$result['banner'];
  393.                $db_udp++;
  394.             }
  395.          }
  396.          if ((isset($db_tmp_port_tcp))&&(is_array($db_tmp_port_tcp)))
  397.             sort($db_tmp_port_tcp);
  398.          if ((isset($db_tmp_port_udp))&&(is_array($db_tmp_port_udp)))
  399.             sort($db_tmp_port_udp);
  400.          if (($db_tcp==0)&&($db_udp==0)) //In case we have no info in the db
  401.          {
  402.             $db_protocol[0]='tcp';
  403.             $db_port[0]=0;
  404.             $db_porttstmp[0]='0000-00-00 00:00:00';
  405.             $db_banner[0]=':'
  406.          }
  407.          else
  408.          {
  409.             for ($i=0;$i<$db_tcp;$i++)
  410.             {
  411.                $db_protocol[$i]='tcp';
  412.                $db_port[$i]=$db_tmp_port_tcp[$i]['port'];
  413.                $db_porttstmp[$i]=$db_tmp_port_tcp[$i]['timestamp'];
  414.                $db_banner[$i]=$db_tmp_port_tcp[$i]['banner'];
  415.             }
  416.             for (;$i<($db_tcp+$db_udp);$i++)
  417.             {
  418.                $db_protocol[$i]='udp';
  419.                $db_port[$i]=$db_tmp_port_udp[($i-$db_tcp)]['port'];
  420.                $db_porttstmp[$i]=$db_tmp_port_udp[($i-$db_tcp)]['timestamp'];
  421.                $db_banner[$i]=$db_tmp_port_udp[($i-$db_tcp)]['banner'];
  422.             }
  423.          }
  424.          $tcp=0;
  425.          $udp=0;
  426.          for ($i=0;$i<$ports;$i++)
  427.          {
  428.             if ($data['port'][$i]['protocol']=='tcp')
  429.             {
  430.                $tmp_port_tcp[$tcp]['port']=$data['port'][$i]['portid'];
  431.                $tmp_port_tcp[$tcp]['banner']=$data['port'][$i]['description'];
  432.                $tcp++;
  433.             }
  434.             else if ($data['port'][$i]['protocol']=='udp')
  435.             {
  436.                $tmp_port_udp[