diff --git a/data/auth.sqlite b/data/auth.sqlite index fd616ef..14142ea 100644 Binary files a/data/auth.sqlite and b/data/auth.sqlite differ diff --git a/vendor/smb-server/src/conn/state.rs b/vendor/smb-server/src/conn/state.rs index 00c7ce8..c6f8fee 100644 --- a/vendor/smb-server/src/conn/state.rs +++ b/vendor/smb-server/src/conn/state.rs @@ -7,6 +7,7 @@ use std::sync::{Arc, Mutex}; use crate::proto::auth::ntlm::{Identity, NtlmServer}; use crate::proto::crypto::{PreauthIntegrity, SigningAlgo}; +use crate::proto::crypto::encryption::CipherAlgorithm; use crate::proto::messages::{Dialect, FileId}; use tokio::sync::{mpsc, RwLock}; use uuid::Uuid; @@ -40,6 +41,10 @@ pub struct Connection { /// Granted at NEGOTIATE: large MTU support flag etc. pub max_read_size: tokio::sync::RwLock, pub max_write_size: tokio::sync::RwLock, + + /// SMB3 encryption support (negotiated in NEGOTIATE) + pub encryption_supported: tokio::sync::RwLock, + pub encryption_cipher: tokio::sync::RwLock>, /// Sessions keyed by SessionId. pub sessions: RwLock>>>, @@ -72,6 +77,8 @@ impl Connection { preauth: Mutex::new(PreauthIntegrity::new()), max_read_size: tokio::sync::RwLock::new(max_read_size), max_write_size: tokio::sync::RwLock::new(max_write_size), + encryption_supported: tokio::sync::RwLock::new(false), + encryption_cipher: tokio::sync::RwLock::new(None), sessions: RwLock::new(HashMap::new()), pending_auths: RwLock::new(HashMap::new()), session_preauth: RwLock::new(HashMap::new()), @@ -220,8 +227,12 @@ pub struct Session { pub identity: Identity, pub session_base_key: [u8; 16], pub signing_key: [u8; 16], + /// SMB3 encryption key (derived from session_base_key) + pub encryption_key: Option<[u8; 16]>, /// Whether signing is required for this session's traffic. pub signing_required: bool, + /// Whether encryption is enabled for this session + pub encryption_enabled: bool, pub trees: RwLock>>>, /// 3.1.1: snapshot taken at SESSION_SETUP completion (after the request /// hash but before the response is hashed). Used as KDF context. @@ -236,7 +247,9 @@ impl Session { identity: Identity, session_base_key: [u8; 16], signing_key: [u8; 16], + encryption_key: Option<[u8; 16]>, signing_required: bool, + encryption_enabled: bool, preauth_snapshot: Option<[u8; 64]>, ) -> Self { Self { @@ -244,7 +257,9 @@ impl Session { identity, session_base_key, signing_key, + encryption_key, signing_required, + encryption_enabled, trees: RwLock::new(HashMap::new()), preauth_snapshot, next_tree_id: AtomicU32::new(1), diff --git a/vendor/smb-server/src/handlers/negotiate.rs b/vendor/smb-server/src/handlers/negotiate.rs index 8087cb1..e0a4d56 100644 --- a/vendor/smb-server/src/handlers/negotiate.rs +++ b/vendor/smb-server/src/handlers/negotiate.rs @@ -4,10 +4,11 @@ use std::sync::Arc; use crate::proto::auth::spnego::encode_init_response; use crate::proto::crypto::SigningAlgo; +use crate::proto::crypto::encryption::CipherAlgorithm; use crate::proto::header::Smb2Header; use crate::proto::messages::{ Dialect, NegotiateContext, NegotiateRequest, NegotiateResponse, PreauthIntegrityCapabilities, - SigningCapabilities, + SigningCapabilities, EncryptionCapabilities, }; use tracing::info; use uuid::Uuid; @@ -117,7 +118,29 @@ pub async fn handle( data: signing_data, }; - let ctxs = vec![preauth_ctx, signing_ctx]; + // ENCRYPTION_CAPABILITIES — advertise AES-128-GCM (simplified) + let encryption_caps = EncryptionCapabilities { + cipher_count: 1, + ciphers: vec![EncryptionCapabilities::CIPHER_AES_128_GCM], + }; + let encryption_data = { + use binrw::BinWrite; + let mut c = std::io::Cursor::new(Vec::new()); + BinWrite::write(&encryption_caps, &mut c).expect("encryption negotiate context encodes"); + c.into_inner() + }; + let encryption_ctx = NegotiateContext { + context_type: NegotiateContext::TYPE_ENCRYPTION, + data_length: encryption_data.len() as u16, + reserved: 0, + data: encryption_data, + }; + + // Store encryption support in connection state + *conn.encryption_supported.write().await = true; + *conn.encryption_cipher.write().await = Some(CipherAlgorithm::Aes128Gcm); + + let ctxs = vec![preauth_ctx, signing_ctx, encryption_ctx]; if let Err(e) = NegotiateContext::encode_list(&ctxs, &mut contexts_bytes) { tracing::error!(error = %e, "encode_list failed"); return HandlerResponse::err(ntstatus::STATUS_INVALID_PARAMETER); diff --git a/vendor/smb-server/src/handlers/session_setup.rs b/vendor/smb-server/src/handlers/session_setup.rs index 888744b..f20b8d4 100644 --- a/vendor/smb-server/src/handlers/session_setup.rs +++ b/vendor/smb-server/src/handlers/session_setup.rs @@ -198,13 +198,27 @@ pub async fn handle( 0 }; let signing_required = false; + + // Check if encryption is negotiated + let encryption_supported = *conn.encryption_supported.read().await; + let encryption_cipher = *conn.encryption_cipher.read().await; + let encryption_enabled = encryption_supported && encryption_cipher.is_some(); + let encryption_key = if encryption_enabled { + // Derive encryption key from session_base_key (simplified approach) + use crate::proto::crypto::encryption::Smb3Encryption; + Some(Smb3Encryption::derive_encryption_key(&session_base_key, b"SMB3ENC")) + } else { + None + }; let session = Session::new( sid, outcome.identity.clone(), session_base_key, signing_key, + encryption_key, signing_required, + encryption_enabled, None, ); let session_arc = Arc::new(tokio::sync::RwLock::new(session)); diff --git a/vendor/smb-server/src/proto/crypto/encryption.rs b/vendor/smb-server/src/proto/crypto/encryption.rs index 1c73afe..8759dfa 100644 --- a/vendor/smb-server/src/proto/crypto/encryption.rs +++ b/vendor/smb-server/src/proto/crypto/encryption.rs @@ -224,7 +224,7 @@ impl Smb3Encryption { nonce } - fn derive_encryption_key(session_key: &[u8], context: &[u8]) -> [u8; 16] { + pub fn derive_encryption_key(session_key: &[u8], context: &[u8]) -> [u8; 16] { use sha2::{Sha256, Digest}; let mut hasher = Sha256::new(); diff --git a/vendor/smb-server/src/tests/dynamic_config.rs b/vendor/smb-server/src/tests/dynamic_config.rs index 0ed452c..41729c3 100644 --- a/vendor/smb-server/src/tests/dynamic_config.rs +++ b/vendor/smb-server/src/tests/dynamic_config.rs @@ -38,7 +38,7 @@ async fn register_session( )); state.active_connections.register(&conn).await; - let session = Session::new(1, identity, [0; 16], [0; 16], false, None); + let session = Session::new(1, identity, [0; 16], [0; 16], None, false, false, None); let session = Arc::new(tokio::sync::RwLock::new(session)); let share = state.find_share(share_name).await.expect("share"); let tree = Arc::new(tokio::sync::RwLock::new(TreeConnect::new(