Passed
Pull Request — master (#117)
by Juan José
01:13
created

ospd_openvas.daemon.OSPDopenvas.parse_param()   B

Complexity

Conditions 8

Size

Total Lines 22
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 19
nop 1
dl 0
loc 22
rs 7.3333
c 0
b 0
f 0
1
# -*- coding: utf-8 -*-
2
# Copyright (C) 2019 Greenbone Networks GmbH
3
#
4
# SPDX-License-Identifier: GPL-2.0-or-later
5
#
6
# This program is free software; you can redistribute it and/or
7
# modify it under the terms of the GNU General Public License
8
# as published by the Free Software Foundation; either version 2
9
# of the 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 General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
20
# pylint: disable=too-many-lines
21
22
""" Setup for the OSP OpenVAS Server. """
23
24
import logging
25
import subprocess
26
import time
27
import uuid
28
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.main import main as daemon_main
40
from ospd.cvss import CVSS
41
from ospd.vtfilter import VtsFilter
42
43
from ospd_openvas import __version__
44
from ospd_openvas.errors import OspdOpenvasError
45
46
from ospd_openvas.nvticache import NVTICache
47
from ospd_openvas.db import OpenvasDB
48
49
logger = logging.getLogger(__name__)
50
51
OSPD_DESC = """
52
This scanner runs OpenVAS to scan the target hosts.
53
54
OpenVAS (Open Vulnerability Assessment Scanner) is a powerful scanner
55
for vulnerabilities in IT infrastrucutres. The capabilities include
56
unauthenticated scanning as well as authenticated scanning for
57
various types of systems and services.
58
59
For more details about OpenVAS see:
60
http://www.openvas.org/
61
62
The current version of ospd-openvas is a simple frame, which sends
63
the server parameters to the Greenbone Vulnerability Manager daemon (GVMd) and
64
checks the existence of OpenVAS binary. But it can not run scans yet.
65
"""
66
67
OSPD_PARAMS = {
68
    'auto_enable_dependencies': {
69
        'type': 'boolean',
70
        'name': 'auto_enable_dependencies',
71
        'default': 1,
72
        'mandatory': 1,
73
        'description': 'Automatically enable the plugins that are depended on',
74
    },
75
    'cgi_path': {
76
        'type': 'string',
77
        'name': 'cgi_path',
78
        'default': '/cgi-bin:/scripts',
79
        'mandatory': 1,
80
        'description': 'Look for default CGIs in /cgi-bin and /scripts',
81
    },
82
    'checks_read_timeout': {
83
        'type': 'integer',
84
        'name': 'checks_read_timeout',
85
        'default': 5,
86
        'mandatory': 1,
87
        'description': (
88
            'Number  of seconds that the security checks will '
89
            + 'wait for when doing a recv()'
90
        ),
91
    },
92
    'drop_privileges': {
93
        'type': 'boolean',
94
        'name': 'drop_privileges',
95
        'default': 0,
96
        'mandatory': 1,
97
        'description': '',
98
    },
99
    'network_scan': {
100
        'type': 'boolean',
101
        'name': 'network_scan',
102
        'default': 0,
103
        'mandatory': 1,
104
        'description': '',
105
    },
106
    'non_simult_ports': {
107
        'type': 'string',
108
        'name': 'non_simult_ports',
109
        'default': '139, 445, 3389, Services/irc',
110
        'mandatory': 1,
111
        'description': (
112
            'Prevent to make two connections on the same given '
113
            + 'ports at the same time.'
114
        ),
115
    },
116
    'open_sock_max_attempts': {
117
        'type': 'integer',
118
        'name': 'open_sock_max_attempts',
119
        'default': 5,
120
        'mandatory': 0,
121
        'description': (
122
            'Number of unsuccessful retries to open the socket '
123
            + 'before to set the port as closed.'
124
        ),
125
    },
126
    'timeout_retry': {
127
        'type': 'integer',
128
        'name': 'timeout_retry',
129
        'default': 5,
130
        'mandatory': 0,
131
        'description': (
132
            'Number of retries when a socket connection attempt ' + 'timesout.'
133
        ),
134
    },
135
    'optimize_test': {
136
        'type': 'integer',
137
        'name': 'optimize_test',
138
        'default': 5,
139
        'mandatory': 0,
140
        'description': (
141
            'By default, openvas does not trust the remote ' + 'host banners.'
142
        ),
143
    },
144
    'plugins_timeout': {
145
        'type': 'integer',
146
        'name': 'plugins_timeout',
147
        'default': 5,
148
        'mandatory': 0,
149
        'description': 'This is the maximum lifetime, in seconds of a plugin.',
150
    },
151
    'report_host_details': {
152
        'type': 'boolean',
153
        'name': 'report_host_details',
154
        'default': 1,
155
        'mandatory': 1,
156
        'description': '',
157
    },
158
    'safe_checks': {
159
        'type': 'boolean',
160
        'name': 'safe_checks',
161
        'default': 1,
162
        'mandatory': 1,
163
        'description': (
164
            'Disable the plugins with potential to crash '
165
            + 'the remote services'
166
        ),
167
    },
168
    'scanner_plugins_timeout': {
169
        'type': 'integer',
170
        'name': 'scanner_plugins_timeout',
171
        'default': 36000,
172
        'mandatory': 1,
173
        'description': 'Like plugins_timeout, but for ACT_SCANNER plugins.',
174
    },
175
    'time_between_request': {
176
        'type': 'integer',
177
        'name': 'time_between_request',
178
        'default': 0,
179
        'mandatory': 0,
180
        'description': (
181
            'Allow to set a wait time between two actions '
182
            + '(open, send, close).'
183
        ),
184
    },
185
    'unscanned_closed': {
186
        'type': 'boolean',
187
        'name': 'unscanned_closed',
188
        'default': 1,
189
        'mandatory': 1,
190
        'description': '',
191
    },
192
    'unscanned_closed_udp': {
193
        'type': 'boolean',
194
        'name': 'unscanned_closed_udp',
195
        'default': 1,
196
        'mandatory': 1,
197
        'description': '',
198
    },
199
    'use_mac_addr': {
200
        'type': 'boolean',
201
        'name': 'use_mac_addr',
202
        'default': 0,
203
        'mandatory': 0,
204
        'description': 'To test the local network. '
205
        + 'Hosts will be referred to by their MAC address.',
206
    },
207
    'vhosts': {
208
        'type': 'string',
209
        'name': 'vhosts',
210
        'default': '',
211
        'mandatory': 0,
212
        'description': '',
213
    },
214
    'vhosts_ip': {
215
        'type': 'string',
216
        'name': 'vhosts_ip',
217
        'default': '',
218
        'mandatory': 0,
219
        'description': '',
220
    },
221
}
222
223
OID_SSH_AUTH = "1.3.6.1.4.1.25623.1.0.103591"
224
OID_SMB_AUTH = "1.3.6.1.4.1.25623.1.0.90023"
225
OID_ESXI_AUTH = "1.3.6.1.4.1.25623.1.0.105058"
226
OID_SNMP_AUTH = "1.3.6.1.4.1.25623.1.0.105076"
227
228
229
def _from_bool_to_str(value):
230
    """ The OpenVAS scanner use yes and no as boolean values, whereas ospd
231
    uses 1 and 0."""
232
    return 'yes' if value == 1 else 'no'
233
234
235
class OpenVasVtsFilter(VtsFilter):
236
    """ Methods to overwrite the ones in the original class.
237
    Each method formats the value to be compatible with the filter
238
    """
239
240
    def format_vt_modification_time(self, value):
241
        """ Convert the string seconds since epoch into a 19 character
242
        string representing YearMonthDayHourMinuteSecond,
243
        e.g. 20190319122532. This always refers to UTC.
244
        """
245
246
        return datetime.utcfromtimestamp(int(value)).strftime("%Y%m%d%H%M%S")
247
248
249
class OSPDopenvas(OSPDaemon):
250
251
    """ Class for ospd-openvas daemon. """
252
253
    def __init__(self, *, niceness=None, **kwargs):
254
        """ Initializes the ospd-openvas daemon's internal data. """
255
256
        super().__init__(customvtfilter=OpenVasVtsFilter())
257
258
        self.server_version = __version__
259
260
        self._niceness = str(niceness)
261
262
        self.scanner_info['name'] = 'openvas'
263
        self.scanner_info['version'] = ''  # achieved during self.check()
264
        self.scanner_info['description'] = OSPD_DESC
265
266
        for name, param in OSPD_PARAMS.items():
267
            self.add_scanner_param(name, param)
268
269
        self._sudo_available = None
270
        self._is_running_as_root = None
271
272
        self.scan_only_params = dict()
273
274
        self.main_kbindex = None
275
276
        self.openvas_db = OpenvasDB()
277
278
        self.nvti = NVTICache(self.openvas_db)
279
280
        self.pending_feed = None
281
282
    def init(self):
283
        self.openvas_db.db_init()
284
285
        ctx = self.openvas_db.db_find(self.nvti.NVTICACHE_STR)
286
287
        if not ctx:
288
            self.redis_nvticache_init()
289
            ctx = self.openvas_db.db_find(self.nvti.NVTICACHE_STR)
290
291
        self.openvas_db.set_redisctx(ctx)
292
293
        self.load_vts()
294
295
    def parse_param(self):
296
        """ Set OSPD_PARAMS with the params taken from the openvas_scanner. """
297
        bool_dict = {'no': 0, 'yes': 1}
298
299
        result = subprocess.check_output(
300
            ['openvas', '-s'], stderr=subprocess.STDOUT
301
        )
302
        result = result.decode('ascii')
303
        param_list = dict()
304
        for conf in result.split('\n'):
305
            elem = conf.split('=')
306
            if len(elem) == 2:
307
                value = str.strip(elem[1])
308
                if str.strip(elem[1]) in bool_dict:
309
                    value = bool_dict[value]
310
                param_list[str.strip(elem[0])] = value
311
        for elem in OSPD_PARAMS:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable OSPD_PARAMS does not seem to be defined.
Loading history...
312
            if elem in param_list:
313
                OSPD_PARAMS[elem]['default'] = param_list[elem]
314
        for elem in param_list:
315
            if elem not in OSPD_PARAMS:
316
                self.scan_only_params[elem] = param_list[elem]
317
318
    def redis_nvticache_init(self):
319
        """ Loads NVT's metadata into Redis DB. """
320
        try:
321
            logger.debug('Loading NVTs in Redis DB')
322
            subprocess.check_call(['openvas', '--update-vt-info'])
323
        except subprocess.CalledProcessError as err:
324
            logger.error('OpenVAS Scanner failed to load NVTs. %s', err)
325
326
    def feed_is_outdated(self, current_feed):
327
        """ Compare the current feed with the one in the disk.
328
329
        Return:
330
            False if there is no new feed.
331
            True if the feed version in disk is newer than the feed in
332
            redis cache.
333
            None if there is no feed
334
            the disk.
335
        """
336
        plugins_folder = self.scan_only_params.get('plugins_folder')
337
        if not plugins_folder:
338
            raise OspdOpenvasError("Error: Path to plugins folder not found.")
339
340
        feed_info_file = Path(plugins_folder) / 'plugin_feed_info.inc'
341
        if not feed_info_file.exists():
342
            self.parse_param()
343
            msg = 'Plugins feed file %s not found.' % feed_info_file
344
            logger.debug(msg)
345
            return None
346
347
        date = 0
348
        with open(str(feed_info_file)) as fcontent:
349
            for line in fcontent:
350
                if "PLUGIN_SET" in line:
351
                    date = line.split(' = ')[1]
352
                    date = date.replace(';', '')
353
                    date = date.replace('"', '')
354
        if int(current_feed) < int(date) or int(date) == 0:
355
            return True
356
        return False
357
358
    def check_feed(self):
359
        """ Check if there is a feed update. Wait until all the running
360
        scans finished. Set a flag to anounce there is a pending feed update,
361
        which avoid to start a new scan.
362
        """
363
        current_feed = self.nvti.get_feed_version()
364
        # Check if the feed is already accessible in the disk.
365
        if self.feed_is_outdated(current_feed) is None:
366
            self.pending_feed = True
367
            return
368
369
        # Check if the nvticache in redis is outdated
370
        if not current_feed or self.feed_is_outdated(current_feed):
371
            self.redis_nvticache_init()
372
            ctx = self.openvas_db.db_find(self.nvti.NVTICACHE_STR)
373
            self.openvas_db.set_redisctx(ctx)
374
            self.pending_feed = True
375
376
        _running_scan = False
377
        for scan_id in self.scan_processes:
378
            if self.scan_processes[scan_id].is_alive():
379
                _running_scan = True
380
381
        # Check if the NVT dict is outdated
382
        if self.pending_feed:
383
            _pending_feed = True
384
        else:
385
            _pending_feed = (
386
                self.get_vts_version() != self.nvti.get_feed_version()
387
            )
388
389
        if _running_scan and _pending_feed:
390
            if not self.pending_feed:
391
                self.pending_feed = True
392
                logger.debug(
393
                    'There is a running scan. Therefore the feed '
394
                    'update will be performed later.'
395
                )
396
        elif not _running_scan and _pending_feed:
397
            self.vts = dict()
398
            self.load_vts()
399
400
    def scheduler(self):
401
        """This method is called periodically to run tasks."""
402
        self.check_feed()
403
404
    def load_vts(self):
405
        """ Load the NVT's metadata into the vts
406
        global  dictionary. """
407
        logger.debug('Loading vts in memory.')
408
        oids = dict(self.nvti.get_oids())
409
        for _filename, vt_id in oids.items():
410
            _vt_params = self.nvti.get_nvt_params(vt_id)
411
            _vt_refs = self.nvti.get_nvt_refs(vt_id)
412
            _custom = self.nvti.get_nvt_metadata(vt_id)
413
            _name = _custom.pop('name')
414
            _vt_creation_time = _custom.pop('creation_date')
415
            _vt_modification_time = _custom.pop('last_modification')
416
417
            _summary = None
418
            _impact = None
419
            _affected = None
420
            _insight = None
421
            _solution = None
422
            _solution_t = None
423
            _vuldetect = None
424
            _qod_t = None
425
            _qod_v = None
426
427
            if 'summary' in _custom:
428
                _summary = _custom.pop('summary')
429
            if 'impact' in _custom:
430
                _impact = _custom.pop('impact')
431
            if 'affected' in _custom:
432
                _affected = _custom.pop('affected')
433
            if 'insight' in _custom:
434
                _insight = _custom.pop('insight')
435
            if 'solution' in _custom:
436
                _solution = _custom.pop('solution')
437
                if 'solution_type' in _custom:
438
                    _solution_t = _custom.pop('solution_type')
439
440
            if 'vuldetect' in _custom:
441
                _vuldetect = _custom.pop('vuldetect')
442
            if 'qod_type' in _custom:
443
                _qod_t = _custom.pop('qod_type')
444
            elif 'qod' in _custom:
445
                _qod_v = _custom.pop('qod')
446
447
            _severity = dict()
448
            if 'severity_base_vector' in _custom:
449
                _severity_vector = _custom.pop('severity_base_vector')
450
            else:
451
                _severity_vector = _custom.pop('cvss_base_vector')
452
            _severity['severity_base_vector'] = _severity_vector
453
            if 'severity_type' in _custom:
454
                _severity_type = _custom.pop('severity_type')
455
            else:
456
                _severity_type = 'cvss_base_v2'
457
            _severity['severity_type'] = _severity_type
458
            if 'severity_origin' in _custom:
459
                _severity['severity_origin'] = _custom.pop('severity_origin')
460
461
            _vt_dependencies = list()
462
            if 'dependencies' in _custom:
463
                _deps = _custom.pop('dependencies')
464
                _deps_list = _deps.split(', ')
465
                for dep in _deps_list:
466
                    _vt_dependencies.append(oids.get('filename:' + dep))
467
468
            try:
469
                self.add_vt(
470
                    vt_id,
471
                    name=_name,
472
                    vt_params=_vt_params,
473
                    vt_refs=_vt_refs,
474
                    custom=_custom,
475
                    vt_creation_time=_vt_creation_time,
476
                    vt_modification_time=_vt_modification_time,
477
                    vt_dependencies=_vt_dependencies,
478
                    summary=_summary,
479
                    impact=_impact,
480
                    affected=_affected,
481
                    insight=_insight,
482
                    solution=_solution,
483
                    solution_t=_solution_t,
484
                    detection=_vuldetect,
485
                    qod_t=_qod_t,
486
                    qod_v=_qod_v,
487
                    severities=_severity,
488
                )
489
            except OspdError as e:
490
                logger.info("Error while adding vt. %s", e)
491
492
        _feed_version = self.nvti.get_feed_version()
493
        self.set_vts_version(vts_version=_feed_version)
494
        self.pending_feed = False
495
        logger.debug('Finish loading up vts.')
496
497
    @staticmethod
498
    def get_custom_vt_as_xml_str(vt_id, custom):
499
        """ Return an xml element with custom metadata formatted as string.
500
        Arguments:
501
            vt_id (str): VT OID. Only used for logging in error case.
502
            custom (dict): Dictionary with the custom metadata.
503
        Return:
504
            string: xml element as string.
505
        """
506
507
        _custom = Element('custom')
508
        for key, val in custom.items():
509
            xml_key = SubElement(_custom, key)
510
            xml_key.text = val
511
512
        return tostring(_custom).decode('utf-8')
513
514
    @staticmethod
515
    def get_severities_vt_as_xml_str(vt_id, severities):
516
        """ Return an xml element with severities as string.
517
        Arguments:
518
            vt_id (str): VT OID. Only used for logging in error case.
519
            severities (dict): Dictionary with the severities.
520
        Return:
521
            string: xml element as string.
522
        """
523
        _severities = Element('severities')
524
        _severity = SubElement(_severities, 'severity')
525
        if 'severity_base_vector' in severities:
526
            _severity.text = severities.get('severity_base_vector')
527
        if 'severity_origin' in severities:
528
            _severity.set('origin', severities.get('severity_origin'))
529
        if 'severity_type' in severities:
530
            _severity.set('type', severities.get('severity_type'))
531
532
        return tostring(_severities).decode('utf-8')
533
534
    @staticmethod
535
    def get_params_vt_as_xml_str(vt_id, vt_params):
536
        """ Return an xml element with params formatted as string.
537
        Arguments:
538
            vt_id (str): VT OID. Only used for logging in error case.
539
            vt_params (dict): Dictionary with the VT parameters.
540
        Return:
541
            string: xml element as string.
542
        """
543
        vt_params_xml = Element('params')
544
        for _pref_id, prefs in vt_params.items():
545
            vt_param = Element('param')
546
            vt_param.set('type', prefs['type'])
547
            vt_param.set('id', _pref_id)
548
            xml_name = SubElement(vt_param, 'name')
549
            xml_name.text = prefs['name']
550
            if prefs['default']:
551
                xml_def = SubElement(vt_param, 'default')
552
                xml_def.text = prefs['default']
553
            vt_params_xml.append(vt_param)
554
555
        return tostring(vt_params_xml).decode('utf-8')
556
557
    @staticmethod
558
    def get_refs_vt_as_xml_str(vt_id, vt_refs):
559
        """ Return an xml element with references formatted as string.
560
        Arguments:
561
            vt_id (str): VT OID. Only used for logging in error case.
562
            vt_refs (dict): Dictionary with the VT references.
563
        Return:
564
            string: xml element as string.
565
        """
566
        vt_refs_xml = Element('refs')
567
        for ref_type, ref_values in vt_refs.items():
568
            for value in ref_values:
569
                vt_ref = Element('ref')
570
                if ref_type == "xref" and value:
571
                    for xref in value.split(', '):
572
                        try:
573
                            _type, _id = xref.split(':', 1)
574
                        except ValueError:
575
                            logger.error(
576
                                'Not possible to parse xref %s for vt %s',
577
                                xref,
578
                                vt_id,
579
                            )
580
                            continue
581
                        vt_ref.set('type', _type.lower())
582
                        vt_ref.set('id', _id)
583
                elif value:
584
                    vt_ref.set('type', ref_type.lower())
585
                    vt_ref.set('id', value)
586
                else:
587
                    continue
588
                vt_refs_xml.append(vt_ref)
589
590
        return tostring(vt_refs_xml).decode('utf-8')
591
592
    @staticmethod
593
    def get_dependencies_vt_as_xml_str(
594
        vt_id, dep_list
595
    ):  # pylint: disable=arguments-differ
596
        """ Return  an xml element with dependencies as string.
597
        Arguments:
598
            vt_id (str): VT OID. Only used for logging in error case.
599
            dep_list (List): List with the VT dependencies.
600
        Return:
601
            string: xml element as string.
602
        """
603
        vt_deps_xml = Element('dependencies')
604
        for dep in dep_list:
605
            _vt_dep = Element('dependency')
606
            try:
607
                _vt_dep.set('vt_id', dep)
608
            except TypeError:
609
                logger.error(
610
                    'Not possible to add dependency %s for vt %s', dep, vt_id
611
                )
612
                continue
613
            vt_deps_xml.append(_vt_dep)
614
615
        return tostring(vt_deps_xml).decode('utf-8')
616
617
    @staticmethod
618
    def get_creation_time_vt_as_xml_str(
619
        vt_id, creation_time
620
    ):  # pylint: disable=arguments-differ
621
        """ Return creation time as string.
622
        Arguments:
623
            vt_id (str): VT OID. Only used for logging in error case.
624
            creation_time (str): String with the VT creation time.
625
        Return:
626
            string: xml element as string.
627
        """
628
        _time = Element('creation_time')
629
        _time.text = creation_time
630
        return tostring(_time).decode('utf-8')
631
632
    @staticmethod
633
    def get_modification_time_vt_as_xml_str(
634
        vt_id, modification_time
635
    ):  # pylint: disable=arguments-differ
636
        """ Return modification time as string.
637
        Arguments:
638
            vt_id (str): VT OID. Only used for logging in error case.
639
            modification_time (str): String with the VT modification time.
640
        Return:
641
            string: xml element as string.
642
        """
643
        _time = Element('modification_time')
644
        _time.text = modification_time
645
        return tostring(_time).decode('utf-8')
646
647
    @staticmethod
648
    def get_summary_vt_as_xml_str(vt_id, summary):
649
        """ Return summary as string.
650
        Arguments:
651
            vt_id (str): VT OID. Only used for logging in error case.
652
            summary (str): String with a VT summary.
653
        Return:
654
            string: xml element as string.
655
        """
656
        _summary = Element('summary')
657
        _summary.text = summary
658
        return tostring(_summary).decode('utf-8')
659
660
    @staticmethod
661
    def get_impact_vt_as_xml_str(vt_id, impact):
662
        """ Return impact as string.
663
664
        Arguments:
665
            vt_id (str): VT OID. Only used for logging in error case.
666
            impact (str): String which explain the vulneravility impact.
667
        Return:
668
            string: xml element as string.
669
        """
670
        _impact = Element('impact')
671
        _impact.text = impact
672
        return tostring(_impact).decode('utf-8')
673
674
    @staticmethod
675
    def get_affected_vt_as_xml_str(vt_id, affected):
676
        """ Return affected as string.
677
        Arguments:
678
            vt_id (str): VT OID. Only used for logging in error case.
679
            affected (str): String which explain what is affected.
680
        Return:
681
            string: xml element as string.
682
        """
683
        _affected = Element('affected')
684
        _affected.text = affected
685
        return tostring(_affected).decode('utf-8')
686
687
    @staticmethod
688
    def get_insight_vt_as_xml_str(vt_id, insight):
689
        """ Return insight as string.
690
        Arguments:
691
            vt_id (str): VT OID. Only used for logging in error case.
692
            insight (str): String giving an insight of the vulnerability.
693
        Return:
694
            string: xml element as string.
695
        """
696
        _insight = Element('insight')
697
        _insight.text = insight
698
        return tostring(_insight).decode('utf-8')
699
700
    @staticmethod
701
    def get_solution_vt_as_xml_str(vt_id, solution, solution_type=None):
702
        """ Return solution as string.
703
        Arguments:
704
            vt_id (str): VT OID. Only used for logging in error case.
705
            solution (str): String giving a possible solution.
706
            solution_type (str): A solution type
707
        Return:
708
            string: xml element as string.
709
        """
710
        _solution = Element('solution')
711
        _solution.text = solution
712
        if solution_type:
713
            _solution.set('type', solution_type)
714
        return tostring(_solution).decode('utf-8')
715
716
    @staticmethod
717
    def get_detection_vt_as_xml_str(
718
        vt_id, vuldetect=None, qod_type=None, qod=None
719
    ):  # pylint: disable=arguments-differ
720
        """ Return detection as string.
721
        Arguments:
722
            vt_id (str): VT OID. Only used for logging in error case.
723
            vuldetect (str, opt): String which explain how the vulnerability
724
                was detected.
725
            qod_type (str, opt): qod type.
726
            qod (str, opt): qod value.
727
        Return:
728
            string: xml element as string.
729
        """
730
        _detection = Element('detection')
731
        if vuldetect:
732
            _detection.text = vuldetect
733
        if qod_type:
734
            _detection.set('qod_type', qod_type)
735
        elif qod:
736
            _detection.set('qod', qod)
737
738
        return tostring(_detection).decode('utf-8')
739
740
    @property
741
    def is_running_as_root(self):
742
        """ Check if it is running as root user."""
743
        if self._is_running_as_root is not None:
744
            return self._is_running_as_root
745
746
        self._is_running_as_root = False
747
        if geteuid() == 0:
748
            self._is_running_as_root = True
749
750
        return self._is_running_as_root
751
752
    @property
753
    def sudo_available(self):
754
        """ Checks that sudo is available """
755
        if self._sudo_available is not None:
756
            return self._sudo_available
757
758
        if self.is_running_as_root:
759
            self._sudo_available = False
760
            return self._sudo_available
761
762
        try:
763
            subprocess.check_call(
764
                ['sudo', '-n', 'openvas', '-s'], stdout=subprocess.PIPE
765
            )
766
            self._sudo_available = True
767
        except (subprocess.SubprocessError, OSError) as e:
768
            logger.debug(
769
                'It was not possible to call openvas with sudo. '
770
                'The scanner will run as non-root user. Reason %s',
771
                e,
772
            )
773
            self._sudo_available = False
774
775
        return self._sudo_available
776
777
    def check(self):
778
        """ Checks that openvas command line tool is found and
779
        is executable. """
780
        try:
781
            result = subprocess.check_output(
782
                ['openvas', '-V'], stderr=subprocess.STDOUT
783
            )
784
            result = result.decode('ascii')
785
        except OSError:
786
            # The command is not available
787
            return False
788
789
        if result is None:
790
            return False
791
792
        version = result.split('\n')
793
        if version[0].find('OpenVAS') < 0:
794
            return False
795
796
        self.parse_param()
797
        self.scanner_info['version'] = version[0]
798
799
        return True
800
801
    def update_progress(self, scan_id, target, current_host, msg):
802
        """ Calculate percentage and update the scan status of a host
803
        for the progress bar.
804
        Arguments:
805
            scan_id (uuid): Scan ID to identify the current scan process.
806
            target (str): Target to be updated with the calculated
807
                          scan progress.
808
            msg (str): String with launched and total plugins.
809
        """
810
        try:
811
            launched, total = msg.split('/')
812
        except ValueError:
813
            return
814
        if float(total) == 0:
815
            return
816
        elif float(total) == -1:
817
            host_prog = 100
818
        else:
819
            host_prog = (float(launched) / float(total)) * 100
820
        self.set_scan_host_progress(scan_id, target, current_host, host_prog)
821
822
    def get_openvas_status(self, scan_id, target, current_host):
823
        """ Get all status entries from redis kb.
824
        Arguments:
825
            scan_id (uuid): Scan ID to identify the current scan.
826
            target (str): Target progress to be updated.
827
        """
828
        res = self.openvas_db.get_status()
829
        while res:
830
            self.update_progress(scan_id, target, current_host, res)
831
            res = self.openvas_db.get_status()
832
833
    def get_severity_score(self, oid):
834
        """ Return the severity score for the given oid.
835
        Arguments:
836
            oid (str): VT OID from which to get the severity vector
837
        Returns:
838
            The calculated cvss base value. None if there is no severity
839
            vector or severity type is not cvss base version 2.
840
        """
841
        severity_type = self.vts[oid]['severities'].get('severity_type')
842
        severity_vector = self.vts[oid]['severities'].get(
843
            'severity_base_vector'
844
        )
845
846
        if severity_type == "cvss_base_v2" and severity_vector:
847
            return CVSS.cvss_base_v2_value(severity_vector)
848
849
        return None
850
851
    def get_openvas_result(self, scan_id, current_host):
852
        """ Get all result entries from redis kb. """
853
        res = self.openvas_db.get_result()
854
        while res:
855
            msg = res.split('|||')
856
            roid = msg[3]
857
            rqod = ''
858
            rname = ''
859
            rhostname = msg[1] if msg[1] else ''
860
            host_is_dead = "Host dead" in msg[4]
861
862
            if not host_is_dead:
863
                if self.vts[roid].get('qod_type'):
864
                    qod_t = self.vts[roid].get('qod_type')
865
                    rqod = self.nvti.QOD_TYPES[qod_t]
866
                elif self.vts[roid].get('qod'):
867
                    rqod = self.vts[roid].get('qod')
868
869
                rname = self.vts[roid].get('name')
870
871
            if msg[0] == 'ERRMSG':
872
                self.add_scan_error(
873
                    scan_id,
874
                    host=current_host,
875
                    hostname=rhostname,
876
                    name=rname,
877
                    value=msg[4],
878
                    port=msg[2],
879
                )
880
881
            if msg[0] == 'LOG':
882
                self.add_scan_log(
883
                    scan_id,
884
                    host=current_host,
885
                    hostname=rhostname,
886
                    name=rname,
887
                    value=msg[4],
888
                    port=msg[2],
889
                    qod=rqod,
890
                    test_id=roid,
891
                )
892
893
            if msg[0] == 'HOST_DETAIL':
894
                self.add_scan_host_detail(
895
                    scan_id,
896
                    host=current_host,
897
                    hostname=rhostname,
898
                    name=rname,
899
                    value=msg[4],
900
                )
901
902
            if msg[0] == 'ALARM':
903
                rseverity = self.get_severity_score(roid)
904
                self.add_scan_alarm(
905
                    scan_id,
906
                    host=current_host,
907
                    hostname=rhostname,
908
                    name=rname,
909
                    value=msg[4],
910
                    port=msg[2],
911
                    test_id=roid,
912
                    severity=rseverity,
913
                    qod=rqod,
914
                )
915
916
            res = self.openvas_db.get_result()
917
918
    def get_openvas_timestamp_scan_host(self, scan_id, target):
919
        """ Get start and end timestamp of a host scan from redis kb. """
920
        timestamp = self.openvas_db.get_host_scan_scan_end_time()
921
        if timestamp:
922
            self.add_scan_log(
923
                scan_id, host=target, name='HOST_END', value=timestamp
924
            )
925
            return
926
        timestamp = self.openvas_db.get_host_scan_scan_start_time()
927
        if timestamp:
928
            self.add_scan_log(
929
                scan_id, host=target, name='HOST_START', value=timestamp
930
            )
931
            return
932
933
    def host_is_finished(self, scan_id):
934
        """ Check if the host has finished. """
935
        status = self.openvas_db.get_single_item('internal/%s' % scan_id)
936
        return status == 'finished'
937
938
    def target_is_finished(self, scan_id):
939
        """ Check if a target has finished. The scan id to be used is
940
        the scan id passed to the openvas, is not the global scan id."""
941
        ctx = self.openvas_db.kb_connect(dbnum=self.main_kbindex)
942
        scan_id = self.openvas_db.get_single_item(
943
            'internal/%s/globalscanid' % scan_id, ctx=ctx
944
        )
945
        status = self.openvas_db.get_single_item(
946
            'internal/%s' % scan_id, ctx=ctx
947
        )
948
949
        return status == 'finished' or status is None
950
951
    def scan_is_stopped(self, scan_id):
952
        """ Check if the parent process has received the stop_scan order.
953
        @in scan_id: ID to identify the scan to be stopped.
954
        @return 1 if yes, None in other case.
955
        """
956
        ctx = self.openvas_db.kb_connect(dbnum=self.main_kbindex)
957
        self.openvas_db.set_redisctx(ctx)
958
        status = self.openvas_db.get_single_item('internal/%s' % scan_id)
959
        return status == 'stop_all'
960
961
    def stop_scan_cleanup(
962
        self, global_scan_id
963
    ):  # pylint: disable=arguments-differ
964
        """ Set a key in redis to indicate the wrapper is stopped.
965
        It is done through redis because it is a new multiprocess
966
        instance and it is not possible to reach the variables
967
        of the grandchild process. Send SIGUSR2 to openvas to stop
968
        each running scan."""
969
        ctx = self.openvas_db.kb_connect()
970
        for current_kbi in range(0, self.openvas_db.max_dbindex):
971
            self.openvas_db.select_kb(ctx, str(current_kbi), set_global=True)
972
            scan_id = self.openvas_db.get_single_item(
973
                'internal/%s/globalscanid' % global_scan_id
974
            )
975
            if scan_id:
976
                self.openvas_db.set_single_item(
977
                    'internal/%s' % scan_id, ['stop_all']
978
                )
979
                ovas_pid = self.openvas_db.get_single_item('internal/ovas_pid')
980
                parent = None
981
                try:
982
                    parent = psutil.Process(int(ovas_pid))
983
                except psutil.NoSuchProcess:
984
                    logger.debug(
985
                        'Process with pid %s already stopped', ovas_pid
986
                    )
987
                except TypeError:
988
                    logger.debug(
989
                        'Scan with ID %s never started and stopped '
990
                        'unexpectedly',
991
                        scan_id,
992
                    )
993
994
                if parent:
995
                    cmd = ['openvas', '--scan-stop', scan_id]
996
                    if not self.is_running_as_root and self.sudo_available:
997
                        cmd = ['sudo', '-n'] + cmd
998
999
                    try:
1000
                        subprocess.Popen(cmd, shell=False)
1001
                    except OSError as e:
1002
                        # the command is not available
1003
                        logger.debug(
1004
                            'Not possible to Stopping process: %s.' 'Reason %s',
1005
                            parent,
1006
                            e,
1007
                        )
1008
                        return False
1009
1010
                    logger.debug('Stopping process: %s', parent)
1011
                    while parent:
1012
                        try:
1013
                            parent = psutil.Process(int(ovas_pid))
1014
                        except psutil.NoSuchProcess:
1015
                            parent = None
1016
1017
                self.openvas_db.release_db(current_kbi)
1018
1019
    def get_vts_in_groups(self, filters):
1020
        """ Return a list of vts which match with the given filter.
1021
1022
        @input filters A list of filters. Each filter has key, operator and
1023
                       a value. They are separated by a space.
1024
                       Supported keys: family
1025
        @return Return a list of vts which match with the given filter.
1026
        """
1027
        vts_list = list()
1028
        families = dict()
1029
        for oid in self.vts:
1030
            family = self.vts[oid]['custom'].get('family')
1031
            if family not in families:
1032
                families[family] = list()
1033
            families[family].append(oid)
1034
1035
        for elem in filters:
1036
            key, value = elem.split('=')
1037
            if key == 'family' and value in families:
1038
                vts_list.extend(families[value])
1039
        return vts_list
1040
1041
    def get_vt_param_type(self, vtid, vt_param_id):
1042
        """ Return the type of the vt parameter from the vts dictionary. """
1043
1044
        vt_params_list = self.vts[vtid].get("vt_params")
1045
        if vt_params_list.get(vt_param_id):
1046
            return vt_params_list[vt_param_id]["type"]
1047
        return None
1048
1049
    def get_vt_param_name(self, vtid, vt_param_id):
1050
        """ Return the type of the vt parameter from the vts dictionary. """
1051
1052
        vt_params_list = self.vts[vtid].get("vt_params")
1053
        if vt_params_list.get(vt_param_id):
1054
            return vt_params_list[vt_param_id]["name"]
1055
        return None
1056
1057
    @staticmethod
1058
    def check_param_type(vt_param_value, param_type):
1059
        """ Check if the value of a vt parameter matches with
1060
        the type founded.
1061
        """
1062
        if param_type in [
1063
            'entry',
1064
            'file',
1065
            'password',
1066
            'radio',
1067
            'sshlogin',
1068
        ] and isinstance(vt_param_value, str):
1069
            return None
1070
        elif param_type == 'checkbox' and (
1071
            vt_param_value == '0' or vt_param_value == '1'
1072
        ):
1073
            return None
1074
        elif param_type == 'integer':
1075
            try:
1076
                int(vt_param_value)
1077
            except ValueError:
1078
                return 1
1079
            return None
1080
1081
        return 1
1082
1083
    def process_vts(self, vts):
1084
        """ Add single VTs and their parameters. """
1085
        vts_list = []
1086
        vts_params = []
1087
        vtgroups = vts.pop('vt_groups')
1088
1089
        if vtgroups:
1090
            vts_list = self.get_vts_in_groups(vtgroups)
1091
1092
        for vtid, vt_params in vts.items():
1093
            if vtid not in self.vts.keys():
1094
                logger.warning(
1095
                    'The vt %s was not found and it will not be loaded.',
1096
                    vtid,
1097
                )
1098
                continue
1099
            vts_list.append(vtid)
1100
            for vt_param_id, vt_param_value in vt_params.items():
1101
                param_type = self.get_vt_param_type(vtid, vt_param_id)
1102
                param_name = self.get_vt_param_name(vtid, vt_param_id)
1103
                if not param_type or not param_name:
1104
                    logger.debug(
1105
                        'The vt parameter %s for %s could not be loaded.',
1106
                        vt_param_id,
1107
                        vtid,
1108
                    )
1109
                    continue
1110
                if vt_param_id == '0':
1111
                    type_aux = 'integer'
1112
                else:
1113
                    type_aux = param_type
1114
                if self.check_param_type(vt_param_value, type_aux):
1115
                    logger.debug(
1116
                        'Expected %s type for parameter value %s',
1117
                        type_aux,
1118
                        str(vt_param_value),
1119
                    )
1120
                    continue
1121
                if type_aux == 'checkbox':
1122
                    vt_param_value = _from_bool_to_str(int(vt_param_value))
1123
                param = [
1124
                    "{0}:{1}:{2}:{3}".format(
1125
                        vtid, vt_param_id, param_type, param_name
1126
                    ),
1127
                    str(vt_param_value),
1128
                ]
1129
                vts_params.append(param)
1130
        return vts_list, vts_params
1131
1132
    @staticmethod
1133
    def build_credentials_as_prefs(credentials):
1134
        """ Parse the credential dictionary.
1135
        @param credentials: Dictionary with the credentials.
1136
1137
        @return A list with the credentials in string format to be
1138
                added to the redis KB.
1139
        """
1140
        cred_prefs_list = []
1141
        for credential in credentials.items():
1142
            service = credential[0]
1143
            cred_params = credentials.get(service)
1144
            cred_type = cred_params.get('type', '')
1145
            username = cred_params.get('username', '')
1146
            password = cred_params.get('password', '')
1147
1148
            if service == 'ssh':
1149
                port = cred_params.get('port', '')
1150
                cred_prefs_list.append('auth_port_ssh|||' + '{0}'.format(port))
1151
                cred_prefs_list.append(
1152
                    OID_SSH_AUTH
1153
                    + ':1:'
1154
                    + 'entry:SSH login '
1155
                    + 'name:|||{0}'.format(username)
1156
                )
1157
                if cred_type == 'up':
1158
                    cred_prefs_list.append(
1159
                        OID_SSH_AUTH
1160
                        + ':3:'
1161
                        + 'password:SSH password '
1162
                        + '(unsafe!):|||{0}'.format(password)
1163
                    )
1164
                else:
1165
                    private = cred_params.get('private', '')
1166
                    cred_prefs_list.append(
1167
                        OID_SSH_AUTH
1168
                        + ':2:'
1169
                        + 'password:SSH key passphrase:|||'
1170
                        + '{0}'.format(password)
1171
                    )
1172
                    cred_prefs_list.append(
1173
                        OID_SSH_AUTH
1174
                        + ':4:'
1175
                        + 'file:SSH private key:|||'
1176
                        + '{0}'.format(private)
1177
                    )
1178
            if service == 'smb':
1179
                cred_prefs_list.append(
1180
                    OID_SMB_AUTH
1181
                    + ':1:entry'
1182
                    + ':SMB login:|||{0}'.format(username)
1183
                )
1184
                cred_prefs_list.append(
1185
                    OID_SMB_AUTH
1186
                    + ':2:'
1187
                    + 'password:SMB password:|||'
1188
                    + '{0}'.format(password)
1189
                )
1190
            if service == 'esxi':
1191
                cred_prefs_list.append(
1192
                    OID_ESXI_AUTH
1193
                    + ':1:entry:'
1194
                    + 'ESXi login name:|||'
1195
                    + '{0}'.format(username)
1196
                )
1197
                cred_prefs_list.append(
1198
                    OID_ESXI_AUTH
1199
                    + ':2:'
1200
                    + 'password:ESXi login password:|||'
1201
                    + '{0}'.format(password)
1202
                )
1203
1204
            if service == 'snmp':
1205
                community = cred_params.get('community', '')
1206
                auth_algorithm = cred_params.get('auth_algorithm', '')
1207
                privacy_password = cred_params.get('privacy_password', '')
1208
                privacy_algorithm = cred_params.get('privacy_algorithm', '')
1209
1210
                cred_prefs_list.append(
1211
                    OID_SNMP_AUTH
1212
                    + ':1:'
1213
                    + 'password:SNMP Community:'
1214
                    + '{0}'.format(community)
1215
                )
1216
                cred_prefs_list.append(
1217
                    OID_SNMP_AUTH
1218
                    + ':2:'
1219
                    + 'entry:SNMPv3 Username:'
1220
                    + '{0}'.format(username)
1221
                )
1222
                cred_prefs_list.append(
1223
                    OID_SNMP_AUTH + ':3:'
1224
                    'password:SNMPv3 Password:' + '{0}'.format(password)
1225
                )
1226
                cred_prefs_list.append(
1227
                    OID_SNMP_AUTH
1228
                    + ':4:'
1229
                    + 'radio:SNMPv3 Authentication '
1230
                    + 'Algorithm:{0}'.format(auth_algorithm)
1231
                )
1232
                cred_prefs_list.append(
1233
                    OID_SNMP_AUTH
1234
                    + ':5:'
1235
                    + 'password:SNMPv3 Privacy Password:'
1236
                    + '{0}'.format(privacy_password)
1237
                )
1238
                cred_prefs_list.append(
1239
                    OID_SNMP_AUTH
1240
                    + ':6:'
1241
                    + 'radio:SNMPv3 Privacy Algorithm:'
1242
                    + '{0}'.format(privacy_algorithm)
1243
                )
1244
1245
        return cred_prefs_list
1246
1247
    def exec_scan(self, scan_id, target):
1248
        """ Starts the OpenVAS scanner for scan_id scan. """
1249
        if self.pending_feed:
1250
            logger.info(
1251
                '%s: There is a pending feed update. '
1252
                'The scan can not be started.',
1253
                scan_id,
1254
            )
1255
            self.add_scan_error(
1256
                scan_id,
1257
                name='',
1258
                host=target,
1259
                value=(
1260
                    'It was not possible to start the scan,'
1261
                    'because a pending feed update. Please try later'
1262
                ),
1263
            )
1264
            return 2
1265
1266
        ports = self.get_scan_ports(scan_id, target)
1267
        if not ports:
1268
            self.add_scan_error(
1269
                scan_id, name='', host=target, value='No port list defined.'
1270
            )
1271
            return 2
1272
1273
        # Get scan options
1274
        options = self.get_scan_options(scan_id)
1275
        prefs_val = []
1276
        ctx = self.openvas_db.kb_new()
1277
        self.openvas_db.set_redisctx(ctx)
1278
        self.main_kbindex = self.openvas_db.db_index
1279
1280
        # To avoid interference between scan process during a parallel scanning
1281
        # new uuid is used internally for each scan.
1282
        openvas_scan_id = str(uuid.uuid4())
1283
        self.openvas_db.add_single_item(
1284
            'internal/%s' % openvas_scan_id, ['new']
1285
        )
1286
        self.openvas_db.add_single_item(
1287
            'internal/%s/globalscanid' % scan_id, [openvas_scan_id]
1288
        )
1289
        self.openvas_db.add_single_item(
1290
            'internal/scanid', [openvas_scan_id]
1291
        )
1292
1293
        exclude_hosts = self.get_scan_exclude_hosts(scan_id, target)
1294
        if exclude_hosts:
1295
            options['exclude_hosts'] = exclude_hosts
1296
1297
        # Get unfinished hosts, in case it is a resumed scan. And added
1298
        # into exclude_hosts scan preference. Set progress for the finished ones
1299
        # to 100%.
1300
        finished_hosts = self.get_scan_finished_hosts(scan_id)
1301
        if finished_hosts:
1302
            if exclude_hosts:
1303
                finished_hosts_str = ','.join(finished_hosts)
1304
                exclude_hosts = exclude_hosts + ',' + finished_hosts_str
1305
                options['exclude_hosts'] = exclude_hosts
1306
            else:
1307
                options['exclude_hosts'] = ','.join(finished_hosts)
1308
1309
        # Set scan preferences
1310
        for key, value in options.items():
1311
            item_type = ''
1312
            if key in OSPD_PARAMS:
1313
                item_type = OSPD_PARAMS[key].get('type')
1314
            if item_type == 'boolean':
1315
                val = _from_bool_to_str(value)
1316
            else:
1317
                val = str(value)
1318
            prefs_val.append(key + "|||" + val)
1319
        self.openvas_db.add_single_item(
1320
            'internal/%s/scanprefs' % openvas_scan_id, prefs_val
1321
        )
1322
1323
        # Store main_kbindex as global preference
1324
        ov_maindbid = 'ov_maindbid|||%d' % self.main_kbindex
1325
        self.openvas_db.add_single_item(
1326
            'internal/%s/scanprefs' % openvas_scan_id, [ov_maindbid]
1327
        )
1328
1329
        # Set target
1330
        target_aux = 'TARGET|||%s' % target
1331
        self.openvas_db.add_single_item(
1332
            'internal/%s/scanprefs' % openvas_scan_id, [target_aux]
1333
        )
1334
        # Set port range
1335
        port_range = 'port_range|||%s' % ports
1336
        self.openvas_db.add_single_item(
1337
            'internal/%s/scanprefs' % openvas_scan_id, [port_range]
1338
        )
1339
1340
        # Set credentials
1341
        credentials = self.get_scan_credentials(scan_id, target)
1342
        if credentials:
1343
            cred_prefs = self.build_credentials_as_prefs(credentials)
1344
            self.openvas_db.add_single_item(
1345
                'internal/%s/scanprefs' % openvas_scan_id, cred_prefs
1346
            )
1347
1348
        # Set plugins to run
1349
        nvts = self.get_scan_vts(scan_id)
1350
        if nvts != '':
1351
            nvts_list, nvts_params = self.process_vts(nvts)
1352
            # Add nvts list
1353
            separ = ';'
1354
            plugin_list = 'plugin_set|||%s' % separ.join(nvts_list)
1355
            self.openvas_db.add_single_item(
1356
                'internal/%s/scanprefs' % openvas_scan_id, [plugin_list]
1357
            )
1358
            # Add nvts parameters
1359
            for elem in nvts_params:
1360
                item = '%s|||%s' % (elem[0], elem[1])
1361
                self.openvas_db.add_single_item(
1362
                    'internal/%s/scanprefs' % openvas_scan_id, [item]
1363
                )
1364
        else:
1365
            self.openvas_db.release_db(self.main_kbindex)
1366
            self.add_scan_error(
1367
                scan_id, name='', host=target, value='No VTS to run.'
1368
            )
1369
            return 2
1370
1371
        cmd = ['openvas', '--scan-start', openvas_scan_id]
1372
        if not self.is_running_as_root and self.sudo_available:
1373
            cmd = ['sudo', '-n'] + cmd
1374
1375
        if self._niceness is not None:
1376
            cmd = ['nice', '-n', self._niceness] + cmd
1377
1378
        logger.debug("Running scan with niceness %s", self._niceness)
1379
        try:
1380
            result = subprocess.Popen(cmd, shell=False)
1381
        except OSError:
1382
            # the command is not available
1383
            return False
1384
1385
        ovas_pid = result.pid
1386
        logger.debug('pid = %s', ovas_pid)
1387
        self.openvas_db.add_single_item('internal/ovas_pid', [ovas_pid])
1388
1389
        # Wait until the scanner starts and loads all the preferences.
1390
        while (
1391
            self.openvas_db.get_single_item('internal/' + openvas_scan_id)
1392
            == 'new'
1393
        ):
1394
            res = result.poll()
1395
            if res and res < 0:
1396
                self.stop_scan_cleanup(scan_id)
1397
                msg = (
1398
                    'It was not possible run the task %s, since openvas ended '
1399
                    'unexpectedly with errors during launching.' % scan_id
1400
                )
1401
                logger.error(msg)
1402
                return 1
1403
            time.sleep(1)
1404
1405
        no_id_found = False
1406
        while True:
1407
            time.sleep(3)
1408
            # Check if the client stopped the whole scan
1409
            if self.scan_is_stopped(openvas_scan_id):
1410
                return 1
1411
1412
            ctx = self.openvas_db.kb_connect(self.main_kbindex)
1413
            self.openvas_db.set_redisctx(ctx)
1414
            dbs = self.openvas_db.get_list_item('internal/dbindex')
1415
            for i in list(dbs):
1416
                if i == self.main_kbindex:
1417
                    continue
1418
                self.openvas_db.select_kb(ctx, str(i), set_global=True)
1419
                id_aux = self.openvas_db.get_single_item('internal/scan_id')
1420
                if not id_aux:
1421
                    continue
1422
                if id_aux == openvas_scan_id:
1423
                    no_id_found = False
1424
                    current_host = self.openvas_db.get_host_ip()
1425
                    self.get_openvas_result(scan_id, current_host)
1426
                    self.get_openvas_status(scan_id, target, current_host)
1427
                    self.get_openvas_timestamp_scan_host(scan_id, current_host)
1428
                    if self.host_is_finished(openvas_scan_id):
1429
                        self.set_scan_host_finished(
1430
                            scan_id, target, current_host
1431
                        )
1432
                        self.get_openvas_status(scan_id, target, current_host)
1433
                        self.get_openvas_timestamp_scan_host(
1434
                            scan_id, current_host
1435
                        )
1436
                        self.openvas_db.select_kb(
1437
                            ctx, str(self.main_kbindex), set_global=False
1438
                        )
1439
                        self.openvas_db.remove_list_item('internal/dbindex', i)
1440
                        self.openvas_db.release_db(i)
1441
1442
            # Scan end. No kb in use for this scan id
1443
            if no_id_found and self.target_is_finished(scan_id):
1444
                break
1445
            no_id_found = True
1446
1447
        # Delete keys from KB related to this scan task.
1448
        self.openvas_db.release_db(self.main_kbindex)
1449
        return 1
1450
1451
1452
def main():
1453
    """ OSP openvas main function. """
1454
    daemon_main('OSPD - openvas', OSPDopenvas)
1455
1456
1457
if __name__ == '__main__':
1458
    main()
1459