1+ pub mod config;
12pub mod negotiation_state;
23pub mod netbios_client;
34pub mod preauth_hash;
45pub mod transformer;
56pub mod worker;
67
8+ use crate :: dialects:: get_dialect_impl;
79use crate :: packets:: guid:: Guid ;
810use crate :: packets:: smb2:: { Command , Message } ;
911use crate :: Error ;
@@ -19,8 +21,9 @@ use crate::{
1921 session:: Session ,
2022} ;
2123use binrw:: prelude:: * ;
24+ pub use config:: * ;
2225use maybe_async:: * ;
23- use negotiation_state:: NegotiateState ;
26+ use negotiation_state:: { ConnectionInfo , NegotiatedProperties } ;
2427use netbios_client:: NetBiosClient ;
2528use std:: cmp:: max;
2629use std:: sync:: atomic:: { AtomicU16 , AtomicU64 } ;
@@ -31,20 +34,21 @@ use worker::{Worker, WorkerImpl};
3134
3235pub struct Connection {
3336 handler : HandlerReference < ConnectionMessageHandler > ,
34- timeout : Option < std :: time :: Duration > ,
37+ config : ConnectionConfig ,
3538}
3639
3740impl Connection {
38- pub fn new ( ) -> Connection {
39- Connection {
41+ pub fn build ( config : ConnectionConfig ) -> crate :: Result < Connection > {
42+ config. validate ( ) ?;
43+ Ok ( Connection {
4044 handler : HandlerReference :: new ( ConnectionMessageHandler :: new ( ) ) ,
41- timeout : None ,
42- }
45+ config ,
46+ } )
4347 }
4448
4549 #[ maybe_async]
4650 pub async fn set_timeout ( & mut self , timeout : Option < Duration > ) -> crate :: Result < ( ) > {
47- self . timeout = timeout;
51+ self . config . timeout = timeout;
4852 if let Some ( worker) = self . handler . worker . get ( ) {
4953 worker. set_timeout ( timeout) . await ?;
5054 }
@@ -53,7 +57,7 @@ impl Connection {
5357
5458 #[ maybe_async]
5559 pub async fn connect ( & mut self , address : & str ) -> crate :: Result < ( ) > {
56- let mut netbios_client = NetBiosClient :: new ( self . timeout ) ;
60+ let mut netbios_client = NetBiosClient :: new ( self . config . timeout ) ;
5761
5862 log:: debug!( "Connecting to {}..." , address) ;
5963 netbios_client. connect ( address) . await ?;
@@ -118,26 +122,48 @@ impl Connection {
118122 }
119123 }
120124
121- Ok ( WorkerImpl :: start ( netbios_client, self . timeout ) . await ?)
125+ Ok ( WorkerImpl :: start ( netbios_client, self . config . timeout ) . await ?)
122126 }
123127
124128 #[ maybe_async]
125129 async fn negotiate_smb2 ( & mut self ) -> crate :: Result < ( ) > {
126130 // Confirm that we're not already negotiated.
127- if self . handler . negotiate_state ( ) . is_some ( ) {
131+ if self . handler . negotiate_info ( ) . is_some ( ) {
128132 return Err ( Error :: InvalidState ( "Already negotiated" . into ( ) ) ) ;
129133 }
130134
131135 log:: debug!( "Negotiating SMB2" ) ;
132136
137+ // List possible versions to run with.
138+ let min_dialect = self . config . min_dialect . unwrap_or ( Dialect :: MIN ) ;
139+ let max_dialect = self . config . max_dialect . unwrap_or ( Dialect :: MAX ) ;
140+ let dialects: Vec < Dialect > = Dialect :: ALL
141+ . iter ( )
142+ . filter ( |dialect| * * dialect >= min_dialect && * * dialect <= max_dialect)
143+ . copied ( )
144+ . collect ( ) ;
145+
146+ if dialects. is_empty ( ) {
147+ return Err ( Error :: InvalidConfiguration (
148+ "No dialects to negotiate" . to_string ( ) ,
149+ ) ) ;
150+ }
151+
152+ let encryption_algos = if !self . config . encryption_mode . is_disabled ( ) {
153+ crypto:: SIGNING_ALGOS . into ( )
154+ } else {
155+ vec ! [ ]
156+ } ;
157+
133158 // Send SMB2 negotiate request
134159 let client_guid = self . handler . client_guid ;
135160 let response = self
136161 . handler
137162 . send_recv ( Content :: NegotiateRequest ( NegotiateRequest :: new (
138163 "AVIV-MBP" . to_string ( ) ,
139164 client_guid,
140- crypto:: SIGNING_ALGOS . into ( ) ,
165+ dialects. clone ( ) ,
166+ encryption_algos,
141167 crypto:: ENCRYPTING_ALGOS . to_vec ( ) ,
142168 compression:: SUPPORTED_ALGORITHMS . to_vec ( ) ,
143169 ) ) )
@@ -152,72 +178,55 @@ impl Connection {
152178 ) ) ?;
153179
154180 // well, only 3.1 is supported for starters.
155- if smb2_negotiate_response. dialect_revision != NegotiateDialect :: Smb0311 {
156- return Err ( Error :: UnsupportedDialect (
157- smb2_negotiate_response. dialect_revision ,
158- ) ) ;
159- }
160-
161- if let None = smb2_negotiate_response. negotiate_context_list {
162- return Err ( Error :: InvalidMessage (
163- "Expected negotiate context list" . to_string ( ) ,
181+ if !dialects. contains ( & smb2_negotiate_response. dialect_revision . try_into ( ) ?) {
182+ return Err ( Error :: NegotiationError (
183+ "Server selected an unsupported dialect." . into ( ) ,
164184 ) ) ;
165185 }
166186
167- let signing_algo = if let Some ( signing_algo) = smb2_negotiate_response. get_signing_algo ( ) {
168- if !crypto:: SIGNING_ALGOS . contains ( & signing_algo) {
169- return Err ( Error :: NegotiationError (
170- "Unsupported signing algorithm selected!" . into ( ) ,
171- ) ) ;
172- }
173- Some ( signing_algo)
174- } else {
175- None
176- } ;
177-
178- // Make sure preauth integrity capability is SHA-512, if it exists in response:
179- if let Some ( algo) = smb2_negotiate_response. get_preauth_integrity_algo ( ) {
180- if !preauth_hash:: SUPPORTED_ALGOS . contains ( & algo) {
181- return Err ( Error :: NegotiationError (
182- "Unsupported preauth integrity algorithm received" . into ( ) ,
183- ) ) ;
184- }
185- }
186-
187- // And verify that the encryption algorithm is supported.
188- let encryption_cipher = smb2_negotiate_response. get_encryption_cipher ( ) ;
189- if let Some ( encryption_cipher) = & encryption_cipher {
190- if !crypto:: ENCRYPTING_ALGOS . contains ( & encryption_cipher) {
191- return Err ( Error :: NegotiationError (
192- "Unsupported encryption algorithm received" . into ( ) ,
193- ) ) ;
194- }
195- }
196-
197- let compression: Option < CompressionCaps > = match smb2_negotiate_response. get_compression ( ) {
198- Some ( compression) => Some ( compression. clone ( ) ) ,
199- None => None ,
200- } ;
201-
202- let negotiate_state = NegotiateState {
187+ let dialect_rev = smb2_negotiate_response. dialect_revision . try_into ( ) ?;
188+ let dialect_impl = get_dialect_impl ( & dialect_rev) ;
189+ let mut state = NegotiatedProperties {
203190 server_guid : smb2_negotiate_response. server_guid ,
204- global_caps : smb2_negotiate_response. capabilities . clone ( ) ,
191+ caps : smb2_negotiate_response. capabilities . clone ( ) ,
205192 max_transact_size : smb2_negotiate_response. max_transact_size ,
206193 max_read_size : smb2_negotiate_response. max_read_size ,
207194 max_write_size : smb2_negotiate_response. max_write_size ,
208- gss_token : smb2_negotiate_response. buffer ,
209- selected_dialect : smb2_negotiate_response . dialect_revision . try_into ( ) ? ,
210- signing_algo ,
211- encryption_cipher ,
212- compression ,
195+ gss_token : smb2_negotiate_response. buffer . clone ( ) ,
196+ signing_algo : None ,
197+ encryption_cipher : None ,
198+ compression : None ,
199+ dialect_rev ,
213200 } ;
201+
202+ dialect_impl. process_negotiate_request (
203+ & smb2_negotiate_response,
204+ & mut state,
205+ & self . config ,
206+ ) ?;
207+ if ( ( !u32:: from_le_bytes ( dialect_impl. get_negotiate_caps_mask ( ) . into_bytes ( ) ) )
208+ & u32:: from_le_bytes ( state. caps . into_bytes ( ) ) )
209+ != 0
210+ {
211+ return Err ( Error :: NegotiationError (
212+ "Server capabilities are invalid for the selected dialect." . into ( ) ,
213+ ) ) ;
214+ }
215+
214216 log:: trace!(
215217 "Negotiated SMB results: dialect={:?}, state={:?}" ,
216- negotiate_state . selected_dialect ,
217- & negotiate_state
218+ dialect_rev ,
219+ & state
218220 ) ;
219221
220- self . handler . negotiate_state . set ( negotiate_state) . unwrap ( ) ;
222+ self . handler
223+ . negotiate_info
224+ . set ( ConnectionInfo {
225+ state,
226+ dialect : dialect_impl,
227+ config : self . config . clone ( ) ,
228+ } )
229+ . unwrap ( ) ;
221230
222231 Ok ( ( ) )
223232 }
@@ -229,7 +238,7 @@ impl Connection {
229238 netbios_client : NetBiosClient ,
230239 multi_protocol : bool ,
231240 ) -> crate :: Result < ( ) > {
232- if self . handler . negotiate_state ( ) . is_some ( ) {
241+ if self . handler . negotiate_info ( ) . is_some ( ) {
233242 return Err ( Error :: InvalidState ( "Already negotiated" . into ( ) ) ) ;
234243 }
235244
@@ -247,7 +256,7 @@ impl Connection {
247256 . get ( )
248257 . ok_or ( "Worker is uninitialized" )
249258 . unwrap ( )
250- . negotaite_complete ( & self . handler . negotiate_state ( ) . unwrap ( ) )
259+ . negotaite_complete ( & self . handler . negotiate_info ( ) . unwrap ( ) )
251260 . await ;
252261 log:: info!( "Negotiation successful" ) ;
253262 Ok ( ( ) )
@@ -277,7 +286,7 @@ pub struct ConnectionMessageHandler {
277286 worker : OnceCell < Arc < WorkerImpl > > ,
278287
279288 // Negotiation-related state.
280- negotiate_state : OnceCell < NegotiateState > ,
289+ negotiate_info : OnceCell < ConnectionInfo > ,
281290
282291 /// Number of credits available to the client at the moment, for the next requests.
283292 curr_credits : Semaphore ,
@@ -292,16 +301,16 @@ impl ConnectionMessageHandler {
292301 ConnectionMessageHandler {
293302 client_guid : Guid :: gen ( ) ,
294303 worker : OnceCell :: new ( ) ,
295- negotiate_state : OnceCell :: new ( ) ,
304+ negotiate_info : OnceCell :: new ( ) ,
296305 extra_credits_to_request : 4 ,
297306 curr_credits : Semaphore :: new ( 1 ) ,
298307 curr_msg_id : AtomicU64 :: new ( 1 ) ,
299308 credit_pool : AtomicU16 :: new ( 1 ) ,
300309 }
301310 }
302311
303- pub fn negotiate_state ( & self ) -> Option < & NegotiateState > {
304- self . negotiate_state . get ( )
312+ pub fn negotiate_info ( & self ) -> Option < & ConnectionInfo > {
313+ self . negotiate_info . get ( )
305314 }
306315
307316 pub fn worker ( & self ) -> Option < & Arc < WorkerImpl > > {
@@ -319,8 +328,8 @@ impl ConnectionMessageHandler {
319328
320329 #[ maybe_async]
321330 async fn process_sequence_outgoing ( & self , msg : & mut OutgoingMessage ) -> crate :: Result < ( ) > {
322- if let Some ( neg) = self . negotiate_state ( ) {
323- if neg. selected_dialect > Dialect :: Smb0202 && neg. global_caps . large_mtu ( ) {
331+ if let Some ( neg) = self . negotiate_info ( ) {
332+ if neg. state . dialect_rev > Dialect :: Smb0202 && neg. state . caps . large_mtu ( ) {
324333 // Calculate the cost of the message (charge).
325334 let cost = if Self :: SET_CREDIT_CHARGE_CMDS
326335 . iter ( )
@@ -370,8 +379,8 @@ impl ConnectionMessageHandler {
370379
371380 #[ maybe_async]
372381 async fn process_sequence_incoming ( & self , msg : & IncomingMessage ) -> crate :: Result < ( ) > {
373- if let Some ( neg) = self . negotiate_state ( ) {
374- if neg. selected_dialect > Dialect :: Smb0202 && neg. global_caps . large_mtu ( ) {
382+ if let Some ( neg) = self . negotiate_info ( ) {
383+ if neg. state . dialect_rev > Dialect :: Smb0202 && neg. state . caps . large_mtu ( ) {
375384 let granted_credits = msg. message . header . credit_request ;
376385 let charged_credits = msg. message . header . credit_charge ;
377386 // Update the pool size - return how many EXTRA credits were granted.
@@ -400,8 +409,8 @@ impl MessageHandler for ConnectionMessageHandler {
400409 #[ maybe_async]
401410 async fn sendo ( & self , mut msg : OutgoingMessage ) -> crate :: Result < SendMessageResult > {
402411 // TODO: Add assertion in the struct regarding the selected dialect!
403- let priority_value = match self . negotiate_state . get ( ) {
404- Some ( negotiate_state ) => match negotiate_state . selected_dialect {
412+ let priority_value = match self . negotiate_info . get ( ) {
413+ Some ( neg_info ) => match neg_info . state . dialect_rev {
405414 Dialect :: Smb0311 => 1 ,
406415 _ => 0 ,
407416 } ,
0 commit comments