Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add host nasl built-in functions #1758

Merged
merged 5 commits into from
Dec 3, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix conflicts and improve code
Also implements get_host_names
jjnicola committed Nov 28, 2024

Verified

This commit was signed with the committer’s verified signature.
jjnicola Juan José Nicola
commit b28e2ee03f41bf2afae88a151019765da9b75bc6
6 changes: 3 additions & 3 deletions rust/src/feed/update/mod.rs
Original file line number Diff line number Diff line change
@@ -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,
152 changes: 94 additions & 58 deletions rust/src/nasl/builtin/host/mod.rs
Original file line number Diff line number Diff line change
@@ -13,49 +13,11 @@ use std::{
use dns_lookup::lookup_addr;

use crate::function_set;
use crate::nasl::utils::{error::FunctionErrorKind, lookup_keys::TARGET};
use crate::nasl::utils::{error::FunctionErrorKind, hosts::resolve, lookup_keys::TARGET};

use crate::nasl::syntax::NaslValue;
use crate::nasl::utils::{Context, ContextType, Register};

/// Resolves IP address of target to hostname
///
/// It does lookup TARGET and when not found falls back to 127.0.0.1 to resolve.
/// If the TARGET is not a IP address than we assume that it already is a fqdn or a hostname and will return that instead.
fn resolve_hostname(register: &Register) -> Result<String, FunctionErrorKind> {
let default_ip = "127.0.0.1";
// currently we use shadow variables as _FC_ANON_ARGS; the original openvas uses redis for that purpose.
let target = register.named(TARGET).map_or_else(
|| default_ip.to_owned(),
|x| match x {
ContextType::Value(NaslValue::String(x)) => x.clone(),
_ => default_ip.to_owned(),
},
);

match target.to_socket_addrs() {
Ok(mut addr) => Ok(addr.next().map_or_else(String::new, |x| x.to_string())),
// assumes that target is already a hostname
Err(_) => Ok(target),
}
}

/// NASL function to get all stored vhosts
///
/// As of now (2023-01-20) there is no vhost handling.
/// Therefore this function does load the registered TARGET and if it is an IP Address resolves it via DNS instead.
fn get_host_names(register: &Register, _: &Context) -> Result<NaslValue, FunctionErrorKind> {
resolve_hostname(register).map(|x| NaslValue::Array(vec![NaslValue::String(x)]))
}

/// NASL function to get the current hostname
///
/// As of now (2023-01-20) there is no vhost handling.
/// Therefore this function does load the registered TARGET and if it is an IP Address resolves it via DNS instead.
fn get_host_name(register: &Register, _: &Context) -> Result<NaslValue, FunctionErrorKind> {
resolve_hostname(register).map(NaslValue::String)
}

/// Return the target's IP address as IpAddr.
pub fn get_host_ip(context: &Context) -> Result<IpAddr, FunctionErrorKind> {
let default_ip = "127.0.0.1";
@@ -73,26 +35,99 @@ pub fn get_host_ip(context: &Context) -> Result<IpAddr, FunctionErrorKind> {
}
}

/// Return the target's IP address or 127.0.0.1 if not set.
fn nasl_get_host_ip(
pub fn add_host_name(
register: &Register,
context: &Context,
) -> Result<NaslValue, FunctionErrorKind> {
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_names(
_register: &Register,
context: &Context,
) -> Result<NaslValue, FunctionErrorKind> {
let ip = get_host_ip(context)?;
Ok(NaslValue::String(ip.to_string()))
if context.target_vhosts().is_none() {
return Ok(NaslValue::Array(vec![NaslValue::String(
context.target_ip().to_string(),
)]));
}

let vhosts = context
.target_vhosts()
.unwrap()
.iter()
.map(|(h, _s)| NaslValue::String(h.to_string()))
.collect();
Ok(NaslValue::Array(vhosts))
}
pub fn get_host_name(
_register: &Register,
context: &Context,
) -> Result<NaslValue, FunctionErrorKind> {
let mut v = Vec::new();
if let Some(vh) = context.target_vhosts() {
v = vh
.into_iter()
.map(|(v, _s)| NaslValue::String(v))
.collect::<Vec<_>>();
}
jjnicola marked this conversation as resolved.
Show resolved Hide resolved

if !v.is_empty() {
return Ok(NaslValue::Fork(v));
}
jjnicola marked this conversation as resolved.
Show resolved Hide resolved

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()))
}
jjnicola marked this conversation as resolved.
Show resolved Hide resolved
}

fn resolve(
mut hostname: String,
) -> Result<Option<Box<dyn Iterator<Item = SocketAddr>>>, FunctionErrorKind> {
//std::net to_socket_addrs() requires a port. Therefore, using a dummy port
hostname.push_str(":5000");
pub fn get_host_name_source(
register: &Register,
context: &Context,
) -> Result<NaslValue, FunctionErrorKind> {
jjnicola marked this conversation as resolved.
Show resolved Hide resolved
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"));
}
};
jjnicola marked this conversation as resolved.
Show resolved Hide resolved

match hostname.to_socket_addrs() {
Ok(addr) => Ok(Some(Box::new(addr))),
// assumes that target is already a hostname
Err(_) => Err(FunctionErrorKind::diagnostic_ret_null("Missing 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,
context: &Context,
) -> Result<NaslValue, FunctionErrorKind> {
let ip = get_host_ip(context)?;
Ok(NaslValue::String(ip.to_string()))
}

/// Get an IP address corresponding to the host name
@@ -175,6 +210,9 @@ fn target_is_ipv6(_register: &Register, context: &Context) -> Result<NaslValue,
}
}

/// Compare if two hosts are the same.
/// The first two unnamed arguments are string containing the host to compare
/// If the named argument cmp_hostname is set to TRUE, the given hosts are resolved into their hostnames
fn same_host(register: &Register, _: &Context) -> Result<NaslValue, FunctionErrorKind> {
let positional = register.positional();
if positional.len() != 2 {
@@ -255,11 +293,7 @@ fn same_host(register: &Register, _: &Context) -> Result<NaslValue, FunctionErro
}
}

if flag {
Ok(NaslValue::Boolean(true))
} else {
Ok(NaslValue::Boolean(false))
}
Ok(NaslValue::Boolean(flag))
}

pub struct Host;
@@ -274,6 +308,8 @@ function_set! {
resolve_host_name,
resolve_hostname_to_multiple_ips,
(target_is_ipv6, "TARGET_IS_IPV6"),
same_host
same_host,
add_host_name,
get_host_name_source
)
}
7 changes: 5 additions & 2 deletions rust/src/nasl/builtin/mod.rs
Original file line number Diff line number Diff line change
@@ -30,6 +30,8 @@ use crate::nasl::syntax::{Loader, NoOpLoader};
use crate::nasl::utils::{Context, Executor, NaslVarRegister, NaslVarRegisterBuilder, Register};
use crate::storage::{ContextKey, DefaultDispatcher, Storage};

use super::utils::context::Target;

/// Creates a new Executor and adds all the functions to it.
///
/// When you have a function that is considered experimental due to either dependencies on
@@ -137,11 +139,12 @@ where

/// Creates a new Context with the shared loader, logger and function register
pub fn build(&self, key: ContextKey) -> 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,
6 changes: 2 additions & 4 deletions rust/src/nasl/builtin/ssh/mod.rs
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ mod tests;
pub use error::SshError;
pub use sessions::SshSessions as Ssh;

use std::time::Duration;
use std::{borrow::BorrowMut, time::Duration};

use ::russh::{cipher, Preferred};
use russh_keys::key;
@@ -153,9 +153,7 @@ impl Ssh {
let port = port
.filter(|_| socket.is_none())
.unwrap_or(DEFAULT_SSH_PORT);
let ip = ctx.target_ip().map_err(|e| {
SshError::from(SshErrorKind::InvalidIpAddr(ctx.target().to_string(), e))
})?;
let ip = ctx.target_ip();
let timeout = timeout.map(Duration::from_secs);
let keytype = keytype
.map(|keytype| keytype.0)
Loading