Passed
Push — master ( dc0158...bd40ef )
by
unknown
01:39 queued 15s
created

ospd_openvas.wrapper.OSPDopenvas.process_vts()   B

Complexity

Conditions 6

Size

Total Lines 28
Code Lines 24

Duplication

Lines 28
Ratio 100 %

Importance

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