diff --git a/rust/Cargo.lock b/rust/Cargo.lock index a7854f5dc..2731bef11 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", ] @@ -1961,7 +1967,12 @@ dependencies = [ name = "openvas" version = "0.1.0" dependencies = [ + "configparser", "models", + "redis", + "redis-storage", + "storage", + "tracing", ] [[package]] @@ -2484,9 +2495,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 +2521,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", "system-configuration", "tokio", "tokio-rustls 0.24.1", @@ -2575,9 +2587,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 +2619,7 @@ dependencies = [ "log", "ring", "rustls-pki-types", - "rustls-webpki 0.102.1", + "rustls-webpki 0.102.2", "subtle", "zeroize", ] @@ -2646,9 +2658,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 +2674,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 +3017,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 +3049,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 +3078,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 +3131,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 +3151,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 +3195,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 +3273,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 +3307,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 +3614,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 +3624,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 +3639,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 +3651,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 +3661,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 +3674,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 +3690,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 +3911,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/doc/openapi.yml b/rust/doc/openapi.yml index 7fcbd7b5c..38432b0ef 100644 --- a/rust/doc/openapi.yml +++ b/rust/doc/openapi.yml @@ -469,6 +469,12 @@ components: items: description: "Contains either an IPv4, IPv6, IPv4 range, IPv6 range, IPv4 CIDR, IPv6 CIDR or hostname." type: "string" + excluded_hosts: + description: "A list of excluded hosts." + type: "array" + items: + description: "Contains either an IPv4, IPv6, IPv4 range, IPv6 range, IPv4 CIDR, IPv6 CIDR or hostname." + type: "string" ports: description: "A list of ports." type: "array" @@ -566,6 +572,15 @@ components: type: "string" password: description: "Password for authentication." + privilege_credential: + description: "Privilege username and password for SSH service" + type: "object" + properties: + username: + description: "Privilege username for authentication." + type: "string" + password: + description: "Privilege password for authentication." required: - username @@ -878,6 +893,10 @@ components: "2002::1234:abcd:ffff:c0a8:101/64", "examplehost", ], + "excluded_hosts": + [ + "192.168.0.14" + ], "ports": [ { diff --git a/rust/models/src/credential.rs b/rust/models/src/credential.rs index 03a7f06e2..7706d8427 100644 --- a/rust/models/src/credential.rs +++ b/rust/models/src/credential.rs @@ -50,6 +50,7 @@ impl Default for Credential { credential_type: CredentialType::UP { username: "root".to_string(), password: "".to_string(), + privilege_credential: None, }, } } @@ -103,6 +104,8 @@ pub enum CredentialType { username: String, /// The password for authentication. password: String, + /// privilege credential + privilege_credential: Option>, }, #[cfg_attr(feature = "serde_support", serde(rename = "usk"))] /// User/ssh-key credentials. @@ -140,9 +143,14 @@ impl CredentialType { F: FnOnce(String) -> Result, { Ok(match self { - CredentialType::UP { username, password } => CredentialType::UP { + CredentialType::UP { + username, + password, + privilege_credential, + } => CredentialType::UP { username, password: f(password)?, + privilege_credential, }, CredentialType::USK { username, diff --git a/rust/models/src/lib.rs b/rust/models/src/lib.rs index 0bb3a6444..80bf0db1e 100644 --- a/rust/models/src/lib.rs +++ b/rust/models/src/lib.rs @@ -72,6 +72,9 @@ mod tests { "2002::1234:abcd:ffff:c0a8:101/64", "examplehost" ], + "excluded_hosts": [ + "192.168.0.14" + ], "ports": [ { "protocol": "udp", diff --git a/rust/models/src/port.rs b/rust/models/src/port.rs index 086cb94a3..4977ecf6e 100644 --- a/rust/models/src/port.rs +++ b/rust/models/src/port.rs @@ -77,3 +77,99 @@ 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)); + } + }); + if udp != *"udp:" { + tcp.push_str(&udp); + } + Some(tcp) +} + +#[cfg(test)] +mod tests { + + use crate::{ports_to_openvas_port_list, Port, PortRange, Protocol}; + + #[test] + fn test_port_conversion_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 c255c3dce..20deaacb4 100644 --- a/rust/models/src/target.rs +++ b/rust/models/src/target.rs @@ -17,6 +17,9 @@ pub struct Target { /// List of ports used for scanning pub ports: Vec, #[cfg_attr(feature = "serde_support", serde(default))] + /// List of excluded hosts to scan + pub excluded_hosts: Vec, + #[cfg_attr(feature = "serde_support", serde(default))] /// List of credentials used to get access to a system pub credentials: Vec, #[cfg_attr(feature = "serde_support", serde(default))] @@ -43,9 +46,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 afdf2859f..a9b2f927d 100644 --- a/rust/openvasctl/Cargo.toml +++ b/rust/openvasctl/Cargo.toml @@ -6,4 +6,9 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +configparser = "3.0.4" models = { path = "../models" } +redis = "0.22.0" +redis-storage = { version = "0.1.0", path = "../redis-storage" } +storage = { version = "0.1.0", path = "../storage" } +tracing = "0.1.40" diff --git a/rust/openvasctl/src/cmd.rs b/rust/openvasctl/src/cmd.rs index 89419d230..109c16e4d 100644 --- a/rust/openvasctl/src/cmd.rs +++ b/rust/openvasctl/src/cmd.rs @@ -1,3 +1,8 @@ +// SPDX-FileCopyrightText: 2024 Greenbone AG +// +// SPDX-License-Identifier: GPL-2.0-or-later + +use configparser::ini::Ini; use std::{ io::Result, process::{Child, Command}, @@ -13,6 +18,17 @@ pub fn check_sudo() -> bool { Command::new("sudo").args(["-n", "openvas"]).spawn().is_ok() } +pub fn read_openvas_config() -> Result { + let oconfig = Command::new("openvas").arg("-s").output()?; + + let mut config = Ini::new(); + let oconfig = oconfig.stdout.iter().map(|x| *x as char).collect(); + config + .read(oconfig) + .expect("Error reading openvas configuration"); + Ok(config) +} + /// Start a new scan with the openvas executable with the given string. Before a scan can be /// started all data needed for the scan must put into redis before. pub fn start(id: &str, sudo: bool, nice: Option) -> Result { diff --git a/rust/openvasctl/src/lib.rs b/rust/openvasctl/src/lib.rs index 223804030..72fb18bb5 100644 --- a/rust/openvasctl/src/lib.rs +++ b/rust/openvasctl/src/lib.rs @@ -1,3 +1,9 @@ +// SPDX-FileCopyrightText: 2024 Greenbone AG +// +// SPDX-License-Identifier: GPL-2.0-or-later + 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 000000000..1009c335e --- /dev/null +++ b/rust/openvasctl/src/openvas_redis.rs @@ -0,0 +1,124 @@ +// 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::{ + collections::HashMap, + sync::{Arc, Mutex}, +}; +use storage::item::Nvt; + +#[derive(Debug, Default)] +pub struct RedisHelper +where + R: RedisWrapper, +{ + cache: Arc>, + task_kb: Arc>, +} + +impl RedisHelper +where + R: RedisWrapper, +{ + /// 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)), + }) + } +} + +pub trait KbAccess { + fn push_kb_item( + &mut self, + key: &str, + value: T, + ) -> RedisStorageResult<()>; + fn kb_id(&self) -> RedisStorageResult; +} + +impl KbAccess for RedisHelper { + fn push_kb_item( + &mut 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(()) + } + /// Provide access to the cache + fn kb_id(&self) -> RedisStorageResult { + let cache = Arc::as_ref(&self.cache) + .lock() + .map_err(|e| DbError::SystemError(format!("{e:?}")))?; + Ok(cache.db) + } +} + +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) + } +} + +pub struct FakeRedis { + pub data: HashMap>>, +} + +impl FakeRedis { + pub fn item_exists(&self, key: &str, value: &str) -> bool { + let mut v: Vec = Vec::new(); + if let Some(item) = self.data.get(key) { + for i in item { + v.push(String::from_utf8(i.to_vec()).unwrap()); + } + } + v.contains(&value.to_string()) + } +} + +impl VtHelper for FakeRedis { + fn get_vt(&self, _: &str) -> RedisStorageResult> { + Ok(None) + } +} + +impl KbAccess for FakeRedis { + fn push_kb_item( + &mut self, + key: &str, + value: T, + ) -> RedisStorageResult<()> { + self.data.insert(key.to_string(), value.to_redis_args()); + Ok(()) + } + fn kb_id(&self) -> RedisStorageResult { + Ok(3) + } +} diff --git a/rust/openvasctl/src/pref_handler.rs b/rust/openvasctl/src/pref_handler.rs new file mode 100644 index 000000000..1418e45d3 --- /dev/null +++ b/rust/openvasctl/src/pref_handler.rs @@ -0,0 +1,645 @@ +// SPDX-FileCopyrightText: 2024 Greenbone AG +// +// SPDX-License-Identifier: GPL-2.0-or-later + +use std::collections::HashMap; + +use models::{ports_to_openvas_port_list, AliveTestMethods, CredentialType, Scan, Service, VT}; +use redis_storage::dberror::RedisStorageResult; + +use super::cmd; +use super::openvas_redis::{KbAccess, 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 bool_to_str(value: &str) -> String { + if value == "0" { + return "no".to_string(); + } + "yes".to_string() +} + +pub struct PreferenceHandler { + scan_config: Scan, + redis_connector: H, + nvt_params: HashMap, +} + +impl PreferenceHandler +where + H: VtHelper + KbAccess, +{ + pub fn new(scan_config: Scan, redis_connector: H) -> Self { + Self { + scan_config, + redis_connector, + nvt_params: HashMap::new(), + } + } + + pub fn prepare_preferences_for_openvas(&mut self) -> RedisStorageResult<()> { + self.prepare_scan_id_for_openvas()?; + self.prepare_target_for_openvas()?; + self.prepare_ports_for_openvas()?; + self.prepare_credentials_for_openvas()?; + self.prepare_plugins_for_openvas()?; + self.prepare_main_kbindex_for_openvas()?; + self.prepare_host_options_for_openvas()?; + self.prepare_scan_params_for_openvas()?; + self.prepare_reverse_lookup_opt_for_openvas()?; + self.prepare_alive_test_option_for_openvas()?; + + // VT preferences are stored after all preferences have been processed, + // since alive tests preferences have to be able to overwrite default + // preferences of ping_host.nasl for the classic method. + self.prepare_nvt_preferences()?; + self.prepare_boreas_alive_test() + } + + fn prepare_main_kbindex_for_openvas(&mut 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(()) + } + + fn prepare_scan_id_for_openvas(&mut 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 { + tracing::debug!("{} not found or handled via notus", vt.oid); + continue; + } + } + + (vts_list, pref_list) + } + + 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(";")), + ) + } + + fn prepare_nvt_preferences(&mut 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, + ) + } + + 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 (alive_test & AliveTestMethods::TcpAck as u8) != 0 + || (alive_test & AliveTestMethods::TcpSyn as u8) != 0 + { + value = "yes" + } + prefs.insert( + format!("{OID_PING_HOST}:1:checkbox:Do a TCP ping"), + value.to_string(), + ); + + //Preference 2 + value = "no"; + if (alive_test & AliveTestMethods::TcpAck as u8) != 0 + && (alive_test & AliveTestMethods::TcpSyn as u8) != 0 + { + 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 (alive_test & AliveTestMethods::TcpSyn as u8) != 0 + && !(alive_test & AliveTestMethods::TcpAck as u8) != 0 + { + 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 (alive_test & AliveTestMethods::Icmp as u8) != 0 { + value = "yes" + } + prefs.insert( + format!("{OID_PING_HOST}:3:checkbox:Do an ICMP ping"), + value.to_string(), + ); + + //Preference 4 + value = "no"; + if (alive_test & AliveTestMethods::Arp as u8) != 0 { + 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 (alive_test & AliveTestMethods::ConsiderAlive as u8) != 0 { + value = "no" // NO, means that hosts are not considered as dead. + } + + prefs.insert( + format!("{OID_PING_HOST}:5:checkbox:Mark unreachable 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(()) + } + + fn prepare_boreas_alive_test(&mut self) -> RedisStorageResult<()> { + // Check "test_alive_hosts_only" configuration from openvas.conf + // If set no, boreas is disabled and alive_host.nasl is used instead. + if let Ok(config) = cmd::read_openvas_config() { + if let Some(setting) = config.get("default", "test_alive_hosts_only") { + if setting == "no" { + return Ok(()); + } + } + } + + 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(()) + } + + fn prepare_reverse_lookup_opt_for_openvas(&mut 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, + ) + } + + fn prepare_target_for_openvas(&mut 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), + ) + } + + fn prepare_ports_for_openvas(&mut 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(()) + } + + fn prepare_host_options_for_openvas(&mut 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), + ) + } + + fn prepare_scan_params_for_openvas(&mut 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, + ) + } + fn prepare_credentials_for_openvas(&mut 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, + privilege_credential, + } = 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 Some(pcred) = privilege_credential { + if let CredentialType::UP { + username, password, .. + } = pcred.as_ref() + { + 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 + )); + } + } + }; + 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::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(()) + } +} + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + + use models::{AliveTestMethods, Credential, Port, PortRange, Scan}; + + use super::PreferenceHandler; + use crate::openvas_redis::{FakeRedis, KbAccess}; + + #[test] + fn test_prefs() { + let mut scan = Scan::default(); + scan.scan_id = Some("123-456".to_string()); + scan.target.alive_test_methods = vec![AliveTestMethods::Icmp, AliveTestMethods::TcpSyn]; + scan.target.credentials = vec![Credential { + service: models::Service::SSH, + port: Some(22), + credential_type: models::CredentialType::UP { + username: "user".to_string(), + password: "pass".to_string(), + privilege_credential: None, + }, + }]; + scan.target.excluded_hosts = vec!["127.0.0.1".to_string()]; + scan.target.ports = vec![Port { + protocol: Some(models::Protocol::TCP), + range: vec![ + PortRange { + start: 22, + end: Some(25), + }, + PortRange { + start: 80, + end: None, + }, + ], + }]; + + let rc = FakeRedis { + data: HashMap::new(), + }; + + let mut prefh = PreferenceHandler::new(scan, rc); + assert_eq!(prefh.redis_connector.kb_id().unwrap(), 3); + assert!(prefh.prepare_scan_id_for_openvas().is_ok()); + assert!(prefh + .redis_connector + .item_exists("internal/scanid", "123-456")); + assert!(prefh.redis_connector.item_exists("internal/123-456", "new")); + + assert!(prefh.prepare_main_kbindex_for_openvas().is_ok()); + assert!(prefh + .redis_connector + .item_exists("internal/123-456/scanprefs", "ov_maindbid|||3")); + + assert!(prefh.prepare_boreas_alive_test().is_ok()); + assert!(prefh + .redis_connector + .item_exists("internal/123-456/scanprefs", "ALIVE_TEST|||3")); + + assert!(prefh.prepare_host_options_for_openvas().is_ok()); + assert!(prefh + .redis_connector + .item_exists("internal/123-456/scanprefs", "excluded_hosts|||127.0.0.1")); + + assert!(prefh.prepare_credentials_for_openvas().is_ok()); + assert!(prefh.redis_connector.item_exists( + "internal/123-456/scanprefs", + "1.3.6.1.4.1.25623.1.0.103591:3:password:SSH password (unsafe!):|||pass" + )); + assert!(prefh.redis_connector.item_exists( + "internal/123-456/scanprefs", + "1.3.6.1.4.1.25623.1.0.103591:1:entry:SSH login name:|||user" + )); + + assert!(prefh.prepare_ports_for_openvas().is_ok()); + assert!(prefh + .redis_connector + .item_exists("internal/123-456/scanprefs", "PORTS|||tcp:22-25,80,")); + } +} diff --git a/rust/openvasd/src/storage/inmemory.rs b/rust/openvasd/src/storage/inmemory.rs index cfa5b3798..edce7379f 100644 --- a/rust/openvasd/src/storage/inmemory.rs +++ b/rust/openvasd/src/storage/inmemory.rs @@ -456,6 +456,7 @@ mod tests { credential_type: models::CredentialType::UP { username: "test".to_string(), password: "test".to_string(), + privilege_credential: None, }, ..Default::default() }; diff --git a/rust/osp/src/commands.rs b/rust/osp/src/commands.rs index 445be7ee3..50b5de187 100644 --- a/rust/osp/src/commands.rs +++ b/rust/osp/src/commands.rs @@ -287,13 +287,22 @@ fn write_credentials(scan: &Scan, writer: &mut Writer) -> Result<()> { writer.within_parameter_element("credential", parameter, &mut |writer| { match &c.credential_type { - CredentialType::UP { username, password } => { - // TODO need to add privilege escalation for root when service is ssh - // see - // https://docs.greenbone.net/API/OSP/osp-22.04.html#element_credential - // 5.1.3 + CredentialType::UP { + username, + password, + privilege_credential, + } => { write_str_element(writer, "username", username)?; write_str_element(writer, "password", password)?; + if let Some(pcred) = privilege_credential { + if let CredentialType::UP { + username, password, .. + } = pcred.as_ref() + { + write_str_element(writer, "priv_username", username)?; + write_str_element(writer, "priv_password", password)?; + } + } } CredentialType::USK { username, diff --git a/rust/storage/src/item.rs b/rust/storage/src/item.rs index 2f0bf07fa..28c23fd5f 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,23 @@ 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 26700ae73..c90c61f27 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())