1 | # -*- coding: utf-8 -*- |
||
2 | # Copyright (C) 2014-2021 Greenbone Networks GmbH |
||
3 | # |
||
4 | # SPDX-License-Identifier: AGPL-3.0-or-later |
||
5 | # |
||
6 | # This program is free software: you can redistribute it and/or modify |
||
7 | # it under the terms of the GNU Affero General Public License as |
||
8 | # published by the Free Software Foundation, either version 3 of the |
||
9 | # License, or (at your option) any later version. |
||
10 | # |
||
11 | # This program is distributed in the hope that it will be useful, |
||
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
14 | # GNU Affero General Public License for more details. |
||
15 | # |
||
16 | # You should have received a copy of the GNU Affero General Public License |
||
17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
||
18 | |||
19 | |||
20 | """ Access management for redis-based OpenVAS Scanner Database.""" |
||
21 | import logging |
||
22 | import sys |
||
23 | import time |
||
24 | |||
25 | from typing import List, NewType, Optional, Iterable, Iterator, Tuple |
||
26 | |||
27 | import redis |
||
28 | |||
29 | from ospd.errors import RequiredArgument |
||
30 | from ospd_openvas.errors import OspdOpenvasError |
||
31 | from ospd_openvas.openvas import Openvas |
||
32 | |||
33 | SOCKET_TIMEOUT = 60 # in seconds |
||
34 | LIST_FIRST_POS = 0 |
||
35 | LIST_LAST_POS = -1 |
||
36 | LIST_ALL = 0 |
||
37 | |||
38 | # Possible positions of nvt values in cache list. |
||
39 | NVT_META_FIELDS = [ |
||
40 | "NVT_FILENAME_POS", |
||
41 | "NVT_REQUIRED_KEYS_POS", |
||
42 | "NVT_MANDATORY_KEYS_POS", |
||
43 | "NVT_EXCLUDED_KEYS_POS", |
||
44 | "NVT_REQUIRED_UDP_PORTS_POS", |
||
45 | "NVT_REQUIRED_PORTS_POS", |
||
46 | "NVT_DEPENDENCIES_POS", |
||
47 | "NVT_TAGS_POS", |
||
48 | "NVT_CVES_POS", |
||
49 | "NVT_BIDS_POS", |
||
50 | "NVT_XREFS_POS", |
||
51 | "NVT_CATEGORY_POS", |
||
52 | "NVT_TIMEOUT_POS", |
||
53 | "NVT_FAMILY_POS", |
||
54 | "NVT_NAME_POS", |
||
55 | ] |
||
56 | |||
57 | # Name of the namespace usage bitmap in redis. |
||
58 | DBINDEX_NAME = "GVM.__GlobalDBIndex" |
||
59 | |||
60 | logger = logging.getLogger(__name__) |
||
61 | |||
62 | # Types |
||
63 | RedisCtx = NewType('RedisCtx', redis.Redis) |
||
64 | |||
65 | |||
66 | class OpenvasDB: |
||
67 | """Class to connect to redis, to perform queries, and to move |
||
68 | from a KB to another.""" |
||
69 | |||
70 | _db_address = None |
||
71 | |||
72 | @classmethod |
||
73 | def get_database_address(cls) -> Optional[str]: |
||
74 | if not cls._db_address: |
||
75 | settings = Openvas.get_settings() |
||
76 | |||
77 | cls._db_address = settings.get('db_address') |
||
78 | |||
79 | return cls._db_address |
||
80 | |||
81 | @classmethod |
||
82 | def create_context( |
||
83 | cls, dbnum: Optional[int] = 0, encoding: Optional[str] = 'latin-1' |
||
84 | ) -> RedisCtx: |
||
85 | """Connect to redis to the given database or to the default db 0 . |
||
86 | |||
87 | Arguments: |
||
88 | dbnum: The db number to connect to. |
||
89 | encoding: The encoding to be used to read and write. |
||
90 | |||
91 | Return a new redis context on success. |
||
92 | """ |
||
93 | tries = 5 |
||
94 | while tries: |
||
95 | try: |
||
96 | ctx = redis.Redis( |
||
97 | unix_socket_path=cls.get_database_address(), |
||
98 | db=dbnum, |
||
99 | socket_timeout=SOCKET_TIMEOUT, |
||
100 | encoding=encoding, |
||
101 | decode_responses=True, |
||
102 | ) |
||
103 | ctx.keys("test") |
||
104 | except (redis.exceptions.ConnectionError, FileNotFoundError) as err: |
||
105 | logger.debug( |
||
106 | 'Redis connection lost: %s. Trying again in 5 seconds.', err |
||
107 | ) |
||
108 | tries = tries - 1 |
||
109 | time.sleep(5) |
||
110 | continue |
||
111 | break |
||
112 | |||
113 | if not tries: |
||
114 | logger.error('Redis Error: Not possible to connect to the kb.') |
||
115 | sys.exit(1) |
||
116 | |||
117 | return ctx |
||
0 ignored issues
–
show
introduced
by
![]() |
|||
118 | |||
119 | @classmethod |
||
120 | def find_database_by_pattern( |
||
121 | cls, pattern: str, max_database_index: int |
||
122 | ) -> Tuple[Optional[RedisCtx], Optional[int]]: |
||
123 | """Search a pattern inside all kbs up to max_database_index. |
||
124 | |||
125 | Returns the redis context for the db and its index as a tuple or |
||
126 | None, None if the db with the pattern couldn't be found. |
||
127 | """ |
||
128 | for i in range(0, max_database_index): |
||
129 | ctx = cls.create_context(i) |
||
130 | if ctx.keys(pattern): |
||
131 | return (ctx, i) |
||
132 | |||
133 | return (None, None) |
||
134 | |||
135 | @staticmethod |
||
136 | def select_database(ctx: RedisCtx, kbindex: str): |
||
137 | """Use an existent redis connection and select a redis kb. |
||
138 | |||
139 | Arguments: |
||
140 | ctx: Redis context to use. |
||
141 | kbindex: The new kb to select |
||
142 | """ |
||
143 | if not ctx: |
||
144 | raise RequiredArgument('select_database', 'ctx') |
||
145 | if not kbindex: |
||
146 | raise RequiredArgument('select_database', 'kbindex') |
||
147 | |||
148 | ctx.execute_command('SELECT ' + str(kbindex)) |
||
149 | |||
150 | @staticmethod |
||
151 | def get_list_item( |
||
152 | ctx: RedisCtx, |
||
153 | name: str, |
||
154 | start: Optional[int] = LIST_FIRST_POS, |
||
155 | end: Optional[int] = LIST_LAST_POS, |
||
156 | ) -> Optional[list]: |
||
157 | """Returns the specified elements from `start` to `end` of the |
||
158 | list stored as `name`. |
||
159 | |||
160 | Arguments: |
||
161 | ctx: Redis context to use. |
||
162 | name: key name of a list. |
||
163 | start: first range element to get. |
||
164 | end: last range element to get. |
||
165 | |||
166 | Return List specified elements in the key. |
||
167 | """ |
||
168 | if not ctx: |
||
169 | raise RequiredArgument('get_list_item', 'ctx') |
||
170 | if not name: |
||
171 | raise RequiredArgument('get_list_item', 'name') |
||
172 | |||
173 | return ctx.lrange(name, start, end) |
||
174 | |||
175 | @staticmethod |
||
176 | def get_last_list_item(ctx: RedisCtx, name: str) -> str: |
||
177 | if not ctx: |
||
178 | raise RequiredArgument('get_last_list_item', 'ctx') |
||
179 | if not name: |
||
180 | raise RequiredArgument('get_last_list_item', 'name') |
||
181 | |||
182 | return ctx.rpop(name) |
||
183 | |||
184 | @staticmethod |
||
185 | def pop_list_items(ctx: RedisCtx, name: str) -> List[str]: |
||
186 | if not ctx: |
||
187 | raise RequiredArgument('pop_list_items', 'ctx') |
||
188 | if not name: |
||
189 | raise RequiredArgument('pop_list_items', 'name') |
||
190 | |||
191 | pipe = ctx.pipeline() |
||
192 | pipe.lrange(name, LIST_FIRST_POS, LIST_LAST_POS) |
||
193 | pipe.delete(name) |
||
194 | results, redis_return_code = pipe.execute() |
||
195 | |||
196 | # The results are left-pushed. To preserver the order |
||
197 | # the result list must be reversed. |
||
198 | if redis_return_code: |
||
199 | results.reverse() |
||
200 | else: |
||
201 | results = [] |
||
202 | |||
203 | return results |
||
204 | |||
205 | @staticmethod |
||
206 | def get_key_count(ctx: RedisCtx, pattern: Optional[str] = None) -> int: |
||
207 | """Get the number of keys matching with the pattern. |
||
208 | |||
209 | Arguments: |
||
210 | ctx: Redis context to use. |
||
211 | pattern: pattern used as filter. |
||
212 | """ |
||
213 | if not pattern: |
||
214 | pattern = "*" |
||
215 | |||
216 | if not ctx: |
||
217 | raise RequiredArgument('get_key_count', 'ctx') |
||
218 | |||
219 | return len(ctx.keys(pattern)) |
||
220 | |||
221 | @staticmethod |
||
222 | def remove_list_item(ctx: RedisCtx, key: str, value: str): |
||
223 | """Remove item from the key list. |
||
224 | |||
225 | Arguments: |
||
226 | ctx: Redis context to use. |
||
227 | key: key name of a list. |
||
228 | value: Value to be removed from the key. |
||
229 | """ |
||
230 | if not ctx: |
||
231 | raise RequiredArgument('remove_list_item ', 'ctx') |
||
232 | if not key: |
||
233 | raise RequiredArgument('remove_list_item', 'key') |
||
234 | if not value: |
||
235 | raise RequiredArgument('remove_list_item ', 'value') |
||
236 | |||
237 | ctx.lrem(key, count=LIST_ALL, value=value) |
||
238 | |||
239 | @staticmethod |
||
240 | def get_single_item( |
||
241 | ctx: RedisCtx, |
||
242 | name: str, |
||
243 | index: Optional[int] = LIST_FIRST_POS, |
||
244 | ) -> Optional[str]: |
||
245 | """Get a single KB element. |
||
246 | |||
247 | Arguments: |
||
248 | ctx: Redis context to use. |
||
249 | name: key name of a list. |
||
250 | index: index of the element to be return. |
||
251 | Defaults to the first element in the list. |
||
252 | |||
253 | Return the first element of the list or None if the name couldn't be |
||
254 | found. |
||
255 | """ |
||
256 | if not ctx: |
||
257 | raise RequiredArgument('get_single_item', 'ctx') |
||
258 | if not name: |
||
259 | raise RequiredArgument('get_single_item', 'name') |
||
260 | |||
261 | return ctx.lindex(name, index) |
||
262 | |||
263 | @staticmethod |
||
264 | def add_single_list(ctx: RedisCtx, name: str, values: Iterable): |
||
265 | """Add a single KB element with one or more values. |
||
266 | The values can be repeated. If the key already exists will |
||
267 | be removed an completely replaced. |
||
268 | |||
269 | Arguments: |
||
270 | ctx: Redis context to use. |
||
271 | name: key name of a list. |
||
272 | value: Elements to add to the key. |
||
273 | """ |
||
274 | if not ctx: |
||
275 | raise RequiredArgument('add_single_list', 'ctx') |
||
276 | if not name: |
||
277 | raise RequiredArgument('add_single_list', 'name') |
||
278 | if not values: |
||
279 | raise RequiredArgument('add_single_list', 'value') |
||
280 | |||
281 | pipe = ctx.pipeline() |
||
282 | pipe.delete(name) |
||
283 | pipe.rpush(name, *values) |
||
284 | pipe.execute() |
||
285 | |||
286 | @staticmethod |
||
287 | def add_single_item(ctx: RedisCtx, name: str, values: Iterable): |
||
288 | """Add a single KB element with one or more values. Don't add |
||
289 | duplicated values during this operation, but if the the same |
||
290 | values already exists under the key, this will not be overwritten. |
||
291 | |||
292 | Arguments: |
||
293 | ctx: Redis context to use. |
||
294 | name: key name of a list. |
||
295 | value: Elements to add to the key. |
||
296 | """ |
||
297 | if not ctx: |
||
298 | raise RequiredArgument('add_single_item', 'ctx') |
||
299 | if not name: |
||
300 | raise RequiredArgument('add_single_item', 'name') |
||
301 | if not values: |
||
302 | raise RequiredArgument('add_single_item', 'value') |
||
303 | |||
304 | ctx.rpush(name, *set(values)) |
||
305 | |||
306 | @staticmethod |
||
307 | def set_single_item(ctx: RedisCtx, name: str, value: Iterable): |
||
308 | """Set (replace) a single KB element. If the same key exists |
||
309 | in the kb, it is completed removed. Values added are unique. |
||
310 | |||
311 | Arguments: |
||
312 | ctx: Redis context to use. |
||
313 | name: key name of a list. |
||
314 | value: New elements to add to the key. |
||
315 | """ |
||
316 | if not ctx: |
||
317 | raise RequiredArgument('set_single_item', 'ctx') |
||
318 | if not name: |
||
319 | raise RequiredArgument('set_single_item', 'name') |
||
320 | if not value: |
||
321 | raise RequiredArgument('set_single_item', 'value') |
||
322 | |||
323 | pipe = ctx.pipeline() |
||
324 | pipe.delete(name) |
||
325 | pipe.rpush(name, *set(value)) |
||
326 | pipe.execute() |
||
327 | |||
328 | @staticmethod |
||
329 | def get_pattern(ctx: RedisCtx, pattern: str) -> List: |
||
330 | """Get all items stored under a given pattern. |
||
331 | |||
332 | Arguments: |
||
333 | ctx: Redis context to use. |
||
334 | pattern: key pattern to match. |
||
335 | |||
336 | Return a list with the elements under the matched key. |
||
337 | """ |
||
338 | if not ctx: |
||
339 | raise RequiredArgument('get_pattern', 'ctx') |
||
340 | if not pattern: |
||
341 | raise RequiredArgument('get_pattern', 'pattern') |
||
342 | |||
343 | items = ctx.keys(pattern) |
||
344 | |||
345 | elem_list = [] |
||
346 | for item in items: |
||
347 | elem_list.append( |
||
348 | [ |
||
349 | item, |
||
350 | ctx.lrange(item, start=LIST_FIRST_POS, end=LIST_LAST_POS), |
||
351 | ] |
||
352 | ) |
||
353 | return elem_list |
||
354 | |||
355 | @classmethod |
||
356 | def get_keys_by_pattern(cls, ctx: RedisCtx, pattern: str) -> List[str]: |
||
357 | """Get all items with index 'index', stored under |
||
358 | a given pattern. |
||
359 | |||
360 | Arguments: |
||
361 | ctx: Redis context to use. |
||
362 | pattern: key pattern to match. |
||
363 | |||
364 | Return a sorted list with the elements under the matched key |
||
365 | """ |
||
366 | if not ctx: |
||
367 | raise RequiredArgument('get_elem_pattern_by_index', 'ctx') |
||
368 | if not pattern: |
||
369 | raise RequiredArgument('get_elem_pattern_by_index', 'pattern') |
||
370 | |||
371 | return sorted(ctx.keys(pattern)) |
||
372 | |||
373 | @classmethod |
||
374 | def get_filenames_and_oids( |
||
375 | cls, |
||
376 | ctx: RedisCtx, |
||
377 | ) -> Iterable[Tuple[str, str]]: |
||
378 | """Get all items with index 'index', stored under |
||
379 | a given pattern. |
||
380 | |||
381 | Arguments: |
||
382 | ctx: Redis context to use. |
||
383 | |||
384 | Return an iterable where each single tuple contains the filename |
||
385 | as first element and the oid as the second one. |
||
386 | """ |
||
387 | if not ctx: |
||
388 | raise RequiredArgument('get_filenames_and_oids', 'ctx') |
||
389 | |||
390 | items = cls.get_keys_by_pattern(ctx, 'nvt:*') |
||
391 | |||
392 | return ((ctx.lindex(item, 0), item[4:]) for item in items) |
||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||
393 | |||
394 | |||
395 | class BaseDB: |
||
396 | def __init__(self, kbindex: int, ctx: Optional[RedisCtx] = None): |
||
397 | if ctx is None: |
||
398 | self.ctx = OpenvasDB.create_context(kbindex) |
||
399 | else: |
||
400 | self.ctx = ctx |
||
401 | |||
402 | self.index = kbindex |
||
403 | |||
404 | def flush(self): |
||
405 | """Flush the database""" |
||
406 | self.ctx.flushdb() |
||
407 | |||
408 | |||
409 | class BaseKbDB(BaseDB): |
||
410 | def _add_single_item( |
||
411 | self, name: str, values: Iterable, utf8_enc: Optional[bool] = False |
||
412 | ): |
||
413 | """Changing the encoding format of an existing redis context |
||
414 | is not possible. Therefore a new temporary redis context is |
||
415 | created to store key-values encoded with utf-8.""" |
||
416 | if utf8_enc: |
||
417 | ctx = OpenvasDB.create_context(self.index, encoding='utf-8') |
||
418 | OpenvasDB.add_single_item(ctx, name, values) |
||
419 | else: |
||
420 | OpenvasDB.add_single_item(self.ctx, name, values) |
||
421 | |||
422 | def _set_single_item(self, name: str, value: Iterable): |
||
423 | """Set (replace) a single KB element. |
||
424 | |||
425 | Arguments: |
||
426 | name: key name of a list. |
||
427 | value: New elements to add to the key. |
||
428 | """ |
||
429 | OpenvasDB.set_single_item(self.ctx, name, value) |
||
430 | |||
431 | def _get_single_item(self, name: str) -> Optional[str]: |
||
432 | """Get a single KB element. |
||
433 | |||
434 | Arguments: |
||
435 | name: key name of a list. |
||
436 | """ |
||
437 | return OpenvasDB.get_single_item(self.ctx, name) |
||
438 | |||
439 | def _get_list_item( |
||
440 | self, |
||
441 | name: str, |
||
442 | ) -> Optional[List]: |
||
443 | """Returns the specified elements from `start` to `end` of the |
||
444 | list stored as `name`. |
||
445 | |||
446 | Arguments: |
||
447 | name: key name of a list. |
||
448 | |||
449 | Return List specified elements in the key. |
||
450 | """ |
||
451 | return OpenvasDB.get_list_item(self.ctx, name) |
||
452 | |||
453 | def _pop_list_items(self, name: str) -> List: |
||
454 | return OpenvasDB.pop_list_items(self.ctx, name) |
||
455 | |||
456 | def _remove_list_item(self, key: str, value: str): |
||
457 | """Remove item from the key list. |
||
458 | |||
459 | Arguments: |
||
460 | key: key name of a list. |
||
461 | value: Value to be removed from the key. |
||
462 | """ |
||
463 | OpenvasDB.remove_list_item(self.ctx, key, value) |
||
464 | |||
465 | def get_result(self) -> Optional[str]: |
||
466 | """Get and remove the oldest result from the list. |
||
467 | |||
468 | Return the oldest scan results |
||
469 | """ |
||
470 | return self._pop_list_items("internal/results") |
||
471 | |||
472 | def get_status(self, openvas_scan_id: str) -> Optional[str]: |
||
473 | """Return the status of the host scan""" |
||
474 | return self._get_single_item('internal/{}'.format(openvas_scan_id)) |
||
475 | |||
476 | def __repr__(self): |
||
477 | return '<{} index={}>'.format(self.__class__.__name__, self.index) |
||
478 | |||
479 | |||
480 | class ScanDB(BaseKbDB): |
||
481 | """Database for a scanning a single host""" |
||
482 | |||
483 | def select(self, kbindex: int) -> "ScanDB": |
||
484 | """Select a redis kb. |
||
485 | |||
486 | Arguments: |
||
487 | kbindex: The new kb to select |
||
488 | """ |
||
489 | OpenvasDB.select_database(self.ctx, kbindex) |
||
490 | self.index = kbindex |
||
491 | return self |
||
492 | |||
493 | |||
494 | class KbDB(BaseKbDB): |
||
495 | def get_scan_databases(self) -> Iterator[ScanDB]: |
||
496 | """Returns an iterator yielding corresponding ScanDBs |
||
497 | |||
498 | The returned Iterator can't be converted to an Iterable like a List. |
||
499 | Each yielded ScanDB must be used independently in a for loop. If the |
||
500 | Iterator gets converted into an Iterable all returned ScanDBs will use |
||
501 | the same redis context pointing to the same redis database. |
||
502 | """ |
||
503 | dbs = self._get_list_item('internal/dbindex') |
||
504 | scan_db = ScanDB(self.index) |
||
505 | for kbindex in dbs: |
||
506 | if kbindex == self.index: |
||
507 | continue |
||
508 | |||
509 | yield scan_db.select(kbindex) |
||
510 | |||
511 | def add_scan_id(self, scan_id: str): |
||
512 | self._add_single_item('internal/{}'.format(scan_id), ['new']) |
||
513 | self._add_single_item('internal/scanid', [scan_id]) |
||
514 | |||
515 | def add_scan_preferences(self, openvas_scan_id: str, preferences: Iterable): |
||
516 | self._add_single_item( |
||
517 | 'internal/{}/scanprefs'.format(openvas_scan_id), preferences |
||
518 | ) |
||
519 | |||
520 | def add_credentials_to_scan_preferences( |
||
521 | self, openvas_scan_id: str, preferences: Iterable |
||
522 | ): |
||
523 | """Force the usage of the utf-8 encoding, since some credentials |
||
524 | contain special chars not supported by latin-1 encoding.""" |
||
525 | self._add_single_item( |
||
526 | 'internal/{}/scanprefs'.format(openvas_scan_id), |
||
527 | preferences, |
||
528 | utf8_enc=True, |
||
529 | ) |
||
530 | |||
531 | def add_scan_process_id(self, pid: int): |
||
532 | self._add_single_item('internal/ovas_pid', [pid]) |
||
533 | |||
534 | def get_scan_process_id(self) -> Optional[str]: |
||
535 | return self._get_single_item('internal/ovas_pid') |
||
536 | |||
537 | def remove_scan_database(self, scan_db: ScanDB): |
||
538 | self._remove_list_item('internal/dbindex', scan_db.index) |
||
539 | |||
540 | def target_is_finished(self, scan_id: str) -> bool: |
||
541 | """Check if a target has finished.""" |
||
542 | |||
543 | status = self._get_single_item('internal/{}'.format(scan_id)) |
||
544 | |||
545 | if status is None: |
||
546 | logger.error( |
||
547 | "%s: Target set as finished because redis returned None as " |
||
548 | "scanner status.", |
||
549 | scan_id, |
||
550 | ) |
||
551 | |||
552 | return status == 'finished' or status is None |
||
553 | |||
554 | def stop_scan(self, openvas_scan_id: str): |
||
555 | self._set_single_item( |
||
556 | 'internal/{}'.format(openvas_scan_id), ['stop_all'] |
||
557 | ) |
||
558 | |||
559 | def scan_is_stopped(self, scan_id: str) -> bool: |
||
560 | """Check if the scan should be stopped""" |
||
561 | status = self._get_single_item('internal/%s' % scan_id) |
||
562 | return status == 'stop_all' |
||
563 | |||
564 | def get_scan_status(self) -> List: |
||
565 | """Get and remove the oldest host scan status from the list. |
||
566 | |||
567 | Return a string which represents the host scan status. |
||
568 | """ |
||
569 | return self._pop_list_items("internal/status") |
||
570 | |||
571 | |||
572 | class MainDB(BaseDB): |
||
573 | """Main Database""" |
||
574 | |||
575 | DEFAULT_INDEX = 0 |
||
576 | |||
577 | def __init__(self, ctx=None): |
||
578 | super().__init__(self.DEFAULT_INDEX, ctx) |
||
579 | |||
580 | self._max_dbindex = None |
||
581 | |||
582 | @property |
||
583 | def max_database_index(self): |
||
584 | """Set the number of databases have been configured into kbr struct.""" |
||
585 | if self._max_dbindex is None: |
||
586 | resp = self.ctx.config_get('databases') |
||
587 | |||
588 | if len(resp) == 1: |
||
589 | self._max_dbindex = int(resp.get('databases')) |
||
590 | else: |
||
591 | raise OspdOpenvasError( |
||
592 | 'Redis Error: Not possible to get max_dbindex.' |
||
593 | ) from None |
||
594 | |||
595 | return self._max_dbindex |
||
596 | |||
597 | def try_database(self, index: int) -> bool: |
||
598 | """Check if a redis db is already in use. If not, set it |
||
599 | as in use and return. |
||
600 | |||
601 | Arguments: |
||
602 | ctx: Redis object connected to the kb with the |
||
603 | DBINDEX_NAME key. |
||
604 | index: Number intended to be used. |
||
605 | |||
606 | Return True if it is possible to use the db. False if the given db |
||
607 | number is already in use. |
||
608 | """ |
||
609 | _in_use = 1 |
||
610 | try: |
||
611 | resp = self.ctx.hsetnx(DBINDEX_NAME, index, _in_use) |
||
612 | except: |
||
613 | raise OspdOpenvasError( |
||
614 | 'Redis Error: Not possible to set %s.' % DBINDEX_NAME |
||
615 | ) from None |
||
616 | |||
617 | return resp == 1 |
||
618 | |||
619 | def get_new_kb_database(self) -> Optional[KbDB]: |
||
620 | """Return a new kb db to an empty kb.""" |
||
621 | for index in range(1, self.max_database_index): |
||
622 | if self.try_database(index): |
||
623 | kbdb = KbDB(index) |
||
624 | kbdb.flush() |
||
625 | return kbdb |
||
626 | |||
627 | return None |
||
628 | |||
629 | def find_kb_database_by_scan_id( |
||
630 | self, scan_id: str |
||
631 | ) -> Tuple[Optional[str], Optional["KbDB"]]: |
||
632 | """Find a kb db by via a scan id""" |
||
633 | for index in range(1, self.max_database_index): |
||
634 | ctx = OpenvasDB.create_context(index) |
||
635 | if OpenvasDB.get_key_count(ctx, 'internal/{}'.format(scan_id)): |
||
636 | return KbDB(index, ctx) |
||
637 | |||
638 | return None |
||
639 | |||
640 | def release_database(self, database: BaseDB): |
||
641 | self.release_database_by_index(database.index) |
||
642 | database.flush() |
||
643 | |||
644 | def release_database_by_index(self, index: int): |
||
645 | self.ctx.hdel(DBINDEX_NAME, index) |
||
646 | |||
647 | def release(self): |
||
648 | self.release_database(self) |
||
649 |