Skip to content

Commit

Permalink
Merge pull request #1698 from greenbone/async-interpreter
Browse files Browse the repository at this point in the history
-- Structure of Scanner Code --
This PR adds the `Scanner` type in the `nasl_interpreter` crate. (It would be nicer to have this in a different crate but I don't want to add a new one. Maybe in the future if we ever merge some of the crates, this can be moved/renamed. ) The `Scanner` is the single instance managing all scans during a run with `Openvasd` scanner type. To do so, it starts a number of `RunningScan`s, each of which is responsible for a single `Scan`.
The `RunningScan` is responsible for computing the execution plan for this particular `Scan` and running it to completion. It also takes care of controlling the status of the scan and stopping it if necessary. Internally, it runs code via the `ScanRunner` (sorry, I ran out of names) which is responsible for performing all VTs in all stages on each target host. It is also responsible for sticking to the scheduling requirements. I suspect that this part of the code is where we should be able to easily improve performance by making the scripts run concurrently.
Finally, for a given VT and a given Host, the VT is then run to completion using the `VTRunner`.

-- Tests --
Since all of our NASL test employed the `Iterator` impl on `CodeInterpreter` which cannot exist anymore (since the `CodeInterpreter` is now async), they all needed to be changed. I took this as an opportunity to move the tests to a more unified structure, so that it will be easier to 1. add new tests and 2. change how the tests are done internally without having to make changes all over the code base.

Here is an example how the tests work now:
```rust
let mut t = TestBuilder::default();
t.run("l = [1,2,3,4,5];");
t.ok("max_index(l);", 5);
```
The `TestBuilder` struct can be used to set up the proper `Context`, `Storage` and function `Loader` and has a number of utility functions to perform common tests.
Lines of code can be added to the `TestBuilder` one-by-one, along with certain requirements on their results (For example, the `t.ok("max_index(l)", 5)` above will check that the result of the `max_index(l)` call is `NaslValue::Number(5)`.). At the end of the test, the `TestBuilder` will automatically check that all of the requirements are fulfilled. Even though lines are added individually, the `TestBuilder` keeps track of the state of the `Context` during execution, so tests that require multiple lines to set up (like the one above) will still work as intended.

To test that the correct `Err` variant is returned in certain lines, the `check_err_matches!` macro can be used as before, by passing it the `TestBuilder` as an argument:

```rust
check_err_matches!(
    t,
    r#"typeof(23,76);"#,
    FunctionErrorKind::TrailingPositionalArguments { .. }
);
```

To quickly check individual lines, `check_ok` still exists.

-- Function executor - -
Previously, every set of NASL functions was added to the standard library by implementing the `NaslFunctionExecutor` trait for a struct (that might be either a marker ZST or a proper stateful struct). This approach was difficult to incorporate with the new `async` interpreter where NASL functions might be both sync or async, since async functions can't really be referred to by a simple type like `fn(&Context, &Register) -> NaslResult` like sync functions can.
Instead, the executor part of the `Context` is now an actual struct: `Executor`. This internally tracks all the registered functions and keeps all the necessary state (such as open ssh connections) around. Moreover, it provides the same functionality that `NaslFunctionExecutor` provided previously (namely executing functions by name).

The `Executor` can store multiple `FunctionSet`s which are collections as functions. A new `FunctionSet` can be created as such:
```rust
pub struct Array;
function_set! {
    Array,
    sync_stateless,
    (
        make_array,
        make_list,
        (nasl_sort, "sort"),
        keys,
        max_index,
    )
}
```
which automatically implements the `IntoFunctionSet` trait on `Array` and adds the given 5 functions. The functions will automatically be entered with their own name unless specified otherwise (as for `nasl_sort` here). This replaces the `lookup` functions we had to write before.

At this point, NASL functions come in four flavors:

1. `async_stateful` (for `async fn(&S, &Register, &Context)`)
2. `sync_stateful` (for `fn(&S, &Register, &Context)`)
3. `async_stateless` (for `async fn(&Register, &Context)`)
4. `sync_stateless` (for `fn(&Register, &Context)`)

At the moment, the user (i.e. the person using the `function_set!` macro) unfortunately needs to specify what flavour of function the set contains. (This is because the rust compiler does not know that something implementing `Fn(A, B) -> Z` will never also implement `Fn(A, B, C) -> Z`, so that implementing a common trait for all four flavours is impossible since the compiler will think that we implemented the trait multiple times for a single object. Unfortunately, specialization is not yet a thing in Rust.).

In the future, I think it should be possible to do something very cool with the `Executor`: Previously, we only got read access to the state (i.e. to the SSH handles), so we had to employ interior mutability in order to work with this state if we ever needed mutable access (in order to add a new SSH handle to our list of handles, for example). Since all the states are now managed by a `StatefulFunctionSet`, and we can tell whether a given function needs mutable access or not by its signature, I think we should be able to use an external RwLock and improve this situation with some careful logic. Concretely, this could mean having `Vec<Arc<Mutex<SSHSession>>>` instead of `Arc<Mutex<Vec<SSHSession>>>` which we previously needed, which would enable tons of concurrency that wasn't previously possible. This is pretty futuristic at this point though.

-- Regressions --
- Removed ability to have a generic reader in `HashSumLoader`. This was done for simplicity and since we currently don't use this ability, however it can be reverted if necessary by adding `Send` bounds on the trait object.
- Removed the ability to have a generic `NaslFunctionExecutor` and replaced it with a `Executor` which is a concrete struct. We still have the ability to only register any subset of functions that we want, so I don't think this is a true regression, but I'm writing it down for completeness' sake.
- Previously, the `NaslFunctionExecutor` trait had a `nasl_fn_cache_clear` method. This wasn't really called properly anywhere other than in `scannerctl` and wasn't handled properly in the `std` library either. I don't think there are currently any instances that needed this functionality, since RAII should take care of it. However, if we ever need this, it is very easy to add back in again.
- I had to revamp how `HostInfo` works to clean up the logic and make it possible to deal with asynchronous scans. First of all, I noticed that `HostInfo` internally stored something which I believe was supposed to be some kind of percentage progress. However, this progress was never actually updated or treated in any way. I removed this percentage value for now. This has an impact on how the update of a `HostInfo` works when a new result comes in and `scanner.do_addition()` returns true. I don't know what the expected behavior is there and since all tests still pass, I will just leave this as it is for now. Secondly, the new `HostInfo` stores the number of leftover VTs for each target host. In this way, it can properly track which hosts still have running VTs and which ones are finished. This was necessary because we now start multiple VTs at the same time, whereas previously they were done one by one.
  • Loading branch information
Tehforsch authored Sep 5, 2024
2 parents cfc22f8 + d4a0bac commit 8cb02bd
Show file tree
Hide file tree
Showing 142 changed files with 7,118 additions and 6,607 deletions.
452 changes: 273 additions & 179 deletions rust/Cargo.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ members = [
"nasl-function-proc-macro",
]

[workspace.dependencies]
tokio = { version = "1.39.3", features = ["full"] }
futures = "0.3.30"

[workspace.package]
version = "0.1.0"
edition = "2021"
Expand Down
18 changes: 18 additions & 0 deletions rust/examples/feed/nasl/error_message.nasl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
if (description)
{
script_oid("0.0.0.0.0.0.0.0.0.5");
script_version("2023-02-23T13:33:44+0000");
script_tag(name:"last_modification", value:"2020-12-07 13:33:44 +0000 (Mon, 07 Dec 2020)");
script_tag(name:"creation_date", value:"2009-05-12 22:04:51 +0200 (Tue, 12 May 2009)");
script_tag(name:"cvss_base_vector", value:"AV:N/AC:L/Au:N/C:N/I:N/A:N");
script_name("Application Server Detection (HTTP)");
script_category(ACT_ATTACK);
script_tag(name:"qod_type", value:"remote_banner");
script_family("Product detection");
script_copyright("Copyright (C) 2023 Greenbone AG");
script_tag(name:"summary", value:"HTTP AS detection");
script_xref(name:"URL", value:"https://greenbone.net");
script_dependencies("1.nasl");
exit(0);
}
error_message(data: "0.0.0.0.0.0.0.0.0.5", uri: "file:///tmp/openvasd/oids");
22 changes: 22 additions & 0 deletions rust/examples/feed/nasl/http2_get.nasl
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
if (description)
{
script_oid("0.0.0.0.0.0.0.0.0.6");
script_version("2023-02-23T13:33:44+0000");
script_tag(name:"last_modification", value:"2020-12-07 13:33:44 +0000 (Mon, 07 Dec 2020)");
script_tag(name:"creation_date", value:"2009-05-12 22:04:51 +0200 (Tue, 12 May 2009)");
script_tag(name:"cvss_base_vector", value:"AV:N/AC:L/Au:N/C:N/I:N/A:N");
script_name("Application Server Detection (HTTP)");
script_category(ACT_GATHER_INFO);
script_tag(name:"qod_type", value:"remote_banner");
script_family("Product detection");
script_copyright("Copyright (C) 2023 Greenbone AG");
script_tag(name:"summary", value:"HTTP AS detection");
script_xref(name:"URL", value:"https://greenbone.net");
exit(0);
}

h = http2_handle();
r = http2_get(handle:h, port:3000, item:"/vts", schema:"http");
log_message(data: r);
rc = http2_get_response_code(handle:h);
log_message(data: rc);
18 changes: 18 additions & 0 deletions rust/examples/feed/nasl/log_message.nasl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
if (description)
{
script_oid("0.0.0.0.0.0.0.0.0.3");
script_version("2023-02-23T13:33:44+0000");
script_tag(name:"last_modification", value:"2020-12-07 13:33:44 +0000 (Mon, 07 Dec 2020)");
script_tag(name:"creation_date", value:"2009-05-12 22:04:51 +0200 (Tue, 12 May 2009)");
script_tag(name:"cvss_base_vector", value:"AV:N/AC:L/Au:N/C:N/I:N/A:N");
script_name("Application Server Detection (HTTP)");
script_category(ACT_ATTACK);
script_tag(name:"qod_type", value:"remote_banner");
script_family("Product detection");
script_copyright("Copyright (C) 2023 Greenbone AG");
script_tag(name:"summary", value:"HTTP AS detection");
script_xref(name:"URL", value:"https://greenbone.net");
script_dependencies("1.nasl");
exit(0);
}
log_message(data: "0.0.0.0.0.0.0.0.0.3", uri: "file:///tmp/openvasd/oids");
18 changes: 18 additions & 0 deletions rust/examples/feed/nasl/security_message.nasl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
if (description)
{
script_oid("0.0.0.0.0.0.0.0.0.4");
script_version("2023-02-23T13:33:44+0000");
script_tag(name:"last_modification", value:"2020-12-07 13:33:44 +0000 (Mon, 07 Dec 2020)");
script_tag(name:"creation_date", value:"2009-05-12 22:04:51 +0200 (Tue, 12 May 2009)");
script_tag(name:"cvss_base_vector", value:"AV:N/AC:L/Au:N/C:N/I:N/A:N");
script_name("Application Server Detection (HTTP)");
script_category(ACT_ATTACK);
script_tag(name:"qod_type", value:"remote_banner");
script_family("Product detection");
script_copyright("Copyright (C) 2023 Greenbone AG");
script_tag(name:"summary", value:"HTTP AS detection");
script_xref(name:"URL", value:"https://greenbone.net");
script_dependencies("1.nasl");
exit(0);
}
security_message(data: "0.0.0.0.0.0.0.0.0.4", uri: "file:///tmp/openvasd/oids");
7 changes: 5 additions & 2 deletions rust/examples/feed/nasl/sha256sums
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
1685505110659defb2d310f54950128fa5cc8867f89ff35cb647228e091e87e9 ./1.nasl
2e21d3f6973e02e74ebc10dcc4ca77e7fbe414d6a8b985b2e0cda0111199a6aa ./plugin_feed_info.inc
dd086316af60a82f1e4a8fe4fc8a92885a81bc538a55df1528cc509bc3e13c72 ./sha256sums
88924cd3cbec2b070afd45fcd4aabd9096763379eb816c200b3dc4abfae96b08 ./2.nasl
2e21d3f6973e02e74ebc10dcc4ca77e7fbe414d6a8b985b2e0cda0111199a6aa ./plugin_feed_info.inc
35c0de9dcf2cdf84d3f7731a9ca036bb6ad97bbbbc595bb3c0c90094c1e8577e ./log_message.nasl
82b8531619fc6518c31f04b29207b702098357b2bd7538642d063488447d5b2e ./security_message.nasl
231eda974fc891bc72dd53a9923cfeb1c39d6284e59ec7bf3dd941e48678f07e ./error_message.nasl
c22fd805b4cee8b230bf1b00a6ebfd0c44770784eacc89edef18edcac2c77a31 ./http2_get.nasl
5 changes: 5 additions & 0 deletions rust/examples/feed/nasl/sha256sums.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# This script creates a sha256sums over the nasl and inc files within this dir.
#!/bin/sh
PWD=$(pwd)
set -e
find . -type f -regex ".*\.\(nasl\|inc\)\$" -exec sha256sum {} \; | tee sha256sums
2 changes: 1 addition & 1 deletion rust/examples/openvasd/config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ key = "/var/lib/openvasd/tls/server.rsa"
client_certs = "/etc/openvasd/tls/client"

[scanner]
# currently only ospd is supported, that may change.
# Supported types: ospd, openvas, openvasd
type = "ospd"

[scanner.ospd]
Expand Down
17 changes: 17 additions & 0 deletions rust/examples/openvasd/example-feed-scan.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"scan_id": "0",
"target": {
"hosts": [
"127.0.0.1"
Expand All @@ -24,9 +25,25 @@
},
"scanner_preferences": [],
"vts": [
{
"oid": "0.0.0.0.0.0.0.0.0.1",
"parameters": []
},
{
"oid": "0.0.0.0.0.0.0.0.0.2",
"parameters": []
},
{
"oid": "0.0.0.0.0.0.0.0.0.3",
"parameters": []
},
{
"oid": "0.0.0.0.0.0.0.0.0.4",
"parameters": []
},
{
"oid": "0.0.0.0.0.0.0.0.0.5",
"parameters": []
}
]
}
2 changes: 2 additions & 0 deletions rust/feed/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ tracing = "0.1.37"
glob = "0.3.1"
serde = { version = "1.0", features = ["derive"]}
thiserror = "1.0.62"
futures = { workspace = true }
tokio = { workspace = true }

[dev-dependencies]
toml = "0.8.8"
22 changes: 0 additions & 22 deletions rust/feed/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,28 +30,6 @@ let loader = FSPluginLoader::new(path);
let verifier = feed::HashSumNameLoader::sha256(&loader).expect("sha256sums");
```

## Update

Is [implemented](./src/update/mod.rs) as a Iterator over String and [UpdateError](./src/update/error.rs) and utilizes an iterator over filenames as String and [VerifyError](./src/verify/mod.rs).

### Example

```no_run
use nasl_interpreter::{Interpreter, FSPluginLoader, Register};
use storage::DefaultDispatcher;
let storage: DefaultDispatcher = DefaultDispatcher::new(false);
let path = "/var/lib/openvas/plugins/";
let loader = FSPluginLoader::new(path);
let verifier = feed::HashSumNameLoader::sha256(&loader).expect("sha256sums");
let max_retries = 5;
let openvas_version = "1";
let updater = feed::Update::init(openvas_version, max_retries, &loader, &storage, verifier);
for s in updater {
println!("updated {s:?}");
}
```

## Current status

Only feed update is implemented.
Expand Down
1 change: 0 additions & 1 deletion rust/feed/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later WITH x11vnc-openssl-exception

#![doc = include_str!("../README.md")]
#![warn(missing_docs)]
mod oid;
pub mod transpile;
mod update;
Expand Down
12 changes: 5 additions & 7 deletions rust/feed/src/oid/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

//! Is a module to get oids within a feed
use std::{fs::File, io::Read};
use std::fs::File;

use nasl_interpreter::{AsBufReader, Loader};
use nasl_syntax::{IdentifierType, Statement, StatementKind, TokenCategory};
Expand All @@ -20,11 +20,10 @@ pub struct Oid<L, V> {
loader: L,
verifier: V,
}
impl<'a, L, V, R> Oid<L, V>
impl<'a, L, V> Oid<L, V>
where
L: Sync + Send + Loader + AsBufReader<File>,
V: Iterator<Item = Result<HashSumFileItem<'a, R>, verify::Error>>,
R: Read + 'a,
V: Iterator<Item = Result<HashSumFileItem<'a>, verify::Error>>,
{
/// Creates an oid finder. Returns a tuple of (filename, oid).
///
Expand Down Expand Up @@ -71,11 +70,10 @@ where
}
}

impl<'a, L, V, R> Iterator for Oid<L, V>
impl<'a, L, V> Iterator for Oid<L, V>
where
L: Sync + Send + Loader + AsBufReader<File>,
V: Iterator<Item = Result<HashSumFileItem<'a, R>, verify::Error>>,
R: Read + 'a,
V: Iterator<Item = Result<HashSumFileItem<'a>, verify::Error>>,
{
type Item = Result<(String, String), update::Error>;

Expand Down
Loading

0 comments on commit 8cb02bd

Please sign in to comment.