Add new parser: IKEv2

Add a new parser for Internet Key Exchange version (IKEv2), defined in
RFC 7296.
The IKEv2 parser itself is external. The embedded code includes the
parser state and associated variables, the state machine, and the
detection code.

The parser looks the first two messages of a connection, and analyzes
the client and server proposals to check the cryptographic parameters.
pull/3315/head
Pierre Chifflier 8 years ago committed by Victor Julien
parent b810275b16
commit c99b9462d7

@ -10,7 +10,7 @@ debug = true
[features]
lua = []
experimental = ["ntp-parser"]
experimental = ["ntp-parser", "ipsec-parser"]
strict = []
debug = []
@ -22,3 +22,4 @@ der-parser = "0.5.2"
kerberos-parser = "0.1.0"
ntp-parser = { version = "^0", optional = true }
ipsec-parser = { version = "0.3", optional = true }

@ -84,6 +84,8 @@ type_map = {
"TFTPState": "TFTPState",
"SMBState": "SMBState",
"SMBTransaction": "SMBTransaction",
"IKEV2State": "IKEV2State",
"IKEV2Transaction": "IKEV2Transaction",
"JsonT": "json_t",
"DetectEngineState": "DetectEngineState",
"core::DetectEngineState": "DetectEngineState",

@ -0,0 +1,701 @@
/* Copyright (C) 2017-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>
use ikev2::ipsec_parser::*;
use ikev2::state::IKEV2ConnectionState;
use core;
use core::{AppProto,Flow,ALPROTO_UNKNOWN,ALPROTO_FAILED,STREAM_TOSERVER,STREAM_TOCLIENT};
use applayer;
use parser::*;
use libc;
use std;
use std::ffi::{CStr,CString};
use log::*;
use nom::IResult;
#[repr(u32)]
pub enum IKEV2Event {
MalformedData = 0,
NoEncryption,
WeakCryptoEnc,
WeakCryptoPRF,
WeakCryptoDH,
WeakCryptoAuth,
WeakCryptoNoDH,
WeakCryptoNoAuth,
InvalidProposal,
UnknownProposal,
}
pub struct IKEV2State {
/// List of transactions for this session
transactions: Vec<IKEV2Transaction>,
/// Events counter
events: u16,
/// tx counter for assigning incrementing id's to tx's
tx_id: u64,
/// The connection state
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,
}
#[derive(Debug)]
pub struct IKEV2Transaction {
/// The IKEV2 reference ID
pub xid: u64,
pub hdr: IkeV2Header,
/// The internal transaction id
id: u64,
/// The detection engine state, if present
de_state: Option<*mut core::DetectEngineState>,
/// The events associated with this transaction
events: *mut core::AppLayerDecoderEvents,
logged: applayer::LoggerFlags,
}
impl IKEV2State {
pub fn new() -> IKEV2State {
IKEV2State{
transactions: Vec::new(),
events: 0,
tx_id: 0,
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,
}
}
}
impl IKEV2State {
/// Parse an IKEV2 request message
///
/// Returns The number of messages parsed, or -1 on error
fn parse(&mut self, i: &[u8], direction: u8) -> i8 {
match parse_ikev2_header(i) {
IResult::Done(rem,ref hdr) => {
SCLogDebug!("parse_ikev2: {:?}",hdr);
if rem.len() == 0 && hdr.length == 28 {
return 1;
}
// Rule 0: check version
if hdr.maj_ver != 2 || hdr.min_ver != 0 {
SCLogInfo!("Unknown header version: {}.{}", hdr.maj_ver, hdr.min_ver);
}
if hdr.init_spi == 0 {
SCLogInfo!("Malformed SPI - wrong length");
self.set_event(IKEV2Event::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();
match parse_ikev2_payload_list(rem,hdr.next_payload) {
IResult::Done(_,Ok(ref p)) => {
for payload in p {
match payload.content {
IkeV2PayloadContent::Dummy => (),
IkeV2PayloadContent::SA(ref prop) => {
// if hdr.flags & IKEV2_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);
},
// XXX CertificateRequest
// XXX Certificate
// XXX Authentication
// XXX TSi
// XXX TSr
// XXX IDr
_ => {
SCLogInfo!("Unknown payload content {:?}", payload.content);
},
}
self.connection_state = self.connection_state.advance(payload);
};
},
e @ _ => SCLogInfo!("parse_ikev2_payload_with_type: {:?}",e),
}
self.transactions.push(tx);
1
},
IResult::Incomplete(_) => {
SCLogDebug!("Insufficient data while parsing IKEV2 data");
self.set_event(IKEV2Event::MalformedData);
-1
},
IResult::Error(_) => {
SCLogDebug!("Error while parsing IKEV2 data");
self.set_event(IKEV2Event::MalformedData);
-1
},
}
}
fn free(&mut self) {
// All transactions are freed when the `transactions` object is freed.
// But let's be explicit
self.transactions.clear();
}
fn new_tx(&mut self) -> IKEV2Transaction {
self.tx_id += 1;
IKEV2Transaction::new(self.tx_id)
}
fn get_tx_by_id(&mut self, tx_id: u64) -> Option<&IKEV2Transaction> {
self.transactions.iter().find(|&tx| tx.id == tx_id + 1)
}
fn free_tx(&mut self, tx_id: u64) {
let tx = self.transactions.iter().position(|ref tx| tx.id == tx_id + 1);
debug_assert!(tx != None);
if let Some(idx) = tx {
let _ = self.transactions.remove(idx);
}
}
/// Set an event. The event is set on the most recent transaction.
fn set_event(&mut self, event: IKEV2Event) {
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);
self.events += 1;
}
}
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(IKEV2Event::WeakCryptoEnc);
},
_ => (),
}
},
IkeV2Transform::PRF(ref prf) => {
match *prf {
IkeTransformPRFType::PRF_NULL => {
SCLogDebug!("'Null' PRF transform proposed");
self.set_event(IKEV2Event::InvalidProposal);
},
IkeTransformPRFType::PRF_HMAC_MD5 |
IkeTransformPRFType::PRF_HMAC_SHA1 => {
SCLogDebug!("Weak PRF: {:?}", prf);
self.set_event(IKEV2Event::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(IKEV2Event::WeakCryptoAuth);
},
_ => (),
}
},
IkeV2Transform::DH(ref dh) => {
match *dh {
IkeTransformDHType::None => {
SCLogDebug!("'None' DH transform proposed");
self.set_event(IKEV2Event::InvalidProposal);
},
IkeTransformDHType::Modp768 |
IkeTransformDHType::Modp1024 |
IkeTransformDHType::Modp1024s160 |
IkeTransformDHType::Modp1536 => {
SCLogDebug!("Weak DH: {:?}", dh);
self.set_event(IKEV2Event::WeakCryptoDH);
},
_ => (),
}
},
IkeV2Transform::Unknown(tx_type,tx_id) => {
SCLogDebug!("Unknown proposal: type={:?}, id={}", tx_type, tx_id);
self.set_event(IKEV2Event::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");
self.set_event(IKEV2Event::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(IKEV2Event::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");
self.set_event(IKEV2Event::WeakCryptoNoAuth);
}
}
// 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);
}
}
}
}
impl IKEV2Transaction {
pub fn new(id: u64) -> IKEV2Transaction {
IKEV2Transaction {
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,
},
id: id,
de_state: None,
events: std::ptr::null_mut(),
logged: applayer::LoggerFlags::new(),
}
}
fn free(&mut self) {
if self.events != std::ptr::null_mut() {
core::sc_app_layer_decoder_events_free_events(&mut self.events);
}
}
}
impl Drop for IKEV2Transaction {
fn drop(&mut self) {
self.free();
}
}
/// Returns *mut IKEV2State
#[no_mangle]
pub extern "C" fn rs_ikev2_state_new() -> *mut libc::c_void {
let state = IKEV2State::new();
let boxed = Box::new(state);
return unsafe{std::mem::transmute(boxed)};
}
/// Params:
/// - state: *mut IKEV2State as void pointer
#[no_mangle]
pub extern "C" fn rs_ikev2_state_free(state: *mut libc::c_void) {
// Just unbox...
let mut ikev2_state: Box<IKEV2State> = unsafe{std::mem::transmute(state)};
ikev2_state.free();
}
#[no_mangle]
pub extern "C" fn rs_ikev2_parse_request(_flow: *const core::Flow,
state: *mut libc::c_void,
_pstate: *mut libc::c_void,
input: *const libc::uint8_t,
input_len: u32,
_data: *const libc::c_void) -> i8 {
let buf = build_slice!(input,input_len as usize);
let state = cast_pointer!(state,IKEV2State);
state.parse(buf, STREAM_TOSERVER)
}
#[no_mangle]
pub extern "C" fn rs_ikev2_parse_response(_flow: *const core::Flow,
state: *mut libc::c_void,
pstate: *mut libc::c_void,
input: *const libc::uint8_t,
input_len: u32,
_data: *const libc::c_void) -> i8 {
let buf = build_slice!(input,input_len as usize);
let state = cast_pointer!(state,IKEV2State);
let res = state.parse(buf, STREAM_TOCLIENT);
if state.connection_state == IKEV2ConnectionState::ParsingDone {
unsafe{
AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_NO_INSPECTION |
APP_LAYER_PARSER_NO_REASSEMBLY |
APP_LAYER_PARSER_BYPASS_READY)
};
}
res
}
#[no_mangle]
pub extern "C" fn rs_ikev2_state_get_tx(state: *mut libc::c_void,
tx_id: libc::uint64_t)
-> *mut libc::c_void
{
let state = cast_pointer!(state,IKEV2State);
match state.get_tx_by_id(tx_id) {
Some(tx) => unsafe{std::mem::transmute(tx)},
None => std::ptr::null_mut(),
}
}
#[no_mangle]
pub extern "C" fn rs_ikev2_state_get_tx_count(state: *mut libc::c_void)
-> libc::uint64_t
{
let state = cast_pointer!(state,IKEV2State);
state.tx_id
}
#[no_mangle]
pub extern "C" fn rs_ikev2_state_tx_free(state: *mut libc::c_void,
tx_id: libc::uint64_t)
{
let state = cast_pointer!(state,IKEV2State);
state.free_tx(tx_id);
}
#[no_mangle]
pub extern "C" fn rs_ikev2_state_progress_completion_status(
_direction: libc::uint8_t)
-> libc::c_int
{
return 1;
}
#[no_mangle]
pub extern "C" fn rs_ikev2_tx_get_alstate_progress(_tx: *mut libc::c_void,
_direction: libc::uint8_t)
-> libc::c_int
{
1
}
#[no_mangle]
pub extern "C" fn rs_ikev2_tx_set_logged(_state: *mut libc::c_void,
tx: *mut libc::c_void,
logged: libc::uint32_t)
{
let tx = cast_pointer!(tx,IKEV2Transaction);
tx.logged.set(logged);
}
#[no_mangle]
pub extern "C" fn rs_ikev2_tx_get_logged(_state: *mut libc::c_void,
tx: *mut libc::c_void)
-> u32
{
let tx = cast_pointer!(tx,IKEV2Transaction);
return tx.logged.get();
}
#[no_mangle]
pub extern "C" fn rs_ikev2_state_set_tx_detect_state(
tx: *mut libc::c_void,
de_state: &mut core::DetectEngineState) -> libc::c_int
{
let tx = cast_pointer!(tx,IKEV2Transaction);
tx.de_state = Some(de_state);
0
}
#[no_mangle]
pub extern "C" fn rs_ikev2_state_get_tx_detect_state(
tx: *mut libc::c_void)
-> *mut core::DetectEngineState
{
let tx = cast_pointer!(tx,IKEV2Transaction);
match tx.de_state {
Some(ds) => ds,
None => std::ptr::null_mut(),
}
}
#[no_mangle]
pub extern "C" fn rs_ikev2_state_get_events(state: *mut libc::c_void,
tx_id: libc::uint64_t)
-> *mut core::AppLayerDecoderEvents
{
let state = cast_pointer!(state,IKEV2State);
match state.get_tx_by_id(tx_id) {
Some(tx) => tx.events,
_ => std::ptr::null_mut(),
}
}
#[no_mangle]
pub extern "C" fn rs_ikev2_state_get_event_info(event_name: *const libc::c_char,
event_id: *mut libc::c_int,
event_type: *mut core::AppLayerEventType)
-> libc::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" => IKEV2Event::MalformedData as i32,
"no_encryption" => IKEV2Event::NoEncryption as i32,
"weak_crypto_enc" => IKEV2Event::WeakCryptoEnc as i32,
"weak_crypto_prf" => IKEV2Event::WeakCryptoPRF as i32,
"weak_crypto_auth" => IKEV2Event::WeakCryptoAuth as i32,
"weak_crypto_dh" => IKEV2Event::WeakCryptoDH as i32,
"weak_crypto_nodh" => IKEV2Event::WeakCryptoNoDH as i32,
"weak_crypto_noauth" => IKEV2Event::WeakCryptoNoAuth as i32,
"invalid_proposal" => IKEV2Event::InvalidProposal as i32,
"unknown_proposal" => IKEV2Event::UnknownProposal as i32,
_ => -1, // unknown event
}
},
Err(_) => -1, // UTF-8 conversion failed
};
unsafe{
*event_type = core::APP_LAYER_EVENT_TYPE_TRANSACTION;
*event_id = event as libc::c_int;
};
0
}
static mut ALPROTO_IKEV2 : AppProto = ALPROTO_UNKNOWN;
#[no_mangle]
pub extern "C" fn rs_ikev2_probing_parser(_flow: *const Flow, input:*const libc::uint8_t, input_len: u32, _offset: *const u32) -> AppProto {
let slice = build_slice!(input,input_len as usize);
let alproto = unsafe{ ALPROTO_IKEV2 };
match parse_ikev2_header(slice) {
IResult::Done(_, 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;
},
IResult::Incomplete(_) => {
return ALPROTO_UNKNOWN;
},
IResult::Error(_) => {
return unsafe{ALPROTO_FAILED};
},
}
}
const PARSER_NAME : &'static [u8] = b"ikev2\0";
#[no_mangle]
pub unsafe extern "C" fn rs_register_ikev2_parser() {
let default_port = CString::new("500").unwrap();
let parser = RustParser {
name : PARSER_NAME.as_ptr() as *const libc::c_char,
default_port : default_port.as_ptr(),
ipproto : libc::IPPROTO_UDP,
probe_ts : rs_ikev2_probing_parser,
probe_tc : rs_ikev2_probing_parser,
min_depth : 0,
max_depth : 16,
state_new : rs_ikev2_state_new,
state_free : rs_ikev2_state_free,
tx_free : rs_ikev2_state_tx_free,
parse_ts : rs_ikev2_parse_request,
parse_tc : rs_ikev2_parse_response,
get_tx_count : rs_ikev2_state_get_tx_count,
get_tx : rs_ikev2_state_get_tx,
tx_get_comp_st : rs_ikev2_state_progress_completion_status,
tx_get_progress : rs_ikev2_tx_get_alstate_progress,
get_tx_logged : Some(rs_ikev2_tx_get_logged),
set_tx_logged : Some(rs_ikev2_tx_set_logged),
get_de_state : rs_ikev2_state_get_tx_detect_state,
set_de_state : rs_ikev2_state_set_tx_detect_state,
get_events : Some(rs_ikev2_state_get_events),
get_eventinfo : Some(rs_ikev2_state_get_event_info),
localstorage_new : None,
localstorage_free : None,
get_tx_mpm_id : None,
set_tx_mpm_id : None,
get_files : None,
};
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_IKEV2 = alproto;
if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
let _ = AppLayerRegisterParser(&parser, alproto);
}
} else {
SCLogDebug!("Protocol detecter and parser disabled for IKEV2.");
}
}
#[cfg(test)]
mod tests {
use super::IKEV2State;
#[test]
fn test_ikev2_parse_request_valid() {
// A UDP IKEV2 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 = IKEV2State::new();
assert_eq!(1, state.parse(REQ, 0));
}
}

@ -0,0 +1,23 @@
/* Copyright (C) 2017-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;
pub mod ikev2;
pub mod state;

@ -0,0 +1,57 @@
/* 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 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,
}
}
}

@ -50,6 +50,9 @@ pub mod nfs;
pub mod ftp;
pub mod smb;
#[cfg(feature = "experimental")]
pub mod ikev2;
#[cfg(feature = "experimental")]
pub mod ntp;
pub mod tftp;

@ -46,6 +46,7 @@ app-layer-nfs-udp.c app-layer-nfs-udp.h \
app-layer-ntp.c app-layer-ntp.h \
app-layer-register.c app-layer-register.h \
app-layer-tftp.c app-layer-tftp.h \
app-layer-ikev2.c app-layer-ikev2.h \
app-layer-template.c app-layer-template.h \
app-layer-ssh.c app-layer-ssh.h \
app-layer-ssl.c app-layer-ssl.h \

@ -725,6 +725,8 @@ static void AppLayerProtoDetectPrintProbingParsers(AppLayerProtoDetectProbingPar
printf(" alproto: ALPROTO_NTP\n");
else if (pp_pe->alproto == ALPROTO_TFTP)
printf(" alproto: ALPROTO_TFTP\n");
else if (pp_pe->alproto == ALPROTO_IKEV2)
printf(" alproto: ALPROTO_IKEV2\n");
else if (pp_pe->alproto == ALPROTO_TEMPLATE)
printf(" alproto: ALPROTO_TEMPLATE\n");
else if (pp_pe->alproto == ALPROTO_DNP3)
@ -790,6 +792,8 @@ static void AppLayerProtoDetectPrintProbingParsers(AppLayerProtoDetectProbingPar
printf(" alproto: ALPROTO_NTP\n");
else if (pp_pe->alproto == ALPROTO_TFTP)
printf(" alproto: ALPROTO_TFTP\n");
else if (pp_pe->alproto == ALPROTO_IKEV2)
printf(" alproto: ALPROTO_IKEV2\n");
else if (pp_pe->alproto == ALPROTO_TEMPLATE)
printf(" alproto: ALPROTO_TEMPLATE\n");
else if (pp_pe->alproto == ALPROTO_DNP3)

@ -0,0 +1,66 @@
/* Copyright (C) 2015 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 Pierre Chifflier <chifflier@wzdftpd.net>
*
* Parser for IKEv2 application layer running on UDP port 500.
*/
#include "suricata-common.h"
#include "stream.h"
#include "conf.h"
#include "util-unittest.h"
#include "app-layer-detect-proto.h"
#include "app-layer-parser.h"
#include "app-layer-ikev2.h"
#if defined(HAVE_RUST) && defined(HAVE_RUST_EXTERNAL)
#include "rust-ikev2-ikev2-gen.h"
void RegisterIKEV2Parsers(void)
{
rs_register_ikev2_parser();
#ifdef UNITTESTS
AppLayerParserRegisterProtocolUnittests(IPPROTO_UDP, ALPROTO_IKEV2,
IKEV2ParserRegisterTests);
#endif
}
#ifdef UNITTESTS
#endif
void IKEV2ParserRegisterTests(void)
{
#ifdef UNITTESTS
#endif
}
#else /* HAVE_RUST */
void RegisterIKEV2Parsers(void)
{
}
#endif /* HAVE_RUST */

@ -0,0 +1,34 @@
/* Copyright (C) 2015 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 Pierre Chifflier <chifflier@wzdftpd.net>
*/
#ifndef __APP_LAYER_IKEV2_H__
#define __APP_LAYER_IKEV2_H__
void RegisterIKEV2Parsers(void);
void IKEV2ParserRegisterTests(void);
/** Opaque Rust types. */
typedef struct IKEV2State_ IKEV2State;
typedef struct IKEV2Transaction_ IKEV2Transaction;
#endif /* __APP_LAYER_IKEV2_H__ */

@ -64,6 +64,7 @@
#include "app-layer-nfs-udp.h"
#include "app-layer-ntp.h"
#include "app-layer-tftp.h"
#include "app-layer-ikev2.h"
#include "app-layer-template.h"
#include "conf.h"
@ -1450,6 +1451,7 @@ void AppLayerParserRegisterProtocolParsers(void)
RegisterNFSUDPParsers();
RegisterNTPParsers();
RegisterTFTPParsers();
RegisterIKEV2Parsers();
RegisterTemplateParsers();
/** IMAP */

@ -93,6 +93,9 @@ const char *AppProtoToString(AppProto alproto)
case ALPROTO_TFTP:
proto_name = "tftp";
break;
case ALPROTO_IKEV2:
proto_name = "ikev2";
break;
case ALPROTO_TEMPLATE:
proto_name = "template";
break;
@ -132,6 +135,7 @@ AppProto StringToAppProto(const char *proto_name)
if (strcmp(proto_name,"dnp3")==0) return ALPROTO_DNP3;
if (strcmp(proto_name,"nfs")==0) return ALPROTO_NFS;
if (strcmp(proto_name,"ntp")==0) return ALPROTO_NTP;
if (strcmp(proto_name,"ikev2")==0) return ALPROTO_IKEV2;
if (strcmp(proto_name,"template")==0) return ALPROTO_TEMPLATE;
if (strcmp(proto_name,"failed")==0) return ALPROTO_FAILED;

@ -48,6 +48,7 @@ enum AppProtoEnum {
ALPROTO_NTP,
ALPROTO_FTPDATA,
ALPROTO_TFTP,
ALPROTO_IKEV2,
ALPROTO_TEMPLATE,
/* used by the probing parser when alproto detection fails

@ -268,6 +268,7 @@ outputs:
@rust_config_comment@- nfs
@rust_config_comment@- smb
@rust_config_comment@- tftp
@rust_config_comment@- ikev2
- ssh
- stats:
totals: yes # stats for all threads merged together
@ -774,6 +775,8 @@ pcap-file:
# "detection-only" enables protocol detection only (parser disabled).
app-layer:
protocols:
ikev2:
enabled: yes
tls:
enabled: yes
detection-ports:

Loading…
Cancel
Save