Skip to content

Commit 9571806

Browse files
authored
Merge pull request #36 from AvivNaaman/28-support-smb-300
28 support smb 300
2 parents bd750bd + 1418ced commit 9571806

File tree

13 files changed

+230
-128
lines changed

13 files changed

+230
-128
lines changed

docker-compose.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ services:
33
build:
44
context: smb/tests
55
dockerfile: Dockerfile
6+
tags:
7+
- ghcr.io/avivnaaman/smb-tests:latest
8+
platforms:
9+
- linux/amd64
610
ports:
711
- 445:445
812
networks:

smb/src/connection.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ pub mod preauth_hash;
55
pub mod transformer;
66
pub mod worker;
77

8-
use crate::dialects::get_dialect_impl;
8+
use crate::dialects::DialectImpl;
99
use crate::packets::guid::Guid;
1010
use crate::packets::smb2::{Command, Message};
1111
use crate::Error;
@@ -185,7 +185,7 @@ impl Connection {
185185
}
186186

187187
let dialect_rev = smb2_negotiate_response.dialect_revision.try_into()?;
188-
let dialect_impl = get_dialect_impl(&dialect_rev);
188+
let dialect_impl = DialectImpl::new(dialect_rev);
189189
let mut state = NegotiatedProperties {
190190
server_guid: smb2_negotiate_response.server_guid,
191191
caps: smb2_negotiate_response.capabilities.clone(),

smb/src/connection/config.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub struct ConnectionConfig {
3232
pub min_dialect: Option<Dialect>,
3333
pub max_dialect: Option<Dialect>,
3434
pub encryption_mode: EncryptionMode,
35+
pub compression_enabled: bool,
3536
}
3637

3738
impl ConnectionConfig {

smb/src/connection/negotiation_state.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,6 @@ impl NegotiatedProperties {
4343
#[derive(Debug)]
4444
pub struct ConnectionInfo {
4545
pub state: NegotiatedProperties,
46-
pub dialect: Arc<dyn DialectImpl>,
46+
pub dialect: Arc<DialectImpl>,
4747
pub config: ConnectionConfig,
4848
}

smb/src/connection/transformer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ impl Transformer {
4747
}
4848

4949
let mut config = self.config.write().await?;
50-
if neg_info.dialect.supports_compression() {
50+
if neg_info.dialect.supports_compression() && neg_info.config.compression_enabled {
5151
let compress = match &neg_info.state.compression {
5252
Some(compression) => {
5353
Some((Compressor::new(compression), Decompressor::new(compression)))

smb/src/dialects.rs

Lines changed: 141 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -7,54 +7,161 @@ use crate::{
77
crypto,
88
packets::smb2::{
99
CompressionCaps, Dialect, GlobalCapabilities, NegotiateResponse, SigningAlgorithmId,
10+
TreeCapabilities, TreeConnectShareFlagsCacheMode, TreeShareFlags,
1011
},
1112
ConnectionConfig, Error,
1213
};
1314

14-
pub trait DialectImpl: std::fmt::Debug + Send + Sync {
15-
fn get_dialect(&self) -> Dialect;
16-
fn get_negotiate_caps_mask(&self) -> GlobalCapabilities;
17-
fn process_negotiate_request(
15+
/// This is a utility struct that returns constants and functions for the given dialect.
16+
#[derive(Debug)]
17+
pub struct DialectImpl {
18+
pub dialect: Dialect,
19+
}
20+
21+
impl DialectImpl {
22+
pub fn new(dialect: Dialect) -> Arc<Self> {
23+
Arc::new(Self { dialect })
24+
}
25+
26+
pub fn get_negotiate_caps_mask(&self) -> GlobalCapabilities {
27+
let mut mask = GlobalCapabilities::new()
28+
.with_dfs(true)
29+
.with_leasing(true)
30+
.with_large_mtu(true)
31+
.with_multi_channel(true)
32+
.with_persistent_handles(true)
33+
.with_directory_leasing(true);
34+
35+
mask.set_encryption(Dialect::Smb030 <= self.dialect && self.dialect <= Dialect::Smb0302);
36+
mask.set_notifications(self.dialect == Dialect::Smb0311);
37+
38+
mask
39+
}
40+
41+
pub fn get_share_flags_mask(&self) -> TreeShareFlags {
42+
let mut mask = TreeShareFlags::new()
43+
.with_caching_mode(TreeConnectShareFlagsCacheMode::All)
44+
.with_dfs(true)
45+
.with_dfs_root(true)
46+
.with_restrict_exclusive_opens(true)
47+
.with_force_shared_delete(true)
48+
.with_allow_namespace_caching(true)
49+
.with_access_based_directory_enum(true)
50+
.with_force_levelii_oplock(true)
51+
.with_identity_remoting(true)
52+
.with_isolated_transport(true);
53+
54+
if self.dialect > Dialect::Smb0202 {
55+
mask.set_enable_hash_v1(true);
56+
}
57+
if self.dialect >= Dialect::Smb021 {
58+
mask.set_enable_hash_v2(true);
59+
}
60+
if self.dialect.is_smb3() {
61+
mask.set_encrypt_data(true);
62+
}
63+
if self.dialect >= Dialect::Smb0311 {
64+
mask.set_compress_data(true);
65+
}
66+
67+
mask
68+
}
69+
70+
pub fn get_tree_connect_caps_mask(&self) -> TreeCapabilities {
71+
let mut mask = TreeCapabilities::new().with_dfs(true);
72+
73+
if self.dialect.is_smb3() {
74+
mask = mask
75+
.with_continuous_availability(true)
76+
.with_scaleout(true)
77+
.with_cluster(true);
78+
}
79+
80+
if self.dialect >= Dialect::Smb0302 {
81+
mask.set_asymmetric(true);
82+
}
83+
84+
if self.dialect == Dialect::Smb0311 {
85+
mask = mask.with_redirect_to_owner(true);
86+
}
87+
88+
mask
89+
}
90+
91+
pub fn process_negotiate_request(
1892
&self,
1993
response: &NegotiateResponse,
2094
state: &mut NegotiatedProperties,
2195
config: &ConnectionConfig,
22-
) -> crate::Result<()>;
96+
) -> crate::Result<()> {
97+
match self.dialect {
98+
Dialect::Smb0311 => Smb311.process_negotiate_request(response, state, config),
99+
Dialect::Smb0302 | Dialect::Smb030 => {
100+
Smb300_302.process_negotiate_request(response, state, config)
101+
}
102+
_ => unimplemented!(),
103+
}
104+
}
23105

24-
fn get_signing_nonce(&self) -> &[u8];
25-
fn preauth_hash_supported(&self) -> bool;
26-
fn default_signing_algo(&self) -> SigningAlgorithmId {
27-
SigningAlgorithmId::HmacSha256
106+
pub fn get_signing_derive_label(&self) -> &[u8] {
107+
match self.dialect {
108+
Dialect::Smb0311 => Smb311::SIGNING_KEY_LABEL,
109+
Dialect::Smb0302 | Dialect::Smb030 => Smb300_302::SIGNING_KEY_LABEL,
110+
_ => unimplemented!(),
111+
}
112+
}
113+
pub fn preauth_hash_supported(&self) -> bool {
114+
self.dialect == Dialect::Smb0311
115+
}
116+
pub fn default_signing_algo(&self) -> SigningAlgorithmId {
117+
match self.dialect {
118+
Dialect::Smb0311 | Dialect::Smb0302 | Dialect::Smb030 => SigningAlgorithmId::AesCmac,
119+
Dialect::Smb0202 | Dialect::Smb021 => SigningAlgorithmId::HmacSha256,
120+
}
28121
}
29122

30-
fn supports_compression(&self) -> bool {
31-
false
123+
pub fn supports_compression(&self) -> bool {
124+
self.dialect == Dialect::Smb0311
32125
}
33126

34-
fn supports_encryption(&self) -> bool {
35-
false
127+
pub fn supports_encryption(&self) -> bool {
128+
self.dialect.is_smb3()
36129
}
37-
fn s2c_encryption_key_label(&self) -> &[u8];
38-
fn c2s_encryption_key_label(&self) -> &[u8];
39-
}
40130

41-
/// Get the dialect implementation for the given dialect.
42-
pub fn get_dialect_impl(dialect: &Dialect) -> Arc<dyn DialectImpl> {
43-
match dialect {
44-
Dialect::Smb0311 => Arc::new(Smb0311Dialect),
45-
Dialect::Smb0302 => Arc::new(Smb302Dialect),
46-
_ => unimplemented!(),
131+
pub fn s2c_encrypt_key_derive_label(&self) -> &[u8] {
132+
match self.dialect {
133+
Dialect::Smb0311 => Smb311::ENCRYPTION_S2C_KEY_LABEL,
134+
Dialect::Smb0302 | Dialect::Smb030 => Smb300_302::ENCRYPTION_KEY_LABEL,
135+
_ => panic!("Encryption is not supported for this dialect!"),
136+
}
137+
}
138+
pub fn c2s_encrypt_key_derive_label(&self) -> &[u8] {
139+
match self.dialect {
140+
Dialect::Smb0311 => Smb311::ENCRYPTION_C2S_KEY_LABEL,
141+
Dialect::Smb0302 | Dialect::Smb030 => Smb300_302::ENCRYPTION_KEY_LABEL,
142+
_ => panic!("Encryption is not supported for this dialect!"),
143+
}
47144
}
48145
}
49146

50-
#[derive(Debug)]
51-
struct Smb0311Dialect;
147+
trait DialectMethods {
148+
const SIGNING_KEY_LABEL: &[u8];
149+
fn process_negotiate_request(
150+
&self,
151+
response: &NegotiateResponse,
152+
_state: &mut NegotiatedProperties,
153+
config: &ConnectionConfig,
154+
) -> crate::Result<()>;
155+
}
52156

53-
impl DialectImpl for Smb0311Dialect {
54-
fn get_dialect(&self) -> Dialect {
55-
Dialect::Smb0311
56-
}
157+
struct Smb311;
158+
impl Smb311 {
159+
pub const ENCRYPTION_S2C_KEY_LABEL: &[u8] = b"SMBS2CCipherKey\x00";
160+
pub const ENCRYPTION_C2S_KEY_LABEL: &[u8] = b"SMBC2SCipherKey\x00";
161+
}
57162

163+
impl DialectMethods for Smb311 {
164+
const SIGNING_KEY_LABEL: &[u8] = b"SMBSigningKey\x00";
58165
fn process_negotiate_request(
59166
&self,
60167
response: &NegotiateResponse,
@@ -112,55 +219,15 @@ impl DialectImpl for Smb0311Dialect {
112219

113220
Ok(())
114221
}
115-
116-
fn get_negotiate_caps_mask(&self) -> GlobalCapabilities {
117-
GlobalCapabilities::new()
118-
.with_dfs(true)
119-
.with_leasing(true)
120-
.with_large_mtu(true)
121-
.with_multi_channel(true)
122-
.with_persistent_handles(true)
123-
.with_directory_leasing(true)
124-
.with_encryption(false)
125-
.with_notifications(true)
126-
}
127-
128-
fn get_signing_nonce(&self) -> &[u8] {
129-
b"SMBSigningKey\x00"
130-
}
131-
132-
fn preauth_hash_supported(&self) -> bool {
133-
true
134-
}
135-
136-
fn default_signing_algo(&self) -> SigningAlgorithmId {
137-
SigningAlgorithmId::AesCmac
138-
}
139-
140-
fn supports_compression(&self) -> bool {
141-
true
142-
}
143-
fn supports_encryption(&self) -> bool {
144-
true
145-
}
146-
fn s2c_encryption_key_label(&self) -> &[u8] {
147-
b"SMBS2CCipherKey\x00"
148-
}
149-
fn c2s_encryption_key_label(&self) -> &[u8] {
150-
b"SMBC2SCipherKey\x00"
151-
}
152222
}
153223

154-
const SMB2_ENCRYPTION_KEY_LABEL: &[u8] = b"SMB2AESCCM\x00";
155-
156-
#[derive(Debug)]
157-
struct Smb302Dialect;
158-
159-
impl DialectImpl for Smb302Dialect {
160-
fn get_dialect(&self) -> Dialect {
161-
Dialect::Smb0302
162-
}
224+
struct Smb300_302;
225+
impl Smb300_302 {
226+
pub const ENCRYPTION_KEY_LABEL: &[u8] = b"SMB2AESCCM\x00";
227+
}
163228

229+
impl DialectMethods for Smb300_302 {
230+
const SIGNING_KEY_LABEL: &[u8] = b"SMB2AESCMAC\x00";
164231
fn process_negotiate_request(
165232
&self,
166233
response: &NegotiateResponse,
@@ -181,40 +248,4 @@ impl DialectImpl for Smb302Dialect {
181248

182249
Ok(())
183250
}
184-
185-
fn get_negotiate_caps_mask(&self) -> GlobalCapabilities {
186-
GlobalCapabilities::new()
187-
.with_dfs(true)
188-
.with_leasing(true)
189-
.with_large_mtu(true)
190-
.with_multi_channel(true)
191-
.with_persistent_handles(true)
192-
.with_directory_leasing(true)
193-
.with_encryption(true)
194-
.with_notifications(false)
195-
}
196-
197-
fn get_signing_nonce(&self) -> &[u8] {
198-
b"SMB2AESCMAC\x00"
199-
}
200-
201-
fn preauth_hash_supported(&self) -> bool {
202-
false
203-
}
204-
205-
fn default_signing_algo(&self) -> SigningAlgorithmId {
206-
SigningAlgorithmId::AesCmac
207-
}
208-
209-
fn supports_encryption(&self) -> bool {
210-
true
211-
}
212-
213-
fn s2c_encryption_key_label(&self) -> &[u8] {
214-
SMB2_ENCRYPTION_KEY_LABEL
215-
}
216-
217-
fn c2s_encryption_key_label(&self) -> &[u8] {
218-
SMB2_ENCRYPTION_KEY_LABEL
219-
}
220251
}

smb/src/packets/smb2/negotiate.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,10 @@ impl NegotiateRequest {
7878
encrypting_algorithms: Vec<EncryptionCipher>,
7979
compression_algorithms: Vec<CompressionAlgorithm>,
8080
) -> NegotiateRequest {
81-
let mut caps = GlobalCapabilities::new();
81+
let mut capabilities = GlobalCapabilities::new();
8282
let mut security_mode = NegotiateSecurityMode::new();
8383
if signing_algorithms.len() > 0 {
84-
caps.set_encryption(true);
84+
capabilities.set_encryption(true);
8585
}
8686

8787
let ctx_list = if supported_dialects.contains(&Dialect::Smb0311) {
@@ -134,15 +134,23 @@ impl NegotiateRequest {
134134
} else {
135135
None
136136
};
137-
NegotiateRequest {
138-
security_mode: security_mode,
139-
capabilities: caps
137+
138+
// Set capabilities to 0 if no SMB3 dialects are supported.
139+
capabilities = if supported_dialects.iter().all(|d| !d.is_smb3()) {
140+
GlobalCapabilities::new()
141+
} else {
142+
capabilities
140143
.with_dfs(true)
141144
.with_leasing(true)
142145
.with_large_mtu(true)
143146
.with_multi_channel(true)
144147
.with_persistent_handles(true)
145-
.with_directory_leasing(true),
148+
.with_directory_leasing(true)
149+
};
150+
151+
NegotiateRequest {
152+
security_mode: security_mode,
153+
capabilities,
146154
client_guid,
147155
dialects: supported_dialects,
148156
negotiate_context_list: ctx_list,
@@ -268,6 +276,11 @@ impl Dialect {
268276
Dialect::Smb0302,
269277
Dialect::Smb0311,
270278
];
279+
280+
#[inline]
281+
pub fn is_smb3(&self) -> bool {
282+
matches!(self, Dialect::Smb030 | Dialect::Smb0302 | Dialect::Smb0311)
283+
}
271284
}
272285

273286
/// Dialects that may be used in the SMB Negotiate Request.

0 commit comments

Comments
 (0)