Completed
Push — master ( 2ca5c0...4db484 )
by Juan José
26s queued 22s
created

ospd_openvas.daemon.OSPDopenvas.scheduler()   A

Complexity

Conditions 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
# -*- coding: utf-8 -*-
2
# Copyright (C) 2014-2020 Greenbone Networks GmbH
3
#
4
# SPDX-License-Identifier: AGPL-3.0-or-later
5
#
6
# This program is free software: you can redistribute it and/or modify
7
# it under the terms of the GNU Affero General Public License as
8
# published by the Free Software Foundation, either version 3 of the
9
# License, or (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU Affero General Public License for more details.
15
#
16
# You should have received a copy of the GNU Affero General Public License
17
# along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19
20
# pylint: disable=too-many-lines
21
22
""" Setup for the OSP OpenVAS Server. """
23
24
import logging
25
import time
26
import copy
27
28
from typing import Optional, Dict, List, Tuple, Iterator
29
from datetime import datetime
30
31
from pathlib import Path
32
from os import geteuid
33
from lxml.etree import tostring, SubElement, Element
34
35
import psutil
36
37
from ospd.errors import OspdError
38
from ospd.ospd import OSPDaemon
39
from ospd.server import BaseServer
40
from ospd.main import main as daemon_main
41
from ospd.cvss import CVSS
42
from ospd.vtfilter import VtsFilter
43
44
from ospd_openvas import __version__
45
from ospd_openvas.errors import OspdOpenvasError
46
47
from ospd_openvas.nvticache import NVTICache
48
from ospd_openvas.db import MainDB, BaseDB, ScanDB
49
from ospd_openvas.lock import LockFile
50
from ospd_openvas.preferencehandler import PreferenceHandler
51
from ospd_openvas.openvas import Openvas
52
53
logger = logging.getLogger(__name__)
54
55
56
OSPD_DESC = """
57
This scanner runs OpenVAS to scan the target hosts.
58
59
OpenVAS (Open Vulnerability Assessment Scanner) is a powerful scanner
60
for vulnerabilities in IT infrastrucutres. The capabilities include
61
unauthenticated scanning as well as authenticated scanning for
62
various types of systems and services.
63
64
For more details about OpenVAS see:
65
http://www.openvas.org/
66
67
The current version of ospd-openvas is a simple frame, which sends
68
the server parameters to the Greenbone Vulnerability Manager daemon (GVMd) and
69
checks the existence of OpenVAS binary. But it can not run scans yet.
70
"""
71
72
OSPD_PARAMS = {
73
    'auto_enable_dependencies': {
74
        'type': 'boolean',
75
        'name': 'auto_enable_dependencies',
76
        'default': 1,
77
        'mandatory': 1,
78
        'description': 'Automatically enable the plugins that are depended on',
79
    },
80
    'cgi_path': {
81
        'type': 'string',
82
        'name': 'cgi_path',
83
        'default': '/cgi-bin:/scripts',
84
        'mandatory': 1,
85
        'description': 'Look for default CGIs in /cgi-bin and /scripts',
86
    },
87
    'checks_read_timeout': {
88
        'type': 'integer',
89
        'name': 'checks_read_timeout',
90
        'default': 5,
91
        'mandatory': 1,
92
        'description': (
93
            'Number  of seconds that the security checks will '
94
            + 'wait for when doing a recv()'
95
        ),
96
    },
97
    'drop_privileges': {
98
        'type': 'boolean',
99
        'name': 'drop_privileges',
100
        'default': 0,
101
        'mandatory': 1,
102
        'description': '',
103
    },
104
    'network_scan': {
105
        'type': 'boolean',
106
        'name': 'network_scan',
107
        'default': 0,
108
        'mandatory': 1,
109
        'description': '',
110
    },
111
    'non_simult_ports': {
112
        'type': 'string',
113
        'name': 'non_simult_ports',
114
        'default': '139, 445, 3389, Services/irc',
115
        'mandatory': 1,
116
        'description': (
117
            'Prevent to make two connections on the same given '
118
            + 'ports at the same time.'
119
        ),
120
    },
121
    'open_sock_max_attempts': {
122
        'type': 'integer',
123
        'name': 'open_sock_max_attempts',
124
        'default': 5,
125
        'mandatory': 0,
126
        'description': (
127
            'Number of unsuccessful retries to open the socket '
128
            + 'before to set the port as closed.'
129
        ),
130
    },
131
    'timeout_retry': {
132
        'type': 'integer',
133
        'name': 'timeout_retry',
134
        'default': 5,
135
        'mandatory': 0,
136
        'description': (
137
            'Number of retries when a socket connection attempt ' + 'timesout.'
138
        ),
139
    },
140
    'optimize_test': {
141
        'type': 'integer',
142
        'name': 'optimize_test',
143
        'default': 5,
144
        'mandatory': 0,
145
        'description': (
146
            'By default, openvas does not trust the remote ' + 'host banners.'
147
        ),
148
    },
149
    'plugins_timeout': {
150
        'type': 'integer',
151
        'name': 'plugins_timeout',
152
        'default': 5,
153
        'mandatory': 0,
154
        'description': 'This is the maximum lifetime, in seconds of a plugin.',
155
    },
156
    'report_host_details': {
157
        'type': 'boolean',
158
        'name': 'report_host_details',
159
        'default': 1,
160
        'mandatory': 1,
161
        'description': '',
162
    },
163
    'safe_checks': {
164
        'type': 'boolean',
165
        'name': 'safe_checks',
166
        'default': 1,
167
        'mandatory': 1,
168
        'description': (
169
            'Disable the plugins with potential to crash '
170
            + 'the remote services'
171
        ),
172
    },
173
    'scanner_plugins_timeout': {
174
        'type': 'integer',
175
        'name': 'scanner_plugins_timeout',
176
        'default': 36000,
177
        'mandatory': 1,
178
        'description': 'Like plugins_timeout, but for ACT_SCANNER plugins.',
179
    },
180
    'time_between_request': {
181
        'type': 'integer',
182
        'name': 'time_between_request',
183
        'default': 0,
184
        'mandatory': 0,
185
        'description': (
186
            'Allow to set a wait time between two actions '
187
            + '(open, send, close).'
188
        ),
189
    },
190
    'unscanned_closed': {
191
        'type': 'boolean',
192
        'name': 'unscanned_closed',
193
        'default': 1,
194
        'mandatory': 1,
195
        'description': '',
196
    },
197
    'unscanned_closed_udp': {
198
        'type': 'boolean',
199
        'name': 'unscanned_closed_udp',
200
        'default': 1,
201
        'mandatory': 1,
202
        'description': '',
203
    },
204
    'expand_vhosts': {
205
        'type': 'boolean',
206
        'name': 'expand_vhosts',
207
        'default': 1,
208
        'mandatory': 0,
209
        'description': 'Whether to expand the target hosts '
210
        + 'list of vhosts with values gathered from sources '
0 ignored issues
show
Coding Style introduced by
Wrong continued indentation (add 15 spaces).
Loading history...
211
        + 'such as reverse-lookup queries and VT checks '
0 ignored issues
show
Coding Style introduced by
Wrong continued indentation (add 15 spaces).
Loading history...
212
        + 'for SSL/TLS certificates.',
0 ignored issues
show
Coding Style introduced by
Wrong continued indentation (add 15 spaces).
Loading history...
213
    },
214
    'test_empty_vhost': {
215
        'type': 'boolean',
216
        'name': 'test_empty_vhost',
217
        'default': 0,
218
        'mandatory': 0,
219
        'description': 'If  set  to  yes, the scanner will '
220
        + 'also test the target by using empty vhost value '
0 ignored issues
show
Coding Style introduced by
Wrong continued indentation (add 15 spaces).
Loading history...
221
        + 'in addition to the targets associated vhost values.',
0 ignored issues
show
Coding Style introduced by
Wrong continued indentation (add 15 spaces).
Loading history...
222
    },
223
}
224
225
226
def safe_int(value: str) -> Optional[int]:
227
    """ Convert a sring into an integer and return None in case of errors during
228
    conversion
229
    """
230
    try:
231
        return int(value)
232
    except (ValueError, TypeError):
233
        return None
234
235
236
class OpenVasVtsFilter(VtsFilter):
237
    """ Methods to overwrite the ones in the original class.
238
    Each method formats the value to be compatible with the filter
239
    """
240
241
    def format_vt_modification_time(self, value: str) -> str:
242
        """ Convert the string seconds since epoch into a 19 character
243
        string representing YearMonthDayHourMinuteSecond,
244
        e.g. 20190319122532. This always refers to UTC.
245
        """
246
247
        return datetime.utcfromtimestamp(int(value)).strftime("%Y%m%d%H%M%S")
248
249
250
class OSPDopenvas(OSPDaemon):
251
252
    """ Class for ospd-openvas daemon. """
253
254
    def __init__(
255
        self, *, niceness=None, lock_file_dir='/var/run/ospd', **kwargs
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
256
    ):
257
        """ Initializes the ospd-openvas daemon's internal data. """
258
259
        super().__init__(customvtfilter=OpenVasVtsFilter(), **kwargs)
260
261
        self.server_version = __version__
262
263
        self._niceness = str(niceness)
264
265
        self.feed_lock = LockFile(Path(lock_file_dir) / 'feed-update.lock')
266
        self.daemon_info['name'] = 'OSPd OpenVAS'
267
        self.scanner_info['name'] = 'openvas'
268
        self.scanner_info['version'] = ''  # achieved during self.init()
269
        self.scanner_info['description'] = OSPD_DESC
270
271
        for name, param in OSPD_PARAMS.items():
272
            self.set_scanner_param(name, param)
273
274
        self._sudo_available = None
275
        self._is_running_as_root = None
276
277
        self.scan_only_params = dict()
278
279
        self.main_db = MainDB()
280
281
        self.nvti = NVTICache(self.main_db)
282
283
        self.pending_feed = None
284
285
    def init(self, server: BaseServer) -> None:
286
287
        server.start(self.handle_client_stream)
288
289
        self.scanner_info['version'] = Openvas.get_version()
290
291
        self.set_params_from_openvas_settings()
292
293
        if not self.nvti.ctx:
294
            with self.feed_lock.wait_for_lock():
295
                Openvas.load_vts_into_redis()
296
297
        self.load_vts()
298
299
        self.initialized = True
300
301
    def set_params_from_openvas_settings(self):
302
        """ Set OSPD_PARAMS with the params taken from the openvas executable.
303
        """
304
        param_list = Openvas.get_settings()
305
306
        for elem in param_list:
307
            if elem not in OSPD_PARAMS:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable OSPD_PARAMS does not seem to be defined.
Loading history...
308
                self.scan_only_params[elem] = param_list[elem]
309
            else:
310
                OSPD_PARAMS[elem]['default'] = param_list[elem]
311
312
    def feed_is_outdated(self, current_feed: str) -> Optional[bool]:
313
        """ Compare the current feed with the one in the disk.
314
315
        Return:
316
            False if there is no new feed.
317
            True if the feed version in disk is newer than the feed in
318
                redis cache.
319
            None if there is no feed on the disk.
320
        """
321
        plugins_folder = self.scan_only_params.get('plugins_folder')
322
        if not plugins_folder:
323
            raise OspdOpenvasError("Error: Path to plugins folder not found.")
324
325
        feed_info_file = Path(plugins_folder) / 'plugin_feed_info.inc'
326
        if not feed_info_file.exists():
327
            self.set_params_from_openvas_settings()
328
            logger.debug('Plugins feed file %s not found.', feed_info_file)
329
            return None
330
331
        current_feed = safe_int(current_feed)
332
333
        feed_date = None
334
        with feed_info_file.open() as fcontent:
335
            for line in fcontent:
336
                if "PLUGIN_SET" in line:
337
                    feed_date = line.split('=', 1)[1]
338
                    feed_date = feed_date.strip()
339
                    feed_date = feed_date.replace(';', '')
340
                    feed_date = feed_date.replace('"', '')
341
                    feed_date = safe_int(feed_date)
342
                    break
343
344
        logger.debug("Current feed version: %s", current_feed)
345
        logger.debug("Plugin feed version: %s", feed_date)
346
347
        return (
348
            (not feed_date) or (not current_feed) or (current_feed < feed_date)
349
        )
350
351
    def feed_is_healthy(self):
352
        """ Compare the amount of filename keys and nvt keys in redis
353
        with the amount of oid loaded in memory.
354
355
        Return:
356
            True if the count is matching. False on failure.
357
        """
358
        filename_count = self.nvti.get_nvt_files_count()
359
        nvt_count = self.nvti.get_nvt_count()
360
361
        return len(self.vts) == filename_count == nvt_count
362
363
    def check_feed(self):
364
        """ Check if there is a feed update.
365
366
        Wait until all the running scans finished. Set a flag to announce there
367
        is a pending feed update, which avoids to start a new scan.
368
        """
369
        current_feed = self.nvti.get_feed_version()
370
        is_outdated = self.feed_is_outdated(current_feed)
371
372
        # Check if the feed is already accessible from the disk.
373
        if current_feed and is_outdated is None:
374
            self.pending_feed = True
375
            return
376
377
        # Check if the nvticache in redis is outdated
378
        if not current_feed or is_outdated:
379
            self.pending_feed = True
380
381
            with self.feed_lock as fl:
382
                if fl.has_lock():
383
                    Openvas.load_vts_into_redis()
384
                else:
385
                    logger.debug(
386
                        "The feed was not upload or it is outdated, "
387
                        "but other process is locking the update. "
388
                        "Trying again later..."
389
                    )
390
                    return
391
392
        _running_scan = False
393
        for scan_id in self.scan_processes:
394
            if self.scan_processes[scan_id].is_alive():
395
                _running_scan = True
396
397
        # Check if the NVT dict is outdated
398
        if self.pending_feed:
399
            _pending_feed = True
400
        else:
401
            _pending_feed = (
402
                self.get_vts_version() != self.nvti.get_feed_version()
403
            )
404
405
        _feed_is_healthy = self.feed_is_healthy()
406
        if _running_scan and not _feed_is_healthy:
407
            _pending_feed = True
408
409
            with self.feed_lock as fl:
410
                if fl.has_lock():
411
                    self.nvti.force_reload()
412
                    Openvas.load_vts_into_redis()
413
                else:
414
                    logger.debug(
415
                        "The VT Cache in memory is not healthy "
416
                        "and other process is locking the update. "
417
                        "Trying again later..."
418
                    )
419
                    return
420
421
        if _running_scan and _pending_feed:
422
            if not self.pending_feed:
423
                self.pending_feed = True
424
                logger.info(
425
                    'There is a running scan process locking the feed update. '
426
                    'Therefore the feed update will be performed later.'
427
                )
428
        elif (
429
            _pending_feed
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
430
            and not _running_scan
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
431
            and not self.feed_lock.is_locked()
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
432
        ):
433
            self.vts.clear()
434
            self.load_vts()
435
436
    def scheduler(self):
437
        """This method is called periodically to run tasks."""
438
        self.check_feed()
439
440
    def get_single_vt(self, vt_id, oids=None):
441
        _vt_params = self.nvti.get_nvt_params(vt_id)
442
        _vt_refs = self.nvti.get_nvt_refs(vt_id)
443
        _custom = self.nvti.get_nvt_metadata(vt_id)
444
445
        _name = _custom.pop('name')
446
        _vt_creation_time = _custom.pop('creation_date')
447
        _vt_modification_time = _custom.pop('last_modification')
448
449
        if oids:
450
            _vt_dependencies = list()
451
            if 'dependencies' in _custom:
452
                _deps = _custom.pop('dependencies')
453
                _deps_list = _deps.split(', ')
454
                for dep in _deps_list:
455
                    _vt_dependencies.append(oids.get(dep))
456
        else:
457
            _vt_dependencies = None
458
459
        _summary = None
460
        _impact = None
461
        _affected = None
462
        _insight = None
463
        _solution = None
464
        _solution_t = None
465
        _vuldetect = None
466
        _qod_t = None
467
        _qod_v = None
468
469
        if 'summary' in _custom:
470
            _summary = _custom.pop('summary')
471
        if 'impact' in _custom:
472
            _impact = _custom.pop('impact')
473
        if 'affected' in _custom:
474
            _affected = _custom.pop('affected')
475
        if 'insight' in _custom:
476
            _insight = _custom.pop('insight')
477
        if 'solution' in _custom:
478
            _solution = _custom.pop('solution')
479
            if 'solution_type' in _custom:
480
                _solution_t = _custom.pop('solution_type')
481
482
        if 'vuldetect' in _custom:
483
            _vuldetect = _custom.pop('vuldetect')
484
        if 'qod_type' in _custom:
485
            _qod_t = _custom.pop('qod_type')
486
        elif 'qod' in _custom:
487
            _qod_v = _custom.pop('qod')
488
489
        _severity = dict()
490
        if 'severity_base_vector' in _custom:
491
            _severity_vector = _custom.pop('severity_base_vector')
492
        else:
493
            _severity_vector = _custom.pop('cvss_base_vector')
494
        _severity['severity_base_vector'] = _severity_vector
495
        if 'severity_type' in _custom:
496
            _severity_type = _custom.pop('severity_type')
497
        else:
498
            _severity_type = 'cvss_base_v2'
499
        _severity['severity_type'] = _severity_type
500
        if 'severity_origin' in _custom:
501
            _severity['severity_origin'] = _custom.pop('severity_origin')
502
503
        if _name is None:
504
            _name = ''
505
506
        vt = {'name': _name}
507
        if _custom is not None:
508
            vt["custom"] = _custom
509
        if _vt_params is not None:
510
            vt["vt_params"] = _vt_params
511
        if _vt_refs is not None:
512
            vt["vt_refs"] = _vt_refs
513
        if _vt_dependencies is not None:
514
            vt["vt_dependencies"] = _vt_dependencies
515
        if _vt_creation_time is not None:
516
            vt["creation_time"] = _vt_creation_time
517
        if _vt_modification_time is not None:
518
            vt["modification_time"] = _vt_modification_time
519
        if _summary is not None:
520
            vt["summary"] = _summary
521
        if _impact is not None:
522
            vt["impact"] = _impact
523
        if _affected is not None:
524
            vt["affected"] = _affected
525
        if _insight is not None:
526
            vt["insight"] = _insight
527
528
        if _solution is not None:
529
            vt["solution"] = _solution
530
            if _solution_t is not None:
531
                vt["solution_type"] = _solution_t
532
533
        if _vuldetect is not None:
534
            vt["detection"] = _vuldetect
535
536
        if _qod_t is not None:
537
            vt["qod_type"] = _qod_t
538
        elif _qod_v is not None:
539
            vt["qod"] = _qod_v
540
541
        if _severity is not None:
542
            vt["severities"] = _severity
543
544
        return vt
545
546
    def get_vt_iterator(
547
        self, vt_selection: List[str] = None, details: bool = True
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
548
    ) -> Iterator[Tuple[str, Dict]]:
549
        """ Yield the vts from the Redis NVTicache. """
550
551
        oids = None
552
        if details:
553
            oids = dict(self.nvti.get_oids())
554
555
        for vt_id in vt_selection:
556
            vt = self.get_single_vt(vt_id, oids)
557
            yield (vt_id, vt)
558
559
    def load_vts(self):
560
        """ Load the VT's metadata into the vts global dictionary. """
561
562
        with self.feed_lock as fl:
563
            if not fl.has_lock():
564
                logger.warning(
565
                    'Error acquiring feed lock. Trying again later...'
566
                )
567
                return
568
569
            self.initialized = False
570
            logger.info('Loading VTs in memory.')
571
572
            oids = dict(self.nvti.get_oids())
573
574
            logger.debug('Found %s NVTs in redis.', len(oids))
575
576
            for _, vt_id in oids.items():
577
                vt = self.get_single_vt(vt_id, oids)
578
579
                if (
580
                    not vt
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
581
                    or vt.get('vt_params') is None
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
582
                    or vt.get('custom') is None
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
583
                ):
584
                    logger.warning(
585
                        'Error loading VTs in memory. Trying again later...'
586
                    )
587
                    return
588
589
                custom = {'family': vt['custom'].get('family')}
590
                try:
591
                    self.add_vt(
592
                        vt_id,
593
                        name=vt.get('name'),
594
                        qod_t=vt.get('qod_type'),
595
                        qod_v=vt.get('qod'),
596
                        severities=vt.get('severities'),
597
                        vt_modification_time=vt.get('modification_time'),
598
                        vt_params=vt.get('vt_params'),
599
                        custom=custom,
600
                    )
601
                except OspdError as e:
602
                    logger.warning("Error while adding VT %s. %s", vt_id, e)
603
604
            _feed_version = self.nvti.get_feed_version()
605
606
            self.set_vts_version(vts_version=_feed_version)
607
            self.vts.calculate_vts_collection_hash()
608
            self.pending_feed = False
609
            self.initialized = True
610
611
            logger.info('Finish loading up vts.')
612
613
            logger.debug('Loaded %s vts.', len(self.vts))
614
615
    @staticmethod
616
    def get_custom_vt_as_xml_str(vt_id: str, custom: Dict) -> str:
617
        """ Return an xml element with custom metadata formatted as string.
618
        Arguments:
619
            vt_id: VT OID. Only used for logging in error case.
620
            custom: Dictionary with the custom metadata.
621
        Return:
622
            Xml element as string.
623
        """
624
625
        _custom = Element('custom')
626
        for key, val in custom.items():
627
            xml_key = SubElement(_custom, key)
628
            try:
629
                xml_key.text = val
630
            except ValueError as e:
631
                logger.warning(
632
                    "Not possible to parse custom tag for VT %s: %s", vt_id, e
633
                )
634
        return tostring(_custom).decode('utf-8')
635
636
    @staticmethod
637
    def get_severities_vt_as_xml_str(vt_id: str, severities: Dict) -> str:
638
        """ Return an xml element with severities as string.
639
        Arguments:
640
            vt_id: VT OID. Only used for logging in error case.
641
            severities: Dictionary with the severities.
642
        Return:
643
            Xml element as string.
644
        """
645
        _severities = Element('severities')
646
        _severity = SubElement(_severities, 'severity')
647
        if 'severity_base_vector' in severities:
648
            try:
649
                _severity.text = severities.get('severity_base_vector')
650
            except ValueError as e:
651
                logger.warning(
652
                    "Not possible to parse severity tag for vt %s: %s", vt_id, e
653
                )
654
        if 'severity_origin' in severities:
655
            _severity.set('origin', severities.get('severity_origin'))
656
        if 'severity_type' in severities:
657
            _severity.set('type', severities.get('severity_type'))
658
659
        return tostring(_severities).decode('utf-8')
660
661
    @staticmethod
662
    def get_params_vt_as_xml_str(vt_id: str, vt_params: Dict) -> str:
663
        """ Return an xml element with params formatted as string.
664
        Arguments:
665
            vt_id: VT OID. Only used for logging in error case.
666
            vt_params: Dictionary with the VT parameters.
667
        Return:
668
            Xml element as string.
669
        """
670
        vt_params_xml = Element('params')
671
        for _pref_id, prefs in vt_params.items():
672
            vt_param = Element('param')
673
            vt_param.set('type', prefs['type'])
674
            vt_param.set('id', _pref_id)
675
            xml_name = SubElement(vt_param, 'name')
676
            try:
677
                xml_name.text = prefs['name']
678
            except ValueError as e:
679
                logger.warning(
680
                    "Not possible to parse parameter for VT %s: %s", vt_id, e
681
                )
682
            if prefs['default']:
683
                xml_def = SubElement(vt_param, 'default')
684
                try:
685
                    xml_def.text = prefs['default']
686
                except ValueError as e:
687
                    logger.warning(
688
                        "Not possible to parse default parameter for VT %s: %s",
689
                        vt_id,
690
                        e,
691
                    )
692
            vt_params_xml.append(vt_param)
693
694
        return tostring(vt_params_xml).decode('utf-8')
695
696
    @staticmethod
697
    def get_refs_vt_as_xml_str(vt_id: str, vt_refs: Dict) -> str:
698
        """ Return an xml element with references formatted as string.
699
        Arguments:
700
            vt_id: VT OID. Only used for logging in error case.
701
            vt_refs: Dictionary with the VT references.
702
        Return:
703
            Xml element as string.
704
        """
705
        vt_refs_xml = Element('refs')
706
        for ref_type, ref_values in vt_refs.items():
707
            for value in ref_values:
708
                vt_ref = Element('ref')
709
                if ref_type == "xref" and value:
710
                    for xref in value.split(', '):
711
                        try:
712
                            _type, _id = xref.split(':', 1)
713
                        except ValueError:
714
                            logger.error(
715
                                'Not possible to parse xref %s for VT %s',
716
                                xref,
717
                                vt_id,
718
                            )
719
                            continue
720
                        vt_ref.set('type', _type.lower())
721
                        vt_ref.set('id', _id)
722
                elif value:
723
                    vt_ref.set('type', ref_type.lower())
724
                    vt_ref.set('id', value)
725
                else:
726
                    continue
727
                vt_refs_xml.append(vt_ref)
728
729
        return tostring(vt_refs_xml).decode('utf-8')
730
731
    @staticmethod
732
    def get_dependencies_vt_as_xml_str(
733
        vt_id: str, vt_dependencies: List
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
734
    ) -> str:
735
        """ Return  an xml element with dependencies as string.
736
        Arguments:
737
            vt_id: VT OID. Only used for logging in error case.
738
            vt_dependencies: List with the VT dependencies.
739
        Return:
740
            Xml element as string.
741
        """
742
        vt_deps_xml = Element('dependencies')
743
        for dep in vt_dependencies:
744
            _vt_dep = Element('dependency')
745
            try:
746
                _vt_dep.set('vt_id', dep)
747
            except (ValueError, TypeError):
748
                logger.error(
749
                    'Not possible to add dependency %s for VT %s', dep, vt_id
750
                )
751
                continue
752
            vt_deps_xml.append(_vt_dep)
753
754
        return tostring(vt_deps_xml).decode('utf-8')
755
756
    @staticmethod
757
    def get_creation_time_vt_as_xml_str(
758
        vt_id: str, vt_creation_time: str
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
759
    ) -> str:
760
        """ Return creation time as string.
761
        Arguments:
762
            vt_id: VT OID. Only used for logging in error case.
763
            vt_creation_time: String with the VT creation time.
764
        Return:
765
           Xml element as string.
766
        """
767
        _time = Element('creation_time')
768
        try:
769
            _time.text = vt_creation_time
770
        except ValueError as e:
771
            logger.warning(
772
                "Not possible to parse creation time for VT %s: %s", vt_id, e
773
            )
774
        return tostring(_time).decode('utf-8')
775
776
    @staticmethod
777
    def get_modification_time_vt_as_xml_str(
778
        vt_id: str, vt_modification_time: str
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
779
    ) -> str:
780
        """ Return modification time as string.
781
        Arguments:
782
            vt_id: VT OID. Only used for logging in error case.
783
            vt_modification_time: String with the VT modification time.
784
        Return:
785
            Xml element as string.
786
        """
787
        _time = Element('modification_time')
788
        try:
789
            _time.text = vt_modification_time
790
        except ValueError as e:
791
            logger.warning(
792
                "Not possible to parse modification time for VT %s: %s",
793
                vt_id,
794
                e,
795
            )
796
        return tostring(_time).decode('utf-8')
797
798
    @staticmethod
799
    def get_summary_vt_as_xml_str(vt_id: str, summary: str) -> str:
800
        """ Return summary as string.
801
        Arguments:
802
            vt_id: VT OID. Only used for logging in error case.
803
            summary: String with a VT summary.
804
        Return:
805
            Xml element as string.
806
        """
807
        _summary = Element('summary')
808
        try:
809
            _summary.text = summary
810
        except ValueError as e:
811
            logger.warning(
812
                "Not possible to parse summary tag for VT %s: %s", vt_id, e
813
            )
814
        return tostring(_summary).decode('utf-8')
815
816
    @staticmethod
817
    def get_impact_vt_as_xml_str(vt_id: str, impact) -> str:
818
        """ Return impact as string.
819
820
        Arguments:
821
            vt_id (str): VT OID. Only used for logging in error case.
822
            impact (str): String which explain the vulneravility impact.
823
        Return:
824
            string: xml element as string.
825
        """
826
        _impact = Element('impact')
827
        try:
828
            _impact.text = impact
829
        except ValueError as e:
830
            logger.warning(
831
                "Not possible to parse impact tag for VT %s: %s", vt_id, e
832
            )
833
        return tostring(_impact).decode('utf-8')
834
835
    @staticmethod
836
    def get_affected_vt_as_xml_str(vt_id: str, affected: str) -> str:
837
        """ Return affected as string.
838
        Arguments:
839
            vt_id: VT OID. Only used for logging in error case.
840
            affected: String which explain what is affected.
841
        Return:
842
            Xml element as string.
843
        """
844
        _affected = Element('affected')
845
        try:
846
            _affected.text = affected
847
        except ValueError as e:
848
            logger.warning(
849
                "Not possible to parse affected tag for VT %s: %s", vt_id, e
850
            )
851
        return tostring(_affected).decode('utf-8')
852
853
    @staticmethod
854
    def get_insight_vt_as_xml_str(vt_id: str, insight: str) -> str:
855
        """ Return insight as string.
856
        Arguments:
857
            vt_id: VT OID. Only used for logging in error case.
858
            insight: String giving an insight of the vulnerability.
859
        Return:
860
            Xml element as string.
861
        """
862
        _insight = Element('insight')
863
        try:
864
            _insight.text = insight
865
        except ValueError as e:
866
            logger.warning(
867
                "Not possible to parse insight tag for VT %s: %s", vt_id, e
868
            )
869
        return tostring(_insight).decode('utf-8')
870
871
    @staticmethod
872
    def get_solution_vt_as_xml_str(
873
        vt_id: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
874
        solution: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
875
        solution_type: Optional[str] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
876
        solution_method: Optional[str] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
877
    ) -> str:
878
        """ Return solution as string.
879
        Arguments:
880
            vt_id: VT OID. Only used for logging in error case.
881
            solution: String giving a possible solution.
882
            solution_type: A solution type
883
            solution_method: A solution method
884
        Return:
885
            Xml element as string.
886
        """
887
        _solution = Element('solution')
888
        try:
889
            _solution.text = solution
890
        except ValueError as e:
891
            logger.warning(
892
                "Not possible to parse solution tag for VT %s: %s", vt_id, e
893
            )
894
        if solution_type:
895
            _solution.set('type', solution_type)
896
        if solution_method:
897
            _solution.set('method', solution_method)
898
        return tostring(_solution).decode('utf-8')
899
900
    @staticmethod
901
    def get_detection_vt_as_xml_str(
902
        vt_id: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
903
        detection: Optional[str] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
904
        qod_type: Optional[str] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
905
        qod: Optional[str] = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
906
    ) -> str:
907
        """ Return detection as string.
908
        Arguments:
909
            vt_id: VT OID. Only used for logging in error case.
910
            detection: String which explain how the vulnerability
911
              was detected.
912
            qod_type: qod type.
913
            qod: qod value.
914
        Return:
915
            Xml element as string.
916
        """
917
        _detection = Element('detection')
918
        if detection:
919
            try:
920
                _detection.text = detection
921
            except ValueError as e:
922
                logger.warning(
923
                    "Not possible to parse detection tag for VT %s: %s",
924
                    vt_id,
925
                    e,
926
                )
927
        if qod_type:
928
            _detection.set('qod_type', qod_type)
929
        elif qod:
930
            _detection.set('qod', qod)
931
932
        return tostring(_detection).decode('utf-8')
933
934
    @property
935
    def is_running_as_root(self) -> bool:
936
        """ Check if it is running as root user."""
937
        if self._is_running_as_root is not None:
938
            return self._is_running_as_root
939
940
        self._is_running_as_root = False
941
        if geteuid() == 0:
942
            self._is_running_as_root = True
943
944
        return self._is_running_as_root
945
946
    @property
947
    def sudo_available(self) -> bool:
948
        """ Checks that sudo is available """
949
        if self._sudo_available is not None:
950
            return self._sudo_available
951
952
        if self.is_running_as_root:
953
            self._sudo_available = False
954
            return self._sudo_available
955
956
        self._sudo_available = Openvas.check_sudo()
957
958
        return self._sudo_available
959
960
    def check(self) -> bool:
961
        """ Checks that openvas command line tool is found and
962
        is executable. """
963
        has_openvas = Openvas.check()
964
        if not has_openvas:
965
            logger.error(
966
                'openvas executable not available. Please install openvas'
967
                ' into your PATH.'
968
            )
969
        return has_openvas
970
971
    def update_progress(self, scan_id: str, current_host: str, msg: str):
972
        """ Calculate percentage and update the scan status of a host
973
        for the progress bar.
974
        Arguments:
975
            scan_id: Scan ID to identify the current scan process.
976
            current_host: Host in the target to be updated.
977
            msg: String with launched and total plugins.
978
        """
979
        try:
980
            launched, total = msg.split('/')
981
        except ValueError:
982
            return
983
        if float(total) == 0:
984
            return
985
        elif float(total) == -1:
986
            host_prog = 100
987
        else:
988
            host_prog = (float(launched) / float(total)) * 100
989
        self.set_scan_host_progress(scan_id, current_host, host_prog)
990
991
    def report_openvas_scan_status(
992
        self, scan_db: ScanDB, scan_id: str, current_host: str
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
993
    ):
994
        """ Get all status entries from redis kb.
995
996
        Arguments:
997
            scan_id: Scan ID to identify the current scan.
998
            current_host: Host to be updated.
999
        """
1000
        res = scan_db.get_scan_status()
1001
        while res:
1002
            self.update_progress(scan_id, current_host, res)
1003
            res = scan_db.get_scan_status()
1004
1005
    def get_severity_score(self, vt_aux: dict) -> Optional[float]:
1006
        """ Return the severity score for the given oid.
1007
        Arguments:
1008
            vt_aux: VT element from which to get the severity vector
1009
        Returns:
1010
            The calculated cvss base value. None if there is no severity
1011
            vector or severity type is not cvss base version 2.
1012
        """
1013
        if vt_aux:
1014
            severity_type = vt_aux['severities'].get('severity_type')
1015
            severity_vector = vt_aux['severities'].get('severity_base_vector')
1016
1017
            if severity_type == "cvss_base_v2" and severity_vector:
1018
                return CVSS.cvss_base_v2_value(severity_vector)
1019
1020
        return None
1021
1022
    def report_openvas_results(
1023
        self, db: BaseDB, scan_id: str, current_host: str
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1024
    ):
1025
        """ Get all result entries from redis kb. """
1026
        res = db.get_result()
1027
        while res:
1028
            msg = res.split('|||')
1029
            roid = msg[3].strip()
1030
            rqod = ''
1031
            rname = ''
1032
            rhostname = msg[1].strip() if msg[1] else ''
1033
            host_is_dead = "Host dead" in msg[4]
1034
            vt_aux = None
1035
1036
            if roid and not host_is_dead:
1037
                vt_aux = copy.deepcopy(self.vts.get(roid))
1038
1039
            if not vt_aux and not host_is_dead:
1040
                logger.warning('Invalid VT oid %s for a result', roid)
1041
1042
            if vt_aux:
1043
                if vt_aux.get('qod_type'):
1044
                    qod_t = vt_aux.get('qod_type')
1045
                    rqod = self.nvti.QOD_TYPES[qod_t]
1046
                elif vt_aux.get('qod'):
1047
                    rqod = vt_aux.get('qod')
1048
1049
                rname = vt_aux.get('name')
1050
1051
            if msg[0] == 'ERRMSG':
1052
                self.add_scan_error(
1053
                    scan_id,
1054
                    host=current_host,
1055
                    hostname=rhostname,
1056
                    name=rname,
1057
                    value=msg[4],
1058
                    port=msg[2],
1059
                    test_id=roid,
1060
                )
1061
1062
            if msg[0] == 'LOG':
1063
                self.add_scan_log(
1064
                    scan_id,
1065
                    host=current_host,
1066
                    hostname=rhostname,
1067
                    name=rname,
1068
                    value=msg[4],
1069
                    port=msg[2],
1070
                    qod=rqod,
1071
                    test_id=roid,
1072
                )
1073
1074
            if msg[0] == 'HOST_DETAIL':
1075
                self.add_scan_host_detail(
1076
                    scan_id,
1077
                    host=current_host,
1078
                    hostname=rhostname,
1079
                    name=rname,
1080
                    value=msg[4],
1081
                )
1082
1083
            if msg[0] == 'ALARM':
1084
                rseverity = self.get_severity_score(vt_aux)
1085
                self.add_scan_alarm(
1086
                    scan_id,
1087
                    host=current_host,
1088
                    hostname=rhostname,
1089
                    name=rname,
1090
                    value=msg[4],
1091
                    port=msg[2],
1092
                    test_id=roid,
1093
                    severity=rseverity,
1094
                    qod=rqod,
1095
                )
1096
1097
            vt_aux = None
1098
            del vt_aux
1099
            res = db.get_result()
1100
1101
    def report_openvas_timestamp_scan_host(
1102
        self, scan_db: ScanDB, scan_id: str, host: str
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1103
    ):
1104
        """ Get start and end timestamp of a host scan from redis kb. """
1105
        timestamp = scan_db.get_host_scan_end_time()
1106
        if timestamp:
1107
            self.add_scan_log(
1108
                scan_id, host=host, name='HOST_END', value=timestamp
1109
            )
1110
            return
1111
1112
        timestamp = scan_db.get_host_scan_start_time()
1113
        if timestamp:
1114
            self.add_scan_log(
1115
                scan_id, host=host, name='HOST_START', value=timestamp
1116
            )
1117
            return
1118
1119
    def is_openvas_process_alive(
1120
        self, kbdb: BaseDB, ovas_pid: str, openvas_scan_id: str
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1121
    ) -> bool:
1122
        parent_exists = True
1123
        parent = None
1124
        try:
1125
            parent = psutil.Process(int(ovas_pid))
1126
        except psutil.NoSuchProcess:
1127
            logger.debug('Process with pid %s already stopped', ovas_pid)
1128
            parent_exists = False
1129
        except TypeError:
1130
            logger.debug(
1131
                'Scan with ID %s never started and stopped unexpectedly',
1132
                openvas_scan_id,
1133
            )
1134
            parent_exists = False
1135
1136
        is_zombie = False
1137
        if parent and parent.status() == psutil.STATUS_ZOMBIE:
1138
            is_zombie = True
1139
1140
        if (not parent_exists or is_zombie) and kbdb:
1141
            if kbdb and kbdb.scan_is_stopped(openvas_scan_id):
1142
                return True
1143
            return False
1144
1145
        return True
1146
1147
    def stop_scan_cleanup(  # pylint: disable=arguments-differ
1148
        self, global_scan_id: str
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1149
    ):
1150
        """ Set a key in redis to indicate the wrapper is stopped.
1151
        It is done through redis because it is a new multiprocess
1152
        instance and it is not possible to reach the variables
1153
        of the grandchild process. Send SIGUSR2 to openvas to stop
1154
        each running scan."""
1155
1156
        openvas_scan_id, kbdb = self.main_db.find_kb_database_by_scan_id(
1157
            global_scan_id
1158
        )
1159
        if kbdb:
1160
            kbdb.stop_scan(openvas_scan_id)
1161
            ovas_pid = kbdb.get_scan_process_id()
1162
1163
            parent = None
1164
            try:
1165
                parent = psutil.Process(int(ovas_pid))
1166
            except psutil.NoSuchProcess:
1167
                logger.debug('Process with pid %s already stopped', ovas_pid)
1168
            except TypeError:
1169
                logger.debug(
1170
                    'Scan with ID %s never started and stopped unexpectedly',
1171
                    openvas_scan_id,
1172
                )
1173
1174
            if parent:
1175
                can_stop_scan = Openvas.stop_scan(
1176
                    openvas_scan_id,
1177
                    not self.is_running_as_root and self.sudo_available,
1178
                )
1179
                if not can_stop_scan:
1180
                    logger.debug(
1181
                        'Not possible to stop scan process: %s.', parent,
1182
                    )
1183
                    return False
1184
1185
                logger.debug('Stopping process: %s', parent)
1186
1187
                while parent:
1188
                    try:
1189
                        parent = psutil.Process(int(ovas_pid))
1190
                    except psutil.NoSuchProcess:
1191
                        parent = None
1192
1193
            for scan_db in kbdb.get_scan_databases():
1194
                self.main_db.release_database(scan_db)
1195
1196
    def exec_scan(self, scan_id: str):
1197
        """ Starts the OpenVAS scanner for scan_id scan. """
1198
        if self.pending_feed:
1199
            logger.info(
1200
                '%s: There is a pending feed update. '
1201
                'The scan can not be started.',
1202
                scan_id,
1203
            )
1204
            self.add_scan_error(
1205
                scan_id,
1206
                name='',
1207
                host='',
1208
                value=(
1209
                    'It was not possible to start the scan,'
1210
                    'because a pending feed update. Please try later'
1211
                ),
1212
            )
1213
            return 2
1214
1215
        do_not_launch = False
1216
1217
        # Set plugins to run.
1218
        # Make a deepcopy of the vts dictionary. Otherwise, consulting the
1219
        # DictProxy object of multiprocessing directly is to expensinve
1220
        # (interprocess communication).
1221
        temp_vts = self.vts.copy()
1222
1223
        kbdb = self.main_db.get_new_kb_database()
1224
        scan_prefs = PreferenceHandler(
1225
            scan_id, kbdb, self.scan_collection, temp_vts
1226
        )
1227
        openvas_scan_id = scan_prefs.prepare_openvas_scan_id_for_openvas()
1228
        scan_prefs.prepare_target_for_openvas()
1229
1230
        if not scan_prefs.prepare_ports_for_openvas():
1231
            self.add_scan_error(
1232
                scan_id, name='', host='', value='No port list defined.'
1233
            )
1234
            do_not_launch = True
1235
1236
        # Set credentials
1237
        if not scan_prefs.prepare_credentials_for_openvas():
1238
            self.add_scan_error(
1239
                scan_id, name='', host='', value='Malformed credential.'
1240
            )
1241
            do_not_launch = True
1242
1243
        if not scan_prefs.prepare_plugins_for_openvas():
1244
            self.add_scan_error(
1245
                scan_id, name='', host='', value='No VTS to run.'
1246
            )
1247
            do_not_launch = True
1248
1249
        temp_vts = None
1250
1251
        scan_prefs.prepare_main_kbindex_for_openvas()
1252
        scan_prefs.prepare_host_options_for_openvas()
1253
        scan_prefs.prepare_scan_params_for_openvas(OSPD_PARAMS)
1254
        scan_prefs.prepare_reverse_lookup_opt_for_openvas()
1255
        scan_prefs.prepare_alive_test_option_for_openvas()
1256
1257
        # Release memory used for scan preferences.
1258
        del scan_prefs
1259
1260
        if do_not_launch:
1261
            self.main_db.release_database(kbdb)
1262
            return 2
1263
1264
        result = Openvas.start_scan(
1265
            openvas_scan_id,
1266
            not self.is_running_as_root and self.sudo_available,
1267
            self._niceness,
1268
        )
1269
1270
        if result is None:
1271
            self.main_db.release_database(kbdb)
1272
            return False
1273
1274
        ovas_pid = result.pid
1275
        kbdb.add_scan_process_id(ovas_pid)
1276
        logger.debug('pid = %s', ovas_pid)
1277
1278
        # Wait until the scanner starts and loads all the preferences.
1279
        while kbdb.get_status(openvas_scan_id) == 'new':
1280
            res = result.poll()
1281
            if res and res < 0:
1282
                self.stop_scan_cleanup(scan_id)
1283
                logger.error(
1284
                    'It was not possible run the task %s, since openvas ended '
1285
                    'unexpectedly with errors during launching.',
1286
                    scan_id,
1287
                )
1288
                return 1
1289
1290
            time.sleep(1)
1291
1292
        no_id_found = False
1293
        while True:
1294
            if not self.is_openvas_process_alive(
1295
                kbdb, ovas_pid, openvas_scan_id
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
1296
            ):
1297
                logger.error(
1298
                    'Task %s was unexpectedly stopped or killed.', scan_id,
1299
                )
1300
                self.add_scan_error(
1301
                    scan_id,
1302
                    name='',
1303
                    host='',
1304
                    value='Task was unexpectedly stopped or killed.',
1305
                )
1306
                kbdb.stop_scan(openvas_scan_id)
1307
                for scan_db in kbdb.get_scan_databases():
1308
                    self.main_db.release_database(scan_db)
1309
                self.main_db.release_database(kbdb)
1310
                return 1
1311
1312
            time.sleep(3)
1313
            # Check if the client stopped the whole scan
1314
            if kbdb.scan_is_stopped(openvas_scan_id):
1315
                # clean main_db
1316
                self.main_db.release_database(kbdb)
1317
                return 1
1318
1319
            self.report_openvas_results(kbdb, scan_id, "")
1320
1321
            for scan_db in kbdb.get_scan_databases():
1322
1323
                id_aux = scan_db.get_scan_id()
1324
                if not id_aux:
1325
                    continue
1326
1327
                if id_aux == openvas_scan_id:
1328
                    no_id_found = False
1329
                    current_host = scan_db.get_host_ip()
1330
1331
                    self.report_openvas_results(scan_db, scan_id, current_host)
1332
                    self.report_openvas_scan_status(
1333
                        scan_db, scan_id, current_host
1334
                    )
1335
                    self.report_openvas_timestamp_scan_host(
1336
                        scan_db, scan_id, current_host
1337
                    )
1338
1339
                    if scan_db.host_is_finished(openvas_scan_id):
1340
                        self.set_scan_host_finished(scan_id, current_host)
1341
                        self.report_openvas_scan_status(
1342
                            scan_db, scan_id, current_host
1343
                        )
1344
                        self.report_openvas_timestamp_scan_host(
1345
                            scan_db, scan_id, current_host
1346
                        )
1347
1348
                        kbdb.remove_scan_database(scan_db)
1349
                        self.main_db.release_database(scan_db)
1350
1351
            # Scan end. No kb in use for this scan id
1352
            if no_id_found and kbdb.target_is_finished(scan_id):
1353
                break
1354
1355
            no_id_found = True
1356
1357
        # Delete keys from KB related to this scan task.
1358
        self.main_db.release_database(kbdb)
1359
1360
1361
def main():
1362
    """ OSP openvas main function. """
1363
    daemon_main('OSPD - openvas', OSPDopenvas)
1364
1365
1366
if __name__ == '__main__':
1367
    main()
1368