diff --git a/rust/Cargo.lock b/rust/Cargo.lock index a7854f5dc6..ddc10aaa2a 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -107,9 +107,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2faccea4cc4ab4a667ce676a30e8ec13922a692c99bb8f5b11f1502c72e04220" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "anstyle-parse" @@ -432,9 +432,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.18" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" +checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f" dependencies = [ "clap_builder", "clap_derive", @@ -442,9 +442,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.18" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" +checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99" dependencies = [ "anstream", "anstyle", @@ -454,9 +454,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" dependencies = [ "heck", "proc-macro2", @@ -466,9 +466,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "cmac" @@ -816,7 +816,7 @@ dependencies = [ "serde", "sha2", "storage", - "toml 0.8.8", + "toml 0.8.10", "tracing", ] @@ -1089,9 +1089,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" +checksum = "d0c62115964e08cb8039170eb33c1d0e2388a256930279edca206fff675f82c3" [[package]] name = "hex" @@ -1255,9 +1255,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdea9aac0dbe5a9240d68cfd9501e2db94222c6dc06843e06640b9e07f0fdc67" +checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" dependencies = [ "bytes", "futures-channel", @@ -1275,9 +1275,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.59" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1308,9 +1308,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.1" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433de089bd45971eecf4668ee0ee8f4cec17db4f8bd8f7bc3197a6ce37aa7d9b" +checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" dependencies = [ "equivalent", "hashbrown", @@ -1358,12 +1358,12 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" +checksum = "fe8f25ce1159c7740ff0b9b2f5cdf4a8428742ba7c112b9f20f22cd5219c7dab" dependencies = [ "hermit-abi", - "rustix", + "libc", "windows-sys 0.52.0", ] @@ -1393,9 +1393,9 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" -version = "0.3.67" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" +checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" dependencies = [ "wasm-bindgen", ] @@ -1467,9 +1467,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.152" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libgcrypt-sys" @@ -1611,9 +1611,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] @@ -1796,7 +1796,7 @@ dependencies = [ "serde", "serde_json", "storage", - "toml 0.8.8", + "toml 0.8.10", "tracing", "tracing-subscriber 0.3.18", "walkdir", @@ -1857,11 +1857,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] @@ -1962,6 +1968,9 @@ name = "openvas" version = "0.1.0" dependencies = [ "models", + "redis", + "redis-storage", + "storage", ] [[package]] @@ -2484,9 +2493,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.23" +version = "0.11.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" dependencies = [ "base64", "bytes", @@ -2510,6 +2519,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", "system-configuration", "tokio", "tokio-rustls 0.24.1", @@ -2575,9 +2585,9 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.30" +version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ "bitflags 2.4.2", "errno 0.3.8", @@ -2607,7 +2617,7 @@ dependencies = [ "log", "ring", "rustls-pki-types", - "rustls-webpki 0.102.1", + "rustls-webpki 0.102.2", "subtle", "zeroize", ] @@ -2646,9 +2656,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e9d979b3ce68192e42760c7810125eb6cf2ea10efae545a156063e61f314e2a" +checksum = "0a716eb65e3158e90e17cd93d855216e27bde02745ab842f2cab4a39dba1bacf" [[package]] name = "rustls-webpki" @@ -2662,9 +2672,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.1" +version = "0.102.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4ca26037c909dedb327b48c3327d0ba91d3dd3c4e05dad328f210ffb68e95b" +checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" dependencies = [ "ring", "rustls-pki-types", @@ -3005,9 +3015,9 @@ dependencies = [ [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" [[package]] name = "subtle" @@ -3037,6 +3047,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "system-configuration" version = "0.5.1" @@ -3060,13 +3076,12 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.9.0" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", "rustix", "windows-sys 0.52.0", ] @@ -3114,11 +3129,12 @@ dependencies = [ [[package]] name = "time" -version = "0.3.31" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" dependencies = [ "deranged", + "num-conv", "powerfmt", "serde", "time-core", @@ -3133,10 +3149,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" dependencies = [ + "num-conv", "time-core", ] @@ -3176,9 +3193,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.1" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", @@ -3254,14 +3271,14 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.21.0", + "toml_edit 0.22.4", ] [[package]] @@ -3288,9 +3305,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.21.0" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +checksum = "0c9ffdf896f8daaabf9b66ba8e77ea1ed5ed0f72821b398aba62352e95062951" dependencies = [ "indexmap", "serde", @@ -3595,9 +3612,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -3605,9 +3622,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" dependencies = [ "bumpalo", "log", @@ -3620,9 +3637,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.40" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" +checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" dependencies = [ "cfg-if", "js-sys", @@ -3632,9 +3649,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3642,9 +3659,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" dependencies = [ "proc-macro2", "quote", @@ -3655,15 +3672,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" +checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" [[package]] name = "web-sys" -version = "0.3.67" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" +checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" dependencies = [ "js-sys", "wasm-bindgen", @@ -3671,9 +3688,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.3" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "winapi" @@ -3892,9 +3909,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.35" +version = "0.5.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1931d78a9c73861da0134f453bb1f790ce49b2e30eba8410b4b79bac72b46a2d" +checksum = "5389a154b01683d28c77f8f68f49dea75f0a4da32557a58f68ee51ebba472d29" dependencies = [ "memchr", ] diff --git a/rust/models/src/credential.rs b/rust/models/src/credential.rs index 03a7f06e2a..d1f13b2a87 100644 --- a/rust/models/src/credential.rs +++ b/rust/models/src/credential.rs @@ -17,6 +17,7 @@ pub struct Credential { #[cfg_attr(feature = "serde_support", serde(flatten))] /// Type of the credential to get access. Different services support different types. pub credential_type: CredentialType, + } impl Credential { @@ -75,12 +76,16 @@ pub enum Service { #[cfg_attr(feature = "serde_support", serde(rename = "snmp"))] /// SNMP, supports [SNMP](CredentialType::SNMP) SNMP, + #[cfg_attr(feature = "serde_support", serde(rename = "privilege_ssh"))] + /// Privilege SSH, supports [SSH](CredentialType::UP) + PSSH, } impl AsRef for Service { fn as_ref(&self) -> &str { match self { Service::SSH => "ssh", + Service::PSSH => "privilege_ssh", Service::SMB => "smb", Service::ESXi => "esxi", Service::SNMP => "snmp", diff --git a/rust/models/src/port.rs b/rust/models/src/port.rs index 086cb94a38..83c219c85a 100644 --- a/rust/models/src/port.rs +++ b/rust/models/src/port.rs @@ -77,3 +77,93 @@ impl TryFrom<&str> for Protocol { } } } + +pub fn ports_to_openvas_port_list(ports: Vec) -> Option { + + fn add_range_to_list (list: &mut String, start: usize, end: Option) { + // Add range + if let Some(end) = end { + list.push_str(start.to_string().as_str()); + list.push('-'); + list.push_str(end.to_string().as_str()); + list.push(','); + // Add single port + } else { + list.push_str(start.to_string().as_str()); + list.push(','); + } + + } + if ports.is_empty() { + return None; + } + + let mut udp = String::from("udp:"); + let mut tcp = String::from("tcp:"); + + ports.iter().for_each( + |p| match p.protocol { + Some(Protocol::TCP) => {p.range.iter().for_each(|r| add_range_to_list(&mut tcp, r.start, r.end));}, + Some(Protocol::UDP) => {p.range.iter().for_each(|r| add_range_to_list(&mut udp, r.start, r.end));}, + None => { + p.range.iter().for_each(|r| add_range_to_list(&mut tcp, r.start, r.end)); + p.range.iter().for_each(|r| add_range_to_list(&mut udp, r.start, r.end)); + } + } + ); + tcp.push_str(&udp); + Some(tcp) + +} + +#[cfg(test)] +mod tests { + use std::ptr::eq; + + use crate::{Protocol, Port,PortRange, ports_to_openvas_port_list}; + + #[test] + fn test_port_convertion_to_string() { + + let ports = vec![ + Port{ + protocol: Some(Protocol::TCP), + range: vec![ + PortRange{ + start: 22, + end: Some(25), + }, + PortRange{ + start: 80, + end: None, + }, + ] + }, + Port{ + protocol: Some(Protocol::UDP), + range: vec![ + PortRange{ + start: 30, + end: Some(40), + }, + PortRange{ + start: 5060, + end: None, + }, + ] + }, + Port{ + protocol: None, + range: vec![ + PortRange{ + start: 1000, + end: None, + }, + ] + }, + ]; + assert_eq!(ports_to_openvas_port_list(ports), Some("tcp:22-25,80,1000,udp:30-40,5060,1000,".to_string())); + + } +} + diff --git a/rust/models/src/target.rs b/rust/models/src/target.rs index c255c3dcea..635b8e19a6 100644 --- a/rust/models/src/target.rs +++ b/rust/models/src/target.rs @@ -14,6 +14,8 @@ use super::{credential::Credential, port::Port}; pub struct Target { /// List of hosts to scan pub hosts: Vec, + /// List of excluded hosts to scan + pub excluded_hosts: Vec, /// List of ports used for scanning pub ports: Vec, #[cfg_attr(feature = "serde_support", serde(default))] @@ -43,9 +45,9 @@ pub struct Target { #[cfg_attr(feature = "bincode_support", derive(bincode::Encode, bincode::Decode))] #[cfg_attr(feature = "serde_support", serde(rename_all = "snake_case"))] pub enum AliveTestMethods { - Icmp, - TcpSyn, - TcpAck, - Arp, - ConsiderAlive, + Icmp = 0x01, + TcpSyn = 0x02, + TcpAck = 0x04, + Arp = 0x08, + ConsiderAlive = 0x16, } diff --git a/rust/openvasctl/Cargo.toml b/rust/openvasctl/Cargo.toml index afdf2859fa..f6b1c44e58 100644 --- a/rust/openvasctl/Cargo.toml +++ b/rust/openvasctl/Cargo.toml @@ -7,3 +7,6 @@ edition = "2021" [dependencies] models = { path = "../models" } +redis = "0.22.0" +redis-storage = { version = "0.1.0", path = "../redis-storage" } +storage = { version = "0.1.0", path = "../storage" } diff --git a/rust/openvasctl/src/lib.rs b/rust/openvasctl/src/lib.rs index 2238040304..54582f3af6 100644 --- a/rust/openvasctl/src/lib.rs +++ b/rust/openvasctl/src/lib.rs @@ -1,3 +1,5 @@ pub mod cmd; pub mod ctl; pub mod error; +pub mod openvas_redis; +pub mod pref_handler; diff --git a/rust/openvasctl/src/openvas_redis.rs b/rust/openvasctl/src/openvas_redis.rs new file mode 100644 index 0000000000..652b5d0f51 --- /dev/null +++ b/rust/openvasctl/src/openvas_redis.rs @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: 2024 Greenbone AG +// +// SPDX-License-Identifier: GPL-2.0-or-later + +use redis_storage::{ + dberror::{DbError, RedisStorageResult}, + NameSpaceSelector, RedisCtx, RedisGetNvt, RedisWrapper, +}; +use std::sync::{Arc, Mutex}; +use storage::item::Nvt; + +#[derive(Debug, Default)] +pub struct RedisHelper +where + R: RedisWrapper, +{ + cache: Arc>, + task_kb: Arc>, +} + +impl RedisHelper { + /// Initialize a RedisHelper struct with the connection to access the NVT cache + /// and a empty task knowledge base to store the scan configuration to be sent to openvas. + pub fn init( + redis_url: &str, + selector: &[NameSpaceSelector], + ) -> RedisStorageResult> { + let mut rctx = RedisCtx::open(redis_url, selector)?; + rctx.delete_namespace()?; + let cache = RedisCtx::open(redis_url, selector)?; + + Ok(RedisHelper:: { + cache: Arc::new(Mutex::new(cache)), + task_kb: Arc::new(Mutex::new(rctx)), + }) + } + + /// Provide access to the cache + pub fn kb_id(&self) -> RedisStorageResult { + let cache = &self + .cache + .lock() + .map_err(|e| DbError::SystemError(format!("{e:?}")))?; + Ok(cache.db) + } +} + +pub trait KbAccess { + fn push_kb_item(&self, key: &str, value: T) -> RedisStorageResult<()>; +} + +impl KbAccess for RedisHelper { + fn push_kb_item(&self, key: &str, value: T) -> RedisStorageResult<()> { + let mut kb = Arc::as_ref(&self.task_kb) + .lock() + .map_err(|e| DbError::SystemError(format!("{e:?}")))?; + + kb.lpush(key, value)?; + Ok(()) + } +} + +pub trait VtHelper { + fn get_vt(&self, oid: &str) -> RedisStorageResult>; +} + +impl VtHelper for RedisHelper { + fn get_vt(&self, oid: &str) -> RedisStorageResult> { + let mut cache = Arc::as_ref(&self.cache) + .lock() + .map_err(|e| DbError::SystemError(format!("{e:?}")))?; + + cache.redis_get_vt(oid) + } +} diff --git a/rust/openvasctl/src/pref_handler.rs b/rust/openvasctl/src/pref_handler.rs new file mode 100644 index 0000000000..c74175ee51 --- /dev/null +++ b/rust/openvasctl/src/pref_handler.rs @@ -0,0 +1,539 @@ +// SPDX-FileCopyrightText: 2024 Greenbone AG +// +// SPDX-License-Identifier: GPL-2.0-or-later + +use std::{collections::HashMap, marker::PhantomData}; + +use models::{ports_to_openvas_port_list, AliveTestMethods, CredentialType, Scan, Service, VT}; +use redis_storage::{dberror::RedisStorageResult, RedisCtx, RedisWrapper}; + +use crate::openvas_redis::{KbAccess, RedisHelper, VtHelper}; + +const OID_SSH_AUTH: &str = "1.3.6.1.4.1.25623.1.0.103591"; +const OID_SMB_AUTH: &str = "1.3.6.1.4.1.25623.1.0.90023"; +const OID_ESXI_AUTH: &str = "1.3.6.1.4.1.25623.1.0.105058"; +const OID_SNMP_AUTH: &str = "1.3.6.1.4.1.25623.1.0.105076"; +const OID_PING_HOST: &str = "1.3.6.1.4.1.25623.1.0.100315"; + +const BOREAS_ALIVE_TEST: &str = "ALIVE_TEST"; +const BOREAS_ALIVE_TEST_PORTS: &str = "ALIVE_TEST_PORTS"; +const ALIVE_TEST_SCAN_CONFIG_DEFAULT: u8 = 0x00; + +fn int_to_bool(value: u8) -> bool { + if value == 0 { + return false; + } + true +} + +fn bool_to_str(value: &str) -> String { + if value == "0" { + return "no".to_string(); + } + "yes".to_string() +} + +pub struct PreferenceHandler +where + R: RedisWrapper, +{ + scan_config: Scan, + redis_connector: RedisHelper, + nvt_params: HashMap, + phantom: PhantomData, +} + +impl PreferenceHandler +where + R: RedisWrapper, +{ + pub fn new(scan_config: Scan, redis_connector: RedisHelper) -> Self { + Self { + scan_config, + redis_connector, + nvt_params: HashMap::new(), + phantom: PhantomData, + } + } + + pub fn prepare_main_kbindex_for_openvas(&self) -> RedisStorageResult<()> { + self.redis_connector.push_kb_item( + format!( + "internal/{}/scanprefs", + self.scan_config.scan_id.clone().expect("Missing scan ID") + ) + .as_str(), + format!("ov_maindbid|||{}", self.redis_connector.kb_id()?), + )?; + Ok(()) + } + + pub fn prepare_scan_id_for_openvas(&self) -> RedisStorageResult<()> { + self.redis_connector.push_kb_item( + format!( + "internal/{}", + &self.scan_config.scan_id.clone().expect("Missing scan ID") + ) + .as_str(), + "new", + )?; + self.redis_connector.push_kb_item( + "internal/scanid", + self.scan_config.scan_id.clone().expect("Missing scan ID"), + )?; + + Ok(()) + } + + fn process_vts(&self, vts: &Vec) -> (Vec, HashMap) { + let mut vts_list: Vec = vec![]; + let mut pref_list: HashMap = HashMap::new(); + + for vt in vts { + if let Some(nvt) = self.redis_connector.get_vt(&vt.oid).unwrap() { + // add oid to the target list + vts_list.push(vt.oid.clone()); + + // prepare vt preferences + for pref in vt.parameters.iter() { + let (prefid, class, name, value): (String, String, String, String) = nvt + .preferences + .get(pref.id as usize) + .expect("Valid pref id") + .into(); + + let value_aux: String = if class == *"checkbox" { + bool_to_str(&pref.value) + } else { + value + }; + + pref_list.insert( + format!("{}:{}:{}:{}", vt.oid, prefid, class, name), + value_aux, + ); + } + } else { + //TODO: log message. Not found or handled via notus + continue; + } + } + + (vts_list, pref_list) + } + + pub fn prepare_plugins_for_openvas(&mut self) -> RedisStorageResult<()> { + let nvts = &self.scan_config.vts; + + if nvts.is_empty() { + return Ok(()); + } + + let (nvts, prefs) = self.process_vts(nvts); + // update list of preferences + self.nvt_params.extend(prefs); + + // prepare vts + self.redis_connector.push_kb_item( + format!( + "internal/{}/scanprefs", + self.scan_config.scan_id.clone().expect("Missing scan ID") + ) + .as_str(), + format!("plugin_set|||{}", nvts.join(";")), + ) + } + + pub fn prepare_nvt_preferences(&self) -> RedisStorageResult<()> { + let mut items: Vec = vec![]; + + let _ = self + .nvt_params + .clone() + .into_iter() + .map(|(k, v)| items.push(format!("{}|||{}", k, v))); + self.redis_connector.push_kb_item( + format!( + "internal/{}/scanprefs", + self.scan_config.scan_id.clone().expect("Missing scan ID") + ) + .as_str(), + items, + ) + } + + pub fn prepare_alive_test_option_for_openvas(&mut self) -> RedisStorageResult<()> { + let mut prefs: HashMap = HashMap::new(); + let mut alive_test = ALIVE_TEST_SCAN_CONFIG_DEFAULT; + let mut value: &str = "no"; + + let methods = self.scan_config.target.alive_test_methods.clone(); + for m in methods { + alive_test |= m as u8; + } + + //Preference 1 + if int_to_bool(alive_test & AliveTestMethods::TcpAck as u8) + || int_to_bool(alive_test & AliveTestMethods::TcpSyn as u8) + { + value = "yes" + } + prefs.insert( + format!("{OID_PING_HOST}:1:checkbox:Do a TCP ping"), + value.to_string(), + ); + + //Preference 2 + value = "no"; + if int_to_bool(alive_test & AliveTestMethods::TcpAck as u8) + && int_to_bool(alive_test & AliveTestMethods::TcpSyn as u8) + { + value = "yes" + } + prefs.insert( + format!("{OID_PING_HOST}:2:checkbox:TCP ping tries also TCP-SYN ping"), + value.to_string(), + ); + + //Preference 7 + value = "no"; + if int_to_bool(alive_test & AliveTestMethods::TcpSyn as u8) + && !int_to_bool(alive_test & AliveTestMethods::TcpAck as u8) + { + value = "yes" + } + prefs.insert( + format!("{OID_PING_HOST}:7:checkbox:TCP ping tries only TCP-SYN ping"), + value.to_string(), + ); + + //Preference 3 + value = "no"; + if int_to_bool(alive_test & AliveTestMethods::Icmp as u8) { + value = "yes" + } + prefs.insert( + format!("{OID_PING_HOST}:3:checkbox:Do an ICMP ping"), + value.to_string(), + ); + + //Preference 4 + value = "no"; + if int_to_bool(alive_test & AliveTestMethods::Arp as u8) { + value = "yes" + } + prefs.insert( + format!("{OID_PING_HOST}:4:checkbox:Use ARP"), + value.to_string(), + ); + + //Preference 5. This preference is confusing. Since the method name and the preference name have different logics + value = "yes"; // consider hosts as dead + if int_to_bool(alive_test & AliveTestMethods::ConsiderAlive as u8) { + value = "no" // NO, means that hosts are not considered as dead. + } + + prefs.insert( + format!("{OID_PING_HOST}:5:checkbox:Mark unrechable Hosts as dead (not scanning)"), + value.to_string(), + ); + + // It will replace the defaults with the values sent by the client + self.nvt_params.extend(prefs); + Ok(()) + } + + pub fn prepare_boreas_alive_test(&self) -> RedisStorageResult<()> { + // TODO! get "test_alive_hosts_only" confiuration from openvas.conf + // return here if not set. Otherwise, prepare the boreas config. + let methods = self.scan_config.target.alive_test_methods.clone(); + + let mut alive_test = ALIVE_TEST_SCAN_CONFIG_DEFAULT; + for m in methods { + alive_test |= m as u8; + } + + if (1..=31).contains(&alive_test) { + self.redis_connector.push_kb_item( + format!( + "internal/{}/scanprefs", + self.scan_config.scan_id.clone().expect("Missing scan ID") + ) + .as_str(), + format!("{BOREAS_ALIVE_TEST}|||{}", alive_test), + )?; + }; + + if alive_test == ALIVE_TEST_SCAN_CONFIG_DEFAULT { + self.redis_connector.push_kb_item( + format!( + "internal/{}/scanprefs", + self.scan_config.scan_id.clone().expect("Missing scan ID") + ) + .as_str(), + format!("{BOREAS_ALIVE_TEST}|||{}", AliveTestMethods::Icmp as u8), + )?; + } + + let alive_test_ports = self.scan_config.target.alive_test_ports.clone(); + if let Some(ports) = ports_to_openvas_port_list(alive_test_ports) { + self.redis_connector.push_kb_item( + format!( + "internal/{}/scanprefs", + self.scan_config.scan_id.clone().expect("Missing scan ID") + ) + .as_str(), + format!("{BOREAS_ALIVE_TEST_PORTS}|||{}", ports), + )?; + }; + + Ok(()) + } + + pub fn prepare_reverse_lookup_opt_for_openvas(&self) -> RedisStorageResult<()> { + let mut lookup_opts: Vec = vec![]; + + if let Some(reverse_lookup_only) = self.scan_config.target.reverse_lookup_only { + if reverse_lookup_only { + lookup_opts.push(format!("reverse_lookup_only|||{}", "yes")); + } + } else { + lookup_opts.push(format!("reverse_lookup_only|||{}", "no")); + } + + if let Some(reverse_lookup_unify) = self.scan_config.target.reverse_lookup_unify { + if reverse_lookup_unify { + lookup_opts.push(format!("reverse_lookup_unify|||{}", "yes")); + } + } else { + lookup_opts.push(format!("reverse_lookup_unify|||{}", "no")); + } + + self.redis_connector.push_kb_item( + format!( + "internal/{}/scanprefs", + self.scan_config.scan_id.clone().expect("Missing scan ID") + ) + .as_str(), + lookup_opts, + ) + } + + pub fn prepare_target_for_openvas(&self) -> RedisStorageResult<()> { + let target = self.scan_config.target.hosts.join(","); + self.redis_connector.push_kb_item( + format!( + "internal/{}/scanprefs", + self.scan_config.scan_id.clone().expect("Missing scan ID") + ) + .as_str(), + format!("TARGET|||{}", target), + ) + } + + pub fn prepare_ports_for_openvas(&self) -> RedisStorageResult<()> { + let ports = self.scan_config.target.ports.clone(); + if let Some(ports) = ports_to_openvas_port_list(ports) { + self.redis_connector.push_kb_item( + format!( + "internal/{}/scanprefs", + self.scan_config.scan_id.clone().expect("Missing scan ID") + ) + .as_str(), + format!("PORTS|||{}", ports), + )?; + }; + + Ok(()) + } + + pub fn prepare_host_options_for_openvas(&self) -> RedisStorageResult<()> { + let excluded_hosts = self.scan_config.target.excluded_hosts.join(","); + if excluded_hosts.is_empty() { + return Ok(()); + } + + self.redis_connector.push_kb_item( + format!( + "internal/{}/scanprefs", + self.scan_config.scan_id.clone().expect("Missing scan ID") + ) + .as_str(), + format!("excluded_hosts|||{}", excluded_hosts), + ) + } + + pub fn prepare_scan_params_for_openvas(&self) -> RedisStorageResult<()> { + let options = self + .scan_config + .scanner_preferences + .clone() + .iter() + .map(|x| format!("{}|||{}", x.id, x.value)) + .collect::>(); + + if options.is_empty() { + return Ok(()); + } + + self.redis_connector.push_kb_item( + format!( + "internal/{}/scanprefs", + self.scan_config.scan_id.clone().expect("Missing scan ID") + ) + .as_str(), + options, + ) + } + pub fn prepare_credentials_for_openvas(&self) -> RedisStorageResult<()> { + let credentials = self.scan_config.target.credentials.clone(); + + let mut credential_preferences: Vec = vec![]; + for credential in credentials { + match credential.service { + Service::SSH => { + if let Some(port) = credential.port { + credential_preferences.push(format!("auth_port_ssh|||{}", port)); + } else { + credential_preferences.push("auth_port_ssh|||22".to_string()); + }; + + if let CredentialType::UP { username, password } = + credential.credential_type.clone() + { + credential_preferences.push(format!( + "{OID_SSH_AUTH}:3:password:SSH password (unsafe!):|||{}", + password + )); + credential_preferences.push(format!( + "{OID_SSH_AUTH}:1:entry:SSH login name:|||{}", + username + )); + }; + if let CredentialType::USK { + username, + password, + private_key, + } = credential.credential_type + { + credential_preferences.push(format!( + "{OID_SSH_AUTH}:1:entry:SSH login name:|||{}", + username + )); + credential_preferences.push(format!( + "{OID_SSH_AUTH}:2:password:SSH key passphrase:|||{}", + password + )); + credential_preferences.push(format!( + "{OID_SSH_AUTH}:4:file:SSH private key:|||{}", + private_key + )); + }; + } + Service::PSSH => { + if let CredentialType::UP { username, password } = credential.credential_type { + credential_preferences.push(format!( + "{OID_SSH_AUTH}:7:entry:SSH privilege login name:|||{}", + username + )); + credential_preferences.push(format!( + "{OID_SSH_AUTH}:8:password:SSH privilege password:|||{}", + password + )); + }; + } + + Service::SMB => { + if let CredentialType::UP { username, password } = + credential.credential_type.clone() + { + credential_preferences + .push(format!("{OID_SMB_AUTH}:1:entry:SMB login:|||{}", username)); + credential_preferences.push(format!( + "{OID_SMB_AUTH}:2:password:SMB password:|||{}", + password + )); + }; + } + + Service::ESXi => { + if let CredentialType::UP { username, password } = + credential.credential_type.clone() + { + credential_preferences.push(format!( + "{OID_ESXI_AUTH}:1:entry:ESXi login name:|||{}", + username + )); + credential_preferences.push(format!( + "{OID_ESXI_AUTH}:2:password:ESXi login password:|||{}", + password + )); + }; + } + + Service::SNMP => { + if let CredentialType::SNMP { + username, + password, + community, + auth_algorithm, + privacy_password, + privacy_algorithm, + } = credential.credential_type + { + // if there is a privacy password, a valid privacy algorithm must be provided. + if privacy_algorithm.is_empty() + && (!privacy_password.is_empty() + || (privacy_algorithm != "aes" && privacy_algorithm != "des")) + { + continue; + }; + + if auth_algorithm.is_empty() + || (auth_algorithm != "md5" && auth_algorithm != "sha1") + { + continue; + }; + + credential_preferences.push(format!( + "{OID_SNMP_AUTH}:1:password:SNMP Community:|||{}", + community + )); + credential_preferences.push(format!( + "{OID_SNMP_AUTH}:2:entry:SNMPv3 Username:|||{}", + username + )); + credential_preferences.push(format!( + "{OID_SNMP_AUTH}:3:password:SNMPv3 Password:|||{}", + password + )); + credential_preferences.push(format!( + "{OID_SNMP_AUTH}:4:radio:SNMPv3 Authentication Algorithm:|||{}", + auth_algorithm + )); + credential_preferences.push(format!( + "{OID_SNMP_AUTH}:5:password:SNMPv3 Privacy Password:|||{}", + privacy_password + )); + credential_preferences.push(format!( + "{OID_SNMP_AUTH}:6:radio:SNMPv3 Privacy Algorithm:|||{}", + privacy_algorithm + )); + } + } + } + } + + if !credential_preferences.is_empty() { + self.redis_connector.push_kb_item( + format!( + "internal/{}/scanprefs", + self.scan_config.scan_id.clone().expect("Missing scan ID") + ) + .as_str(), + credential_preferences, + )?; + } + Ok(()) + } +} diff --git a/rust/redis-storage/src/connector.rs b/rust/redis-storage/src/connector.rs index 5bb8a2fd4f..dd9cf75952 100644 --- a/rust/redis-storage/src/connector.rs +++ b/rust/redis-storage/src/connector.rs @@ -663,7 +663,13 @@ where cache.delete_namespace() } - + /// Provide access to the cache + pub fn db_id(&self) -> RedisStorageResult { + let cache = Arc::as_ref(&self.cache) + .lock() + .map_err(|e| DbError::SystemError(format!("{e:?}")))?; + Ok(cache.db) + } } impl storage::item::ItemDispatcher for CacheDispatcher diff --git a/rust/storage/src/item.rs b/rust/storage/src/item.rs index baee48ed0d..b4b722bb15 100644 --- a/rust/storage/src/item.rs +++ b/rust/storage/src/item.rs @@ -207,7 +207,8 @@ make_str_lookup_enum! { file => File, password => Password, radio => Radio, - sshlogin => SshLogin + sshlogin => SshLogin, + integer => Integer } } @@ -387,6 +388,24 @@ impl From<(&str, &str, &str, &str)> for NvtPreference { } } } +impl From<&NvtPreference> for (String,String,String,String) { + fn from(pref: &NvtPreference) -> Self { + + let id = pref.id().unwrap().to_string(); + let class = match pref.class { + PreferenceType::CheckBox => "checkbox", + PreferenceType::Entry => "entry", + PreferenceType::File => "file", + PreferenceType::Password => "password", + PreferenceType::Radio => "radio", + PreferenceType::SshLogin => "sshlogin", + PreferenceType::Integer => "integer", + }; + let name = pref.name().to_string(); + let def = pref.default().to_string(); + (id, class.to_string(), name, def) + } +} /// TagValue is a type containing value types of script_tag pub type TagValue = types::Primitive; diff --git a/rust/storage/src/types.rs b/rust/storage/src/types.rs index 26700ae73c..c90c61f27a 100644 --- a/rust/storage/src/types.rs +++ b/rust/storage/src/types.rs @@ -44,6 +44,12 @@ impl From for Primitive { } } +impl From> for Primitive { + fn from(s: Vec) -> Self { + Self::Array(s.into_iter().map(|x| x.into()).collect()) + } +} + impl From<&str> for Primitive { fn from(s: &str) -> Self { Self::String(s.to_owned())