1 /* Copyright (C) 2020 Open Information Security Foundation
3 * You can copy, redistribute or modify this Program under the terms of
4 * the GNU General Public License version 2 as published by the Free
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * version 2 along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 // Author: Frank Honza <frank.honza@dcso.de>
20 extern crate ipsec_parser;
21 use self::ipsec_parser::*;
24 use crate::applayer::*;
25 use crate::core::{self, *};
26 use crate::ike::ikev1::{handle_ikev1, IkeV1Header, Ikev1Container};
27 use crate::ike::ikev2::{handle_ikev2, Ikev2Container};
28 use crate::ike::parser::*;
31 use std::collections::HashSet;
32 use std::ffi::CString;
34 #[derive(AppLayerEvent)]
47 MultipleServerProposal,
50 pub struct IkeHeaderWrapper {
51 pub spi_initiator: String,
52 pub spi_responder: String,
57 pub ikev1_transforms: Vec<Vec<SaAttribute>>,
58 pub ikev2_transforms: Vec<IkeV2Transform>,
59 pub ikev1_header: IkeV1Header,
60 pub ikev2_header: IkeV2Header,
63 impl IkeHeaderWrapper {
64 pub fn new() -> IkeHeaderWrapper {
66 spi_initiator: String::new(),
67 spi_responder: String::new(),
72 ikev1_transforms: Vec::new(),
73 ikev2_transforms: Vec::new(),
74 ikev1_header: IkeV1Header::default(),
75 ikev2_header: IkeV2Header {
78 next_payload: IkePayloadType::NoNextPayload,
81 exch_type: IkeExchangeType(0),
91 pub struct IkePayloadWrapper {
92 pub ikev1_payload_types: Option<HashSet<u8>>,
93 pub ikev2_payload_types: Vec<IkePayloadType>,
96 pub struct IKETransaction {
100 pub hdr: IkeHeaderWrapper,
101 pub payload_types: IkePayloadWrapper,
102 pub notify_types: Vec<NotifyType>,
104 /// errors seen during exchange
108 tx_data: applayer::AppLayerTxData,
111 impl Transaction for IKETransaction {
112 fn id(&self) -> u64 {
117 impl IKETransaction {
118 pub fn new() -> IKETransaction {
122 hdr: IkeHeaderWrapper::new(),
123 payload_types: Default::default(),
124 notify_types: vec![],
125 logged: LoggerFlags::new(),
126 tx_data: applayer::AppLayerTxData::new(),
132 pub fn set_event(&mut self, event: IkeEvent) {
133 self.tx_data.set_event(event as u8);
138 pub struct IKEState {
140 pub transactions: Vec<IKETransaction>,
142 pub ikev1_container: Ikev1Container,
143 pub ikev2_container: Ikev2Container,
146 impl State<IKETransaction> for IKEState {
147 fn get_transactions(&self) -> &[IKETransaction] {
153 // Free a transaction by ID.
154 fn free_tx(&mut self, tx_id: u64) {
158 .position(|tx| tx.tx_id == tx_id + 1);
159 debug_assert!(tx != None);
160 if let Some(idx) = tx {
161 let _ = self.transactions.remove(idx);
165 pub fn get_tx(&mut self, tx_id: u64) -> Option<&mut IKETransaction> {
166 for tx in &mut self.transactions {
167 if tx.tx_id == tx_id + 1 {
174 pub fn new_tx(&mut self) -> IKETransaction {
175 let mut tx = IKETransaction::new();
177 tx.tx_id = self.tx_id;
181 /// Set an event. The event is set on the most recent transaction.
182 pub fn set_event(&mut self, event: IkeEvent) {
183 if let Some(tx) = self.transactions.last_mut() {
187 "IKE: trying to set event {} on non-existing transaction",
193 fn handle_input(&mut self, input: &[u8], direction: Direction) -> AppLayerResult {
194 // We're not interested in empty requests.
195 if input.len() == 0 {
196 return AppLayerResult::ok();
199 let mut current = input;
200 match parse_isakmp_header(current) {
201 Ok((rem, isakmp_header)) => {
204 if isakmp_header.maj_ver != 1 && isakmp_header.maj_ver != 2 {
205 SCLogDebug!("Unsupported ISAKMP major_version");
206 return AppLayerResult::err();
209 if isakmp_header.maj_ver == 1 {
210 handle_ikev1(self, current, isakmp_header, direction);
211 } else if isakmp_header.maj_ver == 2 {
212 handle_ikev2(self, current, isakmp_header, direction);
214 return AppLayerResult::err();
216 return AppLayerResult::ok(); // todo either remove outer loop or check header length-field if we have completely read everything
218 Err(nom::Err::Incomplete(_)) => {
219 SCLogDebug!("Insufficient data while parsing IKE");
220 return AppLayerResult::err();
223 SCLogDebug!("Error while parsing IKE packet");
224 return AppLayerResult::err();
230 /// Probe to see if this input looks like a request or response.
231 fn probe(input: &[u8], direction: Direction, rdir: *mut u8) -> bool {
232 match parse_isakmp_header(input) {
233 Ok((_, isakmp_header)) => {
234 if isakmp_header.maj_ver == 1 {
235 if isakmp_header.resp_spi == 0 && direction != Direction::ToServer {
237 *rdir = Direction::ToServer.into();
241 } else if isakmp_header.maj_ver == 2 {
242 if isakmp_header.min_ver != 0 {
244 "ipsec_probe: could be ipsec, but with unsupported/invalid version {}.{}",
245 isakmp_header.maj_ver,
246 isakmp_header.min_ver
250 if isakmp_header.exch_type < 34 || isakmp_header.exch_type > 37 {
251 SCLogDebug!("ipsec_probe: could be ipsec, but with unsupported/invalid exchange type {}",
252 isakmp_header.exch_type);
255 if isakmp_header.length as usize != input.len() {
256 SCLogDebug!("ipsec_probe: could be ipsec, but length does not match");
260 if isakmp_header.resp_spi == 0 && direction != Direction::ToServer {
262 *rdir = Direction::ToServer.into();
270 Err(_) => return false,
276 /// C entry point for a probing parser.
278 pub unsafe extern "C" fn rs_ike_probing_parser(
279 _flow: *const Flow, direction: u8, input: *const u8, input_len: u32, rdir: *mut u8,
282 // at least the ISAKMP_HEADER must be there, not ALPROTO_UNKNOWN because over UDP
283 return ALPROTO_FAILED;
286 if !input.is_null() {
287 let slice = build_slice!(input, input_len as usize);
288 if probe(slice, direction.into(), rdir) {
292 return ALPROTO_FAILED;
296 pub extern "C" fn rs_ike_state_new(
297 _orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto,
298 ) -> *mut std::os::raw::c_void {
299 let state = IKEState::default();
300 let boxed = Box::new(state);
301 return Box::into_raw(boxed) as *mut _;
305 pub unsafe extern "C" fn rs_ike_state_free(state: *mut std::os::raw::c_void) {
307 std::mem::drop(Box::from_raw(state as *mut IKEState));
311 pub unsafe extern "C" fn rs_ike_state_tx_free(state: *mut std::os::raw::c_void, tx_id: u64) {
312 let state = cast_pointer!(state, IKEState);
313 state.free_tx(tx_id);
317 pub unsafe extern "C" fn rs_ike_parse_request(
318 _flow: *const Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void,
319 input: *const u8, input_len: u32, _data: *const std::os::raw::c_void, _flags: u8,
320 ) -> AppLayerResult {
321 let state = cast_pointer!(state, IKEState);
322 let buf = build_slice!(input, input_len as usize);
324 return state.handle_input(buf, Direction::ToServer);
328 pub unsafe extern "C" fn rs_ike_parse_response(
329 _flow: *const Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void,
330 input: *const u8, input_len: u32, _data: *const std::os::raw::c_void, _flags: u8,
331 ) -> AppLayerResult {
332 let state = cast_pointer!(state, IKEState);
333 let buf = build_slice!(input, input_len as usize);
334 return state.handle_input(buf, Direction::ToClient);
338 pub unsafe extern "C" fn rs_ike_state_get_tx(
339 state: *mut std::os::raw::c_void, tx_id: u64,
340 ) -> *mut std::os::raw::c_void {
341 let state = cast_pointer!(state, IKEState);
342 match state.get_tx(tx_id) {
344 return tx as *const _ as *mut _;
347 return std::ptr::null_mut();
353 pub unsafe extern "C" fn rs_ike_state_get_tx_count(state: *mut std::os::raw::c_void) -> u64 {
354 let state = cast_pointer!(state, IKEState);
359 pub extern "C" fn rs_ike_state_progress_completion_status(_direction: u8) -> std::os::raw::c_int {
360 // This parser uses 1 to signal transaction completion status.
365 pub extern "C" fn rs_ike_tx_get_alstate_progress(
366 _tx: *mut std::os::raw::c_void, _direction: u8,
367 ) -> std::os::raw::c_int {
372 pub unsafe extern "C" fn rs_ike_tx_get_logged(
373 _state: *mut std::os::raw::c_void, tx: *mut std::os::raw::c_void,
375 let tx = cast_pointer!(tx, IKETransaction);
376 return tx.logged.get();
380 pub unsafe extern "C" fn rs_ike_tx_set_logged(
381 _state: *mut std::os::raw::c_void, tx: *mut std::os::raw::c_void, logged: u32,
383 let tx = cast_pointer!(tx, IKETransaction);
384 tx.logged.set(logged);
387 static mut ALPROTO_IKE : AppProto = ALPROTO_UNKNOWN;
389 // Parser name as a C style string.
390 const PARSER_NAME: &'static [u8] = b"ike\0";
391 const PARSER_ALIAS: &'static [u8] = b"ikev2\0";
393 export_tx_data_get!(rs_ike_get_tx_data, IKETransaction);
396 pub unsafe extern "C" fn rs_ike_register_parser() {
397 let default_port = CString::new("500").unwrap();
398 let parser = RustParser {
399 name : PARSER_NAME.as_ptr() as *const std::os::raw::c_char,
400 default_port : default_port.as_ptr(),
401 ipproto : core::IPPROTO_UDP,
402 probe_ts : Some(rs_ike_probing_parser),
403 probe_tc : Some(rs_ike_probing_parser),
406 state_new : rs_ike_state_new,
407 state_free : rs_ike_state_free,
408 tx_free : rs_ike_state_tx_free,
409 parse_ts : rs_ike_parse_request,
410 parse_tc : rs_ike_parse_response,
411 get_tx_count : rs_ike_state_get_tx_count,
412 get_tx : rs_ike_state_get_tx,
415 tx_get_progress : rs_ike_tx_get_alstate_progress,
416 get_eventinfo : Some(IkeEvent::get_event_info),
417 get_eventinfo_byid : Some(IkeEvent::get_event_info_by_id),
418 localstorage_new : None,
419 localstorage_free : None,
421 get_tx_iterator : Some(applayer::state_get_tx_iterator::<IKEState, IKETransaction>),
422 get_tx_data : rs_ike_get_tx_data,
423 apply_tx_config : None,
424 flags : APP_LAYER_PARSER_OPT_UNIDIR_TXS,
428 let ip_proto_str = CString::new("udp").unwrap();
430 if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
431 let alproto = AppLayerRegisterProtocolDetection(&parser, 1);
432 ALPROTO_IKE = alproto;
433 if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
434 let _ = AppLayerRegisterParser(&parser, alproto);
437 AppLayerRegisterParserAlias(
438 PARSER_NAME.as_ptr() as *const std::os::raw::c_char,
439 PARSER_ALIAS.as_ptr() as *const std::os::raw::c_char,
441 SCLogDebug!("Rust IKE parser registered.");
443 SCLogDebug!("Protocol detector and parser disabled for IKE.");