Passed
Pull Request — master (#29)
by Juan José
01:25
created

OSPDopenvas.get_refs_vt_as_xml_str()   C

Complexity

Conditions 9

Size

Total Lines 28
Code Lines 25

Duplication

Lines 28
Ratio 100 %

Importance

Changes 0
Metric Value
eloc 25
dl 28
loc 28
rs 6.6666
c 0
b 0
f 0
cc 9
nop 1
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...
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):
261
        """ Load the NVT's OIDs and their filename into the vts
262
        global  dictionary. """
263
        oids = nvti.get_oids()
264
        str_out = True
0 ignored issues
show
Unused Code introduced by
The variable str_out seems to be unused.
Loading history...
265
        for oid in oids:
266
            vt_id = oid[1]
267
            filename = oid[0].split(':')
268
            ret = self.add_vt(vt_id,
269
                              name=filename[1],
270
                              vt_params=nvti.get_nvt_params(vt_id),
271
                              vt_refs=nvti.get_nvt_refs(vt_id),
272
                              custom=nvti.get_nvt_metadata(vt_id))
273
            if ret == -1:
274
                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...
275
            if ret == -2:
276
                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...
277
278
    @staticmethod
279
    def get_custom_vt_as_xml_str(custom):
280
        """ Return an xml element with custom metadata formatted as string."""
281
282
        nvt = Element('vt')
283
        for key, val in custom.items():
284
            xml_key = SubElement(nvt, key)
285
            xml_key.text = val
286
287
        itera = nvt.iter()
288
        metadata = ''
289
        for elem in itera:
290
            if elem.tag != 'vt' and elem.tag != 'file_name':
291
                metadata += (tostring(elem).decode('utf-8'))
292
        return metadata
293
294
    @staticmethod
295
    def get_params_vt_as_xml_str(vt_params):
296
        """ Return an xml element with params formatted as string."""
297
        vt_params_xml = Element('vt_params')
298
        for prefs in vt_params.items():
299
            vt_param = Element('vt_param')
300
            vt_param.set('type', prefs[1]['type'])
301
            vt_param.set('id', prefs[0])
302
            xml_name = SubElement(vt_param, 'name')
303
            xml_name.text = prefs[1]['name']
304
            if prefs[1]['default']:
305
                xml_def = SubElement(vt_param, 'default')
306
                xml_def.text = prefs[1]['default']
307
            vt_params_xml.append(vt_param)
308
309
        params_list = vt_params_xml.findall("vt_param")
310
        params = ''
311
        for param in params_list:
312
            params += (tostring(param).decode('utf-8'))
313
        return params
314
315
    @staticmethod
316
    def get_refs_vt_as_xml_str(vt_refs):
317
        """ Return an xml element with references formatted as string."""
318
        vt_refs_xml = Element('vt_refs')
319
        for ref_type, ref_values in vt_refs.items():
320
            for value in ref_values:
321
                vt_ref = Element('ref')
322
                if ref_type == "xref" and value:
323
                    for xref in value.split(', '):
324
                        try:
325
                            _type, _id = xref.split(':', 1)
326
                        except ValueError:
327
                            logger.error('Not possible to parse xref')
328
                            continue
329
                        vt_ref.set('type', _type.lower())
330
                        vt_ref.set('id', _id)
331
                elif value:
332
                    vt_ref.set('type', ref_type.lower())
333
                    vt_ref.set('id', value)
334
                else:
335
                    continue
336
                vt_refs_xml.append(vt_ref)
337
338
        refs_list = vt_refs_xml.findall("ref")
339
        refs = ''
340
        for ref in refs_list:
341
            refs += (tostring(ref).decode('utf-8'))
342
        return refs
343
344
    def check(self):
345
        """ Checks that openvassd command line tool is found and
346
        is executable. """
347
        try:
348
            result = subprocess.check_output(['openvassd', '-V'],
349
                                             stderr=subprocess.STDOUT)
350
            result = result.decode('ascii')
351
        except OSError:
352
            # The command is not available
353
            return False
354
355
        if result is None:
356
            return False
357
358
        version = result.split('\n')
359
        if version[0].find('OpenVAS') < 0:
360
            return False
361
362
        self.parse_param()
363
        self.scanner_info['version'] = version[0]
364
365
        return True
366
367
    def update_progress(self, scan_id, target, msg):
368
        """ Calculate porcentage and update the scan status
369
        for the progress bar. """
370
        host_progress_dict = dict()
371
        prog = str.split(msg, '/')
372
        if float(prog[1]) == 0:
373
            return
374
        host_prog = (float(prog[0]) / float(prog[1])) * 100
375
        host_progress_dict[target] = host_prog
376
        total_host = len(target_str_to_list(target))
377
        self.set_scan_target_progress(scan_id, target,
378
                                      sum(host_progress_dict.values()) / total_host)
379
380
    def get_openvas_status(self, scan_id, target):
381
        """ Get all status entries from redis kb. """
382
        res = openvas_db.get_status()
383
        while res:
384
            self.update_progress(scan_id, target, res)
385
            res = openvas_db.get_status()
386
387
    def get_openvas_result(self, scan_id):
388
        """ Get all result entries from redis kb. """
389
        res = openvas_db.get_result()
390
        ctx = openvas_db.db_find(nvti.NVTICACHE_STR)
391
        while res:
392
            msg = res.split('|||')
393
            host_aux = openvas_db.item_get_single('internal/ip')
394
            roid = msg[3]
395
            tag = nvti.get_nvt_tag(ctx, roid)
396
            rqod = nvti.get_nvt_qod(ctx, tag)
397
            rseverity = nvti.get_nvt_severity(ctx, tag)
398
            rname = nvti.get_nvt_name(ctx, roid)
399
400
            if msg[0] == 'ERRMSG':
401
                self.add_scan_error(scan_id, host=host_aux,
402
                                    name=rname, value=msg[4], port=msg[2])
403
            if msg[0] == 'LOG':
404
                self.add_scan_log(scan_id, host=host_aux, name=rname,
405
                                  value=msg[4], port=msg[2], qod=rqod,
406
                                  test_id=roid)
407
            if msg[0] == 'HOST_DETAIL':
408
                self.add_scan_log(scan_id, host=host_aux, name=rname,
409
                                  value=msg[4])
410
            if msg[0] == 'ALARM':
411
                self.add_scan_alarm(scan_id, host=host_aux, name=rname,
412
                                    value=msg[4], port=msg[2],
413
                                    test_id=roid, severity=rseverity,
414
                                    qod=rqod)
415
            res = openvas_db.get_result()
416
417
    def get_openvas_timestamp_scan_host(self, scan_id, target):
418
        """ Get start and end timestamp of a host scan from redis kb. """
419
        timestamp = openvas_db.get_host_scan_scan_end_time()
420
        if timestamp:
421
            self.add_scan_log(scan_id, host=target, name='HOST_END',
422
                              value=timestamp)
423
            return
424
        timestamp = openvas_db.get_host_scan_scan_start_time()
425
        if timestamp:
426
            self.add_scan_log(scan_id, host=target, name='HOST_START',
427
                              value=timestamp)
428
            return
429
430
    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...
431
        """ Check if the scan has finished. """
432
        status = openvas_db.item_get_single(('internal/%s' % scan_id))
433
        return status == 'finished'
434
435
    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...
436
        """ Check if the parent process has received the stop_scan order.
437
        @in scan_id: ID to identify the scan to be stopped.
438
        @return 1 if yes, None in other case.
439
        """
440
        ctx = openvas_db.kb_connect(dbnum=MAIN_KBINDEX)
441
        openvas_db.set_global_redisctx(ctx)
442
        status = openvas_db.item_get_single(('internal/%s' % scan_id))
443
        return status == 'stop_all'
444
445
    @staticmethod
446
    def stop_scan(global_scan_id):
0 ignored issues
show
Bug introduced by
Parameters differ from overridden 'stop_scan' method
Loading history...
447
        """ Set a key in redis to indicate the wrapper is stopped.
448
        It is done through redis because it is a new multiprocess
449
        instance and it is not possible to reach the variables
450
        of the grandchild process. Send SIGUSR2 to openvas to stop
451
        each running scan."""
452
        ctx = openvas_db.kb_connect()
453
        for current_kbi in range(0, openvas_db.MAX_DBINDEX):
454
            ctx.execute_command('SELECT '+ str(current_kbi))
455
            openvas_db.set_global_redisctx(ctx)
456
            scan_id = openvas_db.item_get_single(
457
                ('internal/%s/globalscanid' % global_scan_id))
458
            if scan_id:
459
                openvas_db.item_set_single(('internal/%s' % scan_id),
460
                                           ['stop_all', ])
461
                ovas_pid = openvas_db.item_get_single('internal/ovas_pid')
462
                parent = psutil.Process(int(ovas_pid))
463
                openvas_db.release_db(current_kbi)
464
                parent.send_signal(signal.SIGUSR2)
465
                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...
466
467
    @staticmethod
468
    def get_vts_in_groups(ctx, filters):
469
        """ Return a list of vts which match with the given filter.
470
471
        @input filters A list of filters. Each filter has key, operator and
472
                       a value. They are separated by a space.
473
                       Supported keys: family
474
        @return Return a list of vts which match with the given filter.
475
        """
476
        vts_list = list()
477
        families = dict()
478
        oids = nvti.get_oids()
479
        for oid in oids:
480
            family = nvti.get_nvt_family(ctx, oid[1])
481
            if family not in families:
482
                families[family] = list()
483
            families[family].append(oid[1])
484
485
        for elem in filters:
486
            key, value = elem.split('=')
487
            if key == 'family' and value in families:
488
                vts_list.extend(families[value])
489
        return vts_list
490
491
    def get_vt_param_type(self, vtid, vt_param_id):
492
        """ Return the type of the vt parameter from the vts dictionary. """
493
        vt_params_list = self.vts[vtid].get("vt_params")
494
        return vt_params_list[vt_param_id]["type"]
495
496
    @staticmethod
497
    def check_param_type(vt_param_value, param_type):
498
        """ Check if the value of a vt parameter matches with
499
        the type founded.
500
        """
501
        if (param_type in ['entry',
502
                           'file',
503
                           'password',
504
                           'radio',
505
                           'sshlogin', ] and isinstance(vt_param_value, str)):
506
            return None
507
        elif (param_type == 'checkbox' and
508
              (vt_param_value == 'yes' or vt_param_value == 'no')):
509
            return None
510
        elif param_type == 'integer':
511
            try:
512
                int(vt_param_value)
513
            except ValueError:
514
                return 1
515
            return None
516
517
        return 1
518
519
    def process_vts(self, vts):
520
        """ Add single VTs and their parameters. """
521
        vts_list = []
522
        vts_params = []
523
        vtgroups = vts.pop('vt_groups')
524
525
        ctx = openvas_db.db_find(nvti.NVTICACHE_STR)
526
        openvas_db.set_global_redisctx(ctx)
527
        if vtgroups:
528
            vts_list = self.get_vts_in_groups(ctx, vtgroups)
529
530
        for vtid, vt_params in vts.items():
531
            vts_list.append(vtid)
532
            nvt_name = nvti.get_nvt_name(ctx, vtid)
533
            for vt_param_id, vt_param_value in vt_params.items():
534
                param_type = self.get_vt_param_type(vtid, vt_param_id)
535
                if vt_param_id == 'timeout':
536
                    type_aux = 'integer'
537
                else:
538
                    type_aux = param_type
539
                if self.check_param_type(vt_param_value, type_aux):
540
                    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...
541
                                 .format(type_aux, str(vt_param_value)))
542
                param = ["{0}[{1}]:{2}".format(nvt_name, param_type,
543
                                               vt_param_id),
544
                         str(vt_param_value)]
545
                vts_params.append(param)
546
        return vts_list, vts_params
547
548
    @staticmethod
549
    def build_credentials_as_prefs(credentials):
550
        """ Parse the credential dictionary.
551
        @param credentials: Dictionary with the credentials.
552
553
        @return A list with the credentials in string format to be
554
                added to the redis KB.
555
        """
556
        cred_prefs_list = []
557
        for credential in credentials.items():
558
            service = credential[0]
559
            cred_params = credentials.get(service)
560
            cred_type = cred_params.get('type', '')
561
            username = cred_params.get('username', '')
562
            password = cred_params.get('password', '')
563
564
            if service == 'ssh':
565
                port = cred_params.get('port', '')
566
                cred_prefs_list.append('auth_port_ssh|||' +
567
                                       '{0}'.format(port))
568
                cred_prefs_list.append('SSH Authorization[entry]:SSH login ' +
569
                                       'name:|||{0}'.format(username))
570
                if cred_type == 'up':
571
                    cred_prefs_list.append('SSH Authorization[password]:' +
572
                                           'SSH password (unsafe!):|||' +
573
                                           '{0}'.format(password))
574
                else:
575
                    private = cred_params.get('private', '')
576
                    cred_prefs_list.append('SSH Authorization[password]:' +
577
                                           'SSH key passphrase:|||' +
578
                                           '{0}'.format(password))
579
                    cred_prefs_list.append('SSH Authorization[file]:' +
580
                                           'SSH private key:|||' +
581
                                           '{0}'.format(private))
582
            if service == 'smb':
583
                cred_prefs_list.append('SMB Authorization[entry]:SMB login:' +
584
                                       '|||{0}'.format(username))
585
                cred_prefs_list.append('SMB Authorization[password]:' +
586
                                       'SMB password :|||' +
587
                                       '{0}'.format(password))
588
            if service == 'esxi':
589
                cred_prefs_list.append('ESXi Authorization[entry]:ESXi login ' +
590
                                       'name:|||{0}'.format(username))
591
                cred_prefs_list.append('ESXi Authorization[password]:' +
592
                                       'ESXi login password:|||' +
593
                                       '{0}'.format(password))
594
595
            if service == 'snmp':
596
                community = cred_params.get('community', '')
597
                auth_algorithm = cred_params.get('auth_algorithm', '')
598
                privacy_password = cred_params.get('privacy_password', '')
599
                privacy_algorithm = cred_params.get('privacy_algorithm', '')
600
601
                cred_prefs_list.append('SNMP Authorization[password]:' +
602
                                       'SNMP Community:' +
603
                                       '{0}'.format(community))
604
                cred_prefs_list.append('SNMP Authorization[entry]:' +
605
                                       'SNMPv3 Username:' +
606
                                       '{0}'.format(username))
607
                cred_prefs_list.append('SNMP Authorization[password]:' +
608
                                       'SNMPv3 Password:' +
609
                                       '{0}'.format(password))
610
                cred_prefs_list.append('SNMP Authorization[radio]:' +
611
                                       'SNMPv3 Authentication Algorithm:' +
612
                                       '{0}'.format(auth_algorithm))
613
                cred_prefs_list.append('SNMP Authorization[password]:' +
614
                                       'SNMPv3 Privacy Password:' +
615
                                       '{0}'.format(privacy_password))
616
                cred_prefs_list.append('SNMP Authorization[radio]:' +
617
                                       'SNMPv3 Privacy Algorithm:' +
618
                                       '{0}'.format(privacy_algorithm))
619
620
        return cred_prefs_list
621
622
    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...
623
        """ Starts the OpenVAS scanner for scan_id scan. """
624
        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...
625
        ports = self.get_scan_ports(scan_id, target)
626
        if not ports:
627
            self.add_scan_error(scan_id, name='', host=target,
628
                                value='No port list defined.')
629
            return 2
630
631
        # Get scan options
632
        options = self.get_scan_options(scan_id)
633
        prefs_val = []
634
        ctx = openvas_db.kb_new()
635
        openvas_db.set_global_redisctx(ctx)
636
        MAIN_KBINDEX = openvas_db.DB_INDEX
637
638
        # To avoid interference between scan process during a parallel scanning
639
        # new uuid is used internally for each scan.
640
        openvas_scan_id = str(uuid.uuid4())
641
        openvas_db.item_add_single(('internal/%s' % openvas_scan_id), ['new', ])
642
        openvas_db.item_add_single(('internal/%s/globalscanid' % scan_id), [openvas_scan_id, ])
643
644
        # Set scan preferences
645
        for item in options.items():
646
            item_type = ''
647
            if item[0] in OSPD_PARAMS:
648
                item_type = OSPD_PARAMS[item[0]].get('type')
649
            if item_type == 'boolean' and item[1] == 1:
650
                val = 'yes'
651
            else:
652
                val = str(item[1])
653
            prefs_val.append(item[0] + "|||" + val)
654
        openvas_db.item_add_single(str('internal/%s/scanprefs' % (openvas_scan_id)),
655
                                   prefs_val)
656
657
        # Store MAIN_KBINDEX as global preference
658
        ov_maindbid = ('ov_maindbid|||%d' % MAIN_KBINDEX)
659
        openvas_db.item_add_single(('internal/%s/scanprefs' % openvas_scan_id),
660
                                   [ov_maindbid, ])
661
662
        # Set target
663
        target_aux = ('TARGET|||%s' % target)
664
        openvas_db.item_add_single(('internal/%s/scanprefs' % openvas_scan_id),
665
                                   [target_aux, ])
666
        # Set port range
667
        port_range = ('port_range|||%s' % ports)
668
        openvas_db.item_add_single(('internal/%s/scanprefs' % openvas_scan_id),
669
                                   [port_range, ])
670
671
        # Set credentials
672
        credentials = self.get_scan_credentials(scan_id, target)
673
        if credentials:
674
            cred_prefs = self.build_credentials_as_prefs(credentials)
675
            openvas_db.item_add_single(str('internal/%s/scanprefs' % openvas_scan_id),
676
                                       cred_prefs)
677
678
        # Set plugins to run
679
        nvts = self.get_scan_vts(scan_id)
680
        if nvts != '':
681
            nvts_list, nvts_params = self.process_vts(nvts)
682
            # Select the scan KB again.
683
            ctx.execute_command('SELECT '+ str(MAIN_KBINDEX))
684
            openvas_db.set_global_redisctx(ctx)
685
            # Add nvts list
686
            separ = ';'
687
            plugin_list = ('plugin_set|||%s' % separ.join(nvts_list))
688
            openvas_db.item_add_single(('internal/%s/scanprefs' % openvas_scan_id),
689
                                       [plugin_list, ])
690
            # Add nvts parameters
691
            for elem in nvts_params:
692
                item = ('%s|||%s' % (elem[0], elem[1]))
693
                openvas_db.item_add_single(('internal/%s/scanprefs' % openvas_scan_id),
694
                                           [item, ])
695
        else:
696
            openvas_db.release_db(MAIN_KBINDEX)
697
            self.add_scan_error(scan_id, name='', host=target,
698
                                value='No VTS to run.')
699
            return 2
700
701
        # Create a general log entry about executing OpenVAS
702
        # It is important to send at least one result, otherwise
703
        # the host details won't be stored.
704
        self.add_scan_log(scan_id, host=target, name='OpenVAS summary',
705
                          value='An OpenVAS Scanner was started for %s.'
706
                          % target)
707
708
        self.add_scan_log(scan_id, host=target, name='KB location Found',
709
                          value='KB location path was found: %s.'
710
                          % openvas_db.DB_ADDRESS)
711
712
        self.add_scan_log(scan_id, host=target, name='Feed Update',
713
                          value='Feed version: %s.'
714
                          % nvti.get_feed_version())
715
716
        cmd = ['openvassd', '--scan-start', openvas_scan_id]
717
        try:
718
            result = subprocess.Popen(cmd, shell=False)
719
        except OSError:
720
            # the command is not available
721
            return False
722
723
        ovas_pid = result.pid
724
        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...
725
        openvas_db.item_add_single(('internal/ovas_pid'), [ovas_pid, ])
726
727
        # Wait until the scanner starts and loads all the preferences.
728
        while openvas_db.item_get_single('internal/'+ openvas_scan_id) == 'new':
729
            time.sleep(1)
730
731
        no_id_found = False
732
        while True:
733
            time.sleep(3)
734
735
            # Check if the client stopped the whole scan
736
            if self.scan_is_stopped(openvas_scan_id):
737
                return 1
738
739
            ctx = openvas_db.kb_connect(MAIN_KBINDEX)
740
            openvas_db.set_global_redisctx(ctx)
741
            dbs = openvas_db.item_get_set('internal/dbindex')
742
            for i in list(dbs):
743
                if i == MAIN_KBINDEX:
744
                    continue
745
                ctx.execute_command('SELECT '+ str(i))
746
                openvas_db.set_global_redisctx(ctx)
747
                id_aux = ctx.execute_command('srandmember internal/scan_id')
748
                if not id_aux:
749
                    continue
750
                if id_aux == openvas_scan_id:
751
                    no_id_found = False
752
                    self.get_openvas_timestamp_scan_host(scan_id, target)
753
                    self.get_openvas_result(scan_id)
754
                    self.get_openvas_status(scan_id, target)
755
                    if self.scan_is_finished(openvas_scan_id):
756
                        ctx.execute_command('SELECT '+ str(MAIN_KBINDEX))
757
                        openvas_db.remove_set_member('internal/dbindex', i)
758
                        openvas_db.release_db(i)
759
760
            # Scan end. No kb in use for this scan id
761
            if no_id_found:
762
                break
763
            no_id_found = True
764
765
        # Delete keys from KB related to this scan task.
766
        openvas_db.release_db(MAIN_KBINDEX)
767
        return 1
768
769
770
def main():
771
    """ OSP openvas main function. """
772
    daemon_main('OSPD - openvas wrapper', OSPDopenvas)
773