Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement heartbeat check #729

Merged
merged 17 commits into from May 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Added
- Add support for volatile keys. [#682](https://github.com/greenbone/openvas/pull/682)
- Extend nasl lint to check Syntax for Arguments for script_xref() function. [#714](https://github.com/greenbone/openvas/pull/714)
- Recheck alive status of host with specified amount of NVT timeouts. [#729](https://github.com/greenbone/openvas/pull/729)

### Changed
- function script_bugtraq_id getting skipped, linter warns. [#724](https://github.com/greenbone/openvas/pull/724)
Expand Down
3 changes: 3 additions & 0 deletions doc/openvas.8.in
Expand Up @@ -133,6 +133,9 @@ This is the maximum lifetime, in seconds of a plugin. It may happen that some pl
.IP scanner_plugins_timeout
Like plugins_timeout, but for ACT_SCANNER plugins.

.IP max_vts_timeouts
During a scan it might happen that a host unexpectedly shuts down, a firewall blocks the traffic, a network device issue break the connection, etc. This leads to many NVT timeouts and long scan durations. This option checks the alive status of the host again after the provided amount of max NVT timeouts are reached. If the host is considered dead the scan will be stopped for this host. Otherwise the scan will continue. This option requires Boreas alive test to be enabled. Default: option not set, disabled.

.IP safe_checks
Most of the time, openvas attempts to reproduce an exceptional condition to determine if the remote services are vulnerable to certain flaws. This includes the reproduction of buffer overflows or format strings, which may make the remote server crash. If you set this option to 'yes', openvas will disable the plugins which have the potential to crash the remote services, and will at the same time make several checks rely on the banner of the service tested instead of its behavior towards a certain input. This reduces false positives and makes openvas nicer towards your network, however this may make you miss important vulnerabilities (as a vulnerability affecting a given service may also affect another one).

Expand Down
4 changes: 2 additions & 2 deletions src/attack.c
Expand Up @@ -551,10 +551,10 @@ attack_host (struct scan_globals *globals, struct in6_addr *ip, GSList *vhosts,
else if (plugin != NULL && plugin == PLUG_RUNNING)
/* 50 milliseconds. */
usleep (50000);
pluginlaunch_wait_for_free_process (kb);
pluginlaunch_wait_for_free_process (main_kb, kb);
}

pluginlaunch_wait (kb);
pluginlaunch_wait (main_kb, kb);
if (!scan_is_stopped ())
{
int ret;
Expand Down
81 changes: 65 additions & 16 deletions src/pluginlaunch.c
Expand Up @@ -78,11 +78,46 @@ static int old_max_running_processes;
static GSList *non_simult_ports = NULL;
const char *hostname = NULL;

/**
* @brief Check if max_nvt_timeouts is set and if has been reached
*
* @return 1 if reached, 0 if not reached or no set.
*/
static int
max_nvt_timeouts_reached ()
{
static int vts_timeouts_counter = 0;
int max_vts_timeouts = 0;
const gchar *max_vts_timeouts_str = NULL;

/* Check if set */
if ((max_vts_timeouts_str = prefs_get ("max_vts_timeouts")) == NULL)
{
g_debug ("%s: max_vts_timeouts not set.", __func__);
return 0;
}

/* Check if enabled and valid value */
max_vts_timeouts = atoi (max_vts_timeouts_str);
if (max_vts_timeouts <= 0)
{
g_debug ("%s: max_vts_timeouts disabled", __func__);
return 0;
}

vts_timeouts_counter++;
/* Check if reached */
if (vts_timeouts_counter >= max_vts_timeouts)
return 1;

return 0;
}

/**
*
*/
static void
update_running_processes (kb_t kb)
update_running_processes (kb_t main_kb, kb_t kb)
{
int i;
struct timeval now;
Expand Down Expand Up @@ -110,17 +145,31 @@ update_running_processes (kb_t kb)
if (is_alive)
{
char msg[2048];

if (log_whole)
g_message ("%s (pid %d) is slow to finish - killing it",
oid, processes[i].pid);

sprintf (msg,
"ERRMSG|||%s||| |||general/tcp|||%s|||"
"NVT timed out after %d seconds.",
hostname, oid ?: " ", processes[i].timeout);
g_snprintf (msg, sizeof (msg),
"ERRMSG|||%s||| |||general/tcp|||%s|||"
"NVT timed out after %d seconds.",
hostname, oid ?: " ", processes[i].timeout);
kb_item_push_str (main_kb, "internal/results", msg);

kb_item_push_str (kb, "internal/results", msg);
/* Check for max VTs timeouts */
if (max_nvt_timeouts_reached ())
{
/* Check if host is still alive and send a message
if it is dead. */
if (check_host_still_alive (kb, hostname) == 0)
{
g_snprintf (msg, sizeof (msg),
"ERRMSG|||%s||| |||general/tcp||| |||"
"Host has been marked as dead. Too many "
"NVT_TIMEOUTs.",
hostname);
kb_item_push_str (main_kb, "internal/results", msg);
}
}

ret_terminate = terminate_process (processes[i].pid);
if (ret_terminate == 0)
Expand Down Expand Up @@ -243,7 +292,7 @@ simult_ports (const char *oid, const char *next_oid)
* in the processes array otherwise.
*/
static int
next_free_process (kb_t kb, struct scheduler_plugin *upcoming)
next_free_process (kb_t main_kb, kb_t kb, struct scheduler_plugin *upcoming)
{
int r;

Expand All @@ -254,7 +303,7 @@ next_free_process (kb_t kb, struct scheduler_plugin *upcoming)
{
while (process_alive (processes[r].pid))
{
update_running_processes (kb);
update_running_processes (main_kb, kb);
usleep (250000);
}
}
Expand Down Expand Up @@ -399,8 +448,8 @@ plugin_launch (struct scan_globals *globals, struct scheduler_plugin *plugin,
int p;

/* Wait for a free slot */
pluginlaunch_wait_for_free_process (main_kb);
p = next_free_process (main_kb, plugin);
pluginlaunch_wait_for_free_process (main_kb, kb);
p = next_free_process (main_kb, kb, plugin);
if (p < 0)
return -1;
processes[p].plugin = plugin;
Expand All @@ -421,11 +470,11 @@ plugin_launch (struct scan_globals *globals, struct scheduler_plugin *plugin,
* @brief Waits and 'pushes' processes until num_running_processes is 0.
*/
void
pluginlaunch_wait (kb_t kb)
pluginlaunch_wait (kb_t main_kb, kb_t kb)
{
while (num_running_processes)
{
update_running_processes (kb);
update_running_processes (main_kb, kb);
if (num_running_processes)
waitpid (-1, NULL, 0);
}
Expand Down Expand Up @@ -454,11 +503,11 @@ timeout_running_processes (void)
* changed.
*/
void
pluginlaunch_wait_for_free_process (kb_t kb)
pluginlaunch_wait_for_free_process (kb_t main_kb, kb_t kb)
{
if (!num_running_processes)
return;
update_running_processes (kb);
update_running_processes (main_kb, kb);
/* Max number of processes are still running, wait for a child to exit or
* to timeout. */
while (
Expand All @@ -474,6 +523,6 @@ pluginlaunch_wait_for_free_process (kb_t kb)
sigaddset (&mask, SIGCHLD);
if (sigtimedwait (&mask, NULL, &ts) < 0 && errno != EAGAIN)
g_warning ("%s: %s", __func__, strerror (errno));
update_running_processes (kb);
update_running_processes (main_kb, kb);
}
}
4 changes: 2 additions & 2 deletions src/pluginlaunch.h
Expand Up @@ -31,8 +31,8 @@

void
pluginlaunch_init (const char *);
void pluginlaunch_wait (kb_t);
void pluginlaunch_wait_for_free_process (kb_t);
void pluginlaunch_wait (kb_t, kb_t);
void pluginlaunch_wait_for_free_process (kb_t, kb_t);

void
pluginlaunch_stop (void);
Expand Down
57 changes: 57 additions & 0 deletions src/utils.c
Expand Up @@ -27,6 +27,7 @@

#include <errno.h> /* for errno() */
#include <gvm/base/prefs.h> /* for prefs_get() */
#include <gvm/boreas/cli.h> /* for is_host_alive() */
#include <stdlib.h> /* for atoi() */
#include <string.h> /* for strcmp() */
#include <sys/ioctl.h> /* for ioctl() */
Expand Down Expand Up @@ -221,6 +222,62 @@ data_left (int soc)
return data;
}

/**
* @brief Check if the hosts is still alive and set it as dead if not.
*
* @param kb Host kb where the host is set as dead.
*
* @return 1 if considered alive, 0 if it is dead. -1 on error
* or option disabled.
*/
int
check_host_still_alive (kb_t kb, const char *hostname)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As suggested above. Maybe move counter logic into other function. Also rename variables according to settings name change (something like heartbeat -> max_nvt_timouts).

{
int is_alive = 0;
boreas_error_t alive_err;

/* Heartbeat will work only with boreas enabled. We check if we
have all what we need before running a heartbeat check. */
if (prefs_get_bool ("test_alive_hosts_only"))
{
const gchar *alive_test_str = prefs_get ("ALIVE_TEST");

/* Don't perfom a hearbeat check if the host is always considered
alive or the alive test is not valid. */
if (!(alive_test_str
&& atoi (alive_test_str) >= ALIVE_TEST_TCP_ACK_SERVICE
&& atoi (alive_test_str) < 32 // max value for alive test combi.
&& !((atoi (alive_test_str)) & ALIVE_TEST_CONSIDER_ALIVE)))
return -1;
}
else
{
g_warning ("%s: Max VTs timeout has been reached, but Boreas is not "
"enabled. Heartbeat check for %s will not be perfomed",
__func__, hostname);
return -1;
}

alive_err = is_host_alive (hostname, &is_alive);
if (alive_err)
{
g_warning ("%s: Heartbeat check failed for %s with error %d.", __func__,
hostname, alive_err);
return -1;
}

if (is_alive == 0)
{
g_message ("%s: Heartbeat check was not successful. The host %s has"
" been set as dead.",
__func__, hostname);
kb_item_set_int (kb, "Host/dead", 1);
return 0;
}

return 1;
}

void
wait_for_children1 (void)
{
Expand Down
2 changes: 2 additions & 0 deletions src/utils.h
Expand Up @@ -48,4 +48,6 @@ is_scanner_only_pref (const char *);
int
store_file (struct scan_globals *, const char *, const char *);

int
check_host_still_alive (kb_t, const char *);
#endif