Skip to content

Commit

Permalink
Fix: Stop and clean finished/running openvas process before resumming…
Browse files Browse the repository at this point in the history
… a scan

What:
Fix: Stop and clean finished/running openvas process before resumming a scan

Jira: SC-624

Why:
If Ospd-Openvas crashes during a scan, probably the scan will continue
running in background, leaving the kb in redis occupied.
After ospd-openvas is initialized, the task can be resumed.
In this case, the scan id of the task will be the same one as before,
and there will be two kbs for the old and the new tasks with the same scanid.

This patch ensure that there is no kb in redis in used with the same scan id,
and also it will stop the scan in case it is still running.

How:

-Run a scan and kill ospd-openvas with SIGKILL (-9) signal: killall -9 ospd-openvas
-Start ospd-openvas
-Check with top/htop/ps, that openvas runs in background, it is an orphan process with ppid = 1 (system).
-Check in gvmd that the task is stopped or interrupted
-Check in redis that there is a main kb in use and the scan id. If you started the test with a clean setup, it should be the kb 2
-Resume the task. It will use the same scan id. Compare it whit the one checked in redis.
-Without the patch: the old openvas process still run and a new kbs are taken with the same scan id for the resume task
-With the patch, the old openvas process are stopped and its kb is released before resuming the scan.
  • Loading branch information
jjnicola committed Aug 29, 2022
2 parents 0772e48 + 137f70e commit 0071f0f
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 5 deletions.
32 changes: 27 additions & 5 deletions ospd_openvas/daemon.py
Expand Up @@ -1017,7 +1017,7 @@ def stop_scan_cleanup(
self,
kbdb: BaseDB,
scan_id: str,
ovas_process: psutil.Popen, # pylint: disable=arguments-differ
ovas_pid: str, # pylint: disable=arguments-differ
):
"""Set a key in redis to indicate the wrapper is stopped.
It is done through redis because it is a new multiprocess
Expand All @@ -1031,8 +1031,17 @@ def stop_scan_cleanup(
# Set stop flag in redis
kbdb.stop_scan(scan_id)

try:
ovas_process = psutil.Process(int(ovas_pid))
except psutil.NoSuchProcess:
ovas_process = None

# Check if openvas is running
if ovas_process.is_running():
if (
ovas_process
and ovas_process.is_running()
and ovas_process.name() == "openvas"
):
# Cleaning in case of Zombie Process
if ovas_process.status() == psutil.STATUS_ZOMBIE:
logger.debug(
Expand Down Expand Up @@ -1066,7 +1075,7 @@ def stop_scan_cleanup(
logger.debug(
"%s: Process with PID %s already stopped",
scan_id,
ovas_process.pid,
ovas_pid,
)

# Clean redis db
Expand All @@ -1081,6 +1090,15 @@ def exec_scan(self, scan_id: str):
dryrun.exec_dry_run_scan(scan_id, self.nvti, OSPD_PARAMS)
return

kbdb, err = self.main_db.check_consistency(scan_id)
if err < 0:
logger.debug(
"An old scan with the same scanID was found in the kb. "
"Waiting for the kb clean up to finish."
)
self.stop_scan_cleanup(kbdb, scan_id, kbdb.get_scan_process_id())
self.main_db.release_database(kbdb)

do_not_launch = False
kbdb = self.main_db.get_new_kb_database()
scan_prefs = PreferenceHandler(
Expand Down Expand Up @@ -1162,7 +1180,9 @@ def exec_scan(self, scan_id: str):
while kbdb.get_status(scan_id) == 'new':
res = openvas_process.poll()
if res and res < 0:
self.stop_scan_cleanup(kbdb, scan_id, openvas_process)
self.stop_scan_cleanup(
kbdb, scan_id, kbdb.get_scan_process_id()
)
logger.error(
'It was not possible run the task %s, since openvas ended '
'unexpectedly with errors during launching.',
Expand All @@ -1189,7 +1209,9 @@ def exec_scan(self, scan_id: str):
if scan_stopped:
logger.debug('%s: Scan stopped by the client', scan_id)

self.stop_scan_cleanup(kbdb, scan_id, openvas_process)
self.stop_scan_cleanup(
kbdb, scan_id, kbdb.get_scan_process_id()
)

# clean main_db, but wait for scanner to finish.
while not kbdb.target_is_finished(scan_id):
Expand Down
19 changes: 19 additions & 0 deletions ospd_openvas/db.py
Expand Up @@ -648,6 +648,25 @@ def find_kb_database_by_scan_id(

return None

def check_consistency(self, scan_id) -> Tuple[Optional[KbDB], int]:
"""Check if the current scan id already exists in a kb.
Return a tuple with the kb or none, and an error code, being 0 if
the db is clean, -1 on old finished scan, -2 on still running scan.
"""
err = 0

kb = self.find_kb_database_by_scan_id(scan_id)
current_status = None
if kb:
current_status = kb.get_status(scan_id)
if current_status == "finished":
err = -1
elif current_status == "stop_all" or current_status == "ready":
err = -2

return (kb, err)

def release_database(self, database: BaseDB):
self.release_database_by_index(database.index)
database.flush()
Expand Down

0 comments on commit 0071f0f

Please sign in to comment.