From 99cb286274e55343def5b94136617e40c3920bef Mon Sep 17 00:00:00 2001 From: fbsanchez Date: Thu, 19 Mar 2020 14:11:09 +0100 Subject: [PATCH 1/2] Pre-check modules (SNMP, TCP, ICMP) Discovery --- pandora_server/lib/PandoraFMS/Core.pm | 2 + .../lib/PandoraFMS/DiscoveryServer.pm | 260 ++++++++++++++---- pandora_server/lib/PandoraFMS/Recon/Base.pm | 32 ++- pandora_server/lib/PandoraFMS/Tools.pm | 18 ++ 4 files changed, 242 insertions(+), 70 deletions(-) diff --git a/pandora_server/lib/PandoraFMS/Core.pm b/pandora_server/lib/PandoraFMS/Core.pm index 6fe43036ff..99ddf48796 100644 --- a/pandora_server/lib/PandoraFMS/Core.pm +++ b/pandora_server/lib/PandoraFMS/Core.pm @@ -3067,6 +3067,8 @@ sub pandora_create_module_from_network_component ($$$$) { pandora_create_module_tags ($pa_config, $dbh, $module_id, $component_tags); logger($pa_config, 'Creating module ' . safe_output ($component->{'nombre'}) . " (ID $module_id) for agent $addr from network component.", 10); + + return $module_id; } ########################################################################## diff --git a/pandora_server/lib/PandoraFMS/DiscoveryServer.pm b/pandora_server/lib/PandoraFMS/DiscoveryServer.pm index 5ee472b3c1..b5785a5c6a 100644 --- a/pandora_server/lib/PandoraFMS/DiscoveryServer.pm +++ b/pandora_server/lib/PandoraFMS/DiscoveryServer.pm @@ -441,11 +441,133 @@ sub PandoraFMS::Recon::Base::tcp_scan ($$) { return $open_ports; } +################################################################################ +# Verifies if a module will be normal. +################################################################################ +sub PandoraFMS::Recon::Base::test_module($$) { + my ($self, $addr, $module) = @_; + + # Default values. + my $test = { + %{$module}, + 'ip_target' => $addr, + }; + + if (is_enabled($module->{'__module_component'})) { + # Component. Translate some fields. + $test->{'id_tipo_modulo'} = $module->{'type'}; + } else { + # Module. + $test->{'id_tipo_modulo'} = $module->{'id_modulo'}; + } + + my $value; + + # 1. Try to retrieve value. + if ($test->{'id_tipo_modulo'} >= 15 && $test->{'id_tipo_modulo'} <= 18) { + # SNMP + $value = $self->call( + 'snmp_get', + $test->{'ip_target'}, + $test->{'snmp_oid'} + ); + } elsif ($test->{'id_tipo_modulo'} == 6) { + # ICMP - alive - already tested. + $value = 1; + + } elsif ($test->{'id_tipo_modulo'} == 7) { + # ICMP - latency + $value = pandora_ping_latency( + $self->{'pa_config'}, + $test->{'ip_target'}, + $test->{'max_timeout'}, + $test->{'max_retries'}, + ); + + } elsif (($test->{'id_tipo_modulo'} >= 1 && $test->{'id_tipo_modulo'} <= 5) + || ($test->{'id_tipo_modulo'} >= 21 && $test->{'id_tipo_modulo'} <= 23) + && is_enabled($test->{'id_plugin'}) + ) { + # Generic, plugins. (21-23 ASYNC) + # XXX TODO: Test plugins. + return 1; + + } elsif ($test->{'id_tipo_modulo'} >= 34 && $test->{'id_tipo_modulo'} <= 37) { + # Remote command. + # XXX TODO: Test remote commands. + return 1; + } elsif ($test->{'id_tipo_modulo'} >= 8 && $test->{'id_tipo_modulo'} <= 11) { + # TCP + + return 0 unless is_numeric($test->{'tcp_port'}) + && $test->{'tcp_port'} > 0 + && $test->{'tcp_port'} <= 65535; + + my $result; + + PandoraFMS::NetworkServer::pandora_query_tcp( + $self->{'pa_config'}, + $test->{'tcp_port'}, + $test->{'ip_target'}, + \$result, + \$value, + $test->{'tcp_send'}, + $test->{'tcp_rcv'}, + $test->{'id_tipo_modulo'}, + $test->{'max_timeout'}, + $test->{'max_retries'}, + '', + ); + + # Result 0 is OK, 1 failed + return 0 unless defined($result) && $result == 0; + return 0 unless defined($value); + + } + + # Invalid data (empty or not defined) + return 0 if is_empty($value); + + # 2. Check if value matches type definition and fits thresholds. + if (is_in_array([1,2,4,5,6,7,8,9,11,15,16,18,21,22,25,30,31,32,34,35,37],$test->{'id_tipo_modulo'})) { + # Numeric. + return 0 unless is_numeric($value); + + if (!is_enabled($test->{'critical_inverse'})) { + return 0 if $value >= $test->{'min_critical'} && $value <= $test->{'max_critical'}; + } else { + return 0 if $value < $test->{'min_critical'} && $value > $test->{'max_critical'}; + } + + if (is_in_array([2,6,9,18,21,31,35], $test->{'id_tipo_modulo'})) { + # Boolean. + if (!is_enabled($test->{'critical_inverse'})) { + return 0 if $value == 0; + } else { + return 0 if $value != 0; + } + } + + } else { + # String. + if (!is_enabled($test->{'critical_inverse'})) { + return 0 if !is_empty($test->{'str_critical'}) && $value =~ /$test->{'str_critical'}/; + } else { + return 0 if !is_empty($test->{'str_critical'}) && $value !~ /$test->{'str_critical'}/; + } + + } + + # Success. + return 1; + +} + ################################################################################ # Create network profile modules for the given agent. ################################################################################ -sub PandoraFMS::Recon::Base::create_network_profile_modules($$$) { - my ($self, $agent_id, $device) = @_; +sub PandoraFMS::Recon::Base::create_network_profile_modules($$) { + my ($self, $device) = @_; # # Plugin @@ -454,50 +576,61 @@ sub PandoraFMS::Recon::Base::create_network_profile_modules($$$) { # ICMP # - use Data::Dumper; - $Data::Dumper::Sortkeys = 1; - - print Dumper($device); - print Dumper($self->{'task_data'}); - - die(); - - return if empty($self->{'id_network_profile'}); + return if is_empty($self->{'id_network_profile'}); my @templates = split /,/, $self->{'id_network_profile'}; - # Get network components associated to the network profile. - my @np_components = get_db_rows($self->{'dbh'}, 'SELECT * FROM tnetwork_profile_component WHERE id_np = ?', $self->{'id_network_profile'}); - foreach my $np_component (@np_components) { + my $data = $self->{'agents_found'}{$device}; - # Get network component data - my $component = get_db_single_row($self->{'dbh'}, 'SELECT * FROM tnetwork_component WHERE id_nc = ?', $np_component->{'id_nc'}); - if (!defined ($component)) { - $self->call('message', "Network component ID " . $np_component->{'id_nc'} . " not found.", 5); - next; + foreach my $t_id (@templates) { + # 1. Retrieve template info. + my $template = get_db_single_row( + $self->{'dbh'}, + 'SELECT * FROM `tnetwork_profile` WHERE `id_np` = ?', + $t_id + ); + + # 2. Verify Private Enterprise Number matches (PEN) + if (defined($template->{'pen'})) { + my @penes = split(',', $template->{'pen'}); + + next unless (is_in_array(\@penes, $data->{'pen'})); } - ## XXX Puede tener varios penes. - #next if (defined($template->{'pen'}) - # && get_enterprise_oid($device) != $template->{'pen'} ); - - # Use snmp_community from network task instead the component snmp_community - $component->{'snmp_community'} = safe_output($self->get_community($device)); - $component->{'tcp_send'} = $self->{'snmp_version'}; - $component->{'custom_string_1'} = $self->{'snmp_privacy_method'}; - $component->{'custom_string_2'} = $self->{'snmp_privacy_pass'}; - $component->{'custom_string_3'} = $self->{'snmp_security_level'}; - $component->{'plugin_parameter'} = $self->{'snmp_auth_method'}; - $component->{'plugin_user'} = $self->{'snmp_auth_user'}; - $component->{'plugin_pass'} = $self->{'snmp_auth_pass'}; - - pandora_create_module_from_network_component( - $self->{'pa_config'}, - $component, - $agent_id, - $self->{'dbh'} + # 2. retrieve module list from target template. + my @np_components = get_db_rows( + $self->{'dbh'}, + 'SELECT * FROM tnetwork_profile_component WHERE id_np = ?', + $t_id ); + + foreach my $np_component (@np_components) { + # 2. Test each possible module. + my $component = get_db_single_row( + $self->{'dbh'}, + 'SELECT * FROM tnetwork_component WHERE id_nc = ?', + $np_component->{'id_nc'} + ); + + $component->{'name'} = safe_output($component->{'name'}); + if ($component->{'type'} >= 15 && $component->{'type'} <= 18) { + $component->{'snmp_community'} = safe_output($self->get_community($device)); + $component->{'tcp_send'} = $self->{'snmp_version'}; + $component->{'custom_string_1'} = $self->{'snmp_privacy_method'}; + $component->{'custom_string_2'} = $self->{'snmp_privacy_pass'}; + $component->{'custom_string_3'} = $self->{'snmp_security_level'}; + $component->{'plugin_parameter'} = $self->{'snmp_auth_method'}; + $component->{'plugin_user'} = $self->{'snmp_auth_user'}; + $component->{'plugin_pass'} = $self->{'snmp_auth_pass'}; + } + + $component->{'__module_component'} = 1; + + # 3. Try to register module into monitoring list. + $self->call('add_module', $device, $component); + } } + } ################################################################################ @@ -580,9 +713,9 @@ sub PandoraFMS::Recon::Base::report_scanned_agents($) { $self->call('message', "Agent id: ".$data->{'agent'}{'agent_id'}, 5); # Create selected modules. - if(ref($data->{'modules'}) eq "ARRAY") { - for (my $i=0; $i < scalar @{$data->{'modules'}}; $i++) { - my $module = $data->{'modules'}[$i]; + if(ref($data->{'modules'}) eq "HASH") { + foreach my $i (keys %{$data->{'modules'}}) { + my $module = $data->{'modules'}{$i}; # Do not create any modules if the agent is not in learning mode. next unless ($agent_learning == 1); @@ -596,22 +729,33 @@ sub PandoraFMS::Recon::Base::report_scanned_agents($) { my $agentmodule_id = $module->{'agentmodule_id'}; if (!defined($agentmodule_id) || $agentmodule_id == 0) { - # Create module. - $agentmodule_id = pandora_create_module_from_hash( - $self->{'pa_config'}, - { - 'id_tipo_modulo' => get_module_id($self->{'dbh'}, $module->{'type'}), - 'id_modulo' => $module->{'id_modulo'}, - 'nombre' => safe_input($module->{'name'}), - 'descripcion' => '', - 'id_agente' => $agent_id, - 'ip_target' => $data->{'agent'}{'direccion'} - }, - $self->{'dbh'} - ); + + if (is_enabled($module->{'__module_component'})) { + # Module from network component. + $agentmodule_id = pandora_create_module_from_network_component( + $self->{'pa_config'}, + $module, + $agent_id, + $self->{'dbh'} + ); + } else { + # Create module - Direct. + $agentmodule_id = pandora_create_module_from_hash( + $self->{'pa_config'}, + { + 'id_tipo_modulo' => get_module_id($self->{'dbh'}, $module->{'type'}), + 'id_modulo' => $module->{'id_modulo'}, + 'nombre' => safe_input($module->{'name'}), + 'descripcion' => '', + 'id_agente' => $agent_id, + 'ip_target' => $data->{'agent'}{'direccion'} + }, + $self->{'dbh'} + ); + } # Store. - $data->{'modules'}[$i]{'agentmodule_id'} = $agentmodule_id; + $data->{'modules'}{$i}{'agentmodule_id'} = $agentmodule_id; $self->call( 'message', @@ -725,13 +869,11 @@ sub PandoraFMS::Recon::Base::apply_monitoring($) { # From 80% to 90%. my ($progress, $step) = (80, 10.0 / scalar(@hosts)); - use Data::Dumper; - print Dumper($self->{'task_data'}); - foreach my $label (keys %{$self->{'agents_found'}}) { $self->call('update_progress', $progress); $progress += $step; - print ">> $label\n"; + $self->call('message', "Checking modules for $label", 5); + $self->call('create_network_profile_modules', $label); } diff --git a/pandora_server/lib/PandoraFMS/Recon/Base.pm b/pandora_server/lib/PandoraFMS/Recon/Base.pm index 6ac2545794..ec437249f0 100644 --- a/pandora_server/lib/PandoraFMS/Recon/Base.pm +++ b/pandora_server/lib/PandoraFMS/Recon/Base.pm @@ -384,9 +384,9 @@ sub are_connected($$$$$) { $dev_1 = $self->{'aliases'}->{$dev_1} if defined($self->{'aliases'}->{$dev_1}); $dev_2 = $self->{'aliases'}->{$dev_2} if defined($self->{'aliases'}->{$dev_2}); - # Use ping modules when interfaces are unknown. - $if_1 = "ping" if $if_1 eq ''; - $if_2 = "ping" if $if_2 eq ''; + # Use Host Alive modules when interfaces are unknown. + $if_1 = "Host Alive" if $if_1 eq ''; + $if_2 = "Host Alive" if $if_2 eq ''; if ( defined($self->{'connections'}->{"${dev_1}\t${if_1}\t${dev_2}\t${if_2}"}) ||defined($self->{'connections'}->{"${dev_2}\t${if_2}\t${dev_1}\t${if_1}"})) { @@ -405,7 +405,7 @@ sub icmp_discovery($$) { $self->prepare_agent($addr); - $self->add_module($addr, 'icmp', + $self->add_module($addr, { 'ip_target' => $addr, 'name' => "Host Alive", @@ -1328,12 +1328,22 @@ sub add_agent($$) { ################################################################################ # Add module to agent (tmp pool) (will be registered at the end of the scan). ################################################################################ -sub add_module($$$$) { - my ($self, $agent, $type, $data) = @_; +sub add_module($$$) { + my ($self, $agent, $data) = @_; $self->prepare_agent($agent); - push @{$self->{'agents_found'}->{$agent}->{'modules'}}, $data; + $self->{'agents_found'}->{$agent}->{'modules'} = {} + unless ref($self->{'agents_found'}->{$agent}->{'modules'}) eq 'HASH'; + + # Test module. Is it well defined? + return unless ref($data) eq 'HASH' && defined($data->{'name'}) + && $data->{'name'} ne ''; + + # Test module. Is it success? + return unless $self->call('test_module', $agent, $data); + + $self->{'agents_found'}->{$agent}->{'modules'}{$data->{'name'}} = $data; } @@ -1409,7 +1419,7 @@ sub scan_subnet($) { { 'fping' => $self->{'fping'}, # XXX CAMBIAR POR 0.5 - 'networktimeout' => 0.01 # use fping defaults + 'networktimeout' => 0.5 # use fping defaults }, @hosts[$block_index .. $to - 1] ); @@ -2136,7 +2146,7 @@ sub wmi_scan { # CPU. my @cpus = $self->wmi_get_value_array($target, $auth, 'SELECT DeviceId FROM Win32_Processor', 0); foreach my $cpu (@cpus) { - $self->add_module($target, 'wmi', + $self->add_module($target, { 'target' => $target, 'query' => "SELECT LoadPercentage FROM Win32_Processor WHERE DeviceId='$cpu'", @@ -2153,7 +2163,7 @@ sub wmi_scan { # Memory. my $mem = $self->wmi_get_value($target, $auth, 'SELECT FreePhysicalMemory FROM Win32_OperatingSystem', 0); if (defined($mem)) { - $self->add_module($target, 'wmi', + $self->add_module($target, { 'target' => $target, 'query' => "SELECT FreePhysicalMemory, TotalVisibleMemorySize FROM Win32_OperatingSystem", @@ -2170,7 +2180,7 @@ sub wmi_scan { # Disk. my @units = $self->wmi_get_value_array($target, $auth, 'SELECT DeviceID FROM Win32_LogicalDisk', 0); foreach my $unit (@units) { - $self->add_module($target, 'wmi', + $self->add_module($target, { 'target' => $target, 'query' => "SELECT FreeSpace FROM Win32_LogicalDisk WHERE DeviceID='$unit'", diff --git a/pandora_server/lib/PandoraFMS/Tools.pm b/pandora_server/lib/PandoraFMS/Tools.pm index a478ca6d67..92c6a06cd5 100755 --- a/pandora_server/lib/PandoraFMS/Tools.pm +++ b/pandora_server/lib/PandoraFMS/Tools.pm @@ -113,6 +113,7 @@ our @EXPORT = qw( is_metaconsole is_offline is_empty + is_in_array to_number clean_blank credential_store_get_key @@ -659,6 +660,23 @@ sub is_empty { return 0; } +################################################################################ +# Check if a value is in an array +################################################################################ +sub is_in_array { + my ($array, $value) = @_; + + if (is_empty($value)) { + return 0; + } + + my %params = map { $_ => 1 } @{$array}; + if (exists($params{$value})) { + return 1; + } + return 0; +} + ################################################################################ # SUB md5check (param_1, param_2) # Verify MD5 file .checksum From 32c6e7f30c6cdb65901a7299d1cd632db3209056 Mon Sep 17 00:00:00 2001 From: fbsanchez Date: Thu, 19 Mar 2020 14:26:01 +0100 Subject: [PATCH 2/2] Minor fixes --- pandora_console/include/functions_ui.php | 24 ++++++++++--------- .../lib/PandoraFMS/DiscoveryServer.pm | 1 + 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/pandora_console/include/functions_ui.php b/pandora_console/include/functions_ui.php index 33f6e85ca6..b210bbfa50 100755 --- a/pandora_console/include/functions_ui.php +++ b/pandora_console/include/functions_ui.php @@ -2873,10 +2873,12 @@ function ui_progress( $text = $progress.'%'; } + $id = uniqid(); + ui_require_css_file('progress'); - $output .= ''; - $output .= ''; + $output .= ''; $output .= ''; if ($ajax !== false && is_array($ajax)) { @@ -2900,8 +2902,8 @@ function ui_progress( success: function(data) { try { val = JSON.parse(data); - $(".progress_main").attr("data-label", val + " %"); - $(".progress").width(val+"%"); + $("#'.$id.'").attr("data-label", val + " %"); + $("#'.$id.'_progress").width(val+"%"); } catch (e) { console.error(e); } @@ -2914,8 +2916,8 @@ function ui_progress( $output .= '