From 792305d060d09b58fffbaf7069c48540b7259a39 Mon Sep 17 00:00:00 2001 From: Juan Jose Nicola Date: Thu, 14 Nov 2024 13:02:46 -0300 Subject: [PATCH] use a target struct which supports vhosts instead of a string as Context member. Also, add more host built-in functions --- rust/src/feed/update/mod.rs | 6 +- rust/src/nasl/builtin/host/mod.rs | 98 +++++++++++++++++++++++++++---- rust/src/nasl/builtin/mod.rs | 7 ++- rust/src/nasl/utils/context.rs | 71 +++++++++++++++++++++- rust/src/nasl/utils/mod.rs | 2 +- rust/src/scanner/scan_runner.rs | 3 +- rust/src/scanner/vt_runner.rs | 7 ++- 7 files changed, 170 insertions(+), 24 deletions(-) diff --git a/rust/src/feed/update/mod.rs b/rust/src/feed/update/mod.rs index 39f50246d..8503898da 100644 --- a/rust/src/feed/update/mod.rs +++ b/rust/src/feed/update/mod.rs @@ -15,6 +15,7 @@ use crate::nasl::interpreter::{CodeInterpreter, Interpreter}; use crate::nasl::nasl_std_functions; use crate::nasl::prelude::*; use crate::nasl::syntax::AsBufReader; +use crate::nasl::utils::context::Target; use crate::nasl::ContextType; use crate::storage::{item::NVTField, ContextKey, Dispatcher, NoOpRetriever}; @@ -48,7 +49,7 @@ pub async fn feed_version( let register = Register::default(); let k = ContextKey::default(); let fr = NoOpRetriever::default(); - let target = String::default(); + let target = Target::default(); // TODO add parameter to struct let functions = nasl_std_functions(); let context = Context::new(k, target, dispatcher, &fr, loader, &functions); @@ -147,9 +148,8 @@ where let register = Register::root_initial(&self.initial); let fr = NoOpRetriever::default(); - let target = String::default(); + let target = Target::default(); let functions = nasl_std_functions(); - let context = Context::new( key.clone(), target, diff --git a/rust/src/nasl/builtin/host/mod.rs b/rust/src/nasl/builtin/host/mod.rs index a35dcf036..bc68d8cb9 100644 --- a/rust/src/nasl/builtin/host/mod.rs +++ b/rust/src/nasl/builtin/host/mod.rs @@ -6,13 +6,12 @@ mod tests; use std::{ - net::{IpAddr, SocketAddr, ToSocketAddrs}, - str::FromStr, + env::var_os, net::{IpAddr, SocketAddr, ToSocketAddrs}, str::FromStr }; use dns_lookup::lookup_addr; -use crate::function_set; +use crate::{function_set, models::Source}; use crate::nasl::utils::{error::FunctionErrorKind, lookup_keys::TARGET}; use crate::nasl::syntax::NaslValue; @@ -52,9 +51,9 @@ fn get_host_names(register: &Register, _: &Context) -> Result Result { - resolve_hostname(register).map(NaslValue::String) -} +//fn get_host_name(register: &Register, _: &Context) -> Result { +// resolve_hostname(register).map(NaslValue::String) +//} /// Return the target's IP address as IpAddr. pub fn get_host_ip(context: &Context) -> Result { @@ -73,6 +72,80 @@ pub fn get_host_ip(context: &Context) -> Result { } } +pub fn add_host_name ( + register: &Register, + context: &Context, +) -> Result { + let hostname = match register.named("hostname") { + Some(ContextType::Value(NaslValue::String(x))) if !x.is_empty() => x.clone(), + _ => { + return Err(FunctionErrorKind::diagnostic_ret_null("Empty Hostname")); + } + }; + let source = match register.named("source") { + Some(ContextType::Value(NaslValue::String(x))) if !x.is_empty() => x.clone(), + _ => "NASL".to_string() + }; + + context.add_hostname(hostname, source); + Ok(NaslValue::Null) +} + + +pub fn get_host_name ( + _register: &Register, + context: &Context, +) -> Result { + let mut v = Vec::new(); + if let Some(vh) = context.target_vhosts() { + v = vh.into_iter().map(|(v,_s)| NaslValue::String(v)).collect::>(); + } + + if !v.is_empty() { + return Ok(NaslValue::Fork(v)); + } + + + if let Ok(ip) = get_host_ip(context) { + match lookup_addr(&ip) { + Ok(host) => Ok(NaslValue::String(host)), + Err(_) => Ok(NaslValue::String(ip.to_string())) + } + } else { + Ok(NaslValue::String(context.target().to_string())) + } +} + + +pub fn get_host_name_source ( + register: &Register, + context: &Context, +) -> Result { + let hostname = match register.named("hostname") { + Some(ContextType::Value(NaslValue::String(x))) if !x.is_empty() => x.clone(), + _ => { + return Err(FunctionErrorKind::diagnostic_ret_null("Empty Hostname")); + } + }; + + + if let Some(vh) = context.target_vhosts() { + if let Some(source) = vh.into_iter() + .find_map(|(v,s)| { + if v == hostname { + Some(s) + } else { + None + } + }) { + return Ok(NaslValue::String(source)); + }; + } + + Ok(NaslValue::Null) + +} + /// Return the target's IP address or 127.0.0.1 if not set. fn nasl_get_host_ip( _register: &Register, @@ -175,6 +248,9 @@ fn target_is_ipv6(_register: &Register, context: &Context) -> Result Result { let positional = register.positional(); if positional.len() != 2 { @@ -255,11 +331,7 @@ fn same_host(register: &Register, _: &Context) -> Result Context { - let target = match &key { + let mut target = Target::default(); + target.set_target(match &key { ContextKey::Scan(_, Some(target)) => target.clone(), ContextKey::Scan(_, None) => String::default(), ContextKey::FileName(target) => target.clone(), - }; + }); Context::new( key, target, diff --git a/rust/src/nasl/utils/context.rs b/rust/src/nasl/utils/context.rs index f3ce070c6..b188f8dd1 100644 --- a/rust/src/nasl/utils/context.rs +++ b/rust/src/nasl/utils/context.rs @@ -289,6 +289,8 @@ impl Default for Register { } } use std::collections::HashMap; +use std::net::IpAddr; +use std::sync::Mutex; type Named = HashMap; /// NaslContext is a struct to contain variables and if root declared functions @@ -328,6 +330,46 @@ impl NaslContext { } } +#[derive(Debug,Default)] +pub struct Target { + + /// The original target. IP or hostname + target: String, + /// The IP address + ip_addr: String, + // The shared state is guarded by a mutex. This is a `std::sync::Mutex` and + // not a Tokio mutex. This is because there are no asynchronous operations + // being performed while holding the mutex. Additionally, the critical + // sections are very small. + // + // A Tokio mutex is mostly intended to be used when locks need to be held + // across `.await` yield points. All other cases are **usually** best + // served by a std mutex. If the critical section does not include any + // async operations but is long (CPU intensive or performing blocking + // operations), then the entire operation, including waiting for the mutex, + // is considered a "blocking" operation and `tokio::task::spawn_blocking` + // should be used. + /// vhost list which resolve to the IP address and their sources. + vhosts: Mutex> +} + +impl Target { + pub fn set_target (&mut self, target: String) -> &Target{ + self.target = target; + self + } + + pub fn add_hostname (&self, hostname: String, source: String) -> &Target{ + self.vhosts.lock().unwrap().push((hostname, source)); + self + } + + pub fn add_ipaddress (&mut self, ip: IpAddr ) -> &Target{ + self.ip_addr = ip.to_string(); + self + } +} + /// Configurations /// /// This struct includes all objects that a nasl function requires. @@ -336,7 +378,7 @@ pub struct Context<'a> { /// key for this context. A file name or a scan id key: ContextKey, /// target to run a scan against - target: String, + target: Target, /// Default Dispatcher dispatcher: &'a dyn Dispatcher, /// Default Retriever @@ -351,7 +393,7 @@ impl<'a> Context<'a> { /// Creates an empty configuration pub fn new( key: ContextKey, - target: String, + target: Target, dispatcher: &'a dyn Dispatcher, retriever: &'a dyn Retriever, loader: &'a dyn Loader, @@ -395,9 +437,32 @@ impl<'a> Context<'a> { /// Get the target host pub fn target(&self) -> &str { - &self.target + &self.target.target + } + + /// Get the target IP address list + pub fn target_ipaddr(&self) -> Option { + Some(self.target.ip_addr.clone()) } + /// Get the target VHost list + pub fn target_vhosts(&self) -> Option> { + Some(self.target.vhosts.lock().unwrap().clone()) + } + + pub fn set_target (&mut self, target: String) { + self.target.target = target; + } + + pub fn add_hostname (&self, hostname: String, source: String) { + self.target.add_hostname(hostname, source); + } + + pub fn add_ipaddress (&mut self, ip: IpAddr ) { + self.target.add_ipaddress(ip); + + } + /// Get the storage pub fn dispatcher(&self) -> &dyn Dispatcher { self.dispatcher diff --git a/rust/src/nasl/utils/mod.rs b/rust/src/nasl/utils/mod.rs index 701ce7fcd..66a1a2a38 100644 --- a/rust/src/nasl/utils/mod.rs +++ b/rust/src/nasl/utils/mod.rs @@ -11,7 +11,7 @@ pub mod lookup_keys; use std::collections::HashMap; -pub use context::{Context, ContextType, Register}; +pub use context::{Context, ContextType, Register, Target}; pub use error::FunctionErrorKind; pub use executor::{Executor, IntoFunctionSet, StoredFunctionSet}; diff --git a/rust/src/scanner/scan_runner.rs b/rust/src/scanner/scan_runner.rs index b0b9081ff..1f6e14306 100644 --- a/rust/src/scanner/scan_runner.rs +++ b/rust/src/scanner/scan_runner.rs @@ -118,6 +118,7 @@ pub(super) mod tests { use crate::models::Target; use crate::models::VT; use crate::nasl::syntax::NaslValue; + use crate::nasl::utils::Target as ContextTarget; use crate::nasl::utils::Context; use crate::nasl::utils::Executor; use crate::nasl::utils::Register; @@ -320,7 +321,7 @@ exit({rc}); let storage = DefaultDispatcher::new(); let register = Register::root_initial(&initial); - let target = String::default(); + let target = ContextTarget::default(); let functions = nasl_std_functions(); let loader = |_: &str| code.to_string(); let key = ContextKey::FileName(id.to_string()); diff --git a/rust/src/scanner/vt_runner.rs b/rust/src/scanner/vt_runner.rs index eae697909..3547b979d 100644 --- a/rust/src/scanner/vt_runner.rs +++ b/rust/src/scanner/vt_runner.rs @@ -1,5 +1,6 @@ use crate::models::{Host, Parameter, Protocol, ScanId}; use crate::nasl::syntax::{Loader, NaslValue}; +use crate::nasl::utils::context::Target; use crate::nasl::utils::{Executor, Register}; use crate::scheduling::Stage; use crate::storage::item::Nvt; @@ -193,10 +194,12 @@ impl<'a, Stack: ScannerStack> VTRunner<'a, Stack> { if let Err(e) = self.check_keys(self.vt) { return e; } - + let mut target = Target::default(); + target.set_target(self.target.clone()); + let context = Context::new( self.generate_key(), - self.target.clone(), + target, self.storage.as_dispatcher(), self.storage.as_retriever(), self.loader,