diff --git a/etc/Config.php b/etc/Config.php index 3a4f1458..2c88397c 100644 --- a/etc/Config.php +++ b/etc/Config.php @@ -45,13 +45,15 @@ final class Config { # Enable authorization? public static $ENABLE_AUTHZ = 1; # Debug - #public static $DEBUG = TRUE; - public static $DEBUG = FALSE; + public static $DEBUG = TRUE; # Directory, where to put the reports (only applied for asynchronous mode) public static $REPORTS_DIR = "/var/tmp/pakiti-reports/"; # Should be report compressed? public static $COMPRESS_REPORTS = 1; + + # If CERN reports are used, path to the private key must be defined, in order to decrypt incomming report + public static $CERN_REPORT_DECRYPTION_KEY = "/etc/ssl/cert.pem"; # Do we want backup the reports public static $BACKUP = TRUE; diff --git a/lib/common/Loader.php b/lib/common/Loader.php index 5e5498db..2f207eea 100644 --- a/lib/common/Loader.php +++ b/lib/common/Loader.php @@ -26,7 +26,6 @@ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. - # Load the constants require_once(realpath(dirname(__FILE__)) . '/Constants.php'); @@ -59,8 +58,8 @@ function __autoload($className) { } elseif (preg_match('/.*Module$/', $className) > 0) { # Get the module name $moduleName = strtolower(preg_replace('/^(.*)Module$/','\1',$className)); - if (file_exists(realpath(dirname(__FILE__)) . '/../modules/' . $moduleName . '/' . $className . '.php')) { - include_once(realpath(dirname(__FILE__)) . '/../modules/' . $moduleName . '/' . $className . '.php'); + if (file_exists(realpath(dirname(__FILE__)) . '/../../modules/' . $moduleName . '/' . $className . '.php')) { + include_once(realpath(dirname(__FILE__)) . '/../../modules/' . $moduleName . '/' . $className . '.php'); } } else { # Models diff --git a/lib/dao/InstalledPkgDao.php b/lib/dao/InstalledPkgDao.php index a943d057..64da75e5 100644 --- a/lib/dao/InstalledPkgDao.php +++ b/lib/dao/InstalledPkgDao.php @@ -91,17 +91,19 @@ public function getInstalledPkgs(Host &$host, $orderBy, $pageSize, $pageNum) { $installedPkgsDb =& $this->db->queryToMultiRow($sql); # Create objects $installedPkgs = array(); - foreach ($installedPkgsDb as $installedPkgDb) { - $installedPkg = new InstalledPkg(); - $installedPkg->setPkgId($installedPkgDb["pkgId"]); - $installedPkg->setHostId($installedPkgDb["hostId"]); - $installedPkg->setArchId($installedPkgDb["archId"]); - $installedPkg->setVersion($installedPkgDb["version"]); - $installedPkg->setRelease($installedPkgDb["release"]); + if ($installedPkgsDb != null) { + foreach ($installedPkgsDb as $installedPkgDb) { + $installedPkg = new InstalledPkg(); + $installedPkg->setPkgId($installedPkgDb["pkgId"]); + $installedPkg->setHostId($installedPkgDb["hostId"]); + $installedPkg->setArchId($installedPkgDb["archId"]); + $installedPkg->setVersion($installedPkgDb["version"]); + $installedPkg->setRelease($installedPkgDb["release"]); - array_push($installedPkgs, $installedPkg); + array_push($installedPkgs, $installedPkg); + } } - + return $installedPkgs; } diff --git a/lib/managers/DbManager.php b/lib/managers/DbManager.php index 6c240d66..d6f310f9 100644 --- a/lib/managers/DbManager.php +++ b/lib/managers/DbManager.php @@ -211,7 +211,7 @@ protected function rawSingleValueMultiRowFetch($res) { * Single raw object fetch, just check if the fetch was successfull and return the result */ protected function rawSingleObjectFetch($res, $class, $params) { - if ($params == null) { + if ($params != null) { if (($row = $res->fetch_object($class, $params)) == null) { return null; } diff --git a/lib/managers/ReportsManager.php b/lib/managers/ReportsManager.php index 0e9eef91..aa326c8a 100644 --- a/lib/managers/ReportsManager.php +++ b/lib/managers/ReportsManager.php @@ -90,6 +90,12 @@ public function createReport(Report &$report, Host &$host) { $this->getPakiti()->getManager("DbManager")->query($sql); + # Assign the lastReportId to the host + $sql = "update Host set lastReportId=".$report->getId()." where id=".$host->getId(); + + $this->getPakiti()->getManager("DbManager")->query($sql); + + return $report; } diff --git a/lib/modules/feeder/FeederModule.php b/lib/modules/feeder/FeederModule.php deleted file mode 100755 index d2d7de33..00000000 --- a/lib/modules/feeder/FeederModule.php +++ /dev/null @@ -1,536 +0,0 @@ -_host = new Host(); - $this->_report = new Report(); - - # Set the time, when we received the report - $this->_report->setReceivedOn(time()); - - # Get the version of the client - if (($this->_version = Utils::getHttpVar(Constants::$REPORT_VERSION)) == null) { - throw new Exception("Client did not send the version!"); - } - - # Get the hostname and ip - $this->_host->setHostname(Utils::getHttpVar(Constants::$REPORT_HOSTNAME)); - $this->_host->setIp(Utils::getHttpVar(Constants::$REPORT_IP)); - - # Get the hostname and ip of the reporting machine (could be a NAT machine) - $this->_host->setReporterIp(isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : "0.0.0.0"); - $this->_host->setReporterHostname(gethostbyaddr($this->_host->getReporterIp())); - Utils::log(LOG_DEBUG, "Report from [reporterHost=".$this->_host->getReporterHostname().",reporterIp=".$this->_host->getReporterIp()."]", - __FILE__, __LINE__); - - # Is the host proxy? - if (Utils::getHttpVar(Constants::$REPORT_PROXY) == Constants::$HOST_IS_PROXY) { - $this->_report->setTroughtProxy(Constants::$HOST_IS_PROXY); - $this->_report->setProxyHostname($this->_host->getReporterHostname()); - - # Check if the proxy is authorized to send the reports - if (!$this->checkProxyAuthz($this->_host->getReporterHostname(), $this->_host->getReporterIp())) { - throw new Exception("Proxy " . $this->_host->getReporterHostname() . " is not authorized to send the reports"); - } - Utils::log(LOG_INFO, "Proxy logging [proxy=" . $this->_host->getReporterHostname()."] for [host=".$this->_host->getHostname()."]"); - - # If we are in proxy mode, the reporterHostname and reporterIp will be replaced with the real hostname and ip of the client machine. - $this->_host->setReporterHostname($this->_host->getHostname()); - $this->_host->setReporterIp($this->_host->getIp()); - } else { - $this->_report->setTroughtProxy(Constants::$HOST_IS_NOT_PROXY); - } - } - - /* - * Process the report, stores the data about the host, installed packages and report itself. - */ - public function processReport() { - - # Start the transaction - $this->getPakiti()->getManager("DbManager")->begin(); - - try { - # Parse the data and store the report - $this->prepareReport(); - - # Process the list of package, synchronize received list of installed packages with one in the DB - $this->storePkgs(); - - # Find vulnerabilities - - # Store the report - $this->storeReport(); - } catch (Exception $e) { - # Rollback the transaction - $this->getPakiti()->getManager("DbManager")->rollback(); - throw $e; - } - # Commit the transaction - $this->getPakiti()->getManager("DbManager")->commit(); - } - - /* - * Process all received entries - */ - public function prepareReport() { - Utils::log(LOG_DEBUG, "Preparing the report", __FILE__, __LINE__); - $tag = null; - $hostGroup = null; - - switch ($this->_version) { - case "4": - Utils::log(LOG_DEBUG, "Client using version 4", __FILE__, __LINE__); - # Get the rest of HTTP variables - # host, os, arch, kernel, site, version, type, pkgs - $this->_host->setOsName(trim(Utils::getHttpVar(Constants::$REPORT_OS))); - $this->_host->setArchName(trim(Utils::getHttpVar(Constants::$REPORT_ARCH))); - $this->_host->setKernel(trim(Utils::getHttpVar(Constants::$REPORT_KERNEL))); - $this->_host->setType(Utils::getHttpVar(Constants::$REPORT_TYPE)); - break; - } - - # Set the initial information about the report - $this->_report->setReceivedOn(time()); - - # Parse the packages list - $this->_pkgs = $this->parsePkgs(Utils::getHttpVar(Constants::$REPORT_PKGS)); - - $this->_report->setNumOfInstalledPkgs(sizeof($this->_pkgs)); - - # Get the host object from the DB, if the host doesn't exist in the DB, this routine will create it - $this->_host = $this->getPakiti()->getManager("HostsManager")->getHostFromReport($this->_host, $this->_pkgs); - - # Get the host group - $hostGroup = new HostGroup(); - $hostGroup->setName(Utils::getHttpVar(Constants::$REPORT_SITE)); - # If the host is already member of the host group, no operation is done - $this->getPakiti()->getManager("HostGroupsManager")->assignHostToHostGroup($this->_host, $hostGroup); - - # Get the host tag and assign it to the host - $tag = new Tag(); - $tag->setName(Utils::getHttpVar(Constants::$REPORT_TAG)); - # If the tag is already assigned, no operation is done - $this->getPakiti()->getManager("TagsManager")->assignTagToHost($this->_host, $tag); - } - - /* - * Store the repor - */ - public function storeReport() { - Utils::log(LOG_DEBUG, "Storing report to the DB", __FILE__, __LINE__); - - $this->_report->setProcessedOn(time()); - - $this->_report = $this->getPakiti()->getManager("ReportsManager")->createReport($this->_report, $this->_host); - - # Set the lastReportId for the host - $this->getPakiti()->getManager("HostsManager")->setLastReportId( $this->_host, $this->_report); - } - - /* - * Stores the report to the file for further processing (only applied in asynchronous mode). - * In order to save resources, store directly variables from the HTTP request ($_GET or $_POST). - */ - public function storeReportToFile() { - Utils::log(LOG_DEBUG, "Storing report to file", __FILE__, __LINE__); - - # Get the host id from the DB - $id = $this->getPakiti()->getManager("HostsManager")->getHostId($this->_host); - - # Get the hashes of the previous report, but only for hosts already stored in the DB - if ($id != -1) { - $this->_host->setId($id); - $lastReportHashes = $this->getPakiti()->getManager("ReportsManager")->getLastReportHashes($this->_host); - $currentReportHeaderHash = $this->computeReportHeaderHash(); - $currentReportPkgsHash = $this->computeReportPkgsHash(); - - # Check if the hashes are equals - if (($lastReportHashes != null) && (($lastReportHashes[Constants::$REPORT_LAST_HEADER_HASH] == $currentReportHeaderHash) || - ($lastReportHashes[Constants::$REPORT_LAST_PKGS_HASH] == $currentReportPkgsHash))) { - # Data sent by the host are the same as stored one, so we do not need to store anything - Utils::log(LOG_DEBUG, "Feeder [host=" . $this->_host->getHostname() . "] doesn't send any new data, exiting...", __FILE__, __LINE__); - exit; - } - } - - # Create temporary file, filename mask: pakiti-report-[host]-[reportHost] and also store the timestamp to the file - $timestamp = microtime(true); - # Maximal number of attempts to open the file - $count = 3; - - $filename = "pakiti-report-".$this->_host->getHostname()."-".$this->_host->getReporterHostname(); - $file = Config::$REPORTS_DIR."/".$filename; - Utils::log(LOG_DEBUG, "Storing report [file=".$file."]", __FILE__, __LINE__); - while (($reportFile = fopen($file,"w")) === FALSE) { - $count--; - - # Wait a bit - sleep(1); - - # Try to create the file three times, if the operation is not successfull, then throw the exception - if ($count == 0) { - Utils::log(LOG_DEBUG, "Error creating the file", __FILE__, __LINE__); - throw new Exception("Cannot create the file containing host report [host=".$this->_host->getHostname(). - ", reporterHostname=".$this->_host->getReporterHostname()."]"); - } - Utils::log(LOG_DEBUG, "Cannot create the file, trying again ($count attempts left)", __FILE__, __LINE__); - } - - switch ($this->_version) { - case "4": - # Prepare the header - $header = Constants::$REPORT_TYPE."='".Utils::getHttpVar(Constants::$REPORT_TYPE)."',". - Constants::$REPORT_HOSTNAME."='".Utils::getHttpVar(Constants::$REPORT_HOSTNAME)."',". - Constants::$REPORT_OS."='".Utils::getHttpVar(Constants::$REPORT_OS)."',". - Constants::$REPORT_TAG."='".Utils::getHttpVar(Constants::$REPORT_TAG)."',". - Constants::$REPORT_KERNEL."='".Utils::getHttpVar(Constants::$REPORT_KERNEL)."',". - Constants::$REPORT_ARCH."='".Utils::getHttpVar(Constants::$REPORT_ARCH)."',". - Constants::$REPORT_SITE."='".Utils::getHttpVar(Constants::$REPORT_SITE)."',". - Constants::$REPORT_VERSION."='".Utils::getHttpVar(Constants::$REPORT_VERSION)."',". - Constants::$REPORT_REPORT."='".Utils::getHttpVar(Constants::$REPORT_REPORT)."',". - Constants::$REPORT_TIMESTAMP."='".$timestamp."'". - "\n"; - - # Store data - if (fwrite($reportFile, $header . Utils::getHttpVar(Constants::$REPORT_PKGS)) === FALSE) { - throw new Exception("Cannot write to the file '$file'"); - } - break; - } - - # Finally close the handler - fclose($reportFile); - - # Store the hashes into the DB, but only for hosts already stored in the DB - if ($id != -1) { - $this->getPakiti()->getManager("ReportsManager")->storeReportHashes($host, currentReportHeaderHash, $currentReportPkgsHash); - } - } - - /* - * Stores the report to the file for the backup. - */ - public function backupReport() { - Utils::log(LOG_DEBUG, "Storing report to file", __FILE__, __LINE__); - - # Check if the backup directory exits, if not, then create it - if (is_dir(Config::$BACKUP_DIR) === FALSE) { - mkdir(Config::$BACKUP_DIR, 0700); - } - - # Check if the directory for the host exits, if not, then create it - $hostDir = Config::$BACKUP_DIR . $this->_host->getHostname()."-".$this->_host->getReporterHostname(); - if (is_dir($hostDir) == FALSE) { - mkdir($hostDir, 0700); - } - - # Create the file, filename mask: [timestamp of the report].pakiti - $timestamp = microtime(true); - # Maximal number of attempts to open the file - $count = 3; - - $filename = $timestamp . ".pakiti"; - $file = $hostDir."/".$filename; - if (Config::$COMPRESS_REPORTS) { - # If compression is enabled, add appropriate extension - $file .= ".gz"; - } - - Utils::log(LOG_DEBUG, "Storing backup report [file=".$file."]", __FILE__, __LINE__); - while (($reportFile = fopen($file,"w")) === FALSE) { - $count--; - - # Wait a bit - sleep(1); - - # Try to create the file three times, if the operation is not successfull, then throw the exception - if ($count == 0) { - Utils::log(LOG_DEBUG, "Error creating the file", __FILE__, __LINE__); - throw new Exception("Cannot create the file containing host backup report [host=".$this->_host->getHostname(). - ", reporterHostname=".$this->_host->getReporterHostname()."]"); - } - Utils::log(LOG_DEBUG, "Cannot create the backup file, trying again ($count attempts left)", __FILE__, __LINE__); - } - - switch ($this->_version) { - case "4": - # Prepare the header - $header = Constants::$REPORT_TYPE."='".Utils::getHttpVar(Constants::$REPORT_TYPE)."',". - Constants::$REPORT_HOSTNAME."='".Utils::getHttpVar(Constants::$REPORT_HOSTNAME)."',". - Constants::$REPORT_OS."='".Utils::getHttpVar(Constants::$REPORT_OS)."',". - Constants::$REPORT_TAG."='".Utils::getHttpVar(Constants::$REPORT_TAG)."',". - Constants::$REPORT_KERNEL."='".Utils::getHttpVar(Constants::$REPORT_KERNEL)."',". - Constants::$REPORT_ARCH."='".Utils::getHttpVar(Constants::$REPORT_ARCH)."',". - Constants::$REPORT_SITE."='".Utils::getHttpVar(Constants::$REPORT_SITE)."',". - Constants::$REPORT_VERSION."='".Utils::getHttpVar(Constants::$REPORT_VERSION)."',". - Constants::$REPORT_REPORT."='".Utils::getHttpVar(Constants::$REPORT_REPORT)."',". - Constants::$REPORT_TIMESTAMP."='".$timestamp."'". - "\n"; - - # Store the data - $dataToStore = $header . Utils::getHttpVar(Constants::$REPORT_PKGS); - if (Config::$COMPRESS_REPORTS) { - # Compress data - $dataToStore = gzcompress($dataToStore); - } - if (fwrite($reportFile, $dataToStore) === FALSE) { - throw new Exception("Cannot write to the file '$file'"); - } - break; - } - - # Finally close the handler - fclose($reportFile); - } - - /* - * Process packages. - */ - public function storePkgs() { - Utils::log(LOG_DEBUG, "Storing the packages", __FILE__, __LINE__); - # Load the actually stored packages from the DB, the array is already sorted by the pkgName - $pkgs =& $this->getPakiti()->getManager("PkgsManager")->getInstalledPkgsAsArray($this->_host); - - $pkgsToAdd = array(); - $pkgsToUpdate = array(); - $pkgsToRemove = array(); - - // Find packages which should be added or updated - foreach ($this->_pkgs as $pkgName => &$value) { - if (!array_key_exists($pkgName,$pkgs)) { - # Package is missing in the DB - $pkgsToAdd[$pkgName] =& $value; - } elseif ($value['pkgVersion'] != $pkgs[$pkgName]['pkgVersion']) { - # Package has different version - $pkgsToUpdate[$pkgName] =& $value; - } elseif ($value['pkgRelease'] != $pkgs[$pkgName]['pkgRelease']) { - # Package has different release - $pkgsToUpdate[$pkgName] =& $value; - } elseif ($value['pkgArch'] != $pkgs[$pkgName]['pkgArch']) { - # Package has different architecture - $pkgsToUpdate[$pkgName] =& $value; - } - } - - // Find packages which should be deleted - foreach (array_keys($pkgs) as $pkgName) { - if (!array_key_exists($pkgName, $this->_pkgs)) { - array_push($pkgsToRemove, $pkgName); - } - } - - if (sizeof($pkgsToAdd) > 0) { - Utils::log(LOG_DEBUG, "Adding ".sizeof($pkgsToAdd)." new packages", __FILE__, __LINE__); - $this->getPakiti()->getManager("PkgsManager")->addPkgs($this->_host, $pkgsToAdd); - } - if (sizeof($pkgsToUpdate) > 0) { - Utils::log(LOG_DEBUG, "Updating ".sizeof($pkgsToAdd)." packages", __FILE__, __LINE__); - $this->getPakiti()->getManager("PkgsManager")->updatePkgs($this->_host, $pkgsToUpdate); - } - if (sizeof($pkgsToRemove) > 0) { - Utils::log(LOG_DEBUG, "Removing ".sizeof($pkgsToAdd)." packages", __FILE__, __LINE__); - $this->getPakiti()->getManager("PkgsManager")->removePkgs($this->_host, $pkgsToRemove); - } - } - - /* - * Sends the results of the packages processing back to the client. - */ - public function sendResultsBack() { - //FIXME not implemented yet - } - - /* - * Parse the long string containing list of installed packages. - */ - protected function parsePkgs(&$pkgs) { - Utils::log(LOG_DEBUG, "Parsing packages", __FILE__, __LINE__); - $parsedPkgs = array(); - switch ($this->_version) { - case "4": - # Remove escape characters - $pkgs = str_replace ("\\", "", $pkgs); - - # Go throught the string, each entry is separated by the new line - $tok = strtok($pkgs, "\n"); - while ($tok !== FALSE) { - preg_match("/'(.*)' '(.*)' '(.*)' '(.*)'/", $tok, $entries); - # We have to make the names of the packages lowercase in order to have same names for all OSes (MySQL is case insesitive in searches) - $pkgName = strtolower($entries[1]); - $pkgVersion = $entries[2]; - $pkgRelease = $entries[3]; - $pkgArch = $entries[4]; - - # If the host uses dpkg we need to split version manually to version and release by the dash - # Suppress warnings, if the version doesn't contain dash, only version will be filled, release will be empty - if ($this->_host->getType() == Constants::$PACKAGER_SYSTEM_DPKG) { - @list ($pkgVersion, $pkgRelease) = explode('-',$pkgVersion); - } - - ## Remove blacklisted packages - # Remove packages which fits the patterns provided in the configuration - if (in_array($pkgName, Config::$IGNORE_PACKAGES)) { - $tok = strtok("\n"); - continue; - } - # Guess which package represents running kernel - if (in_array($pkgName, Config::$KERNEL_PACKAGES_NAMES)) { - # Remove epoch from the version - $versionWithoutEpoch = Utils::removeEpoch($pkgVersion); - # Compare result of the uname -r with the package version - if ($this->_host->getKernel() != $versionWithoutEpoch."-".$pkgRelease) { - # This verion of the kernel isn't booted - $tok = strtok("\n"); - continue; - } - } - - # Finally iterate through all regexp which defines packages to ignore - foreach (Config::$IGNORE_PACKAGES_PATTERNS as &$pkgNamePattern) { - if (preg_match("/$pkgNamePattern/",$pkgName) == 1) { - # Skip this package, because it is in ignore list - $tok = strtok("\n"); - continue; - } - } - unset($pkgNamePattern); - - # $parsedPkgs['pkgName'] = array ( pkgVersion, pkgRelease, pkgArch ); - $parsedPkgs[$pkgName] = array ( 'pkgVersion' => $pkgVersion, 'pkgRelease' => $pkgRelease, 'pkgArch' => $pkgArch ); - $tok = strtok("\n"); - } - break; - } - - return $parsedPkgs; - } - - /* - * Check whether the proxy is authorized to send the reports on behalf of the host. - */ - protected function checkProxyAuthz($proxyHostname, $proxyIp) { - Utils::log(LOG_DEBUG, "Checking the proxy authorization", __FILE__, __LINE__); - switch (Config::$PROXY_AUTHENTICATION_MODE) { - case Constants::$PROXY_AUTHN_MODE_HOSTNAME: - if (in_array(Utils::getHttpVar(Constants::$REPORT_HOSTNAME), Config::$PROXY_ALLOWED_PROXIES)) { - return TRUE; - } else { - return FALSE; - } - break; - case Constants::$PROXY_AUTHN_MODE_IP: - if (in_array(Utils::getHttpVar(Constants::$REPORT_IP), Config::$PROXY_ALLOWED_PROXIES)) { - return TRUE; - } else { - return FALSE; - } - break; - case Constants::$PROXY_AUTHN_MODE_SUBJECT; - if (in_array(Utils::getServerVar(Constants::$SSL_CLIENT_SUBJECT), Config::$PROXY_ALLOWED_PROXIES)) { - return TRUE; - } else { - return FALSE; - } - break; - } - } - - /* - * Compute hash of the report header (hostname, ip, version, kernel, ...) - */ - protected function computeReportHeaderHash() { - Utils::log(LOG_DEBUG, "Computing the hash of the report header", __FILE__, __LINE__); - switch ($this->_version) { - case "4": - $header = Utils::getHttpVar(Constants::$REPORT_TYPE). - Utils::getHttpVar(Constants::$REPORT_HOSTNAME). - Utils::getHttpVar(Constants::$REPORT_OS). - Utils::getHttpVar(Constants::$REPORT_TAG). - Utils::getHttpVar(Constants::$REPORT_KERNEL). - Utils::getHttpVar(Constants::$REPORT_ARCH). - Utils::getHttpVar(Constants::$REPORT_SITE). - Utils::getHttpVar(Constants::$REPORT_VERSION). - Utils::getHttpVar(Constants::$REPORT_REPORT); - - return $this->computeHash($header); - break; - } - } - - /* - * Compute hash of the list of packages - */ - protected function computeReportPkgsHash() { - Utils::log(LOG_DEBUG, "Computing the hash of the list of the packages", __FILE__, __LINE__); - return $this->computeHash(Utils::getHttpVar(Constants::$REPORT_PKGS)); - } - - /* - * Compute the hash, currently MD5 - */ - protected function computeHash($string) { - return md5($string); - } - - /* - * Make diff of the two arrays - */ - protected function array_compare_recursive($array1, $array2) { - $diff = array(); - foreach ($array1 as $key => &$value) { - if (!array_key_exists($key,$array2)) { - $diff[$key] = $value; - } elseif (is_array($value)) { - if (!is_array($array2[$key])) { - $diff[$key] = $value; - } else { - $new = array_compare_recursive($value, $array2[$key]); - if (!empty($new)) { - $diff[$key] = $value; - }; - }; - } elseif ($array2[$key] !== $value) { - $diff[$key] = $value; - }; - }; - unset($value); - return $diff; - } -} - -?> diff --git a/lib/modules/feeder/www/index.php b/lib/modules/feeder/www/index.php deleted file mode 100644 index 8074beaa..00000000 --- a/lib/modules/feeder/www/index.php +++ /dev/null @@ -1,85 +0,0 @@ -storeReportToFile(); - } - - # Synchronous mode - process data immediatelly - #--------------------------------------------- - elseif (Config::$FEEDER_MODE ==Constants::$FEEDER_SYNCHRONOUS_MODE) { - Utils::log(LOG_DEBUG, "Processing report in synchronous mode"); - # Process incomming data - $feeder->processReport(); - - # Should we send the results back to the client? - if (Utils::getHttpVar(Constants::$REPORT_REPORT) == Constants::$SEND_REPORT) { - $feeder->sendResultsBack(); - } - } - - # Something is wrong here - #------------------------ - else { - Utils::log(LOG_ERROR, "Undefined feeder mode"); - print Constants::$RETURN_ERROR; - exit; - } - - # End - Utils::log(LOG_INFO, "Report done for [host=".Utils::getHttpVar(Constants::$REPORT_HOSTNAME). - "] in ".Utils::getTimer($time)."s\n"); - print Constants::$RETURN_OK; - - # Do we need backup? - #------------------- - if (Config::$BACKUP === TRUE) { - $feeder->backupReport(); - } - - exit; -} catch (Exception $e) { - Utils::log(LOG_ERR, $e->getMessage()); - print Constants::$RETURN_ERROR; - if ($e->getMessage() != "") { - print "\nPakiti server error message: {$e->getMessage()}"; - } - exit; -} -?> diff --git a/modules/feeder/FeederModule.php b/modules/feeder/FeederModule.php index 3f9d3a5e..54dfd245 100755 --- a/modules/feeder/FeederModule.php +++ b/modules/feeder/FeederModule.php @@ -33,23 +33,35 @@ class FeederModule extends DefaultModule { private $_host; private $_pkgs; + private $_report_hostname; + private $_report_ip; + private $_report_proxy; + private $_report_os; + private $_report_arch; + private $_report_kernel; + private $_report_type; + private $_report_site; + private $_report_tag; + private $_report_report; + private $_report_pkgs; + public function __construct(Pakiti &$pakiti) { parent::__construct($pakiti); $this->_host = new Host(); $this->_report = new Report(); - # Set the time, when we received the report - $this->_report->setReceivedOn(time()); - # Get the version of the client if (($this->_version = Utils::getHttpVar(Constants::$REPORT_VERSION)) == null) { throw new Exception("Client did not send the version!"); } - + + # Map variables in the report to the internal variables + $this->doReportMapping($this->_version); + # Get the hostname and ip - $this->_host->setHostname(Utils::getHttpVar(Constants::$REPORT_HOSTNAME)); - $this->_host->setIp(Utils::getHttpVar(Constants::$REPORT_IP)); + $this->_host->setHostname($this->_report_hostname); + $this->_host->setIp($this->_report_ip); # Get the hostname and ip of the reporting machine (could be a NAT machine) $this->_host->setReporterIp(isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : "0.0.0.0"); @@ -58,7 +70,7 @@ public function __construct(Pakiti &$pakiti) { __FILE__, __LINE__); # Is the host proxy? - if (Utils::getHttpVar(Constants::$REPORT_PROXY) == Constants::$HOST_IS_PROXY) { + if ($this->_report_proxy == Constants::$HOST_IS_PROXY) { $this->_report->setTroughtProxy(Constants::$HOST_IS_PROXY); # Check if the proxy is authorized to send the reports @@ -74,7 +86,107 @@ public function __construct(Pakiti &$pakiti) { $this->_report->setTroughtProxy(Constants::$HOST_IS_NOT_PROXY); } } - + + /* + * Returns hostname of the reporting machine + */ + public function getReportHost() { + return $this->_host->getReporterHostname(); + } + + /* + * Maps variables from the reports (depends on the report version) onto the local variables + */ + private function doReportMapping($version) { + switch ($version) { + # Legacy Pakiti client + case "4": + $this->_report_hostname = Utils::getHttpVar(Constants::$REPORT_HOSTNAME); + $this->_report_ip = Utils::getHttpVar(Constants::$REPORT_IP); + $this->_report_proxy = Utils::getHttpVar(Constants::$REPORT_PROXY); + $this->_report_os = Utils::getHttpVar(Constants::$REPORT_OS); + $this->_report_arch = Utils::getHttpVar(Constants::$REPORT_ARCH); + $this->_report_kernel = Utils::getHttpVar(Constants::$REPORT_KERNEL); + $this->_report_type = Utils::getHttpVar(Constants::$REPORT_TYPE); + $this->_report_site = Utils::getHttpVar(Constants::$REPORT_SITE); + $this->_report_tag = Utils::getHttpVar(Constants::$REPORT_TAG); + $this->_report_report = Utils::getHttpVar(Constants::$REPORT_REPORT); + $this->_report_pkgs = Utils::getHttpVar(Constants::$REPORT_PKGS); + # Report from RPM based OS: version and release are splitted by space. Debian and CERN reports separates version and release by a dash. + # FIXME, we need set is in a changelog + $this->_report_pkgs_format = $this->_report_type == "rpm" ? "space" : "dash"; + break; + case "cern_1": + # Example of the report: + ## + #ip: 128.142.145.197 + #ts: 1412031358 + #arch: x86_64 + #host: dpm-puppet01.cern.ch dpm-puppet01.ipv6.cern.ch + #kernel: 2.6.32-431.3.1.el6.x86_64 + #packager: rpm + #site: MYSITE + #system: Scientific Linux CERN SLC release 6.5 (Carbon) + ## + #CERN-CA-certs 0:20140325-2.slc6 noarch + + # Map onto the variables + + # Decrypt the report + $data = Utils::getHttpVar('data'); + $tmpFileIn = tempnam("/dev/shm/", "cern_IN_"); + $tmpFileOut = tempnam("/dev/shm/", "cern_OUT_"); + # Store encrypted report into the file and the use openssl smime to decode it + if (file_put_contents($tmpFileIn, $data) === FALSE) { + throw new Exception("Cannot write to the file '$tmpFileIn' during decoding cern_1 report"); + } + if (system("openssl smime -decrypt -binary -inform DER -inkey ". Config::$CERN_REPORT_DECRYPTION_KEY ." -in $tmpFileIn -out $tmpFileOut") === FALSE) { + throw new Exception("Cannot run openssl smime on the file '$tmpFileIn'"); + } + # Clean up + unlink($tmpFileIn); + + $handle = fopen("$tmpFileOut", "r"); + $lineNumber = 0; + if ($handle) { + while (($line = fgets($handle)) !== false) { + $lineNumber++; + if ($lineNumber == 1 && trim($line) != "#") { + throw new Exception("Bad format of the report, it should start with # '$tmpFileOut'"); + } + if ($lineNumber > 1 && trim($line) == "#") { + # We have reached end of header + break; + } + # Get field name and value separatedly + $fields = explode(':', $line, 2); + switch(trim($fields[0])) { + case "ip": $this->_report_ip = trim($fields[1]); break; + case "arch": $this->_report_arch = trim($fields[1]); break; + # Get only the first hostname in the list, CERN sends all possible hostnames of the host + case "host": $this->_report_hostname = trim($fields[1]); break; + case "kernel": $this->_report_kernel = trim($fields[1]); break; + case "packager": $this->_report_type = trim($fields[1]); break; + case "site": $this->_report_site = trim($fields[1]); break; + case "system": $this->_report_os = trim($fields[1]); break; + } + } + + while (($line = fgets($handle)) !== false) { + if ($line == "#" || empty($line)) continue; + # Store packages into the internal variable + $this->_report_pkgs .= $line; + } + } else { + // error opening the file. + throw new Exception("Cannot open file with the report '$tmpFileOut'"); + } + fclose($handle); + unlink($tmpFileOut); + break; + } + } + /* * Process the report, stores the data about the host, installed packages and report itself. */ @@ -116,34 +228,43 @@ public function prepareReport() { Utils::log(LOG_DEBUG, "Client in version 4", __FILE__, __LINE__); # Get the rest of HTTP variables # host, os, arch, kernel, site, version, type, pkgs - $this->_host->setOs(Utils::getHttpVar(Constants::$REPORT_OS)); - $this->_host->setArch(Utils::getHttpVar(Constants::$REPORT_ARCH)); - $this->_host->setKernel(Utils::getHttpVar(Constants::$REPORT_KERNEL)); - $this->_host->setType(Utils::getHttpVar(Constants::$REPORT_TYPE)); + $this->_host->setOsName($this->_report_os); + $this->_host->setArchName($this->_report_arch); + $this->_host->setKernel($this->_report_kernel); + $this->_host->setType($this->_report_type); + break; + case "cern_1": + Utils::log(LOG_DEBUG, "Client in version CERN 1", __FILE__, __LINE__); + # Get the rest of HTTP variables + # host, os, arch, kernel, site, version, type, pkgs + $this->_host->setOsName($this->_report_os); + $this->_host->setArchName($this->_report_arch); + $this->_host->setKernel($this->_report_kernel); + $this->_host->setType($this->_report_type); break; } - + + # Parse the packages list + $this->_pkgs = $this->parsePkgs($this->_report_pkgs); + # Set the initial information about the report $this->_report->setReceivedOn(time()); # Get the host object from the DB, if the host doesn't exist in the DB, this routine will create it - $this->_host = $this->getPakiti()->getManager("HostsManager")->getHostFromReport($this->_host); + $this->_host = $this->getPakiti()->getManager("HostsManager")->getHostFromReport($this->_host, $this->_pkgs); # Get the host group $hostGroup = new HostGroup(); - $hostGroup->setName(Utils::getHttpVar(Constants::$REPORT_SITE)); + $hostGroup->setName($this->_report_site); # If the host is already member of the host group, no operation is done $this->getPakiti()->getManager("HostGroupsManager")->assignHostToHostGroup($this->_host, $hostGroup); # Get the host tag and assign it to the host $tag = new Tag(); - $tag->setName(Utils::getHttpVar(Constants::$REPORT_TAG)); + $tag->setName($this->_report_tag); # If the tag is already assigned, no operation is done $this->getPakiti()->getManager("TagsManager")->assignTagToHost($this->_host, $tag); - # Parse the packages list - $this->_pkgs = $this->parsePkgs(Utils::getHttpVar(Constants::$REPORT_PKGS)); - $this->_report->setNumOfInstalledPkgs(sizeof($this->_pkgs)); } @@ -210,16 +331,16 @@ public function storeReportToFile() { switch ($this->_version) { case "4": # Prepare the header - $header = Constants::$REPORT_TYPE."='".Utils::getHttpVar(Constants::$REPORT_TYPE)."',". - Constants::$REPORT_HOSTNAME."='".Utils::getHttpVar(Constants::$REPORT_HOSTNAME)."',". - Constants::$REPORT_OS."='".Utils::getHttpVar(Constants::$REPORT_OS)."',". - Constants::$REPORT_TAG."='".Utils::getHttpVar(Constants::$REPORT_TAG)."',". - Constants::$REPORT_KERNEL."='".Utils::getHttpVar(Constants::$REPORT_KERNEL)."',". - Constants::$REPORT_ARCH."='".Utils::getHttpVar(Constants::$REPORT_ARCH)."',". - Constants::$REPORT_SITE."='".Utils::getHttpVar(Constants::$REPORT_SITE)."',". - Constants::$REPORT_VERSION."='".Utils::getHttpVar(Constants::$REPORT_VERSION)."',". - Constants::$REPORT_REPORT."='".Utils::getHttpVar(Constants::$REPORT_REPORT)."',". - Constants::$REPORT_TIMESTAMP."='".$timestamp."'". + $header = Constants::$REPORT_TYPE."='".$this->_report_type."',". + Constants::$REPORT_HOSTNAME."='".$this->_report_hostname."',". + Constants::$REPORT_OS."='".$this->_report_os."',". + Constants::$REPORT_TAG."='".$this->_report_tag."',". + Constants::$REPORT_KERNEL."='".$this->_report_kernel."',". + Constants::$REPORT_ARCH."='".$this->_report_arch."',". + Constants::$REPORT_SITE."='".$this->_report_site."',". + Constants::$REPORT_VERSION."='".$this->_version."',". + Constants::$REPORT_REPORT."='".$this->_report_report."',". + Constants::$REPORT_TIMESTAMP."='".$timestamp."'". "\n"; # Store the data @@ -244,7 +365,7 @@ public function storeReportToFile() { public function storePkgs() { Utils::log(LOG_DEBUG, "Storing the packages", __FILE__, __LINE__); # Load the actually stored packages from the DB, the array is already sorted by the pkgName - $pkgs =& $this->getPakiti()->getManager("PkgsManager")->getPkgs($this->_host); + $pkgs =& $this->getPakiti()->getManager("PkgsManager")->getInstalledPkgsAsArray($this->_host); $pkgsToAdd = array(); $pkgsToUpdate = array(); @@ -282,26 +403,35 @@ public function storePkgs() { protected function parsePkgs(&$pkgs) { Utils::log(LOG_DEBUG, "Parsing packages", __FILE__, __LINE__); $parsedPkgs = array(); - switch ($this->_version) { - case "4": - # Remove escape characters - $pkgs = str_replace ("\\", "", $pkgs); + # Remove escape characters + $pkgs = str_replace ("\\", "", $pkgs); - # Go throught the string, each entry is separated by the new line - $tok = strtok($pkgs, "\n"); - while ($tok !== FALSE) { - preg_match("/'(.*)' '(.*)' '(.*)' '(.*)'/", $tok, $entries); - $pkgName = $entries[1]; - $pkgVersion = $entries[2]; - $pkgRelease = $entries[3]; - $pkgArch = $entries[4]; - - # If the host uses dpkg we need to split version manually to version and release by the dash - # Suppress warnings, if the version doesn't contain dash, only version will be filled, release will be empty - if ($this->_host->getType() == Constants::$PACKAGER_SYSTEM_DPKG) { - @list ($pkgVersion, $pkgRelease) = explode('-',$pkgVersion); - } - + # Go throught the string, each entry is separated by the new line + $tok = strtok($pkgs, "\n"); + while ($tok !== FALSE) { + switch ($this->_version) { + case "4": + preg_match("/'(.*)' '(.*)' '(.*)' '(.*)'/", $tok, $entries); + $pkgName = $entries[1]; + $pkgVersion = $entries[2]; + $pkgRelease = $entries[3]; + $pkgArch = $entries[4]; + + # If the host uses dpkg we need to split version manually to version and release by the dash. + # Suppress warnings, if the version doesn't contain dash, only version will be filled, release will be empty + if ($this->_host->getType() == Constants::$PACKAGER_SYSTEM_DPKG) { + @list ($pkgVersion, $pkgRelease) = explode('-',$pkgVersion); + } + break; + case "cern_1": + preg_match("/(.*)[ \t](.*)-(.*)[ \t](.*)/", $tok, $entries); + $pkgName = $entries[1]; + $pkgVersion = $entries[2]; + $pkgRelease = $entries[3]; + $pkgArch = $entries[4]; + break; + } + ## Remove blacklisted packages # Remove packages which fits the patterns provided in the configuration if (in_array($pkgName, Config::$IGNORE_PACKAGES)) { @@ -333,8 +463,6 @@ protected function parsePkgs(&$pkgs) { # $parsedPkgs['pkgName'] = array ( pkgVersion, pkgRelease, pkgArch ); $parsedPkgs[$pkgName] = array ( 'pkgVersion' => $pkgVersion, 'pkgRelease' => $pkgRelease, 'pkgArch' => $pkgArch ); $tok = strtok("\n"); - } - break; } return $parsedPkgs; @@ -433,4 +561,4 @@ protected function array_compare_recursive($array1, $array2) { } } -?> \ No newline at end of file +?> diff --git a/modules/feeder/www/index.php b/modules/feeder/www/index.php index 58886e71..afa8aead 100644 --- a/modules/feeder/www/index.php +++ b/modules/feeder/www/index.php @@ -28,7 +28,7 @@ # POSSIBILITY OF SUCH DAMAGE. $time = microtime(true); -require(realpath(dirname(__FILE__)) . '/../../../loader.php'); +require(realpath(dirname(__FILE__)) . '/../../../lib/common/Loader.php'); try { # Initialize @@ -61,7 +61,7 @@ } # End - Utils::log(LOG_INFO, "Report done for [host=".Utils::getHttpVar(Constants::$REPORT_HOSTNAME). + Utils::log(LOG_INFO, "Report done for [host=".$feeder->getReportHost(). "] in ".Utils::getTimer($time)."s\n"); print Constants::$RETURN_OK; exit; @@ -70,4 +70,4 @@ print Constants::$RETURN_ERROR; exit; } -?> \ No newline at end of file +?>