mirror of https://github.com/OISF/suricata
ikev1: add ikev1 parser
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()
|
||||
}
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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__ */
|
||||
Loading…
Reference in New Issue