1- use std:: collections:: HashMap ;
2- use std:: sync:: LazyLock ;
31use std:: time:: Duration ;
42
53use async_trait:: async_trait;
6- use pavao:: { SmbClient , SmbCredentials , SmbDirentType , SmbOptions } ;
7- use tokio:: sync:: Mutex ;
84
5+ use crate :: Plugin ;
96use crate :: creds:: Credentials ;
107use crate :: session:: { Error , Loot } ;
11- use crate :: Plugin ;
12- use crate :: { utils, Options } ;
13-
14- pub ( crate ) mod options;
15-
16- static SHARE_CACHE : LazyLock < Mutex < HashMap < String , String > > > =
17- LazyLock :: new ( || Mutex :: new ( HashMap :: new ( ) ) ) ;
18- static PAVAO_LOCK : Mutex < ( ) > = Mutex :: const_new ( ( ) ) ;
8+ use crate :: { Options , utils} ;
199
2010super :: manager:: register_plugin! {
2111 "smb" => SMB :: new( )
2212}
2313
2414#[ derive( Clone ) ]
25- pub ( crate ) struct SMB {
26- share : Option < String > ,
27- workgroup : String ,
28- }
15+ pub ( crate ) struct SMB { }
2916
3017impl SMB {
3118 pub fn new ( ) -> Self {
32- SMB {
33- share : None ,
34- workgroup : String :: default ( ) ,
35- }
36- }
37-
38- fn get_samba_client (
39- & self ,
40- server : & str ,
41- workgroup : & str ,
42- share : & str ,
43- username : & str ,
44- password : & str ,
45- ) -> Result < SmbClient , Error > {
46- SmbClient :: new (
47- SmbCredentials :: default ( )
48- . server ( server)
49- . share ( share)
50- . username ( username)
51- . password ( password)
52- . workgroup ( workgroup) ,
53- SmbOptions :: default ( )
54- . no_auto_anonymous_login ( false )
55- . one_share_per_server ( true ) ,
56- )
57- . map_err ( |e| format ! ( "error creating client for {}: {}" , share, e) )
58- }
59-
60- async fn get_share_for ( & self , target : & str ) -> Result < String , Error > {
61- if let Some ( share) = self . share . as_ref ( ) {
62- // return from arguments
63- return Ok ( share. clone ( ) ) ;
64- }
65-
66- let mut guard = SHARE_CACHE . lock ( ) . await ;
67- if let Some ( share) = guard. get ( target) {
68- // return from cache
69- return Ok ( share. clone ( ) ) ;
70- }
71-
72- // get from listing
73- log:: info!( "searching private share for {} ..." , target) ;
74-
75- let server = format ! ( "smb://{}" , target) ;
76- let root_cli = self . get_samba_client ( & server, & self . workgroup , "" , "" , "" ) ?;
77-
78- if let Ok ( entries) = root_cli. list_dir ( "" ) {
79- for entry in entries {
80- match entry. get_type ( ) {
81- SmbDirentType :: FileShare | SmbDirentType :: Dir => {
82- let share = format ! ( "/{}" , entry. name( ) ) ;
83- // if share is private we expect an error
84- let sub_cli =
85- self . get_samba_client ( & server, & self . workgroup , & share, "" , "" ) ?;
86- let listing = sub_cli. list_dir ( "" ) ;
87- if listing. is_err ( ) {
88- log:: info!( "{}{} found" , & server, & share) ;
89- // found a private share, update the cache and return.
90- guard. insert ( target. to_owned ( ) , share. clone ( ) ) ;
91- return Ok ( share) ;
92- }
93- }
94- _ => { }
95- }
96- }
97- }
98-
99- Err ( format ! (
100- "could not find private share for {}, provide one with --smb-share" ,
101- target
102- ) )
19+ SMB { }
10320 }
10421}
10522
@@ -109,9 +26,7 @@ impl Plugin for SMB {
10926 "Samba password authentication."
11027 }
11128
112- async fn setup ( & mut self , opts : & Options ) -> Result < ( ) , Error > {
113- self . share = opts. smb . smb_share . clone ( ) ;
114- self . workgroup = opts. smb . smb_workgroup . clone ( ) ;
29+ async fn setup ( & mut self , _: & Options ) -> Result < ( ) , Error > {
11530 Ok ( ( ) )
11631 }
11732
@@ -120,36 +35,41 @@ impl Plugin for SMB {
12035 creds : & Credentials ,
12136 timeout : Duration ,
12237 ) -> Result < Option < Vec < Loot > > , Error > {
123- let address = utils:: parse_target_address ( & creds. target , 445 ) ?;
124- let server = format ! ( "smb://{}" , & address) ;
125- let share = tokio:: time:: timeout ( timeout, self . get_share_for ( & address) )
126- . await
127- . map_err ( |e : tokio:: time:: error:: Elapsed | e. to_string ( ) ) ?
38+ let ( address, port) = utils:: parse_target ( & creds. target , 445 ) ?;
39+
40+ let mut config = smb:: ClientConfig :: default ( ) ;
41+
42+ config. connection . port = Some ( port) ;
43+ config. connection . timeout = Some ( timeout) ;
44+
45+ let mut conn = smb:: Connection :: build ( & address, config. connection . clone ( ) )
12846 . map_err ( |e| e. to_string ( ) ) ?;
12947
130- // HACK: pavao doesn't seem to be thread safe, so we need to acquire this lock here.
131- // Sadly this decreases performances, but it appears that there are no alternatives
132- // for rust :/
133- let _guard = PAVAO_LOCK . lock ( ) . await ;
134- let client = self . get_samba_client (
135- & server,
136- & self . workgroup ,
137- & share,
138- & creds. username ,
139- & creds. password ,
140- ) ?;
48+ conn. connect ( ) . await . map_err ( |e| e. to_string ( ) ) ?;
14149
142- return if client. list_dir ( "/" ) . is_ok ( ) {
143- Ok ( Some ( vec ! [ Loot :: new(
50+ return match conn
51+ . authenticate ( & creds. username , creds. password . clone ( ) )
52+ . await
53+ {
54+ Ok ( _) => Ok ( Some ( vec ! [ Loot :: new(
14455 "smb" ,
14556 & address,
14657 [
14758 ( "username" . to_owned( ) , creds. username. to_owned( ) ) ,
14859 ( "password" . to_owned( ) , creds. password. to_owned( ) ) ,
14960 ] ,
150- ) ] ) )
151- } else {
152- Ok ( None )
61+ ) ] ) ) ,
62+ // correct user, wrong pass: Some(UnexpectedMessageStatus(3221225581))
63+ Err ( smb:: Error :: UnexpectedMessageStatus ( _) ) => Ok ( Some ( vec ! [
64+ Loot :: new(
65+ "smb" ,
66+ & address,
67+ [ ( "username" . to_owned( ) , creds. username. to_owned( ) ) ] ,
68+ )
69+ . set_partial( ) ,
70+ ] ) ) ,
71+ // wrong user: Some(InvalidMessage("Message not signed or encrypted, but signing is required for the session!"))
72+ Err ( _) => Ok ( None ) ,
15373 } ;
15474 }
15575}
0 commit comments