]> git.ipfire.org Git - people/ms/suricata.git/commitdiff
ikev1: add ikev1 parser
authorSascha Steinbiss <satta@debian.org>
Sun, 3 Jan 2021 22:42:24 +0000 (23:42 +0100)
committerVictor Julien <victor@inliniac.net>
Fri, 5 Mar 2021 13:47:10 +0000 (14:47 +0100)
31 files changed:
rust/src/ike/detect.rs [new file with mode: 0644]
rust/src/ike/ike.rs
rust/src/ike/ikev1.rs [new file with mode: 0644]
rust/src/ike/ikev2.rs [new file with mode: 0644]
rust/src/ike/log.rs [deleted file]
rust/src/ike/logger.rs [new file with mode: 0644]
rust/src/ike/mod.rs
rust/src/ike/parser.rs [new file with mode: 0644]
rust/src/ike/state.rs [deleted file]
src/Makefile.am
src/app-layer-ike.c
src/app-layer-ike.h
src/detect-engine-register.c
src/detect-engine-register.h
src/detect-ike-chosen-sa.c [new file with mode: 0644]
src/detect-ike-chosen-sa.h [new file with mode: 0644]
src/detect-ike-exch-type.c [new file with mode: 0644]
src/detect-ike-exch-type.h [new file with mode: 0644]
src/detect-ike-key-exchange-payload-length.c [new file with mode: 0644]
src/detect-ike-key-exchange-payload-length.h [new file with mode: 0644]
src/detect-ike-key-exchange-payload.c [new file with mode: 0644]
src/detect-ike-key-exchange-payload.h [new file with mode: 0644]
src/detect-ike-nonce-payload-length.c [new file with mode: 0644]
src/detect-ike-nonce-payload-length.h [new file with mode: 0644]
src/detect-ike-nonce-payload.c [new file with mode: 0644]
src/detect-ike-nonce-payload.h [new file with mode: 0644]
src/detect-ike-spi.c [new file with mode: 0644]
src/detect-ike-spi.h [new file with mode: 0644]
src/detect-ike-vendor.c [new file with mode: 0644]
src/detect-ike-vendor.h [new file with mode: 0644]
src/output-json-ike.c

diff --git a/rust/src/ike/detect.rs b/rust/src/ike/detect.rs
new file mode 100644 (file)
index 0000000..787032b
--- /dev/null
@@ -0,0 +1,242 @@
+/* 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;
+}
index 2cae016044f167c63e231ae04b5694da0d850214..8c87f1ab7802f12788665b5f89a550eed39f67c5 100644 (file)
@@ -1,4 +1,4 @@
-/* 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 {
@@ -39,10 +46,11 @@ 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),
@@ -54,547 +62,402 @@ impl IkeEvent {
             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;
         };
@@ -605,32 +468,34 @@ pub extern "C" fn rs_ike_state_get_event_info_by_id(event_id: std::os::raw::c_in
 }
 
 #[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;
     };
@@ -640,46 +505,31 @@ pub extern "C" fn rs_ike_state_get_event_info(event_name: *const std::os::raw::c
 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,
@@ -699,8 +549,8 @@ pub unsafe extern "C" fn rs_register_ike_parser() {
         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),
@@ -715,36 +565,20 @@ pub unsafe extern "C" fn rs_register_ike_parser() {
     };
 
     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));
-    }
-}
diff --git a/rust/src/ike/ikev1.rs b/rust/src/ike/ikev1.rs
new file mode 100644 (file)
index 0000000..043f249
--- /dev/null
@@ -0,0 +1,162 @@
+/* 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();
+}
diff --git a/rust/src/ike/ikev2.rs b/rust/src/ike/ikev2.rs
new file mode 100644 (file)
index 0000000..8f97c26
--- /dev/null
@@ -0,0 +1,340 @@
+/* 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);
+        }
+    }
+}
diff --git a/rust/src/ike/log.rs b/rust/src/ike/log.rs
deleted file mode 100644 (file)
index d1654e5..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/* 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()
-}
diff --git a/rust/src/ike/logger.rs b/rust/src/ike/logger.rs
new file mode 100644 (file)
index 0000000..3a022a1
--- /dev/null
@@ -0,0 +1,227 @@
+/* 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()
+}
index 2601e27cbec7757280b6552718ee5f626819e0bc..4f8114d2a0af8fdc8dd608c844021f7b43bd2654 100644 (file)
@@ -1,4 +1,4 @@
-/* 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
@@ -19,6 +19,9 @@
 
 extern crate ipsec_parser;
 
+mod detect;
 pub mod ike;
-pub mod state;
-pub mod log;
+mod ikev1;
+mod ikev2;
+pub mod logger;
+mod parser;
diff --git a/rust/src/ike/parser.rs b/rust/src/ike/parser.rs
new file mode 100644 (file)
index 0000000..f3fe86f
--- /dev/null
@@ -0,0 +1,739 @@
+/* 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(()),
+    }
+}
diff --git a/rust/src/ike/state.rs b/rust/src/ike/state.rs
deleted file mode 100644 (file)
index 282b7fe..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/* 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,
-        }
-    }
-}
index a272d0a61e17f324a98f40f76e2da6c46b9fada1..abc6699264869ee854cf40cdb3b450d93e1b643f 100755 (executable)
@@ -213,6 +213,14 @@ noinst_HEADERS = \
        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 \
@@ -781,6 +789,14 @@ libsuricata_c_a_SOURCES = \
        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 \
index f6a5c193332b8554389c7f9342527d2220be5e21..9f8877aee7bbee32815e18de47e8bac5c9ddce45 100644 (file)
@@ -1,4 +1,4 @@
-/* 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
 }
index d8eff7a5c0397e3e9e3535f04baf61d85dc66386..c6270cccc5831a4762da69b33f506b8e1e66c57f 100644 (file)
@@ -1,4 +1,4 @@
-/* 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;
index 1ee5ef91f6979cd39a606448432e8163993a99d7..d5ae96de74c11d676da8d0ec08335c8d5fdabe06 100644 (file)
 #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"
@@ -486,6 +494,15 @@ void SigTableSetup(void)
     DetectEnipCommandRegister();
     DetectDNP3Register();
 
+    DetectIkeExchTypeRegister();
+    DetectIkeSpiRegister();
+    DetectIkeVendorRegister();
+    DetectIkeChosenSaRegister();
+    DetectIkeKeyExchangePayloadLengthRegister();
+    DetectIkeNoncePayloadLengthRegister();
+    DetectIkeNonceRegister();
+    DetectIkeKeyExchangeRegister();
+
     DetectTlsSniRegister();
     DetectTlsIssuerRegister();
     DetectTlsSubjectRegister();
index aea54e28f0d5924f34c7f69c468b9a7bae93460f..ab77074d44c1cf4f56df0cbf5e56c36819ecd290 100644 (file)
@@ -299,6 +299,16 @@ enum DetectKeywordId {
     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,
 };
diff --git a/src/detect-ike-chosen-sa.c b/src/detect-ike-chosen-sa.c
new file mode 100644 (file)
index 0000000..d08785d
--- /dev/null
@@ -0,0 +1,283 @@
+/* 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 */
+}
diff --git a/src/detect-ike-chosen-sa.h b/src/detect-ike-chosen-sa.h
new file mode 100644 (file)
index 0000000..efbd5fe
--- /dev/null
@@ -0,0 +1,29 @@
+/* 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__ */
diff --git a/src/detect-ike-exch-type.c b/src/detect-ike-exch-type.c
new file mode 100644 (file)
index 0000000..8132e0a
--- /dev/null
@@ -0,0 +1,156 @@
+/* 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);
+}
diff --git a/src/detect-ike-exch-type.h b/src/detect-ike-exch-type.h
new file mode 100644 (file)
index 0000000..f7360eb
--- /dev/null
@@ -0,0 +1,29 @@
+/* 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__ */
diff --git a/src/detect-ike-key-exchange-payload-length.c b/src/detect-ike-key-exchange-payload-length.c
new file mode 100644 (file)
index 0000000..65580e8
--- /dev/null
@@ -0,0 +1,162 @@
+/* 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);
+}
diff --git a/src/detect-ike-key-exchange-payload-length.h b/src/detect-ike-key-exchange-payload-length.h
new file mode 100644 (file)
index 0000000..7d76e55
--- /dev/null
@@ -0,0 +1,28 @@
+/* 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__ */
diff --git a/src/detect-ike-key-exchange-payload.c b/src/detect-ike-key-exchange-payload.c
new file mode 100644 (file)
index 0000000..a11227e
--- /dev/null
@@ -0,0 +1,120 @@
+/* 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");
+}
diff --git a/src/detect-ike-key-exchange-payload.h b/src/detect-ike-key-exchange-payload.h
new file mode 100644 (file)
index 0000000..55a914b
--- /dev/null
@@ -0,0 +1,28 @@
+/* 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__ */
diff --git a/src/detect-ike-nonce-payload-length.c b/src/detect-ike-nonce-payload-length.c
new file mode 100644 (file)
index 0000000..d7091b2
--- /dev/null
@@ -0,0 +1,156 @@
+/* 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);
+}
diff --git a/src/detect-ike-nonce-payload-length.h b/src/detect-ike-nonce-payload-length.h
new file mode 100644 (file)
index 0000000..8a17f55
--- /dev/null
@@ -0,0 +1,28 @@
+/* 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__ */
diff --git a/src/detect-ike-nonce-payload.c b/src/detect-ike-nonce-payload.c
new file mode 100644 (file)
index 0000000..168613c
--- /dev/null
@@ -0,0 +1,119 @@
+/* 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");
+}
diff --git a/src/detect-ike-nonce-payload.h b/src/detect-ike-nonce-payload.h
new file mode 100644 (file)
index 0000000..f0923d8
--- /dev/null
@@ -0,0 +1,28 @@
+/* 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__ */
diff --git a/src/detect-ike-spi.c b/src/detect-ike-spi.c
new file mode 100644 (file)
index 0000000..7699f9c
--- /dev/null
@@ -0,0 +1,172 @@
+/* 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");
+}
diff --git a/src/detect-ike-spi.h b/src/detect-ike-spi.h
new file mode 100644 (file)
index 0000000..e4ae75d
--- /dev/null
@@ -0,0 +1,28 @@
+/* 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__ */
diff --git a/src/detect-ike-vendor.c b/src/detect-ike-vendor.c
new file mode 100644 (file)
index 0000000..800ab0d
--- /dev/null
@@ -0,0 +1,211 @@
+/* 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;
+}
diff --git a/src/detect-ike-vendor.h b/src/detect-ike-vendor.h
new file mode 100644 (file)
index 0000000..2570f91
--- /dev/null
@@ -0,0 +1,29 @@
+/* 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__ */
index adba6d7a47537b4b9bfec70c1ae4280697d73a74..165ffa62a4cbec1740bafe290e717f6463dcb78d 100644 (file)
@@ -19,6 +19,7 @@
  * \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_ {
@@ -63,21 +67,16 @@ 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);
@@ -107,18 +106,24 @@ static OutputInitResult OutputIKELogInitSub(ConfNode *conf, OutputCtx *parent_ct
         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;
@@ -179,6 +184,4 @@ void JsonIKELogRegister(void)
     OutputRegisterTxSubModule(LOGGER_JSON_IKE, "eve-log", "JsonIKELog", "eve-log.ike",
             OutputIKELogInitSub, ALPROTO_IKE, JsonIKELogger, JsonIKELogThreadInit,
             JsonIKELogThreadDeinit, NULL);
-
-    SCLogDebug("IKE JSON logger registered.");
 }