ikev1: add ikev1 parser

pull/5959/head
Sascha Steinbiss 5 years ago committed by Victor Julien
parent ecdf9f6b0b
commit e2dbdd7fd5

@ -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;
}

File diff suppressed because it is too large Load Diff

@ -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();
}

@ -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);
}
}
}

@ -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()
}

@ -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()
}

@ -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;

@ -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(()),
}
}

@ -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,
}
}
}

@ -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 \

@ -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
@ -19,8 +19,10 @@
* \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"
@ -37,5 +39,152 @@
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
}

@ -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
@ -19,12 +19,14 @@
* \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;

@ -256,6 +256,14 @@
#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();

@ -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,
};

@ -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 */
}

@ -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__ */

@ -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);
}

@ -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__ */

@ -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);
}

@ -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__ */

@ -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");
}

@ -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__ */

@ -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);
}

@ -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__ */

@ -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");
}

@ -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__ */

@ -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");
}

@ -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__ */

@ -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;
}

@ -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__ */

@ -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.
*/
@ -49,9 +50,12 @@
#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.");
}

Loading…
Cancel
Save