--- /dev/null
+/* Copyright (C) 2020 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+// Author: Frank Honza <frank.honza@dcso.de>
+
+use super::ipsec_parser::IkeV2Transform;
+use crate::ike::ike::*;
+use std::ffi::CStr;
+use std::ptr;
+
+#[no_mangle]
+pub extern "C" fn rs_ike_state_get_exch_type(tx: &mut IKETransaction, exch_type: *mut u8) -> u8 {
+ debug_validate_bug_on!(exch_type == std::ptr::null_mut());
+
+ if tx.ike_version == 1 {
+ if let Some(r) = tx.hdr.ikev1_header.exchange_type {
+ unsafe {
+ *exch_type = r;
+ }
+ return 1;
+ }
+ } else if tx.ike_version == 2 {
+ unsafe {
+ *exch_type = tx.hdr.ikev2_header.exch_type.0;
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+#[no_mangle]
+pub extern "C" fn rs_ike_state_get_spi_initiator(
+ tx: &mut IKETransaction, buffer: *mut *const u8, buffer_len: *mut u32,
+) -> u8 {
+ debug_validate_bug_on!(buffer == std::ptr::null_mut() || buffer_len == std::ptr::null_mut());
+
+ unsafe {
+ *buffer = tx.hdr.spi_initiator.as_ptr();
+ *buffer_len = tx.hdr.spi_initiator.len() as u32;
+ }
+ return 1;
+}
+
+#[no_mangle]
+pub extern "C" fn rs_ike_state_get_spi_responder(
+ tx: &mut IKETransaction, buffer: *mut *const u8, buffer_len: *mut u32,
+) -> u8 {
+ debug_validate_bug_on!(buffer == std::ptr::null_mut() || buffer_len == std::ptr::null_mut());
+
+ unsafe {
+ *buffer = tx.hdr.spi_responder.as_ptr();
+ *buffer_len = tx.hdr.spi_responder.len() as u32;
+ }
+ return 1;
+}
+
+#[no_mangle]
+pub extern "C" fn rs_ike_state_get_nonce(
+ tx: &mut IKETransaction, buffer: *mut *const u8, buffer_len: *mut u32,
+) -> u8 {
+ debug_validate_bug_on!(buffer == std::ptr::null_mut() || buffer_len == std::ptr::null_mut());
+
+ if tx.ike_version == 1 && !tx.hdr.ikev1_header.nonce.is_empty() {
+ let p = &tx.hdr.ikev1_header.nonce;
+ unsafe {
+ *buffer = p.as_ptr();
+ *buffer_len = p.len() as u32;
+ }
+ return 1;
+ }
+
+ unsafe {
+ *buffer = ptr::null();
+ *buffer_len = 0;
+ }
+
+ return 0;
+}
+
+#[no_mangle]
+pub extern "C" fn rs_ike_state_get_key_exchange(
+ tx: &mut IKETransaction, buffer: *mut *const u8, buffer_len: *mut u32,
+) -> u8 {
+ debug_validate_bug_on!(buffer == std::ptr::null_mut() || buffer_len == std::ptr::null_mut());
+
+ if tx.ike_version == 1 && !tx.hdr.ikev1_header.key_exchange.is_empty() {
+ let p = &tx.hdr.ikev1_header.key_exchange;
+ unsafe {
+ *buffer = p.as_ptr();
+ *buffer_len = p.len() as u32;
+ }
+ return 1;
+ }
+
+ unsafe {
+ *buffer = ptr::null();
+ *buffer_len = 0;
+ }
+
+ return 0;
+}
+
+#[no_mangle]
+pub extern "C" fn rs_ike_tx_get_vendor(
+ tx: &IKETransaction, i: u16, buf: *mut *const u8, len: *mut u32,
+) -> u8 {
+ if tx.ike_version == 1 && i < tx.hdr.ikev1_header.vendor_ids.len() as u16 {
+ unsafe {
+ *len = tx.hdr.ikev1_header.vendor_ids[i as usize].len() as u32;
+ *buf = tx.hdr.ikev1_header.vendor_ids[i as usize].as_ptr();
+ }
+ return 1;
+ }
+
+ unsafe {
+ *buf = ptr::null();
+ *len = 0;
+ }
+
+ return 0;
+}
+
+#[no_mangle]
+pub extern "C" fn rs_ike_state_get_sa_attribute(
+ tx: &mut IKETransaction, sa_type: *const std::os::raw::c_char, value: *mut u32,
+) -> u8 {
+ debug_validate_bug_on!(value == std::ptr::null_mut());
+ let mut ret_val = 0;
+ let mut ret_code = 0;
+ let sa_type_s: Result<_,_>;
+
+ unsafe {
+ sa_type_s = CStr::from_ptr(sa_type).to_str()
+ }
+ SCLogInfo!("{:#?}", sa_type_s);
+
+ if let Ok(sa) = sa_type_s {
+ if tx.ike_version == 1 {
+ 'outer1: for (i, server_transform) in tx.hdr.ikev1_transforms.iter().enumerate() {
+ if i >= 1 {
+ debug_validate_bug_on!(true);
+ break;
+ }
+ for attr in server_transform {
+ if attr.attribute_type.to_string() == sa {
+ if let Some(numeric_value) = attr.numeric_value {
+ ret_val = numeric_value;
+ ret_code = 1;
+ break 'outer1;
+ }
+ }
+ }
+ }
+ } else if tx.ike_version == 2 {
+ 'outer2: for server_transform in tx.hdr.ikev2_transforms.iter() {
+ for attr in server_transform {
+ match attr {
+ IkeV2Transform::Encryption(e) => {
+ if sa == "alg_enc" {
+ ret_val = e.0 as u32;
+ ret_code = 1;
+ break 'outer2;
+ }
+ }
+ IkeV2Transform::Auth(e) => {
+ if sa == "alg_auth" {
+ ret_val = e.0 as u32;
+ ret_code = 1;
+ break 'outer2;
+ }
+ }
+ IkeV2Transform::PRF(ref e) => {
+ if sa == "alg_prf" {
+ ret_val = e.0 as u32;
+ ret_code = 1;
+ break 'outer2;
+ }
+ }
+ IkeV2Transform::DH(ref e) => {
+ if sa == "alg_dh" {
+ ret_val = e.0 as u32;
+ ret_code = 1;
+ break 'outer2;
+ }
+ }
+ _ => (),
+ }
+ }
+ }
+ }
+ }
+
+ unsafe {
+ *value = ret_val;
+ }
+ return ret_code;
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rs_ike_state_get_key_exchange_payload_length(
+ tx: &mut IKETransaction, value: *mut u32,
+) -> u8 {
+ debug_validate_bug_on!(value == std::ptr::null_mut());
+
+ if tx.ike_version == 1 && !tx.hdr.ikev1_header.key_exchange.is_empty() {
+ *value = tx.hdr.ikev1_header.key_exchange.len() as u32;
+ return 1;
+ }
+
+ *value = 0;
+ return 0;
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rs_ike_state_get_nonce_payload_length(
+ tx: &mut IKETransaction, value: *mut u32,
+) -> u8 {
+ debug_validate_bug_on!(value == std::ptr::null_mut());
+
+ if tx.ike_version == 1 && !tx.hdr.ikev1_header.nonce.is_empty() {
+ *value = tx.hdr.ikev1_header.nonce.len() as u32;
+ return 1;
+ }
+
+ *value = 0;
+ return 0;
+}
-/* Copyright (C) 2017-2020 Open Information Security Foundation
+/* Copyright (C) 2020 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* 02110-1301, USA.
*/
-// written by Pierre Chifflier <chifflier@wzdftpd.net>
+// Author: Frank Honza <frank.honza@dcso.de>
-use crate::ike::ipsec_parser::*;
-use crate::ike::state::IKEConnectionState;
-use crate::core;
-use crate::core::{AppProto,Flow,ALPROTO_UNKNOWN,ALPROTO_FAILED,STREAM_TOSERVER,STREAM_TOCLIENT};
-use crate::applayer::{self, *};
-use std;
-use std::ffi::{CStr,CString};
+extern crate ipsec_parser;
+use self::ipsec_parser::*;
+use crate::applayer;
+use crate::applayer::*;
+use crate::core::{
+ self, AppProto, Flow, ALPROTO_FAILED, ALPROTO_UNKNOWN, STREAM_TOCLIENT, STREAM_TOSERVER,
+};
+use crate::ike::ikev1::{handle_ikev1, IkeV1Header, Ikev1Container};
+use crate::ike::ikev2::{handle_ikev2, Ikev2Container};
+use crate::ike::parser::*;
use nom;
+use std;
+use std::collections::HashSet;
+use std::ffi::{CStr, CString};
+use std::mem::transmute;
#[repr(u32)]
pub enum IkeEvent {
WeakCryptoNoAuth,
InvalidProposal,
UnknownProposal,
+ PayloadExtraData,
}
impl IkeEvent {
- fn from_i32(value: i32) -> Option<IkeEvent> {
+ pub fn from_i32(value: i32) -> Option<IkeEvent> {
match value {
0 => Some(IkeEvent::MalformedData),
1 => Some(IkeEvent::NoEncryption),
7 => Some(IkeEvent::WeakCryptoNoAuth),
8 => Some(IkeEvent::InvalidProposal),
9 => Some(IkeEvent::UnknownProposal),
+ 10 => Some(IkeEvent::PayloadExtraData),
_ => None,
}
}
}
-pub struct IKEState {
- /// List of transactions for this session
- transactions: Vec<IKETransaction>,
-
- /// tx counter for assigning incrementing id's to tx's
- tx_id: u64,
-
- /// The connection state
- connection_state: IKEConnectionState,
-
- /// The transforms proposed by the initiator
- pub client_transforms : Vec<Vec<IkeV2Transform>>,
-
- /// The transforms selected by the responder
- pub server_transforms : Vec<Vec<IkeV2Transform>>,
-
- /// The encryption algorithm selected by the responder
- pub alg_enc: IkeTransformEncType,
- /// The authentication algorithm selected by the responder
- pub alg_auth: IkeTransformAuthType,
- /// The PRF algorithm selected by the responder
- pub alg_prf: IkeTransformPRFType,
- /// The Diffie-Hellman algorithm selected by the responder
- pub alg_dh: IkeTransformDHType,
- /// The extended sequence numbers parameter selected by the responder
- pub alg_esn: IkeTransformESNType,
+pub struct IkeHeaderWrapper {
+ pub spi_initiator: String,
+ pub spi_responder: String,
+ pub maj_ver: u8,
+ pub min_ver: u8,
+ pub msg_id: u32,
+ pub flags: u8,
+ pub ikev1_transforms: Vec<Vec<SaAttribute>>,
+ pub ikev2_transforms: Vec<Vec<IkeV2Transform>>,
+ pub ikev1_header: IkeV1Header,
+ pub ikev2_header: IkeV2Header,
+}
- /// The Diffie-Hellman group from the server KE message, if present.
- pub dh_group: IkeTransformDHType,
+impl IkeHeaderWrapper {
+ pub fn new() -> IkeHeaderWrapper {
+ IkeHeaderWrapper {
+ spi_initiator: String::new(),
+ spi_responder: String::new(),
+ maj_ver: 0,
+ min_ver: 0,
+ msg_id: 0,
+ flags: 0,
+ ikev1_transforms: Vec::new(),
+ ikev2_transforms: Vec::new(),
+ ikev1_header: IkeV1Header::default(),
+ ikev2_header: IkeV2Header {
+ init_spi: 0,
+ resp_spi: 0,
+ next_payload: IkePayloadType::NoNextPayload,
+ maj_ver: 0,
+ min_ver: 0,
+ exch_type: IkeExchangeType(0),
+ flags: 0,
+ msg_id: 0,
+ length: 0,
+ },
+ }
+ }
+}
+#[derive(Default)]
+pub struct IkePayloadWrapper {
+ pub ikev1_payload_types: Option<HashSet<u8>>,
+ pub ikev2_payload_types: Vec<IkePayloadType>,
}
-#[derive(Debug)]
pub struct IKETransaction {
- /// The IKE reference ID
- pub xid: u64,
-
- pub hdr: IkeV2Header,
+ tx_id: u64,
- pub payload_types: Vec<IkePayloadType>,
+ pub ike_version: u8,
+ pub hdr: IkeHeaderWrapper,
+ pub payload_types: IkePayloadWrapper,
pub notify_types: Vec<NotifyType>,
- /// IKEv2 errors seen during exchange
+ /// errors seen during exchange
pub errors: u32,
- /// The internal transaction id
- id: u64,
-
- /// The detection engine state, if present
+ logged: LoggerFlags,
de_state: Option<*mut core::DetectEngineState>,
-
- /// The events associated with this transaction
events: *mut core::AppLayerDecoderEvents,
-
tx_data: applayer::AppLayerTxData,
}
-
-
-impl IKEState {
- pub fn new() -> IKEState {
- IKEState{
- transactions: Vec::new(),
+impl IKETransaction {
+ pub fn new() -> IKETransaction {
+ IKETransaction {
tx_id: 0,
- connection_state: IKEConnectionState::Init,
- dh_group: IkeTransformDHType::None,
- client_transforms: Vec::new(),
- server_transforms: Vec::new(),
- alg_enc: IkeTransformEncType::ENCR_NULL,
- alg_auth: IkeTransformAuthType::NONE,
- alg_prf: IkeTransformPRFType::PRF_NULL,
- alg_dh: IkeTransformDHType::None,
- alg_esn: IkeTransformESNType::NoESN,
+ ike_version: 0,
+ hdr: IkeHeaderWrapper::new(),
+ payload_types: Default::default(),
+ notify_types: vec![],
+ logged: LoggerFlags::new(),
+ de_state: None,
+ events: std::ptr::null_mut(),
+ tx_data: applayer::AppLayerTxData::new(),
+ errors: 0,
}
}
-}
-impl IKEState {
- /// Parse an IKE request message
- ///
- /// Returns The number of messages parsed, or -1 on error
- fn parse(&mut self, i: &[u8], direction: u8) -> i32 {
- match parse_ikev2_header(i) {
- Ok((rem,ref hdr)) => {
- if rem.len() == 0 && hdr.length == 28 {
- return 1;
- }
- // Rule 0: check version
- if hdr.maj_ver != 2 || hdr.min_ver != 0 {
- self.set_event(IkeEvent::MalformedData);
- return -1;
- }
- if hdr.init_spi == 0 {
- self.set_event(IkeEvent::MalformedData);
- return -1;
- }
- // only analyse IKE_SA, other payloads are encrypted
- if hdr.exch_type != IkeExchangeType::IKE_SA_INIT {
- return 0;
- }
- let mut tx = self.new_tx();
- // use init_spi as transaction identifier
- tx.xid = hdr.init_spi;
- tx.hdr = (*hdr).clone();
- self.transactions.push(tx);
- let mut payload_types = Vec::new();
- let mut errors = 0;
- let mut notify_types = Vec::new();
- match parse_ikev2_payload_list(rem,hdr.next_payload) {
- Ok((_,Ok(ref p))) => {
- for payload in p {
- payload_types.push(payload.hdr.next_payload_type);
- match payload.content {
- IkeV2PayloadContent::Dummy => (),
- IkeV2PayloadContent::SA(ref prop) => {
- // if hdr.flags & IKE_FLAG_INITIATOR != 0 {
- self.add_proposals(prop, direction);
- // }
- },
- IkeV2PayloadContent::KE(ref kex) => {
- SCLogDebug!("KEX {:?}", kex.dh_group);
- if direction == STREAM_TOCLIENT {
- self.dh_group = kex.dh_group;
- }
- },
- IkeV2PayloadContent::Nonce(ref n) => {
- SCLogDebug!("Nonce: {:?}", n);
- },
- IkeV2PayloadContent::Notify(ref n) => {
- SCLogDebug!("Notify: {:?}", n);
- if n.notify_type.is_error() {
- errors += 1;
- }
- notify_types.push(n.notify_type);
- },
- // XXX CertificateRequest
- // XXX Certificate
- // XXX Authentication
- // XXX TSi
- // XXX TSr
- // XXX IDr
- _ => {
- SCLogDebug!("Unknown payload content {:?}", payload.content);
- },
- }
- self.connection_state = self.connection_state.advance(payload);
- if let Some(tx) = self.transactions.last_mut() {
- // borrow back tx to update it
- tx.payload_types.append(&mut payload_types);
- tx.errors = errors;
- tx.notify_types.append(&mut notify_types);
- }
- };
- },
- e => { SCLogDebug!("parse_ike_payload_with_type: {:?}",e); () },
- }
- 1
- },
- Err(nom::Err::Incomplete(_)) => {
- SCLogDebug!("Insufficient data while parsing IKE data");
- self.set_event(IkeEvent::MalformedData);
- -1
- },
- Err(_) => {
- SCLogDebug!("Error while parsing IKE data");
- self.set_event(IkeEvent::MalformedData);
- -1
- },
+ pub fn free(&mut self) {
+ if self.events != std::ptr::null_mut() {
+ core::sc_app_layer_decoder_events_free_events(&mut self.events);
+ }
+ if let Some(state) = self.de_state {
+ core::sc_detect_engine_state_free(state);
}
}
+}
- fn free(&mut self) {
- // All transactions are freed when the `transactions` object is freed.
- // But let's be explicit
- self.transactions.clear();
+impl Drop for IKETransaction {
+ fn drop(&mut self) {
+ self.free();
}
+}
- fn new_tx(&mut self) -> IKETransaction {
- self.tx_id += 1;
- IKETransaction::new(self.tx_id)
- }
+#[derive(Default)]
+pub struct IKEState {
+ tx_id: u64,
+ pub transactions: Vec<IKETransaction>,
- fn get_tx_by_id(&mut self, tx_id: u64) -> Option<&IKETransaction> {
- self.transactions.iter().find(|&tx| tx.id == tx_id + 1)
- }
+ pub ikev1_container: Ikev1Container,
+ pub ikev2_container: Ikev2Container,
+}
+impl IKEState {
+ // Free a transaction by ID.
fn free_tx(&mut self, tx_id: u64) {
- let tx = self.transactions.iter().position(|ref tx| tx.id == tx_id + 1);
+ let tx = self
+ .transactions
+ .iter()
+ .position(|ref tx| tx.tx_id == tx_id + 1);
debug_assert!(tx != None);
if let Some(idx) = tx {
let _ = self.transactions.remove(idx);
}
}
+ pub fn get_tx(&mut self, tx_id: u64) -> Option<&mut IKETransaction> {
+ for tx in &mut self.transactions {
+ if tx.tx_id == tx_id + 1 {
+ return Some(tx);
+ }
+ }
+ return None;
+ }
+
+ pub fn new_tx(&mut self) -> IKETransaction {
+ let mut tx = IKETransaction::new();
+ self.tx_id += 1;
+ tx.tx_id = self.tx_id;
+ return tx;
+ }
+
/// Set an event. The event is set on the most recent transaction.
- fn set_event(&mut self, event: IkeEvent) {
+ pub fn set_event(&mut self, event: IkeEvent) {
if let Some(tx) = self.transactions.last_mut() {
let ev = event as u8;
core::sc_app_layer_decoder_events_set_event_raw(&mut tx.events, ev);
} else {
- SCLogDebug!("IKEv2: trying to set event {} on non-existing transaction", event as u32);
+ SCLogDebug!(
+ "IKE: trying to set event {} on non-existing transaction",
+ event as u32
+ );
}
}
- fn add_proposals(&mut self, prop: &Vec<IkeV2Proposal>, direction: u8) {
- for ref p in prop {
- let transforms : Vec<IkeV2Transform> = p.transforms.iter().map(|x| x.into()).collect();
- // Rule 1: warn on weak or unknown transforms
- for xform in &transforms {
- match *xform {
- IkeV2Transform::Encryption(ref enc) => {
- match *enc {
- IkeTransformEncType::ENCR_DES_IV64 |
- IkeTransformEncType::ENCR_DES |
- IkeTransformEncType::ENCR_3DES |
- IkeTransformEncType::ENCR_RC5 |
- IkeTransformEncType::ENCR_IDEA |
- IkeTransformEncType::ENCR_CAST |
- IkeTransformEncType::ENCR_BLOWFISH |
- IkeTransformEncType::ENCR_3IDEA |
- IkeTransformEncType::ENCR_DES_IV32 |
- IkeTransformEncType::ENCR_NULL => {
- SCLogDebug!("Weak Encryption: {:?}", enc);
- // XXX send event only if direction == STREAM_TOCLIENT ?
- self.set_event(IkeEvent::WeakCryptoEnc);
- },
- _ => (),
- }
- },
- IkeV2Transform::PRF(ref prf) => {
- match *prf {
- IkeTransformPRFType::PRF_NULL => {
- SCLogDebug!("'Null' PRF transform proposed");
- self.set_event(IkeEvent::InvalidProposal);
- },
- IkeTransformPRFType::PRF_HMAC_MD5 |
- IkeTransformPRFType::PRF_HMAC_SHA1 => {
- SCLogDebug!("Weak PRF: {:?}", prf);
- self.set_event(IkeEvent::WeakCryptoPRF);
- },
- _ => (),
- }
- },
- IkeV2Transform::Auth(ref auth) => {
- match *auth {
- IkeTransformAuthType::NONE => {
- // Note: this could be expected with an AEAD encription alg.
- // See rule 4
- ()
- },
- IkeTransformAuthType::AUTH_HMAC_MD5_96 |
- IkeTransformAuthType::AUTH_HMAC_SHA1_96 |
- IkeTransformAuthType::AUTH_DES_MAC |
- IkeTransformAuthType::AUTH_KPDK_MD5 |
- IkeTransformAuthType::AUTH_AES_XCBC_96 |
- IkeTransformAuthType::AUTH_HMAC_MD5_128 |
- IkeTransformAuthType::AUTH_HMAC_SHA1_160 => {
- SCLogDebug!("Weak auth: {:?}", auth);
- self.set_event(IkeEvent::WeakCryptoAuth);
- },
- _ => (),
- }
- },
- IkeV2Transform::DH(ref dh) => {
- match *dh {
- IkeTransformDHType::None => {
- SCLogDebug!("'None' DH transform proposed");
- self.set_event(IkeEvent::InvalidProposal);
- },
- IkeTransformDHType::Modp768 |
- IkeTransformDHType::Modp1024 |
- IkeTransformDHType::Modp1024s160 |
- IkeTransformDHType::Modp1536 => {
- SCLogDebug!("Weak DH: {:?}", dh);
- self.set_event(IkeEvent::WeakCryptoDH);
- },
- _ => (),
- }
- },
- IkeV2Transform::Unknown(tx_type,tx_id) => {
- SCLogDebug!("Unknown proposal: type={:?}, id={}", tx_type, tx_id);
- self.set_event(IkeEvent::UnknownProposal);
- },
- _ => (),
+ fn handle_input(&mut self, input: &[u8], direction: u8) -> AppLayerResult {
+ // We're not interested in empty requests.
+ if input.len() == 0 {
+ return AppLayerResult::ok();
+ }
+
+ let mut current = input;
+ match parse_isakmp_header(current) {
+ Ok((rem, isakmp_header)) => {
+ current = rem;
+
+ if isakmp_header.maj_ver != 1 && isakmp_header.maj_ver != 2 {
+ SCLogDebug!("Unsupported ISAKMP major_version");
+ return AppLayerResult::err();
}
- }
- // Rule 2: check if no DH was proposed
- if ! transforms.iter().any(|x| {
- match *x {
- IkeV2Transform::DH(_) => true,
- _ => false
+
+ if isakmp_header.maj_ver == 1 {
+ handle_ikev1(self, current, isakmp_header, direction);
+ } else if isakmp_header.maj_ver == 2 {
+ handle_ikev2(self, current, isakmp_header, direction);
+ } else {
+ return AppLayerResult::err();
}
- })
- {
- SCLogDebug!("No DH transform found");
- self.set_event(IkeEvent::WeakCryptoNoDH);
- }
- // Rule 3: check if proposing AH ([RFC7296] section 3.3.1)
- if p.protocol_id == ProtocolID::AH {
- SCLogDebug!("Proposal uses protocol AH - no confidentiality");
- self.set_event(IkeEvent::NoEncryption);
+ return AppLayerResult::ok(); // todo either remove outer loop or check header length-field if we have completely read everything
}
- // Rule 4: lack of integrity is accepted only if using an AEAD proposal
- // Look if no auth was proposed, including if proposal is Auth::None
- if ! transforms.iter().any(|x| {
- match *x {
- IkeV2Transform::Auth(IkeTransformAuthType::NONE) => false,
- IkeV2Transform::Auth(_) => true,
- _ => false,
- }
- })
- {
- if ! transforms.iter().any(|x| {
- match *x {
- IkeV2Transform::Encryption(ref enc) => enc.is_aead(),
- _ => false
- }
- }) {
- SCLogDebug!("No integrity transform found");
- self.set_event(IkeEvent::WeakCryptoNoAuth);
- }
+ Err(nom::Err::Incomplete(_)) => {
+ SCLogDebug!("Insufficient data while parsing IKE");
+ return AppLayerResult::err();
}
- // Finally
- if direction == STREAM_TOCLIENT {
- transforms.iter().for_each(|t|
- match *t {
- IkeV2Transform::Encryption(ref e) => self.alg_enc = *e,
- IkeV2Transform::Auth(ref a) => self.alg_auth = *a,
- IkeV2Transform::PRF(ref p) => self.alg_prf = *p,
- IkeV2Transform::DH(ref dh) => self.alg_dh = *dh,
- IkeV2Transform::ESN(ref e) => self.alg_esn = *e,
- _ => (),
- });
- SCLogDebug!("Selected transforms: {:?}", transforms);
- self.server_transforms.push(transforms);
- } else {
- SCLogDebug!("Proposed transforms: {:?}", transforms);
- self.client_transforms.push(transforms);
+ Err(_) => {
+ SCLogDebug!("Error while parsing IKE packet");
+ return AppLayerResult::err();
}
}
}
-}
-impl IKETransaction {
- pub fn new(id: u64) -> IKETransaction {
- IKETransaction {
- xid: 0,
- hdr: IkeV2Header {
- init_spi: 0,
- resp_spi: 0,
- next_payload: IkePayloadType::NoNextPayload,
- maj_ver: 0,
- min_ver: 0,
- exch_type: IkeExchangeType(0),
- flags: 0,
- msg_id: 0,
- length: 0,
- },
- payload_types: Vec::new(),
- notify_types: Vec::new(),
- errors: 0,
- id: id,
- de_state: None,
- events: std::ptr::null_mut(),
- tx_data: applayer::AppLayerTxData::new(),
+ fn tx_iterator(
+ &mut self, min_tx_id: u64, state: &mut u64,
+ ) -> Option<(&IKETransaction, u64, bool)> {
+ let mut index = *state as usize;
+ let len = self.transactions.len();
+
+ while index < len {
+ let tx = &self.transactions[index];
+ if tx.tx_id < min_tx_id + 1 {
+ index += 1;
+ continue;
+ }
+ *state = index as u64;
+
+ return Some((tx, tx.tx_id - 1, (len - index) > 1));
}
+
+ return None;
}
+}
- fn free(&mut self) {
- if self.events != std::ptr::null_mut() {
- core::sc_app_layer_decoder_events_free_events(&mut self.events);
- }
- if let Some(state) = self.de_state {
- core::sc_detect_engine_state_free(state);
+/// Probe to see if this input looks like a request or response.
+fn probe(input: &[u8], direction: u8, rdir: *mut u8) -> bool {
+ match parse_isakmp_header(input) {
+ Ok((_, isakmp_header)) => {
+ if isakmp_header.maj_ver == 1 {
+ if isakmp_header.resp_spi == 0 && direction != STREAM_TOSERVER {
+ unsafe {
+ *rdir = STREAM_TOSERVER;
+ }
+ }
+ return true;
+ } else if isakmp_header.maj_ver == 2 {
+ if isakmp_header.min_ver != 0 {
+ SCLogDebug!(
+ "ipsec_probe: could be ipsec, but with unsupported/invalid version {}.{}",
+ isakmp_header.maj_ver,
+ isakmp_header.min_ver
+ );
+ return false;
+ }
+ if isakmp_header.exch_type < 34 || isakmp_header.exch_type > 37 {
+ SCLogDebug!("ipsec_probe: could be ipsec, but with unsupported/invalid exchange type {}",
+ isakmp_header.exch_type);
+ return false;
+ }
+ if isakmp_header.length as usize != input.len() {
+ SCLogDebug!("ipsec_probe: could be ipsec, but length does not match");
+ return false;
+ }
+
+ if isakmp_header.resp_spi == 0 && direction != STREAM_TOSERVER {
+ unsafe {
+ *rdir = STREAM_TOSERVER;
+ }
+ }
+ return true;
+ }
+
+ return false;
}
+ Err(_) => return false,
}
}
-impl Drop for IKETransaction {
- fn drop(&mut self) {
- self.free();
+// C exports.
+export_tx_get_detect_state!(rs_ike_tx_get_detect_state, IKETransaction);
+export_tx_set_detect_state!(rs_ike_tx_set_detect_state, IKETransaction);
+
+/// C entry point for a probing parser.
+#[no_mangle]
+pub extern "C" fn rs_ike_probing_parser(
+ _flow: *const Flow, direction: u8, input: *const u8, input_len: u32, rdir: *mut u8,
+) -> AppProto {
+ if input_len < 28 {
+ // at least the ISAKMP_HEADER must be there, not ALPROTO_UNKNOWN because over UDP
+ return unsafe { ALPROTO_FAILED };
+ }
+
+ if input != std::ptr::null_mut() {
+ let slice = build_slice!(input, input_len as usize);
+ if probe(slice, direction, rdir) {
+ return unsafe { ALPROTO_IKE };
+ }
}
+ return unsafe { ALPROTO_FAILED };
}
-/// Returns *mut IKEState
#[no_mangle]
-pub extern "C" fn rs_ike_state_new(_orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto) -> *mut std::os::raw::c_void {
- let state = IKEState::new();
+pub extern "C" fn rs_ike_state_new(
+ _orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto,
+) -> *mut std::os::raw::c_void {
+ let state = IKEState::default();
let boxed = Box::new(state);
- return unsafe{std::mem::transmute(boxed)};
+ return unsafe { transmute(boxed) };
}
-/// Params:
-/// - state: *mut IKEState as void pointer
#[no_mangle]
pub extern "C" fn rs_ike_state_free(state: *mut std::os::raw::c_void) {
// Just unbox...
- let mut ike_state: Box<IKEState> = unsafe{std::mem::transmute(state)};
- ike_state.free();
+ let _drop: Box<IKEState> = unsafe { transmute(state) };
}
#[no_mangle]
-pub extern "C" fn rs_ike_parse_request(_flow: *const core::Flow,
- state: *mut std::os::raw::c_void,
- _pstate: *mut std::os::raw::c_void,
- input: *const u8,
- input_len: u32,
- _data: *const std::os::raw::c_void,
- _flags: u8) -> AppLayerResult {
- let buf = build_slice!(input,input_len as usize);
- let state = cast_pointer!(state,IKEState);
- if state.parse(buf, STREAM_TOSERVER) < 0 {
- return AppLayerResult::err();
- }
- return AppLayerResult::ok();
+pub extern "C" fn rs_ike_state_tx_free(state: *mut std::os::raw::c_void, tx_id: u64) {
+ let state = cast_pointer!(state, IKEState);
+ state.free_tx(tx_id);
}
#[no_mangle]
-pub extern "C" fn rs_ike_parse_response(_flow: *const core::Flow,
- state: *mut std::os::raw::c_void,
- pstate: *mut std::os::raw::c_void,
- input: *const u8,
- input_len: u32,
- _data: *const std::os::raw::c_void,
- _flags: u8) -> AppLayerResult {
- let buf = build_slice!(input,input_len as usize);
- let state = cast_pointer!(state,IKEState);
- let res = state.parse(buf, STREAM_TOCLIENT);
- if state.connection_state == IKEConnectionState::ParsingDone {
- unsafe{
- AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_NO_INSPECTION |
- APP_LAYER_PARSER_NO_REASSEMBLY |
- APP_LAYER_PARSER_BYPASS_READY)
- };
- }
- if res < 0 {
- return AppLayerResult::err();
- }
- return AppLayerResult::ok();
+pub extern "C" fn rs_ike_parse_request(
+ _flow: *const Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void,
+ input: *const u8, input_len: u32, _data: *const std::os::raw::c_void, _flags: u8,
+) -> AppLayerResult {
+ let state = cast_pointer!(state, IKEState);
+ let buf = build_slice!(input, input_len as usize);
+
+ return state.handle_input(buf, STREAM_TOSERVER);
}
#[no_mangle]
-pub extern "C" fn rs_ike_state_get_tx(state: *mut std::os::raw::c_void,
- tx_id: u64)
- -> *mut std::os::raw::c_void
-{
- let state = cast_pointer!(state,IKEState);
- match state.get_tx_by_id(tx_id) {
- Some(tx) => unsafe{std::mem::transmute(tx)},
- None => std::ptr::null_mut(),
- }
+pub extern "C" fn rs_ike_parse_response(
+ _flow: *const Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void,
+ input: *const u8, input_len: u32, _data: *const std::os::raw::c_void, _flags: u8,
+) -> AppLayerResult {
+ let state = cast_pointer!(state, IKEState);
+ let buf = build_slice!(input, input_len as usize);
+ return state.handle_input(buf, STREAM_TOCLIENT);
}
#[no_mangle]
-pub extern "C" fn rs_ike_state_get_tx_count(state: *mut std::os::raw::c_void)
- -> u64
-{
- let state = cast_pointer!(state,IKEState);
- state.tx_id
+pub extern "C" fn rs_ike_state_get_tx(
+ state: *mut std::os::raw::c_void, tx_id: u64,
+) -> *mut std::os::raw::c_void {
+ let state = cast_pointer!(state, IKEState);
+ match state.get_tx(tx_id) {
+ Some(tx) => {
+ return unsafe { transmute(tx) };
+ }
+ None => {
+ return std::ptr::null_mut();
+ }
+ }
}
#[no_mangle]
-pub extern "C" fn rs_ike_state_tx_free(state: *mut std::os::raw::c_void,
- tx_id: u64)
-{
- let state = cast_pointer!(state,IKEState);
- state.free_tx(tx_id);
+pub extern "C" fn rs_ike_state_get_tx_count(state: *mut std::os::raw::c_void) -> u64 {
+ let state = cast_pointer!(state, IKEState);
+ return state.tx_id;
}
#[no_mangle]
-pub extern "C" fn rs_ike_state_progress_completion_status(
- _direction: u8)
- -> std::os::raw::c_int
-{
+pub extern "C" fn rs_ike_state_progress_completion_status(_direction: u8) -> std::os::raw::c_int {
+ // This parser uses 1 to signal transaction completion status.
return 1;
}
#[no_mangle]
-pub extern "C" fn rs_ike_tx_get_alstate_progress(_tx: *mut std::os::raw::c_void,
- _direction: u8)
- -> std::os::raw::c_int
-{
- 1
+pub extern "C" fn rs_ike_tx_get_alstate_progress(
+ _tx: *mut std::os::raw::c_void, _direction: u8,
+) -> std::os::raw::c_int {
+ return 1;
}
#[no_mangle]
-pub extern "C" fn rs_ike_state_set_tx_detect_state(
- tx: *mut std::os::raw::c_void,
- de_state: &mut core::DetectEngineState) -> std::os::raw::c_int
-{
- let tx = cast_pointer!(tx,IKETransaction);
- tx.de_state = Some(de_state);
- 0
+pub extern "C" fn rs_ike_tx_get_logged(
+ _state: *mut std::os::raw::c_void, tx: *mut std::os::raw::c_void,
+) -> u32 {
+ let tx = cast_pointer!(tx, IKETransaction);
+ return tx.logged.get();
}
#[no_mangle]
-pub extern "C" fn rs_ike_state_get_tx_detect_state(
- tx: *mut std::os::raw::c_void)
- -> *mut core::DetectEngineState
-{
- let tx = cast_pointer!(tx,IKETransaction);
- match tx.de_state {
- Some(ds) => ds,
- None => std::ptr::null_mut(),
- }
+pub extern "C" fn rs_ike_tx_set_logged(
+ _state: *mut std::os::raw::c_void, tx: *mut std::os::raw::c_void, logged: u32,
+) {
+ let tx = cast_pointer!(tx, IKETransaction);
+ tx.logged.set(logged);
}
-
#[no_mangle]
-pub extern "C" fn rs_ike_state_get_events(tx: *mut std::os::raw::c_void)
- -> *mut core::AppLayerDecoderEvents
-{
+pub extern "C" fn rs_ike_state_get_events(
+ tx: *mut std::os::raw::c_void,
+) -> *mut core::AppLayerDecoderEvents {
let tx = cast_pointer!(tx, IKETransaction);
return tx.events;
}
#[no_mangle]
-pub extern "C" fn rs_ike_state_get_event_info_by_id(event_id: std::os::raw::c_int,
- event_name: *mut *const std::os::raw::c_char,
- event_type: *mut core::AppLayerEventType)
- -> i8
-{
+pub extern "C" fn rs_ike_state_get_event_info_by_id(
+ event_id: std::os::raw::c_int, event_name: *mut *const std::os::raw::c_char,
+ event_type: *mut core::AppLayerEventType,
+) -> i8 {
if let Some(e) = IkeEvent::from_i32(event_id as i32) {
let estr = match e {
- IkeEvent::MalformedData => { "malformed_data\0" },
- IkeEvent::NoEncryption => { "no_encryption\0" },
- IkeEvent::WeakCryptoEnc => { "weak_crypto_enc\0" },
- IkeEvent::WeakCryptoPRF => { "weak_crypto_prf\0" },
- IkeEvent::WeakCryptoDH => { "weak_crypto_dh\0" },
- IkeEvent::WeakCryptoAuth => { "weak_crypto_auth\0" },
- IkeEvent::WeakCryptoNoDH => { "weak_crypto_nodh\0" },
- IkeEvent::WeakCryptoNoAuth => { "weak_crypto_noauth\0" },
- IkeEvent::InvalidProposal => { "invalid_proposal\0" },
- IkeEvent::UnknownProposal => { "unknown_proposal\0" },
+ IkeEvent::MalformedData => "malformed_data\0",
+ IkeEvent::NoEncryption => "no_encryption\0",
+ IkeEvent::WeakCryptoEnc => "weak_crypto_enc\0",
+ IkeEvent::WeakCryptoPRF => "weak_crypto_prf\0",
+ IkeEvent::WeakCryptoDH => "weak_crypto_dh\0",
+ IkeEvent::WeakCryptoAuth => "weak_crypto_auth\0",
+ IkeEvent::WeakCryptoNoDH => "weak_crypto_nodh\0",
+ IkeEvent::WeakCryptoNoAuth => "weak_crypto_noauth\0",
+ IkeEvent::InvalidProposal => "invalid_proposal\0",
+ IkeEvent::UnknownProposal => "unknown_proposal\0",
+ IkeEvent::PayloadExtraData => "payload_extra_data\0",
};
- unsafe{
+ unsafe {
*event_name = estr.as_ptr() as *const std::os::raw::c_char;
*event_type = core::APP_LAYER_EVENT_TYPE_TRANSACTION;
};
}
#[no_mangle]
-pub extern "C" fn rs_ike_state_get_event_info(event_name: *const std::os::raw::c_char,
- event_id: *mut std::os::raw::c_int,
- event_type: *mut core::AppLayerEventType)
- -> std::os::raw::c_int
-{
- if event_name == std::ptr::null() { return -1; }
+pub extern "C" fn rs_ike_state_get_event_info(
+ event_name: *const std::os::raw::c_char, event_id: *mut std::os::raw::c_int,
+ event_type: *mut core::AppLayerEventType,
+) -> std::os::raw::c_int {
+ if event_name == std::ptr::null() {
+ return -1;
+ }
let c_event_name: &CStr = unsafe { CStr::from_ptr(event_name) };
let event = match c_event_name.to_str() {
Ok(s) => {
match s {
- "malformed_data" => IkeEvent::MalformedData as i32,
- "no_encryption" => IkeEvent::NoEncryption as i32,
- "weak_crypto_enc" => IkeEvent::WeakCryptoEnc as i32,
- "weak_crypto_prf" => IkeEvent::WeakCryptoPRF as i32,
- "weak_crypto_auth" => IkeEvent::WeakCryptoAuth as i32,
- "weak_crypto_dh" => IkeEvent::WeakCryptoDH as i32,
- "weak_crypto_nodh" => IkeEvent::WeakCryptoNoDH as i32,
+ "malformed_data" => IkeEvent::MalformedData as i32,
+ "no_encryption" => IkeEvent::NoEncryption as i32,
+ "weak_crypto_enc" => IkeEvent::WeakCryptoEnc as i32,
+ "weak_crypto_prf" => IkeEvent::WeakCryptoPRF as i32,
+ "weak_crypto_auth" => IkeEvent::WeakCryptoAuth as i32,
+ "weak_crypto_dh" => IkeEvent::WeakCryptoDH as i32,
+ "weak_crypto_nodh" => IkeEvent::WeakCryptoNoDH as i32,
"weak_crypto_noauth" => IkeEvent::WeakCryptoNoAuth as i32,
- "invalid_proposal" => IkeEvent::InvalidProposal as i32,
- "unknown_proposal" => IkeEvent::UnknownProposal as i32,
- _ => -1, // unknown event
+ "invalid_proposal" => IkeEvent::InvalidProposal as i32,
+ "unknown_proposal" => IkeEvent::UnknownProposal as i32,
+ "payload_extra_data" => IkeEvent::PayloadExtraData as i32,
+ _ => -1, // unknown event
}
- },
+ }
Err(_) => -1, // UTF-8 conversion failed
};
- unsafe{
+ unsafe {
*event_type = core::APP_LAYER_EVENT_TYPE_TRANSACTION;
*event_id = event as std::os::raw::c_int;
};
static mut ALPROTO_IKE : AppProto = ALPROTO_UNKNOWN;
#[no_mangle]
-pub extern "C" fn rs_ike_probing_parser(_flow: *const Flow,
- _direction: u8,
- input:*const u8, input_len: u32,
- _rdir: *mut u8) -> AppProto
-{
- let slice = build_slice!(input,input_len as usize);
- let alproto = unsafe{ ALPROTO_IKE };
- match parse_ikev2_header(slice) {
- Ok((_, ref hdr)) => {
- if hdr.maj_ver != 2 || hdr.min_ver != 0 {
- SCLogDebug!("ipsec_probe: could be ipsec, but with unsupported/invalid version {}.{}",
- hdr.maj_ver, hdr.min_ver);
- return unsafe{ALPROTO_FAILED};
- }
- if hdr.exch_type.0 < 34 || hdr.exch_type.0 > 37 {
- SCLogDebug!("ipsec_probe: could be ipsec, but with unsupported/invalid exchange type {}",
- hdr.exch_type.0);
- return unsafe{ALPROTO_FAILED};
- }
- if hdr.length as usize != slice.len() {
- SCLogDebug!("ipsec_probe: could be ipsec, but length does not match");
- return unsafe{ALPROTO_FAILED};
- }
- return alproto;
- },
- Err(nom::Err::Incomplete(_)) => {
- return ALPROTO_UNKNOWN;
- },
- Err(_) => {
- return unsafe{ALPROTO_FAILED};
- },
+pub extern "C" fn rs_ike_state_get_tx_iterator(
+ _ipproto: u8, _alproto: AppProto, state: *mut std::os::raw::c_void, min_tx_id: u64,
+ _max_tx_id: u64, istate: &mut u64,
+) -> applayer::AppLayerGetTxIterTuple {
+ let state = cast_pointer!(state, IKEState);
+ match state.tx_iterator(min_tx_id, istate) {
+ Some((tx, out_tx_id, has_next)) => {
+ let c_tx = unsafe { transmute(tx) };
+ let ires = applayer::AppLayerGetTxIterTuple::with_values(c_tx, out_tx_id, has_next);
+ return ires;
+ }
+ None => {
+ return applayer::AppLayerGetTxIterTuple::not_found();
+ }
}
}
-export_tx_data_get!(rs_ike_get_tx_data, IKETransaction);
+// Parser name as a C style string.
+const PARSER_NAME: &'static [u8] = b"ike\0";
+const PARSER_ALIAS: &'static [u8] = b"ikev2\0";
-const PARSER_NAME : &'static [u8] = b"ike\0";
+export_tx_data_get!(rs_ike_get_tx_data, IKETransaction);
#[no_mangle]
-pub unsafe extern "C" fn rs_register_ike_parser() {
+pub unsafe extern "C" fn rs_ike_register_parser() {
let default_port = CString::new("500").unwrap();
let parser = RustParser {
name : PARSER_NAME.as_ptr() as *const std::os::raw::c_char,
tx_comp_st_ts : 1,
tx_comp_st_tc : 1,
tx_get_progress : rs_ike_tx_get_alstate_progress,
- get_de_state : rs_ike_state_get_tx_detect_state,
- set_de_state : rs_ike_state_set_tx_detect_state,
+ get_de_state : rs_ike_tx_get_detect_state,
+ set_de_state : rs_ike_tx_set_detect_state,
get_events : Some(rs_ike_state_get_events),
get_eventinfo : Some(rs_ike_state_get_event_info),
get_eventinfo_byid : Some(rs_ike_state_get_event_info_by_id),
};
let ip_proto_str = CString::new("udp").unwrap();
+
if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
let alproto = AppLayerRegisterProtocolDetection(&parser, 1);
- // store the allocated ID for the probe function
ALPROTO_IKE = alproto;
if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
let _ = AppLayerRegisterParser(&parser, alproto);
}
+
+ AppLayerRegisterParserAlias(
+ PARSER_NAME.as_ptr() as *const std::os::raw::c_char,
+ PARSER_ALIAS.as_ptr() as *const std::os::raw::c_char,
+ );
+ SCLogDebug!("Rust IKE parser registered.");
} else {
SCLogDebug!("Protocol detector and parser disabled for IKE.");
}
}
-
-
-#[cfg(test)]
-mod tests {
- use super::IKEState;
-
- #[test]
- fn test_ike_parse_request_valid() {
- // A UDP IKE v4 request, in client mode
- const REQ : &[u8] = &[
- 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x20, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x18, 0x57, 0xab, 0xc3, 0x4a, 0x5f, 0x2c, 0xfe
- ];
-
- let mut state = IKEState::new();
- assert_eq!(1, state.parse(REQ, 0));
- }
-}
--- /dev/null
+/* Copyright (C) 2020 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+// Author: Frank Honza <frank.honza@dcso.de>
+
+use crate::applayer::*;
+use crate::common::to_hex;
+use crate::core::STREAM_TOSERVER;
+use crate::ike::ike::{IKEState, IkeEvent};
+use crate::ike::parser::*;
+use nom;
+use std;
+use std::collections::HashSet;
+
+#[derive(Default)]
+pub struct IkeV1Header {
+ pub exchange_type: Option<u8>,
+ pub encrypted_payloads: bool,
+
+ pub key_exchange: Vec<u8>,
+ pub nonce: Vec<u8>,
+ pub vendor_ids: Vec<String>,
+}
+
+#[derive(Default)]
+pub struct Ikev1ParticipantData {
+ pub key_exchange: String,
+ pub nonce: String,
+ pub vendor_ids: HashSet<String>,
+ /// nested Vec, outer Vec per Proposal/Transform, inner Vec has the list of attributes.
+ pub transforms: Vec<Vec<SaAttribute>>,
+}
+
+impl Ikev1ParticipantData {
+ pub fn reset(&mut self) {
+ self.key_exchange.clear();
+ self.nonce.clear();
+ self.vendor_ids.clear();
+ self.transforms.clear();
+ }
+
+ pub fn update(
+ &mut self, key_exchange: &String, nonce: &String, vendor_ids: &Vec<String>,
+ transforms: &Vec<Vec<SaAttribute>>,
+ ) {
+ self.key_exchange = key_exchange.clone();
+ self.nonce = nonce.clone();
+ self.vendor_ids.extend(vendor_ids.iter().cloned());
+ self.transforms.extend(transforms.iter().cloned());
+ }
+}
+
+#[derive(Default)]
+pub struct Ikev1Container {
+ pub domain_of_interpretation: Option<u32>,
+ pub client: Ikev1ParticipantData,
+ pub server: Ikev1ParticipantData,
+}
+
+pub fn handle_ikev1(
+ state: &mut IKEState, current: &[u8], isakmp_header: IsakmpHeader, direction: u8,
+) -> AppLayerResult {
+ let mut tx = state.new_tx();
+
+ tx.ike_version = 1;
+ tx.hdr.spi_initiator = format!("{:016x}", isakmp_header.init_spi);
+ tx.hdr.spi_responder = format!("{:016x}", isakmp_header.resp_spi);
+ tx.hdr.maj_ver = isakmp_header.maj_ver;
+ tx.hdr.min_ver = isakmp_header.min_ver;
+ tx.hdr.ikev1_header.exchange_type = Some(isakmp_header.exch_type);
+ tx.hdr.msg_id = isakmp_header.msg_id;
+ tx.hdr.flags = isakmp_header.flags;
+
+ let mut cur_payload_type = isakmp_header.next_payload;
+ let mut payload_types: HashSet<u8> = HashSet::new();
+ payload_types.insert(cur_payload_type);
+
+ if isakmp_header.flags & 0x01 != 0x01 {
+ match parse_ikev1_payload_list(current) {
+ Ok((rem, payload_list)) => {
+ for isakmp_payload in payload_list {
+ if let Err(_) = parse_payload(
+ cur_payload_type,
+ isakmp_payload.data,
+ isakmp_payload.data.len() as u16,
+ &mut state.ikev1_container.domain_of_interpretation,
+ &mut tx.hdr.ikev1_header.key_exchange,
+ &mut tx.hdr.ikev1_header.nonce,
+ &mut tx.hdr.ikev1_transforms,
+ &mut tx.hdr.ikev1_header.vendor_ids,
+ &mut payload_types,
+ ) {
+ SCLogDebug!("Error while parsing IKEV1 payloads");
+ return AppLayerResult::err();
+ }
+
+ cur_payload_type = isakmp_payload.payload_header.next_payload;
+ }
+
+ if payload_types.contains(&(IsakmpPayloadType::SecurityAssociation as u8)) {
+ // clear transforms on a new SA in case there is happening a new key exchange
+ // on the same flow, elsewise properties would be added to the old/other SA
+ if direction == STREAM_TOSERVER {
+ state.ikev1_container.client.reset();
+ } else {
+ state.ikev1_container.server.reset();
+ }
+ }
+
+ // add transaction values to state values
+ if direction == STREAM_TOSERVER {
+ state.ikev1_container.client.update(
+ &to_hex(tx.hdr.ikev1_header.key_exchange.as_ref()),
+ &to_hex(tx.hdr.ikev1_header.nonce.as_ref()),
+ &tx.hdr.ikev1_header.vendor_ids,
+ &tx.hdr.ikev1_transforms,
+ );
+ } else {
+ state.ikev1_container.server.update(
+ &to_hex(tx.hdr.ikev1_header.key_exchange.as_ref()),
+ &to_hex(tx.hdr.ikev1_header.nonce.as_ref()),
+ &tx.hdr.ikev1_header.vendor_ids,
+ &tx.hdr.ikev1_transforms,
+ );
+ }
+
+ if rem.len() > 0 {
+ // more data left unread than should be
+ SCLogDebug!("Unread Payload Data");
+ state.set_event(IkeEvent::PayloadExtraData);
+ }
+ }
+ Err(nom::Err::Incomplete(_)) => {
+ SCLogDebug!("Insufficient data while parsing IKEV1");
+ return AppLayerResult::err();
+ }
+ Err(_) => {
+ SCLogDebug!("Error while parsing payloads and adding to the state");
+ return AppLayerResult::err();
+ }
+ }
+ }
+
+ tx.payload_types.ikev1_payload_types = Some(payload_types);
+ tx.hdr.ikev1_header.encrypted_payloads = isakmp_header.flags & 0x01 == 0x01;
+ state.transactions.push(tx);
+ return AppLayerResult::ok();
+}
--- /dev/null
+/* Copyright (C) 2017-2020 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+// written by Pierre Chifflier <chifflier@wzdftpd.net>
+
+use crate::applayer::*;
+use crate::core::STREAM_TOCLIENT;
+use crate::ike::ipsec_parser::*;
+
+use super::ipsec_parser::IkeV2Transform;
+use crate::ike::ike::{IKEState, IkeEvent};
+use crate::ike::parser::IsakmpHeader;
+use ipsec_parser::{IkeExchangeType, IkePayloadType, IkeV2Header};
+
+#[derive(Clone, Debug, PartialEq)]
+#[repr(u8)]
+pub enum IKEV2ConnectionState {
+ Init,
+ InitSASent,
+ InitKESent,
+ InitNonceSent,
+ RespSASent,
+ RespKESent,
+ RespNonceSent,
+ RespCertReqSent,
+
+ ParsingDone,
+
+ Invalid,
+}
+
+impl IKEV2ConnectionState {
+ pub fn advance(&self, payload: &IkeV2Payload) -> IKEV2ConnectionState {
+ use self::IKEV2ConnectionState::*;
+ match (self, &payload.content) {
+ (&Init, &IkeV2PayloadContent::SA(_)) => InitSASent,
+ (&InitSASent, &IkeV2PayloadContent::KE(_)) => InitKESent,
+ (&InitKESent, &IkeV2PayloadContent::Nonce(_)) => InitNonceSent,
+ (&InitNonceSent, &IkeV2PayloadContent::SA(_)) => RespSASent,
+ (&RespSASent, &IkeV2PayloadContent::KE(_)) => RespKESent,
+ (&RespKESent, &IkeV2PayloadContent::Nonce(_)) => ParsingDone, // RespNonceSent,
+ (&RespNonceSent, &IkeV2PayloadContent::CertificateRequest(_)) => ParsingDone, // RespCertReqSent,
+ (&ParsingDone, _) => self.clone(),
+ (_, &IkeV2PayloadContent::Notify(_)) => self.clone(),
+ (_, &IkeV2PayloadContent::Dummy) => self.clone(),
+ (_, _) => Invalid,
+ }
+ }
+}
+
+pub struct Ikev2Container {
+ /// The connection state
+ pub connection_state: IKEV2ConnectionState,
+
+ /// The transforms proposed by the initiator
+ pub client_transforms: Vec<Vec<IkeV2Transform>>,
+
+ /// The transforms selected by the responder
+ pub server_transforms: Vec<Vec<IkeV2Transform>>,
+
+ /// The encryption algorithm selected by the responder
+ pub alg_enc: IkeTransformEncType,
+ /// The authentication algorithm selected by the responder
+ pub alg_auth: IkeTransformAuthType,
+ /// The PRF algorithm selected by the responder
+ pub alg_prf: IkeTransformPRFType,
+ /// The Diffie-Hellman algorithm selected by the responder
+ pub alg_dh: IkeTransformDHType,
+ /// The extended sequence numbers parameter selected by the responder
+ pub alg_esn: IkeTransformESNType,
+ /// The Diffie-Hellman group from the server KE message, if present.
+ pub dh_group: IkeTransformDHType,
+}
+
+impl Default for Ikev2Container {
+ fn default() -> Ikev2Container {
+ Ikev2Container {
+ connection_state: IKEV2ConnectionState::Init,
+ dh_group: IkeTransformDHType::None,
+ client_transforms: Vec::new(),
+ server_transforms: Vec::new(),
+ alg_enc: IkeTransformEncType::ENCR_NULL,
+ alg_auth: IkeTransformAuthType::NONE,
+ alg_prf: IkeTransformPRFType::PRF_NULL,
+ alg_dh: IkeTransformDHType::None,
+ alg_esn: IkeTransformESNType::NoESN,
+ }
+ }
+}
+
+pub fn handle_ikev2(
+ mut state: &mut IKEState, current: &[u8], isakmp_header: IsakmpHeader, direction: u8,
+) -> AppLayerResult {
+ let hdr = IkeV2Header {
+ init_spi: isakmp_header.init_spi,
+ resp_spi: isakmp_header.resp_spi,
+ next_payload: IkePayloadType(isakmp_header.next_payload),
+ maj_ver: isakmp_header.maj_ver,
+ min_ver: isakmp_header.min_ver,
+ exch_type: IkeExchangeType(isakmp_header.exch_type),
+ flags: isakmp_header.flags,
+ msg_id: isakmp_header.msg_id,
+ length: isakmp_header.length,
+ };
+
+ let mut tx = state.new_tx();
+ tx.ike_version = 2;
+ // use init_spi as transaction identifier
+ // tx.xid = hdr.init_spi; todo is this used somewhere?
+ tx.hdr.ikev2_header = hdr.clone();
+ tx.hdr.spi_initiator = format!("{:016x}", isakmp_header.init_spi);
+ tx.hdr.spi_responder = format!("{:016x}", isakmp_header.resp_spi);
+ tx.hdr.maj_ver = isakmp_header.maj_ver;
+ tx.hdr.min_ver = isakmp_header.min_ver;
+ tx.hdr.msg_id = isakmp_header.msg_id;
+ tx.hdr.flags = isakmp_header.flags;
+ state.transactions.push(tx);
+ let mut payload_types = Vec::new();
+ let mut errors = 0;
+ let mut notify_types = Vec::new();
+ match parse_ikev2_payload_list(current, hdr.next_payload) {
+ Ok((_, Ok(ref p))) => {
+ for payload in p {
+ payload_types.push(payload.hdr.next_payload_type);
+ match payload.content {
+ IkeV2PayloadContent::Dummy => (),
+ IkeV2PayloadContent::SA(ref prop) => {
+ // if hdr.flags & IKEV2_FLAG_INITIATOR != 0 {
+ add_proposals(state, prop, direction);
+ // }
+ }
+ IkeV2PayloadContent::KE(ref kex) => {
+ SCLogDebug!("KEX {:?}", kex.dh_group);
+ if direction == STREAM_TOCLIENT {
+ state.ikev2_container.dh_group = kex.dh_group;
+ }
+ }
+ IkeV2PayloadContent::Nonce(ref n) => {
+ SCLogDebug!("Nonce: {:?}", n);
+ }
+ IkeV2PayloadContent::Notify(ref n) => {
+ SCLogDebug!("Notify: {:?}", n);
+ if n.notify_type.is_error() {
+ errors += 1;
+ }
+ notify_types.push(n.notify_type);
+ }
+ // XXX CertificateRequest
+ // XXX Certificate
+ // XXX Authentication
+ // XXX TSi
+ // XXX TSr
+ // XXX IDr
+ _ => {
+ SCLogDebug!("Unknown payload content {:?}", payload.content);
+ }
+ }
+ state.ikev2_container.connection_state =
+ state.ikev2_container.connection_state.advance(payload);
+ if let Some(tx) = state.transactions.last_mut() {
+ // borrow back tx to update it
+ tx.payload_types
+ .ikev2_payload_types
+ .append(&mut payload_types);
+ tx.errors = errors;
+ tx.notify_types.append(&mut notify_types);
+
+ if direction == STREAM_TOCLIENT
+ && state.ikev2_container.server_transforms.len() > 0
+ {
+ tx.hdr.ikev2_transforms.clear();
+ for transforms in &state.ikev2_container.server_transforms {
+ let mut proposal = Vec::new();
+ transforms.iter().for_each(|t| match *t {
+ IkeV2Transform::Encryption(ref e) => {
+ proposal.push(IkeV2Transform::Encryption(*e))
+ }
+ IkeV2Transform::Auth(ref e) => {
+ proposal.push(IkeV2Transform::Auth(*e))
+ }
+ IkeV2Transform::PRF(ref e) => {
+ proposal.push(IkeV2Transform::PRF(*e))
+ }
+ IkeV2Transform::DH(ref e) => proposal.push(IkeV2Transform::DH(*e)),
+ IkeV2Transform::ESN(ref e) => {
+ proposal.push(IkeV2Transform::ESN(*e))
+ }
+ _ => (),
+ });
+ tx.hdr.ikev2_transforms.push(proposal);
+ }
+ }
+ }
+ }
+ }
+ e => {
+ SCLogDebug!("parse_ikev2_payload_with_type: {:?}", e);
+ ()
+ }
+ }
+ return AppLayerResult::ok();
+}
+
+fn add_proposals(state: &mut IKEState, prop: &Vec<IkeV2Proposal>, direction: u8) {
+ for ref p in prop {
+ let transforms: Vec<IkeV2Transform> = p.transforms.iter().map(|x| x.into()).collect();
+ // Rule 1: warn on weak or unknown transforms
+ for xform in &transforms {
+ match *xform {
+ IkeV2Transform::Encryption(ref enc) => {
+ match *enc {
+ IkeTransformEncType::ENCR_DES_IV64
+ | IkeTransformEncType::ENCR_DES
+ | IkeTransformEncType::ENCR_3DES
+ | IkeTransformEncType::ENCR_RC5
+ | IkeTransformEncType::ENCR_IDEA
+ | IkeTransformEncType::ENCR_CAST
+ | IkeTransformEncType::ENCR_BLOWFISH
+ | IkeTransformEncType::ENCR_3IDEA
+ | IkeTransformEncType::ENCR_DES_IV32
+ | IkeTransformEncType::ENCR_NULL => {
+ SCLogDebug!("Weak Encryption: {:?}", enc);
+ // XXX send event only if direction == STREAM_TOCLIENT ?
+ state.set_event(IkeEvent::WeakCryptoEnc);
+ }
+ _ => (),
+ }
+ }
+ IkeV2Transform::PRF(ref prf) => match *prf {
+ IkeTransformPRFType::PRF_NULL => {
+ SCLogDebug!("'Null' PRF transform proposed");
+ state.set_event(IkeEvent::InvalidProposal);
+ }
+ IkeTransformPRFType::PRF_HMAC_MD5 | IkeTransformPRFType::PRF_HMAC_SHA1 => {
+ SCLogDebug!("Weak PRF: {:?}", prf);
+ state.set_event(IkeEvent::WeakCryptoPRF);
+ }
+ _ => (),
+ },
+ IkeV2Transform::Auth(ref auth) => {
+ match *auth {
+ IkeTransformAuthType::NONE => {
+ // Note: this could be expected with an AEAD encription alg.
+ // See rule 4
+ ()
+ }
+ IkeTransformAuthType::AUTH_HMAC_MD5_96
+ | IkeTransformAuthType::AUTH_HMAC_SHA1_96
+ | IkeTransformAuthType::AUTH_DES_MAC
+ | IkeTransformAuthType::AUTH_KPDK_MD5
+ | IkeTransformAuthType::AUTH_AES_XCBC_96
+ | IkeTransformAuthType::AUTH_HMAC_MD5_128
+ | IkeTransformAuthType::AUTH_HMAC_SHA1_160 => {
+ SCLogDebug!("Weak auth: {:?}", auth);
+ state.set_event(IkeEvent::WeakCryptoAuth);
+ }
+ _ => (),
+ }
+ }
+ IkeV2Transform::DH(ref dh) => match *dh {
+ IkeTransformDHType::None => {
+ SCLogDebug!("'None' DH transform proposed");
+ state.set_event(IkeEvent::InvalidProposal);
+ }
+ IkeTransformDHType::Modp768
+ | IkeTransformDHType::Modp1024
+ | IkeTransformDHType::Modp1024s160
+ | IkeTransformDHType::Modp1536 => {
+ SCLogDebug!("Weak DH: {:?}", dh);
+ state.set_event(IkeEvent::WeakCryptoDH);
+ }
+ _ => (),
+ },
+ IkeV2Transform::Unknown(tx_type, tx_id) => {
+ SCLogDebug!("Unknown proposal: type={:?}, id={}", tx_type, tx_id);
+ state.set_event(IkeEvent::UnknownProposal);
+ }
+ _ => (),
+ }
+ }
+ // Rule 2: check if no DH was proposed
+ if !transforms.iter().any(|x| match *x {
+ IkeV2Transform::DH(_) => true,
+ _ => false,
+ }) {
+ SCLogDebug!("No DH transform found");
+ state.set_event(IkeEvent::WeakCryptoNoDH);
+ }
+ // Rule 3: check if proposing AH ([RFC7296] section 3.3.1)
+ if p.protocol_id == ProtocolID::AH {
+ SCLogDebug!("Proposal uses protocol AH - no confidentiality");
+ state.set_event(IkeEvent::NoEncryption);
+ }
+ // Rule 4: lack of integrity is accepted only if using an AEAD proposal
+ // Look if no auth was proposed, including if proposal is Auth::None
+ if !transforms.iter().any(|x| match *x {
+ IkeV2Transform::Auth(IkeTransformAuthType::NONE) => false,
+ IkeV2Transform::Auth(_) => true,
+ _ => false,
+ }) {
+ if !transforms.iter().any(|x| match *x {
+ IkeV2Transform::Encryption(ref enc) => enc.is_aead(),
+ _ => false,
+ }) {
+ SCLogDebug!("No integrity transform found");
+ state.set_event(IkeEvent::WeakCryptoNoAuth);
+ }
+ }
+ // Finally
+ if direction == STREAM_TOCLIENT {
+ transforms.iter().for_each(|t| match *t {
+ IkeV2Transform::Encryption(ref e) => state.ikev2_container.alg_enc = *e,
+ IkeV2Transform::Auth(ref a) => state.ikev2_container.alg_auth = *a,
+ IkeV2Transform::PRF(ref p) => state.ikev2_container.alg_prf = *p,
+ IkeV2Transform::DH(ref dh) => state.ikev2_container.alg_dh = *dh,
+ IkeV2Transform::ESN(ref e) => state.ikev2_container.alg_esn = *e,
+ _ => (),
+ });
+ SCLogDebug!("Selected transforms: {:?}", transforms);
+ state.ikev2_container.server_transforms.push(transforms);
+ } else {
+ SCLogDebug!("Proposed transforms: {:?}", transforms);
+ state.ikev2_container.client_transforms.push(transforms);
+ }
+ }
+}
+++ /dev/null
-/* Copyright (C) 2018-2020 Open Information Security Foundation
- *
- * You can copy, redistribute or modify this Program under the terms of
- * the GNU General Public License version 2 as published by the Free
- * Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * version 2 along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- */
-
-// written by Pierre Chifflier <chifflier@wzdftpd.net>
-
-use crate::jsonbuilder::{JsonBuilder, JsonError};
-use crate::ike::ike::{IKEState,IKETransaction};
-
-use crate::ike::ipsec_parser::IKEV2_FLAG_INITIATOR;
-
-fn ike_log_response(state: &mut IKEState,
- tx: &mut IKETransaction,
- jb: &mut JsonBuilder)
- -> Result<(), JsonError>
-{
- jb.set_uint("version_major", tx.hdr.maj_ver as u64)?;
- jb.set_uint("version_minor", tx.hdr.min_ver as u64)?;
- jb.set_uint("exchange_type", tx.hdr.exch_type.0 as u64)?;
- jb.set_uint("message_id", tx.hdr.msg_id as u64)?;
- jb.set_string("init_spi", &format!("{:016x}", tx.hdr.init_spi))?;
- jb.set_string("resp_spi", &format!("{:016x}", tx.hdr.resp_spi))?;
- if tx.hdr.flags & IKEV2_FLAG_INITIATOR != 0 {
- jb.set_string("role", &"initiator")?;
- } else {
- jb.set_string("role", &"responder")?;
- jb.set_string("alg_enc", &format!("{:?}", state.alg_enc))?;
- jb.set_string("alg_auth", &format!("{:?}", state.alg_auth))?;
- jb.set_string("alg_prf", &format!("{:?}", state.alg_prf))?;
- jb.set_string("alg_dh", &format!("{:?}", state.alg_dh))?;
- jb.set_string("alg_esn", &format!("{:?}", state.alg_esn))?;
- }
- jb.set_uint("errors", tx.errors as u64)?;
- jb.open_array("payload")?;
- for payload in tx.payload_types.iter() {
- jb.append_string(&format!("{:?}", payload))?;
- }
- jb.close()?;
- jb.open_array("notify")?;
- for notify in tx.notify_types.iter() {
- jb.append_string(&format!("{:?}", notify))?;
- }
- jb.close()?;
- Ok(())
-}
-
-#[no_mangle]
-pub extern "C" fn rs_ike_log_json_response(state: &mut IKEState,
- tx: &mut IKETransaction,
- jb: &mut JsonBuilder)
- -> bool
-{
- ike_log_response(state, tx, jb).is_ok()
-}
--- /dev/null
+/* Copyright (C) 2020 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+use super::ike::{IKEState, IKETransaction};
+use super::ipsec_parser::IKEV2_FLAG_INITIATOR;
+use crate::ike::parser::{ExchangeType, IsakmpPayloadType, SaAttribute};
+use crate::jsonbuilder::{JsonBuilder, JsonError};
+use std;
+use std::convert::TryFrom;
+use num_traits::FromPrimitive;
+
+const LOG_EXTENDED: u32 = 0x01;
+
+fn add_attributes(transform: &Vec<SaAttribute>, js: &mut JsonBuilder) -> Result<(), JsonError> {
+ for attribute in transform {
+ js.set_string(
+ attribute.attribute_type.to_string().as_str(),
+ format!("{}", attribute.attribute_value.to_string()).as_str(),
+ )?;
+
+ if let Some(numeric_value) = attribute.numeric_value {
+ js.set_uint(
+ format!("{}_raw", attribute.attribute_type).as_str(),
+ numeric_value as u64,
+ )?;
+ } else if let Some(hex_value) = &attribute.hex_value {
+ js.set_string(
+ format!("{}_raw", attribute.attribute_type).as_str(),
+ &hex_value,
+ )?;
+ }
+ }
+
+ return Ok(());
+}
+
+fn log_ike(
+ state: &IKEState, tx: &IKETransaction, flags: u32, jb: &mut JsonBuilder,
+) -> Result<(), JsonError> {
+ jb.open_object("ike")?;
+
+ jb.set_uint("version_major", tx.hdr.maj_ver as u64)?;
+ jb.set_uint("version_minor", tx.hdr.min_ver as u64)?;
+ jb.set_string("init_spi", &tx.hdr.spi_initiator)?;
+ jb.set_string("resp_spi", &tx.hdr.spi_responder)?;
+ jb.set_uint("message_id", tx.hdr.msg_id as u64)?;
+
+ if tx.ike_version == 1 {
+ if let Some(exchange_type) = tx.hdr.ikev1_header.exchange_type {
+ jb.set_uint("exchange_type", exchange_type as u64)?;
+ if (flags & LOG_EXTENDED) == LOG_EXTENDED {
+ if let Some(etype) = ExchangeType::from_u8(exchange_type) {
+ jb.set_string("exchange_type_verbose", etype.to_string().as_str())?;
+ };
+ }
+ }
+ } else if tx.ike_version == 2 {
+ jb.set_uint("exchange_type", tx.hdr.ikev2_header.exch_type.0 as u64)?;
+ }
+
+ if tx.ike_version == 1 {
+ let mut index = 0;
+ for server_transform in &state.ikev1_container.server.transforms {
+ if index >= 1 {
+ debug_validate_bug_on!(true);
+ break;
+ }
+ add_attributes(server_transform, jb)?;
+ index += 1;
+ }
+ } else if tx.ike_version == 2 {
+ if tx.hdr.flags & IKEV2_FLAG_INITIATOR != 0 {
+ jb.set_string("role", &"initiator")?;
+ } else {
+ jb.set_string("role", &"responder")?;
+ jb.set_string("alg_enc", &format!("{:?}", state.ikev2_container.alg_enc))?;
+ jb.set_string("alg_auth", &format!("{:?}", state.ikev2_container.alg_auth))?;
+ jb.set_string("alg_prf", &format!("{:?}", state.ikev2_container.alg_prf))?;
+ jb.set_string("alg_dh", &format!("{:?}", state.ikev2_container.alg_dh))?;
+ jb.set_string("alg_esn", &format!("{:?}", state.ikev2_container.alg_esn))?;
+ }
+ }
+
+ // payloads in packet
+ jb.open_array("payload")?;
+ if tx.ike_version == 1 {
+ if let Some(payload_types) = &tx.payload_types.ikev1_payload_types {
+ for pt in payload_types {
+ append_payload_type_extended(jb, pt)?;
+ }
+ }
+ } else if tx.ike_version == 2 {
+ for payload in tx.payload_types.ikev2_payload_types.iter() {
+ jb.append_string(&format!("{:?}", payload))?;
+ }
+ }
+ jb.close()?;
+
+ if tx.ike_version == 1 {
+ log_ikev1(state, tx, jb)?;
+ } else if tx.ike_version == 2 {
+ log_ikev2(tx, jb)?;
+ }
+ jb.close()?;
+ return Ok(());
+}
+
+fn log_ikev1(state: &IKEState, tx: &IKETransaction, jb: &mut JsonBuilder) -> Result<(), JsonError> {
+ jb.open_object("ikev1")?;
+
+ if let Some(doi) = state.ikev1_container.domain_of_interpretation {
+ jb.set_uint("doi", doi as u64)?;
+ }
+ jb.set_bool("encrypted_payloads", tx.hdr.ikev1_header.encrypted_payloads)?;
+
+ if !tx.hdr.ikev1_header.encrypted_payloads {
+ // enable logging of collected state if not-encrypted payloads
+
+ // client data
+ jb.open_object("client")?;
+ if state.ikev1_container.client.key_exchange.len() > 0 {
+ jb.set_string(
+ "key_exchange_payload",
+ &state.ikev1_container.client.key_exchange,
+ )?;
+ if let Ok(client_key_length) =
+ u64::try_from(state.ikev1_container.client.key_exchange.len())
+ {
+ jb.set_uint("key_exchange_payload_length", client_key_length / 2)?;
+ }
+ }
+ if state.ikev1_container.client.nonce.len() > 0 {
+ jb.set_string("nonce_payload", &state.ikev1_container.client.nonce)?;
+ if let Ok(client_nonce_length) = u64::try_from(state.ikev1_container.client.nonce.len())
+ {
+ jb.set_uint("nonce_payload_length", client_nonce_length / 2)?;
+ }
+ }
+
+ jb.open_array("proposals")?;
+ for client_transform in &state.ikev1_container.client.transforms {
+ jb.start_object()?;
+ add_attributes(client_transform, jb)?;
+ jb.close()?;
+ }
+ jb.close()?; // proposals
+ jb.close()?; // client
+
+ // server data
+ jb.open_object("server")?;
+ if state.ikev1_container.server.key_exchange.len() > 0 {
+ jb.set_string(
+ "key_exchange_payload",
+ &state.ikev1_container.server.key_exchange,
+ )?;
+ if let Ok(server_key_length) =
+ u64::try_from(state.ikev1_container.server.key_exchange.len())
+ {
+ jb.set_uint("key_exchange_payload_length", server_key_length / 2)?;
+ }
+ }
+ if state.ikev1_container.server.nonce.len() > 0 {
+ jb.set_string("nonce_payload", &state.ikev1_container.server.nonce)?;
+ if let Ok(server_nonce_length) = u64::try_from(state.ikev1_container.server.nonce.len())
+ {
+ jb.set_uint("nonce_payload_length", server_nonce_length / 2)?;
+ }
+ }
+ jb.close()?; // server
+
+ jb.open_array("vendor_ids")?;
+ for vendor in state
+ .ikev1_container
+ .client
+ .vendor_ids
+ .union(&state.ikev1_container.server.vendor_ids)
+ {
+ jb.append_string(vendor)?;
+ }
+ jb.close()?; // vendor_ids
+ }
+ jb.close()?;
+
+ return Ok(());
+}
+
+fn append_payload_type_extended(js: &mut JsonBuilder, pt: &u8) -> Result<(), JsonError> {
+ if let Some(v) = IsakmpPayloadType::from_u8(*pt) {
+ js.append_string(&format!("{:?}", v))?;
+ }
+ Ok(())
+}
+
+fn log_ikev2(tx: &IKETransaction, jb: &mut JsonBuilder) -> Result<(), JsonError> {
+ jb.open_object("ikev2")?;
+
+ jb.set_uint("errors", tx.errors as u64)?;
+ jb.open_array("notify")?;
+ for notify in tx.notify_types.iter() {
+ jb.append_string(&format!("{:?}", notify))?;
+ }
+ jb.close()?;
+ jb.close()?;
+ Ok(())
+}
+
+#[no_mangle]
+pub extern "C" fn rs_ike_logger_log(
+ state: &mut IKEState, tx: *mut std::os::raw::c_void, flags: u32, js: &mut JsonBuilder,
+) -> bool {
+ let tx = cast_pointer!(tx, IKETransaction);
+ log_ike(state, tx, flags, js).is_ok()
+}
-/* Copyright (C) 2017-2018 Open Information Security Foundation
+/* Copyright (C) 2017-2020 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
extern crate ipsec_parser;
+mod detect;
pub mod ike;
-pub mod state;
-pub mod log;
+mod ikev1;
+mod ikev2;
+pub mod logger;
+mod parser;
--- /dev/null
+/* Copyright (C) 2020 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+use crate::common::to_hex;
+use core::fmt;
+use nom::number::streaming::{be_u16, be_u32, be_u64, be_u8};
+use nom::*;
+use std::collections::HashSet;
+
+// Generic ISAKMP "Container" structs
+#[repr(u8)]
+#[derive(Copy, Clone, FromPrimitive)]
+pub enum ExchangeType {
+ None = 0,
+ Base = 1,
+ IdentityProtection = 2,
+ AuthenticationOnly = 3,
+ Aggressive = 4,
+ Informational = 5,
+ Transaction = 6,
+ QuickMode = 32,
+ NewGroupMode = 33,
+}
+
+impl fmt::Display for ExchangeType {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ ExchangeType::Base => write!(f, "Base"),
+ ExchangeType::IdentityProtection => write!(f, "Identity Protection"),
+ ExchangeType::AuthenticationOnly => write!(f, "Authentication Only"),
+ ExchangeType::Aggressive => write!(f, "Aggressive"),
+ ExchangeType::Informational => write!(f, "Informational"),
+ ExchangeType::Transaction => write!(f, "Transaction (Config Mode)"),
+ ExchangeType::QuickMode => write!(f, "Quick Mode"),
+ ExchangeType::NewGroupMode => write!(f, "New Group Mode"),
+ _ => write!(f, "Unknown Exchange Type"),
+ }
+ }
+}
+
+pub struct IsakmpHeader {
+ pub init_spi: u64,
+ pub resp_spi: u64,
+ pub next_payload: u8,
+ pub maj_ver: u8,
+ pub min_ver: u8,
+ pub exch_type: u8,
+ pub flags: u8,
+ pub msg_id: u32,
+ pub length: u32,
+}
+
+pub struct IsakmpPayloadHeader {
+ pub next_payload: u8,
+ pub reserved: u8,
+ pub payload_length: u16,
+}
+
+pub struct IsakmpPayload<'a> {
+ pub payload_header: IsakmpPayloadHeader,
+ pub data: &'a [u8],
+}
+
+// IKEV1 specific payloads
+
+// 1 -> Security Association
+pub struct SecurityAssociationPayload<'a> {
+ pub domain_of_interpretation: u32,
+ pub situation: Option<&'a [u8]>,
+ pub data: Option<&'a [u8]>,
+}
+
+// 2 -> Proposal
+pub struct ProposalPayload<'a> {
+ pub proposal_number: u8,
+ pub proposal_type: u8,
+ pub spi_size: u8,
+ pub number_transforms: u8,
+ pub spi: &'a [u8],
+ pub data: &'a [u8],
+}
+
+// 3 -> Transform
+pub struct TransformPayload<'a> {
+ pub transform_number: u8,
+ pub transform_type: u8,
+ pub sa_attributes: &'a [u8],
+}
+
+// 4 -> Key Exchange
+pub struct KeyExchangePayload<'a> {
+ pub key_exchange_data: &'a [u8],
+}
+
+// 5 -> Identification
+// 6 -> Certificate
+// 7 -> Certificate Request
+// 8 -> Hash
+// 9 -> Signature
+
+// 10 -> Nonce
+pub struct NoncePayload<'a> {
+ pub nonce_data: &'a [u8],
+}
+
+// 11 -> Notification
+// 12 -> Delete
+
+// 13 -> Vendor ID
+pub struct VendorPayload<'a> {
+ pub vendor_id: &'a [u8],
+}
+
+// Attributes inside Transform
+#[derive(Debug, Clone)]
+pub enum AttributeType {
+ Unknown = 0,
+ EncryptionAlgorithm = 1,
+ HashAlgorithm = 2,
+ AuthenticationMethod = 3,
+ GroupDescription = 4,
+ GroupType = 5,
+ GroupPrime = 6,
+ GroupGeneratorOne = 7,
+ GroupGeneratorTwo = 8,
+ GroupCurveA = 9,
+ GroupCurveB = 10,
+ LifeType = 11,
+ LifeDuration = 12,
+ PRF = 13,
+ KeyLength = 14,
+ FieldSize = 15,
+ GroupOrder = 16,
+}
+
+impl fmt::Display for AttributeType {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ AttributeType::EncryptionAlgorithm => write!(f, "alg_enc"),
+ AttributeType::HashAlgorithm => write!(f, "alg_hash"),
+ AttributeType::AuthenticationMethod => write!(f, "alg_auth"),
+ AttributeType::GroupDescription => write!(f, "alg_dh"),
+ AttributeType::GroupType => write!(f, "sa_group_type"),
+ AttributeType::GroupPrime => write!(f, "sa_group_prime"),
+ AttributeType::GroupGeneratorOne => write!(f, "sa_group_generator_one"),
+ AttributeType::GroupGeneratorTwo => write!(f, "sa_group_generator_two"),
+ AttributeType::GroupCurveA => write!(f, "sa_group_curve_a"),
+ AttributeType::GroupCurveB => write!(f, "sa_group_curve_b"),
+ AttributeType::LifeType => write!(f, "sa_life_type"),
+ AttributeType::LifeDuration => write!(f, "sa_life_duration"),
+ AttributeType::PRF => write!(f, "alg_prf"),
+ AttributeType::KeyLength => write!(f, "sa_key_length"),
+ AttributeType::FieldSize => write!(f, "sa_field_size"),
+ AttributeType::GroupOrder => write!(f, "sa_group_order"),
+ _ => write!(f, "unknown"),
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub enum AttributeValue {
+ // https://www.iana.org/assignments/ipsec-registry/ipsec-registry.xhtml
+ Unknown,
+ // Encryption Algorithm
+ EncDesCbc,
+ EncIdeaCbc,
+ EncBlowfishCbc,
+ EncRc5R16B64Cbc,
+ EncTripleDesCbc,
+ EncCastCbc,
+ EncAesCbc,
+ EncCamelliaCbc,
+ // Hash Algorithm
+ HashMd5,
+ HashSha,
+ HashTiger,
+ HashSha2_256,
+ HashSha2_384,
+ HashSha2_512,
+ // Authentication Method
+ AuthPreSharedKey,
+ AuthDssSignatures,
+ AuthRsaSignatures,
+ AuthEncryptionWithRsa,
+ AuthRevisedEncryptionWithRsa,
+ AuthReserved,
+ AuthEcdsaSha256,
+ AuthEcdsaSha384,
+ AuthEcdsaSha512,
+ // Group Description
+ GroupDefault768BitModp,
+ GroupAlternate1024BitModpGroup,
+ GroupEc2nOnGp2p155,
+ GroupEc2nOnGp2p185,
+ GroupModp1536Bit,
+ GroupEc2nOverGf2p163,
+ GroupEc2nOverGf2p283,
+ GroupEc2nOverGf2p409,
+ GroupEc2nOverGf2p571,
+ GroupModp2048Bit,
+ GroupModp3072Bit,
+ GroupModp4096Bit,
+ GroupModp6144Bit,
+ GroupModp8192Bit,
+ GroupRandomEcp256,
+ GroupRandomEcp384,
+ GroupRandomEcp521,
+ GroupModp1024With160BitPrime,
+ GroupModp2048With224BitPrime,
+ GroupModp2048With256BitPrime,
+ GroupRandomEcp192,
+ GroupRandomEcp224,
+ GroupBrainpoolEcp224,
+ GroupBrainpoolEcp256,
+ GroupBrainpoolEcp384,
+ GroupBrainpoolEcp512,
+ // Life Type
+ LifeTypeSeconds,
+ LifeTypeKilobytes,
+}
+
+impl fmt::Display for AttributeValue {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{:?}", self)
+ }
+}
+
+#[derive(Clone)]
+pub struct SaAttribute {
+ pub attribute_format: u8,
+ pub attribute_type: AttributeType,
+ pub attribute_value: AttributeValue,
+ pub numeric_value: Option<u32>,
+ pub hex_value: Option<String>,
+}
+
+named! {pub parse_isakmp_header<IsakmpHeader>,
+ do_parse!(
+ init_spi: be_u64 >>
+ resp_spi: be_u64 >>
+ np: be_u8 >>
+ vers: bits!(
+ tuple!(take_bits!(4u8),take_bits!(4u8))
+ ) >>
+ ex: be_u8 >>
+ flags: be_u8 >>
+ id: be_u32 >>
+ l: be_u32 >>
+ (
+ IsakmpHeader {
+ init_spi,
+ resp_spi,
+ next_payload: np,
+ maj_ver: vers.0,
+ min_ver: vers.1,
+ exch_type: ex,
+ flags,
+ msg_id: id,
+ length: l,
+ }
+ )
+ )
+}
+
+pub fn parse_security_association(i: &[u8]) -> IResult<&[u8], SecurityAssociationPayload> {
+ do_parse!(
+ i,
+ domain_of_interpretation: be_u32
+ >> situation: cond!(domain_of_interpretation == 1, take!(4))
+ >> data: cond!(
+ domain_of_interpretation == 1 && i.len() >= 8,
+ take!(i.len() - 8)
+ )
+ >> (SecurityAssociationPayload {
+ domain_of_interpretation,
+ situation,
+ data
+ })
+ )
+}
+
+pub fn parse_key_exchange(i: &[u8], length: u16) -> IResult<&[u8], KeyExchangePayload> {
+ do_parse!(
+ i,
+ key_exchange_data: take!(length) >> (KeyExchangePayload { key_exchange_data })
+ )
+}
+
+pub fn parse_proposal(i: &[u8]) -> IResult<&[u8], ProposalPayload> {
+ do_parse!(
+ i,
+ proposal_number: be_u8
+ >> proposal_type: be_u8
+ >> spi_size: be_u8
+ >> number_transforms: be_u8
+ >> spi: take!(spi_size)
+ >> payload_data:
+ cond!(
+ (i.len() as i16 - 4) - spi_size as i16 >= 0,
+ take!((i.len() as u16 - 4) - spi_size as u16)
+ )
+ >> (ProposalPayload {
+ proposal_number,
+ proposal_type,
+ spi_size,
+ number_transforms,
+ spi,
+ data: if let Some(_data) = payload_data {
+ _data
+ } else {
+ b""
+ }
+ })
+ )
+}
+
+pub fn parse_transform(i: &[u8], length: u16) -> IResult<&[u8], TransformPayload> {
+ do_parse!(
+ i,
+ transform_number: be_u8
+ >> transform_type: be_u8
+ >> be_u16
+ >> payload_data: cond!(length >= 4, take!(length - 4))
+ >> (TransformPayload {
+ transform_number,
+ transform_type,
+ sa_attributes: if let Some(_data) = payload_data {
+ _data
+ } else {
+ b""
+ }
+ })
+ )
+}
+
+pub fn parse_vendor_id(i: &[u8], length: u16) -> IResult<&[u8], VendorPayload> {
+ map!(i, take!(length), |v| VendorPayload { vendor_id: v })
+}
+
+fn get_attribute_type(v: u16) -> AttributeType {
+ match v {
+ 1 => AttributeType::EncryptionAlgorithm,
+ 2 => AttributeType::HashAlgorithm,
+ 3 => AttributeType::AuthenticationMethod,
+ 4 => AttributeType::GroupDescription,
+ 5 => AttributeType::GroupType,
+ 6 => AttributeType::GroupPrime,
+ 7 => AttributeType::GroupGeneratorOne,
+ 8 => AttributeType::GroupGeneratorTwo,
+ 9 => AttributeType::GroupCurveA,
+ 10 => AttributeType::GroupCurveB,
+ 11 => AttributeType::LifeType,
+ 12 => AttributeType::LifeDuration,
+ 13 => AttributeType::PRF,
+ 14 => AttributeType::KeyLength,
+ 15 => AttributeType::FieldSize,
+ 16 => AttributeType::GroupOrder,
+ _ => AttributeType::Unknown,
+ }
+}
+
+fn get_encryption_algorithm(v: u16) -> AttributeValue {
+ match v {
+ 1 => AttributeValue::EncDesCbc,
+ 2 => AttributeValue::EncIdeaCbc,
+ 3 => AttributeValue::EncBlowfishCbc,
+ 4 => AttributeValue::EncRc5R16B64Cbc,
+ 5 => AttributeValue::EncTripleDesCbc,
+ 6 => AttributeValue::EncCastCbc,
+ 7 => AttributeValue::EncAesCbc,
+ 8 => AttributeValue::EncCamelliaCbc,
+ _ => AttributeValue::Unknown,
+ }
+}
+
+fn get_hash_algorithm(v: u16) -> AttributeValue {
+ match v {
+ 1 => AttributeValue::HashMd5,
+ 2 => AttributeValue::HashSha,
+ 3 => AttributeValue::HashTiger,
+ 4 => AttributeValue::HashSha2_256,
+ 5 => AttributeValue::HashSha2_384,
+ 6 => AttributeValue::HashSha2_512,
+ _ => AttributeValue::Unknown,
+ }
+}
+
+fn get_authentication_method(v: u16) -> AttributeValue {
+ match v {
+ 1 => AttributeValue::AuthPreSharedKey,
+ 2 => AttributeValue::AuthDssSignatures,
+ 3 => AttributeValue::AuthRsaSignatures,
+ 4 => AttributeValue::AuthEncryptionWithRsa,
+ 5 => AttributeValue::AuthRevisedEncryptionWithRsa,
+ 6 => AttributeValue::AuthReserved,
+ 7 => AttributeValue::AuthReserved,
+ 8 => AttributeValue::AuthReserved,
+ 9 => AttributeValue::AuthEcdsaSha256,
+ 10 => AttributeValue::AuthEcdsaSha384,
+ 11 => AttributeValue::AuthEcdsaSha512,
+ _ => AttributeValue::Unknown,
+ }
+}
+
+fn get_group_description(v: u16) -> AttributeValue {
+ match v {
+ 1 => AttributeValue::GroupDefault768BitModp,
+ 2 => AttributeValue::GroupAlternate1024BitModpGroup,
+ 3 => AttributeValue::GroupEc2nOnGp2p155,
+ 4 => AttributeValue::GroupEc2nOnGp2p185,
+ 5 => AttributeValue::GroupModp1536Bit,
+ 6 => AttributeValue::GroupEc2nOverGf2p163,
+ 7 => AttributeValue::GroupEc2nOverGf2p163,
+ 8 => AttributeValue::GroupEc2nOverGf2p283,
+ 9 => AttributeValue::GroupEc2nOverGf2p283,
+ 10 => AttributeValue::GroupEc2nOverGf2p409,
+ 11 => AttributeValue::GroupEc2nOverGf2p409,
+ 12 => AttributeValue::GroupEc2nOverGf2p571,
+ 13 => AttributeValue::GroupEc2nOverGf2p571,
+ 14 => AttributeValue::GroupModp2048Bit,
+ 15 => AttributeValue::GroupModp3072Bit,
+ 16 => AttributeValue::GroupModp4096Bit,
+ 17 => AttributeValue::GroupModp6144Bit,
+ 18 => AttributeValue::GroupModp8192Bit,
+ 19 => AttributeValue::GroupRandomEcp256,
+ 20 => AttributeValue::GroupRandomEcp384,
+ 21 => AttributeValue::GroupRandomEcp521,
+ 22 => AttributeValue::GroupModp1024With160BitPrime,
+ 23 => AttributeValue::GroupModp2048With224BitPrime,
+ 24 => AttributeValue::GroupModp2048With256BitPrime,
+ 25 => AttributeValue::GroupRandomEcp192,
+ 26 => AttributeValue::GroupRandomEcp224,
+ 27 => AttributeValue::GroupBrainpoolEcp224,
+ 28 => AttributeValue::GroupBrainpoolEcp256,
+ 29 => AttributeValue::GroupBrainpoolEcp384,
+ 30 => AttributeValue::GroupBrainpoolEcp512,
+ _ => AttributeValue::Unknown,
+ }
+}
+
+named! { pub parse_sa_attribute<&[u8], Vec<SaAttribute>>,
+ many0!(
+ complete!(
+ do_parse!(
+ format: bits!(tuple!(take_bits!(1u8),take_bits!(15u16))) >>
+ attribute_length_or_value: be_u16 >> // depends on format bit: 1 -> value | 0 -> number of following bytes
+ numeric_variable_value: cond!(format.0 == 0 && attribute_length_or_value == 4, be_u32) >> // interpret as number
+ variable_attribute_value: cond!(format.0 == 0 && attribute_length_or_value != 4, take!(attribute_length_or_value)) >>
+ (
+ SaAttribute {
+ attribute_format: format.0,
+ attribute_type: get_attribute_type(format.1),
+ attribute_value : match format.1 {
+ 1 => get_encryption_algorithm(attribute_length_or_value),
+ 2 => get_hash_algorithm(attribute_length_or_value),
+ 3 => get_authentication_method(attribute_length_or_value),
+ 4 => get_group_description(attribute_length_or_value),
+ 11 => match attribute_length_or_value {
+ 1 => AttributeValue::LifeTypeSeconds,
+ 2 => AttributeValue::LifeTypeKilobytes,
+ _ => AttributeValue::Unknown
+ }
+ _ => AttributeValue::Unknown
+ },
+ numeric_value: match format.0 {
+ 1 => Some(attribute_length_or_value as u32),
+ 0 => {
+ if let Some(_numeric_variable_value) = numeric_variable_value {
+ Some(_numeric_variable_value)
+ }
+ else {
+ None
+ }
+ },
+ _ => None,
+ },
+ hex_value: match format.0 {
+ 0 => {
+ if let Some(_variable_attribute_value) = variable_attribute_value {
+ Some(to_hex(_variable_attribute_value))
+ } else {
+ None
+ }
+ }
+ _ => None,
+ }
+ }
+ )
+ )
+ )
+ )
+}
+
+pub fn parse_nonce(i: &[u8], length: u16) -> IResult<&[u8], NoncePayload> {
+ map!(i, take!(length), |v| NoncePayload { nonce_data: v })
+}
+
+named! { pub parse_ikev1_payload_list<&[u8], Vec<IsakmpPayload>>,
+ many0!(
+ complete!(
+ do_parse!(
+ next_payload: be_u8 >>
+ reserved: be_u8 >>
+ payload_length: be_u16 >>
+ payload_data: cond!(payload_length >= 4, take!(payload_length - 4)) >>
+ (
+ IsakmpPayload {
+ payload_header: IsakmpPayloadHeader {
+ next_payload,
+ reserved,
+ payload_length
+ },
+ data: if let Some(_data) = payload_data { _data } else { b"" }
+ }
+ )
+ )
+ )
+ )
+}
+
+#[derive(FromPrimitive, Debug)]
+pub enum IsakmpPayloadType {
+ None = 0,
+ SecurityAssociation = 1,
+ Proposal = 2,
+ Transform = 3,
+ KeyExchange = 4,
+ Identification = 5,
+ Certificate = 6,
+ CertificateRequest = 7,
+ Hash = 8,
+ Signature = 9,
+ Nonce = 10,
+ Notification = 11,
+ Delete = 12,
+ VendorID = 13,
+ SaKekPayload = 15,
+ SaTekPayload = 16,
+ KeyDownload = 17,
+ SequenceNumber = 18,
+ ProofOfPossession = 19,
+ NatDiscovery = 20,
+ NatOriginalAddress = 21,
+ GroupAssociatedPolicy = 22,
+}
+
+impl fmt::Display for IsakmpPayloadType {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{:?}", self)
+ }
+}
+
+pub fn parse_payload<'a>(
+ payload_type: u8, data: &'a [u8], data_length: u16, domain_of_interpretation: &mut Option<u32>,
+ key_exchange: &mut Vec<u8>, nonce: &mut Vec<u8>, transforms: &mut Vec<Vec<SaAttribute>>,
+ vendor_ids: &mut Vec<String>, payload_types: &mut HashSet<u8>,
+) -> Result<(), ()> {
+ payload_types.insert(payload_type);
+
+ let element = num::FromPrimitive::from_u8(payload_type);
+ match element {
+ Some(IsakmpPayloadType::SecurityAssociation) => {
+ if let Err(_) = parse_security_association_payload(
+ data,
+ data_length,
+ domain_of_interpretation,
+ key_exchange,
+ nonce,
+ transforms,
+ vendor_ids,
+ payload_types,
+ ) {
+ SCLogDebug!("Error parsing SecurityAssociation");
+ return Err(());
+ }
+ Ok(())
+ }
+ Some(IsakmpPayloadType::Proposal) => {
+ if let Err(_) = parse_proposal_payload(
+ data,
+ data_length,
+ domain_of_interpretation,
+ key_exchange,
+ nonce,
+ transforms,
+ vendor_ids,
+ payload_types,
+ ) {
+ SCLogDebug!("Error parsing Proposal");
+ return Err(());
+ }
+ Ok(())
+ }
+ Some(IsakmpPayloadType::Transform) => {
+ if let Ok((_rem, payload)) = parse_transform(data, data_length) {
+ if let Ok((_, attribute_list)) = parse_sa_attribute(payload.sa_attributes) {
+ transforms.push(attribute_list);
+ }
+ }
+ Ok(())
+ }
+ Some(IsakmpPayloadType::KeyExchange) => {
+ let res = parse_key_exchange(data, data_length);
+ if let Ok((_rem, payload)) = res {
+ *key_exchange = Vec::from(payload.key_exchange_data);
+ }
+ Ok(())
+ }
+ Some(IsakmpPayloadType::Nonce) => {
+ let res = parse_nonce(data, data_length);
+ if let Ok((_rem, payload)) = res {
+ *nonce = Vec::from(payload.nonce_data);
+ }
+ Ok(())
+ }
+ Some(IsakmpPayloadType::VendorID) => {
+ let res = parse_vendor_id(data, data_length);
+ if let Ok((_rem, payload)) = res {
+ vendor_ids.push(to_hex(payload.vendor_id));
+ }
+ Ok(())
+ }
+ _ => Ok(()),
+ }
+}
+
+fn parse_proposal_payload<'a>(
+ data: &'a [u8], data_length: u16, domain_of_interpretation: &mut Option<u32>,
+ key_exchange: &mut Vec<u8>, nonce: &mut Vec<u8>, transforms: &mut Vec<Vec<SaAttribute>>,
+ vendor_ids: &mut Vec<String>, payload_types: &mut HashSet<u8>,
+) -> Result<(), ()> {
+ match parse_proposal(&data[0..data_length as usize]) {
+ Ok((_rem, payload)) => {
+ let mut cur_payload_type = IsakmpPayloadType::Transform as u8;
+ match parse_ikev1_payload_list(payload.data) {
+ Ok((_, payload_list)) => {
+ for isakmp_payload in payload_list {
+ if let Err(_) = parse_payload(
+ cur_payload_type,
+ isakmp_payload.data,
+ isakmp_payload.data.len() as u16,
+ domain_of_interpretation,
+ key_exchange,
+ nonce,
+ transforms,
+ vendor_ids,
+ payload_types,
+ ) {
+ SCLogDebug!("Error parsing transform payload");
+ return Err(());
+ }
+ cur_payload_type = isakmp_payload.payload_header.next_payload;
+ }
+ Ok(())
+ }
+ Err(nom::Err::Incomplete(_)) => {
+ SCLogDebug!("Incomplete data parsing payload list");
+ Err(())
+ }
+ Err(_) => {
+ SCLogDebug!("Error parsing payload list");
+ Err(())
+ }
+ }
+ }
+ Err(nom::Err::Incomplete(_)) => {
+ SCLogDebug!("Incomplete data");
+ Err(())
+ }
+ Err(_) => Err(()),
+ }
+}
+
+fn parse_security_association_payload<'a>(
+ data: &'a [u8], data_length: u16, domain_of_interpretation: &mut Option<u32>,
+ key_exchange: &mut Vec<u8>, nonce: &mut Vec<u8>, transforms: &mut Vec<Vec<SaAttribute>>,
+ vendor_ids: &mut Vec<String>, payload_types: &mut HashSet<u8>,
+) -> Result<(), ()> {
+ match parse_security_association(&data[0..data_length as usize]) {
+ Ok((_rem, payload)) => {
+ *domain_of_interpretation = Some(payload.domain_of_interpretation);
+ if payload.domain_of_interpretation == 1 {
+ // 1 is assigned to IPsec DOI
+ let mut cur_payload_type = IsakmpPayloadType::Proposal as u8;
+ if let Some(p_data) = payload.data {
+ match parse_ikev1_payload_list(p_data) {
+ Ok((_, payload_list)) => {
+ for isakmp_payload in payload_list {
+ if let Err(_) = parse_payload(
+ cur_payload_type,
+ isakmp_payload.data,
+ isakmp_payload.data.len() as u16,
+ domain_of_interpretation,
+ key_exchange,
+ nonce,
+ transforms,
+ vendor_ids,
+ payload_types,
+ ) {
+ SCLogDebug!("Error parsing proposal payload");
+ return Err(());
+ }
+ cur_payload_type = isakmp_payload.payload_header.next_payload;
+ }
+ }
+ Err(nom::Err::Incomplete(_)) => {
+ SCLogDebug!("Incomplete data parsing payload list");
+ return Err(());
+ }
+ Err(_) => {
+ SCLogDebug!("Error parsing payload list");
+ return Err(());
+ }
+ }
+ }
+ }
+ Ok(())
+ }
+ Err(nom::Err::Incomplete(_)) => {
+ SCLogDebug!("Incomplete data");
+ Err(())
+ }
+ Err(_) => Err(()),
+ }
+}
+++ /dev/null
-/* Copyright (C) 2018 Open Information Security Foundation
- *
- * You can copy, redistribute or modify this Program under the terms of
- * the GNU General Public License version 2 as published by the Free
- * Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * version 2 along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- */
-
-// written by Pierre Chifflier <chifflier@wzdftpd.net>
-
-extern crate ipsec_parser;
-use self::ipsec_parser::*;
-
-#[derive(Clone, Debug, PartialEq)]
-#[repr(u8)]
-pub enum IKEConnectionState {
- Init,
- InitSASent,
- InitKESent,
- InitNonceSent,
- RespSASent,
- RespKESent,
- RespNonceSent,
- RespCertReqSent,
-
- ParsingDone,
-
- Invalid,
-}
-
-impl IKEConnectionState {
- pub fn advance(&self, payload: &IkeV2Payload) -> IKEConnectionState {
- use self::IKEConnectionState::*;
- match (self, &payload.content) {
- (&Init, &IkeV2PayloadContent::SA(_)) => InitSASent,
- (&InitSASent, &IkeV2PayloadContent::KE(_)) => InitKESent,
- (&InitKESent, &IkeV2PayloadContent::Nonce(_)) => InitNonceSent,
- (&InitNonceSent, &IkeV2PayloadContent::SA(_)) => RespSASent,
- (&RespSASent, &IkeV2PayloadContent::KE(_)) => RespKESent,
- (&RespKESent, &IkeV2PayloadContent::Nonce(_)) => ParsingDone, // RespNonceSent,
- (&RespNonceSent, &IkeV2PayloadContent::CertificateRequest(_)) => ParsingDone, // RespCertReqSent,
- (&ParsingDone,_) => self.clone(),
- (_, &IkeV2PayloadContent::Notify(_)) => self.clone(),
- (_, &IkeV2PayloadContent::Dummy) => self.clone(),
- (_,_) => Invalid,
- }
- }
-}
detect-icmpv6-mtu.h \
detect-icode.h \
detect-id.h \
+ detect-ike-exch-type.h \
+ detect-ike-spi.h \
+ detect-ike-vendor.h \
+ detect-ike-chosen-sa.h \
+ detect-ike-key-exchange-payload-length.h \
+ detect-ike-nonce-payload-length.h \
+ detect-ike-nonce-payload.h \
+ detect-ike-key-exchange-payload.h \
detect-ipopts.h \
detect-ipproto.h \
detect-iprep.h \
detect-icmpv6-mtu.c \
detect-icode.c \
detect-id.c \
+ detect-ike-exch-type.c \
+ detect-ike-spi.c \
+ detect-ike-vendor.c \
+ detect-ike-chosen-sa.c \
+ detect-ike-key-exchange-payload-length.c \
+ detect-ike-nonce-payload-length.c \
+ detect-ike-nonce-payload.c \
+ detect-ike-key-exchange-payload.c \
detect-ipopts.c \
detect-ipproto.c \
detect-iprep.c \
-/* Copyright (C) 2015 Open Information Security Foundation
+/* Copyright (C) 2020 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* \file
*
* \author Pierre Chifflier <chifflier@wzdftpd.net>
+ * \author Frank Honza <frank.honza@dcso.de>
+ *
+ * IKE application layer detector and parser.
*
- * Parser for IKE application layer running on UDP port 500.
*/
#include "suricata-common.h"
void RegisterIKEParsers(void)
{
- rs_register_ike_parser();
+ rs_ike_register_parser();
+#ifdef UNITTESTS
+ AppLayerParserRegisterProtocolUnittests(IPPROTO_UDP, ALPROTO_IKE, IKEParserRegisterTests);
+#endif
+}
+
+#ifdef UNITTESTS
+#include "stream-tcp.h"
+#include "util-unittest-helper.h"
+#include "flow-util.h"
+
+static int IkeParserTest(void)
+{
+ uint64_t ret[4];
+
+ Flow f;
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_UDP;
+ f.protomap = FlowGetProtoMapping(f.proto);
+ f.alproto = ALPROTO_IKE;
+
+ StreamTcpInitConfig(TRUE);
+
+ static const unsigned char initiator_sa[] = { 0xe4, 0x7a, 0x59, 0x1f, 0xd0, 0x57, 0x58, 0x7f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xa8, 0x0d, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x01,
+ 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x07, 0x80, 0x0e, 0x00, 0x80, 0x80, 0x02, 0x00, 0x02,
+ 0x80, 0x04, 0x00, 0x02, 0x80, 0x03, 0x00, 0x01, 0x80, 0x0b, 0x00, 0x01, 0x00, 0x0c, 0x00,
+ 0x04, 0x00, 0x01, 0x51, 0x80, 0x0d, 0x00, 0x00, 0x14, 0x4a, 0x13, 0x1c, 0x81, 0x07, 0x03,
+ 0x58, 0x45, 0x5c, 0x57, 0x28, 0xf2, 0x0e, 0x95, 0x45, 0x2f, 0x0d, 0x00, 0x00, 0x14, 0x43,
+ 0x9b, 0x59, 0xf8, 0xba, 0x67, 0x6c, 0x4c, 0x77, 0x37, 0xae, 0x22, 0xea, 0xb8, 0xf5, 0x82,
+ 0x0d, 0x00, 0x00, 0x14, 0x7d, 0x94, 0x19, 0xa6, 0x53, 0x10, 0xca, 0x6f, 0x2c, 0x17, 0x9d,
+ 0x92, 0x15, 0x52, 0x9d, 0x56, 0x00, 0x00, 0x00, 0x14, 0x90, 0xcb, 0x80, 0x91, 0x3e, 0xbb,
+ 0x69, 0x6e, 0x08, 0x63, 0x81, 0xb5, 0xec, 0x42, 0x7b, 0x1f };
+
+ // the initiator sending the security association with proposals
+ int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_IKE, STREAM_TOSERVER | STREAM_START,
+ (uint8_t *)initiator_sa, sizeof(initiator_sa));
+ FAIL_IF_NOT(r == 0);
+
+ static const unsigned char responder_sa[] = { 0xe4, 0x7a, 0x59, 0x1f, 0xd0, 0x57, 0x58, 0x7f,
+ 0xa0, 0x0b, 0x8e, 0xf0, 0x90, 0x2b, 0xb8, 0xec, 0x01, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x6c, 0x0d, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x01,
+ 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x07, 0x80, 0x0e, 0x00, 0x80, 0x80, 0x02, 0x00, 0x02,
+ 0x80, 0x04, 0x00, 0x02, 0x80, 0x03, 0x00, 0x01, 0x80, 0x0b, 0x00, 0x01, 0x00, 0x0c, 0x00,
+ 0x04, 0x00, 0x01, 0x51, 0x80, 0x00, 0x00, 0x00, 0x14, 0x4a, 0x13, 0x1c, 0x81, 0x07, 0x03,
+ 0x58, 0x45, 0x5c, 0x57, 0x28, 0xf2, 0x0e, 0x95, 0x45, 0x2f };
+
+ // responder answering with chosen proposal
+ r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_IKE, STREAM_TOCLIENT,
+ (uint8_t *)responder_sa, sizeof(responder_sa));
+ FAIL_IF_NOT(r == 0);
+
+ static const unsigned char initiator_key[] = { 0xe4, 0x7a, 0x59, 0x1f, 0xd0, 0x57, 0x58, 0x7f,
+ 0xa0, 0x0b, 0x8e, 0xf0, 0x90, 0x2b, 0xb8, 0xec, 0x04, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x1c, 0x0a, 0x00, 0x00, 0x84, 0x35, 0x04, 0xd3, 0xd2, 0xed, 0x14,
+ 0xe0, 0xca, 0x03, 0xb8, 0x51, 0xa5, 0x1a, 0x9d, 0xa2, 0xe5, 0xa4, 0xc1, 0x4c, 0x1d, 0x7e,
+ 0xc3, 0xe1, 0xfb, 0xe9, 0x50, 0x02, 0x54, 0x24, 0x51, 0x4b, 0x3c, 0x69, 0xed, 0x7f, 0xbb,
+ 0x44, 0xe0, 0x92, 0x25, 0xda, 0x52, 0xd2, 0xa9, 0x26, 0x04, 0xa9, 0x9b, 0xf6, 0x1b, 0x7b,
+ 0xee, 0xd7, 0xfb, 0xfa, 0x63, 0x5e, 0x82, 0xf0, 0x65, 0xf4, 0xfe, 0x78, 0x07, 0x51, 0x35,
+ 0x4d, 0xbe, 0x47, 0x4c, 0x3d, 0xe7, 0x20, 0x7d, 0xcf, 0x69, 0xfd, 0xbb, 0xed, 0x32, 0xc1,
+ 0x69, 0x1c, 0xc1, 0x49, 0xb3, 0x18, 0xee, 0xe0, 0x03, 0x70, 0xe6, 0x5f, 0xc3, 0x06, 0x9b,
+ 0xba, 0xcf, 0xb0, 0x13, 0x46, 0x71, 0x73, 0x96, 0x6e, 0x9d, 0x5f, 0x4b, 0xc4, 0xf3, 0x85,
+ 0x7e, 0x35, 0x9b, 0xba, 0x3a, 0xdb, 0xb6, 0xef, 0xee, 0xa5, 0x16, 0xf3, 0x89, 0x7d, 0x85,
+ 0x34, 0xf3, 0x0d, 0x00, 0x00, 0x18, 0x89, 0xd7, 0xc8, 0xfb, 0xf9, 0x4b, 0x51, 0x5b, 0x52,
+ 0x1d, 0x5d, 0x95, 0x89, 0xc2, 0x60, 0x20, 0x21, 0xe1, 0xa7, 0x09, 0x0d, 0x00, 0x00, 0x14,
+ 0xaf, 0xca, 0xd7, 0x13, 0x68, 0xa1, 0xf1, 0xc9, 0x6b, 0x86, 0x96, 0xfc, 0x77, 0x57, 0x01,
+ 0x00, 0x0d, 0x00, 0x00, 0x14, 0x11, 0xbd, 0xfe, 0x02, 0xd0, 0x56, 0x58, 0x7f, 0x2c, 0x18,
+ 0x12, 0x59, 0x72, 0xc3, 0x24, 0x01, 0x14, 0x00, 0x00, 0x0c, 0x09, 0x00, 0x26, 0x89, 0xdf,
+ 0xd6, 0xb7, 0x12, 0x14, 0x00, 0x00, 0x18, 0x15, 0x74, 0xd6, 0x4c, 0x01, 0x65, 0xba, 0xd1,
+ 0x6a, 0x02, 0x3f, 0x03, 0x8d, 0x45, 0xa0, 0x74, 0x98, 0xd8, 0xd0, 0x51, 0x00, 0x00, 0x00,
+ 0x18, 0xfe, 0xbf, 0x46, 0x2f, 0x1c, 0xd7, 0x58, 0x05, 0xa7, 0xba, 0xa2, 0x87, 0x47, 0xe7,
+ 0x69, 0xd6, 0x74, 0xf8, 0x56, 0x00 };
+
+ // the initiator sending key exchange
+ r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_IKE, STREAM_TOSERVER,
+ (uint8_t *)initiator_key, sizeof(initiator_key));
+ FAIL_IF_NOT(r == 0);
+
+ static const unsigned char responder_key[] = { 0xe4, 0x7a, 0x59, 0x1f, 0xd0, 0x57, 0x58, 0x7f,
+ 0xa0, 0x0b, 0x8e, 0xf0, 0x90, 0x2b, 0xb8, 0xec, 0x04, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x30, 0x0a, 0x00, 0x00, 0x84, 0x6d, 0x02, 0x6d, 0x56, 0x16, 0xc4,
+ 0x5b, 0xe0, 0x5e, 0x5b, 0x89, 0x84, 0x11, 0xe9, 0xf9, 0x5d, 0x19, 0x5c, 0xea, 0x00, 0x9a,
+ 0xd2, 0x2c, 0x62, 0xbe, 0xf0, 0x6c, 0x57, 0x1b, 0x7c, 0xfb, 0xc4, 0x79, 0x2f, 0x45, 0x56,
+ 0x4e, 0xc7, 0x10, 0xac, 0x58, 0x4a, 0xa1, 0x8d, 0x20, 0xcb, 0xc8, 0xf5, 0xf8, 0x91, 0x06,
+ 0x66, 0xb8, 0x9e, 0x4e, 0xe2, 0xf9, 0x5a, 0xbc, 0x02, 0x30, 0xe2, 0xcb, 0xa1, 0xb8, 0x8a,
+ 0xc4, 0xbb, 0xa7, 0xfc, 0xc8, 0x18, 0xa9, 0x86, 0xc0, 0x1a, 0x4c, 0xa8, 0x65, 0xa5, 0xeb,
+ 0x82, 0x88, 0x4d, 0xbe, 0xc8, 0x5b, 0xfd, 0x7d, 0x1a, 0x30, 0x3b, 0x09, 0x89, 0x4d, 0xcf,
+ 0x2e, 0x37, 0x85, 0xfd, 0x79, 0xdb, 0xa2, 0x25, 0x37, 0x7c, 0xf8, 0xcc, 0xa0, 0x09, 0xce,
+ 0xff, 0xbb, 0x6a, 0xa3, 0x8b, 0x64, 0x8c, 0x4b, 0x05, 0x40, 0x4f, 0x1c, 0xfa, 0xac, 0x36,
+ 0x1a, 0xff, 0x0d, 0x00, 0x00, 0x18, 0x15, 0xb6, 0x88, 0x42, 0x1e, 0xd5, 0xc3, 0xdd, 0x92,
+ 0xd3, 0xb8, 0x6e, 0x47, 0xa7, 0x6f, 0x0d, 0x39, 0xcc, 0x09, 0xe0, 0x0d, 0x00, 0x00, 0x14,
+ 0x12, 0xf5, 0xf2, 0x8c, 0x45, 0x71, 0x68, 0xa9, 0x70, 0x2d, 0x9f, 0xe2, 0x74, 0xcc, 0x01,
+ 0x00, 0x0d, 0x00, 0x00, 0x14, 0xaf, 0xca, 0xd7, 0x13, 0x68, 0xa1, 0xf1, 0xc9, 0x6b, 0x86,
+ 0x96, 0xfc, 0x77, 0x57, 0x01, 0x00, 0x0d, 0x00, 0x00, 0x14, 0x55, 0xcc, 0x29, 0xed, 0x90,
+ 0x2a, 0xb8, 0xec, 0x53, 0xb1, 0xdf, 0x86, 0x7c, 0x61, 0x09, 0x29, 0x14, 0x00, 0x00, 0x0c,
+ 0x09, 0x00, 0x26, 0x89, 0xdf, 0xd6, 0xb7, 0x12, 0x14, 0x00, 0x00, 0x18, 0xfe, 0xbf, 0x46,
+ 0x2f, 0x1c, 0xd7, 0x58, 0x05, 0xa7, 0xba, 0xa2, 0x87, 0x47, 0xe7, 0x69, 0xd6, 0x74, 0xf8,
+ 0x56, 0x00, 0x00, 0x00, 0x00, 0x18, 0x15, 0x74, 0xd6, 0x4c, 0x01, 0x65, 0xba, 0xd1, 0x6a,
+ 0x02, 0x3f, 0x03, 0x8d, 0x45, 0xa0, 0x74, 0x98, 0xd8, 0xd0, 0x51 };
+
+ // responder sending key exchange
+ r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_IKE, STREAM_TOCLIENT,
+ (uint8_t *)responder_key, sizeof(responder_key));
+ FAIL_IF_NOT(r == 0);
+
+ static const unsigned char encrypted[] = { 0xe4, 0x7a, 0x59, 0x1f, 0xd0, 0x57, 0x58, 0x7f, 0xa0,
+ 0x0b, 0x8e, 0xf0, 0x90, 0x2b, 0xb8, 0xec, 0x05, 0x10, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x6c, 0xa4, 0x85, 0xa5, 0xd5, 0x86, 0x8a, 0x3c, 0x92, 0x5d, 0xed, 0xf2,
+ 0xd1, 0x0d, 0x5e, 0x47, 0x11, 0x2b, 0xc2, 0x94, 0x60, 0x18, 0xc7, 0x61, 0x28, 0xed, 0x7b,
+ 0xb2, 0x9d, 0xb0, 0x61, 0xfd, 0xab, 0xf7, 0x9a, 0x18, 0xe7, 0x56, 0x89, 0x53, 0x6d, 0x27,
+ 0xcb, 0xe0, 0x92, 0x1d, 0x67, 0xf7, 0x02, 0xf3, 0x47, 0xae, 0x6e, 0x79, 0xde, 0xe1, 0x09,
+ 0x4d, 0xc8, 0x6a, 0x5a, 0x26, 0x44, 0x8a, 0xde, 0x72, 0x83, 0x06, 0x94, 0xe1, 0x5d, 0xca,
+ 0x2d, 0x96, 0x03, 0xeb, 0xc5, 0xf7, 0x90, 0x47, 0x3d };
+ // the initiator sending encrypted data
+ r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_IKE, STREAM_TOSERVER, (uint8_t *)encrypted,
+ sizeof(encrypted));
+ FAIL_IF_NOT(r == 0);
+
+ AppLayerParserTransactionsCleanup(&f);
+ UTHAppLayerParserStateGetIds(f.alparser, &ret[0], &ret[1], &ret[2], &ret[3]);
+ FAIL_IF_NOT(ret[0] == 5); // inspect_id[0]
+ FAIL_IF_NOT(ret[1] == 5); // inspect_id[1]
+ FAIL_IF_NOT(ret[2] == 5); // log_id
+ FAIL_IF_NOT(ret[3] == 5); // min_id
+
+ if (alp_tctx != NULL) {
+ AppLayerParserThreadCtxFree(alp_tctx);
+ }
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ PASS;
+}
+#endif
+
+void IKEParserRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("IkeParserTest", IkeParserTest);
+#endif
}
-/* Copyright (C) 2015 Open Information Security Foundation
+/* Copyright (C) 2020 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* \file
*
* \author Pierre Chifflier <chifflier@wzdftpd.net>
+ * \author Frank Honza <frank.honza@dcso.de>
*/
#ifndef __APP_LAYER_IKE_H__
#define __APP_LAYER_IKE_H__
void RegisterIKEParsers(void);
+void IKEParserRegisterTests(void);
/** Opaque Rust types. */
typedef struct IKEState_ IKEState;
#include "detect-modbus.h"
#include "detect-cipservice.h"
#include "detect-dnp3.h"
+#include "detect-ike-exch-type.h"
+#include "detect-ike-spi.h"
+#include "detect-ike-vendor.h"
+#include "detect-ike-chosen-sa.h"
+#include "detect-ike-key-exchange-payload-length.h"
+#include "detect-ike-nonce-payload-length.h"
+#include "detect-ike-nonce-payload.h"
+#include "detect-ike-key-exchange-payload.h"
#include "action-globals.h"
#include "tm-threads.h"
DetectEnipCommandRegister();
DetectDNP3Register();
+ DetectIkeExchTypeRegister();
+ DetectIkeSpiRegister();
+ DetectIkeVendorRegister();
+ DetectIkeChosenSaRegister();
+ DetectIkeKeyExchangePayloadLengthRegister();
+ DetectIkeNoncePayloadLengthRegister();
+ DetectIkeNonceRegister();
+ DetectIkeKeyExchangeRegister();
+
DetectTlsSniRegister();
DetectTlsIssuerRegister();
DetectTlsSubjectRegister();
DETECT_TRANSFORM_PCREXFORM,
DETECT_TRANSFORM_URL_DECODE,
+ DETECT_AL_IKE_EXCH_TYPE,
+ DETECT_AL_IKE_SPI_INITIATOR,
+ DETECT_AL_IKE_SPI_RESPONDER,
+ DETECT_AL_IKE_VENDOR,
+ DETECT_AL_IKE_CHOSEN_SA,
+ DETECT_AL_IKE_KEY_EXCHANGE_PAYLOAD_LENGTH,
+ DETECT_AL_IKE_NONCE_PAYLOAD_LENGTH,
+ DETECT_AL_IKE_NONCE,
+ DETECT_AL_IKE_KEY_EXCHANGE,
+
/* make sure this stays last */
DETECT_TBLSIZE,
};
--- /dev/null
+/* Copyright (C) 2020 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ *
+ * \author Frank Honza <frank.honza@dcso.de>
+ */
+
+#include "suricata-common.h"
+#include "conf.h"
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-content-inspection.h"
+#include "detect-ike-chosen-sa.h"
+#include "app-layer-parser.h"
+#include "util-byte.h"
+#include "util-unittest.h"
+
+#include "rust-bindings.h"
+
+/**
+ * [ike.chosen_sa_attribute]:<sa_attribute>=<type>;
+ */
+
+// support the basic attributes, which are parsed as integer and life_duration, if variable length
+// is 4 it is stored as integer too
+#define PARSE_REGEX \
+ "^\\s*(alg_enc|alg_hash|alg_auth|alg_dh|\
+sa_group_type|sa_life_type|sa_life_duration|alg_prf|sa_key_length|sa_field_size)\
+\\s*=\\s*([0-9]+)\\s*$"
+
+static DetectParseRegex parse_regex;
+
+typedef struct {
+ char *sa_type;
+ uint32_t sa_value;
+} DetectIkeChosenSaData;
+
+static DetectIkeChosenSaData *DetectIkeChosenSaParse(const char *);
+static int DetectIkeChosenSaSetup(DetectEngineCtx *, Signature *s, const char *str);
+static void DetectIkeChosenSaFree(DetectEngineCtx *, void *);
+static int g_ike_chosen_sa_buffer_id = 0;
+
+static int DetectEngineInspectIkeChosenSaGeneric(DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx, const struct DetectEngineAppInspectionEngine_ *engine,
+ const Signature *s, Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id);
+
+static int DetectIkeChosenSaMatch(DetectEngineThreadCtx *, Flow *, uint8_t, void *, void *,
+ const Signature *, const SigMatchCtx *);
+void IKEChosenSaRegisterTests(void);
+
+/**
+ * \brief Registration function for ike.ChosenSa keyword.
+ */
+void DetectIkeChosenSaRegister(void)
+{
+ sigmatch_table[DETECT_AL_IKE_CHOSEN_SA].name = "ike.chosen_sa_attribute";
+ sigmatch_table[DETECT_AL_IKE_CHOSEN_SA].desc = "match IKE chosen SA Attribute";
+ sigmatch_table[DETECT_AL_IKE_CHOSEN_SA].url =
+ "/rules/ike-keywords.html#ike-chosen_sa_attribute";
+ sigmatch_table[DETECT_AL_IKE_CHOSEN_SA].AppLayerTxMatch = DetectIkeChosenSaMatch;
+ sigmatch_table[DETECT_AL_IKE_CHOSEN_SA].Setup = DetectIkeChosenSaSetup;
+ sigmatch_table[DETECT_AL_IKE_CHOSEN_SA].Free = DetectIkeChosenSaFree;
+#ifdef UNITTESTS
+ sigmatch_table[DETECT_AL_IKE_CHOSEN_SA].RegisterTests = IKEChosenSaRegisterTests;
+#endif
+ DetectSetupParseRegexes(PARSE_REGEX, &parse_regex);
+
+ DetectAppLayerInspectEngineRegister2("ike.chosen_sa_attribute", ALPROTO_IKE, SIG_FLAG_TOCLIENT,
+ 1, DetectEngineInspectIkeChosenSaGeneric, NULL);
+
+ g_ike_chosen_sa_buffer_id = DetectBufferTypeGetByName("ike.chosen_sa_attribute");
+}
+
+static int DetectEngineInspectIkeChosenSaGeneric(DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx, const struct DetectEngineAppInspectionEngine_ *engine,
+ const Signature *s, Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id)
+{
+ return DetectEngineInspectGenericList(
+ de_ctx, det_ctx, s, engine->smd, f, flags, alstate, txv, tx_id);
+}
+
+/**
+ * \internal
+ * \brief Function to match SA attributes of a IKE state
+ *
+ * \param det_ctx Pointer to the pattern matcher thread.
+ * \param f Pointer to the current flow.
+ * \param flags Flags.
+ * \param state App layer state.
+ * \param txv Pointer to the Ike Transaction.
+ * \param s Pointer to the Signature.
+ * \param ctx Pointer to the sigmatch that we will cast into DetectIkeChosenSaData.
+ *
+ * \retval 0 no match.
+ * \retval 1 match.
+ */
+static int DetectIkeChosenSaMatch(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags,
+ void *state, void *txv, const Signature *s, const SigMatchCtx *ctx)
+{
+ SCEnter();
+
+ const DetectIkeChosenSaData *dd = (const DetectIkeChosenSaData *)ctx;
+
+ uint32_t value;
+ if (!rs_ike_state_get_sa_attribute(txv, dd->sa_type, &value))
+ SCReturnInt(0);
+ if (value == dd->sa_value)
+ SCReturnInt(1);
+ SCReturnInt(0);
+}
+
+/**
+ * \internal
+ * \brief Function to parse options passed via ike.chosen_sa_attribute keywords.
+ *
+ * \param rawstr Pointer to the user provided options.
+ *
+ * \retval dd pointer to DetectIkeChosenSaData on success.
+ * \retval NULL on failure.
+ */
+static DetectIkeChosenSaData *DetectIkeChosenSaParse(const char *rawstr)
+{
+ /*
+ * idea: do not implement one c file per type, invent an own syntax:
+ * ike.chosen_sa_attribute:"encryption_algorithm=4"
+ * ike.chosen_sa_attribute:"hash_algorithm=8"
+ */
+ DetectIkeChosenSaData *dd = NULL;
+ int ret = 0, res = 0;
+ int ov[MAX_SUBSTRINGS];
+ char attribute[100];
+ char value[100];
+
+ ret = DetectParsePcreExec(&parse_regex, rawstr, 0, 0, ov, MAX_SUBSTRINGS);
+ if (ret < 3 || ret > 5) {
+ SCLogError(SC_ERR_PCRE_MATCH,
+ "pcre match for ike.chosen_sa_attribute failed, should be: <sa_attribute>=<type>, "
+ "but was: %s; error code %d",
+ rawstr, ret);
+ goto error;
+ }
+
+ res = pcre_copy_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 1, attribute, sizeof(attribute));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre_copy_substring failed");
+ goto error;
+ }
+
+ res = pcre_copy_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 2, value, sizeof(value));
+ if (res < 0) {
+ SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre_copy_substring failed");
+ goto error;
+ }
+
+ dd = SCCalloc(1, sizeof(DetectIkeChosenSaData));
+ if (unlikely(dd == NULL))
+ goto error;
+
+ dd->sa_type = SCStrdup(attribute);
+ if (dd->sa_type == NULL)
+ goto error;
+
+ if (ByteExtractStringUint32(&dd->sa_value, 10, strlen(value), value) <= 0) {
+ SCLogError(SC_ERR_INVALID_SIGNATURE, "invalid input as arg "
+ "to ike.chosen_sa_attribute keyword");
+ goto error;
+ }
+
+ return dd;
+
+error:
+ if (dd) {
+ if (dd->sa_type != NULL)
+ SCFree(dd->sa_type);
+ SCFree(dd);
+ }
+ return NULL;
+}
+
+/**
+ * \brief Function to add the parsed IKE SA attribute query into the current signature.
+ *
+ * \param de_ctx Pointer to the Detection Engine Context.
+ * \param s Pointer to the Current Signature.
+ * \param rawstr Pointer to the user provided flags options.
+ *
+ * \retval 0 on Success.
+ * \retval -1 on Failure.
+ */
+static int DetectIkeChosenSaSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
+{
+ if (DetectSignatureSetAppProto(s, ALPROTO_IKE) != 0)
+ return -1;
+
+ DetectIkeChosenSaData *dd = DetectIkeChosenSaParse(rawstr);
+ if (dd == NULL) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "Parsing \'%s\' failed", rawstr);
+ goto error;
+ }
+
+ /* okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ SigMatch *sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_AL_IKE_CHOSEN_SA;
+ sm->ctx = (void *)dd;
+
+ SigMatchAppendSMToList(s, sm, g_ike_chosen_sa_buffer_id);
+ return 0;
+
+error:
+ DetectIkeChosenSaFree(de_ctx, dd);
+ return -1;
+}
+
+/**
+ * \internal
+ * \brief Function to free memory associated with DetectIkeChosenSaData.
+ *
+ * \param de_ptr Pointer to DetectIkeChosenSaData.
+ */
+static void DetectIkeChosenSaFree(DetectEngineCtx *de_ctx, void *ptr)
+{
+ DetectIkeChosenSaData *dd = (DetectIkeChosenSaData *)ptr;
+ if (dd == NULL)
+ return;
+ if (dd->sa_type != NULL)
+ SCFree(dd->sa_type);
+
+ SCFree(ptr);
+}
+
+/*
+ * ONLY TESTS BELOW THIS COMMENT
+ */
+
+#ifdef UNITTESTS
+
+/**
+ * \test IKEChosenSaParserTest is a test for valid values
+ *
+ * \retval 1 on success
+ * \retval 0 on failure
+ */
+static int IKEChosenSaParserTest(void)
+{
+ DetectIkeChosenSaData *de = NULL;
+ de = DetectIkeChosenSaParse("alg_hash=2");
+
+ FAIL_IF_NULL(de);
+ FAIL_IF(de->sa_value != 2);
+ FAIL_IF(strcmp(de->sa_type, "alg_hash") != 0);
+
+ DetectIkeChosenSaFree(NULL, de);
+ PASS;
+}
+
+#endif /* UNITTESTS */
+
+void IKEChosenSaRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("IKEChosenSaParserTest", IKEChosenSaParserTest);
+#endif /* UNITTESTS */
+}
--- /dev/null
+/* Copyright (C) 2020 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Frank Honza <frank.honza@dcso.de>
+ */
+
+#ifndef __DETECT_IKE_CHOSEN_SA_H__
+#define __DETECT_IKE_CHOSEN_SA_H__
+
+void DetectIkeChosenSaRegister(void);
+
+#endif /* __DETECT_IKE_CHOSEN_SA_H__ */
--- /dev/null
+/* Copyright (C) 2020 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ *
+ * \author Frank Honza <frank.honza@dcso.de>
+ */
+
+#include "suricata-common.h"
+#include "conf.h"
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-content-inspection.h"
+#include "detect-ike-exch-type.h"
+#include "app-layer-parser.h"
+#include "util-byte.h"
+#include "detect-engine-uint.h"
+
+#include "rust-bindings.h"
+
+/**
+ * [ike.exchtype]:[<|>|<=|>=]<type>;
+ */
+
+static int DetectIkeExchTypeSetup(DetectEngineCtx *, Signature *s, const char *str);
+static void DetectIkeExchTypeFree(DetectEngineCtx *, void *);
+static int g_ike_exch_type_buffer_id = 0;
+
+static int DetectEngineInspectIkeExchTypeGeneric(DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx, const struct DetectEngineAppInspectionEngine_ *engine,
+ const Signature *s, Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id);
+
+static int DetectIkeExchTypeMatch(DetectEngineThreadCtx *, Flow *, uint8_t, void *, void *,
+ const Signature *, const SigMatchCtx *);
+
+/**
+ * \brief Registration function for ike.exchtype keyword.
+ */
+void DetectIkeExchTypeRegister(void)
+{
+ sigmatch_table[DETECT_AL_IKE_EXCH_TYPE].name = "ike.exchtype";
+ sigmatch_table[DETECT_AL_IKE_EXCH_TYPE].desc = "match IKE exchange type";
+ sigmatch_table[DETECT_AL_IKE_EXCH_TYPE].url = "/rules/ike-keywords.html#ike-exchtype";
+ sigmatch_table[DETECT_AL_IKE_EXCH_TYPE].Match = NULL;
+ sigmatch_table[DETECT_AL_IKE_EXCH_TYPE].AppLayerTxMatch = DetectIkeExchTypeMatch;
+ sigmatch_table[DETECT_AL_IKE_EXCH_TYPE].Setup = DetectIkeExchTypeSetup;
+ sigmatch_table[DETECT_AL_IKE_EXCH_TYPE].Free = DetectIkeExchTypeFree;
+
+ DetectAppLayerInspectEngineRegister2("ike.exchtype", ALPROTO_IKE, SIG_FLAG_TOSERVER, 1,
+ DetectEngineInspectIkeExchTypeGeneric, NULL);
+
+ DetectAppLayerInspectEngineRegister2("ike.exchtype", ALPROTO_IKE, SIG_FLAG_TOCLIENT, 1,
+ DetectEngineInspectIkeExchTypeGeneric, NULL);
+
+ g_ike_exch_type_buffer_id = DetectBufferTypeGetByName("ike.exchtype");
+
+ DetectUintRegister();
+}
+
+static int DetectEngineInspectIkeExchTypeGeneric(DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx, const struct DetectEngineAppInspectionEngine_ *engine,
+ const Signature *s, Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id)
+{
+ return DetectEngineInspectGenericList(
+ de_ctx, det_ctx, s, engine->smd, f, flags, alstate, txv, tx_id);
+}
+
+/**
+ * \internal
+ * \brief Function to match exchange type of a IKE state
+ *
+ * \param det_ctx Pointer to the pattern matcher thread.
+ * \param f Pointer to the current flow.
+ * \param flags Flags.
+ * \param state App layer state.
+ * \param txv Pointer to the Ike Transaction.
+ * \param s Pointer to the Signature.
+ * \param ctx Pointer to the sigmatch that we will cast into DetectU8Data.
+ *
+ * \retval 0 no match.
+ * \retval 1 match.
+ */
+static int DetectIkeExchTypeMatch(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags,
+ void *state, void *txv, const Signature *s, const SigMatchCtx *ctx)
+{
+ SCEnter();
+
+ uint8_t exch_type;
+ if (!rs_ike_state_get_exch_type(txv, &exch_type))
+ SCReturnInt(0);
+
+ const DetectU8Data *du8 = (const DetectU8Data *)ctx;
+ SCReturnInt(DetectU8Match(exch_type, du8));
+}
+
+/**
+ * \brief Function to add the parsed IKE exchange type field into the current signature.
+ *
+ * \param de_ctx Pointer to the Detection Engine Context.
+ * \param s Pointer to the Current Signature.
+ * \param rawstr Pointer to the user provided flags options.
+ *
+ * \retval 0 on Success.
+ * \retval -1 on Failure.
+ */
+static int DetectIkeExchTypeSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
+{
+ if (DetectSignatureSetAppProto(s, ALPROTO_IKE) != 0)
+ return -1;
+
+ DetectU8Data *ike_exch_type = DetectU8Parse(rawstr);
+ if (ike_exch_type == NULL)
+ return -1;
+
+ /* okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ SigMatch *sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_AL_IKE_EXCH_TYPE;
+ sm->ctx = (SigMatchCtx *)ike_exch_type;
+
+ SigMatchAppendSMToList(s, sm, g_ike_exch_type_buffer_id);
+ return 0;
+
+error:
+ DetectIkeExchTypeFree(de_ctx, ike_exch_type);
+ return -1;
+}
+
+/**
+ * \internal
+ * \brief Function to free memory associated with DetectU8Data.
+ *
+ * \param de_ptr Pointer to DetectU8Data.
+ */
+static void DetectIkeExchTypeFree(DetectEngineCtx *de_ctx, void *ptr)
+{
+ SCFree(ptr);
+}
--- /dev/null
+/* Copyright (C) 2020 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Frank Honza <frank.honza@dcso.de>
+ */
+
+#ifndef __DETECT_IKE_EXCH_TYPE_H__
+#define __DETECT_IKE_EXCH_TYPE_H__
+
+void DetectIkeExchTypeRegister(void);
+
+#endif /* __DETECT_IKE_EXCH_TYPE_H__ */
--- /dev/null
+/* Copyright (C) 2020 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ *
+ * \author Frank Honza <frank.honza@dcso.de>
+ */
+
+#include "suricata-common.h"
+#include "conf.h"
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-content-inspection.h"
+#include "detect-ike-key-exchange-payload-length.h"
+#include "app-layer-parser.h"
+#include "util-byte.h"
+#include "detect-engine-uint.h"
+
+#include "rust-bindings.h"
+
+/**
+ * [ike.key_exchange_payload_length]:[=|<|>|<=|>=]<length>;
+ */
+static int DetectIkeKeyExchangePayloadLengthSetup(DetectEngineCtx *, Signature *s, const char *str);
+static void DetectIkeKeyExchangePayloadLengthFree(DetectEngineCtx *, void *);
+static int g_ike_key_exch_payload_length_buffer_id = 0;
+
+static int DetectEngineInspectIkeKeyExchangePayloadLengthGeneric(DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx, const struct DetectEngineAppInspectionEngine_ *engine,
+ const Signature *s, Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id);
+
+static int DetectIkeKeyExchangePayloadLengthMatch(DetectEngineThreadCtx *, Flow *, uint8_t, void *,
+ void *, const Signature *, const SigMatchCtx *);
+
+/**
+ * \brief Registration function for ike.key_exchange_payload_length keyword.
+ */
+void DetectIkeKeyExchangePayloadLengthRegister(void)
+{
+ sigmatch_table[DETECT_AL_IKE_KEY_EXCHANGE_PAYLOAD_LENGTH].name =
+ "ike.key_exchange_payload_length";
+ sigmatch_table[DETECT_AL_IKE_KEY_EXCHANGE_PAYLOAD_LENGTH].desc =
+ "match IKE key exchange payload length";
+ sigmatch_table[DETECT_AL_IKE_KEY_EXCHANGE_PAYLOAD_LENGTH].url =
+ "/rules/ike-keywords.html#ike-key-exchange-payload-length";
+ sigmatch_table[DETECT_AL_IKE_KEY_EXCHANGE_PAYLOAD_LENGTH].AppLayerTxMatch =
+ DetectIkeKeyExchangePayloadLengthMatch;
+ sigmatch_table[DETECT_AL_IKE_KEY_EXCHANGE_PAYLOAD_LENGTH].Setup =
+ DetectIkeKeyExchangePayloadLengthSetup;
+ sigmatch_table[DETECT_AL_IKE_KEY_EXCHANGE_PAYLOAD_LENGTH].Free =
+ DetectIkeKeyExchangePayloadLengthFree;
+
+ DetectAppLayerInspectEngineRegister2("ike.key_exchange_payload_length", ALPROTO_IKE,
+ SIG_FLAG_TOSERVER, 1, DetectEngineInspectIkeKeyExchangePayloadLengthGeneric, NULL);
+
+ DetectAppLayerInspectEngineRegister2("ike.key_exchange_payload_length", ALPROTO_IKE,
+ SIG_FLAG_TOCLIENT, 1, DetectEngineInspectIkeKeyExchangePayloadLengthGeneric, NULL);
+
+ g_ike_key_exch_payload_length_buffer_id =
+ DetectBufferTypeGetByName("ike.key_exchange_payload_length");
+
+ DetectUintRegister();
+}
+
+static int DetectEngineInspectIkeKeyExchangePayloadLengthGeneric(DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx, const struct DetectEngineAppInspectionEngine_ *engine,
+ const Signature *s, Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id)
+{
+ return DetectEngineInspectGenericList(
+ de_ctx, det_ctx, s, engine->smd, f, flags, alstate, txv, tx_id);
+}
+
+/**
+ * \internal
+ * \brief Function to match key exchange payload length of a IKE state
+ *
+ * \param det_ctx Pointer to the pattern matcher thread.
+ * \param f Pointer to the current flow.
+ * \param flags Flags.
+ * \param state App layer state.
+ * \param txv Pointer to the Ike Transaction.
+ * \param s Pointer to the Signature.
+ * \param ctx Pointer to the sigmatch that we will cast into DetectU32Data.
+ *
+ * \retval 0 no match.
+ * \retval 1 match.
+ */
+static int DetectIkeKeyExchangePayloadLengthMatch(DetectEngineThreadCtx *det_ctx, Flow *f,
+ uint8_t flags, void *state, void *txv, const Signature *s, const SigMatchCtx *ctx)
+{
+ SCEnter();
+
+ uint32_t length;
+ if (!rs_ike_state_get_key_exchange_payload_length(txv, &length))
+ SCReturnInt(0);
+ const DetectU32Data *du32 = (const DetectU32Data *)ctx;
+ return DetectU32Match(length, du32);
+}
+
+/**
+ * \brief Function to add the parsed IKE key exchange payload length query into the current
+ * signature.
+ *
+ * \param de_ctx Pointer to the Detection Engine Context.
+ * \param s Pointer to the Current Signature.
+ * \param rawstr Pointer to the user provided flags options.
+ *
+ * \retval 0 on Success.
+ * \retval -1 on Failure.
+ */
+static int DetectIkeKeyExchangePayloadLengthSetup(
+ DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
+{
+ if (DetectSignatureSetAppProto(s, ALPROTO_IKE) != 0)
+ return -1;
+
+ DetectU32Data *key_exchange_payload_length = DetectU32Parse(rawstr);
+ if (key_exchange_payload_length == NULL)
+ return -1;
+
+ /* okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ SigMatch *sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_AL_IKE_KEY_EXCHANGE_PAYLOAD_LENGTH;
+ sm->ctx = (SigMatchCtx *)key_exchange_payload_length;
+
+ SigMatchAppendSMToList(s, sm, g_ike_key_exch_payload_length_buffer_id);
+ return 0;
+
+error:
+ DetectIkeKeyExchangePayloadLengthFree(de_ctx, key_exchange_payload_length);
+ return -1;
+}
+
+/**
+ * \internal
+ * \brief Function to free memory associated with DetectU32Data.
+ *
+ * \param de_ptr Pointer to DetectU32Data.
+ */
+static void DetectIkeKeyExchangePayloadLengthFree(DetectEngineCtx *de_ctx, void *ptr)
+{
+ SCFree(ptr);
+}
--- /dev/null
+/* Copyright (C) 2020 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ *
+ * \author Frank Honza <frank.honza@dcso.de>
+ */
+
+#ifndef __DETECT_IKE_KEY_EXCHANGE_PAYLOAD_LENGTH_H__
+#define __DETECT_IKE_KEY_EXCHANGE_PAYLOAD_LENGTH_H__
+
+void DetectIkeKeyExchangePayloadLengthRegister(void);
+
+#endif /* __DETECT_IKE_KEY_EXCHANGE_PAYLOAD_LENGTH_H__ */
--- /dev/null
+/* Copyright (C) 2020 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ *
+ * \author Frank Honza <frank.honza@dcso.de>
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "decode.h"
+#include "detect.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-prefilter.h"
+#include "detect-urilen.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-spm.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+
+#include "detect-ike-key-exchange-payload.h"
+#include "stream-tcp.h"
+
+#include "rust.h"
+#include "app-layer-ike.h"
+#include "rust-bindings.h"
+
+#define KEYWORD_NAME_KEY_EXCHANGE "ike.key_exchange_payload"
+#define KEYWORD_DOC_KEY_EXCHANGE "ike-keywords.html#ike-key_exchange_payload";
+#define BUFFER_NAME_KEY_EXCHANGE "ike.key_exchange_payload"
+#define BUFFER_DESC_KEY_EXCHANGE "ike key_exchange payload"
+
+static int g_buffer_key_exchange_id = 0;
+
+static int DetectKeyExchangeSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
+{
+ if (DetectBufferSetActiveList(s, g_buffer_key_exchange_id) < 0)
+ return -1;
+
+ if (DetectSignatureSetAppProto(s, ALPROTO_IKE) < 0)
+ return -1;
+
+ return 0;
+}
+
+static InspectionBuffer *GetKeyExchangeData(DetectEngineThreadCtx *det_ctx,
+ const DetectEngineTransforms *transforms, Flow *_f, const uint8_t _flow_flags, void *txv,
+ const int list_id)
+{
+ InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
+ if (buffer->inspect == NULL) {
+ const uint8_t *b = NULL;
+ uint32_t b_len = 0;
+
+ if (rs_ike_state_get_key_exchange(txv, &b, &b_len) != 1)
+ return NULL;
+ if (b == NULL || b_len == 0)
+ return NULL;
+
+ InspectionBufferSetup(det_ctx, list_id, buffer, b, b_len);
+ InspectionBufferApplyTransforms(buffer, transforms);
+ }
+
+ return buffer;
+}
+
+void DetectIkeKeyExchangeRegister(void)
+{
+ // register key_exchange
+ sigmatch_table[DETECT_AL_IKE_KEY_EXCHANGE].name = KEYWORD_NAME_KEY_EXCHANGE;
+ sigmatch_table[DETECT_AL_IKE_KEY_EXCHANGE].url =
+ "/rules/" KEYWORD_DOC_KEY_EXCHANGE sigmatch_table[DETECT_AL_IKE_KEY_EXCHANGE].desc =
+ "sticky buffer to match on the IKE key_exchange_payload";
+ sigmatch_table[DETECT_AL_IKE_KEY_EXCHANGE].Setup = DetectKeyExchangeSetup;
+ sigmatch_table[DETECT_AL_IKE_KEY_EXCHANGE].flags |=
+ SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER;
+
+ DetectAppLayerInspectEngineRegister2(BUFFER_NAME_KEY_EXCHANGE, ALPROTO_IKE, SIG_FLAG_TOSERVER,
+ 1, DetectEngineInspectBufferGeneric, GetKeyExchangeData);
+
+ DetectAppLayerMpmRegister2(BUFFER_NAME_KEY_EXCHANGE, SIG_FLAG_TOSERVER, 1,
+ PrefilterGenericMpmRegister, GetKeyExchangeData, ALPROTO_IKE, 1);
+
+ DetectAppLayerInspectEngineRegister2(BUFFER_NAME_KEY_EXCHANGE, ALPROTO_IKE, SIG_FLAG_TOCLIENT,
+ 1, DetectEngineInspectBufferGeneric, GetKeyExchangeData);
+
+ DetectAppLayerMpmRegister2(BUFFER_NAME_KEY_EXCHANGE, SIG_FLAG_TOCLIENT, 1,
+ PrefilterGenericMpmRegister, GetKeyExchangeData, ALPROTO_IKE, 1);
+
+ DetectBufferTypeSetDescriptionByName(BUFFER_NAME_KEY_EXCHANGE, BUFFER_DESC_KEY_EXCHANGE);
+
+ g_buffer_key_exchange_id = DetectBufferTypeGetByName(BUFFER_NAME_KEY_EXCHANGE);
+ SCLogDebug("registering " BUFFER_NAME_KEY_EXCHANGE " rule option");
+}
--- /dev/null
+/* Copyright (C) 2020 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ *
+ * \author Frank Honza <frank.honza@dcso.de>
+ */
+
+#ifndef __DETECT_IKE_KEY_EXCHANGE_PAYLOAD_H__
+#define __DETECT_IKE_KEY_EXCHANGE_PAYLOAD_H__
+
+void DetectIkeKeyExchangeRegister(void);
+
+#endif /* __DETECT_IKE_KEY_EXCHANGE_PAYLOAD_H__ */
--- /dev/null
+/* Copyright (C) 2020 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ *
+ * \author Frank Honza <frank.honza@dcso.de>
+ */
+
+#include "suricata-common.h"
+#include "conf.h"
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-content-inspection.h"
+#include "detect-ike-nonce-payload-length.h"
+#include "app-layer-parser.h"
+#include "util-byte.h"
+#include "detect-engine-uint.h"
+
+#include "rust-bindings.h"
+
+/**
+ * [ike.nonce_payload_length]:[=|<|>|<=|>=]<length>;
+ */
+static int DetectIkeNoncePayloadLengthSetup(DetectEngineCtx *, Signature *s, const char *str);
+static void DetectIkeNoncePayloadLengthFree(DetectEngineCtx *, void *);
+static int g_ike_nonce_payload_length_buffer_id = 0;
+
+static int DetectEngineInspectIkeNoncePayloadLengthGeneric(DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx, const struct DetectEngineAppInspectionEngine_ *engine,
+ const Signature *s, Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id);
+
+static int DetectIkeNoncePayloadLengthMatch(DetectEngineThreadCtx *, Flow *, uint8_t, void *,
+ void *, const Signature *, const SigMatchCtx *);
+
+/**
+ * \brief Registration function for ike.nonce_payload_length keyword.
+ */
+void DetectIkeNoncePayloadLengthRegister(void)
+{
+ sigmatch_table[DETECT_AL_IKE_NONCE_PAYLOAD_LENGTH].name = "ike.nonce_payload_length";
+ sigmatch_table[DETECT_AL_IKE_NONCE_PAYLOAD_LENGTH].desc = "match IKE nonce payload length";
+ sigmatch_table[DETECT_AL_IKE_NONCE_PAYLOAD_LENGTH].url =
+ "/rules/ike-keywords.html#ike-nonce-payload-length";
+ sigmatch_table[DETECT_AL_IKE_NONCE_PAYLOAD_LENGTH].AppLayerTxMatch =
+ DetectIkeNoncePayloadLengthMatch;
+ sigmatch_table[DETECT_AL_IKE_NONCE_PAYLOAD_LENGTH].Setup = DetectIkeNoncePayloadLengthSetup;
+ sigmatch_table[DETECT_AL_IKE_NONCE_PAYLOAD_LENGTH].Free = DetectIkeNoncePayloadLengthFree;
+
+ DetectAppLayerInspectEngineRegister2("ike.nonce_payload_length", ALPROTO_IKE, SIG_FLAG_TOSERVER,
+ 1, DetectEngineInspectIkeNoncePayloadLengthGeneric, NULL);
+
+ DetectAppLayerInspectEngineRegister2("ike.nonce_payload_length", ALPROTO_IKE, SIG_FLAG_TOCLIENT,
+ 1, DetectEngineInspectIkeNoncePayloadLengthGeneric, NULL);
+
+ g_ike_nonce_payload_length_buffer_id = DetectBufferTypeGetByName("ike.nonce_payload_length");
+
+ DetectUintRegister();
+}
+
+static int DetectEngineInspectIkeNoncePayloadLengthGeneric(DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx, const struct DetectEngineAppInspectionEngine_ *engine,
+ const Signature *s, Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id)
+{
+ return DetectEngineInspectGenericList(
+ de_ctx, det_ctx, s, engine->smd, f, flags, alstate, txv, tx_id);
+}
+
+/**
+ * \internal
+ * \brief Function to match nonce length of a IKE state
+ *
+ * \param det_ctx Pointer to the pattern matcher thread.
+ * \param f Pointer to the current flow.
+ * \param flags Flags.
+ * \param state App layer state.
+ * \param txv Pointer to the Ike Transaction.
+ * \param s Pointer to the Signature.
+ * \param ctx Pointer to the sigmatch that we will cast into DetectU32Data.
+ *
+ * \retval 0 no match.
+ * \retval 1 match.
+ */
+static int DetectIkeNoncePayloadLengthMatch(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags,
+ void *state, void *txv, const Signature *s, const SigMatchCtx *ctx)
+{
+ SCEnter();
+
+ uint32_t length;
+ if (!rs_ike_state_get_nonce_payload_length(txv, &length))
+ SCReturnInt(0);
+ const DetectU32Data *du32 = (const DetectU32Data *)ctx;
+ return DetectU32Match(length, du32);
+}
+
+/**
+ * \brief Function to add the parsed IKE nonce length field into the current signature.
+ *
+ * \param de_ctx Pointer to the Detection Engine Context.
+ * \param s Pointer to the Current Signature.
+ * \param rawstr Pointer to the user provided flags options.
+ *
+ * \retval 0 on Success.
+ * \retval -1 on Failure.
+ */
+static int DetectIkeNoncePayloadLengthSetup(
+ DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
+{
+ if (DetectSignatureSetAppProto(s, ALPROTO_IKE) != 0)
+ return -1;
+
+ DetectU32Data *nonce_payload_length = DetectU32Parse(rawstr);
+ if (nonce_payload_length == NULL)
+ return -1;
+
+ /* okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ SigMatch *sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_AL_IKE_NONCE_PAYLOAD_LENGTH;
+ sm->ctx = (SigMatchCtx *)nonce_payload_length;
+
+ SigMatchAppendSMToList(s, sm, g_ike_nonce_payload_length_buffer_id);
+ return 0;
+
+error:
+ DetectIkeNoncePayloadLengthFree(de_ctx, nonce_payload_length);
+ return -1;
+}
+
+/**
+ * \internal
+ * \brief Function to free memory associated with DetectU32Data.
+ *
+ * \param de_ptr Pointer to DetectU32Data.
+ */
+static void DetectIkeNoncePayloadLengthFree(DetectEngineCtx *de_ctx, void *ptr)
+{
+ SCFree(ptr);
+}
--- /dev/null
+/* Copyright (C) 2020 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ *
+ * \author Frank Honza <frank.honza@dcso.de>
+ */
+
+#ifndef __DETECT_IKE_NONCE_PAYLOAD_LENGTH_H__
+#define __DETECT_IKE_NONCE_PAYLOAD_LENGTH_H__
+
+void DetectIkeNoncePayloadLengthRegister(void);
+
+#endif /* __DETECT_IKE_NONCE_PAYLOAD_LENGTH_H__ */
--- /dev/null
+/* Copyright (C) 2020 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ *
+ * \author Frank Honza <frank.honza@dcso.de>
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "decode.h"
+#include "detect.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-prefilter.h"
+#include "detect-urilen.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-spm.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+
+#include "detect-ike-nonce-payload.h"
+#include "stream-tcp.h"
+
+#include "rust.h"
+#include "app-layer-ike.h"
+#include "rust-bindings.h"
+
+#define KEYWORD_NAME_NONCE "ike.nonce_payload"
+#define KEYWORD_DOC_NONCE "ike-keywords.html#ike-nonce_payload";
+#define BUFFER_NAME_NONCE "ike.nonce_payload"
+#define BUFFER_DESC_NONCE "ike nonce payload"
+
+static int g_buffer_nonce_id = 0;
+
+static int DetectNonceSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
+{
+ if (DetectBufferSetActiveList(s, g_buffer_nonce_id) < 0)
+ return -1;
+
+ if (DetectSignatureSetAppProto(s, ALPROTO_IKE) < 0)
+ return -1;
+
+ return 0;
+}
+
+static InspectionBuffer *GetNonceData(DetectEngineThreadCtx *det_ctx,
+ const DetectEngineTransforms *transforms, Flow *_f, const uint8_t _flow_flags, void *txv,
+ const int list_id)
+{
+ InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
+ if (buffer->inspect == NULL) {
+ const uint8_t *b = NULL;
+ uint32_t b_len = 0;
+
+ if (rs_ike_state_get_nonce(txv, &b, &b_len) != 1)
+ return NULL;
+ if (b == NULL || b_len == 0)
+ return NULL;
+
+ InspectionBufferSetup(det_ctx, list_id, buffer, b, b_len);
+ InspectionBufferApplyTransforms(buffer, transforms);
+ }
+
+ return buffer;
+}
+
+void DetectIkeNonceRegister(void)
+{
+ // register nonce
+ sigmatch_table[DETECT_AL_IKE_NONCE].name = KEYWORD_NAME_NONCE;
+ sigmatch_table[DETECT_AL_IKE_NONCE].url =
+ "/rules/" KEYWORD_DOC_NONCE sigmatch_table[DETECT_AL_IKE_NONCE].desc =
+ "sticky buffer to match on the IKE nonce_payload";
+ sigmatch_table[DETECT_AL_IKE_NONCE].Setup = DetectNonceSetup;
+ sigmatch_table[DETECT_AL_IKE_NONCE].flags |= SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER;
+
+ DetectAppLayerInspectEngineRegister2(BUFFER_NAME_NONCE, ALPROTO_IKE, SIG_FLAG_TOSERVER, 1,
+ DetectEngineInspectBufferGeneric, GetNonceData);
+
+ DetectAppLayerMpmRegister2(BUFFER_NAME_NONCE, SIG_FLAG_TOSERVER, 1, PrefilterGenericMpmRegister,
+ GetNonceData, ALPROTO_IKE, 1);
+
+ DetectAppLayerInspectEngineRegister2(BUFFER_NAME_NONCE, ALPROTO_IKE, SIG_FLAG_TOCLIENT, 1,
+ DetectEngineInspectBufferGeneric, GetNonceData);
+
+ DetectAppLayerMpmRegister2(BUFFER_NAME_NONCE, SIG_FLAG_TOCLIENT, 1, PrefilterGenericMpmRegister,
+ GetNonceData, ALPROTO_IKE, 1);
+
+ DetectBufferTypeSetDescriptionByName(BUFFER_NAME_NONCE, BUFFER_DESC_NONCE);
+
+ g_buffer_nonce_id = DetectBufferTypeGetByName(BUFFER_NAME_NONCE);
+ SCLogDebug("registering " BUFFER_NAME_NONCE " rule option");
+}
--- /dev/null
+/* Copyright (C) 2020 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ *
+ * \author Frank Honza <frank.honza@dcso.de>
+ */
+
+#ifndef __DETECT_IKE_NONCE_PAYLOAD_H__
+#define __DETECT_IKE_NONCE_PAYLOAD_H__
+
+void DetectIkeNonceRegister(void);
+
+#endif /* __DETECT_IKE_NONCE_PAYLOAD_H__ */
--- /dev/null
+/* Copyright (C) 2020 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ *
+ * \author Frank Honza <frank.honza@dcso.de>
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "debug.h"
+#include "decode.h"
+#include "detect.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-prefilter.h"
+#include "detect-urilen.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-spm.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+
+#include "detect-ike-spi.h"
+#include "stream-tcp.h"
+
+#include "rust.h"
+#include "app-layer-ike.h"
+#include "rust-bindings.h"
+
+#define KEYWORD_NAME_INITIATOR "ike.init_spi"
+#define KEYWORD_DOC_INITIATOR "ike-keywords.html#ike-init_spi";
+#define BUFFER_NAME_INITIATOR "ike.init_spi"
+#define BUFFER_DESC_INITIATOR "ike init spi"
+
+#define KEYWORD_NAME_RESPONDER "ike.resp_spi"
+#define KEYWORD_DOC_RESPONDER "ike-keywords.html#ike-resp_spi";
+#define BUFFER_NAME_RESPONDER "ike.resp_spi"
+#define BUFFER_DESC_RESPONDER "ike resp spi"
+
+static int g_buffer_initiator_id = 0;
+static int g_buffer_responder_id = 0;
+
+static int DetectSpiInitiatorSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
+{
+ if (DetectBufferSetActiveList(s, g_buffer_initiator_id) < 0)
+ return -1;
+
+ if (DetectSignatureSetAppProto(s, ALPROTO_IKE) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int DetectSpiResponderSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
+{
+ if (DetectBufferSetActiveList(s, g_buffer_responder_id) < 0)
+ return -1;
+
+ if (DetectSignatureSetAppProto(s, ALPROTO_IKE) < 0)
+ return -1;
+
+ return 0;
+}
+
+static InspectionBuffer *GetInitiatorData(DetectEngineThreadCtx *det_ctx,
+ const DetectEngineTransforms *transforms, Flow *_f, const uint8_t _flow_flags, void *txv,
+ const int list_id)
+{
+ InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
+ if (buffer->inspect == NULL) {
+ const uint8_t *b = NULL;
+ uint32_t b_len = 0;
+
+ if (rs_ike_state_get_spi_initiator(txv, &b, &b_len) != 1)
+ return NULL;
+ if (b == NULL || b_len == 0)
+ return NULL;
+
+ InspectionBufferSetup(det_ctx, list_id, buffer, b, b_len);
+ InspectionBufferApplyTransforms(buffer, transforms);
+ }
+
+ return buffer;
+}
+
+static InspectionBuffer *GetResponderData(DetectEngineThreadCtx *det_ctx,
+ const DetectEngineTransforms *transforms, Flow *_f, const uint8_t _flow_flags, void *txv,
+ const int list_id)
+{
+ InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
+ if (buffer->inspect == NULL) {
+ const uint8_t *b = NULL;
+ uint32_t b_len = 0;
+
+ if (rs_ike_state_get_spi_responder(txv, &b, &b_len) != 1)
+ return NULL;
+ if (b == NULL || b_len == 0)
+ return NULL;
+
+ InspectionBufferSetup(det_ctx, list_id, buffer, b, b_len);
+ InspectionBufferApplyTransforms(buffer, transforms);
+ }
+
+ return buffer;
+}
+
+void DetectIkeSpiRegister(void)
+{
+ // register initiator
+ sigmatch_table[DETECT_AL_IKE_SPI_INITIATOR].name = KEYWORD_NAME_INITIATOR;
+ sigmatch_table[DETECT_AL_IKE_SPI_INITIATOR].url =
+ "/rules/" KEYWORD_DOC_INITIATOR sigmatch_table[DETECT_AL_IKE_SPI_INITIATOR].desc =
+ "sticky buffer to match on the IKE spi initiator";
+ sigmatch_table[DETECT_AL_IKE_SPI_INITIATOR].Setup = DetectSpiInitiatorSetup;
+ sigmatch_table[DETECT_AL_IKE_SPI_INITIATOR].flags |=
+ SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER;
+
+ DetectAppLayerInspectEngineRegister2(BUFFER_NAME_INITIATOR, ALPROTO_IKE, SIG_FLAG_TOSERVER, 1,
+ DetectEngineInspectBufferGeneric, GetInitiatorData);
+
+ DetectAppLayerMpmRegister2(BUFFER_NAME_INITIATOR, SIG_FLAG_TOSERVER, 1,
+ PrefilterGenericMpmRegister, GetInitiatorData, ALPROTO_IKE, 1);
+
+ DetectBufferTypeSetDescriptionByName(BUFFER_NAME_INITIATOR, BUFFER_DESC_INITIATOR);
+
+ g_buffer_initiator_id = DetectBufferTypeGetByName(BUFFER_NAME_INITIATOR);
+ SCLogDebug("registering " BUFFER_NAME_INITIATOR " rule option");
+
+ // register responder
+ sigmatch_table[DETECT_AL_IKE_SPI_RESPONDER].name = KEYWORD_NAME_RESPONDER;
+ sigmatch_table[DETECT_AL_IKE_SPI_RESPONDER].url =
+ "/rules/" KEYWORD_DOC_RESPONDER sigmatch_table[DETECT_AL_IKE_SPI_RESPONDER].desc =
+ "sticky buffer to match on the IKE spi responder";
+ sigmatch_table[DETECT_AL_IKE_SPI_RESPONDER].Setup = DetectSpiResponderSetup;
+ sigmatch_table[DETECT_AL_IKE_SPI_RESPONDER].flags |=
+ SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER;
+
+ DetectAppLayerInspectEngineRegister2(BUFFER_NAME_RESPONDER, ALPROTO_IKE, SIG_FLAG_TOCLIENT, 1,
+ DetectEngineInspectBufferGeneric, GetResponderData);
+
+ DetectAppLayerMpmRegister2(BUFFER_NAME_RESPONDER, SIG_FLAG_TOCLIENT, 1,
+ PrefilterGenericMpmRegister, GetResponderData, ALPROTO_IKE, 1);
+
+ DetectBufferTypeSetDescriptionByName(BUFFER_NAME_RESPONDER, BUFFER_DESC_RESPONDER);
+
+ g_buffer_responder_id = DetectBufferTypeGetByName(BUFFER_NAME_RESPONDER);
+ SCLogDebug("registering " BUFFER_NAME_RESPONDER " rule option");
+}
--- /dev/null
+/* Copyright (C) 2020 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ *
+ * \author Frank Honza <frank.honza@dcso.de>
+ */
+
+#ifndef __DETECT_IKE_SPI_H__
+#define __DETECT_IKE_SPI_H__
+
+void DetectIkeSpiRegister(void);
+
+#endif /* __DETECT_IKE_SPI_H__ */
--- /dev/null
+/* Copyright (C) 2020 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ *
+ * \author Frank Honza <frank.honza@dcso.de>
+ */
+
+#include "suricata-common.h"
+#include "conf.h"
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-prefilter.h"
+#include "detect-engine-content-inspection.h"
+#include "detect-engine-mpm.h"
+#include "detect-ike-vendor.h"
+#include "app-layer-parser.h"
+#include "util-byte.h"
+
+#include "rust-bindings.h"
+
+static int DetectIkeVendorSetup(DetectEngineCtx *, Signature *, const char *);
+
+typedef struct {
+ char *vendor;
+} DetectIkeVendorData;
+
+struct IkeVendorGetDataArgs {
+ int local_id;
+ void *txv;
+};
+
+typedef struct PrefilterMpmIkeVendor {
+ int list_id;
+ const MpmCtx *mpm_ctx;
+ const DetectEngineTransforms *transforms;
+} PrefilterMpmIkeVendor;
+
+static int g_ike_vendor_buffer_id = 0;
+
+static InspectionBuffer *IkeVendorGetData(DetectEngineThreadCtx *det_ctx,
+ const DetectEngineTransforms *transforms, Flow *f, struct IkeVendorGetDataArgs *cbdata,
+ int list_id, bool first)
+{
+ SCEnter();
+
+ InspectionBufferMultipleForList *fb = InspectionBufferGetMulti(det_ctx, list_id);
+ InspectionBuffer *buffer = InspectionBufferMultipleForListGet(fb, cbdata->local_id);
+ if (buffer == NULL)
+ return NULL;
+ if (!first && buffer->inspect != NULL)
+ return buffer;
+
+ const uint8_t *data;
+ uint32_t data_len;
+ if (rs_ike_tx_get_vendor(cbdata->txv, (uint16_t)cbdata->local_id, &data, &data_len) == 0) {
+ return NULL;
+ }
+
+ InspectionBufferSetup(det_ctx, list_id, buffer, data, data_len);
+ InspectionBufferApplyTransforms(buffer, transforms);
+
+ SCReturnPtr(buffer, "InspectionBuffer");
+}
+
+/** \brief IkeVendor Mpm prefilter callback
+ *
+ * \param det_ctx detection engine thread ctx
+ * \param p packet to inspect
+ * \param f flow to inspect
+ * \param txv tx to inspect
+ * \param pectx inspection context
+ */
+static void PrefilterTxIkeVendor(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p,
+ Flow *f, void *txv, const uint64_t idx, const uint8_t flags)
+{
+ SCEnter();
+
+ const PrefilterMpmIkeVendor *ctx = (const PrefilterMpmIkeVendor *)pectx;
+ const MpmCtx *mpm_ctx = ctx->mpm_ctx;
+ const int list_id = ctx->list_id;
+
+ int local_id = 0;
+ while (1) {
+ struct IkeVendorGetDataArgs cbdata = { local_id, txv };
+ InspectionBuffer *buffer =
+ IkeVendorGetData(det_ctx, ctx->transforms, f, &cbdata, list_id, true);
+ if (buffer == NULL)
+ break;
+
+ if (buffer->inspect_len >= mpm_ctx->minlen) {
+ (void)mpm_table[mpm_ctx->mpm_type].Search(
+ mpm_ctx, &det_ctx->mtcu, &det_ctx->pmq, buffer->inspect, buffer->inspect_len);
+ }
+ local_id++;
+ }
+
+ SCReturn;
+}
+
+static void PrefilterMpmIkeVendorFree(void *ptr)
+{
+ if (ptr != NULL)
+ SCFree(ptr);
+}
+
+static int PrefilterMpmIkeVendorRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
+ MpmCtx *mpm_ctx, const DetectBufferMpmRegistery *mpm_reg, int list_id)
+{
+ PrefilterMpmIkeVendor *pectx = SCCalloc(1, sizeof(*pectx));
+ if (pectx == NULL)
+ return -1;
+ pectx->list_id = list_id;
+ pectx->mpm_ctx = mpm_ctx;
+ pectx->transforms = &mpm_reg->transforms;
+
+ return PrefilterAppendTxEngine(de_ctx, sgh, PrefilterTxIkeVendor, mpm_reg->app_v2.alproto,
+ mpm_reg->app_v2.tx_min_progress, pectx, PrefilterMpmIkeVendorFree, mpm_reg->pname);
+}
+
+static int DetectEngineInspectIkeVendor(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
+ const DetectEngineAppInspectionEngine *engine, const Signature *s, Flow *f, uint8_t flags,
+ void *alstate, void *txv, uint64_t tx_id)
+{
+ int local_id = 0;
+
+ const DetectEngineTransforms *transforms = NULL;
+ if (!engine->mpm) {
+ transforms = engine->v2.transforms;
+ }
+
+ while (1) {
+ struct IkeVendorGetDataArgs cbdata = {
+ local_id,
+ txv,
+ };
+ InspectionBuffer *buffer =
+ IkeVendorGetData(det_ctx, transforms, f, &cbdata, engine->sm_list, false);
+ if (buffer == NULL || buffer->inspect == NULL)
+ break;
+
+ det_ctx->buffer_offset = 0;
+ det_ctx->discontinue_matching = 0;
+ det_ctx->inspection_recursion_counter = 0;
+
+ const int match = DetectEngineContentInspection(de_ctx, det_ctx, s, engine->smd, NULL, f,
+ (uint8_t *)buffer->inspect, buffer->inspect_len, buffer->inspect_offset,
+ DETECT_CI_FLAGS_SINGLE, DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE);
+ if (match == 1) {
+ return DETECT_ENGINE_INSPECT_SIG_MATCH;
+ }
+ local_id++;
+ }
+ return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+}
+
+/**
+ * \brief Registration function for ike.vendor keyword.
+ */
+void DetectIkeVendorRegister(void)
+{
+ sigmatch_table[DETECT_AL_IKE_VENDOR].name = "ike.vendor";
+ sigmatch_table[DETECT_AL_IKE_VENDOR].desc = "match IKE Vendor";
+ sigmatch_table[DETECT_AL_IKE_VENDOR].url = "/rules/ike-keywords.html#ike-vendor";
+ sigmatch_table[DETECT_AL_IKE_VENDOR].Setup = DetectIkeVendorSetup;
+ sigmatch_table[DETECT_AL_IKE_VENDOR].flags |= SIGMATCH_NOOPT;
+ sigmatch_table[DETECT_AL_IKE_VENDOR].flags |= SIGMATCH_INFO_STICKY_BUFFER;
+
+ DetectAppLayerMpmRegister2("ike.vendor", SIG_FLAG_TOSERVER, 1, PrefilterMpmIkeVendorRegister,
+ NULL, ALPROTO_IKE, 1);
+
+ DetectAppLayerInspectEngineRegister2(
+ "ike.vendor", ALPROTO_IKE, SIG_FLAG_TOSERVER, 1, DetectEngineInspectIkeVendor, NULL);
+
+ g_ike_vendor_buffer_id = DetectBufferTypeGetByName("ike.vendor");
+}
+
+/**
+ * \brief setup the sticky buffer keyword used in the rule
+ *
+ * \param de_ctx Pointer to the Detection Engine Context
+ * \param s Pointer to the Signature to which the current keyword belongs
+ * \param str Should hold an empty string always
+ *
+ * \retval 0 On success
+ * \retval -1 On failure
+ */
+
+static int DetectIkeVendorSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
+{
+ if (DetectBufferSetActiveList(s, g_ike_vendor_buffer_id) < 0)
+ return -1;
+ if (DetectSignatureSetAppProto(s, ALPROTO_IKE) < 0)
+ return -1;
+ return 0;
+}
--- /dev/null
+/* Copyright (C) 2020 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Frank Honza <frank.honza@dcso.de>
+ */
+
+#ifndef __DETECT_IKE_VENDOR_H__
+#define __DETECT_IKE_VENDOR_H__
+
+void DetectIkeVendorRegister(void);
+
+#endif /* __DETECT_IKE_VENDOR_H__ */
* \file
*
* \author Pierre Chifflier <chifflier@wzdftpd.net>
+ * \author Frank Honza <frank.honza@dcso.de>
*
* Implement JSON/eve logging app-layer IKE.
*/
#include "rust.h"
+#define LOG_IKE_DEFAULT 0
+#define LOG_IKE_EXTENDED (1 << 0)
+
typedef struct LogIKEFileCtx_ {
LogFileCtx *file_ctx;
- OutputJsonCommonSettings cfg;
+ uint32_t flags;
} LogIKEFileCtx;
typedef struct LogIKELogThread_ {
static int JsonIKELogger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, void *state,
void *tx, uint64_t tx_id)
{
- IKETransaction *iketx = tx;
LogIKELogThread *thread = thread_data;
-
JsonBuilder *jb = CreateEveHeader((Packet *)p, LOG_DIR_PACKET, "ike", NULL);
if (unlikely(jb == NULL)) {
return TM_ECODE_FAILED;
}
- EveAddCommonOptions(&thread->ikelog_ctx->cfg, p, f, jb);
-
- jb_open_object(jb, "ike");
- if (unlikely(!rs_ike_log_json_response(state, iketx, jb))) {
+ LogIKEFileCtx *ike_ctx = thread->ikelog_ctx;
+ if (!rs_ike_logger_log(state, tx, ike_ctx->flags, jb)) {
goto error;
}
- jb_close(jb);
MemBufferReset(thread->buffer);
OutputJsonBuilderBuffer(jb, thread->file_ctx, &thread->buffer);
return result;
}
ikelog_ctx->file_ctx = ajt->file_ctx;
- ikelog_ctx->cfg = ajt->cfg;
OutputCtx *output_ctx = SCCalloc(1, sizeof(*output_ctx));
if (unlikely(output_ctx == NULL)) {
SCFree(ikelog_ctx);
return result;
}
+
+ ikelog_ctx->flags = LOG_IKE_DEFAULT;
+ const char *extended = ConfNodeLookupChildValue(conf, "extended");
+ if (extended) {
+ if (ConfValIsTrue(extended)) {
+ ikelog_ctx->flags = LOG_IKE_EXTENDED;
+ }
+ }
+
output_ctx->data = ikelog_ctx;
output_ctx->DeInit = OutputIKELogDeInitCtxSub;
- SCLogDebug("IKE log sub-module initialized.");
-
AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_IKE);
result.ctx = output_ctx;
OutputRegisterTxSubModule(LOGGER_JSON_IKE, "eve-log", "JsonIKELog", "eve-log.ike",
OutputIKELogInitSub, ALPROTO_IKE, JsonIKELogger, JsonIKELogThreadInit,
JsonIKELogThreadDeinit, NULL);
-
- SCLogDebug("IKE JSON logger registered.");
}