Completed
Push — master ( 903db8...5a1a62 )
by Juan José
10s
created

OSPDopenvas.get_detection_vt_as_xml_str()   A

Complexity

Conditions 4

Size

Total Lines 12
Code Lines 10

Duplication

Lines 12
Ratio 100 %

Importance

Changes 0
Metric Value
eloc 10
dl 12
loc 12
rs 9.9
c 0
b 0
f 0
cc 4
nop 4
1
# -*- coding: utf-8 -*-
0 ignored issues
show
Coding Style introduced by
This module should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
2
# Description:
3
# Setup for the OSP OpenVAS Server
4
#
5
# Authors:
6
# Juan José Nicola <[email protected]>
7
#
8
# Copyright:
9
# Copyright (C) 2018 Greenbone Networks GmbH
10
#
11
# This program is free software; you can redistribute it and/or
12
# modify it under the terms of the GNU General Public License
13
# as published by the Free Software Foundation; either version 2
14
# of the License, or (at your option) any later version.
15
#
16
# This program is distributed in the hope that it will be useful,
17
# but WITHOUT ANY WARRANTY; without even the implied warranty of
18
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
# GNU General Public License for more details.
20
#
21
# You should have received a copy of the GNU General Public License
22
# along with this program; if not, write to the Free Software
23
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24
25
import subprocess
26
import time
27
import signal
28
import uuid
29
from lxml.etree import tostring, SubElement, Element
0 ignored issues
show
Bug introduced by
The name tostring does not seem to exist in module lxml.etree.
Loading history...
Bug introduced by
The name SubElement does not seem to exist in module lxml.etree.
Loading history...
Bug introduced by
The name Element does not seem to exist in module lxml.etree.
Loading history...
30
import psutil
31
32
from ospd.ospd import OSPDaemon, logger
33
from ospd.misc import main as daemon_main
34
from ospd.misc import target_str_to_list
35
from ospd_openvas import __version__
36
37
import ospd_openvas.nvticache as nvti
38
import ospd_openvas.openvas_db as openvas_db
39
40
OSPD_DESC = """
41
This scanner runs 'OpenVAS Scanner' to scan the target hosts.
42
43
OpenVAS (Open Vulnerability Assessment System) is a powerful scanner
44
for vulnerabilities in IT infrastrucutres. The capabilities include
45
unauthzenticated scanning as well as authneticated scanning for
46
various types of systems and services.
47
48
For more details about OpenVAS see the OpenVAS homepage:
49
http://www.openvas.org/
50
51
The current version of ospd-openvas is a simple frame, which sends
52
the server parameters to the Greenbone Vulnerability Manager (GVM) and checks
53
the existence of OpenVAS Scanner binary. But it can not run scans yet.
54
"""
55
56
MAIN_KBINDEX = None
57
58
OSPD_PARAMS = {
59
    'auto_enable_dependencies': {
60
        'type': 'boolean',
61
        'name': 'auto_enable_dependencies',
62
        'default': 1,
63
        'mandatory': 1,
64
        'description': 'Automatically enable the plugins that are depended on',
65
    },
66
    'cgi_path': {
67
        'type': 'string',
68
        'name': 'cgi_path',
69
        'default': '/cgi-bin:/scripts',
70
        'mandatory': 1,
71
        'description': 'Look for default CGIs in /cgi-bin and /scripts',
72
    },
73
    'checks_read_timeout': {
74
        'type': 'integer',
75
        'name': 'checks_read_timeout',
76
        'default': 5,
77
        'mandatory': 1,
78
        'description': ('Number  of seconds that the security checks will ' +
79
                        'wait for when doing a recv()'),
80
    },
81
    'drop_privileges': {
82
        'type': 'boolean',
83
        'name': 'drop_privileges',
84
        'default': 0,
85
        'mandatory': 1,
86
        'description': '',
87
    },
88
    'network_scan': {
89
        'type': 'boolean',
90
        'name': 'network_scan',
91
        'default': 0,
92
        'mandatory': 1,
93
        'description': '',
94
    },
95
    'non_simult_ports': {
96
        'type': 'string',
97
        'name': 'non_simult_ports',
98
        'default': '139, 445, 3389, Services/irc',
99
        'mandatory': 1,
100
        'description': ('Prevent to make two connections on the same given ' +
101
                        'ports at the same time.'),
102
    },
103
    'open_sock_max_attempts': {
104
        'type': 'integer',
105
        'name': 'open_sock_max_attempts',
106
        'default': 5,
107
        'mandatory': 0,
108
        'description': ('Number of unsuccessful retries to open the socket ' +
109
                        'before to set the port as closed.'),
110
    },
111
    'timeout_retry': {
112
        'type': 'integer',
113
        'name': 'timeout_retry',
114
        'default': 5,
115
        'mandatory': 0,
116
        'description': ('Number of retries when a socket connection attempt ' +
117
                        'timesout.'),
118
    },
119
    'optimize_test': {
120
        'type': 'integer',
121
        'name': 'optimize_test',
122
        'default': 5,
123
        'mandatory': 0,
124
        'description': ('By default, openvassd does not trust the remote ' +
125
                        'host banners.'),
126
    },
127
    'plugins_timeout': {
128
        'type': 'integer',
129
        'name': 'plugins_timeout',
130
        'default': 5,
131
        'mandatory': 0,
132
        'description': 'This is the maximum lifetime, in seconds of a plugin.',
133
    },
134
    'report_host_details': {
135
        'type': 'boolean',
136
        'name': 'report_host_details',
137
        'default': 1,
138
        'mandatory': 1,
139
        'description': '',
140
    },
141
    'safe_checks': {
142
        'type': 'boolean',
143
        'name': 'safe_checks',
144
        'default': 1,
145
        'mandatory': 1,
146
        'description': ('Disable the plugins with potential to crash ' +
147
                        'the remote services'),
148
    },
149
    'scanner_plugins_timeout': {
150
        'type': 'integer',
151
        'name': 'scanner_plugins_timeout',
152
        'default': 36000,
153
        'mandatory': 1,
154
        'description': 'Like plugins_timeout, but for ACT_SCANNER plugins.',
155
    },
156
    'time_between_request': {
157
        'type': 'integer',
158
        'name': 'time_between_request',
159
        'default': 0,
160
        'mandatory': 0,
161
        'description': ('Allow to set a wait time between two actions ' +
162
                        '(open, send, close).'),
163
    },
164
    'unscanned_closed': {
165
        'type': 'boolean',
166
        'name': 'unscanned_closed',
167
        'default': 1,
168
        'mandatory': 1,
169
        'description': '',
170
    },
171
    'unscanned_closed_udp': {
172
        'type': 'boolean',
173
        'name': 'unscanned_closed_udp',
174
        'default': 1,
175
        'mandatory': 1,
176
        'description': '',
177
    },
178
    'use_mac_addr': {
179
        'type': 'boolean',
180
        'name': 'use_mac_addr',
181
        'default': 0,
182
        'mandatory': 0,
183
        'description': 'To test the local network. ' +
184
                       'Hosts will be referred to by their MAC address.',
185
    },
186
    'vhosts': {
187
        'type': 'string',
188
        'name': 'vhosts',
189
        'default': '',
190
        'mandatory': 0,
191
        'description': '',
192
    },
193
    'vhosts_ip': {
194
        'type': 'string',
195
        'name': 'vhosts_ip',
196
        'default': '',
197
        'mandatory': 0,
198
        'description': '',
199
    },
200
}
201
202
203 View Code Duplication
class OSPDopenvas(OSPDaemon):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
Unused Code introduced by
The variable __class__ seems to be unused.
Loading history...
best-practice introduced by
Too many public methods (30/20)
Loading history...
204
205
    """ Class for ospd-openvas daemon. """
206
207
    def __init__(self, certfile, keyfile, cafile):
208
        """ Initializes the ospd-openvas daemon's internal data. """
209
210
        super(OSPDopenvas, self).__init__(certfile=certfile, keyfile=keyfile,
211
                                          cafile=cafile)
212
        self.server_version = __version__
213
        self.scanner_info['name'] = 'openvassd'
214
        self.scanner_info['version'] = ''  # achieved during self.check()
215
        self.scanner_info['description'] = OSPD_DESC
216
        for name, param in OSPD_PARAMS.items():
217
            self.add_scanner_param(name, param)
218
219
        if openvas_db.db_init() is False:
220
            logger.error('OpenVAS Redis Error: Not possible '
221
                         'to find db_connection.')
222
            raise Exception
223
224
        ctx = openvas_db.db_find(nvti.NVTICACHE_STR)
225
        if not ctx:
226
            self.redis_nvticache_init()
227
            ctx = openvas_db.db_find(nvti.NVTICACHE_STR)
228
        openvas_db.set_global_redisctx(ctx)
229
        self.load_vts()
230
231
    def parse_param(self):
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
232
        """ Set OSPD_PARAMS with the params taken from the openvas_scanner. """
233
        global OSPD_PARAMS
0 ignored issues
show
Coding Style introduced by
Usage of the global statement should be avoided.

Usage of global can make code hard to read and test, its usage is generally not recommended unless you are dealing with legacy code.

Loading history...
234
        bool_dict = {'no': 0, 'yes': 1}
235
236
        result = subprocess.check_output(['openvassd', '-s'],
237
                                         stderr=subprocess.STDOUT)
238
        result = result.decode('ascii')
239
        param_list = dict()
240
        for conf in result.split('\n'):
241
            elem = conf.split('=')
242
            if len(elem) == 2:
243
                value = str.strip(elem[1])
244
                if str.strip(elem[1]) in bool_dict:
245
                    value = bool_dict[value]
246
                param_list[str.strip(elem[0])] = value
247
        for elem in OSPD_PARAMS:
248
            if elem in param_list:
249
                OSPD_PARAMS[elem]['default'] = param_list[elem]
250
251
    def redis_nvticache_init(self):
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
252
        """ Loads NVT's metadata into Redis DB. """
253
        try:
254
            logger.debug('Loading NVTs in Redis DB')
255
            subprocess.check_call(['openvassd', '-C'])
256
        except subprocess.CalledProcessError as err:
257
            logger.error('OpenVAS Scanner failed to load NVTs.')
258
            raise err
259
260
    def load_vts(self):
0 ignored issues
show
Comprehensibility introduced by
This function exceeds the maximum number of variables (28/15).
Loading history...
261
        """ Load the NVT's metadata into the vts
262
        global  dictionary. """
263
        oids = dict(nvti.get_oids())
264
        for filename, vt_id in oids.items():
0 ignored issues
show
Unused Code introduced by
The variable filename seems to be unused.
Loading history...
265
            _vt_params = nvti.get_nvt_params(vt_id)
266
            _vt_refs = nvti.get_nvt_refs(vt_id)
267
            _custom = nvti.get_nvt_metadata(vt_id)
268
            _name = _custom.pop('name')
269
            _vt_creation_time = _custom.pop('creation_date')
270
            _vt_modification_time = _custom.pop('last_modification')
271
272
            _summary=None
0 ignored issues
show
Coding Style introduced by
Exactly one space required around assignment
Loading history...
273
            _impact=None
0 ignored issues
show
Coding Style introduced by
Exactly one space required around assignment
Loading history...
274
            _affected=None
0 ignored issues
show
Coding Style introduced by
Exactly one space required around assignment
Loading history...
275
            _insight=None
0 ignored issues
show
Coding Style introduced by
Exactly one space required around assignment
Loading history...
276
            _solution=None
0 ignored issues
show
Coding Style introduced by
Exactly one space required around assignment
Loading history...
277
            _solution_t=None
0 ignored issues
show
Coding Style introduced by
Exactly one space required around assignment
Loading history...
278
            _vuldetect=None
0 ignored issues
show
Coding Style introduced by
Exactly one space required around assignment
Loading history...
279
            _qod_t=None
0 ignored issues
show
Coding Style introduced by
Exactly one space required around assignment
Loading history...
280
            _qod_v=None
0 ignored issues
show
Coding Style introduced by
Exactly one space required around assignment
Loading history...
281
282
            if 'summary' in _custom:
283
                _summary  = _custom.pop('summary')
0 ignored issues
show
Coding Style introduced by
Exactly one space required before assignment
Loading history...
284
            if 'impact' in _custom:
285
                _impact   = _custom.pop('impact')
0 ignored issues
show
Coding Style introduced by
Exactly one space required before assignment
Loading history...
286
            if 'affected' in _custom:
287
                _affected = _custom.pop('affected')
288
            if 'insight' in _custom :
0 ignored issues
show
Coding Style introduced by
No space allowed before :
Loading history...
289
                _insight  = _custom.pop('insight')
0 ignored issues
show
Coding Style introduced by
Exactly one space required before assignment
Loading history...
290
            if 'solution' in _custom:
291
                _solution  = _custom.pop('solution')
0 ignored issues
show
Coding Style introduced by
Exactly one space required before assignment
Loading history...
292
                if 'solution_type' in _custom:
293
                    _solution_t  = _custom.pop('solution_type')
0 ignored issues
show
Coding Style introduced by
Exactly one space required before assignment
Loading history...
294
295
            if 'vuldetect' in _custom:
296
                _vuldetect  = _custom.pop('vuldetect')
0 ignored issues
show
Coding Style introduced by
Exactly one space required before assignment
Loading history...
297
            if 'qod_type' in _custom:
298
                _qod_t  = _custom.pop('qod_type')
0 ignored issues
show
Coding Style introduced by
Exactly one space required before assignment
Loading history...
299
            elif 'qod' in _custom:
300
                _qod_v  = _custom.pop('qod')
0 ignored issues
show
Coding Style introduced by
Exactly one space required before assignment
Loading history...
301
302
            _severity = dict()
303
            if 'severity_base_vector' in _custom:
304
                _severity_vector = _custom.pop('severity_base_vector')
305
            else:
306
                _severity_vector = _custom.pop('cvss_base_vector')
307
            _severity['severity_base_vector'] = _severity_vector
308
            if 'severity_type' in _custom:
309
                _severity_type = custom.pop('severity_type')
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable custom does not seem to be defined.
Loading history...
Comprehensibility Best Practice introduced by
Undefined variable 'custom'
Loading history...
310
            else:
311
                _severity_type = 'cvss_base_v2'
312
            _severity['severity_type'] = _severity_type
313
            if 'severity_origin' in _custom:
314
                _severity['severity_origin'] = _custom.pop('severity_origin')
315
316
            _vt_dependencies = list()
317
            if 'dependencies' in _custom:
318
                _deps = _custom.pop('dependencies')
319
                _deps_list = _deps.split(', ')
320
                for dep in _deps_list:
321
                    _vt_dependencies.append(oids.get('filename:' + dep))
322
323
            ret = self.add_vt(
0 ignored issues
show
Bug introduced by
The keyword vt_dependencies does not seem to exist for the method call.
Loading history...
Bug introduced by
The keyword insight does not seem to exist for the method call.
Loading history...
Bug introduced by
The keyword impact does not seem to exist for the method call.
Loading history...
Bug introduced by
The keyword summary does not seem to exist for the method call.
Loading history...
Bug introduced by
The keyword severities does not seem to exist for the method call.
Loading history...
Bug introduced by
The keyword solution_t does not seem to exist for the method call.
Loading history...
Bug introduced by
The keyword qod_t does not seem to exist for the method call.
Loading history...
Bug introduced by
The keyword affected does not seem to exist for the method call.
Loading history...
Bug introduced by
The keyword detection does not seem to exist for the method call.
Loading history...
Bug introduced by
The keyword qod_v does not seem to exist for the method call.
Loading history...
Bug introduced by
The keyword solution does not seem to exist for the method call.
Loading history...
324
                vt_id,
325
                name=_name,
326
                vt_params=_vt_params,
327
                vt_refs=_vt_refs,
328
                custom=_custom,
329
                vt_creation_time=_vt_creation_time,
330
                vt_modification_time=_vt_modification_time,
331
                vt_dependencies=_vt_dependencies,
332
                summary=_summary,
333
                impact=_impact,
334
                affected=_affected,
335
                insight=_insight,
336
                solution=_solution,
337
                solution_t=_solution_t,
338
                detection=_vuldetect,
339
                qod_t=_qod_t,
340
                qod_v=_qod_v,
341
                severities=_severity
342
            )
343
            if ret == -1:
344
                logger.info("Dupplicated VT with OID: {0}".format(vt_id))
0 ignored issues
show
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
345
            if ret == -2:
346
                logger.info("{0}: Invalid OID.".format(vt_id))
0 ignored issues
show
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
347
348
    @staticmethod
349
    def get_custom_vt_as_xml_str(vt_id, custom):
0 ignored issues
show
Bug introduced by
Parameters differ from overridden 'get_custom_vt_as_xml_str' method
Loading history...
Unused Code introduced by
The argument vt_id seems to be unused.
Loading history...
350
        """ Return an xml element with custom metadata formatted as string."""
351
352
        nvt = Element('vt')
353
        for key, val in custom.items():
354
            xml_key = SubElement(nvt, key)
355
            xml_key.text = val
356
357
        itera = nvt.iter()
358
        metadata = ''
359
        for elem in itera:
360
            if elem.tag != 'vt':
361
                metadata += (tostring(elem).decode('utf-8'))
362
        return metadata
363
364
    @staticmethod
365
    def get_severities_vt_as_xml_str(vt_id, severities):
0 ignored issues
show
Unused Code introduced by
The argument vt_id seems to be unused.
Loading history...
366
        """ Return an xml element with severities as string."""
367
368
        _severity = Element('severity')
369
        if 'severity_base_vector' in severities:
370
            _severity.text = severities.pop('severity_base_vector')
371
        if 'severity_origin' in severities:
372
            _severity.set('origin', severities.pop('severity_origin'))
373
        if 'severity_type' in severities:
374
            _severity.set('type', severities.pop('severity_type'))
375
376
        return tostring(_severity).decode('utf-8')
377
378
    @staticmethod
379
    def get_params_vt_as_xml_str(vt_id, vt_params):
0 ignored issues
show
Bug introduced by
Parameters differ from overridden 'get_params_vt_as_xml_str' method
Loading history...
Unused Code introduced by
The argument vt_id seems to be unused.
Loading history...
380
        """ Return an xml element with params formatted as string."""
381
        vt_params_xml = Element('vt_params')
382
        for prefs in vt_params.items():
383
            vt_param = Element('vt_param')
384
            vt_param.set('type', prefs[1]['type'])
385
            vt_param.set('id', prefs[0])
386
            xml_name = SubElement(vt_param, 'name')
387
            xml_name.text = prefs[1]['name']
388
            if prefs[1]['default']:
389
                xml_def = SubElement(vt_param, 'default')
390
                xml_def.text = prefs[1]['default']
391
            vt_params_xml.append(vt_param)
392
393
        params_list = vt_params_xml.findall("vt_param")
394
        params = ''
395
        for param in params_list:
396
            params += (tostring(param).decode('utf-8'))
397
        return params
398
399
    @staticmethod
400
    def get_refs_vt_as_xml_str(vt_id, vt_refs):
0 ignored issues
show
Bug introduced by
Parameters differ from overridden 'get_refs_vt_as_xml_str' method
Loading history...
401
        """ Return an xml element with references formatted as string."""
402
        vt_refs_xml = Element('vt_refs')
403
        for ref_type, ref_values in vt_refs.items():
404
            for value in ref_values:
405
                vt_ref = Element('ref')
406
                if ref_type == "xref" and value:
407
                    for xref in value.split(', '):
408
                        try:
409
                            _type, _id = xref.split(':', 1)
410
                        except ValueError:
411
                            logger.error(
0 ignored issues
show
Coding Style Best Practice introduced by
Specify string format arguments as logging function parameters
Loading history...
412
                                'Not possible to parse xref %s for vt %s' % (
413
                                    xref, vt_id))
414
                            continue
415
                        vt_ref.set('type', _type.lower())
416
                        vt_ref.set('id', _id)
417
                elif value:
418
                    vt_ref.set('type', ref_type.lower())
419
                    vt_ref.set('id', value)
420
                else:
421
                    continue
422
                vt_refs_xml.append(vt_ref)
423
424
        refs_list = vt_refs_xml.findall("ref")
425
        refs = ''
426
        for ref in refs_list:
427
            refs += (tostring(ref).decode('utf-8'))
428
        return refs
429
430
    @staticmethod
431
    def get_dependencies_vt_as_xml_str(vt_id, dep_list):
432
        """ Return  an xml element with dependencies as string."""
433
        vt_deps_xml = Element('vt_deps')
434
        for dep in dep_list:
435
            _vt_dep = Element('dependency')
436
            try:
437
                _vt_dep.set('vt_id', dep)
438
            except TypeError:
439
                logger.error('Not possible to add dependency %s for vt %s' % (
0 ignored issues
show
Coding Style Best Practice introduced by
Specify string format arguments as logging function parameters
Loading history...
440
                    dep, vt_id))
441
                continue
442
            vt_deps_xml.append(_vt_dep)
443
444
        _deps_list = vt_deps_xml.findall("dependency")
445
        deps = ''
446
        for _dep in _deps_list:
447
            deps += (tostring(_dep).decode('utf-8'))
448
        return deps
449
450
    @staticmethod
451
    def get_creation_time_vt_as_xml_str(vt_id, creation_time):
0 ignored issues
show
Bug introduced by
Parameters differ from overridden 'get_creation_time_vt_as_xml_str' method
Loading history...
Unused Code introduced by
The argument vt_id seems to be unused.
Loading history...
452
        """ Return creation time as string."""
453
        return creation_time
454
455
    @staticmethod
456
    def get_modification_time_vt_as_xml_str(vt_id, modification_time):
0 ignored issues
show
Bug introduced by
Parameters differ from overridden 'get_modification_time_vt_as_xml_str' method
Loading history...
Unused Code introduced by
The argument vt_id seems to be unused.
Loading history...
457
        """ Return modification time as string."""
458
        return modification_time
459
460
    @staticmethod
461
    def get_summary_vt_as_xml_str(vt_id, summary):
0 ignored issues
show
Unused Code introduced by
The argument vt_id seems to be unused.
Loading history...
462
        """ Return summary as string."""
463
        _summary = Element('summary')
464
        _summary.text = summary
465
        return tostring(_summary).decode('utf-8')
466
467
    @staticmethod
468
    def get_impact_vt_as_xml_str(vt_id, impact):
0 ignored issues
show
Unused Code introduced by
The argument vt_id seems to be unused.
Loading history...
469
        """ Return impact as string."""
470
        _impact = Element('impact')
471
        _impact.text = impact
472
        return tostring(_impact).decode('utf-8')
473
474
    @staticmethod
475
    def get_affected_vt_as_xml_str(vt_id, affected):
0 ignored issues
show
Unused Code introduced by
The argument vt_id seems to be unused.
Loading history...
476
        """ Return affected as string."""
477
        _affected = Element('affected')
478
        _affected.text = affected
479
        return tostring(_affected).decode('utf-8')
480
481
    @staticmethod
482
    def get_insight_vt_as_xml_str(vt_id, insight):
0 ignored issues
show
Unused Code introduced by
The argument vt_id seems to be unused.
Loading history...
483
        """ Return insight as string."""
484
        _insight = Element('insight')
485
        _insight.text = insight
486
        return tostring(_insight).decode('utf-8')
487
488
    @staticmethod
489
    def get_solution_vt_as_xml_str(vt_id, solution, solution_type=None):
0 ignored issues
show
Unused Code introduced by
The argument vt_id seems to be unused.
Loading history...
490
        """ Return solution as string."""
491
        _solution = Element('solution')
492
        _solution.text = solution
493
        if solution_type:
494
            _solution.set('type', solution_type)
495
        return tostring(_solution).decode('utf-8')
496
497
    @staticmethod
498
    def get_detection_vt_as_xml_str(vt_id, vuldetect=None, qod_type=None, qod=None):
0 ignored issues
show
Unused Code introduced by
The argument vt_id seems to be unused.
Loading history...
499
        """ Return detection as string."""
500
        _detection = Element('detection')
501
        if vuldetect:
502
            _detection.text = vuldetect
503
        if qod_type:
504
            _detection.set('qod_type', qod_type)
505
        elif qod:
506
            _detection.set('qod', qod)
507
508
        return tostring(_detection).decode('utf-8')
509
510
    def check(self):
511
        """ Checks that openvassd command line tool is found and
512
        is executable. """
513
        try:
514
            result = subprocess.check_output(['openvassd', '-V'],
515
                                             stderr=subprocess.STDOUT)
516
            result = result.decode('ascii')
517
        except OSError:
518
            # The command is not available
519
            return False
520
521
        if result is None:
522
            return False
523
524
        version = result.split('\n')
525
        if version[0].find('OpenVAS') < 0:
526
            return False
527
528
        self.parse_param()
529
        self.scanner_info['version'] = version[0]
530
531
        return True
532
533
    def update_progress(self, scan_id, target, msg):
534
        """ Calculate porcentage and update the scan status
535
        for the progress bar. """
536
        host_progress_dict = dict()
537
        prog = str.split(msg, '/')
538
        if float(prog[1]) == 0:
539
            return
540
        host_prog = (float(prog[0]) / float(prog[1])) * 100
541
        host_progress_dict[target] = host_prog
542
        total_host = len(target_str_to_list(target))
543
        self.set_scan_target_progress(scan_id, target,
544
                                      sum(host_progress_dict.values()) / total_host)
545
546
    def get_openvas_status(self, scan_id, target):
547
        """ Get all status entries from redis kb. """
548
        res = openvas_db.get_status()
549
        while res:
550
            self.update_progress(scan_id, target, res)
551
            res = openvas_db.get_status()
552
553
    def get_openvas_result(self, scan_id):
554
        """ Get all result entries from redis kb. """
555
        res = openvas_db.get_result()
556
        ctx = openvas_db.db_find(nvti.NVTICACHE_STR)
0 ignored issues
show
Unused Code introduced by
The variable ctx seems to be unused.
Loading history...
557
        while res:
558
            msg = res.split('|||')
559
            host_aux = openvas_db.item_get_single('internal/ip')
560
            roid = msg[3]
561
562
            rqod = ''
563
            if self.vts[roid].get('qod_type'):
564
                qod_t = self.vts[roid].get('qod_type')
565
                rqod = nvti.QoD_TYPES[qod_t]
566
            elif self.vts[roid].get('qod'):
567
                rqod = self.vts[roid].get('qod')
568
569
            rseverity = self.vts[roid]['severities'].get('cvss_base')
570
            rname = self.vts[roid].get('name')
571
572
            if msg[0] == 'ERRMSG':
573
                self.add_scan_error(scan_id, host=host_aux,
574
                                    name=rname, value=msg[4], port=msg[2])
575
            if msg[0] == 'LOG':
576
                self.add_scan_log(scan_id, host=host_aux, name=rname,
577
                                  value=msg[4], port=msg[2], qod=rqod,
578
                                  test_id=roid)
579
            if msg[0] == 'HOST_DETAIL':
580
                self.add_scan_log(scan_id, host=host_aux, name=rname,
581
                                  value=msg[4])
582
            if msg[0] == 'ALARM':
583
                self.add_scan_alarm(scan_id, host=host_aux, name=rname,
584
                                    value=msg[4], port=msg[2],
585
                                    test_id=roid, severity=rseverity,
586
                                    qod=rqod)
587
            res = openvas_db.get_result()
588
589
    def get_openvas_timestamp_scan_host(self, scan_id, target):
590
        """ Get start and end timestamp of a host scan from redis kb. """
591
        timestamp = openvas_db.get_host_scan_scan_end_time()
592
        if timestamp:
593
            self.add_scan_log(scan_id, host=target, name='HOST_END',
594
                              value=timestamp)
595
            return
596
        timestamp = openvas_db.get_host_scan_scan_start_time()
597
        if timestamp:
598
            self.add_scan_log(scan_id, host=target, name='HOST_START',
599
                              value=timestamp)
600
            return
601
602
    def scan_is_finished(self, scan_id):
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
603
        """ Check if the scan has finished. """
604
        status = openvas_db.item_get_single(('internal/%s' % scan_id))
605
        return status == 'finished'
606
607
    def scan_is_stopped(self, scan_id):
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
608
        """ Check if the parent process has received the stop_scan order.
609
        @in scan_id: ID to identify the scan to be stopped.
610
        @return 1 if yes, None in other case.
611
        """
612
        ctx = openvas_db.kb_connect(dbnum=MAIN_KBINDEX)
613
        openvas_db.set_global_redisctx(ctx)
614
        status = openvas_db.item_get_single(('internal/%s' % scan_id))
615
        return status == 'stop_all'
616
617
    @staticmethod
618
    def stop_scan(global_scan_id):
0 ignored issues
show
Bug introduced by
Parameters differ from overridden 'stop_scan' method
Loading history...
619
        """ Set a key in redis to indicate the wrapper is stopped.
620
        It is done through redis because it is a new multiprocess
621
        instance and it is not possible to reach the variables
622
        of the grandchild process. Send SIGUSR2 to openvas to stop
623
        each running scan."""
624
        ctx = openvas_db.kb_connect()
625
        for current_kbi in range(0, openvas_db.MAX_DBINDEX):
626
            ctx.execute_command('SELECT '+ str(current_kbi))
627
            openvas_db.set_global_redisctx(ctx)
628
            scan_id = openvas_db.item_get_single(
629
                ('internal/%s/globalscanid' % global_scan_id))
630
            if scan_id:
631
                openvas_db.item_set_single(('internal/%s' % scan_id),
632
                                           ['stop_all', ])
633
                ovas_pid = openvas_db.item_get_single('internal/ovas_pid')
634
                parent = psutil.Process(int(ovas_pid))
635
                openvas_db.release_db(current_kbi)
636
                parent.send_signal(signal.SIGUSR2)
637
                logger.debug('Stopping process: {0}'.format(parent))
0 ignored issues
show
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
638
639
    def get_vts_in_groups(self, ctx, filters):
0 ignored issues
show
Unused Code introduced by
The argument ctx seems to be unused.
Loading history...
640
        """ Return a list of vts which match with the given filter.
641
642
        @input filters A list of filters. Each filter has key, operator and
643
                       a value. They are separated by a space.
644
                       Supported keys: family
645
        @return Return a list of vts which match with the given filter.
646
        """
647
        vts_list = list()
648
        families = dict()
649
        oids = nvti.get_oids()
650
        for filename, oid in oids:
0 ignored issues
show
Unused Code introduced by
The variable filename seems to be unused.
Loading history...
651
            family = self.vts[oid]['custom'].get('family')
652
            if family not in families:
653
                families[family] = list()
654
            families[family].append(oid)
655
656
        for elem in filters:
657
            key, value = elem.split('=')
658
            if key == 'family' and value in families:
659
                vts_list.extend(families[value])
660
        return vts_list
661
662
    def get_vt_param_type(self, vtid, vt_param_id):
663
        """ Return the type of the vt parameter from the vts dictionary. """
664
        vt_params_list = self.vts[vtid].get("vt_params")
665
        return vt_params_list[vt_param_id]["type"]
666
667
    @staticmethod
668
    def check_param_type(vt_param_value, param_type):
669
        """ Check if the value of a vt parameter matches with
670
        the type founded.
671
        """
672
        if (param_type in ['entry',
673
                           'file',
674
                           'password',
675
                           'radio',
676
                           'sshlogin', ] and isinstance(vt_param_value, str)):
677
            return None
678
        elif (param_type == 'checkbox' and
679
              (vt_param_value == 'yes' or vt_param_value == 'no')):
680
            return None
681
        elif param_type == 'integer':
682
            try:
683
                int(vt_param_value)
684
            except ValueError:
685
                return 1
686
            return None
687
688
        return 1
689
690
    def process_vts(self, vts):
691
        """ Add single VTs and their parameters. """
692
        vts_list = []
693
        vts_params = []
694
        vtgroups = vts.pop('vt_groups')
695
696
        ctx = openvas_db.db_find(nvti.NVTICACHE_STR)
697
        openvas_db.set_global_redisctx(ctx)
698
        if vtgroups:
699
            vts_list = self.get_vts_in_groups(ctx, vtgroups)
700
701
        for vtid, vt_params in vts.items():
702
            vts_list.append(vtid)
703
            nvt_name = self.vts[vtid].get('name')
704
            for vt_param_id, vt_param_value in vt_params.items():
705
                param_type = self.get_vt_param_type(vtid, vt_param_id)
706
                if vt_param_id == 'timeout':
707
                    type_aux = 'integer'
708
                else:
709
                    type_aux = param_type
710
                if self.check_param_type(vt_param_value, type_aux):
711
                    logger.debug('Expected {} type for parameter value {}'
0 ignored issues
show
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
712
                                 .format(type_aux, str(vt_param_value)))
713
                param = ["{0}[{1}]:{2}".format(nvt_name, param_type,
714
                                               vt_param_id),
715
                         str(vt_param_value)]
716
                vts_params.append(param)
717
        return vts_list, vts_params
718
719
    @staticmethod
720
    def build_credentials_as_prefs(credentials):
721
        """ Parse the credential dictionary.
722
        @param credentials: Dictionary with the credentials.
723
724
        @return A list with the credentials in string format to be
725
                added to the redis KB.
726
        """
727
        cred_prefs_list = []
728
        for credential in credentials.items():
729
            service = credential[0]
730
            cred_params = credentials.get(service)
731
            cred_type = cred_params.get('type', '')
732
            username = cred_params.get('username', '')
733
            password = cred_params.get('password', '')
734
735
            if service == 'ssh':
736
                port = cred_params.get('port', '')
737
                cred_prefs_list.append('auth_port_ssh|||' +
738
                                       '{0}'.format(port))
739
                cred_prefs_list.append('SSH Authorization[entry]:SSH login ' +
740
                                       'name:|||{0}'.format(username))
741
                if cred_type == 'up':
742
                    cred_prefs_list.append('SSH Authorization[password]:' +
743
                                           'SSH password (unsafe!):|||' +
744
                                           '{0}'.format(password))
745
                else:
746
                    private = cred_params.get('private', '')
747
                    cred_prefs_list.append('SSH Authorization[password]:' +
748
                                           'SSH key passphrase:|||' +
749
                                           '{0}'.format(password))
750
                    cred_prefs_list.append('SSH Authorization[file]:' +
751
                                           'SSH private key:|||' +
752
                                           '{0}'.format(private))
753
            if service == 'smb':
754
                cred_prefs_list.append('SMB Authorization[entry]:SMB login:' +
755
                                       '|||{0}'.format(username))
756
                cred_prefs_list.append('SMB Authorization[password]:' +
757
                                       'SMB password :|||' +
758
                                       '{0}'.format(password))
759
            if service == 'esxi':
760
                cred_prefs_list.append('ESXi Authorization[entry]:ESXi login ' +
761
                                       'name:|||{0}'.format(username))
762
                cred_prefs_list.append('ESXi Authorization[password]:' +
763
                                       'ESXi login password:|||' +
764
                                       '{0}'.format(password))
765
766
            if service == 'snmp':
767
                community = cred_params.get('community', '')
768
                auth_algorithm = cred_params.get('auth_algorithm', '')
769
                privacy_password = cred_params.get('privacy_password', '')
770
                privacy_algorithm = cred_params.get('privacy_algorithm', '')
771
772
                cred_prefs_list.append('SNMP Authorization[password]:' +
773
                                       'SNMP Community:' +
774
                                       '{0}'.format(community))
775
                cred_prefs_list.append('SNMP Authorization[entry]:' +
776
                                       'SNMPv3 Username:' +
777
                                       '{0}'.format(username))
778
                cred_prefs_list.append('SNMP Authorization[password]:' +
779
                                       'SNMPv3 Password:' +
780
                                       '{0}'.format(password))
781
                cred_prefs_list.append('SNMP Authorization[radio]:' +
782
                                       'SNMPv3 Authentication Algorithm:' +
783
                                       '{0}'.format(auth_algorithm))
784
                cred_prefs_list.append('SNMP Authorization[password]:' +
785
                                       'SNMPv3 Privacy Password:' +
786
                                       '{0}'.format(privacy_password))
787
                cred_prefs_list.append('SNMP Authorization[radio]:' +
788
                                       'SNMPv3 Privacy Algorithm:' +
789
                                       '{0}'.format(privacy_algorithm))
790
791
        return cred_prefs_list
792
793
    def exec_scan(self, scan_id, target):
0 ignored issues
show
Comprehensibility introduced by
This function exceeds the maximum number of variables (30/15).
Loading history...
794
        """ Starts the OpenVAS scanner for scan_id scan. """
795
        global MAIN_KBINDEX
0 ignored issues
show
Coding Style introduced by
Usage of the global statement should be avoided.

Usage of global can make code hard to read and test, its usage is generally not recommended unless you are dealing with legacy code.

Loading history...
796
        ports = self.get_scan_ports(scan_id, target)
797
        if not ports:
798
            self.add_scan_error(scan_id, name='', host=target,
799
                                value='No port list defined.')
800
            return 2
801
802
        # Get scan options
803
        options = self.get_scan_options(scan_id)
804
        prefs_val = []
805
        ctx = openvas_db.kb_new()
806
        openvas_db.set_global_redisctx(ctx)
807
        MAIN_KBINDEX = openvas_db.DB_INDEX
808
809
        # To avoid interference between scan process during a parallel scanning
810
        # new uuid is used internally for each scan.
811
        openvas_scan_id = str(uuid.uuid4())
812
        openvas_db.item_add_single(('internal/%s' % openvas_scan_id), ['new', ])
813
        openvas_db.item_add_single(('internal/%s/globalscanid' % scan_id), [openvas_scan_id, ])
814
815
        # Set scan preferences
816
        for item in options.items():
817
            item_type = ''
818
            if item[0] in OSPD_PARAMS:
819
                item_type = OSPD_PARAMS[item[0]].get('type')
820
            if item_type == 'boolean' and item[1] == 1:
821
                val = 'yes'
822
            else:
823
                val = str(item[1])
824
            prefs_val.append(item[0] + "|||" + val)
825
        openvas_db.item_add_single(str('internal/%s/scanprefs' % (openvas_scan_id)),
826
                                   prefs_val)
827
828
        # Store MAIN_KBINDEX as global preference
829
        ov_maindbid = ('ov_maindbid|||%d' % MAIN_KBINDEX)
830
        openvas_db.item_add_single(('internal/%s/scanprefs' % openvas_scan_id),
831
                                   [ov_maindbid, ])
832
833
        # Set target
834
        target_aux = ('TARGET|||%s' % target)
835
        openvas_db.item_add_single(('internal/%s/scanprefs' % openvas_scan_id),
836
                                   [target_aux, ])
837
        # Set port range
838
        port_range = ('port_range|||%s' % ports)
839
        openvas_db.item_add_single(('internal/%s/scanprefs' % openvas_scan_id),
840
                                   [port_range, ])
841
842
        # Set credentials
843
        credentials = self.get_scan_credentials(scan_id, target)
844
        if credentials:
845
            cred_prefs = self.build_credentials_as_prefs(credentials)
846
            openvas_db.item_add_single(str('internal/%s/scanprefs' % openvas_scan_id),
847
                                       cred_prefs)
848
849
        # Set plugins to run
850
        nvts = self.get_scan_vts(scan_id)
851
        if nvts != '':
852
            nvts_list, nvts_params = self.process_vts(nvts)
853
            # Select the scan KB again.
854
            ctx.execute_command('SELECT '+ str(MAIN_KBINDEX))
855
            openvas_db.set_global_redisctx(ctx)
856
            # Add nvts list
857
            separ = ';'
858
            plugin_list = ('plugin_set|||%s' % separ.join(nvts_list))
859
            openvas_db.item_add_single(('internal/%s/scanprefs' % openvas_scan_id),
860
                                       [plugin_list, ])
861
            # Add nvts parameters
862
            for elem in nvts_params:
863
                item = ('%s|||%s' % (elem[0], elem[1]))
864
                openvas_db.item_add_single(('internal/%s/scanprefs' % openvas_scan_id),
865
                                           [item, ])
866
        else:
867
            openvas_db.release_db(MAIN_KBINDEX)
868
            self.add_scan_error(scan_id, name='', host=target,
869
                                value='No VTS to run.')
870
            return 2
871
872
        # Create a general log entry about executing OpenVAS
873
        # It is important to send at least one result, otherwise
874
        # the host details won't be stored.
875
        self.add_scan_log(scan_id, host=target, name='OpenVAS summary',
876
                          value='An OpenVAS Scanner was started for %s.'
877
                          % target)
878
879
        self.add_scan_log(scan_id, host=target, name='KB location Found',
880
                          value='KB location path was found: %s.'
881
                          % openvas_db.DB_ADDRESS)
882
883
        self.add_scan_log(scan_id, host=target, name='Feed Update',
884
                          value='Feed version: %s.'
885
                          % nvti.get_feed_version())
886
887
        cmd = ['openvassd', '--scan-start', openvas_scan_id]
888
        try:
889
            result = subprocess.Popen(cmd, shell=False)
890
        except OSError:
891
            # the command is not available
892
            return False
893
894
        ovas_pid = result.pid
895
        logger.debug('pid = {0}'.format(ovas_pid))
0 ignored issues
show
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
896
        openvas_db.item_add_single(('internal/ovas_pid'), [ovas_pid, ])
897
898
        # Wait until the scanner starts and loads all the preferences.
899
        while openvas_db.item_get_single('internal/'+ openvas_scan_id) == 'new':
900
            time.sleep(1)
901
902
        no_id_found = False
903
        while True:
904
            time.sleep(3)
905
906
            # Check if the client stopped the whole scan
907
            if self.scan_is_stopped(openvas_scan_id):
908
                return 1
909
910
            ctx = openvas_db.kb_connect(MAIN_KBINDEX)
911
            openvas_db.set_global_redisctx(ctx)
912
            dbs = openvas_db.item_get_set('internal/dbindex')
913
            for i in list(dbs):
914
                if i == MAIN_KBINDEX:
915
                    continue
916
                ctx.execute_command('SELECT '+ str(i))
917
                openvas_db.set_global_redisctx(ctx)
918
                id_aux = ctx.execute_command('srandmember internal/scan_id')
919
                if not id_aux:
920
                    continue
921
                if id_aux == openvas_scan_id:
922
                    no_id_found = False
923
                    self.get_openvas_timestamp_scan_host(scan_id, target)
924
                    self.get_openvas_result(scan_id)
925
                    self.get_openvas_status(scan_id, target)
926
                    if self.scan_is_finished(openvas_scan_id):
927
                        ctx.execute_command('SELECT '+ str(MAIN_KBINDEX))
928
                        openvas_db.remove_set_member('internal/dbindex', i)
929
                        openvas_db.release_db(i)
930
931
            # Scan end. No kb in use for this scan id
932
            if no_id_found:
933
                break
934
            no_id_found = True
935
936
        # Delete keys from KB related to this scan task.
937
        openvas_db.release_db(MAIN_KBINDEX)
938
        return 1
939
940
941
def main():
942
    """ OSP openvas main function. """
943
    daemon_main('OSPD - openvas wrapper', OSPDopenvas)
944