diff --git a/rust/src/nasl/builtin/ssh/libssh/mod.rs b/rust/src/nasl/builtin/ssh/libssh/mod.rs index 75a9f8b2c..6b2073a4d 100644 --- a/rust/src/nasl/builtin/ssh/libssh/mod.rs +++ b/rust/src/nasl/builtin/ssh/libssh/mod.rs @@ -79,7 +79,7 @@ impl Ssh { /// /// nasl return An integer to identify the ssh session. Zero on error. #[nasl_function(named(socket, port, keytype, csciphers, scciphers, timeout))] - fn nasl_ssh_connect( + async fn nasl_ssh_connect( &mut self, socket: Option, port: Option, @@ -87,7 +87,7 @@ impl Ssh { csciphers: Option<&str>, scciphers: Option<&str>, timeout: Option, - ctx: &Context, + ctx: &Context<'_>, ) -> Result { let port = port.filter(|_| socket.is_none()); let ip_str: String = match ctx.target() { @@ -95,47 +95,49 @@ impl Ssh { _ => "127.0.0.1".to_string(), }; - let session_id = self.add_new_session(|session| { - session.set_option(SshOption::LogLevel(get_log_level()))?; - session.set_option(SshOption::Hostname(ip_str.to_owned()))?; - session.set_option(SshOption::KnownHosts(Some("/dev/null".to_owned())))?; - if let Some(timeout) = timeout { - session.set_option(SshOption::Timeout(Duration::from_secs(timeout as u64)))?; - } - if let Some(keytype) = keytype { - session.set_option(SshOption::HostKeys(keytype.to_owned()))?; - } - if let Some(csciphers) = csciphers { - session.set_option(SshOption::CiphersCS(csciphers.to_owned()))?; - } - if let Some(scciphers) = scciphers { - session.set_option(SshOption::CiphersSC(scciphers.to_owned()))?; - } - if let Some(port) = port { - session.set_option(SshOption::Port(port))?; - } + let session_id = self + .add_new_session(|session| { + session.set_option(SshOption::LogLevel(get_log_level()))?; + session.set_option(SshOption::Hostname(ip_str.to_owned()))?; + session.set_option(SshOption::KnownHosts(Some("/dev/null".to_owned())))?; + if let Some(timeout) = timeout { + session.set_option(SshOption::Timeout(Duration::from_secs(timeout as u64)))?; + } + if let Some(keytype) = keytype { + session.set_option(SshOption::HostKeys(keytype.to_owned()))?; + } + if let Some(csciphers) = csciphers { + session.set_option(SshOption::CiphersCS(csciphers.to_owned()))?; + } + if let Some(scciphers) = scciphers { + session.set_option(SshOption::CiphersSC(scciphers.to_owned()))?; + } + if let Some(port) = port { + session.set_option(SshOption::Port(port))?; + } - if let Some(socket) = socket { - // This is a fake raw socket. - // TODO: implement openvas_get_socket_from_connection() - let my_sock = UdpSocket::bind("127.0.0.1:0").unwrap(); + if let Some(socket) = socket { + // This is a fake raw socket. + // TODO: implement openvas_get_socket_from_connection() + let my_sock = UdpSocket::bind("127.0.0.1:0").unwrap(); + debug!( + ip_str = ip_str, + sock_fd = my_sock.as_raw_fd(), + nasl_sock = socket, + "Setting SSH fd for socket", + ); + session.set_option(SshOption::Socket(my_sock.as_raw_fd()))?; + } debug!( ip_str = ip_str, - sock_fd = my_sock.as_raw_fd(), - nasl_sock = socket, - "Setting SSH fd for socket", + port = port, + socket = socket, + "Connecting to SSH server", ); - session.set_option(SshOption::Socket(my_sock.as_raw_fd()))?; - } - debug!( - ip_str = ip_str, - port = port, - socket = socket, - "Connecting to SSH server", - ); - session.connect()?; - Ok(()) - })?; + session.connect()?; + Ok(()) + }) + .await?; Ok(session_id) } @@ -146,10 +148,10 @@ impl Ssh { /// channels they are closed as well and their ids will be marked as /// invalid. #[nasl_function] - fn nasl_ssh_disconnect(&mut self, session_id: SessionId) -> Result<()> { + async fn nasl_ssh_disconnect(&mut self, session_id: SessionId) -> Result<()> { if session_id != 0 { { - let mut session = self.get_by_id(session_id)?; + let mut session = self.get_by_id(session_id).await?; session.disconnect()?; } self.remove(session_id)?; @@ -159,8 +161,10 @@ impl Ssh { /// Given a socket, return the corresponding session id if available. #[nasl_function] - fn nasl_ssh_session_id_from_sock(&self, socket: Socket) -> Result> { - Ok(self.find_id(|session| session.get_socket() == socket)?) + async fn nasl_ssh_session_id_from_sock(&self, socket: Socket) -> Result> { + Ok(self + .find_id(|session| session.get_socket() == socket) + .await?) } /// Given a session id, return the corresponding socket @@ -173,8 +177,8 @@ impl Ssh { /// /// return An integer representing the socket or -1 on error. #[nasl_function] - fn nasl_ssh_get_sock(&self, session_id: SessionId) -> Result { - let session = self.get_by_id(session_id)?; + async fn nasl_ssh_get_sock(&self, session_id: SessionId) -> Result { + let session = self.get_by_id(session_id).await?; Ok(session.get_socket()) } /// Set the login name for the authentication. @@ -191,8 +195,8 @@ impl Ssh { /// for an established connection, the "login" parameter is silently /// ignored on all further calls. #[nasl_function(named(login))] - fn nasl_ssh_set_login(&self, session_id: SessionId, login: Option<&str>) -> Result<()> { - let mut session = self.get_by_id(session_id)?; + async fn nasl_ssh_set_login(&self, session_id: SessionId, login: Option<&str>) -> Result<()> { + let mut session = self.get_by_id(session_id).await?; Ok(session.set_opt_user(login)?) } @@ -245,7 +249,7 @@ impl Ssh { /// /// return An integer as status value; 0 indicates success. #[nasl_function(named(login, password, privatekey, passphrase))] - fn nasl_ssh_userauth( + async fn nasl_ssh_userauth( &self, session_id: SessionId, login: Option<&str>, @@ -260,7 +264,7 @@ impl Ssh { session_id ))); } - let mut session = self.get_by_id(session_id)?; + let mut session = self.get_by_id(session_id).await?; session.ensure_user_set(login)?; let methods: AuthMethods = session.get_authmethods_cached()?; @@ -387,14 +391,14 @@ impl Ssh { /// If the named parameters @a stdout and @a stderr are not given, the /// function acts exactly as if only @a stdout has been set to 1. #[nasl_function(named(cmd, stdout, stderr))] - fn nasl_ssh_request_exec( + async fn nasl_ssh_request_exec( &self, session_id: SessionId, cmd: &str, stdout: Option, stderr: Option, ) -> Result> { - let session = self.get_by_id(session_id)?; + let session = self.get_by_id(session_id).await?; if cmd.is_empty() { return Ok(None); } @@ -413,8 +417,12 @@ impl Ssh { /// Open a new ssh shell. #[nasl_function(named(pty))] - fn nasl_ssh_shell_open(&self, session_id: SessionId, pty: Option) -> Result { - let mut session = self.get_by_id(session_id)?; + async fn nasl_ssh_shell_open( + &self, + session_id: SessionId, + pty: Option, + ) -> Result { + let mut session = self.get_by_id(session_id).await?; let pty = pty.unwrap_or(true); session.open_shell(pty)?; Ok(session.id()) @@ -425,12 +433,12 @@ impl Ssh { /// there are no more bytes left to read. Otherwise use non_blocking /// read mode. #[nasl_function] - fn nasl_ssh_shell_read( + async fn nasl_ssh_shell_read( &self, session_id: SessionId, timeout: Option>, ) -> Result { - let session = self.get_by_id(session_id)?; + let session = self.get_by_id(session_id).await?; let timeout = Duration::from_secs(timeout.and_then(Maybe::as_option).unwrap_or(0)); let channel = session.get_channel()?; channel.ensure_open()?; @@ -444,8 +452,8 @@ impl Ssh { /// Write the string `cmd` to an ssh shell. #[nasl_function] - fn nasl_ssh_shell_write(&self, session_id: SessionId, cmd: StringOrData) -> Result { - let session = self.get_by_id(session_id)?; + async fn nasl_ssh_shell_write(&self, session_id: SessionId, cmd: StringOrData) -> Result { + let session = self.get_by_id(session_id).await?; let channel = session.get_channel()?; channel.ensure_open()?; @@ -458,8 +466,8 @@ impl Ssh { /// Close an ssh shell. #[nasl_function] - fn nasl_ssh_shell_close(&self, session_id: SessionId) -> Result<()> { - let mut session = self.get_by_id(session_id)?; + async fn nasl_ssh_shell_close(&self, session_id: SessionId) -> Result<()> { + let mut session = self.get_by_id(session_id).await?; session.close(); Ok(()) } @@ -472,12 +480,12 @@ impl Ssh { /// The first time this function is called for a session id, the named /// argument "login" is also expected. #[nasl_function(named(login))] - fn nasl_ssh_login_interactive( + async fn nasl_ssh_login_interactive( &self, session_id: SessionId, login: Option<&str>, ) -> Result> { - let mut session = self.get_by_id(session_id)?; + let mut session = self.get_by_id(session_id).await?; session.ensure_user_set(login)?; let methods = session.get_authmethods_cached()?; debug!("Available methods:\n{:?}", methods); @@ -522,8 +530,12 @@ impl Ssh { /// The function finishes the authentication process started by /// ssh_login_interactive. #[nasl_function(named(password))] - fn nasl_ssh_login_interactive_pass(&self, session_id: SessionId, password: &str) -> Result<()> { - let session = self.get_by_id(session_id)?; + async fn nasl_ssh_login_interactive_pass( + &self, + session_id: SessionId, + password: &str, + ) -> Result<()> { + let session = self.get_by_id(session_id).await?; let info = session.userauth_keyboard_interactive_info()?; debug!( name = info.name, @@ -565,8 +577,8 @@ impl Ssh { /// The function returns a string with the issue banner. This is /// usually displayed before authentication. #[nasl_function] - fn nasl_ssh_get_issue_banner(&self, session_id: SessionId) -> Result> { - let mut session = self.get_by_id(session_id)?; + async fn nasl_ssh_get_issue_banner(&self, session_id: SessionId) -> Result> { + let mut session = self.get_by_id(session_id).await?; session.ensure_user_set(None)?; session.get_authmethods_cached()?; Ok(session.get_issue_banner().ok()) @@ -575,8 +587,8 @@ impl Ssh { /// The function returns a string with the server banner. This is /// usually the first data sent by the server. #[nasl_function] - fn nasl_ssh_get_server_banner(&self, session_id: SessionId) -> Result> { - let session = self.get_by_id(session_id)?; + async fn nasl_ssh_get_server_banner(&self, session_id: SessionId) -> Result> { + let session = self.get_by_id(session_id).await?; // TODO: Check with openvas-nasl why the outputs doesn't match Ok(session.get_server_banner().ok()) } @@ -586,8 +598,8 @@ impl Ssh { /// SSH_MSG_USERAUTH_FAILURE protocol element; however, it has been /// screened and put into a definitive order. #[nasl_function] - fn nasl_ssh_get_auth_methods(&self, session_id: SessionId) -> Result> { - let mut session = self.get_by_id(session_id)?; + async fn nasl_ssh_get_auth_methods(&self, session_id: SessionId) -> Result> { + let mut session = self.get_by_id(session_id).await?; session.ensure_user_set(None)?; let authmethods = session.get_authmethods_cached()?; @@ -616,8 +628,8 @@ impl Ssh { /// Return the MD5 host key. #[nasl_function] - fn nasl_ssh_get_host_key(&self, session_id: SessionId) -> Result> { - let session = self.get_by_id(session_id)?; + async fn nasl_ssh_get_host_key(&self, session_id: SessionId) -> Result> { + let session = self.get_by_id(session_id).await?; let key = session.get_server_public_key()?; match key.get_public_key_hash_hexa(libssh_rs::PublicKeyHashType::Md5) { Ok(hash) => Ok(Some(hash)), @@ -627,8 +639,8 @@ impl Ssh { /// Check if the SFTP subsystem is enabled on the remote SSH server. #[nasl_function] - fn nasl_sftp_enabled_check(&self, session_id: SessionId) -> Result { - let session = self.get_by_id(session_id)?; + async fn nasl_sftp_enabled_check(&self, session_id: SessionId) -> Result { + let session = self.get_by_id(session_id).await?; match session.sftp() { Ok(_) => Ok(0), Err(e) => { @@ -640,8 +652,8 @@ impl Ssh { /// Execute the NETCONF subsystem on the the ssh channel #[nasl_function] - fn nasl_ssh_execute_netconf_subsystem(&self, session_id: SessionId) -> Result { - let mut session = self.get_by_id(session_id)?; + async fn nasl_ssh_execute_netconf_subsystem(&self, session_id: SessionId) -> Result { + let mut session = self.get_by_id(session_id).await?; let channel = session.new_channel()?; channel.open_session()?; channel.request_subsystem("netconf")?; @@ -654,31 +666,31 @@ impl IntoFunctionSet for Ssh { type State = Ssh; fn into_function_set(self) -> StoredFunctionSet { let mut set = StoredFunctionSet::new(self); - set.sync_stateful_mut("ssh_connect", Ssh::nasl_ssh_connect); - set.sync_stateful_mut("ssh_disconnect", Ssh::nasl_ssh_disconnect); - set.sync_stateful( + set.async_stateful_mut("ssh_connect", Ssh::nasl_ssh_connect); + set.async_stateful_mut("ssh_disconnect", Ssh::nasl_ssh_disconnect); + set.async_stateful( "ssh_session_id_from_sock", Ssh::nasl_ssh_session_id_from_sock, ); - set.sync_stateful("ssh_get_sock", Ssh::nasl_ssh_get_sock); - set.sync_stateful("ssh_set_login", Ssh::nasl_ssh_set_login); - set.sync_stateful("ssh_userauth", Ssh::nasl_ssh_userauth); - set.sync_stateful("ssh_request_exec", Ssh::nasl_ssh_request_exec); - set.sync_stateful("ssh_shell_open", Ssh::nasl_ssh_shell_open); - set.sync_stateful("ssh_shell_read", Ssh::nasl_ssh_shell_read); - set.sync_stateful("ssh_shell_write", Ssh::nasl_ssh_shell_write); - set.sync_stateful("ssh_shell_close", Ssh::nasl_ssh_shell_close); - set.sync_stateful("ssh_login_interactive", Ssh::nasl_ssh_login_interactive); - set.sync_stateful( + set.async_stateful("ssh_get_sock", Ssh::nasl_ssh_get_sock); + set.async_stateful("ssh_set_login", Ssh::nasl_ssh_set_login); + set.async_stateful("ssh_userauth", Ssh::nasl_ssh_userauth); + set.async_stateful("ssh_request_exec", Ssh::nasl_ssh_request_exec); + set.async_stateful("ssh_shell_open", Ssh::nasl_ssh_shell_open); + set.async_stateful("ssh_shell_read", Ssh::nasl_ssh_shell_read); + set.async_stateful("ssh_shell_write", Ssh::nasl_ssh_shell_write); + set.async_stateful("ssh_shell_close", Ssh::nasl_ssh_shell_close); + set.async_stateful("ssh_login_interactive", Ssh::nasl_ssh_login_interactive); + set.async_stateful( "ssh_login_interactive_pass", Ssh::nasl_ssh_login_interactive_pass, ); - set.sync_stateful("ssh_get_issue_banner", Ssh::nasl_ssh_get_issue_banner); - set.sync_stateful("ssh_get_server_banner", Ssh::nasl_ssh_get_server_banner); - set.sync_stateful("ssh_get_auth_methods", Ssh::nasl_ssh_get_auth_methods); - set.sync_stateful("ssh_get_host_key", Ssh::nasl_ssh_get_host_key); - set.sync_stateful("sftp_enabled_check", Ssh::nasl_sftp_enabled_check); - set.sync_stateful( + set.async_stateful("ssh_get_issue_banner", Ssh::nasl_ssh_get_issue_banner); + set.async_stateful("ssh_get_server_banner", Ssh::nasl_ssh_get_server_banner); + set.async_stateful("ssh_get_auth_methods", Ssh::nasl_ssh_get_auth_methods); + set.async_stateful("ssh_get_host_key", Ssh::nasl_ssh_get_host_key); + set.async_stateful("sftp_enabled_check", Ssh::nasl_sftp_enabled_check); + set.async_stateful( "ssh_execute_netconf_subsystem", Ssh::nasl_ssh_execute_netconf_subsystem, ); diff --git a/rust/src/nasl/builtin/ssh/libssh/session.rs b/rust/src/nasl/builtin/ssh/libssh/session.rs index b716aa478..cc0aa36d8 100644 --- a/rust/src/nasl/builtin/ssh/libssh/session.rs +++ b/rust/src/nasl/builtin/ssh/libssh/session.rs @@ -1,6 +1,6 @@ use libssh_rs::{AuthMethods, AuthStatus, InteractiveAuthInfo, Session, Sftp, SshKey, SshOption}; -use std::sync::MutexGuard; use std::{os::fd::AsRawFd, time::Duration}; +use tokio::sync::MutexGuard; use tracing::{debug, info}; use crate::nasl::builtin::ssh::SessionId; diff --git a/rust/src/nasl/builtin/ssh/libssh/sessions.rs b/rust/src/nasl/builtin/ssh/libssh/sessions.rs index c252ad993..0b111a9e4 100644 --- a/rust/src/nasl/builtin/ssh/libssh/sessions.rs +++ b/rust/src/nasl/builtin/ssh/libssh/sessions.rs @@ -6,7 +6,7 @@ use std::collections::{HashMap, HashSet}; -use std::sync::Mutex; +use tokio::sync::Mutex; use crate::nasl::builtin::ssh::SessionId; @@ -21,13 +21,13 @@ pub struct Ssh { } impl Ssh { - pub fn get_by_id(&self, id: SessionId) -> Result { + pub async fn get_by_id(&self, id: SessionId) -> Result { Ok(BorrowedSession::new( self.sessions .get(&id) .ok_or_else(|| SshError::InvalidSessionId(id))? .lock() - .map_err(|_| SshError::PoisonedLock)?, + .await, )) } @@ -53,12 +53,12 @@ impl Ssh { Ok(()) } - pub fn find_id<'a>( + pub async fn find_id<'a>( &'a self, f: impl for<'b> Fn(&BorrowedSession<'b>) -> bool, ) -> Result> { for id in self.sessions.keys() { - let session = self.get_by_id(*id)?; + let session = self.get_by_id(*id).await?; if f(&session) { return Ok(Some(session.id())); } @@ -68,15 +68,14 @@ impl Ssh { /// Create a new session, but only add it to the list of active sessions /// if the given closure which modifies the session returns Ok(...). - pub fn add_new_session( + pub async fn add_new_session( &mut self, f: impl Fn(&mut BorrowedSession) -> Result<()>, ) -> Result { let id = self.next_session_id()?; let session = Mutex::new(SshSession::new(id)?); { - let mut borrowed_session = - BorrowedSession::new(session.lock().map_err(|_| SshError::PoisonedLock)?); + let mut borrowed_session = BorrowedSession::new(session.lock().await); if let Err(e) = f(&mut borrowed_session) { borrowed_session.disconnect()?; return Err(e);