rust: format smb files

Ticket: 3836
pull/15663/head
Philippe Antoine 3 weeks ago committed by Victor Julien
parent f8ac56c591
commit ca34fba00c

@ -20,16 +20,18 @@ use crate::kerberos::*;
use crate::smb::ntlmssp_records::*;
use crate::smb::smb::*;
use nom8::{Err, IResult};
use der_parser::ber::BerObjectContent;
use der_parser::der::{parse_der_oid, parse_der_sequence};
use nom8::{Err, IResult};
fn parse_secblob_get_spnego(blob: &[u8]) -> IResult<&[u8], &[u8], SecBlobError>
{
let (rem, base_o) = der_parser::parse_der(blob).map_err(|_| Err::Error(SecBlobError::NotSpNego))?;
fn parse_secblob_get_spnego(blob: &[u8]) -> IResult<&[u8], &[u8], SecBlobError> {
let (rem, base_o) =
der_parser::parse_der(blob).map_err(|_| Err::Error(SecBlobError::NotSpNego))?;
SCLogDebug!("parse_secblob_get_spnego: base_o {:?}", base_o);
let d = match base_o.content.as_slice() {
Err(_) => { return Err(Err::Error(SecBlobError::NotSpNego)); },
Err(_) => {
return Err(Err::Error(SecBlobError::NotSpNego));
}
Ok(d) => d,
};
let (next, o) = parse_der_oid(d).map_err(|_| Err::Error(SecBlobError::NotSpNego))?;
@ -39,17 +41,17 @@ fn parse_secblob_get_spnego(blob: &[u8]) -> IResult<&[u8], &[u8], SecBlobError>
Ok(oid) => oid,
Err(_) => {
return Err(Err::Error(SecBlobError::NotSpNego));
},
}
};
SCLogDebug!("oid {}", oid.to_string());
match oid.to_string().as_str() {
"1.3.6.1.5.5.2" => {
SCLogDebug!("SPNEGO {}", oid);
},
}
_ => {
return Err(Err::Error(SecBlobError::NotSpNego));
},
}
}
SCLogDebug!("parse_secblob_get_spnego: next {:?}", next);
@ -57,17 +59,16 @@ fn parse_secblob_get_spnego(blob: &[u8]) -> IResult<&[u8], &[u8], SecBlobError>
Ok((rem, next))
}
fn parse_secblob_spnego_start(blob: &[u8]) -> IResult<&[u8], &[u8], SecBlobError>
{
fn parse_secblob_spnego_start(blob: &[u8]) -> IResult<&[u8], &[u8], SecBlobError> {
let (rem, o) = der_parser::parse_der(blob).map_err(|_| Err::Error(SecBlobError::NotSpNego))?;
let d = match o.content.as_slice() {
Ok(d) => {
SCLogDebug!("d: next data len {}",d.len());
SCLogDebug!("d: next data len {}", d.len());
d
},
}
_ => {
return Err(Err::Error(SecBlobError::NotSpNego));
},
}
};
Ok((rem, d))
}
@ -78,27 +79,32 @@ pub struct SpnegoRequest {
pub ntlmssp: Option<NtlmsspData>,
}
fn parse_secblob_spnego(blob: &[u8]) -> Option<SpnegoRequest>
{
fn parse_secblob_spnego(blob: &[u8]) -> Option<SpnegoRequest> {
let mut have_ntlmssp = false;
let mut have_kerberos = false;
let mut kticket : Option<Kerberos5Ticket> = None;
let mut ntlmssp : Option<NtlmsspData> = None;
let mut kticket: Option<Kerberos5Ticket> = None;
let mut ntlmssp: Option<NtlmsspData> = None;
let o = match parse_der_sequence(blob) {
Ok((_, o)) => o,
_ => { return None; },
_ => {
return None;
}
};
for s in o {
SCLogDebug!("s {:?}", s);
let n = match s.content.as_slice() {
Ok(s) => s,
_ => { continue; },
_ => {
continue;
}
};
let o = match der_parser::parse_der(n) {
Ok((_,x)) => x,
_ => { continue; },
Ok((_, x)) => x,
_ => {
continue;
}
};
SCLogDebug!("o {:?}", o);
match o.content {
@ -108,20 +114,38 @@ fn parse_secblob_spnego(blob: &[u8]) -> Option<SpnegoRequest>
if let BerObjectContent::OID(ref oid) = se.content {
SCLogDebug!("OID {:?}", oid);
match oid.to_string().as_str() {
"1.2.840.48018.1.2.2" => { SCLogDebug!("Microsoft Kerberos 5"); },
"1.2.840.113554.1.2.2" => { SCLogDebug!("Kerberos 5"); have_kerberos = true; },
"1.2.840.113554.1.2.2.1" => { SCLogDebug!("krb5-name"); },
"1.2.840.113554.1.2.2.2" => { SCLogDebug!("krb5-principal"); },
"1.2.840.113554.1.2.2.3" => { SCLogDebug!("krb5-user-to-user-mech"); },
"1.3.6.1.4.1.311.2.2.10" => { SCLogDebug!("NTLMSSP"); have_ntlmssp = true; },
"1.3.6.1.4.1.311.2.2.30" => { SCLogDebug!("NegoEx"); },
_ => { SCLogDebug!("unexpected OID {:?}", oid); },
"1.2.840.48018.1.2.2" => {
SCLogDebug!("Microsoft Kerberos 5");
}
"1.2.840.113554.1.2.2" => {
SCLogDebug!("Kerberos 5");
have_kerberos = true;
}
"1.2.840.113554.1.2.2.1" => {
SCLogDebug!("krb5-name");
}
"1.2.840.113554.1.2.2.2" => {
SCLogDebug!("krb5-principal");
}
"1.2.840.113554.1.2.2.3" => {
SCLogDebug!("krb5-user-to-user-mech");
}
"1.3.6.1.4.1.311.2.2.10" => {
SCLogDebug!("NTLMSSP");
have_ntlmssp = true;
}
"1.3.6.1.4.1.311.2.2.30" => {
SCLogDebug!("NegoEx");
}
_ => {
SCLogDebug!("unexpected OID {:?}", oid);
}
}
} else {
SCLogDebug!("expected OID, got {:?}", se);
}
}
},
}
BerObjectContent::OctetString(os) => {
if have_kerberos {
if let Ok((_, t)) = parse_kerberos5_request(os) {
@ -133,8 +157,8 @@ fn parse_secblob_spnego(blob: &[u8]) -> Option<SpnegoRequest>
SCLogDebug!("parsing expected NTLMSSP");
ntlmssp = parse_ntlmssp_blob(os);
}
},
_ => {},
}
_ => {}
}
}
@ -145,7 +169,7 @@ fn parse_secblob_spnego(blob: &[u8]) -> Option<SpnegoRequest>
Some(s)
}
#[derive(Debug,PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq)]
pub struct NtlmsspData {
pub host: Vec<u8>,
pub user: Vec<u8>,
@ -155,27 +179,29 @@ pub struct NtlmsspData {
}
/// take in blob, search for the header and parse it
fn parse_ntlmssp_blob(blob: &[u8]) -> Option<NtlmsspData>
{
let mut ntlmssp_data : Option<NtlmsspData> = None;
fn parse_ntlmssp_blob(blob: &[u8]) -> Option<NtlmsspData> {
let mut ntlmssp_data: Option<NtlmsspData> = None;
SCLogDebug!("NTLMSSP {:?}", blob);
if let Ok((_, nd)) = parse_ntlmssp(blob) {
SCLogDebug!("NTLMSSP TYPE {}/{} nd {:?}",
nd.msg_type, &ntlmssp_type_string(nd.msg_type), nd);
SCLogDebug!(
"NTLMSSP TYPE {}/{} nd {:?}",
nd.msg_type,
&ntlmssp_type_string(nd.msg_type),
nd
);
match nd.msg_type {
NTLMSSP_NEGOTIATE => {
},
NTLMSSP_NEGOTIATE => {}
NTLMSSP_AUTH => {
if let Ok((_, ad)) = parse_ntlm_auth_record(nd.data) {
SCLogDebug!("auth data {:?}", ad);
let mut host = ad.host.to_vec();
host.retain(|&i|i != 0x00);
host.retain(|&i| i != 0x00);
let mut user = ad.user.to_vec();
user.retain(|&i|i != 0x00);
user.retain(|&i| i != 0x00);
let mut domain = ad.domain.to_vec();
domain.retain(|&i|i != 0x00);
domain.retain(|&i| i != 0x00);
let d = NtlmsspData {
host,
user,
@ -185,47 +211,38 @@ fn parse_ntlmssp_blob(blob: &[u8]) -> Option<NtlmsspData>
};
ntlmssp_data = Some(d);
}
},
_ => {},
}
_ => {}
}
}
return ntlmssp_data;
}
// if spnego parsing fails try to fall back to ntlmssp
pub fn parse_secblob(blob: &[u8]) -> Option<SpnegoRequest>
{
pub fn parse_secblob(blob: &[u8]) -> Option<SpnegoRequest> {
match parse_secblob_get_spnego(blob) {
Ok((_, spnego)) => {
match parse_secblob_spnego_start(spnego) {
Ok((_, spnego_start)) => {
parse_secblob_spnego(spnego_start)
},
_ => {
match parse_ntlmssp_blob(blob) {
Some(n) => {
let s = SpnegoRequest {
krb: None,
ntlmssp: Some(n),
};
Some(s)
},
None => { None },
}
},
}
},
_ => {
match parse_ntlmssp_blob(blob) {
Ok((_, spnego)) => match parse_secblob_spnego_start(spnego) {
Ok((_, spnego_start)) => parse_secblob_spnego(spnego_start),
_ => match parse_ntlmssp_blob(blob) {
Some(n) => {
let s = SpnegoRequest {
krb: None,
ntlmssp: Some(n),
ntlmssp: Some(n),
};
Some(s)
},
None => { None },
}
None => None,
},
},
_ => match parse_ntlmssp_blob(blob) {
Some(n) => {
let s = SpnegoRequest {
krb: None,
ntlmssp: Some(n),
};
Some(s)
}
None => None,
},
}
}

@ -17,13 +17,13 @@
// written by Victor Julien
use uuid;
use crate::smb::smb::{cfg_max_stub_size, *};
use crate::smb::smb2::*;
use crate::dcerpc::dcerpc::*;
use crate::smb::dcerpc_records::*;
use crate::smb::events::*;
use crate::dcerpc::dcerpc::*;
use crate::smb::smb::{cfg_max_stub_size, *};
use crate::smb::smb2::*;
use crate::smb::smb_status::*;
use uuid;
impl SMBCommonHdr {
/// helper for DCERPC tx tracking. Check if we need
@ -37,21 +37,21 @@ impl SMBCommonHdr {
2 => {
let (_, cmd2) = vercmd.get_smb2_cmd();
let x = match cmd2 {
SMB2_COMMAND_READ => { 0 },
SMB2_COMMAND_WRITE => { 0 },
SMB2_COMMAND_IOCTL => { self.msg_id },
_ => { self.msg_id },
SMB2_COMMAND_READ => 0,
SMB2_COMMAND_WRITE => 0,
SMB2_COMMAND_IOCTL => self.msg_id,
_ => self.msg_id,
};
use_msg_id = x;
},
}
1 => {
SCLogDebug!("FIXME TODO");
//let (_, cmd1) = vercmd.get_smb1_cmd();
//if cmd1 != SMB1_COMMAND_IOCTL {
use_msg_id = 0;
//}
},
_ => { },
}
_ => {}
}
SMBCommonHdr {
ssn_id: self.ssn_id,
@ -109,10 +109,10 @@ impl SMBTransactionDCERPC {
req_set: true,
call_id,
..Default::default()
}
};
}
fn new_response(call_id: u32) -> Self {
return Self {
return Self {
call_id,
..Default::default()
};
@ -124,14 +124,15 @@ impl SMBTransactionDCERPC {
}
impl SMBState {
fn new_dcerpc_tx(&mut self, hdr: SMBCommonHdr, vercmd: SMBVerCmdStat, cmd: u8, call_id: u32)
-> &mut SMBTransaction
{
fn new_dcerpc_tx(
&mut self, hdr: SMBCommonHdr, vercmd: SMBVerCmdStat, cmd: u8, call_id: u32,
) -> &mut SMBTransaction {
let mut tx = self.new_tx();
tx.hdr = hdr;
tx.vercmd = vercmd;
tx.type_data = Some(SMBTransactionTypeData::DCERPC(
SMBTransactionDCERPC::new_request(cmd, call_id)));
SMBTransactionDCERPC::new_request(cmd, call_id),
));
SCLogDebug!("SMB: TX DCERPC created: ID {} hdr {:?}", tx.id, tx.hdr);
self.transactions.push_back(tx);
@ -139,14 +140,15 @@ impl SMBState {
return tx_ref.unwrap();
}
fn new_dcerpc_tx_for_response(&mut self, hdr: SMBCommonHdr, vercmd: SMBVerCmdStat, call_id: u32)
-> &mut SMBTransaction
{
fn new_dcerpc_tx_for_response(
&mut self, hdr: SMBCommonHdr, vercmd: SMBVerCmdStat, call_id: u32,
) -> &mut SMBTransaction {
let mut tx = self.new_tx();
tx.hdr = hdr;
tx.vercmd = vercmd;
tx.type_data = Some(SMBTransactionTypeData::DCERPC(
SMBTransactionDCERPC::new_response(call_id)));
SMBTransactionDCERPC::new_response(call_id),
));
SCLogDebug!("SMB: TX DCERPC created: ID {} hdr {:?}", tx.id, tx.hdr);
self.transactions.push_back(tx);
@ -154,20 +156,18 @@ impl SMBState {
return tx_ref.unwrap();
}
fn get_dcerpc_tx(&mut self, hdr: &SMBCommonHdr, vercmd: &SMBVerCmdStat, call_id: u32)
-> Option<&mut SMBTransaction>
{
fn get_dcerpc_tx(
&mut self, hdr: &SMBCommonHdr, vercmd: &SMBVerCmdStat, call_id: u32,
) -> Option<&mut SMBTransaction> {
let dce_hdr = hdr.to_dcerpc(vercmd);
SCLogDebug!("looking for {:?}", dce_hdr);
for tx in &mut self.transactions {
let found = dce_hdr.compare(&tx.hdr.to_dcerpc(vercmd)) &&
match tx.type_data {
Some(SMBTransactionTypeData::DCERPC(ref x)) => {
x.call_id == call_id
},
_ => { false },
};
let found = dce_hdr.compare(&tx.hdr.to_dcerpc(vercmd))
&& match tx.type_data {
Some(SMBTransactionTypeData::DCERPC(ref x)) => x.call_id == call_id,
_ => false,
};
if found {
tx.tx_data.0.updated_tc = true;
tx.tx_data.0.updated_ts = true;
@ -181,19 +181,22 @@ impl SMBState {
/// Handle DCERPC request data from a WRITE, IOCTL or TRANS record.
/// return bool indicating whether an tx has been created/updated.
///
pub fn smb_write_dcerpc_record(state: &mut SMBState,
vercmd: SMBVerCmdStat,
hdr: SMBCommonHdr,
data: &[u8]) -> bool
{
let mut bind_ifaces : Option<Vec<DCERPCIface>> = None;
pub fn smb_write_dcerpc_record(
state: &mut SMBState, vercmd: SMBVerCmdStat, hdr: SMBCommonHdr, data: &[u8],
) -> bool {
let mut bind_ifaces: Option<Vec<DCERPCIface>> = None;
let mut is_bind = false;
SCLogDebug!("called for {} bytes of data", data.len());
match parse_dcerpc_record(data) {
Ok((_, dcer)) => {
SCLogDebug!("DCERPC: version {}.{} write data {} => {:?}",
dcer.version_major, dcer.version_minor, dcer.data.len(), dcer);
SCLogDebug!(
"DCERPC: version {}.{} write data {} => {:?}",
dcer.version_major,
dcer.version_minor,
dcer.data.len(),
dcer
);
/* if this isn't the first frag, simply update the existing
* tx with the additional stub data */
@ -203,9 +206,15 @@ pub fn smb_write_dcerpc_record(state: &mut SMBState,
Ok((_, recr)) => {
let found = match state.get_dcerpc_tx(&hdr, &vercmd, dcer.call_id) {
Some(tx) => {
SCLogDebug!("previous CMD {} found at tx {} => {:?}",
dcer.packet_type, tx.id, tx);
if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data {
SCLogDebug!(
"previous CMD {} found at tx {} => {:?}",
dcer.packet_type,
tx.id,
tx
);
if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) =
tx.type_data
{
tdn.frag_cnt_ts = tdn.frag_cnt_ts.saturating_add(1);
let max_size = cfg_max_stub_size() as usize;
if recr.data.len() + tdn.stub_data_ts.len() < max_size {
@ -213,38 +222,44 @@ pub fn smb_write_dcerpc_record(state: &mut SMBState,
tdn.stub_data_ts.extend_from_slice(recr.data);
SCLogDebug!("stub_data now {}", tdn.stub_data_ts.len());
} else if tdn.stub_data_ts.len() < max_size {
tdn.stub_data_ts.extend_from_slice(&recr.data[..max_size - tdn.stub_data_ts.len()]);
tdn.stub_data_ts.extend_from_slice(
&recr.data[..max_size - tdn.stub_data_ts.len()],
);
}
}
if dcer.last_frag {
SCLogDebug!("last frag set, so request side of DCERPC closed");
tx.request_done = true;
} else {
SCLogDebug!("NOT last frag, so request side of DCERPC remains open");
SCLogDebug!(
"NOT last frag, so request side of DCERPC remains open"
);
}
true
},
}
None => {
SCLogDebug!("NO previous CMD {} found", dcer.packet_type);
false
},
}
};
return found;
},
}
_ => {
state.set_event(SMBEvent::MalformedData);
return false;
},
}
}
}
let tx = state.new_dcerpc_tx(hdr, vercmd, dcer.packet_type, dcer.call_id);
match dcer.packet_type {
DCERPC_TYPE_REQUEST => {
match parse_dcerpc_request_record(dcer.data, dcer.frag_len, dcer.little_endian) {
match parse_dcerpc_request_record(dcer.data, dcer.frag_len, dcer.little_endian)
{
Ok((_, recr)) => {
SCLogDebug!("DCERPC: REQUEST {:?}", recr);
if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data {
if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data
{
SCLogDebug!("first frag size {}", recr.data.len());
tdn.opnum = recr.opnum;
tdn.context_id = recr.context_id;
@ -252,24 +267,31 @@ pub fn smb_write_dcerpc_record(state: &mut SMBState,
let max_size = cfg_max_stub_size() as usize;
if tdn.stub_data_ts.len() + recr.data.len() < max_size {
tdn.stub_data_ts.extend_from_slice(recr.data);
SCLogDebug!("DCERPC: REQUEST opnum {} stub data len {}",
tdn.opnum, tdn.stub_data_ts.len());
SCLogDebug!(
"DCERPC: REQUEST opnum {} stub data len {}",
tdn.opnum,
tdn.stub_data_ts.len()
);
} else if tdn.stub_data_ts.len() < max_size {
tdn.stub_data_ts.extend_from_slice(&recr.data[..max_size - tdn.stub_data_ts.len()]);
tdn.stub_data_ts.extend_from_slice(
&recr.data[..max_size - tdn.stub_data_ts.len()],
);
}
}
if dcer.last_frag {
tx.request_done = true;
} else {
SCLogDebug!("NOT last frag, so request side of DCERPC remains open");
SCLogDebug!(
"NOT last frag, so request side of DCERPC remains open"
);
}
},
}
_ => {
tx.set_event(SMBEvent::MalformedData);
tx.request_done = true;
},
}
}
},
}
DCERPC_TYPE_BIND => {
let brec = if dcer.little_endian {
parse_dcerpc_bind_record(dcer.data)
@ -285,43 +307,63 @@ pub fn smb_write_dcerpc_record(state: &mut SMBState,
let mut ifaces: Vec<DCERPCIface> = Vec::new();
for i in bindr.ifaces {
let x = if dcer.little_endian {
vec![i.iface[3], i.iface[2], i.iface[1], i.iface[0],
i.iface[5], i.iface[4], i.iface[7], i.iface[6],
i.iface[8], i.iface[9], i.iface[10], i.iface[11],
i.iface[12], i.iface[13], i.iface[14], i.iface[15]]
vec![
i.iface[3],
i.iface[2],
i.iface[1],
i.iface[0],
i.iface[5],
i.iface[4],
i.iface[7],
i.iface[6],
i.iface[8],
i.iface[9],
i.iface[10],
i.iface[11],
i.iface[12],
i.iface[13],
i.iface[14],
i.iface[15],
]
} else {
i.iface.to_vec()
};
let uuid_str = uuid::Uuid::from_slice(&x.clone());
let _uuid_str = uuid_str.map(|uuid_str| uuid_str.to_hyphenated().to_string()).unwrap();
let d = DCERPCIface::new(x,i.ver,i.ver_min,i.ctx_id);
SCLogDebug!("UUID {} version {}/{} bytes {:?}",
_uuid_str,
i.ver, i.ver_min,i.iface);
let _uuid_str = uuid_str
.map(|uuid_str| uuid_str.to_hyphenated().to_string())
.unwrap();
let d = DCERPCIface::new(x, i.ver, i.ver_min, i.ctx_id);
SCLogDebug!(
"UUID {} version {}/{} bytes {:?}",
_uuid_str,
i.ver,
i.ver_min,
i.iface
);
ifaces.push(d);
}
bind_ifaces = Some(ifaces);
}
},
}
_ => {
tx.set_event(SMBEvent::MalformedData);
},
}
}
tx.request_done = true;
}
21..=255 => {
tx.set_event(SMBEvent::MalformedData);
tx.request_done = true;
},
}
_ => {
// valid type w/o special processing
tx.request_done = true;
},
}
}
},
}
_ => {
state.set_event(SMBEvent::MalformedData);
},
}
}
if is_bind {
@ -337,12 +379,9 @@ pub fn smb_write_dcerpc_record(state: &mut SMBState,
/// Update TX for bind ack. Needs to update both tx and state.
///
fn smb_dcerpc_response_bindack(
state: &mut SMBState,
vercmd: SMBVerCmdStat,
hdr: SMBCommonHdr,
dcer: &DceRpcRecord,
ntstatus: u32)
{
state: &mut SMBState, vercmd: SMBVerCmdStat, hdr: SMBCommonHdr, dcer: &DceRpcRecord,
ntstatus: u32,
) {
match parse_dcerpc_bindack_record(dcer.data) {
Ok((_, bindackr)) => {
SCLogDebug!("SMB READ BINDACK {:?}", bindackr);
@ -355,7 +394,7 @@ fn smb_dcerpc_response_bindack(
tx.vercmd.set_ntstatus(ntstatus);
tx.response_done = true;
true
},
}
None => false,
};
if found {
@ -370,17 +409,16 @@ fn smb_dcerpc_response_bindack(
}
}
}
},
}
_ => {
state.set_event(SMBEvent::MalformedData);
},
}
}
}
fn smb_read_dcerpc_record_error(state: &mut SMBState,
hdr: SMBCommonHdr, vercmd: SMBVerCmdStat, ntstatus: u32)
-> bool
{
fn smb_read_dcerpc_record_error(
state: &mut SMBState, hdr: SMBCommonHdr, vercmd: SMBVerCmdStat, ntstatus: u32,
) -> bool {
let ver = vercmd.get_version();
let cmd = if ver == 2 {
let (_, c) = vercmd.get_smb2_cmd();
@ -396,47 +434,43 @@ fn smb_read_dcerpc_record_error(state: &mut SMBState,
tx.set_status(ntstatus, false);
tx.response_done = true;
true
},
}
None => {
SCLogDebug!("NOT found");
false
},
}
};
return found;
}
fn dcerpc_response_handle(tx: &mut SMBTransaction,
vercmd: SMBVerCmdStat,
dcer: &DceRpcRecord)
{
fn dcerpc_response_handle(tx: &mut SMBTransaction, vercmd: SMBVerCmdStat, dcer: &DceRpcRecord) {
let (_, ntstatus) = vercmd.get_ntstatus();
match dcer.packet_type {
DCERPC_TYPE_RESPONSE => {
match parse_dcerpc_response_record(dcer.data, dcer.frag_len) {
Ok((_, respr)) => {
SCLogDebug!("SMBv1 READ RESPONSE {:?}", respr);
if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data {
SCLogDebug!("CMD 11 found at tx {}", tx.id);
tdn.set_result(DCERPC_TYPE_RESPONSE);
let max_size = cfg_max_stub_size() as usize;
tdn.frag_cnt_tc = tdn.frag_cnt_tc.saturating_add(1);
if tdn.stub_data_tc.len() + respr.data.len() < max_size {
tdn.stub_data_tc.extend_from_slice(respr.data);
} else if tdn.stub_data_tc.len() < max_size {
tdn.stub_data_tc.extend_from_slice(&respr.data[..max_size - tdn.stub_data_tc.len()]);
}
DCERPC_TYPE_RESPONSE => match parse_dcerpc_response_record(dcer.data, dcer.frag_len) {
Ok((_, respr)) => {
SCLogDebug!("SMBv1 READ RESPONSE {:?}", respr);
if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data {
SCLogDebug!("CMD 11 found at tx {}", tx.id);
tdn.set_result(DCERPC_TYPE_RESPONSE);
let max_size = cfg_max_stub_size() as usize;
tdn.frag_cnt_tc = tdn.frag_cnt_tc.saturating_add(1);
if tdn.stub_data_tc.len() + respr.data.len() < max_size {
tdn.stub_data_tc.extend_from_slice(respr.data);
} else if tdn.stub_data_tc.len() < max_size {
tdn.stub_data_tc
.extend_from_slice(&respr.data[..max_size - tdn.stub_data_tc.len()]);
}
tx.vercmd.set_ntstatus(ntstatus);
tx.response_done = dcer.last_frag;
},
_ => {
tx.set_event(SMBEvent::MalformedData);
},
}
tx.vercmd.set_ntstatus(ntstatus);
tx.response_done = dcer.last_frag;
}
_ => {
tx.set_event(SMBEvent::MalformedData);
}
},
DCERPC_TYPE_BINDACK => {
// handled elsewhere
},
}
21..=255 => {
if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data {
tdn.set_result(dcer.packet_type);
@ -445,24 +479,22 @@ fn dcerpc_response_handle(tx: &mut SMBTransaction,
tx.response_done = true;
tx.set_event(SMBEvent::MalformedData);
}
_ => { // valid type w/o special processing
_ => {
// valid type w/o special processing
if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data {
tdn.set_result(dcer.packet_type);
}
tx.vercmd.set_ntstatus(ntstatus);
tx.response_done = true;
},
}
}
}
/// Handle DCERPC reply record. Called for READ, TRANS, IOCTL
///
pub fn smb_read_dcerpc_record(state: &mut SMBState,
vercmd: SMBVerCmdStat,
hdr: SMBCommonHdr,
guid: &[u8],
indata: &[u8]) -> bool
{
pub fn smb_read_dcerpc_record(
state: &mut SMBState, vercmd: SMBVerCmdStat, hdr: SMBCommonHdr, guid: &[u8], indata: &[u8],
) -> bool {
let (_, ntstatus) = vercmd.get_ntstatus();
if ntstatus != SMB_NTSTATUS_SUCCESS && ntstatus != SMB_NTSTATUS_BUFFER_OVERFLOW {
@ -471,8 +503,10 @@ pub fn smb_read_dcerpc_record(state: &mut SMBState,
SCLogDebug!("lets first see if we have prior data");
// msg_id 0 as this data crosses cmd/reply pairs
let ehdr = SMBHashKeyHdrGuid::new(SMBCommonHdr::new(SMBHDR_TYPE_TRANS_FRAG,
hdr.ssn_id, hdr.tree_id, 0_u64), guid.to_vec());
let ehdr = SMBHashKeyHdrGuid::new(
SMBCommonHdr::new(SMBHDR_TYPE_TRANS_FRAG, hdr.ssn_id, hdr.tree_id, 0_u64),
guid.to_vec(),
);
let mut prevdata = state.dcerpc_rec_frag_cache.pop(&ehdr).unwrap_or_default();
SCLogDebug!("indata {} prevdata {}", indata.len(), prevdata.len());
prevdata.extend_from_slice(indata);
@ -482,18 +516,25 @@ pub fn smb_read_dcerpc_record(state: &mut SMBState,
if data.is_empty() {
SCLogDebug!("weird: no DCERPC data"); // TODO
// TODO set event?
// TODO set event?
return false;
} else {
match parse_dcerpc_record(&data) {
Ok((_, dcer)) => {
SCLogDebug!("DCERPC: version {}.{} read data {} => {:?}",
dcer.version_major, dcer.version_minor, dcer.data.len(), dcer);
SCLogDebug!(
"DCERPC: version {}.{} read data {} => {:?}",
dcer.version_major,
dcer.version_minor,
dcer.data.len(),
dcer
);
if ntstatus == SMB_NTSTATUS_BUFFER_OVERFLOW && data.len() < dcer.frag_len as usize {
SCLogDebug!("short record {} < {}: storing partial data in state",
data.len(), dcer.frag_len);
SCLogDebug!(
"short record {} < {}: storing partial data in state",
data.len(),
dcer.frag_len
);
state.dcerpc_rec_frag_cache.put(ehdr, data.to_vec());
return true; // TODO review
}
@ -507,21 +548,21 @@ pub fn smb_read_dcerpc_record(state: &mut SMBState,
Some(tx) => {
dcerpc_response_handle(tx, vercmd.clone(), &dcer);
true
},
}
None => {
SCLogDebug!("no tx");
false
},
}
};
if !found {
// pick up DCERPC tx even if we missed the request
let tx = state.new_dcerpc_tx_for_response(hdr, vercmd.clone(), dcer.call_id);
dcerpc_response_handle(tx, vercmd, &dcer);
}
},
}
_ => {
malformed = true;
},
}
}
}
@ -533,16 +574,17 @@ pub fn smb_read_dcerpc_record(state: &mut SMBState,
}
/// Try to find out if the input data looks like DCERPC
pub fn smb_dcerpc_probe(data: &[u8]) -> bool
{
pub fn smb_dcerpc_probe(data: &[u8]) -> bool {
if let Ok((_, recr)) = parse_dcerpc_record(data) {
SCLogDebug!("SMB: could be DCERPC {:?}", recr);
if recr.version_major == 5 && recr.version_minor < 3 &&
recr.frag_len > 0 && recr.packet_type <= 20
{
SCLogDebug!("SMB: looks like we have dcerpc");
return true;
}
if recr.version_major == 5
&& recr.version_minor < 3
&& recr.frag_len > 0
&& recr.packet_type <= 20
{
SCLogDebug!("SMB: looks like we have dcerpc");
return true;
}
}
return false;
}

@ -15,27 +15,27 @@
* 02110-1301, USA.
*/
use nom8::bits::bits;
use crate::smb::error::SmbError;
use nom8::bits::bits;
use nom8::bits::streaming::take as take_bits;
use nom8::bytes::streaming::take;
use nom8::combinator::{cond, rest};
use nom8::multi::count;
use nom8::number::streaming::{be_u16, le_u16, le_u32, le_u8, u16, u32};
use nom8::number::Endianness;
use nom8::number::streaming::{be_u16, le_u8, le_u16, le_u32, u16, u32};
use nom8::Parser;
use nom8::{Err, IResult};
#[derive(Debug,PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq)]
pub struct DceRpcResponseRecord<'a> {
pub data: &'a[u8],
pub data: &'a [u8],
}
/// parse a packet type 'response' DCERPC record. Implemented
/// as function to be able to pass the fraglen in.
pub fn parse_dcerpc_response_record(i:&[u8], frag_len: u16 )
-> IResult<&[u8], DceRpcResponseRecord<'_>, SmbError>
{
pub fn parse_dcerpc_response_record(
i: &[u8], frag_len: u16,
) -> IResult<&[u8], DceRpcResponseRecord<'_>, SmbError> {
if frag_len < 24 {
return Err(Err::Error(SmbError::RecordTooSmall));
}
@ -45,34 +45,42 @@ pub fn parse_dcerpc_response_record(i:&[u8], frag_len: u16 )
Ok((i, record))
}
#[derive(Debug,PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq)]
pub struct DceRpcRequestRecord<'a> {
pub opnum: u16,
pub context_id: u16,
pub data: &'a[u8],
pub data: &'a [u8],
}
/// parse a packet type 'request' DCERPC record. Implemented
/// as function to be able to pass the fraglen in.
pub fn parse_dcerpc_request_record(i:&[u8], frag_len: u16, little: bool)
-> IResult<&[u8], DceRpcRequestRecord<'_>, SmbError>
{
pub fn parse_dcerpc_request_record(
i: &[u8], frag_len: u16, little: bool,
) -> IResult<&[u8], DceRpcRequestRecord<'_>, SmbError> {
if frag_len < 24 {
return Err(Err::Error(SmbError::RecordTooSmall));
}
let (i, _) = take(4_usize).parse(i)?;
let endian = if little { Endianness::Little } else { Endianness::Big };
let endian = if little {
Endianness::Little
} else {
Endianness::Big
};
let (i, context_id) = u16(endian).parse(i)?;
let (i, opnum) = u16(endian).parse(i)?;
let (i, data) = take(frag_len - 24).parse(i)?;
let record = DceRpcRequestRecord { opnum, context_id, data };
let record = DceRpcRequestRecord {
opnum,
context_id,
data,
};
Ok((i, record))
}
#[derive(Debug,PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq)]
pub struct DceRpcBindIface<'a> {
pub ctx_id: u16,
pub iface: &'a[u8],
pub iface: &'a [u8],
pub ver: u16,
pub ver_min: u16,
}
@ -87,7 +95,7 @@ pub fn parse_dcerpc_bind_iface(i: &[u8]) -> IResult<&[u8], DceRpcBindIface<'_>>
let (i, _) = take(20_usize).parse(i)?;
let res = DceRpcBindIface {
ctx_id,
iface:interface,
iface: interface,
ver,
ver_min,
};
@ -104,14 +112,14 @@ pub fn parse_dcerpc_bind_iface_big(i: &[u8]) -> IResult<&[u8], DceRpcBindIface<'
let (i, _) = take(20_usize).parse(i)?;
let res = DceRpcBindIface {
ctx_id,
iface:interface,
iface: interface,
ver,
ver_min,
};
Ok((i, res))
}
#[derive(Debug,PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq)]
pub struct DceRpcBindRecord<'a> {
pub num_ctx_items: u8,
pub ifaces: Vec<DceRpcBindIface<'a>>,
@ -145,11 +153,11 @@ pub fn parse_dcerpc_bind_record_big(i: &[u8]) -> IResult<&[u8], DceRpcBindRecord
Ok((i, record))
}
#[derive(Debug,PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq)]
pub struct DceRpcBindAckResult<'a> {
pub ack_result: u16,
pub ack_reason: u16,
pub transfer_syntax: &'a[u8],
pub transfer_syntax: &'a [u8],
pub syntax_version: u32,
}
@ -167,7 +175,7 @@ pub fn parse_dcerpc_bindack_result(i: &[u8]) -> IResult<&[u8], DceRpcBindAckResu
Ok((i, res))
}
#[derive(Debug,PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq)]
pub struct DceRpcBindAckRecord<'a> {
pub num_results: u8,
pub results: Vec<DceRpcBindAckResult<'a>>,
@ -191,7 +199,7 @@ pub fn parse_dcerpc_bindack_record(i: &[u8]) -> IResult<&[u8], DceRpcBindAckReco
Ok((i, record))
}
#[derive(Debug,PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq)]
pub struct DceRpcRecord<'a> {
pub version_major: u8,
pub version_minor: u8,
@ -206,25 +214,27 @@ pub struct DceRpcRecord<'a> {
pub packet_type: u8,
pub call_id: u32,
pub data: &'a[u8],
pub data: &'a [u8],
}
fn parse_dcerpc_flags1(i:&[u8]) -> IResult<&[u8],(u8,u8,u8)> {
fn parse_dcerpc_flags1(i: &[u8]) -> IResult<&[u8], (u8, u8, u8)> {
use nom8::error::Error;
bits::<_, _, Error<_>, _, _>((
take_bits(6u8),
take_bits(1u8), // last (1)
take_bits(1u8), // last (1)
take_bits(1u8),
)).parse(i)
))
.parse(i)
}
fn parse_dcerpc_flags2(i:&[u8]) -> IResult<&[u8],(u32,u32,u32)> {
fn parse_dcerpc_flags2(i: &[u8]) -> IResult<&[u8], (u32, u32, u32)> {
use nom8::error::Error;
bits::<_, _, Error<_>, _, _>((
take_bits(3u32),
take_bits(1u32), // endianness
take_bits(28u32),
)).parse(i)
take_bits(3u32),
take_bits(1u32), // endianness
take_bits(28u32),
))
.parse(i)
}
pub fn parse_dcerpc_record(i: &[u8]) -> IResult<&[u8], DceRpcRecord<'_>> {
@ -233,7 +243,11 @@ pub fn parse_dcerpc_record(i: &[u8]) -> IResult<&[u8], DceRpcRecord<'_>> {
let (i, packet_type) = le_u8.parse(i)?;
let (i, packet_flags) = parse_dcerpc_flags1(i)?;
let (i, data_rep) = parse_dcerpc_flags2(i)?;
let endian = if data_rep.1 == 0 { Endianness::Big } else { Endianness::Little };
let endian = if data_rep.1 == 0 {
Endianness::Big
} else {
Endianness::Little
};
let (i, frag_len) = u16(endian).parse(i)?;
let (i, _auth) = u16(endian).parse(i)?;
let (i, call_id) = u32(endian).parse(i)?;

@ -19,7 +19,7 @@ use crate::smb::smb::*;
impl SMBState {
#[cfg(not(feature = "debug"))]
pub fn _debug_tx_stats(&self) { }
pub fn _debug_tx_stats(&self) {}
#[cfg(feature = "debug")]
pub fn _debug_tx_stats(&self) {
@ -27,7 +27,12 @@ impl SMBState {
let txf = self.transactions.front().unwrap();
let txl = self.transactions.back().unwrap();
SCLogDebug!("TXs {} MIN {} MAX {}", self.transactions.len(), txf.id, txl.id);
SCLogDebug!(
"TXs {} MIN {} MAX {}",
self.transactions.len(),
txf.id,
txl.id
);
SCLogDebug!("- OLD tx.id {}: {:?}", txf.id, txf);
SCLogDebug!("- NEW tx.id {}: {:?}", txl.id, txl);
self._dump_txs();
@ -35,7 +40,7 @@ impl SMBState {
}
#[cfg(not(feature = "debug"))]
pub fn _dump_txs(&self) { }
pub fn _dump_txs(&self) {}
#[cfg(feature = "debug")]
pub fn _dump_txs(&self) {
let len = self.transactions.len();
@ -52,20 +57,35 @@ impl SMBState {
match tx.type_data {
Some(SMBTransactionTypeData::FILE(ref d)) => {
SCLogDebug!("idx {} tx id {} progress {}/{} filename {} type_data {:?}",
i, tx.id, tx.request_done, tx.response_done,
String::from_utf8_lossy(&d.file_name), tx.type_data);
},
SCLogDebug!(
"idx {} tx id {} progress {}/{} filename {} type_data {:?}",
i,
tx.id,
tx.request_done,
tx.response_done,
String::from_utf8_lossy(&d.file_name),
tx.type_data
);
}
_ => {
SCLogDebug!("idx {} tx id {} ver:{} cmd:{} progress {}/{} type_data {:?} tx {:?}",
i, tx.id, ver, _smbcmd, tx.request_done, tx.response_done, tx.type_data, tx);
},
SCLogDebug!(
"idx {} tx id {} ver:{} cmd:{} progress {}/{} type_data {:?} tx {:?}",
i,
tx.id,
ver,
_smbcmd,
tx.request_done,
tx.response_done,
tx.type_data,
tx
);
}
}
}
}
#[cfg(not(feature = "debug"))]
pub fn _debug_state_stats(&self) { }
pub fn _debug_state_stats(&self) {}
#[cfg(feature = "debug")]
pub fn _debug_state_stats(&self) {

@ -28,10 +28,10 @@ use std::os::raw::{c_char, c_int, c_uint, c_void};
use std::ptr;
use suricata_sys::sys::{
DetectEngineCtx, DetectEngineThreadCtx, Flow, SCDetectBufferSetActiveList,
SCDetectGetLastSMFromLists, SCDetectHelperBufferMpmRegister, SCDetectHelperBufferProgressRegister,
SCDetectHelperKeywordAliasRegister, SCDetectHelperKeywordRegister,
SCDetectSignatureSetAppProto, SCSigMatchAppendSMToList, SCSigTableAppLiteElmt, SigMatchCtx,
Signature,
SCDetectGetLastSMFromLists, SCDetectHelperBufferMpmRegister,
SCDetectHelperBufferProgressRegister, SCDetectHelperKeywordAliasRegister,
SCDetectHelperKeywordRegister, SCDetectSignatureSetAppProto, SCSigMatchAppendSMToList,
SCSigTableAppLiteElmt, SigMatchCtx, Signature,
};
unsafe extern "C" fn smb_tx_get_share(

@ -15,11 +15,11 @@
* 02110-1301, USA.
*/
use std;
use crate::applayer::AppLayerGetFileStateRust;
use crate::core::*;
use crate::direction::Direction;
use crate::filetracker::*;
use std;
use crate::smb::smb::*;
use suricata_sys::sys::SCFileFlowFlagsToFlags;
@ -43,41 +43,42 @@ impl SMBTransactionFile {
return Self {
file_tracker: FileTransferTracker::new(),
..Default::default()
}
};
}
pub fn update_file_flags(&mut self, flow_file_flags: u16) {
let dir_flag = if self.direction == Direction::ToServer { STREAM_TOSERVER } else { STREAM_TOCLIENT };
let dir_flag = if self.direction == Direction::ToServer {
STREAM_TOSERVER
} else {
STREAM_TOCLIENT
};
self.file_tracker.file_flags = unsafe { SCFileFlowFlagsToFlags(flow_file_flags, dir_flag) };
}
}
/// little wrapper around the FileTransferTracker::new_chunk method
pub fn filetracker_newchunk(ft: &mut FileTransferTracker, name: &[u8], data: &[u8],
chunk_offset: u64, chunk_size: u32, is_last: bool, xid: &u32)
{
pub fn filetracker_newchunk(
ft: &mut FileTransferTracker, name: &[u8], data: &[u8], chunk_offset: u64, chunk_size: u32,
is_last: bool, xid: &u32,
) {
if let Some(sfcm) = unsafe { SURICATA_SMB_FILE_CONFIG } {
ft.new_chunk(sfcm, name, data, chunk_offset,
chunk_size, 0, is_last, xid);
ft.new_chunk(sfcm, name, data, chunk_offset, chunk_size, 0, is_last, xid);
}
}
pub fn filetracker_trunc(ft: &mut FileTransferTracker)
{
pub fn filetracker_trunc(ft: &mut FileTransferTracker) {
if let Some(sfcm) = unsafe { SURICATA_SMB_FILE_CONFIG } {
ft.trunc(sfcm);
}
}
pub fn filetracker_close(ft: &mut FileTransferTracker)
{
pub fn filetracker_close(ft: &mut FileTransferTracker) {
if let Some(sfcm) = unsafe { SURICATA_SMB_FILE_CONFIG } {
ft.close(sfcm);
}
}
fn filetracker_update(ft: &mut FileTransferTracker, data: &[u8], gap_size: u32) -> u32
{
fn filetracker_update(ft: &mut FileTransferTracker, data: &[u8], gap_size: u32) -> u32 {
if let Some(sfcm) = unsafe { SURICATA_SMB_FILE_CONFIG } {
ft.update(sfcm, data, gap_size)
} else {
@ -86,9 +87,9 @@ fn filetracker_update(ft: &mut FileTransferTracker, data: &[u8], gap_size: u32)
}
impl SMBState {
pub fn new_file_tx(&mut self, fuid: &[u8], file_name: &[u8], direction: Direction)
-> &mut SMBTransaction
{
pub fn new_file_tx(
&mut self, fuid: &[u8], file_name: &[u8], direction: Direction,
) -> &mut SMBTransaction {
let mut tx = self.new_tx();
tx.type_data = Some(SMBTransactionTypeData::FILE(SMBTransactionFile::new()));
if let Some(SMBTransactionTypeData::FILE(ref mut d)) = tx.type_data {
@ -100,9 +101,16 @@ impl SMBState {
d.update_file_flags(tx.tx_data.0.file_flags);
}
tx.tx_data.init_files_opened();
tx.tx_data.0.file_tx = if direction == Direction::ToServer { STREAM_TOSERVER } else { STREAM_TOCLIENT }; // TODO direction to flag func?
SCLogDebug!("SMB: new_file_tx: TX FILE created: ID {} NAME {}",
tx.id, String::from_utf8_lossy(file_name));
tx.tx_data.0.file_tx = if direction == Direction::ToServer {
STREAM_TOSERVER
} else {
STREAM_TOCLIENT
}; // TODO direction to flag func?
SCLogDebug!(
"SMB: new_file_tx: TX FILE created: ID {} NAME {}",
tx.id,
String::from_utf8_lossy(file_name)
);
self.transactions.push_back(tx);
let tx_ref = self.transactions.back_mut();
return tx_ref.unwrap();
@ -110,16 +118,16 @@ impl SMBState {
/// get file tx for a open file. Returns None if a file for the fuid exists,
/// but has already been closed.
pub fn get_file_tx_by_fuid_with_open_file(&mut self, fuid: &[u8], direction: Direction)
-> Option<&mut SMBTransaction>
{
pub fn get_file_tx_by_fuid_with_open_file(
&mut self, fuid: &[u8], direction: Direction,
) -> Option<&mut SMBTransaction> {
let f = fuid.to_vec();
for tx in &mut self.transactions {
let found = match tx.type_data {
Some(SMBTransactionTypeData::FILE(ref mut d)) => {
direction == d.direction && f == d.fuid && !d.file_tracker.is_done()
},
_ => { false },
}
_ => false,
};
if found {
@ -138,16 +146,16 @@ impl SMBState {
}
/// get file tx for a fuid. File may already have been closed.
pub fn get_file_tx_by_fuid(&mut self, fuid: &[u8], direction: Direction)
-> Option<&mut SMBTransaction>
{
pub fn get_file_tx_by_fuid(
&mut self, fuid: &[u8], direction: Direction,
) -> Option<&mut SMBTransaction> {
let f = fuid.to_vec();
for tx in &mut self.transactions {
let found = match tx.type_data {
Some(SMBTransactionTypeData::FILE(ref mut d)) => {
direction == d.direction && f == d.fuid
},
_ => { false },
}
_ => false,
};
if found {
@ -174,7 +182,7 @@ impl SMBState {
self.file_tc_left
};
if chunk_left == 0 {
return 0
return 0;
}
SCLogDebug!("chunk_left {} data {}", chunk_left, data.len());
let file_handle = if direction == Direction::ToServer {
@ -208,8 +216,12 @@ impl SMBState {
if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
if ssn_gap {
let queued_data = tdf.file_tracker.get_queued_size();
if queued_data > 2000000 { // TODO should probably be configurable
SCLogDebug!("QUEUED size {} while we've seen GAPs. Truncating file.", queued_data);
if queued_data > 2000000 {
// TODO should probably be configurable
SCLogDebug!(
"QUEUED size {} while we've seen GAPs. Truncating file.",
queued_data
);
filetracker_trunc(&mut tdf.file_tracker);
}
}
@ -224,10 +236,11 @@ impl SMBState {
} else {
0
}
},
}
None => {
SCLogDebug!("not found for handle {:?}", file_handle);
0 },
0
}
};
return consumed;
@ -236,13 +249,18 @@ impl SMBState {
use crate::applayer::AppLayerGetFileState;
pub(super) unsafe extern "C" fn smb_gettxfiles(tx: *mut std::ffi::c_void, direction: u8) -> AppLayerGetFileState {
pub(super) unsafe extern "C" fn smb_gettxfiles(
tx: *mut std::ffi::c_void, direction: u8,
) -> AppLayerGetFileState {
let tx = cast_pointer!(tx, SMBTransaction);
if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
let tx_dir : u8 = tdf.direction.into();
let tx_dir: u8 = tdf.direction.into();
if direction & tx_dir != 0 {
if let Some(sfcm) = { SURICATA_SMB_FILE_CONFIG } {
return AppLayerGetFileState { fc: &mut tdf.file_tracker.file, cfg: sfcm.files_sbcfg }
return AppLayerGetFileState {
fc: &mut tdf.file_tracker.file,
cfg: sfcm.files_sbcfg,
};
}
}
}

@ -23,7 +23,7 @@ pub fn fsctl_func_to_string(f: u32) -> String {
0x00060194 => "FSCTL_DFS_GET_REFERRALS",
0x000601B0 => "FSCTL_DFS_GET_REFERRALS_EX",
0x00090000 => "FSCTL_REQUEST_OPLOCK_LEVEL_1",
0x00090004 => "FSCTL_REQUEST_OPLOCK_LEVEL_2",
0x00090004 => "FSCTL_REQUEST_OPLOCK_LEVEL_2",
0x00090008 => "FSCTL_REQUEST_BATCH_OPLOCK",
0x0009000C => "FSCTL_OPLOCK_BREAK_ACKNOWLEDGE",
0x00090010 => "FSCTL_OPBATCH_ACK_CLOSE_PENDING",
@ -38,7 +38,7 @@ pub fn fsctl_func_to_string(f: u32) -> String {
0x0009003C => "FSCTL_GET_COMPRESSION",
0x0009004F => "FSCTL_MARK_AS_SYSTEM_HIVE",
0x00090050 => "FSCTL_OPLOCK_BREAK_ACK_NO_2",
0x00090054 => "FSCTL_INVALIDATE_VOLUMES",
0x00090054 => "FSCTL_INVALIDATE_VOLUMES",
0x00090058 => "FSCTL_QUERY_FAT_BPB",
0x0009005C => "FSCTL_REQUEST_FILTER_OPLOCK",
0x00090060 => "FSCTL_FILESYSTEM_GET_STATISTICS",
@ -48,7 +48,7 @@ pub fn fsctl_func_to_string(f: u32) -> String {
0x00090073 => "FSCTL_GET_RETRIEVAL_POINTERS",
0x00090074 => "FSCTL_MOVE_FILE",
0x00090078 => "FSCTL_IS_VOLUME_DIRTY",
0x0009007C => "FSCTL_GET_HFS_INFORMATION",
0x0009007C => "FSCTL_GET_HFS_INFORMATION",
0x00090083 => "FSCTL_ALLOW_EXTENDED_DASD_IO",
0x00090087 => "FSCTL_READ_PROPERTY_DATA",
0x0009008B => "FSCTL_WRITE_PROPERTY_DATA",
@ -109,6 +109,9 @@ pub fn fsctl_func_to_string(f: u32) -> String {
0x001440F2 => "FSCTL_SRV_COPYCHUNK",
0x001441bb => "FSCTL_SRV_READ_HASH",
0x001480F2 => "FSCTL_SRV_COPYCHUNK_WRITE",
_ => { return (f).to_string(); },
}.to_string()
_ => {
return (f).to_string();
}
}
.to_string()
}

@ -15,23 +15,25 @@
* 02110-1301, USA.
*/
use std::str;
use std::string::String;
use uuid;
use suricata_sys::sys::SCConfNode;
use crate::conf::ConfNode;
use crate::dcerpc::dcerpc::*;
use crate::jsonbuilder::{JsonBuilder, JsonError};
use crate::smb::funcs::*;
use crate::smb::smb::*;
use crate::smb::smb1::*;
use crate::smb::smb2::*;
use crate::dcerpc::dcerpc::*;
use crate::smb::funcs::*;
use crate::smb::smb_status::*;
use std::error::Error;
use std::fmt;
use std::str;
use std::string::String;
use suricata_sys::sys::SCConfNode;
use uuid;
#[cfg(not(feature = "debug"))]
fn debug_add_progress(_js: &mut JsonBuilder, _tx: &SMBTransaction) -> Result<(), JsonError> { Ok(()) }
fn debug_add_progress(_js: &mut JsonBuilder, _tx: &SMBTransaction) -> Result<(), JsonError> {
Ok(())
}
#[cfg(feature = "debug")]
fn debug_add_progress(jsb: &mut JsonBuilder, tx: &SMBTransaction) -> Result<(), JsonError> {
@ -107,7 +109,7 @@ fn smb_common_header(
} else {
let dialect = match state.dialect_vec {
Some(ref d) => str::from_utf8(d).unwrap_or("invalid"),
None => "unknown",
None => "unknown",
};
jsb.set_string("dialect", dialect)?;
}
@ -118,14 +120,14 @@ fn smb_common_header(
if ok {
jsb.set_string("command", &smb1_command_string(cmd))?;
}
},
}
2 => {
let (ok, cmd) = tx.vercmd.get_smb2_cmd();
if ok {
jsb.set_string("command", &smb2_command_string(cmd))?;
}
},
_ => { },
}
_ => {}
}
match tx.vercmd.get_ntstatus() {
@ -136,39 +138,39 @@ fn smb_common_header(
None => {
let status_str = format!("{}", ntstatus);
jsb.set_string("status", &status_str)?
},
}
};
let status_hex = format!("0x{:x}", ntstatus);
jsb.set_string("status_code", &status_hex)?;
},
}
(false, _) => {
#[allow(clippy::single_match)]
match tx.vercmd.get_dos_error() {
(true, errclass, errcode) => {
match errclass {
1 => { // DOSERR
1 => {
// DOSERR
let status = smb_dos_error_string(errcode);
jsb.set_string("status", &status)?;
},
2 => { // SRVERR
}
2 => {
// SRVERR
let status = smb_srv_error_string(errcode);
jsb.set_string("status", &status)?;
}
_ => {
let s = format!("UNKNOWN_{:02x}_{:04x}", errclass, errcode);
jsb.set_string("status", &s)?;
},
}
}
let status_hex = format!("0x{:04x}", errcode);
jsb.set_string("status_code", &status_hex)?;
},
(_, _, _) => {
},
}
(_, _, _) => {}
}
},
}
}
jsb.set_uint("session_id", tx.hdr.ssn_id)?;
jsb.set_uint("tree_id", tx.hdr.tree_id as u64)?;
@ -224,13 +226,13 @@ fn smb_common_header(
jsb.set_string("native_lm", &lm)?;
jsb.close()?;
}
},
}
Some(SMBTransactionTypeData::CREATE(ref x)) => {
if flags != SMB_LOG_DEFAULT_ALL && (flags & SMB_LOG_TYPE_CREATE) == 0 {
return Err(SmbLogError::SkippedByConf);
}
let mut name_raw = x.filename.to_vec();
name_raw.retain(|&i|i != 0x00);
name_raw.retain(|&i| i != 0x00);
if !name_raw.is_empty() {
let name = String::from_utf8_lossy(&name_raw);
if x.directory {
@ -243,13 +245,27 @@ fn smb_common_header(
jsb.set_string("filename", "<share_root>")?;
}
match x.disposition {
0 => { jsb.set_string("disposition", "FILE_SUPERSEDE")?; },
1 => { jsb.set_string("disposition", "FILE_OPEN")?; },
2 => { jsb.set_string("disposition", "FILE_CREATE")?; },
3 => { jsb.set_string("disposition", "FILE_OPEN_IF")?; },
4 => { jsb.set_string("disposition", "FILE_OVERWRITE")?; },
5 => { jsb.set_string("disposition", "FILE_OVERWRITE_IF")?; },
_ => { jsb.set_string("disposition", "UNKNOWN")?; },
0 => {
jsb.set_string("disposition", "FILE_SUPERSEDE")?;
}
1 => {
jsb.set_string("disposition", "FILE_OPEN")?;
}
2 => {
jsb.set_string("disposition", "FILE_CREATE")?;
}
3 => {
jsb.set_string("disposition", "FILE_OPEN_IF")?;
}
4 => {
jsb.set_string("disposition", "FILE_OVERWRITE")?;
}
5 => {
jsb.set_string("disposition", "FILE_OVERWRITE_IF")?;
}
_ => {
jsb.set_string("disposition", "UNKNOWN")?;
}
}
if x.delete_on_close {
jsb.set_string("access", "delete on close")?;
@ -266,7 +282,7 @@ fn smb_common_header(
let gs = fuid_to_string(&x.guid);
jsb.set_string("fuid", &gs)?;
},
}
Some(SMBTransactionTypeData::NEGOTIATE(ref x)) => {
if flags != SMB_LOG_DEFAULT_ALL && (flags & SMB_LOG_TYPE_NEGOTIATE) == 0 {
return Err(SmbLogError::SkippedByConf);
@ -299,7 +315,7 @@ fn smb_common_header(
if state.max_write_size > 0 {
jsb.set_uint("max_write_size", state.max_write_size)?;
}
},
}
Some(SMBTransactionTypeData::TREECONNECT(ref x)) => {
if flags != SMB_LOG_DEFAULT_ALL && (flags & SMB_LOG_TYPE_TREECONNECT) == 0 {
return Err(SmbLogError::SkippedByConf);
@ -328,13 +344,21 @@ fn smb_common_header(
// share type only for SMB2
} else {
match x.share_type {
1 => { jsb.set_string("share_type", "FILE")?; },
2 => { jsb.set_string("share_type", "PIPE")?; },
3 => { jsb.set_string("share_type", "PRINT")?; },
_ => { jsb.set_string("share_type", "UNKNOWN")?; },
1 => {
jsb.set_string("share_type", "FILE")?;
}
2 => {
jsb.set_string("share_type", "PIPE")?;
}
3 => {
jsb.set_string("share_type", "PRINT")?;
}
_ => {
jsb.set_string("share_type", "UNKNOWN")?;
}
}
}
},
}
Some(SMBTransactionTypeData::FILE(ref x)) => {
if flags != SMB_LOG_DEFAULT_ALL && (flags & SMB_LOG_TYPE_FILE) == 0 {
return Err(SmbLogError::SkippedByConf);
@ -345,7 +369,7 @@ fn smb_common_header(
jsb.set_string("share", &share_name)?;
let gs = fuid_to_string(&x.fuid);
jsb.set_string("fuid", &gs)?;
},
}
Some(SMBTransactionTypeData::RENAME(ref x)) => {
if flags != SMB_LOG_DEFAULT_ALL && (flags & SMB_LOG_TYPE_RENAME) == 0 {
return Err(SmbLogError::SkippedByConf);
@ -365,7 +389,7 @@ fn smb_common_header(
jsb.close()?;
let gs = fuid_to_string(&x.fuid);
jsb.set_string("fuid", &gs)?;
},
}
Some(SMBTransactionTypeData::DCERPC(ref x)) => {
if flags != SMB_LOG_DEFAULT_ALL && (flags & SMB_LOG_TYPE_DCERPC) == 0 {
return Err(SmbLogError::SkippedByConf);
@ -403,7 +427,9 @@ fn smb_common_header(
for i in ifaces {
jsb.start_object()?;
let ifstr = uuid::Uuid::from_slice(&i.uuid);
let ifstr = ifstr.map(|ifstr| ifstr.to_hyphenated().to_string()).unwrap();
let ifstr = ifstr
.map(|ifstr| ifstr.to_hyphenated().to_string())
.unwrap();
jsb.set_string("uuid", &ifstr)?;
let vstr = format!("{}.{}", i.ver, i.ver_min);
jsb.set_string("version", &vstr)?;
@ -412,18 +438,20 @@ fn smb_common_header(
jsb.close()?;
}
}
},
}
DCERPC_TYPE_BIND => {
if let Some(ref ifaces) = state.dcerpc_ifaces {
jsb.open_array("interfaces")?;
for i in ifaces {
jsb.start_object()?;
let ifstr = uuid::Uuid::from_slice(&i.uuid);
let ifstr = ifstr.map(|ifstr| ifstr.to_hyphenated().to_string()).unwrap();
let ifstr = ifstr
.map(|ifstr| ifstr.to_hyphenated().to_string())
.unwrap();
jsb.set_string("uuid", &ifstr)?;
let vstr = format!("{}.{}", i.ver, i.ver_min);
jsb.set_string("version", &vstr)?;
if i.acked {
jsb.set_uint("ack_result", i.ack_result as u64)?;
jsb.set_uint("ack_reason", i.ack_reason as u64)?;
@ -432,8 +460,8 @@ fn smb_common_header(
}
jsb.close()?;
}
},
_ => {},
}
_ => {}
}
}
if x.res_set {
@ -444,9 +472,9 @@ fn smb_common_header(
jsb.set_uint("frag_cnt", x.frag_cnt_tc as u64)?;
jsb.set_uint("stub_data_size", x.stub_data_tc.len() as u64)?;
jsb.close()?;
},
}
// we don't handle BINDACK w/o BIND
_ => {},
_ => {}
}
}
jsb.set_uint("call_id", x.call_id as u64)?;
@ -457,13 +485,13 @@ fn smb_common_header(
return Err(SmbLogError::SkippedByConf);
}
jsb.set_string("function", &fsctl_func_to_string(x.func))?;
},
}
Some(SMBTransactionTypeData::SETFILEPATHINFO(ref x)) => {
if flags != SMB_LOG_DEFAULT_ALL && (flags & SMB_LOG_TYPE_SETFILEPATHINFO) == 0 {
return Err(SmbLogError::SkippedByConf);
}
let mut name_raw = x.filename.to_vec();
name_raw.retain(|&i|i != 0x00);
name_raw.retain(|&i| i != 0x00);
if !name_raw.is_empty() {
let name = String::from_utf8_lossy(&name_raw);
jsb.set_string("filename", &name)?;
@ -480,29 +508,30 @@ fn smb_common_header(
match x.subcmd {
8 => {
jsb.set_string("subcmd", "SET_FILE_INFO")?;
},
}
6 => {
jsb.set_string("subcmd", "SET_PATH_INFO")?;
},
_ => { },
}
_ => {}
}
#[allow(clippy::single_match)]
match x.loi {
1013 => { // Set Disposition Information
1013 => {
// Set Disposition Information
jsb.set_string("level_of_interest", "Set Disposition Information")?;
},
_ => { },
}
_ => {}
}
let gs = fuid_to_string(&x.fid);
jsb.set_string("fuid", &gs)?;
},
}
None => {
if flags != SMB_LOG_DEFAULT_ALL && (flags & SMB_LOG_TYPE_GENERIC) == 0 {
return Err(SmbLogError::SkippedByConf);
}
},
}
}
return Ok(());
}

@ -19,26 +19,27 @@
pub mod error;
pub mod smb_records;
#[rustfmt::skip]
pub mod smb_status;
pub mod smb1_records;
pub mod smb2_records;
pub mod nbss_records;
pub mod dcerpc_records;
pub mod nbss_records;
pub mod ntlmssp_records;
pub mod smb1_records;
pub mod smb2_records;
pub mod auth;
pub mod dcerpc;
pub mod debug;
pub mod detect;
pub mod events;
pub mod files;
pub mod funcs;
pub mod log;
pub mod session;
pub mod smb;
pub mod smb1;
pub mod smb1_session;
pub mod smb2;
pub mod smb2_session;
pub mod smb2_ioctl;
pub mod smb2_session;
pub mod smb3;
pub mod dcerpc;
pub mod session;
pub mod log;
pub mod detect;
pub mod debug;
pub mod events;
pub mod auth;
pub mod files;
pub mod funcs;

@ -18,32 +18,32 @@
use nom8::bytes::streaming::take;
use nom8::combinator::rest;
use nom8::number::streaming::be_u32;
use nom8::Parser;
use nom8::IResult;
use nom8::Parser;
pub const NBSS_MSGTYPE_SESSION_MESSAGE: u8 = 0x00;
pub const NBSS_MSGTYPE_SESSION_REQUEST: u8 = 0x81;
pub const NBSS_MSGTYPE_POSITIVE_SSN_RESPONSE: u8 = 0x82;
pub const NBSS_MSGTYPE_NEGATIVE_SSN_RESPONSE: u8 = 0x83;
pub const NBSS_MSGTYPE_RETARG_RESPONSE: u8 = 0x84;
pub const NBSS_MSGTYPE_KEEP_ALIVE: u8 = 0x85;
pub const NBSS_MSGTYPE_SESSION_MESSAGE: u8 = 0x00;
pub const NBSS_MSGTYPE_SESSION_REQUEST: u8 = 0x81;
pub const NBSS_MSGTYPE_POSITIVE_SSN_RESPONSE: u8 = 0x82;
pub const NBSS_MSGTYPE_NEGATIVE_SSN_RESPONSE: u8 = 0x83;
pub const NBSS_MSGTYPE_RETARG_RESPONSE: u8 = 0x84;
pub const NBSS_MSGTYPE_KEEP_ALIVE: u8 = 0x85;
#[derive(Debug,PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq)]
pub struct NbssRecord<'a> {
pub message_type: u8,
pub length: u32,
pub data: &'a[u8],
pub data: &'a [u8],
}
impl NbssRecord<'_> {
pub fn is_valid(&self) -> bool {
let valid = match self.message_type {
NBSS_MSGTYPE_SESSION_MESSAGE |
NBSS_MSGTYPE_SESSION_REQUEST |
NBSS_MSGTYPE_POSITIVE_SSN_RESPONSE |
NBSS_MSGTYPE_NEGATIVE_SSN_RESPONSE |
NBSS_MSGTYPE_RETARG_RESPONSE |
NBSS_MSGTYPE_KEEP_ALIVE => true,
NBSS_MSGTYPE_SESSION_MESSAGE
| NBSS_MSGTYPE_SESSION_REQUEST
| NBSS_MSGTYPE_POSITIVE_SSN_RESPONSE
| NBSS_MSGTYPE_NEGATIVE_SSN_RESPONSE
| NBSS_MSGTYPE_RETARG_RESPONSE
| NBSS_MSGTYPE_KEEP_ALIVE => true,
_ => false,
};
valid
@ -53,8 +53,11 @@ impl NbssRecord<'_> {
}
pub fn is_smb(&self) -> bool {
let valid = self.is_valid();
let smb = self.data.len() >= 4 &&
self.data[1] == b'S' && self.data[2] == b'M' && self.data[3] == b'B' && (self.data[0] == b'\xFE' || self.data[0] == b'\xFF' || self.data[0] == b'\xFD');
let smb = self.data.len() >= 4
&& self.data[1] == b'S'
&& self.data[2] == b'M'
&& self.data[3] == b'B'
&& (self.data[0] == b'\xFE' || self.data[0] == b'\xFF' || self.data[0] == b'\xFD');
valid && smb
}
@ -94,19 +97,15 @@ mod tests {
#[test]
fn test_parse_nbss_record() {
let buff:&[u8] = &[
/* message type */ 0x00,
/* length */ 0x00, 0x00, 0x55,
/* data */ 0xff, 0x53, 0x4d, 0x42, 0x72, 0x00, 0x00, 0x00, 0x00,
0x98, 0x53, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
0xfe, 0x00, 0x00, 0x00, 0x00, 0x11, 0x05, 0x00, 0x03,
0x0a, 0x00, 0x01, 0x00, 0x04, 0x11, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0xe3,
0x00, 0x80, 0x2a, 0x55, 0xc4, 0x38, 0x89, 0x03, 0xcd,
0x01, 0x2c, 0x01, 0x00, 0x10, 0x00, 0xfe, 0x82, 0xf1,
0x64, 0x0b, 0x66, 0xba, 0x4a, 0xbb, 0x81, 0xe1, 0xea,
0x54, 0xae, 0xb8, 0x66];
let buff: &[u8] = &[
/* message type */ 0x00, /* length */ 0x00, 0x00, 0x55, /* data */ 0xff,
0x53, 0x4d, 0x42, 0x72, 0x00, 0x00, 0x00, 0x00, 0x98, 0x53, 0xc8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00,
0x00, 0x00, 0x00, 0x11, 0x05, 0x00, 0x03, 0x0a, 0x00, 0x01, 0x00, 0x04, 0x11, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0xe3, 0x00, 0x80, 0x2a,
0x55, 0xc4, 0x38, 0x89, 0x03, 0xcd, 0x01, 0x2c, 0x01, 0x00, 0x10, 0x00, 0xfe, 0x82,
0xf1, 0x64, 0x0b, 0x66, 0xba, 0x4a, 0xbb, 0x81, 0xe1, 0xea, 0x54, 0xae, 0xb8, 0x66,
];
let result = parse_nbss_record(buff);
match result {
@ -137,19 +136,16 @@ mod tests {
}
// Non-SMB packet scenario
let buff_not_smb:&[u8] = &[
/* message type */ 0x00,
/* length */ 0x00, 0x00, 0x55,
/* data !SMB */ 0xff, 0x52, 0x4e, 0x41, 0x72, 0x00, 0x00, 0x00, 0x00,
0x98, 0x53, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
0xfe, 0x00, 0x00, 0x00, 0x00, 0x11, 0x05, 0x00, 0x03,
0x0a, 0x00, 0x01, 0x00, 0x04, 0x11, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0xe3,
0x00, 0x80, 0x2a, 0x55, 0xc4, 0x38, 0x89, 0x03, 0xcd,
0x01, 0x2c, 0x01, 0x00, 0x10, 0x00, 0xfe, 0x82, 0xf1,
0x64, 0x0b, 0x66, 0xba, 0x4a, 0xbb, 0x81, 0xe1, 0xea,
0x54, 0xae, 0xb8, 0x66];
let buff_not_smb: &[u8] = &[
/* message type */ 0x00, /* length */ 0x00, 0x00, 0x55,
/* data !SMB */ 0xff, 0x52, 0x4e, 0x41, 0x72, 0x00, 0x00, 0x00, 0x00, 0x98, 0x53,
0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x11, 0x05, 0x00, 0x03, 0x0a, 0x00, 0x01,
0x00, 0x04, 0x11, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd,
0xe3, 0x00, 0x80, 0x2a, 0x55, 0xc4, 0x38, 0x89, 0x03, 0xcd, 0x01, 0x2c, 0x01, 0x00,
0x10, 0x00, 0xfe, 0x82, 0xf1, 0x64, 0x0b, 0x66, 0xba, 0x4a, 0xbb, 0x81, 0xe1, 0xea,
0x54, 0xae, 0xb8, 0x66,
];
let result_not_smb = parse_nbss_record(buff_not_smb);
match result_not_smb {
@ -183,14 +179,13 @@ mod tests {
#[test]
fn test_parse_nbss_record_partial() {
let buff:&[u8] = &[
/* message type */ 0x00,
/* length */ 0x00, 0x00, 0x29,
/* data < length*/ 0xff, 0x53, 0x4d, 0x42, 0x04, 0x00, 0x00, 0x00,
0x00, 0x18, 0x43, 0xc8, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x08, 0xbd, 0x20, 0x02, 0x08, 0x06, 0x00,
0x02, 0x40, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00];
let buff: &[u8] = &[
/* message type */ 0x00, /* length */ 0x00, 0x00, 0x29,
/* data < length*/ 0xff, 0x53, 0x4d, 0x42, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18,
0x43, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x08, 0xbd, 0x20, 0x02, 0x08, 0x06, 0x00, 0x02, 0x40, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00,
];
let result = parse_nbss_record_partial(buff);
match result {
@ -219,6 +214,5 @@ mod tests {
panic!("Unexpected behavior!");
}
}
}
}

@ -20,9 +20,9 @@ use nom8::bytes::streaming::take;
use nom8::combinator::{cond, rest, verify};
use nom8::error::{make_error, ErrorKind};
use nom8::number::streaming::{le_u16, le_u32, le_u8};
use nom8::Parser;
use nom8::Err;
use nom8::IResult;
use nom8::Parser;
use std::fmt;
#[derive(Debug, PartialEq, Eq)]

@ -16,10 +16,10 @@
*/
use crate::kerberos::*;
use crate::smb::auth::*;
use crate::smb::smb::*;
use crate::smb::smb1_session::*;
use crate::smb::smb1_records::SessionSetupRequest;
use crate::smb::auth::*;
use crate::smb::smb1_session::*;
#[derive(Default, Debug)]
pub struct SMBTransactionSessionSetup {
@ -31,19 +31,18 @@ pub struct SMBTransactionSessionSetup {
impl SMBTransactionSessionSetup {
pub fn new() -> Self {
return Default::default()
return Default::default();
}
}
impl SMBState {
pub fn new_sessionsetup_tx(&mut self, hdr: SMBCommonHdr)
-> &mut SMBTransaction
{
pub fn new_sessionsetup_tx(&mut self, hdr: SMBCommonHdr) -> &mut SMBTransaction {
let mut tx = self.new_tx();
tx.hdr = hdr;
tx.type_data = Some(SMBTransactionTypeData::SESSIONSETUP(
SMBTransactionSessionSetup::new()));
SMBTransactionSessionSetup::new(),
));
tx.request_done = true;
tx.response_done = self.tc_trunc; // no response expected if tc is truncated
@ -53,14 +52,13 @@ impl SMBState {
return tx_ref.unwrap();
}
pub fn get_sessionsetup_tx(&mut self, hdr: SMBCommonHdr)
-> Option<&mut SMBTransaction>
{
pub fn get_sessionsetup_tx(&mut self, hdr: SMBCommonHdr) -> Option<&mut SMBTransaction> {
for tx in &mut self.transactions {
let hit = tx.hdr.compare(&hdr) && match tx.type_data {
Some(SMBTransactionTypeData::SESSIONSETUP(_)) => { true },
_ => { false },
};
let hit = tx.hdr.compare(&hdr)
&& match tx.type_data {
Some(SMBTransactionTypeData::SESSIONSETUP(_)) => true,
_ => false,
};
if hit {
tx.tx_data.0.updated_tc = true;
tx.tx_data.0.updated_ts = true;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -15,11 +15,11 @@
* 02110-1301, USA.
*/
use crate::smb::smb_records::*;
use crate::smb::smb1_records::*;
use crate::smb::smb::*;
use crate::smb::events::*;
use crate::smb::auth::*;
use crate::smb::events::*;
use crate::smb::smb::*;
use crate::smb::smb1_records::*;
use crate::smb::smb_records::*;
#[derive(Debug)]
pub struct SessionSetupResponse {
@ -27,22 +27,19 @@ pub struct SessionSetupResponse {
pub native_lm: Vec<u8>,
}
pub fn smb1_session_setup_response_host_info(r: &SmbRecord, blob: &[u8]) -> SessionSetupResponse
{
pub fn smb1_session_setup_response_host_info(r: &SmbRecord, blob: &[u8]) -> SessionSetupResponse {
if blob.len() > 1 && r.has_unicode_support() {
let offset = r.data.len() - blob.len();
let blob = if offset % 2 == 1 { &blob[1..] } else { blob };
let (native_os, native_lm) = match smb_get_unicode_string(blob) {
Ok((rem, n1)) => {
match smb_get_unicode_string(rem) {
Ok((_, n2)) => (n1, n2),
_ => { (n1, Vec::new()) },
}
Ok((rem, n1)) => match smb_get_unicode_string(rem) {
Ok((_, n2)) => (n1, n2),
_ => (n1, Vec::new()),
},
_ => { (Vec::new(), Vec::new()) },
_ => (Vec::new(), Vec::new()),
};
SCLogDebug!("name1 {:?} name2 {:?}", native_os,native_lm);
SCLogDebug!("name1 {:?} name2 {:?}", native_os, native_lm);
SessionSetupResponse {
native_os,
native_lm,
@ -50,13 +47,11 @@ pub fn smb1_session_setup_response_host_info(r: &SmbRecord, blob: &[u8]) -> Sess
} else {
SCLogDebug!("session_setup_response_host_info: not unicode");
let (native_os, native_lm) = match smb_get_ascii_string(blob) {
Ok((rem, n1)) => {
match smb_get_ascii_string(rem) {
Ok((_, n2)) => (n1, n2),
_ => { (n1, Vec::new()) },
}
Ok((rem, n1)) => match smb_get_ascii_string(rem) {
Ok((_, n2)) => (n1, n2),
_ => (n1, Vec::new()),
},
_ => { (Vec::new(), Vec::new()) },
_ => (Vec::new(), Vec::new()),
};
SessionSetupResponse {
native_os,
@ -65,14 +60,17 @@ pub fn smb1_session_setup_response_host_info(r: &SmbRecord, blob: &[u8]) -> Sess
}
}
pub fn smb1_session_setup_request(state: &mut SMBState, r: &SmbRecord, andx_offset: usize)
{
pub fn smb1_session_setup_request(state: &mut SMBState, r: &SmbRecord, andx_offset: usize) {
SCLogDebug!("SMB1_COMMAND_SESSION_SETUP_ANDX user_id {}", r.user_id);
#[allow(clippy::single_match)]
match parse_smb_setup_andx_record(&r.data[andx_offset-SMB1_HEADER_SIZE..], r) {
match parse_smb_setup_andx_record(&r.data[andx_offset - SMB1_HEADER_SIZE..], r) {
Ok((_rem, setup)) => {
let hdr = SMBCommonHdr::new(SMBHDR_TYPE_HEADER,
r.ssn_id as u64, 0, r.multiplex_id as u64);
let hdr = SMBCommonHdr::new(
SMBHDR_TYPE_HEADER,
r.ssn_id as u64,
0,
r.multiplex_id as u64,
);
let tx = state.new_sessionsetup_tx(hdr);
tx.vercmd.set_smb1_cmd(r.command);
@ -88,24 +86,23 @@ pub fn smb1_session_setup_request(state: &mut SMBState, r: &SmbRecord, andx_offs
}
}
}
},
}
_ => {
// events.push(SMBEvent::MalformedData);
},
}
}
}
fn smb1_session_setup_update_tx(tx: &mut SMBTransaction, r: &SmbRecord, andx_offset: usize)
{
match parse_smb_response_setup_andx_record(&r.data[andx_offset-SMB1_HEADER_SIZE..]) {
fn smb1_session_setup_update_tx(tx: &mut SMBTransaction, r: &SmbRecord, andx_offset: usize) {
match parse_smb_response_setup_andx_record(&r.data[andx_offset - SMB1_HEADER_SIZE..]) {
Ok((rem, _setup)) => {
if let Some(SMBTransactionTypeData::SESSIONSETUP(ref mut td)) = tx.type_data {
td.response_host = Some(smb1_session_setup_response_host_info(r, rem));
}
},
}
_ => {
tx.set_event(SMBEvent::MalformedData);
},
}
}
// update tx even if we can't parse the response
tx.hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_HEADER); // to overwrite ssn_id 0
@ -113,25 +110,30 @@ fn smb1_session_setup_update_tx(tx: &mut SMBTransaction, r: &SmbRecord, andx_off
tx.response_done = true;
}
pub fn smb1_session_setup_response(state: &mut SMBState, r: &SmbRecord, andx_offset: usize)
{
pub fn smb1_session_setup_response(state: &mut SMBState, r: &SmbRecord, andx_offset: usize) {
// try exact match with session id already set (e.g. NTLMSSP AUTH phase)
let found = r.ssn_id != 0 && match state.get_sessionsetup_tx(
SMBCommonHdr::new(SMBHDR_TYPE_HEADER,
r.ssn_id as u64, 0, r.multiplex_id as u64))
{
Some(tx) => {
smb1_session_setup_update_tx(tx, r, andx_offset);
SCLogDebug!("smb1_session_setup_response: tx {:?}", tx);
true
},
None => { false },
};
let found = r.ssn_id != 0
&& match state.get_sessionsetup_tx(SMBCommonHdr::new(
SMBHDR_TYPE_HEADER,
r.ssn_id as u64,
0,
r.multiplex_id as u64,
)) {
Some(tx) => {
smb1_session_setup_update_tx(tx, r, andx_offset);
SCLogDebug!("smb1_session_setup_response: tx {:?}", tx);
true
}
None => false,
};
// otherwise try match with ssn id 0 (e.g. NTLMSSP_NEGOTIATE)
if !found {
if let Some(tx) = state.get_sessionsetup_tx(
SMBCommonHdr::new(SMBHDR_TYPE_HEADER, 0, 0, r.multiplex_id as u64))
{
if let Some(tx) = state.get_sessionsetup_tx(SMBCommonHdr::new(
SMBHDR_TYPE_HEADER,
0,
0,
r.multiplex_id as u64,
)) {
smb1_session_setup_update_tx(tx, r, andx_offset);
SCLogDebug!("smb1_session_setup_response: tx {:?}", tx);
} else {

@ -18,59 +18,61 @@
use nom8::Err;
use crate::direction::Direction;
use crate::smb::smb::*;
use crate::smb::smb2_records::*;
use crate::smb::smb2_session::*;
use crate::smb::smb2_ioctl::*;
use crate::smb::dcerpc::*;
use crate::smb::events::*;
use crate::smb::files::*;
use crate::smb::smb::*;
use crate::smb::smb2_ioctl::*;
use crate::smb::smb2_records::*;
use crate::smb::smb2_session::*;
use crate::smb::smb_status::*;
pub const SMB2_COMMAND_NEGOTIATE_PROTOCOL: u16 = 0;
pub const SMB2_COMMAND_SESSION_SETUP: u16 = 1;
pub const SMB2_COMMAND_SESSION_LOGOFF: u16 = 2;
pub const SMB2_COMMAND_TREE_CONNECT: u16 = 3;
pub const SMB2_COMMAND_TREE_DISCONNECT: u16 = 4;
pub const SMB2_COMMAND_CREATE: u16 = 5;
pub const SMB2_COMMAND_CLOSE: u16 = 6;
pub const SMB2_COMMAND_FLUSH: u16 = 7;
pub const SMB2_COMMAND_READ: u16 = 8;
pub const SMB2_COMMAND_WRITE: u16 = 9;
pub const SMB2_COMMAND_LOCK: u16 = 10;
pub const SMB2_COMMAND_IOCTL: u16 = 11;
pub const SMB2_COMMAND_CANCEL: u16 = 12;
pub const SMB2_COMMAND_KEEPALIVE: u16 = 13;
pub const SMB2_COMMAND_FIND: u16 = 14;
pub const SMB2_COMMAND_CHANGE_NOTIFY: u16 = 15;
pub const SMB2_COMMAND_GET_INFO: u16 = 16;
pub const SMB2_COMMAND_SET_INFO: u16 = 17;
pub const SMB2_COMMAND_OPLOCK_BREAK: u16 = 18;
pub const SMB2_COMMAND_NEGOTIATE_PROTOCOL: u16 = 0;
pub const SMB2_COMMAND_SESSION_SETUP: u16 = 1;
pub const SMB2_COMMAND_SESSION_LOGOFF: u16 = 2;
pub const SMB2_COMMAND_TREE_CONNECT: u16 = 3;
pub const SMB2_COMMAND_TREE_DISCONNECT: u16 = 4;
pub const SMB2_COMMAND_CREATE: u16 = 5;
pub const SMB2_COMMAND_CLOSE: u16 = 6;
pub const SMB2_COMMAND_FLUSH: u16 = 7;
pub const SMB2_COMMAND_READ: u16 = 8;
pub const SMB2_COMMAND_WRITE: u16 = 9;
pub const SMB2_COMMAND_LOCK: u16 = 10;
pub const SMB2_COMMAND_IOCTL: u16 = 11;
pub const SMB2_COMMAND_CANCEL: u16 = 12;
pub const SMB2_COMMAND_KEEPALIVE: u16 = 13;
pub const SMB2_COMMAND_FIND: u16 = 14;
pub const SMB2_COMMAND_CHANGE_NOTIFY: u16 = 15;
pub const SMB2_COMMAND_GET_INFO: u16 = 16;
pub const SMB2_COMMAND_SET_INFO: u16 = 17;
pub const SMB2_COMMAND_OPLOCK_BREAK: u16 = 18;
pub fn smb2_command_string(c: u16) -> String {
match c {
SMB2_COMMAND_NEGOTIATE_PROTOCOL => "SMB2_COMMAND_NEGOTIATE_PROTOCOL",
SMB2_COMMAND_SESSION_SETUP => "SMB2_COMMAND_SESSION_SETUP",
SMB2_COMMAND_SESSION_LOGOFF => "SMB2_COMMAND_SESSION_LOGOFF",
SMB2_COMMAND_TREE_CONNECT => "SMB2_COMMAND_TREE_CONNECT",
SMB2_COMMAND_TREE_DISCONNECT => "SMB2_COMMAND_TREE_DISCONNECT",
SMB2_COMMAND_CREATE => "SMB2_COMMAND_CREATE",
SMB2_COMMAND_CLOSE => "SMB2_COMMAND_CLOSE",
SMB2_COMMAND_READ => "SMB2_COMMAND_READ",
SMB2_COMMAND_FLUSH => "SMB2_COMMAND_FLUSH",
SMB2_COMMAND_WRITE => "SMB2_COMMAND_WRITE",
SMB2_COMMAND_LOCK => "SMB2_COMMAND_LOCK",
SMB2_COMMAND_IOCTL => "SMB2_COMMAND_IOCTL",
SMB2_COMMAND_CANCEL => "SMB2_COMMAND_CANCEL",
SMB2_COMMAND_KEEPALIVE => "SMB2_COMMAND_KEEPALIVE",
SMB2_COMMAND_FIND => "SMB2_COMMAND_FIND",
SMB2_COMMAND_CHANGE_NOTIFY => "SMB2_COMMAND_CHANGE_NOTIFY",
SMB2_COMMAND_GET_INFO => "SMB2_COMMAND_GET_INFO",
SMB2_COMMAND_SET_INFO => "SMB2_COMMAND_SET_INFO",
SMB2_COMMAND_OPLOCK_BREAK => "SMB2_COMMAND_OPLOCK_BREAK",
_ => { return (c).to_string(); },
}.to_string()
SMB2_COMMAND_NEGOTIATE_PROTOCOL => "SMB2_COMMAND_NEGOTIATE_PROTOCOL",
SMB2_COMMAND_SESSION_SETUP => "SMB2_COMMAND_SESSION_SETUP",
SMB2_COMMAND_SESSION_LOGOFF => "SMB2_COMMAND_SESSION_LOGOFF",
SMB2_COMMAND_TREE_CONNECT => "SMB2_COMMAND_TREE_CONNECT",
SMB2_COMMAND_TREE_DISCONNECT => "SMB2_COMMAND_TREE_DISCONNECT",
SMB2_COMMAND_CREATE => "SMB2_COMMAND_CREATE",
SMB2_COMMAND_CLOSE => "SMB2_COMMAND_CLOSE",
SMB2_COMMAND_READ => "SMB2_COMMAND_READ",
SMB2_COMMAND_FLUSH => "SMB2_COMMAND_FLUSH",
SMB2_COMMAND_WRITE => "SMB2_COMMAND_WRITE",
SMB2_COMMAND_LOCK => "SMB2_COMMAND_LOCK",
SMB2_COMMAND_IOCTL => "SMB2_COMMAND_IOCTL",
SMB2_COMMAND_CANCEL => "SMB2_COMMAND_CANCEL",
SMB2_COMMAND_KEEPALIVE => "SMB2_COMMAND_KEEPALIVE",
SMB2_COMMAND_FIND => "SMB2_COMMAND_FIND",
SMB2_COMMAND_CHANGE_NOTIFY => "SMB2_COMMAND_CHANGE_NOTIFY",
SMB2_COMMAND_GET_INFO => "SMB2_COMMAND_GET_INFO",
SMB2_COMMAND_SET_INFO => "SMB2_COMMAND_SET_INFO",
SMB2_COMMAND_OPLOCK_BREAK => "SMB2_COMMAND_OPLOCK_BREAK",
_ => {
return (c).to_string();
}
}
.to_string()
}
pub fn smb2_dialect_string(d: u16) -> String {
@ -84,24 +86,25 @@ pub fn smb2_dialect_string(d: u16) -> String {
0x0302 => "3.02",
0x0310 => "3.10",
0x0311 => "3.11",
_ => { return (d).to_string(); },
}.to_string()
_ => {
return (d).to_string();
}
}
.to_string()
}
// later we'll use this to determine if we need to
// track a ssn per type
fn smb2_create_new_tx(cmd: u16) -> bool {
match cmd {
SMB2_COMMAND_READ |
SMB2_COMMAND_WRITE |
SMB2_COMMAND_GET_INFO |
SMB2_COMMAND_SET_INFO => { false },
_ => { true },
SMB2_COMMAND_READ | SMB2_COMMAND_WRITE | SMB2_COMMAND_GET_INFO | SMB2_COMMAND_SET_INFO => {
false
}
_ => true,
}
}
fn smb2_read_response_record_generic(state: &mut SMBState, r: &Smb2Record)
{
fn smb2_read_response_record_generic(state: &mut SMBState, r: &Smb2Record) {
if smb2_create_new_tx(r.command) {
let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
let tx = state.get_generic_tx(2, r.command, &tx_hdr);
@ -112,8 +115,7 @@ fn smb2_read_response_record_generic(state: &mut SMBState, r: &Smb2Record)
}
}
pub fn smb2_read_response_record(state: &mut SMBState, r: &Smb2Record, nbss_remaining: u32)
{
pub fn smb2_read_response_record(state: &mut SMBState, r: &Smb2Record, nbss_remaining: u32) {
let max_queue_size = unsafe { SMB_CFG_MAX_READ_QUEUE_SIZE };
let max_queue_cnt = unsafe { SMB_CFG_MAX_READ_QUEUE_CNT };
@ -131,15 +133,14 @@ pub fn smb2_read_response_record(state: &mut SMBState, r: &Smb2Record, nbss_rema
if r.nt_status == SMB_NTSTATUS_BUFFER_OVERFLOW {
SCLogDebug!("SMBv2/READ: incomplete record, expecting a follow up");
// fall through
} else if r.nt_status != SMB_NTSTATUS_SUCCESS {
SCLogDebug!("SMBv2: read response error code received: skip record");
state.set_skip(Direction::ToClient, nbss_remaining);
return;
}
if (state.max_read_size != 0 && rd.len > state.max_read_size) ||
(unsafe { SMB_CFG_MAX_READ_SIZE != 0 && SMB_CFG_MAX_READ_SIZE < rd.len })
if (state.max_read_size != 0 && rd.len > state.max_read_size)
|| (unsafe { SMB_CFG_MAX_READ_SIZE != 0 && SMB_CFG_MAX_READ_SIZE < rd.len })
{
state.set_event(SMBEvent::ReadResponseTooLarge);
state.set_skip(Direction::ToClient, nbss_remaining);
@ -154,31 +155,44 @@ pub fn smb2_read_response_record(state: &mut SMBState, r: &Smb2Record, nbss_rema
let (offset, file_guid) = match state.read_offset_cache.pop(&guid_key) {
Some(o) => (o.offset, o.guid),
None => {
SCLogDebug!("SMBv2 READ response: reply to unknown request {:?}",rd);
SCLogDebug!("SMBv2 READ response: reply to unknown request {:?}", rd);
state.set_skip(Direction::ToClient, nbss_remaining);
return;
},
}
};
SCLogDebug!("SMBv2 READ: GUID {:?} offset {}", file_guid, offset);
let mut set_event_fileoverlap = false;
// look up existing tracker and if we have it update it
let found = if let Some(tx) = state.get_file_tx_by_fuid_with_open_file(&file_guid, Direction::ToClient) {
let found = if let Some(tx) =
state.get_file_tx_by_fuid_with_open_file(&file_guid, Direction::ToClient)
{
if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
let file_id : u32 = tx.id as u32;
let file_id: u32 = tx.id as u32;
if offset < tdf.file_tracker.tracked {
set_event_fileoverlap = true;
}
if max_queue_size != 0 && tdf.file_tracker.get_inflight_size() + rd.len as u64 > u64::from(max_queue_size) {
if max_queue_size != 0
&& tdf.file_tracker.get_inflight_size() + rd.len as u64
> u64::from(max_queue_size)
{
state.set_event(SMBEvent::ReadQueueSizeExceeded);
state.set_skip(Direction::ToClient, nbss_remaining);
} else if max_queue_cnt != 0 && tdf.file_tracker.get_inflight_cnt() >= max_queue_cnt as usize {
} else if max_queue_cnt != 0
&& tdf.file_tracker.get_inflight_cnt() >= max_queue_cnt as usize
{
state.set_event(SMBEvent::ReadQueueCntExceeded);
state.set_skip(Direction::ToClient, nbss_remaining);
} else {
filetracker_newchunk(&mut tdf.file_tracker,
&tdf.file_name, rd.data, offset,
rd.len, false, &file_id);
filetracker_newchunk(
&mut tdf.file_tracker,
&tdf.file_name,
rd.data,
offset,
rd.len,
false,
&file_id,
);
}
}
true
@ -190,15 +204,19 @@ pub fn smb2_read_response_record(state: &mut SMBState, r: &Smb2Record, nbss_rema
let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE);
let (share_name, mut is_pipe) = match state.ssn2tree_cache.get(&tree_key) {
Some(n) => (n.name.to_vec(), n.is_pipe),
_ => { (Vec::new(), false) },
_ => (Vec::new(), false),
};
let mut is_dcerpc = if is_pipe || share_name.is_empty() {
state.get_service_for_guid(&file_guid).1
} else {
false
};
SCLogDebug!("SMBv2/READ: share_name {:?} is_pipe {} is_dcerpc {}",
share_name, is_pipe, is_dcerpc);
SCLogDebug!(
"SMBv2/READ: share_name {:?} is_pipe {} is_dcerpc {}",
share_name,
is_pipe,
is_dcerpc
);
if share_name.is_empty() && !is_pipe {
SCLogDebug!("SMBv2/READ: no tree connect seen, we don't know if we are a pipe");
@ -209,7 +227,9 @@ pub fn smb2_read_response_record(state: &mut SMBState, r: &Smb2Record, nbss_rema
let tree = SMBTree::new(b"suricata::dcerpc".to_vec(), true);
state.ssn2tree_cache.put(tree_key, tree);
if !is_dcerpc {
_ = state.guid2name_cache.put(file_guid.to_vec(), b"suricata::dcerpc".to_vec());
_ = state
.guid2name_cache
.put(file_guid.to_vec(), b"suricata::dcerpc".to_vec());
}
is_pipe = true;
is_dcerpc = true;
@ -228,30 +248,40 @@ pub fn smb2_read_response_record(state: &mut SMBState, r: &Smb2Record, nbss_rema
state.set_skip(Direction::ToClient, nbss_remaining);
} else {
let file_name = match state.guid2name_cache.get(&file_guid) {
Some(n) => { n.to_vec() }
None => { b"<unknown>".to_vec() }
Some(n) => n.to_vec(),
None => b"<unknown>".to_vec(),
};
let tx = state.new_file_tx(&file_guid, &file_name, Direction::ToClient);
tx.vercmd.set_smb2_cmd(SMB2_COMMAND_READ);
tx.hdr = SMBCommonHdr::new(SMBHDR_TYPE_HEADER,
r.session_id, r.tree_id, 0); // TODO move into new_file_tx
tx.hdr = SMBCommonHdr::new(SMBHDR_TYPE_HEADER, r.session_id, r.tree_id, 0); // TODO move into new_file_tx
if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
tdf.share_name = share_name;
let file_id : u32 = tx.id as u32;
let file_id: u32 = tx.id as u32;
if offset < tdf.file_tracker.tracked {
set_event_fileoverlap = true;
}
if max_queue_size != 0 && tdf.file_tracker.get_inflight_size() + rd.len as u64 > u64::from(max_queue_size) {
if max_queue_size != 0
&& tdf.file_tracker.get_inflight_size() + rd.len as u64
> u64::from(max_queue_size)
{
state.set_event(SMBEvent::ReadQueueSizeExceeded);
state.set_skip(Direction::ToClient, nbss_remaining);
} else if max_queue_cnt != 0 && tdf.file_tracker.get_inflight_cnt() >= max_queue_cnt as usize {
} else if max_queue_cnt != 0
&& tdf.file_tracker.get_inflight_cnt() >= max_queue_cnt as usize
{
state.set_event(SMBEvent::ReadQueueCntExceeded);
state.set_skip(Direction::ToClient, nbss_remaining);
} else {
filetracker_newchunk(&mut tdf.file_tracker,
&file_name, rd.data, offset,
rd.len, false, &file_id);
filetracker_newchunk(
&mut tdf.file_tracker,
&file_name,
rd.data,
offset,
rd.len,
false,
&file_id,
);
}
}
}
@ -260,7 +290,12 @@ pub fn smb2_read_response_record(state: &mut SMBState, r: &Smb2Record, nbss_rema
if set_event_fileoverlap {
state.set_event(SMBEvent::FileOverlap);
}
state.set_file_left(Direction::ToClient, rd.len, rd.data.len() as u32, file_guid.to_vec());
state.set_file_left(
Direction::ToClient,
rd.len,
rd.data.len() as u32,
file_guid.to_vec(),
);
}
_ => {
SCLogDebug!("SMBv2: failed to parse read response");
@ -269,8 +304,7 @@ pub fn smb2_read_response_record(state: &mut SMBState, r: &Smb2Record, nbss_rema
}
}
pub fn smb2_write_request_record(state: &mut SMBState, r: &Smb2Record, nbss_remaining: u32)
{
pub fn smb2_write_request_record(state: &mut SMBState, r: &Smb2Record, nbss_remaining: u32) {
let max_queue_size = unsafe { SMB_CFG_MAX_WRITE_QUEUE_SIZE };
let max_queue_cnt = unsafe { SMB_CFG_MAX_WRITE_QUEUE_CNT };
@ -289,8 +323,9 @@ pub fn smb2_write_request_record(state: &mut SMBState, r: &Smb2Record, nbss_rema
state.set_skip(Direction::ToServer, nbss_remaining);
return;
}
if (state.max_write_size != 0 && wr.wr_len > state.max_write_size) ||
(unsafe { SMB_CFG_MAX_WRITE_SIZE != 0 && SMB_CFG_MAX_WRITE_SIZE < wr.wr_len }) {
if (state.max_write_size != 0 && wr.wr_len > state.max_write_size)
|| (unsafe { SMB_CFG_MAX_WRITE_SIZE != 0 && SMB_CFG_MAX_WRITE_SIZE < wr.wr_len })
{
state.set_event(SMBEvent::WriteRequestTooLarge);
state.set_skip(Direction::ToServer, nbss_remaining);
return;
@ -307,22 +342,35 @@ pub fn smb2_write_request_record(state: &mut SMBState, r: &Smb2Record, nbss_rema
};
let mut set_event_fileoverlap = false;
let found = if let Some(tx) = state.get_file_tx_by_fuid_with_open_file(&file_guid, Direction::ToServer) {
let found = if let Some(tx) =
state.get_file_tx_by_fuid_with_open_file(&file_guid, Direction::ToServer)
{
if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
let file_id : u32 = tx.id as u32;
let file_id: u32 = tx.id as u32;
if wr.wr_offset < tdf.file_tracker.tracked {
set_event_fileoverlap = true;
}
if max_queue_size != 0 && tdf.file_tracker.get_inflight_size() + wr.wr_len as u64 > u64::from(max_queue_size) {
if max_queue_size != 0
&& tdf.file_tracker.get_inflight_size() + wr.wr_len as u64
> u64::from(max_queue_size)
{
state.set_event(SMBEvent::WriteQueueSizeExceeded);
state.set_skip(Direction::ToServer, nbss_remaining);
} else if max_queue_cnt != 0 && tdf.file_tracker.get_inflight_cnt() >= max_queue_cnt as usize {
} else if max_queue_cnt != 0
&& tdf.file_tracker.get_inflight_cnt() >= max_queue_cnt as usize
{
state.set_event(SMBEvent::WriteQueueCntExceeded);
state.set_skip(Direction::ToServer, nbss_remaining);
} else {
filetracker_newchunk(&mut tdf.file_tracker,
&file_name, wr.data, wr.wr_offset,
wr.wr_len, false, &file_id);
filetracker_newchunk(
&mut tdf.file_tracker,
&file_name,
wr.data,
wr.wr_offset,
wr.wr_len,
false,
&file_id,
);
}
}
true
@ -332,20 +380,26 @@ pub fn smb2_write_request_record(state: &mut SMBState, r: &Smb2Record, nbss_rema
if !found {
let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE);
let (share_name, mut is_pipe) = match state.ssn2tree_cache.get(&tree_key) {
Some(n) => { (n.name.to_vec(), n.is_pipe) },
_ => { (Vec::new(), false) },
Some(n) => (n.name.to_vec(), n.is_pipe),
_ => (Vec::new(), false),
};
let mut is_dcerpc = if is_pipe || share_name.is_empty() {
state.get_service_for_guid(wr.guid).1
} else {
false
};
SCLogDebug!("SMBv2/WRITE: share_name {:?} is_pipe {} is_dcerpc {}",
share_name, is_pipe, is_dcerpc);
SCLogDebug!(
"SMBv2/WRITE: share_name {:?} is_pipe {} is_dcerpc {}",
share_name,
is_pipe,
is_dcerpc
);
// if we missed the TREE connect we can't be sure if 'is_dcerpc' is correct
if share_name.is_empty() && !is_pipe {
SCLogDebug!("SMBv2/WRITE: no tree connect seen, we don't know if we are a pipe");
SCLogDebug!(
"SMBv2/WRITE: no tree connect seen, we don't know if we are a pipe"
);
if smb_dcerpc_probe(wr.data) {
SCLogDebug!("SMBv2/WRITE: looks like we have dcerpc");
@ -353,8 +407,9 @@ pub fn smb2_write_request_record(state: &mut SMBState, r: &Smb2Record, nbss_rema
let tree = SMBTree::new(b"suricata::dcerpc".to_vec(), true);
state.ssn2tree_cache.put(tree_key, tree);
if !is_dcerpc {
_ = state.guid2name_cache.put(file_guid.to_vec(),
b"suricata::dcerpc".to_vec());
_ = state
.guid2name_cache
.put(file_guid.to_vec(), b"suricata::dcerpc".to_vec());
}
is_pipe = true;
is_dcerpc = true;
@ -373,24 +428,34 @@ pub fn smb2_write_request_record(state: &mut SMBState, r: &Smb2Record, nbss_rema
} else {
let tx = state.new_file_tx(&file_guid, &file_name, Direction::ToServer);
tx.vercmd.set_smb2_cmd(SMB2_COMMAND_WRITE);
tx.hdr = SMBCommonHdr::new(SMBHDR_TYPE_HEADER,
r.session_id, r.tree_id, 0); // TODO move into new_file_tx
tx.hdr = SMBCommonHdr::new(SMBHDR_TYPE_HEADER, r.session_id, r.tree_id, 0); // TODO move into new_file_tx
if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
let file_id : u32 = tx.id as u32;
let file_id: u32 = tx.id as u32;
if wr.wr_offset < tdf.file_tracker.tracked {
set_event_fileoverlap = true;
}
if max_queue_size != 0 && tdf.file_tracker.get_inflight_size() + wr.wr_len as u64 > u64::from(max_queue_size) {
if max_queue_size != 0
&& tdf.file_tracker.get_inflight_size() + wr.wr_len as u64
> u64::from(max_queue_size)
{
state.set_event(SMBEvent::WriteQueueSizeExceeded);
state.set_skip(Direction::ToServer, nbss_remaining);
} else if max_queue_cnt != 0 && tdf.file_tracker.get_inflight_cnt() >= max_queue_cnt as usize {
} else if max_queue_cnt != 0
&& tdf.file_tracker.get_inflight_cnt() >= max_queue_cnt as usize
{
state.set_event(SMBEvent::WriteQueueCntExceeded);
state.set_skip(Direction::ToServer, nbss_remaining);
} else {
filetracker_newchunk(&mut tdf.file_tracker,
&file_name, wr.data, wr.wr_offset,
wr.wr_len, false, &file_id);
filetracker_newchunk(
&mut tdf.file_tracker,
&file_name,
wr.data,
wr.wr_offset,
wr.wr_len,
false,
&file_id,
);
}
}
}
@ -399,20 +464,28 @@ pub fn smb2_write_request_record(state: &mut SMBState, r: &Smb2Record, nbss_rema
if set_event_fileoverlap {
state.set_event(SMBEvent::FileOverlap);
}
state.set_file_left(Direction::ToServer, wr.wr_len, wr.data.len() as u32, file_guid.to_vec());
},
state.set_file_left(
Direction::ToServer,
wr.wr_len,
wr.data.len() as u32,
file_guid.to_vec(),
);
}
_ => {
state.set_event(SMBEvent::MalformedData);
},
}
}
}
pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record)
{
SCLogDebug!("SMBv2 request record, command {} tree {} session {}",
&smb2_command_string(r.command), r.tree_id, r.session_id);
pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record) {
SCLogDebug!(
"SMBv2 request record, command {} tree {} session {}",
&smb2_command_string(r.command),
r.tree_id,
r.session_id
);
let mut events : Vec<SMBEvent> = Vec::new();
let mut events: Vec<SMBEvent> = Vec::new();
let have_tx = match r.command {
SMB2_COMMAND_SET_INFO => {
@ -425,10 +498,10 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record)
Smb2SetInfoRequestData::RENAME(ref ren) => {
let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
let mut newname = ren.name.to_vec();
newname.retain(|&i|i != 0x00);
newname.retain(|&i| i != 0x00);
let oldname = match state.guid2name_cache.get(rd.guid) {
Some(n) => { n.to_vec() },
None => { b"<unknown>".to_vec() },
Some(n) => n.to_vec(),
None => b"<unknown>".to_vec(),
};
let tx = state.new_rename_tx(rd.guid.to_vec(), oldname, newname);
tx.hdr = tx_hdr;
@ -439,22 +512,29 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record)
Smb2SetInfoRequestData::DISPOSITION(ref dis) => {
let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
let fname = match state.guid2name_cache.get(rd.guid) {
Some(n) => { n.to_vec() },
Some(n) => n.to_vec(),
None => {
// try to find latest created file in case of chained commands
let mut guid_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_FILENAME);
let mut guid_key =
SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_FILENAME);
if guid_key.msg_id == 0 {
b"<unknown>".to_vec()
} else {
guid_key.msg_id -= 1;
match state.ssn2vec_cache.get(&guid_key) {
Some(n) => { n.to_vec() },
None => { b"<unknown>".to_vec()},
Some(n) => n.to_vec(),
None => b"<unknown>".to_vec(),
}
}
},
}
};
let tx = state.new_setfileinfo_tx(fname, rd.guid.to_vec(), rd.class as u16, rd.infolvl as u16, dis.delete);
let tx = state.new_setfileinfo_tx(
fname,
rd.guid.to_vec(),
rd.class as u16,
rd.infolvl as u16,
dis.delete,
);
tx.hdr = tx_hdr;
tx.request_done = true;
tx.vercmd.set_smb2_cmd(SMB2_COMMAND_SET_INFO);
@ -462,25 +542,24 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record)
}
_ => false,
}
},
}
Err(Err::Incomplete(_n)) => {
SCLogDebug!("SMB2_COMMAND_SET_INFO: {:?}", _n);
events.push(SMBEvent::MalformedData);
false
},
Err(Err::Error(_e)) |
Err(Err::Failure(_e)) => {
}
Err(Err::Error(_e)) | Err(Err::Failure(_e)) => {
SCLogDebug!("SMB2_COMMAND_SET_INFO: {:?}", _e);
events.push(SMBEvent::MalformedData);
false
},
}
};
have_si_tx
},
}
SMB2_COMMAND_IOCTL => {
smb2_ioctl_request_record(state, r);
true
},
}
SMB2_COMMAND_TREE_DISCONNECT => {
let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE);
state.ssn2tree_cache.pop(&tree_key);
@ -488,7 +567,7 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record)
}
SMB2_COMMAND_NEGOTIATE_PROTOCOL => {
if let Ok((_, rd)) = parse_smb2_request_negotiate_protocol(r.data) {
let mut dialects : Vec<Vec<u8>> = Vec::new();
let mut dialects: Vec<Vec<u8>> = Vec::new();
for d in rd.dialects_vec {
SCLogDebug!("dialect {:x} => {}", d, &smb2_dialect_string(d));
let dvec = smb2_dialect_string(d).as_bytes().to_vec();
@ -508,16 +587,16 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record)
events.push(SMBEvent::MalformedData);
false
}
},
}
SMB2_COMMAND_SESSION_SETUP => {
smb2_session_setup_request(state, r);
true
},
}
SMB2_COMMAND_TREE_CONNECT => {
if let Ok((_, tr)) = parse_smb2_request_tree_connect(r.data) {
let name_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_TREE);
let mut name_val = tr.share_name.to_vec();
name_val.retain(|&i|i != 0x00);
name_val.retain(|&i| i != 0x00);
if name_val.len() > 1 {
name_val = name_val[1..].to_vec();
}
@ -530,26 +609,31 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record)
events.push(SMBEvent::MalformedData);
false
}
},
}
SMB2_COMMAND_READ => {
if let Ok((_, rd)) = parse_smb2_request_read(r.data) {
if (state.max_read_size != 0 && rd.rd_len > state.max_read_size) ||
(unsafe { SMB_CFG_MAX_READ_SIZE != 0 && SMB_CFG_MAX_READ_SIZE < rd.rd_len }) {
events.push(SMBEvent::ReadRequestTooLarge);
} else {
SCLogDebug!("SMBv2 READ: GUID {:?} requesting {} bytes at offset {}",
rd.guid, rd.rd_len, rd.rd_offset);
// store read guid,offset in map
let guid_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_OFFSET);
let guidoff = SMBFileGUIDOffset::new(rd.guid.to_vec(), rd.rd_offset);
state.read_offset_cache.put(guid_key, guidoff);
if (state.max_read_size != 0 && rd.rd_len > state.max_read_size)
|| (unsafe { SMB_CFG_MAX_READ_SIZE != 0 && SMB_CFG_MAX_READ_SIZE < rd.rd_len })
{
events.push(SMBEvent::ReadRequestTooLarge);
} else {
SCLogDebug!(
"SMBv2 READ: GUID {:?} requesting {} bytes at offset {}",
rd.guid,
rd.rd_len,
rd.rd_offset
);
// store read guid,offset in map
let guid_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_OFFSET);
let guidoff = SMBFileGUIDOffset::new(rd.guid.to_vec(), rd.rd_offset);
state.read_offset_cache.put(guid_key, guidoff);
}
} else {
events.push(SMBEvent::MalformedData);
}
false
},
}
SMB2_COMMAND_CREATE => {
if let Ok((_, cr)) = parse_smb2_request_create(r.data) {
let del = cr.create_options & 0x0000_1000 != 0;
@ -568,41 +652,43 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record)
events.push(SMBEvent::MalformedData);
false
}
},
}
SMB2_COMMAND_WRITE => {
smb2_write_request_record(state, r, 0);
true // write handling creates both file tx and generic tx
},
}
SMB2_COMMAND_CLOSE => {
if let Ok((_, cd)) = parse_smb2_request_close(r.data) {
let _name = state.guid2name_cache.pop(cd.guid);
let found_ts = if let Some(tx) = state.get_file_tx_by_fuid(cd.guid, Direction::ToServer) {
if !tx.request_done {
if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
filetracker_close(&mut tdf.file_tracker);
let found_ts =
if let Some(tx) = state.get_file_tx_by_fuid(cd.guid, Direction::ToServer) {
if !tx.request_done {
if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
filetracker_close(&mut tdf.file_tracker);
}
}
}
tx.request_done = true;
tx.response_done = true;
tx.set_status(SMB_NTSTATUS_SUCCESS, false);
true
} else {
false
};
let found_tc = if let Some(tx) = state.get_file_tx_by_fuid(cd.guid, Direction::ToClient) {
if !tx.request_done {
if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
filetracker_close(&mut tdf.file_tracker);
tx.request_done = true;
tx.response_done = true;
tx.set_status(SMB_NTSTATUS_SUCCESS, false);
true
} else {
false
};
let found_tc =
if let Some(tx) = state.get_file_tx_by_fuid(cd.guid, Direction::ToClient) {
if !tx.request_done {
if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
filetracker_close(&mut tdf.file_tracker);
}
}
}
tx.request_done = true;
tx.response_done = true;
tx.set_status(SMB_NTSTATUS_SUCCESS, false);
true
} else {
false
};
tx.request_done = true;
tx.response_done = true;
tx.set_status(SMB_NTSTATUS_SUCCESS, false);
true
} else {
false
};
if !found_ts && !found_tc {
SCLogDebug!("SMBv2: CLOSE(TS): no TX at GUID {:?}", cd.guid);
}
@ -610,46 +696,54 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record)
events.push(SMBEvent::MalformedData);
}
false
},
_ => {
false
},
}
_ => false,
};
/* if we don't have a tx, create it here (maybe) */
if !have_tx && smb2_create_new_tx(r.command) {
let tx_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
let tx = state.new_generic_tx(2, r.command, tx_key);
SCLogDebug!("TS TX {} command {} created with session_id {} tree_id {} message_id {}",
tx.id, r.command, r.session_id, r.tree_id, r.message_id);
SCLogDebug!(
"TS TX {} command {} created with session_id {} tree_id {} message_id {}",
tx.id,
r.command,
r.session_id,
r.tree_id,
r.message_id
);
tx.set_events(events);
}
}
pub fn smb2_response_record(state: &mut SMBState, r: &Smb2Record)
{
SCLogDebug!("SMBv2 response record, command {} status {} tree {} session {} message {}",
&smb2_command_string(r.command), r.nt_status,
r.tree_id, r.session_id, r.message_id);
pub fn smb2_response_record(state: &mut SMBState, r: &Smb2Record) {
SCLogDebug!(
"SMBv2 response record, command {} status {} tree {} session {} message {}",
&smb2_command_string(r.command),
r.nt_status,
r.tree_id,
r.session_id,
r.message_id
);
let mut events : Vec<SMBEvent> = Vec::new();
let mut events: Vec<SMBEvent> = Vec::new();
let have_tx = match r.command {
SMB2_COMMAND_IOCTL => {
smb2_ioctl_response_record(state, r);
true
},
}
SMB2_COMMAND_SESSION_SETUP => {
smb2_session_setup_response(state, r);
true
},
}
SMB2_COMMAND_WRITE => {
if r.nt_status == SMB_NTSTATUS_SUCCESS {
if let Ok((_, _wr)) = parse_smb2_response_write(r.data) {
SCLogDebug!("SMBv2: Write response => {:?}", _wr);
/* search key-guid map */
let guid_key = SMBCommonHdr::new(SMBHDR_TYPE_GUID,
r.session_id, r.tree_id, r.message_id);
let guid_key =
SMBCommonHdr::new(SMBHDR_TYPE_GUID, r.session_id, r.tree_id, r.message_id);
let _guid_vec = state.ssn2vec_cache.pop(&guid_key).unwrap_or_default();
SCLogDebug!("SMBv2 write response for GUID {:?}", _guid_vec);
} else {
@ -657,10 +751,9 @@ pub fn smb2_response_record(state: &mut SMBState, r: &Smb2Record)
}
}
false // the request may have created a generic tx, so handle that here
},
}
SMB2_COMMAND_READ => {
if r.nt_status == SMB_NTSTATUS_SUCCESS ||
r.nt_status == SMB_NTSTATUS_BUFFER_OVERFLOW {
if r.nt_status == SMB_NTSTATUS_SUCCESS || r.nt_status == SMB_NTSTATUS_BUFFER_OVERFLOW {
smb2_read_response_record(state, r, 0);
} else if r.nt_status == SMB_NTSTATUS_END_OF_FILE {
SCLogDebug!("SMBv2: read response => EOF");
@ -685,7 +778,7 @@ pub fn smb2_response_record(state: &mut SMBState, r: &Smb2Record)
SCLogDebug!("SMBv2 READ: status {}", r.nt_status);
}
false
},
}
SMB2_COMMAND_CREATE => {
if r.nt_status == SMB_NTSTATUS_SUCCESS {
if let Ok((_, cr)) = parse_smb2_response_create(r.data) {
@ -693,7 +786,7 @@ pub fn smb2_response_record(state: &mut SMBState, r: &Smb2Record)
let guid_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_FILENAME);
if let Some(mut p) = state.ssn2vec_cache.pop(&guid_key) {
p.retain(|&i|i != 0x00);
p.retain(|&i| i != 0x00);
_ = state.guid2name_cache.put(cr.guid.to_vec(), p);
} else {
SCLogDebug!("SMBv2 response: GUID NOT FOUND");
@ -701,8 +794,12 @@ pub fn smb2_response_record(state: &mut SMBState, r: &Smb2Record)
let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
if let Some(tx) = state.get_generic_tx(2, r.command, &tx_hdr) {
SCLogDebug!("tx {} with {}/{} marked as done",
tx.id, r.command, &smb2_command_string(r.command));
SCLogDebug!(
"tx {} with {}/{} marked as done",
tx.id,
r.command,
&smb2_command_string(r.command)
);
tx.set_status(r.nt_status, false);
tx.response_done = true;
@ -722,7 +819,7 @@ pub fn smb2_response_record(state: &mut SMBState, r: &Smb2Record)
} else {
false
}
},
}
SMB2_COMMAND_TREE_DISCONNECT => {
// normally removed when processing request,
// but in case we missed that try again here
@ -738,7 +835,9 @@ pub fn smb2_response_record(state: &mut SMBState, r: &Smb2Record)
let is_pipe = tr.share_type == 2;
let found = match state.get_treeconnect_tx(name_key) {
Some(tx) => {
if let Some(SMBTransactionTypeData::TREECONNECT(ref mut tdn)) = tx.type_data {
if let Some(SMBTransactionTypeData::TREECONNECT(ref mut tdn)) =
tx.type_data
{
tdn.share_type = tr.share_type;
tdn.is_pipe = is_pipe;
tdn.tree_id = r.tree_id;
@ -749,8 +848,8 @@ pub fn smb2_response_record(state: &mut SMBState, r: &Smb2Record)
tx.response_done = true;
tx.set_status(r.nt_status, false);
true
},
None => { false },
}
None => false,
};
if found {
let tree = SMBTree::new(share_name.to_vec(), is_pipe);
@ -772,7 +871,7 @@ pub fn smb2_response_record(state: &mut SMBState, r: &Smb2Record)
false
}
}
},
}
SMB2_COMMAND_NEGOTIATE_PROTOCOL => {
let res = if r.nt_status == SMB_NTSTATUS_SUCCESS {
parse_smb2_response_negotiate_protocol(r.data)
@ -803,52 +902,63 @@ pub fn smb2_response_record(state: &mut SMBState, r: &Smb2Record)
tx.set_status(r.nt_status, false);
tx.response_done = true;
true
},
None => { false },
}
None => false,
};
// SMB2 response to SMB1 request?
let found1 = !found2 && match state.get_negotiate_tx(1) {
Some(tx) => {
if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data {
tdn.server_guid = rd.server_guid.to_vec();
let found1 = !found2
&& match state.get_negotiate_tx(1) {
Some(tx) => {
if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) =
tx.type_data
{
tdn.server_guid = rd.server_guid.to_vec();
}
tx.set_status(r.nt_status, false);
tx.response_done = true;
true
}
tx.set_status(r.nt_status, false);
tx.response_done = true;
true
},
None => { false },
};
None => false,
};
found1 || found2
} else {
events.push(SMBEvent::MalformedData);
false
}
},
}
_ => {
SCLogDebug!("default case: no TX");
false
},
}
};
if !have_tx {
let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
SCLogDebug!("looking for TX {} with session_id {} tree_id {} message_id {}",
&smb2_command_string(r.command),
r.session_id, r.tree_id, r.message_id);
SCLogDebug!(
"looking for TX {} with session_id {} tree_id {} message_id {}",
&smb2_command_string(r.command),
r.session_id,
r.tree_id,
r.message_id
);
let _found = match state.get_generic_tx(2, r.command, &tx_hdr) {
Some(tx) => {
SCLogDebug!("tx {} with {}/{} marked as done",
tx.id, r.command, &smb2_command_string(r.command));
SCLogDebug!(
"tx {} with {}/{} marked as done",
tx.id,
r.command,
&smb2_command_string(r.command)
);
if r.nt_status != SMB_NTSTATUS_PENDING {
tx.response_done = true;
}
tx.set_status(r.nt_status, false);
tx.set_events(events);
true
},
}
_ => {
SCLogDebug!("no tx found for {:?}", r);
false
},
}
};
}
}

@ -15,13 +15,13 @@
* 02110-1301, USA.
*/
use crate::smb::smb::*;
use crate::smb::smb2::*;
use crate::smb::smb2_records::*;
use crate::smb::dcerpc::*;
use crate::smb::events::*;
#[cfg(feature = "debug")]
use crate::smb::funcs::*;
use crate::smb::smb::*;
use crate::smb::smb2::*;
use crate::smb::smb2_records::*;
use crate::smb::smb_status::*;
#[derive(Debug)]
@ -31,25 +31,26 @@ pub struct SMBTransactionIoctl {
impl SMBTransactionIoctl {
pub fn new(func: u32) -> Self {
return Self {
func,
};
return Self { func };
}
}
impl SMBState {
pub fn new_ioctl_tx(&mut self, hdr: SMBCommonHdr, func: u32)
-> &mut SMBTransaction
{
pub fn new_ioctl_tx(&mut self, hdr: SMBCommonHdr, func: u32) -> &mut SMBTransaction {
let mut tx = self.new_tx();
tx.hdr = hdr;
tx.type_data = Some(SMBTransactionTypeData::IOCTL(
SMBTransactionIoctl::new(func)));
tx.type_data = Some(SMBTransactionTypeData::IOCTL(SMBTransactionIoctl::new(
func,
)));
tx.request_done = true;
tx.response_done = self.tc_trunc; // no response expected if tc is truncated
SCLogDebug!("SMB: TX IOCTL created: ID {} FUNC {:08x}: {}",
tx.id, func, &fsctl_func_to_string(func));
SCLogDebug!(
"SMB: TX IOCTL created: ID {} FUNC {:08x}: {}",
tx.id,
func,
&fsctl_func_to_string(func)
);
self.transactions.push_back(tx);
let tx_ref = self.transactions.back_mut();
return tx_ref.unwrap();
@ -57,8 +58,7 @@ impl SMBState {
}
// IOCTL responses ASYNC don't set the tree id
pub fn smb2_ioctl_request_record(state: &mut SMBState, r: &Smb2Record)
{
pub fn smb2_ioctl_request_record(state: &mut SMBState, r: &Smb2Record) {
let hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER);
match parse_smb2_request_ioctl(r.data) {
Ok((_, rd)) => {
@ -73,21 +73,24 @@ pub fn smb2_ioctl_request_record(state: &mut SMBState, r: &Smb2Record)
let vercmd = SMBVerCmdStat::new2(SMB2_COMMAND_IOCTL);
smb_write_dcerpc_record(state, vercmd, hdr, rd.data);
} else {
SCLogDebug!("IOCTL {:08x} {}", rd.function, &fsctl_func_to_string(rd.function));
SCLogDebug!(
"IOCTL {:08x} {}",
rd.function,
&fsctl_func_to_string(rd.function)
);
let tx = state.new_ioctl_tx(hdr, rd.function);
tx.vercmd.set_smb2_cmd(SMB2_COMMAND_IOCTL);
}
},
}
_ => {
let tx = state.new_generic_tx(2, r.command, hdr);
tx.set_event(SMBEvent::MalformedData);
},
}
};
}
// IOCTL responses ASYNC don't set the tree id
pub fn smb2_ioctl_response_record(state: &mut SMBState, r: &Smb2Record)
{
pub fn smb2_ioctl_response_record(state: &mut SMBState, r: &Smb2Record) {
let hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER);
match parse_smb2_response_ioctl(r.data) {
Ok((_, rd)) => {
@ -104,9 +107,12 @@ pub fn smb2_ioctl_response_record(state: &mut SMBState, r: &Smb2Record)
SCLogDebug!("IOCTL response data is_pipe. Calling smb_read_dcerpc_record");
let vercmd = SMBVerCmdStat::new2_with_ntstatus(SMB2_COMMAND_IOCTL, r.nt_status);
SCLogDebug!("TODO passing empty GUID");
smb_read_dcerpc_record(state, vercmd, hdr, &[],rd.data);
smb_read_dcerpc_record(state, vercmd, hdr, &[], rd.data);
} else {
SCLogDebug!("SMB2_COMMAND_IOCTL/SMB_NTSTATUS_PENDING looking for {:?}", hdr);
SCLogDebug!(
"SMB2_COMMAND_IOCTL/SMB_NTSTATUS_PENDING looking for {:?}",
hdr
);
if let Some(tx) = state.get_generic_tx(2, SMB2_COMMAND_IOCTL, &hdr) {
tx.set_status(r.nt_status, false);
if r.nt_status != SMB_NTSTATUS_PENDING {
@ -114,22 +120,25 @@ pub fn smb2_ioctl_response_record(state: &mut SMBState, r: &Smb2Record)
}
}
}
},
}
_ => {
SCLogDebug!("SMB2_COMMAND_IOCTL/SMB_NTSTATUS_PENDING looking for {:?}", hdr);
SCLogDebug!(
"SMB2_COMMAND_IOCTL/SMB_NTSTATUS_PENDING looking for {:?}",
hdr
);
if let Some(tx) = state.get_generic_tx(2, SMB2_COMMAND_IOCTL, &hdr) {
SCLogDebug!("updated status of tx {}", tx.id);
tx.set_status(r.nt_status, false);
if r.nt_status != SMB_NTSTATUS_PENDING {
tx.response_done = true;
}
// parsing failed for 'SUCCESS' record, set event
if r.nt_status == SMB_NTSTATUS_SUCCESS {
SCLogDebug!("parse fail {:?}", r);
tx.set_event(SMBEvent::MalformedData);
}
}
},
}
};
}

@ -201,7 +201,9 @@ pub struct Smb2SessionSetupRequestRecord<'a> {
pub data: &'a [u8],
}
pub fn parse_smb2_request_session_setup(i: &[u8]) -> IResult<&[u8], Smb2SessionSetupRequestRecord<'_>> {
pub fn parse_smb2_request_session_setup(
i: &[u8],
) -> IResult<&[u8], Smb2SessionSetupRequestRecord<'_>> {
let (i, _struct_size) = take(2_usize).parse(i)?;
let (i, _flags) = le_u8.parse(i)?;
let (i, _security_mode) = le_u8.parse(i)?;
@ -220,7 +222,9 @@ pub struct Smb2TreeConnectRequestRecord<'a> {
pub share_name: &'a [u8],
}
pub fn parse_smb2_request_tree_connect(i: &[u8]) -> IResult<&[u8], Smb2TreeConnectRequestRecord<'_>> {
pub fn parse_smb2_request_tree_connect(
i: &[u8],
) -> IResult<&[u8], Smb2TreeConnectRequestRecord<'_>> {
let (i, _struct_size) = take(2_usize).parse(i)?;
let (i, _offset_length) = take(4_usize).parse(i)?;
let (i, data) = rest.parse(i)?;
@ -363,7 +367,9 @@ pub struct Smb2SetInfoRequestDispoRecord {
pub delete: bool,
}
pub fn parse_smb2_request_setinfo_disposition(i: &[u8]) -> IResult<&[u8], Smb2SetInfoRequestData<'_>> {
pub fn parse_smb2_request_setinfo_disposition(
i: &[u8],
) -> IResult<&[u8], Smb2SetInfoRequestData<'_>> {
let (i, info) = le_u8.parse(i)?;
let record = Smb2SetInfoRequestData::DISPOSITION(Smb2SetInfoRequestDispoRecord {
delete: info & 1 != 0,
@ -415,7 +421,8 @@ pub fn parse_smb2_request_setinfo(i: &[u8]) -> IResult<&[u8], Smb2SetInfoRequest
let (i, guid) = take(16_usize).parse(i)?;
let (i, data) = map_parser(take(setinfo_size), |b| {
parse_smb2_request_setinfo_data(b, class, infolvl)
}).parse(i)?;
})
.parse(i)?;
let record = Smb2SetInfoRequestRecord {
guid,
class,

@ -15,13 +15,12 @@
* 02110-1301, USA.
*/
use crate::smb::smb2_records::*;
use crate::smb::smb::*;
use crate::smb::events::*;
use crate::smb::auth::*;
use crate::smb::events::*;
use crate::smb::smb::*;
use crate::smb::smb2_records::*;
pub fn smb2_session_setup_request(state: &mut SMBState, r: &Smb2Record)
{
pub fn smb2_session_setup_request(state: &mut SMBState, r: &Smb2Record) {
SCLogDebug!("SMB2_COMMAND_SESSION_SETUP: r.data.len() {}", r.data.len());
#[allow(clippy::single_match)]
match parse_smb2_request_session_setup(r.data) {
@ -41,37 +40,34 @@ pub fn smb2_session_setup_request(state: &mut SMBState, r: &Smb2Record)
}
}
}
},
_ => {
// events.push(SMBEvent::MalformedData);
},
}
_ => {
// events.push(SMBEvent::MalformedData);
}
}
}
fn smb2_session_setup_update_tx(tx: &mut SMBTransaction, r: &Smb2Record)
{
fn smb2_session_setup_update_tx(tx: &mut SMBTransaction, r: &Smb2Record) {
tx.hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER); // to overwrite ssn_id 0
tx.set_status(r.nt_status, false);
tx.response_done = true;
}
pub fn smb2_session_setup_response(state: &mut SMBState, r: &Smb2Record)
{
pub fn smb2_session_setup_response(state: &mut SMBState, r: &Smb2Record) {
// try exact match with session id already set (e.g. NTLMSSP AUTH phase)
let found = r.session_id != 0 && match state.get_sessionsetup_tx(
SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER))
{
Some(tx) => {
smb2_session_setup_update_tx(tx, r);
SCLogDebug!("smb2_session_setup_response: tx {:?}", tx);
true
},
None => { false },
};
let found = r.session_id != 0
&& match state.get_sessionsetup_tx(SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER)) {
Some(tx) => {
smb2_session_setup_update_tx(tx, r);
SCLogDebug!("smb2_session_setup_response: tx {:?}", tx);
true
}
None => false,
};
// otherwise try match with ssn id 0 (e.g. NTLMSSP_NEGOTIATE)
if !found {
if let Some(tx) = state.get_sessionsetup_tx(
SMBCommonHdr::new(SMBHDR_TYPE_HEADER, 0, 0, r.message_id))
if let Some(tx) =
state.get_sessionsetup_tx(SMBCommonHdr::new(SMBHDR_TYPE_HEADER, 0, 0, r.message_id))
{
smb2_session_setup_update_tx(tx, r);
SCLogDebug!("smb2_session_setup_response: tx {:?}", tx);

@ -17,14 +17,14 @@
use nom8::bytes::streaming::{tag, take};
use nom8::number::streaming::{le_u16, le_u32, le_u64};
use nom8::Parser;
use nom8::IResult;
use nom8::Parser;
#[derive(Debug,PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq)]
pub struct Smb3TransformRecord<'a> {
pub session_id: u64,
pub enc_algo: u16,
pub enc_data: &'a[u8],
pub enc_data: &'a [u8],
}
pub fn parse_smb3_transform_record(i: &[u8]) -> IResult<&[u8], Smb3TransformRecord<'_>> {
@ -47,8 +47,8 @@ pub fn parse_smb3_transform_record(i: &[u8]) -> IResult<&[u8], Smb3TransformReco
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_smb3_transform_record() {
#[test]
fn test_parse_smb3_transform_record() {
// https://raw.githubusercontent.com/bro/bro/master/testing/btest/Traces/smb/smb3.pcap
let data = hex::decode("fd534d42188d39cea4b1e3f640aff5d0b1569852c0bd665516dbb4b499507f000000000069000000000001003d00009400480000d9f8a66572b40c621bea6f5922a412a8eb2e3cc2af9ce26a277e75898cb523b9eb49ef660a6a1a09368fadd6a58e893e08eb3b7c068bdb74b6cd38e9ed1a2559cefb2ebc2172fd86c08a1a636eb851f20bf53a242f4cfaf7ab44e77291073ad492d6297c3d3a67757c").unwrap();
let result = parse_smb3_transform_record(&data).unwrap();

@ -23,22 +23,21 @@ use nom8::{Err, IResult};
/// parse a UTF16 string that is null terminated. Normally by 2 null
/// bytes, but at the end of the data it can also be a single null.
/// Skip every second byte.
pub fn smb_get_unicode_string(blob: &[u8]) -> IResult<&[u8], Vec<u8>, SmbError>
{
pub fn smb_get_unicode_string(blob: &[u8]) -> IResult<&[u8], Vec<u8>, SmbError> {
SCLogDebug!("get_unicode_string: blob {} {:?}", blob.len(), blob);
let mut name : Vec<u8> = Vec::new();
let mut name: Vec<u8> = Vec::new();
let mut c = blob;
while !c.is_empty() {
if c.len() == 1 && c[0] == 0 {
let rem = &c[1..];
SCLogDebug!("get_unicode_string: name {:?}", name);
return Ok((rem, name))
return Ok((rem, name));
} else if c.len() == 1 {
break;
} else if c[0] == 0 && c[1] == 0 {
let rem = &c[2..];
SCLogDebug!("get_unicode_string: name {:?}", name);
return Ok((rem, name))
return Ok((rem, name));
}
name.push(c[0]);
c = &c[2..];
@ -51,4 +50,3 @@ pub fn smb_get_ascii_string(i: &[u8]) -> IResult<&[u8], Vec<u8>, SmbError> {
let (i, s) = take_until_and_consume(b"\x00").parse(i)?;
Ok((i, s.to_vec()))
}

@ -42,3 +42,6 @@ rustfmt --check rust/src/dns/*.rs rust/src/applayertemplate/*.rs rust/src/asn1/*
rust/src/nfs/*.rs rust/src/pgsql/*.rs rust/src/rdp/*.rs rust/src/sdp/*.rs \
rust/src/sip/*.rs rust/src/telnet/*.rs rust/src/tftp/*.rs rust/src/x509/*.rs \
rust/src/snmp/*.rs rust/src/llmnr/*.rs rust/src/detect/*.rs rust/src/dcerpc/*.rs
# do not do *.rs so as not to rustfmt smb_status.rs which skips formatting in mod.rs
rustfmt --check rust/src/smb/mod.rs

Loading…
Cancel
Save