diff --git a/pandora_server/lib/PandoraFMS/DiscoveryServer.pm b/pandora_server/lib/PandoraFMS/DiscoveryServer.pm index b9ddda908e..3214e011e9 100644 --- a/pandora_server/lib/PandoraFMS/DiscoveryServer.pm +++ b/pandora_server/lib/PandoraFMS/DiscoveryServer.pm @@ -531,16 +531,14 @@ sub PandoraFMS::Recon::Base::test_module($$) { 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'})) { + 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'})) { @@ -550,6 +548,27 @@ sub PandoraFMS::Recon::Base::test_module($$) { } } + my $thresholds_defined = 0; + + if ((!defined($test->{'min_critical'}) || $test->{'min_critical'} == 0) + && (!defined($test->{'max_critical'}) || $test->{'max_critical'} == 0) + ) { + # In Default 0,0 do not test.or not defined + $thresholds_defined = 0; + } else { + # min or max are diferent from 0 + $thresholds_defined = 1; + } + + if ($thresholds_defined > 0) { + # Check thresholds. + 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'}; + } + } + } else { # String. if (!is_enabled($test->{'critical_inverse'})) { @@ -729,6 +748,80 @@ sub PandoraFMS::Recon::Base::create_interface_modules($$) { } +################################################################################ +# Add wmi modules to the given host. +################################################################################ +sub PandoraFMS::Recon::Base::create_wmi_modules { + my ($self, $target) = @_; + + # Add modules to the agent if it responds to WMI. + return unless ($self->wmi_responds($target)); + + my $auth = $self->wmi_credentials($target); + + # Register agent. + $self->add_agent($target); + + # Add modules. + # CPU. + my @cpus = $self->wmi_get_value_array($target, 'SELECT DeviceId FROM Win32_Processor', 0); + foreach my $cpu (@cpus) { + $self->add_module( + $target, + { + 'target' => $target, + 'query' => "SELECT LoadPercentage FROM Win32_Processor WHERE DeviceId='$cpu'", + 'auth' => $auth, + 'column' => 1, + 'name' => "CPU Load $cpu", + 'description' => safe_input("Load for $cpu (%)"), + 'id_tipo_modulo' => 1, + 'id_modulo' => 6, + 'unit' => '%', + } + ); + } + + # Memory. + my $mem = $self->wmi_get_value($target, 'SELECT FreePhysicalMemory FROM Win32_OperatingSystem', 0); + if (defined($mem)) { + $self->add_module( + $target, + { + 'target' => $target, + 'query' => "SELECT FreePhysicalMemory, TotalVisibleMemorySize FROM Win32_OperatingSystem", + 'auth' => $auth, + 'column' => 0, + 'name' => 'FreeMemory', + 'description' => safe_input('Free memory'), + 'id_tipo_modulo' => 1, + 'id_modulo' => 6, + 'unit' => 'KB', + } + ); + } + + # Disk. + my @units = $self->wmi_get_value_array($target, 'SELECT DeviceID FROM Win32_LogicalDisk', 0); + foreach my $unit (@units) { + $self->add_module( + $target, + { + 'target' => $target, + 'query' => "SELECT FreeSpace FROM Win32_LogicalDisk WHERE DeviceID='$unit'", + 'auth' => $auth, + 'column' => 1, + 'name' => "FreeDisk $unit", + 'description' => safe_input('Available disk space in kilobytes'), + 'id_tipo_modulo' => 1, + 'id_modulo' => 6, + 'unit' => 'KB', + } + ); + } + +} + ################################################################################ # Create network profile modules for the given agent. ################################################################################ @@ -797,6 +890,19 @@ sub PandoraFMS::Recon::Base::create_network_profile_modules($$) { } +################################################################################ +# Retrieve a key from credential store. +################################################################################ +sub PandoraFMS::Recon::Base::get_credentials { + my ($self, $key_index) = @_; + + return credential_store_get_key( + $self->{'pa_config'}, + $self->{'dbh'}, + $key_index + ); +} + ################################################################################ # Create agents and modules reported by Recon::Base. ################################################################################ @@ -1183,6 +1289,9 @@ sub PandoraFMS::Recon::Base::apply_monitoring($) { # Monitorization - interfaces $self->call('create_interface_modules', $label); + # Monitorization - WMI modules. + $self->call('create_wmi_modules', $label); + } $self->{'c_network_percent'} = 100; @@ -1358,319 +1467,6 @@ sub PandoraFMS::Recon::Base::create_agents($$) { } - -################################################################################ -# Create an agent for the given device. Returns the ID of the new (or -# existing) agent, undef on error. -################################################################################ -sub PandoraFMS::Recon::Base::create_agent($$) { - my ($self, $device) = @_; - - # Clean name. - $device = clean_blank($device); - - # Resolve hostnames. - my $host_name = (($self->{'resolve_names'} == 1) ? gethostbyaddr(inet_aton($device), AF_INET) : $device); - # Fallback to device IP if host name could not be resolved. - $host_name = $device if (!defined($host_name) || $host_name eq ''); - my $agent = locate_agent($self->{'pa_config'}, $self->{'dbh'}, $host_name); - - my ($agent_id, $agent_learning); - if (!defined($agent)) { - $host_name = $device unless defined ($host_name); - - # Guess the OS. - my $id_os = $self->guess_os($device); - - # Are we filtering hosts by OS? - return if ($self->{'id_os'} > 0 && $id_os != $self->{'id_os'}); - - # Are we filtering hosts by TCP port? - return if ($self->{'recon_ports'} ne '' && $self->tcp_scan($device) == 0); - my $location = get_geoip_info($self->{'pa_config'}, $device); - $agent_id = pandora_create_agent( - $self->{'pa_config'}, $self->{'pa_config'}->{'servername'}, - $host_name, $device, $self->{'group_id'}, 0, $id_os, - '', 300, $self->{'dbh'}, undef, $location->{'longitude'}, - $location->{'latitude'} - ); - return undef unless defined ($agent_id) and ($agent_id > 0); - - # Autoconfigure agent - if (defined($self->{'autoconfiguration_enabled'}) && $self->{'autoconfiguration_enabled'} == 1) { - my $agent_data = PandoraFMS::DB::get_db_single_row($self->{'dbh'}, 'SELECT * FROM tagente WHERE id_agente = ?', $agent_id); - # Update agent configuration once, after create agent. - enterprise_hook('autoconfigure_agent', [$self->{'pa_config'}, $host_name, $agent_id, $agent_data, $self->{'dbh'}, 1]); - } - - if (defined($self->{'main_event_id'})) { - my $addresses_str = join(',', safe_output($self->get_addresses($device))); - pandora_extended_event( - $self->{'pa_config'}, $self->{'dbh'}, $self->{'main_event_id'}, - "[Discovery] New " . safe_output($self->get_device_type($device)) . " found " . $host_name . " (" . $addresses_str . ") Agent $agent_id." - ); - - } - - $agent_learning = 1; - - # Create network profile modules for the agent - $self->create_network_profile_modules($agent_id, $device); - } - else { - $agent_id = $agent->{'id_agente'}; - $agent_learning = $agent->{'modo'}; - } - - # Do not create any modules if the agent is not in learning mode. - return unless ($agent_learning == 1); - - # Add found IP addresses to the agent. - foreach my $ip_addr ($self->get_addresses($device)) { - my $addr_id = get_addr_id($self->{'dbh'}, $ip_addr); - $addr_id = add_address($self->{'dbh'}, $ip_addr) unless ($addr_id > 0); - next unless ($addr_id > 0); - - # Assign the new address to the agent - my $agent_addr_id = get_agent_addr_id($self->{'dbh'}, $addr_id, $agent_id); - if ($agent_addr_id <= 0) { - db_do($self->{'dbh'}, 'INSERT INTO taddress_agent (`id_a`, `id_agent`) - VALUES (?, ?)', $addr_id, $agent_id); - } - } - - # Create a ping module. - my $module_id = get_agent_module_id($self->{'dbh'}, "ping", $agent_id); - if ($module_id <= 0) { - my %module = ('id_tipo_modulo' => 6, - 'id_modulo' => 2, - 'nombre' => "ping", - 'descripcion' => '', - 'id_agente' => $agent_id, - 'ip_target' => $device); - pandora_create_module_from_hash ($self->{'pa_config'}, \%module, $self->{'dbh'}); - } - - # Add interfaces to the agent if it responds to SNMP. - return $agent_id unless ($self->is_snmp_discovered($device)); - my $community = $self->get_community($device); - - my @output = $self->snmp_get_value_array($device, $PandoraFMS::Recon::Base::IFINDEX); - foreach my $if_index (@output) { - next unless ($if_index =~ /^[0-9]+$/); - - # Check the status of the interface. - if (!is_enabled($self->{'all_ifaces'})) { - my $if_status = $self->snmp_get_value($device, "$PandoraFMS::Recon::Base::IFOPERSTATUS.$if_index"); - next unless $if_status == 1; - } - - # Fill the module description with the IP and MAC addresses. - my $mac = $self->get_if_mac($device, $if_index); - my $ip = $self->get_if_ip($device, $if_index); - my $if_desc = ($mac ne '' ? "MAC $mac " : '') . ($ip ne '' ? "IP $ip" : ''); - - # Get the name of the network interface. - my $if_name = $self->snmp_get_value($device, "$PandoraFMS::Recon::Base::IFNAME.$if_index"); - $if_name = "if$if_index" unless defined ($if_name); - $if_name =~ s/"//g; - $if_name = clean_blank($if_name); - - # Check whether the module already exists. - my $module_id = get_agent_module_id($self->{'dbh'}, $if_name.'_ifOperStatus', $agent_id); - - next if ($module_id > 0 && !$agent_learning); - - # Encode problematic characters. - $if_desc = safe_input($if_desc); - - # Interface status module. - $module_id = get_agent_module_id($self->{'dbh'}, $if_name.'_ifOperStatus', $agent_id); - if ($module_id <= 0) { - my %module = ('id_tipo_modulo' => 18, - 'id_modulo' => 2, - 'nombre' => safe_input($if_name)."_ifOperStatus", - 'descripcion' => $if_desc, - 'id_agente' => $agent_id, - 'ip_target' => $device, - 'tcp_send' => $self->{'snmp_version'}, - 'custom_string_1' => $self->{'snmp_privacy_method'}, - 'custom_string_2' => $self->{'snmp_privacy_pass'}, - 'custom_string_3' => $self->{'snmp_security_level'}, - 'plugin_parameter' => $self->{'snmp_auth_method'}, - 'plugin_user' => $self->{'snmp_auth_user'}, - 'plugin_pass' => $self->{'snmp_auth_pass'}, - 'snmp_community' => $community, - 'snmp_oid' => "$PandoraFMS::Recon::Base::IFOPERSTATUS.$if_index" - ); - pandora_create_module_from_hash ($self->{'pa_config'}, \%module, $self->{'dbh'}); - } else { - my %module = ( - 'descripcion' => $if_desc, - 'ip_target' => $device, - 'snmp_community' => $community, - 'tcp_send' => $self->{'snmp_version'}, - 'custom_string_1' => $self->{'snmp_privacy_method'}, - 'custom_string_2' => $self->{'snmp_privacy_pass'}, - 'custom_string_3' => $self->{'snmp_security_level'}, - 'plugin_parameter' => $self->{'snmp_auth_method'}, - 'plugin_user' => $self->{'snmp_auth_user'}, - 'plugin_pass' => $self->{'snmp_auth_pass'}, - 'tcp_send' => $self->{'snmp_version'}, - ); - pandora_update_module_from_hash ($self->{'pa_config'}, \%module, 'id_agente_modulo', $module_id, $self->{'dbh'}); - } - - # Incoming traffic module. - my $if_hc_in_octets = $self->snmp_get_value($device, "$PandoraFMS::Recon::Base::IFHCINOCTECTS.$if_index"); - if (defined($if_hc_in_octets)) { - $module_id = get_agent_module_id($self->{'dbh'}, $if_name.'_ifHCInOctets', $agent_id); - if ($module_id <= 0) { - my %module = ('id_tipo_modulo' => 16, - 'id_modulo' => 2, - 'nombre' => safe_input($if_name)."_ifHCInOctets", - 'descripcion' => 'The total number of octets received on the interface, including framing characters. This object is a 64-bit version of ifInOctets.', - 'id_agente' => $agent_id, - 'ip_target' => $device, - 'tcp_send' => $self->{'snmp_version'}, - 'custom_string_1' => $self->{'snmp_privacy_method'}, - 'custom_string_2' => $self->{'snmp_privacy_pass'}, - 'custom_string_3' => $self->{'snmp_security_level'}, - 'plugin_parameter' => $self->{'snmp_auth_method'}, - 'plugin_user' => $self->{'snmp_auth_user'}, - 'plugin_pass' => $self->{'snmp_auth_pass'}, - 'snmp_community' => $community, - 'snmp_oid' => "$PandoraFMS::Recon::Base::IFHCINOCTECTS.$if_index"); - pandora_create_module_from_hash ($self->{'pa_config'}, \%module, $self->{'dbh'}); - } else { - my %module = ( - 'ip_target' => $device, - 'snmp_community' => $community, - 'tcp_send' => $self->{'snmp_version'}, - 'custom_string_1' => $self->{'snmp_privacy_method'}, - 'custom_string_2' => $self->{'snmp_privacy_pass'}, - 'custom_string_3' => $self->{'snmp_security_level'}, - 'plugin_parameter' => $self->{'snmp_auth_method'}, - 'plugin_user' => $self->{'snmp_auth_user'}, - 'plugin_pass' => $self->{'snmp_auth_pass'}, - ); - pandora_update_module_from_hash ($self->{'pa_config'}, \%module, 'id_agente_modulo', $module_id, $self->{'dbh'}); - } - } - # ifInOctets - elsif (defined($self->snmp_get_value($device, "$PandoraFMS::Recon::Base::IFINOCTECTS.$if_index"))) { - $module_id = get_agent_module_id($self->{'dbh'}, $if_name.'_ifInOctets', $agent_id); - if ($module_id <= 0) { - my %module = ('id_tipo_modulo' => 16, - 'id_modulo' => 2, - 'nombre' => safe_input($if_name)."_ifInOctets", - 'descripcion' => 'The total number of octets received on the interface, including framing characters.', - 'id_agente' => $agent_id, - 'ip_target' => $device, - 'tcp_send' => $self->{'snmp_version'}, - 'custom_string_1' => $self->{'snmp_privacy_method'}, - 'custom_string_2' => $self->{'snmp_privacy_pass'}, - 'custom_string_3' => $self->{'snmp_security_level'}, - 'plugin_parameter' => $self->{'snmp_auth_method'}, - 'plugin_user' => $self->{'snmp_auth_user'}, - 'plugin_pass' => $self->{'snmp_auth_pass'}, - 'snmp_community' => $community, - 'snmp_oid' => "$PandoraFMS::Recon::Base::IFINOCTECTS.$if_index"); - pandora_create_module_from_hash ($self->{'pa_config'}, \%module, $self->{'dbh'}); - } else { - my %module = ( - 'ip_target' => $device, - 'snmp_community' => $community, - 'tcp_send' => $self->{'snmp_version'}, - 'custom_string_1' => $self->{'snmp_privacy_method'}, - 'custom_string_2' => $self->{'snmp_privacy_pass'}, - 'custom_string_3' => $self->{'snmp_security_level'}, - 'plugin_parameter' => $self->{'snmp_auth_method'}, - 'plugin_user' => $self->{'snmp_auth_user'}, - 'plugin_pass' => $self->{'snmp_auth_pass'}, - ); - pandora_update_module_from_hash ($self->{'pa_config'}, \%module, 'id_agente_modulo', $module_id, $self->{'dbh'}); - } - } - - # Outgoing traffic module. - my $if_hc_out_octets = $self->snmp_get_value($device, "$PandoraFMS::Recon::Base::IFHCOUTOCTECTS.$if_index"); - if (defined($if_hc_out_octets)) { - $module_id = get_agent_module_id($self->{'dbh'}, $if_name.'_ifHCOutOctets', $agent_id); - if ($module_id <= 0) { - my %module = ('id_tipo_modulo' => 16, - 'id_modulo' => 2, - 'nombre' => safe_input($if_name)."_ifHCOutOctets", - 'descripcion' => 'The total number of octets received on the interface, including framing characters. This object is a 64-bit version of ifOutOctets.', - 'id_agente' => $agent_id, - 'ip_target' => $device, - 'tcp_send' => $self->{'snmp_version'}, - 'custom_string_1' => $self->{'snmp_privacy_method'}, - 'custom_string_2' => $self->{'snmp_privacy_pass'}, - 'custom_string_3' => $self->{'snmp_security_level'}, - 'plugin_parameter' => $self->{'snmp_auth_method'}, - 'plugin_user' => $self->{'snmp_auth_user'}, - 'plugin_pass' => $self->{'snmp_auth_pass'}, - 'snmp_community' => $community, - 'snmp_oid' => "$PandoraFMS::Recon::Base::IFHCOUTOCTECTS.$if_index"); - pandora_create_module_from_hash ($self->{'pa_config'}, \%module, $self->{'dbh'}); - } else { - my %module = ( - 'ip_target' => $device, - 'snmp_community' => $community, - 'tcp_send' => $self->{'snmp_version'}, - 'tcp_send' => $self->{'snmp_version'}, - 'custom_string_1' => $self->{'snmp_privacy_method'}, - 'custom_string_2' => $self->{'snmp_privacy_pass'}, - 'custom_string_3' => $self->{'snmp_security_level'}, - 'plugin_parameter' => $self->{'snmp_auth_method'}, - 'plugin_user' => $self->{'snmp_auth_user'}, - 'plugin_pass' => $self->{'snmp_auth_pass'}, - ); - pandora_update_module_from_hash ($self->{'pa_config'}, \%module, 'id_agente_modulo', $module_id, $self->{'dbh'}); - } - } - # ifOutOctets - elsif (defined($self->snmp_get_value($device, "$PandoraFMS::Recon::Base::IFOUTOCTECTS.$if_index"))) { - $module_id = get_agent_module_id($self->{'dbh'}, "${if_name}_ifOutOctets", $agent_id); - if ($module_id <= 0) { - my %module = ('id_tipo_modulo' => 16, - 'id_modulo' => 2, - 'nombre' => safe_input($if_name)."_ifOutOctets", - 'descripcion' => 'The total number of octets received on the interface, including framing characters.', - 'id_agente' => $agent_id, - 'ip_target' => $device, - 'tcp_send' => $self->{'snmp_version'}, - 'custom_string_1' => $self->{'snmp_privacy_method'}, - 'custom_string_2' => $self->{'snmp_privacy_pass'}, - 'custom_string_3' => $self->{'snmp_security_level'}, - 'plugin_parameter' => $self->{'snmp_auth_method'}, - 'plugin_user' => $self->{'snmp_auth_user'}, - 'plugin_pass' => $self->{'snmp_auth_pass'}, - 'snmp_community' => $community, - 'snmp_oid' => "$PandoraFMS::Recon::Base::IFOUTOCTECTS.$if_index"); - pandora_create_module_from_hash ($self->{'pa_config'}, \%module, $self->{'dbh'}); - } else { - my %module = ( - 'ip_target' => $device, - 'snmp_community' => $community, - 'tcp_send' => $self->{'snmp_version'}, - 'tcp_send' => $self->{'snmp_version'}, - 'custom_string_1' => $self->{'snmp_privacy_method'}, - 'custom_string_2' => $self->{'snmp_privacy_pass'}, - 'custom_string_3' => $self->{'snmp_security_level'}, - 'plugin_parameter' => $self->{'snmp_auth_method'}, - 'plugin_user' => $self->{'snmp_auth_user'}, - 'plugin_pass' => $self->{'snmp_auth_pass'}, - ); - pandora_update_module_from_hash ($self->{'pa_config'}, \%module, 'id_agente_modulo', $module_id, $self->{'dbh'}); - } - } - } - - return $agent_id; -} - ################################################################################ # Delete already existing connections. ################################################################################ diff --git a/pandora_server/lib/PandoraFMS/Recon/Base.pm b/pandora_server/lib/PandoraFMS/Recon/Base.pm index 8d2db93fb2..3ae1182b0e 100644 --- a/pandora_server/lib/PandoraFMS/Recon/Base.pm +++ b/pandora_server/lib/PandoraFMS/Recon/Base.pm @@ -42,6 +42,10 @@ use constant { DISCOVERY_REVIEW => 0, DISCOVERY_STANDARD => 1, DISCOVERY_RESULTS => 2, + WMI_UNREACHABLE => 1, + WMI_BAD_PASSWORD => 2, + WMI_GENERIC_ERROR => 3, + WMI_OK => 0, }; # $DEVNULL @@ -398,13 +402,17 @@ sub are_connected($$$$$) { } ################################################################################ -# Initialize tmp pool for device. -# Already discovered by scan_subnet. +# Initialize tmp pool for addr. +# Already discovered by scan_subnet. Registration only. ################################################################################ sub icmp_discovery($$) { my ($self, $addr) = @_; - $self->prepare_agent($addr); + # Create an agent for the device and add it to the list of known hosts. + push(@{$self->{'hosts'}}, $addr); + + # Create an agent for the device and add it to the list of known hosts. + $self->add_agent($addr); $self->add_module($addr, { @@ -459,13 +467,6 @@ sub snmp_discovery($$) { $self->snmp_pen($device); } } - - # Create an agent for the device and add it to the list of known hosts. - push(@{$self->{'hosts'}}, $device); - - # Create an agent for the device and add it to the list of known hosts. - $self->add_agent($device); - } ################################################################################ @@ -1379,7 +1380,7 @@ sub test_capabilities($$) { # WMI discovery. if (is_enabled($self->{'wmi_enabled'})) { # Add wmi scan if enabled. - $self->wmi_scan($addr); + $self->wmi_discovery($addr); } } @@ -2129,20 +2130,58 @@ sub traceroute_connectivity($$) { # Returns the credentials with which the host responds to WMI queries or # undef if it does not respond to WMI. ################################################################################ -sub responds_to_wmi { +sub wmi_credentials { + my ($self, $target) = @_; + return $self->{'wmi_auth'}{$target}; +} + +################################################################################ +# Calculate WMI credentials for target, 1 if calculated, undef if cannot +# connect to target. Credentials could be empty (-N) +################################################################################ +sub wmi_credentials_calculation { my ($self, $target) = @_; - foreach my $auth (@{$self->{'auth_strings_array'}}) { - my @output; - if ($auth ne '') { - @output = `$self->{'timeout_cmd'}$self->{'wmi_client'} -U $auth //$target "SELECT * FROM Win32_ComputerSystem" 2>&1`; - } else { - @output = `$self->{'timeout_cmd'}$self->{'wmi_client'} -N //$target "SELECT * FROM Win32_ComputerSystem" 2>&1`; + # Test empty credentials. + my @output = `$self->{'timeout_cmd'}$self->{'wmi_client'} -N //$target "SELECT * FROM Win32_ComputerSystem" 2>&1`; + my $rs = $self->wmi_output_check($?, @output); + + if ($rs == WMI_OK) { + $self->{'wmi_auth'}{$target} = ''; + return 1; + } + + if ($rs == WMI_UNREACHABLE) { + # Target does not respond. + $self->{'wmi'}{$target} = 0; + return undef; + } + + # Test all credentials selected. + foreach my $key_index (@{$self->{'auth_strings_array'}}) { + my $cred = $self->call('get_credentials', $key_index); + next if ref($cred) ne 'HASH'; + + my $auth = $cred->{'username'}.'%'.$cred->{'password'}; + next if $auth eq '%'; + + @output = `$self->{'timeout_cmd'}$self->{'wmi_client'} -U $auth //$target "SELECT * FROM Win32_ComputerSystem" 2>&1`; + + my $rs = $self->wmi_output_check($?, @output); + + if ($rs == WMI_OK) { + $self->{'wmi_auth'}{$target} = $auth; + $self->{'wmi'}{$target} = 1; + $self->{'summary'}->{'WMI'} += 1; + $self->call('message', "[".$target."] WMI available.", 10); + return 1; } - foreach my $line (@output) { - chomp($line); - return $auth if ($line =~ m/^CLASS: Win32_ComputerSystem$/); + if ($rs == WMI_UNREACHABLE) { + # Target does not respond. + $self->call('message', "[".$target."] WMI unreachable.", 10); + $self->{'wmi'}{$target} = 0; + return undef; } } @@ -2150,74 +2189,16 @@ sub responds_to_wmi { } ################################################################################ -# Add wmi modules to the given host. +# Tests wmi capability for addr. ################################################################################ -sub wmi_scan { - my ($self, $target) = @_; +sub wmi_discovery { + my ($self, $addr) = @_; - $self->call('message', "[".$target."] Checking WMI.", 5); + # Initialization. + $self->{'wmi'} = {} unless ref($self->{'wmi'}) eq 'HASH'; - my $auth = $self->responds_to_wmi($target); - return unless defined($auth); - - $self->{'summary'}->{'WMI'} += 1; - - $self->call('message', "[".$target."] WMI available.", 10); - - # Register agent. - $self->add_agent($target); - - # Add modules. - # CPU. - my @cpus = $self->wmi_get_value_array($target, $auth, 'SELECT DeviceId FROM Win32_Processor', 0); - foreach my $cpu (@cpus) { - $self->add_module($target, - { - 'target' => $target, - 'query' => "SELECT LoadPercentage FROM Win32_Processor WHERE DeviceId='$cpu'", - 'auth' => $auth, - 'column' => 1, - 'name' => "CPU Load $cpu", - 'description' => "Load for $cpu (%)", - 'type' => 'generic_data', - 'unit' => '%', - } - ); - } - - # Memory. - my $mem = $self->wmi_get_value($target, $auth, 'SELECT FreePhysicalMemory FROM Win32_OperatingSystem', 0); - if (defined($mem)) { - $self->add_module($target, - { - 'target' => $target, - 'query' => "SELECT FreePhysicalMemory, TotalVisibleMemorySize FROM Win32_OperatingSystem", - 'auth' => $auth, - 'column' => 0, - 'name' => 'FreeMemory', - 'description' => 'Free memory', - 'type' => 'generic_data', - 'unit' => 'KB', - } - ); - } - - # Disk. - my @units = $self->wmi_get_value_array($target, $auth, 'SELECT DeviceID FROM Win32_LogicalDisk', 0); - foreach my $unit (@units) { - $self->add_module($target, - { - 'target' => $target, - 'query' => "SELECT FreeSpace FROM Win32_LogicalDisk WHERE DeviceID='$unit'", - 'auth' => $auth, - 'column' => 1, - 'name' => "FreeDisk $unit", - 'description' => 'Available disk space in kilobytes', - 'type' => 'generic_data', - 'unit' => 'KB', - } - ); - } + # Calculate credentials. + $self->wmi_credentials_calculation($addr); } @@ -2225,10 +2206,52 @@ sub wmi_scan { # Extra: WMI imported methods. DO NOT EXPORT TO AVOID DOUBLE DEF. ################################################################################ +################################################################################ +# Validate wmi output. (err code and messages). +################################################################################ +sub wmi_output_check { + my ($self, $rc, @output) = @_; + if ($? != 0) { + # Something went wrong. + if (defined($output[-1]) && $output[-1] =~ /NTSTATUS: (.*)/) { + my $err = $1; + $self->{'last_wmi_error'} = $err; + + if ($err =~ /NT_STATUS_IO_TIMEOUT/ + || $err =~ /NT_STATUS_CONNECTION_REFUSED/ + ) { + # Fail. + return WMI_UNREACHABLE; + } + + if ($err =~ /NT_STATUS_ACCESS_DENIED/) { + return WMI_BAD_PASSWORD; + } + } + + # Fail. + return WMI_GENERIC_ERROR; + } + + # Ok. + return WMI_OK; +} + ################################################################################ # Performs a wmi get requests and returns the response as an array. ################################################################################ sub wmi_get { + my ($self, $target, $query) = @_; + + return () unless $self->wmi_responds($target); + + return $self->wmi_get_command($target, $self->{'wmi_auth'}{$target}, $query); +} + +################################################################################ +# Performs a wmi get requests and returns the response as an array. +################################################################################ +sub wmi_get_command { my ($self, $target, $auth, $query) = @_; my @output; @@ -2238,10 +2261,28 @@ sub wmi_get { @output = `$self->{'timeout_cmd'}"$self->{'wmi_client'}" -N //$target "$query" 2>&1`; } - # Something went wrong. - return () if ($? != 0); + my $rs = $self->wmi_output_check($?, @output); - return @output; + if ($rs == WMI_OK) { + return @output; + } + + $self->call( + 'message', + "[".$target."] WMI error: ".$self->{'last_wmi_error'}, + 10 + ); + + return (); +} + +################################################################################ +# Checks if target is reachable using wmi. +################################################################################ +sub wmi_responds { + my ($self, $target) = @_; + return 1 if is_enabled($self->{'wmi'}{$target}); + return 0; } ################################################################################ @@ -2249,10 +2290,10 @@ sub wmi_get { # Returns undef on error. ################################################################################ sub wmi_get_value { - my ($self, $target, $auth, $query, $column) = @_; + my ($self, $target, $query, $column) = @_; my @result; - my @output = $self->wmi_get($target, $auth, $query); + my @output = $self->wmi_get($target, $query); return undef unless defined($output[2]); my $line = $output[2]; @@ -2268,10 +2309,10 @@ sub wmi_get_value { # in an array. ################################################################################ sub wmi_get_value_array { - my ($self, $target, $auth, $query, $column) = @_; + my ($self, $target, $query, $column) = @_; my @result; - my @output = $self->wmi_get($target, $auth, $query); + my @output = $self->wmi_get($target, $query); foreach (my $i = 2; defined($output[$i]); $i++) { my $line = $output[$i]; chomp($line);