diff --git a/rust/src/krb/krb5.rs b/rust/src/krb/krb5.rs index faeacaed84..79f160f29c 100644 --- a/rust/src/krb/krb5.rs +++ b/rust/src/krb/krb5.rs @@ -17,21 +17,23 @@ // written by Pierre Chifflier -use std; -use std::ffi::CString; -use nom7::{Err, IResult}; -use nom7::number::streaming::be_u32; -use der_parser::der::der_read_element_header; -use der_parser::ber::Class; -use kerberos_parser::krb5_parser; -use kerberos_parser::krb5::{EncryptionType,ErrorCode,MessageType,PrincipalName,Realm,KrbError}; -use asn1_rs::FromDer; -use suricata_sys::sys::AppProto; use crate::applayer::{self, *}; use crate::core; -use crate::core::{ALPROTO_FAILED,ALPROTO_UNKNOWN, IPPROTO_TCP, IPPROTO_UDP}; +use crate::core::{ALPROTO_FAILED, ALPROTO_UNKNOWN, IPPROTO_TCP, IPPROTO_UDP}; use crate::direction::Direction; use crate::flow::Flow; +use asn1_rs::FromDer; +use der_parser::ber::Class; +use der_parser::der::der_read_element_header; +use kerberos_parser::krb5::{ + EncryptionType, ErrorCode, KrbError, MessageType, PrincipalName, Realm, +}; +use kerberos_parser::krb5_parser; +use nom7::number::streaming::be_u32; +use nom7::{Err, IResult}; +use std; +use std::ffi::CString; +use suricata_sys::sys::AppProto; #[derive(AppLayerEvent)] pub enum KRB5Event { @@ -126,13 +128,15 @@ impl KRB5State { /// Returns 0 in case of success, or -1 on error fn parse(&mut self, i: &[u8], direction: Direction) -> i32 { match der_read_element_header(i) { - Ok((_rem,hdr)) => { + Ok((_rem, hdr)) => { // Kerberos messages start with an APPLICATION header - if hdr.class() != Class::Application { return 0; } + if hdr.class() != Class::Application { + return 0; + } match hdr.tag().0 { 10 => { let req = krb5_parser::parse_as_req(i); - if let Ok((_,kdc_req)) = req { + if let Ok((_, kdc_req)) = req { let mut tx = self.new_tx(direction); tx.msg_type = MessageType::KRB_AS_REQ; tx.cname = kdc_req.req_body.cname; @@ -142,10 +146,10 @@ impl KRB5State { self.transactions.push(tx); }; self.req_id = 10; - }, + } 11 => { let res = krb5_parser::parse_as_rep(i); - if let Ok((_,kdc_rep)) = res { + if let Ok((_, kdc_rep)) = res { let mut tx = self.new_tx(direction); tx.msg_type = MessageType::KRB_AS_REP; if self.req_id > 0 { @@ -164,10 +168,10 @@ impl KRB5State { } }; self.req_id = 0; - }, + } 12 => { let req = krb5_parser::parse_tgs_req(i); - if let Ok((_,kdc_req)) = req { + if let Ok((_, kdc_req)) = req { let mut tx = self.new_tx(direction); tx.msg_type = MessageType::KRB_TGS_REQ; tx.cname = kdc_req.req_body.cname; @@ -177,10 +181,10 @@ impl KRB5State { self.transactions.push(tx); }; self.req_id = 12; - }, + } 13 => { let res = krb5_parser::parse_tgs_rep(i); - if let Ok((_,kdc_rep)) = res { + if let Ok((_, kdc_rep)) = res { let mut tx = self.new_tx(direction); tx.msg_type = MessageType::KRB_TGS_REP; if self.req_id > 0 { @@ -199,16 +203,16 @@ impl KRB5State { } }; self.req_id = 0; - }, + } 14 => { self.req_id = 14; - }, + } 15 => { self.req_id = 0; - }, + } 30 => { let res = KrbError::from_der(i); - if let Ok((_,error)) = res { + if let Ok((_, error)) = res { let mut tx = self.new_tx(direction); if self.req_id > 0 { // set request type only if previous message @@ -223,21 +227,23 @@ impl KRB5State { self.transactions.push(tx); }; self.req_id = 0; - }, - _ => { SCLogDebug!("unknown/unsupported tag {}", hdr.tag()); }, + } + _ => { + SCLogDebug!("unknown/unsupported tag {}", hdr.tag()); + } } 0 - }, + } Err(Err::Incomplete(_)) => { SCLogDebug!("Insufficient data while parsing KRB5 data"); self.set_event(KRB5Event::MalformedData); -1 - }, + } Err(_) => { SCLogDebug!("Error while parsing KRB5 data"); self.set_event(KRB5Event::MalformedData); -1 - }, + } } } @@ -274,7 +280,7 @@ impl KRB5State { impl KRB5Transaction { pub fn new(direction: Direction, id: u64) -> KRB5Transaction { - let krbtx = KRB5Transaction{ + let krbtx = KRB5Transaction { msg_type: MessageType(0), cname: None, realm: None, @@ -291,24 +297,22 @@ impl KRB5Transaction { } /// Return true if Kerberos `EncryptionType` is weak -pub fn test_weak_encryption(alg:EncryptionType) -> bool { +pub fn test_weak_encryption(alg: EncryptionType) -> bool { match alg { - EncryptionType::AES128_CTS_HMAC_SHA1_96 | - EncryptionType::AES256_CTS_HMAC_SHA1_96 | - EncryptionType::AES128_CTS_HMAC_SHA256_128 | - EncryptionType::AES256_CTS_HMAC_SHA384_192 | - EncryptionType::CAMELLIA128_CTS_CMAC | - EncryptionType::CAMELLIA256_CTS_CMAC => false, + EncryptionType::AES128_CTS_HMAC_SHA1_96 + | EncryptionType::AES256_CTS_HMAC_SHA1_96 + | EncryptionType::AES128_CTS_HMAC_SHA256_128 + | EncryptionType::AES256_CTS_HMAC_SHA384_192 + | EncryptionType::CAMELLIA128_CTS_CMAC + | EncryptionType::CAMELLIA256_CTS_CMAC => false, _ => true, // all other ciphers are weak or deprecated } } - - - - /// Returns *mut KRB5State -extern "C" fn krb5_state_new(_orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto) -> *mut std::os::raw::c_void { +extern "C" fn krb5_state_new( + _orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto, +) -> *mut std::os::raw::c_void { let state = KRB5State::new(); let boxed = Box::new(state); return Box::into_raw(boxed) as *mut _; @@ -317,156 +321,156 @@ extern "C" fn krb5_state_new(_orig_state: *mut std::os::raw::c_void, _orig_proto /// Params: /// - state: *mut KRB5State as void pointer extern "C" fn krb5_state_free(state: *mut std::os::raw::c_void) { - let mut state: Box = unsafe{Box::from_raw(state as _)}; + let mut state: Box = unsafe { Box::from_raw(state as _) }; state.free(); } -unsafe extern "C" fn krb5_state_get_tx(state: *mut std::os::raw::c_void, - tx_id: u64) - -> *mut std::os::raw::c_void -{ - let state = cast_pointer!(state,KRB5State); +unsafe extern "C" fn krb5_state_get_tx( + state: *mut std::os::raw::c_void, tx_id: u64, +) -> *mut std::os::raw::c_void { + let state = cast_pointer!(state, KRB5State); match state.get_tx_by_id(tx_id) { Some(tx) => tx as *const _ as *mut _, - None => std::ptr::null_mut(), + None => std::ptr::null_mut(), } } -unsafe extern "C" fn krb5_state_get_tx_count(state: *mut std::os::raw::c_void) - -> u64 -{ - let state = cast_pointer!(state,KRB5State); +unsafe extern "C" fn krb5_state_get_tx_count(state: *mut std::os::raw::c_void) -> u64 { + let state = cast_pointer!(state, KRB5State); state.tx_id } -unsafe extern "C" fn krb5_state_tx_free(state: *mut std::os::raw::c_void, - tx_id: u64) -{ - let state = cast_pointer!(state,KRB5State); +unsafe extern "C" fn krb5_state_tx_free(state: *mut std::os::raw::c_void, tx_id: u64) { + let state = cast_pointer!(state, KRB5State); state.free_tx(tx_id); } -pub extern "C" fn krb5_tx_get_alstate_progress(_tx: *mut std::os::raw::c_void, - _direction: u8) - -> std::os::raw::c_int -{ +pub extern "C" fn krb5_tx_get_alstate_progress( + _tx: *mut std::os::raw::c_void, _direction: u8, +) -> std::os::raw::c_int { 1 } -static mut ALPROTO_KRB5 : AppProto = ALPROTO_UNKNOWN; +static mut ALPROTO_KRB5: AppProto = ALPROTO_UNKNOWN; -unsafe extern "C" fn krb5_probing_parser(_flow: *const Flow, - _direction: u8, - input:*const u8, input_len: u32, - _rdir: *mut u8) -> AppProto -{ +unsafe extern "C" fn krb5_probing_parser( + _flow: *const Flow, _direction: u8, input: *const u8, input_len: u32, _rdir: *mut u8, +) -> AppProto { if input.is_null() { return ALPROTO_UNKNOWN; } - let slice = build_slice!(input,input_len as usize); + let slice = build_slice!(input, input_len as usize); let alproto = ALPROTO_KRB5; - if slice.len() <= 10 { return ALPROTO_FAILED; } + if slice.len() <= 10 { + return ALPROTO_FAILED; + } match der_read_element_header(slice) { Ok((rem, ref hdr)) => { // Kerberos messages start with an APPLICATION header - if hdr.class() != Class::Application { return ALPROTO_FAILED; } + if hdr.class() != Class::Application { + return ALPROTO_FAILED; + } // Tag number should be <= 30 - if hdr.tag().0 > 30 { return ALPROTO_FAILED; } + if hdr.tag().0 > 30 { + return ALPROTO_FAILED; + } // Kerberos messages contain sequences - if rem.is_empty() || rem[0] != 0x30 { return ALPROTO_FAILED; } + if rem.is_empty() || rem[0] != 0x30 { + return ALPROTO_FAILED; + } // Check kerberos version - if let Ok((rem,_hdr)) = der_read_element_header(rem) { + if let Ok((rem, _hdr)) = der_read_element_header(rem) { if rem.len() > 5 { #[allow(clippy::single_match)] - match (rem[2],rem[3],rem[4]) { + match (rem[2], rem[3], rem[4]) { // Encoding of DER integer 5 (version) - (2,1,5) => { return alproto; }, - _ => (), + (2, 1, 5) => { + return alproto; + } + _ => (), } } } return ALPROTO_FAILED; - }, + } Err(Err::Incomplete(_)) => { return ALPROTO_UNKNOWN; - }, + } Err(_) => { return ALPROTO_FAILED; - }, + } } } -unsafe extern "C" fn krb5_probing_parser_tcp(_flow: *const Flow, - direction: u8, - input:*const u8, input_len: u32, - rdir: *mut u8) -> AppProto -{ +unsafe extern "C" fn krb5_probing_parser_tcp( + _flow: *const Flow, direction: u8, input: *const u8, input_len: u32, rdir: *mut u8, +) -> AppProto { if input.is_null() { return ALPROTO_UNKNOWN; } - let slice = build_slice!(input,input_len as usize); - if slice.len() <= 14 { return ALPROTO_FAILED; } - match be_u32(slice) as IResult<&[u8],u32> { + let slice = build_slice!(input, input_len as usize); + if slice.len() <= 14 { + return ALPROTO_FAILED; + } + match be_u32(slice) as IResult<&[u8], u32> { Ok((rem, record_mark)) => { // protocol implementations forbid very large requests - if record_mark > 16384 { return ALPROTO_FAILED; } - return krb5_probing_parser(_flow, direction, - rem.as_ptr(), rem.len() as u32, rdir); - }, + if record_mark > 16384 { + return ALPROTO_FAILED; + } + return krb5_probing_parser(_flow, direction, rem.as_ptr(), rem.len() as u32, rdir); + } Err(Err::Incomplete(_)) => { return ALPROTO_UNKNOWN; - }, + } Err(_) => { return ALPROTO_FAILED; - }, + } } } -pub unsafe extern "C" fn krb5_parse_request(_flow: *const Flow, - state: *mut std::os::raw::c_void, - _pstate: *mut std::os::raw::c_void, - stream_slice: StreamSlice, - _data: *const std::os::raw::c_void, - ) -> AppLayerResult { +pub unsafe extern "C" fn krb5_parse_request( + _flow: *const Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void, + stream_slice: StreamSlice, _data: *const std::os::raw::c_void, +) -> AppLayerResult { let buf = stream_slice.as_slice(); - let state = cast_pointer!(state,KRB5State); + let state = cast_pointer!(state, KRB5State); if state.parse(buf, Direction::ToServer) < 0 { return AppLayerResult::err(); } AppLayerResult::ok() } -unsafe extern "C" fn krb5_parse_response(_flow: *const Flow, - state: *mut std::os::raw::c_void, - _pstate: *mut std::os::raw::c_void, - stream_slice: StreamSlice, - _data: *const std::os::raw::c_void, - ) -> AppLayerResult { +unsafe extern "C" fn krb5_parse_response( + _flow: *const Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void, + stream_slice: StreamSlice, _data: *const std::os::raw::c_void, +) -> AppLayerResult { let buf = stream_slice.as_slice(); - let state = cast_pointer!(state,KRB5State); + let state = cast_pointer!(state, KRB5State); if state.parse(buf, Direction::ToClient) < 0 { return AppLayerResult::err(); } AppLayerResult::ok() } -unsafe extern "C" fn krb5_parse_request_tcp(_flow: *const Flow, - state: *mut std::os::raw::c_void, - _pstate: *mut std::os::raw::c_void, - stream_slice: StreamSlice, - _data: *const std::os::raw::c_void, - ) -> AppLayerResult { - let state = cast_pointer!(state,KRB5State); +unsafe extern "C" fn krb5_parse_request_tcp( + _flow: *const Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void, + stream_slice: StreamSlice, _data: *const std::os::raw::c_void, +) -> AppLayerResult { + let state = cast_pointer!(state, KRB5State); let buf = stream_slice.as_slice(); - let mut v : Vec; + let mut v: Vec; let tcp_buffer = match state.record_ts { 0 => buf, _ => { // sanity check to avoid memory exhaustion if state.defrag_buf_ts.len() + buf.len() > 100000 { - SCLogDebug!("rs_krb5_parse_request_tcp: TCP buffer exploded {} {}", - state.defrag_buf_ts.len(), buf.len()); + SCLogDebug!( + "rs_krb5_parse_request_tcp: TCP buffer exploded {} {}", + state.defrag_buf_ts.len(), + buf.len() + ); return AppLayerResult::err(); } v = state.defrag_buf_ts.split_off(0); @@ -477,11 +481,11 @@ unsafe extern "C" fn krb5_parse_request_tcp(_flow: *const Flow, let mut cur_i = tcp_buffer; while !cur_i.is_empty() { if state.record_ts == 0 { - match be_u32(cur_i) as IResult<&[u8],u32> { - Ok((rem,record)) => { + match be_u32(cur_i) as IResult<&[u8], u32> { + Ok((rem, record)) => { state.record_ts = record as usize; cur_i = rem; - }, + } Err(Err::Incomplete(_)) => { state.defrag_buf_ts.extend_from_slice(cur_i); return AppLayerResult::ok(); @@ -507,23 +511,24 @@ unsafe extern "C" fn krb5_parse_request_tcp(_flow: *const Flow, AppLayerResult::ok() } -unsafe extern "C" fn krb5_parse_response_tcp(_flow: *const Flow, - state: *mut std::os::raw::c_void, - _pstate: *mut std::os::raw::c_void, - stream_slice: StreamSlice, - _data: *const std::os::raw::c_void, - ) -> AppLayerResult { - let state = cast_pointer!(state,KRB5State); +unsafe extern "C" fn krb5_parse_response_tcp( + _flow: *const Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void, + stream_slice: StreamSlice, _data: *const std::os::raw::c_void, +) -> AppLayerResult { + let state = cast_pointer!(state, KRB5State); let buf = stream_slice.as_slice(); - let mut v : Vec; + let mut v: Vec; let tcp_buffer = match state.record_tc { 0 => buf, _ => { // sanity check to avoid memory exhaustion if state.defrag_buf_tc.len() + buf.len() > 100000 { - SCLogDebug!("rs_krb5_parse_response_tcp: TCP buffer exploded {} {}", - state.defrag_buf_tc.len(), buf.len()); + SCLogDebug!( + "rs_krb5_parse_response_tcp: TCP buffer exploded {} {}", + state.defrag_buf_tc.len(), + buf.len() + ); return AppLayerResult::err(); } v = state.defrag_buf_tc.split_off(0); @@ -534,11 +539,11 @@ unsafe extern "C" fn krb5_parse_response_tcp(_flow: *const Flow, let mut cur_i = tcp_buffer; while !cur_i.is_empty() { if state.record_tc == 0 { - match be_u32(cur_i) as IResult<&[u8],_> { - Ok((rem,record)) => { + match be_u32(cur_i) as IResult<&[u8], _> { + Ok((rem, record)) => { state.record_tc = record as usize; cur_i = rem; - }, + } Err(Err::Incomplete(_)) => { state.defrag_buf_tc.extend_from_slice(cur_i); return AppLayerResult::ok(); @@ -567,39 +572,39 @@ unsafe extern "C" fn krb5_parse_response_tcp(_flow: *const Flow, export_tx_data_get!(krb5_get_tx_data, KRB5Transaction); export_state_data_get!(krb5_get_state_data, KRB5State); -const PARSER_NAME : &[u8] = b"krb5\0"; +const PARSER_NAME: &[u8] = b"krb5\0"; #[no_mangle] pub unsafe extern "C" fn SCRegisterKrb5Parser() { let default_port = CString::new("88").unwrap(); let mut parser = RustParser { - name : PARSER_NAME.as_ptr() as *const std::os::raw::c_char, - default_port : default_port.as_ptr(), - ipproto : core::IPPROTO_UDP, - probe_ts : Some(krb5_probing_parser), - probe_tc : Some(krb5_probing_parser), - min_depth : 0, - max_depth : 16, - state_new : krb5_state_new, - state_free : krb5_state_free, - tx_free : krb5_state_tx_free, - parse_ts : krb5_parse_request, - parse_tc : krb5_parse_response, - get_tx_count : krb5_state_get_tx_count, - get_tx : krb5_state_get_tx, - tx_comp_st_ts : 1, - tx_comp_st_tc : 1, - tx_get_progress : krb5_tx_get_alstate_progress, - get_eventinfo : Some(KRB5Event::get_event_info), - get_eventinfo_byid : Some(KRB5Event::get_event_info_by_id), - localstorage_new : None, - localstorage_free : None, - get_tx_files : None, - get_tx_iterator : Some(applayer::state_get_tx_iterator::), - get_tx_data : krb5_get_tx_data, - get_state_data : krb5_get_state_data, - apply_tx_config : None, - flags : 0, + name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char, + default_port: default_port.as_ptr(), + ipproto: core::IPPROTO_UDP, + probe_ts: Some(krb5_probing_parser), + probe_tc: Some(krb5_probing_parser), + min_depth: 0, + max_depth: 16, + state_new: krb5_state_new, + state_free: krb5_state_free, + tx_free: krb5_state_tx_free, + parse_ts: krb5_parse_request, + parse_tc: krb5_parse_response, + get_tx_count: krb5_state_get_tx_count, + get_tx: krb5_state_get_tx, + tx_comp_st_ts: 1, + tx_comp_st_tc: 1, + tx_get_progress: krb5_tx_get_alstate_progress, + get_eventinfo: Some(KRB5Event::get_event_info), + get_eventinfo_byid: Some(KRB5Event::get_event_info_by_id), + localstorage_new: None, + localstorage_free: None, + get_tx_files: None, + get_tx_iterator: Some(applayer::state_get_tx_iterator::), + get_tx_data: krb5_get_tx_data, + get_state_data: krb5_get_state_data, + apply_tx_config: None, + flags: 0, get_frame_id_by_name: None, get_frame_name_by_id: None, get_state_id_by_name: None, diff --git a/rust/src/krb/log.rs b/rust/src/krb/log.rs index c34b58a931..088081814e 100644 --- a/rust/src/krb/log.rs +++ b/rust/src/krb/log.rs @@ -18,10 +18,9 @@ // written by Pierre Chifflier use crate::jsonbuilder::{JsonBuilder, JsonError}; -use crate::krb::krb5::{KRB5Transaction,test_weak_encryption}; +use crate::krb::krb5::{test_weak_encryption, KRB5Transaction}; -fn krb5_log_response(jsb: &mut JsonBuilder, tx: &KRB5Transaction) -> Result<(), JsonError> -{ +fn krb5_log_response(jsb: &mut JsonBuilder, tx: &KRB5Transaction) -> Result<(), JsonError> { jsb.open_object("krb5")?; match tx.error_code { Some(c) => { @@ -35,30 +34,35 @@ fn krb5_log_response(jsb: &mut JsonBuilder, tx: &KRB5Transaction) -> Result<(), jsb.set_string("failed_request", "UNKNOWN")?; } jsb.set_string("error_code", &format!("{:?}", c))?; - }, - None => { jsb.set_string("msg_type", &format!("{:?}", tx.msg_type))?; }, + } + None => { + jsb.set_string("msg_type", &format!("{:?}", tx.msg_type))?; + } } let cname = match tx.cname { Some(ref x) => format!("{}", x), - None => "".to_owned(), + None => "".to_owned(), }; let realm = match tx.realm { Some(ref x) => x.0.to_string(), - None => "".to_owned(), + None => "".to_owned(), }; let sname = match tx.sname { Some(ref x) => format!("{}", x), - None => "".to_owned(), + None => "".to_owned(), }; let encryption = match tx.etype { Some(ref x) => format!("{:?}", x), - None => "".to_owned(), + None => "".to_owned(), }; jsb.set_string("cname", &cname)?; jsb.set_string("realm", &realm)?; jsb.set_string("sname", &sname)?; jsb.set_string("encryption", &encryption)?; - jsb.set_bool("weak_encryption", tx.etype.map_or(false,test_weak_encryption))?; + jsb.set_bool( + "weak_encryption", + tx.etype.map_or(false, test_weak_encryption), + )?; if let Some(x) = tx.ticket_etype { let refs = format!("{:?}", x); jsb.set_string("ticket_encryption", &refs)?; @@ -70,7 +74,6 @@ fn krb5_log_response(jsb: &mut JsonBuilder, tx: &KRB5Transaction) -> Result<(), } #[no_mangle] -pub extern "C" fn SCKrb5LogJsonResponse(tx: &KRB5Transaction, jsb: &mut JsonBuilder) -> bool -{ +pub extern "C" fn SCKrb5LogJsonResponse(tx: &KRB5Transaction, jsb: &mut JsonBuilder) -> bool { krb5_log_response(jsb, tx).is_ok() } diff --git a/rust/src/krb/mod.rs b/rust/src/krb/mod.rs index ca6237d7bb..d13990241d 100644 --- a/rust/src/krb/mod.rs +++ b/rust/src/krb/mod.rs @@ -19,6 +19,6 @@ // written by Pierre Chifflier -pub mod krb5; pub mod detect; +pub mod krb5; pub mod log;