Skip to content

Commit 193c654

Browse files
committed
Implement smb and wmi functions
1 parent d3f21ab commit 193c654

8 files changed

Lines changed: 1971 additions & 264 deletions

File tree

rust/Cargo.lock

Lines changed: 1620 additions & 249 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,12 @@ bzip2 = "0"
140140
zstd = "0"
141141
rustls-pki-types = { version = "1.14.0", features = ["std"] }
142142
cidr = "0.3.2"
143+
smb = "0.11.2"
144+
smb-msg = "0.11.2"
145+
use = "0.0.1-pre.0"
146+
smb-dtyp = "0.11.2"
147+
sddl = "0.1.9"
148+
binrw = "0.15.1"
143149

144150
[workspace]
145151
members = ["crates/nasl-function-proc-macro"]

rust/src/nasl/builtin/error.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use super::find_service::FindServiceError;
1616
use super::host::HostError;
1717
use super::http::HttpError;
1818
use super::isotime::IsotimeError;
19+
use super::naslsmb::SmbError;
1920
use super::raw_ip::RawIpError;
2021
use super::regex::RegexError;
2122
use super::snmp::SnmpError;
@@ -29,6 +30,8 @@ pub enum BuiltinError {
2930
#[error("{0}")]
3031
Http(HttpError),
3132
#[error("{0}")]
33+
NaslWmi(SmbError),
34+
#[error("{0}")]
3235
Notus(NotusError),
3336
#[error("{0}")]
3437
String(StringError),
@@ -97,6 +100,7 @@ builtin_error_variant!(SocketError, Socket);
97100
builtin_error_variant!(CryptographicError, Cryptographic);
98101
builtin_error_variant!(SshError, Ssh);
99102
builtin_error_variant!(HttpError, Http);
103+
builtin_error_variant!(SmbError, NaslWmi);
100104
builtin_error_variant!(IsotimeError, Isotime);
101105
builtin_error_variant!(RegexError, Regex);
102106
builtin_error_variant!(KBError, KB);

rust/src/nasl/builtin/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ mod isotime;
1616
mod knowledge_base;
1717
mod krb5;
1818
pub mod misc;
19+
mod naslsmb;
1920
pub mod network;
2021
mod notus;
2122
mod preferences;
@@ -51,6 +52,7 @@ pub fn nasl_std_functions() -> Executor {
5152
.add_set(host::Host)
5253
.add_set(http::NaslHttp2::default())
5354
.add_set(http::NaslHttp)
55+
.add_set(naslsmb::Smb::default())
5456
.add_set(network::socket::SocketFns)
5557
.add_set(network::network::Network)
5658
.add_set(regex::RegularExpressions)
@@ -62,9 +64,9 @@ pub fn nasl_std_functions() -> Executor {
6264
.add_set(sys::Sys)
6365
.add_set(ssh::Ssh::default())
6466
.add_set(find_service::FindService)
65-
.add_set(wmi::Wmi)
6667
.add_set(snmp::Snmp)
6768
.add_set(cert::NaslCerts::default())
69+
.add_set(wmi::Wmi)
6870
.add_set(notus::NaslNotus::default());
6971

7072
executor.add_set(krb5::Krb5::default());
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// SPDX-FileCopyrightText: 2025 Greenbone AG
2+
//
3+
// SPDX-License-Identifier: GPL-2.0-or-later WITH x11vnc-openssl-exception
4+
5+
use std::io;
6+
7+
use thiserror::Error;
8+
9+
pub type Result<T> = std::result::Result<T, SmbError>;
10+
11+
#[derive(Debug, Error)]
12+
pub enum SmbError {
13+
#[error("Handle ID {0} not found.")]
14+
SMBHandleIdNotFound(i32),
15+
#[error("SMB error: {0}")]
16+
Smb(String),
17+
#[error("IO error during SMB: {0}")]
18+
IO(io::ErrorKind),
19+
#[error("Invalid host: {0}")]
20+
InvalidHost(String),
21+
#[error("SMB errror serializing response: {0}")]
22+
SerializeError(String),
23+
#[error("SMB query: {0}")]
24+
SmbQuery(String),
25+
}
26+
27+
impl From<io::Error> for SmbError {
28+
fn from(value: io::Error) -> Self {
29+
Self::IO(value.kind())
30+
}
31+
}
32+
33+
impl From<smb::Error> for SmbError {
34+
fn from(value: smb::Error) -> Self {
35+
Self::Smb(format!("{value}"))
36+
}
37+
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
// SPDX-FileCopyrightText: 2026 Greenbone AG
2+
//
3+
// SPDX-License-Identifier: GPL-2.0-or-later WITH x11vnc-openssl-exception
4+
5+
use super::error::{Result, SmbError};
6+
use smb::{
7+
AdditionalInfo, Client, ClientConfig, FileAccessMask, FileCreateArgs, Resource, UncPath,
8+
};
9+
use std::{
10+
collections::{HashMap, HashSet},
11+
io::Cursor,
12+
};
13+
use tokio::sync::{Mutex, MutexGuard};
14+
15+
use binrw::BinWrite;
16+
type BorrowedHandle<'a> = MutexGuard<'a, SmbHandle>;
17+
pub type HandleId = i32;
18+
19+
pub const MIN_HANDLE_ID: HandleId = 9000;
20+
21+
#[derive(Default)]
22+
pub struct SmbHandle {
23+
/// SMB Client
24+
handle: Client,
25+
target_path: Option<UncPath>,
26+
}
27+
28+
impl SmbHandle {
29+
pub async fn new(
30+
server: String,
31+
share: String,
32+
username: String,
33+
password: String,
34+
) -> Result<Self> {
35+
let mut config = ClientConfig::default();
36+
config.connection.auth_methods.ntlm = true;
37+
38+
let handle = Client::new(config);
39+
let target_path = UncPath::new(&server)
40+
.map_err(SmbError::from)?
41+
.with_share(share.as_str())
42+
.map_err(SmbError::from)?;
43+
44+
handle
45+
.share_connect(&target_path, &username, password)
46+
.await
47+
.map_err(SmbError::from)?;
48+
49+
Ok(Self {
50+
handle,
51+
target_path: Some(target_path),
52+
})
53+
}
54+
55+
pub async fn disconnect(&mut self) -> Result<()> {
56+
self.handle.close().await.map_err(SmbError::from)
57+
}
58+
59+
pub async fn get_full_info(&self, filename: String) -> Result<String> {
60+
// Safe to unwrap because there is always a server and share.
61+
let target_path = self
62+
.target_path
63+
.clone()
64+
.unwrap()
65+
.with_path(filename.as_str());
66+
67+
// Define read access mask
68+
let file_open_args =
69+
FileCreateArgs::make_open_existing(FileAccessMask::new().with_generic_read(true));
70+
71+
let resource = self.handle.create_file(&target_path, &file_open_args).await;
72+
73+
// From openvas-smb the query secinfo flags are 0x7 which means the following three.
74+
let additional_info = AdditionalInfo::new()
75+
.with_owner_security_information(true)
76+
.with_group_security_information(true)
77+
.with_dacl_security_information(true);
78+
79+
let response = match resource {
80+
Ok(Resource::File(f)) => {
81+
let r = f
82+
.query_security_info(additional_info)
83+
.await
84+
.map_err(|e| SmbError::SmbQuery(e.to_string()))?;
85+
f.close().await.map_err(|e| SmbError::Smb(e.to_string()))?;
86+
r
87+
}
88+
Ok(Resource::Directory(d)) => {
89+
let r = d
90+
.query_security_info(additional_info)
91+
.await
92+
.map_err(|e| SmbError::SmbQuery(e.to_string()))?;
93+
d.close().await.map_err(|e| SmbError::Smb(e.to_string()))?;
94+
r
95+
}
96+
Ok(Resource::Pipe(p)) => {
97+
let r = p
98+
.query_security_info(additional_info)
99+
.await
100+
.map_err(|e| SmbError::SmbQuery(e.to_string()))?;
101+
p.close().await.map_err(|e| SmbError::Smb(e.to_string()))?;
102+
r
103+
}
104+
Err(e) => return Err(SmbError::Smb(e.to_string())),
105+
};
106+
107+
// convert response to SDDL format
108+
let mut writer = Cursor::new(Vec::new());
109+
response
110+
.write(&mut writer)
111+
.map_err(|e| SmbError::SerializeError(e.to_string()))?;
112+
let raw_sd = writer.into_inner();
113+
let sd = sddl::SecurityDescriptor::from_bytes(&raw_sd)
114+
.map_err(|e| SmbError::SerializeError(e.to_string()))?;
115+
let ret = format!("{}", sd);
116+
117+
Ok(ret)
118+
}
119+
}
120+
121+
#[derive(Default)]
122+
pub struct SmbHandles {
123+
handles: HashMap<HandleId, Mutex<SmbHandle>>,
124+
}
125+
126+
impl SmbHandles {
127+
pub async fn get_by_id(&self, id: HandleId) -> Result<BorrowedHandle<'_>> {
128+
Ok(self
129+
.handles
130+
.get(&id)
131+
.ok_or(SmbError::SMBHandleIdNotFound(id))?
132+
.lock()
133+
.await)
134+
}
135+
136+
/// Return the next available session ID
137+
pub fn next_handle_id(&self) -> Result<HandleId> {
138+
// Note that the first session ID we will
139+
// hand out is an arbitrary high number, this is only to help
140+
// debugging.
141+
let taken_ids: HashSet<_> = self.handles.keys().collect();
142+
if taken_ids.is_empty() {
143+
Ok(MIN_HANDLE_ID)
144+
} else {
145+
let max_val = **taken_ids.iter().max().unwrap() + 1;
146+
Ok((MIN_HANDLE_ID..=max_val)
147+
.find(|id| !taken_ids.contains(id))
148+
.unwrap())
149+
}
150+
}
151+
152+
pub fn insert(&mut self, handle_id: HandleId, handle: Mutex<SmbHandle>) {
153+
self.handles.insert(handle_id, handle);
154+
}
155+
156+
pub fn remove(&mut self, handle_id: HandleId) -> Result<()> {
157+
self.handles.remove(&handle_id);
158+
Ok(())
159+
}
160+
}

0 commit comments

Comments
 (0)