use log::{debug, error, trace, warn};
use smb2_client::SmbClient;
pub enum SmbAuth<'a> {
Password(&'a str),
Hash(&'a [u8; 16]),
}
pub fn ipc_unc(host: &str) -> String {
format!(r"\\{host}\IPC$")
}
pub fn sysvol_unc(host: &str) -> String {
format!(r"\\{host}\SYSVOL")
}
pub async fn connect_authenticated(
host: &str,
domain: &str,
user: &str,
auth: SmbAuth<'_>,
) -> anyhow::Result<SmbClient> {
let target = format!("{host}:445");
trace!("[{host}] SMB connecting to {target}");
let mut smb = SmbClient::connect(&target).await.map_err(|e| {
error!("[{host}] SMB connect failed: {e}");
anyhow::anyhow!("connect: {e}")
})?;
debug!("[{host}] SMB connected, negotiate done");
match auth {
SmbAuth::Hash(nt) => {
trace!("[{host}] SMB SESSION_SETUP as {domain}\\{user} (pass the hash)");
smb.login_hash(host, domain, user, nt).await.map_err(|e| {
error!("[{host}] SMB auth failed for {domain}\\{user} (pass the hash): {e}");
anyhow::anyhow!("auth(PTH): {e}")
})?;
}
SmbAuth::Password(password) => {
trace!("[{host}] SMB SESSION_SETUP as {domain}\\{user} (password)");
smb.login(host, domain, user, password).await.map_err(|e| {
error!("[{host}] SMB auth failed for {domain}\\{user}: {e}");
anyhow::anyhow!("auth: {e}")
})?;
}
}
debug!("[{host}] SMB authenticated as {domain}\\{user}");
Ok(smb)
}
pub async fn tree_connect(smb: &mut SmbClient, host: &str, unc: &str) -> anyhow::Result<()> {
trace!("[{host}] SMB tree connect {unc}");
smb.tree_connect(unc).await.map_err(|e| {
error!("[{host}] SMB tree connect to {unc} failed: {e}");
anyhow::anyhow!("tree connect {unc}: {e}")
})?;
debug!("[{host}] tree connected: {unc}");
Ok(())
}
pub async fn connect_ipc(
host: &str,
domain: &str,
user: &str,
auth: SmbAuth<'_>,
) -> anyhow::Result<SmbClient> {
let mut smb = connect_authenticated(host, domain, user, auth).await?;
tree_connect(&mut smb, host, &ipc_unc(host)).await?;
debug!("[{host}] IPC$ ready, RPC pipes available");
Ok(smb)
}
pub async fn connect_sysvol(
host: &str,
domain: &str,
user: &str,
auth: SmbAuth<'_>,
) -> anyhow::Result<SmbClient> {
let mut smb = connect_authenticated(host, domain, user, auth).await?;
tree_connect(&mut smb, host, &sysvol_unc(host)).await?;
debug!("[{host}] SYSVOL ready, GPO files readable");
Ok(smb)
}
pub async fn open_rpc_pipe(
smb: &mut SmbClient,
host: &str,
pipe: &str,
) -> anyhow::Result<[u8; 16]> {
trace!("[{host}] opening RPC pipe {pipe}");
match smb.open_pipe(pipe).await {
Ok(file_id) => {
debug!("[{host}] RPC pipe {pipe} opened");
Ok(file_id)
}
Err(e) => {
warn!("[{host}] cannot open RPC pipe {pipe}: {e}");
Err(anyhow::anyhow!("{pipe}: {e}"))
}
}
}