Phase 1.3: SMB3 packet encryption handling complete
- Add handle_encrypted_frame() to dispatch.rs - Detect TRANSFORM_HEADER magic (0x534D4220) - Decrypt incoming packets using session.encryption_key - Encrypt outgoing responses - All encryption tests pass (3 passed) Phase 1 SMB3 encryption complete: ~380 lines total
This commit is contained in:
99
vendor/smb-server/src/dispatch.rs
vendored
99
vendor/smb-server/src/dispatch.rs
vendored
@@ -4,6 +4,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use crate::proto::auth::ntlm::Identity;
|
use crate::proto::auth::ntlm::Identity;
|
||||||
use crate::proto::crypto::{PreauthIntegrity, sign};
|
use crate::proto::crypto::{PreauthIntegrity, sign};
|
||||||
|
use crate::proto::crypto::encryption::{Smb3Encryption, CipherAlgorithm, TransformHeader};
|
||||||
use crate::proto::header::{
|
use crate::proto::header::{
|
||||||
Command, HeaderTail, SMB2_FLAGS_ASYNC_COMMAND, SMB2_FLAGS_RELATED_OPERATIONS,
|
Command, HeaderTail, SMB2_FLAGS_ASYNC_COMMAND, SMB2_FLAGS_RELATED_OPERATIONS,
|
||||||
SMB2_FLAGS_SERVER_TO_REDIR, SMB2_FLAGS_SIGNED, SMB2_HEADER_LEN, Smb2Header,
|
SMB2_FLAGS_SERVER_TO_REDIR, SMB2_FLAGS_SIGNED, SMB2_HEADER_LEN, Smb2Header,
|
||||||
@@ -82,6 +83,16 @@ pub async fn dispatch_frame(
|
|||||||
if let Some(bytes) = handle_smb1_multi_protocol(server, conn, frame).await {
|
if let Some(bytes) = handle_smb1_multi_protocol(server, conn, frame).await {
|
||||||
return Some(bytes);
|
return Some(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SMB3 encryption check: TRANSFORM_HEADER magic (0x534D4220 = "SMB ")
|
||||||
|
if frame.len() >= 4 {
|
||||||
|
let magic = u32::from_be_bytes([frame[0], frame[1], frame[2], frame[3]]);
|
||||||
|
if magic == 0x534D4220 {
|
||||||
|
// Encrypted packet - decrypt and process
|
||||||
|
return handle_encrypted_frame(server, conn, frame).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if frame.len() < SMB2_HEADER_LEN {
|
if frame.len() < SMB2_HEADER_LEN {
|
||||||
warn!(len = frame.len(), "frame too short for SMB2 header");
|
warn!(len = frame.len(), "frame too short for SMB2 header");
|
||||||
return None;
|
return None;
|
||||||
@@ -156,6 +167,94 @@ pub async fn dispatch_frame(
|
|||||||
Some(stitch_responses(conn, responses).await)
|
Some(stitch_responses(conn, responses).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handle SMB3 encrypted frame (TRANSFORM_HEADER)
|
||||||
|
async fn handle_encrypted_frame(
|
||||||
|
server: &Arc<ServerState>,
|
||||||
|
conn: &Arc<Connection>,
|
||||||
|
encrypted_frame: &[u8],
|
||||||
|
) -> Option<Vec<u8>> {
|
||||||
|
// Parse TRANSFORM_HEADER
|
||||||
|
let header = match TransformHeader::read_from_bytes(encrypted_frame) {
|
||||||
|
Ok(h) => h,
|
||||||
|
Err(e) => {
|
||||||
|
warn!(error = %e, "failed to parse TRANSFORM_HEADER");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get session encryption key
|
||||||
|
let sessions = conn.sessions.read().await;
|
||||||
|
let session_arc = match sessions.get(&header.session_id).cloned() {
|
||||||
|
Some(s) => s,
|
||||||
|
None => {
|
||||||
|
warn!(session_id = header.session_id, "session not found for encrypted packet");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let session = session_arc.read().await;
|
||||||
|
let encryption_enabled = session.encryption_enabled;
|
||||||
|
let encryption_key = session.encryption_key;
|
||||||
|
|
||||||
|
if !encryption_enabled {
|
||||||
|
warn!("session does not have encryption enabled");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let encryption_key = match encryption_key {
|
||||||
|
Some(k) => k,
|
||||||
|
None => {
|
||||||
|
warn!("session has no encryption key");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Decrypt packet
|
||||||
|
let encryption = match Smb3Encryption::new(&encryption_key, CipherAlgorithm::Aes128Gcm) {
|
||||||
|
Ok(e) => e,
|
||||||
|
Err(e) => {
|
||||||
|
warn!(error = %e, "failed to create encryption context");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let decrypted = match encryption.decrypt_packet(encrypted_frame) {
|
||||||
|
Ok(d) => d,
|
||||||
|
Err(e) => {
|
||||||
|
warn!(error = %e, "failed to decrypt packet");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
debug!(session_id = header.session_id, "decrypted SMB3 packet");
|
||||||
|
|
||||||
|
// Process decrypted frame (non-recursive: call dispatch_one directly)
|
||||||
|
if decrypted.len() < SMB2_HEADER_LEN {
|
||||||
|
warn!("decrypted frame too short");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = dispatch_one(server, conn, &decrypted).await;
|
||||||
|
|
||||||
|
// Encrypt response if needed
|
||||||
|
if let Some(resp_bytes) = response {
|
||||||
|
if encryption_enabled {
|
||||||
|
let encrypted_response = match encryption.encrypt_packet(&resp_bytes, header.session_id) {
|
||||||
|
Ok(e) => e,
|
||||||
|
Err(e) => {
|
||||||
|
warn!(error = %e, "failed to encrypt response");
|
||||||
|
return Some(resp_bytes);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
debug!("encrypted response packet");
|
||||||
|
return Some(encrypted_response);
|
||||||
|
}
|
||||||
|
return Some(resp_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn inherit_related_context(
|
fn inherit_related_context(
|
||||||
sub_frame: &mut [u8],
|
sub_frame: &mut [u8],
|
||||||
req_hdr: &mut Smb2Header,
|
req_hdr: &mut Smb2Header,
|
||||||
|
|||||||
Reference in New Issue
Block a user