diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:39:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:39:49 +0000 |
commit | a0aa2307322cd47bbf416810ac0292925e03be87 (patch) | |
tree | 37076262a026c4b48c8a0e84f44ff9187556ca35 /rust/src/ike/ikev1.rs | |
parent | Initial commit. (diff) | |
download | suricata-a0aa2307322cd47bbf416810ac0292925e03be87.tar.xz suricata-a0aa2307322cd47bbf416810ac0292925e03be87.zip |
Adding upstream version 1:7.0.3.upstream/1%7.0.3
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'rust/src/ike/ikev1.rs')
-rw-r--r-- | rust/src/ike/ikev1.rs | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/rust/src/ike/ikev1.rs b/rust/src/ike/ikev1.rs new file mode 100644 index 0000000..1e79c29 --- /dev/null +++ b/rust/src/ike/ikev1.rs @@ -0,0 +1,169 @@ +/* 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::Direction; +use crate::ike::ike::{IKEState, IkeEvent}; +use crate::ike::parser::*; +use nom7::Err; +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 nb_transforms: u64, + pub transform: Vec<SaAttribute>, +} + +impl Ikev1ParticipantData { + pub fn reset(&mut self) { + self.key_exchange.clear(); + self.nonce.clear(); + self.nb_transforms = 0; + self.transform.clear(); + } + + pub fn update( + &mut self, key_exchange: &str, nonce: &str, transforms: &Vec<Vec<SaAttribute>>, + ) { + self.key_exchange = key_exchange.to_string(); + self.nonce = nonce.to_string(); + if self.nb_transforms == 0 && !transforms.is_empty() { + self.transform.extend(transforms[0].iter().cloned()); + } + self.nb_transforms += transforms.len() as u64; + } +} + +#[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: Direction, +) -> AppLayerResult { + let mut tx = state.new_tx(direction); + + 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 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, + ).is_err() { + 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 == Direction::ToServer { + state.ikev1_container.client.reset(); + } else { + state.ikev1_container.server.reset(); + } + } + + // add transaction values to state values + if direction == Direction::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_transforms, + ); + } else { + if state.ikev1_container.server.nb_transforms <= 1 + && state.ikev1_container.server.nb_transforms + + tx.hdr.ikev1_transforms.len() as u64 + > 1 + { + SCLogDebug!("More than one chosen server proposal"); + state.set_event(IkeEvent::MultipleServerProposal); + } + + 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_transforms, + ); + } + + if !rem.is_empty() { + // more data left unread than should be + SCLogDebug!("Unread Payload Data"); + state.set_event(IkeEvent::PayloadExtraData); + } + } + Err(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(); +} |