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