Coverage Report

Created: 2025-07-23 07:29

/src/suricata7/rust/src/ike/ike.rs
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2020-2021 Open Information Security Foundation
2
 *
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
5
 * Software Foundation.
6
 *
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.
11
 *
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
15
 * 02110-1301, USA.
16
 */
17
18
// Author: Frank Honza <frank.honza@dcso.de>
19
20
extern crate ipsec_parser;
21
use self::ipsec_parser::*;
22
23
use crate::applayer;
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::*;
29
use nom7::Err;
30
use std;
31
use std::collections::HashSet;
32
use std::ffi::CString;
33
34
99
#[derive(AppLayerEvent)]
Unexecuted instantiation: <suricata::ike::ike::IkeEvent as suricata::applayer::AppLayerEvent>::from_id
<suricata::ike::ike::IkeEvent as suricata::applayer::AppLayerEvent>::as_i32
Line
Count
Source
34
44
#[derive(AppLayerEvent)]
Unexecuted instantiation: <suricata::ike::ike::IkeEvent as suricata::applayer::AppLayerEvent>::to_cstring
<suricata::ike::ike::IkeEvent as suricata::applayer::AppLayerEvent>::from_string
Line
Count
Source
34
55
#[derive(AppLayerEvent)]
<suricata::ike::ike::IkeEvent as suricata::applayer::AppLayerEvent>::get_event_info
Line
Count
Source
34
55
#[derive(AppLayerEvent)]
Unexecuted instantiation: <suricata::ike::ike::IkeEvent as suricata::applayer::AppLayerEvent>::get_event_info_by_id
35
pub enum IkeEvent {
36
    MalformedData,
37
    NoEncryption,
38
    WeakCryptoEnc,
39
    WeakCryptoPrf,
40
    WeakCryptoDh,
41
    WeakCryptoAuth,
42
    WeakCryptoNoDh,
43
    WeakCryptoNoAuth,
44
    InvalidProposal,
45
    UnknownProposal,
46
    PayloadExtraData,
47
    MultipleServerProposal,
48
}
49
50
pub struct IkeHeaderWrapper {
51
    pub spi_initiator: String,
52
    pub spi_responder: String,
53
    pub maj_ver: u8,
54
    pub min_ver: u8,
55
    pub msg_id: u32,
56
    pub flags: u8,
57
    pub ikev1_transforms: Vec<Vec<SaAttribute>>,
58
    pub ikev2_transforms: Vec<IkeV2Transform>,
59
    pub ikev1_header: IkeV1Header,
60
    pub ikev2_header: IkeV2Header,
61
}
62
63
impl Default for IkeHeaderWrapper {
64
364k
    fn default() -> Self {
65
364k
        Self::new()
66
364k
    }
67
}
68
69
impl IkeHeaderWrapper {
70
364k
    pub fn new() -> Self {
71
364k
        Self {
72
364k
            spi_initiator: String::new(),
73
364k
            spi_responder: String::new(),
74
364k
            maj_ver: 0,
75
364k
            min_ver: 0,
76
364k
            msg_id: 0,
77
364k
            flags: 0,
78
364k
            ikev1_transforms: Vec::new(),
79
364k
            ikev2_transforms: Vec::new(),
80
364k
            ikev1_header: IkeV1Header::default(),
81
364k
            ikev2_header: IkeV2Header {
82
364k
                init_spi: 0,
83
364k
                resp_spi: 0,
84
364k
                next_payload: IkePayloadType::NoNextPayload,
85
364k
                maj_ver: 0,
86
364k
                min_ver: 0,
87
364k
                exch_type: IkeExchangeType(0),
88
364k
                flags: 0,
89
364k
                msg_id: 0,
90
364k
                length: 0,
91
364k
            },
92
364k
        }
93
364k
    }
94
}
95
96
#[derive(Default)]
97
pub struct IkePayloadWrapper {
98
    pub ikev1_payload_types: Option<HashSet<u8>>,
99
    pub ikev2_payload_types: Vec<IkePayloadType>,
100
}
101
102
#[derive(Default)]
103
pub struct IKETransaction {
104
    tx_id: u64,
105
106
    pub ike_version: u8,
107
    pub direction: Direction,
108
    pub hdr: IkeHeaderWrapper,
109
    pub payload_types: IkePayloadWrapper,
110
    pub notify_types: Vec<NotifyType>,
111
112
    /// errors seen during exchange
113
    pub errors: u32,
114
115
    logged: LoggerFlags,
116
    pub tx_data: applayer::AppLayerTxData,
117
}
118
119
impl Transaction for IKETransaction {
120
565k
    fn id(&self) -> u64 {
121
565k
        self.tx_id
122
565k
    }
123
}
124
125
impl IKETransaction {
126
364k
    pub fn new(direction: Direction) -> Self {
127
364k
  Self {
128
364k
      direction,
129
364k
      tx_data: applayer::AppLayerTxData::for_direction(direction),
130
364k
      ..Default::default()
131
364k
  }
132
364k
    }
133
134
    /// Set an event.
135
206k
    pub fn set_event(&mut self, event: IkeEvent) {
136
206k
        self.tx_data.set_event(event as u8);
137
206k
    }
138
}
139
140
#[derive(Default)]
141
pub struct IKEState {
142
    state_data: AppLayerStateData,
143
    tx_id: u64,
144
    pub transactions: Vec<IKETransaction>,
145
146
    pub ikev1_container: Ikev1Container,
147
    pub ikev2_container: Ikev2Container,
148
}
149
150
impl State<IKETransaction> for IKEState {
151
375k
    fn get_transaction_count(&self) -> usize {
152
375k
        self.transactions.len()
153
375k
    }
154
155
282k
    fn get_transaction_by_index(&self, index: usize) -> Option<&IKETransaction> {
156
282k
        self.transactions.get(index)
157
282k
    }
158
}
159
160
impl IKEState {
161
    // Free a transaction by ID.
162
272k
    fn free_tx(&mut self, tx_id: u64) {
163
272k
        let tx = self
164
272k
            .transactions
165
272k
            .iter()
166
272k
            .position(|tx| tx.tx_id == tx_id + 1);
167
272k
        debug_assert!(tx.is_some());
168
272k
        if let Some(idx) = tx {
169
272k
            let _ = self.transactions.remove(idx);
170
272k
        }
171
272k
    }
172
173
450
    pub fn get_tx(&mut self, tx_id: u64) -> Option<&mut IKETransaction> {
174
450
        self.transactions.iter_mut().find(|tx| tx.tx_id == tx_id + 1)
175
450
    }
176
177
364k
    pub fn new_tx(&mut self, direction: Direction) -> IKETransaction {
178
364k
        let mut tx = IKETransaction::new(direction);
179
364k
        self.tx_id += 1;
180
364k
        tx.tx_id = self.tx_id;
181
364k
        return tx;
182
364k
    }
183
184
    /// Set an event. The event is set on the most recent transaction.
185
77.9k
    pub fn set_event(&mut self, event: IkeEvent) {
186
77.9k
        if let Some(tx) = self.transactions.last_mut() {
187
0
            tx.set_event(event);
188
77.9k
        } else {
189
77.9k
            SCLogDebug!(
190
77.9k
                "IKE: trying to set event {} on non-existing transaction",
191
77.9k
                event as u32
192
77.9k
            );
193
77.9k
        }
194
77.9k
    }
195
196
366k
    fn handle_input(&mut self, input: &[u8], direction: Direction) -> AppLayerResult {
197
366k
        // We're not interested in empty requests.
198
366k
        if input.is_empty() {
199
0
            return AppLayerResult::ok();
200
366k
        }
201
366k
202
366k
        let mut current = input;
203
366k
        match parse_isakmp_header(current) {
204
366k
            Ok((rem, isakmp_header)) => {
205
366k
                current = rem;
206
366k
207
366k
                if isakmp_header.maj_ver != 1 && isakmp_header.maj_ver != 2 {
208
                    SCLogDebug!("Unsupported ISAKMP major_version");
209
2.34k
                    return AppLayerResult::err();
210
364k
                }
211
364k
212
364k
                if isakmp_header.maj_ver == 1 {
213
195k
                    handle_ikev1(self, current, isakmp_header, direction);
214
195k
                } else if isakmp_header.maj_ver == 2 {
215
168k
                    handle_ikev2(self, current, isakmp_header, direction);
216
168k
                } else {
217
0
                    return AppLayerResult::err();
218
                }
219
364k
                return AppLayerResult::ok(); // todo either remove outer loop or check header length-field if we have completely read everything
220
            }
221
            Err(Err::Incomplete(_)) => {
222
                SCLogDebug!("Insufficient data while parsing IKE");
223
489
                return AppLayerResult::err();
224
            }
225
            Err(_) => {
226
                SCLogDebug!("Error while parsing IKE packet");
227
0
                return AppLayerResult::err();
228
            }
229
        }
230
366k
    }
231
}
232
233
/// Probe to see if this input looks like a request or response.
234
827
fn probe(input: &[u8], direction: Direction, rdir: *mut u8) -> bool {
235
827
    match parse_isakmp_header(input) {
236
827
        Ok((_, isakmp_header)) => {
237
827
            if isakmp_header.maj_ver == 1 {
238
530
                if isakmp_header.resp_spi == 0 && direction != Direction::ToServer {
239
1
                    unsafe {
240
1
                        *rdir = Direction::ToServer.into();
241
1
                    }
242
529
                }
243
530
                return true;
244
297
            } else if isakmp_header.maj_ver == 2 {
245
200
                if isakmp_header.min_ver != 0 {
246
                    SCLogDebug!(
247
                        "ipsec_probe: could be ipsec, but with unsupported/invalid version {}.{}",
248
                        isakmp_header.maj_ver,
249
                        isakmp_header.min_ver
250
                    );
251
59
                    return false;
252
141
                }
253
141
                if isakmp_header.exch_type < 34 || isakmp_header.exch_type > 37 {
254
                    SCLogDebug!("ipsec_probe: could be ipsec, but with unsupported/invalid exchange type {}",
255
                           isakmp_header.exch_type);
256
64
                    return false;
257
77
                }
258
77
                if isakmp_header.length as usize != input.len() {
259
                    SCLogDebug!("ipsec_probe: could be ipsec, but length does not match");
260
10
                    return false;
261
67
                }
262
67
263
67
                if isakmp_header.resp_spi == 0 && direction != Direction::ToServer {
264
1
                    unsafe {
265
1
                        *rdir = Direction::ToServer.into();
266
1
                    }
267
66
                }
268
67
                return true;
269
97
            }
270
97
271
97
            return false;
272
        }
273
0
        Err(_) => return false,
274
    }
275
827
}
276
277
// C exports.
278
279
/// C entry point for a probing parser.
280
#[no_mangle]
281
836
pub unsafe extern "C" fn rs_ike_probing_parser(
282
836
    _flow: *const Flow, direction: u8, input: *const u8, input_len: u32, rdir: *mut u8,
283
836
) -> AppProto {
284
836
    if input_len < 28 {
285
        // at least the ISAKMP_HEADER must be there, not ALPROTO_UNKNOWN because over UDP
286
9
        return ALPROTO_FAILED;
287
827
    }
288
827
289
827
    if !input.is_null() {
290
827
        let slice = build_slice!(input, input_len as usize);
291
827
        if probe(slice, direction.into(), rdir) {
292
597
            return ALPROTO_IKE;
293
230
        }
294
0
    }
295
230
    return ALPROTO_FAILED;
296
836
}
297
298
#[no_mangle]
299
5.92k
pub extern "C" fn rs_ike_state_new(
300
5.92k
    _orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto,
301
5.92k
) -> *mut std::os::raw::c_void {
302
5.92k
    let state = IKEState::default();
303
5.92k
    let boxed = Box::new(state);
304
5.92k
    return Box::into_raw(boxed) as *mut _;
305
5.92k
}
306
307
#[no_mangle]
308
5.92k
pub unsafe extern "C" fn rs_ike_state_free(state: *mut std::os::raw::c_void) {
309
5.92k
    // Just unbox...
310
5.92k
    std::mem::drop(Box::from_raw(state as *mut IKEState));
311
5.92k
}
312
313
#[no_mangle]
314
272k
pub unsafe extern "C" fn rs_ike_state_tx_free(state: *mut std::os::raw::c_void, tx_id: u64) {
315
272k
    let state = cast_pointer!(state, IKEState);
316
272k
    state.free_tx(tx_id);
317
272k
}
318
319
#[no_mangle]
320
189k
pub unsafe extern "C" fn rs_ike_parse_request(
321
189k
    _flow: *const Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void,
322
189k
    stream_slice: StreamSlice, _data: *const std::os::raw::c_void,
323
189k
) -> AppLayerResult {
324
189k
    let state = cast_pointer!(state, IKEState);
325
189k
    return state.handle_input(stream_slice.as_slice(), Direction::ToServer);
326
189k
}
327
328
#[no_mangle]
329
177k
pub unsafe extern "C" fn rs_ike_parse_response(
330
177k
    _flow: *const Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void,
331
177k
    stream_slice: StreamSlice, _data: *const std::os::raw::c_void,
332
177k
) -> AppLayerResult {
333
177k
    let state = cast_pointer!(state, IKEState);
334
177k
    return state.handle_input(stream_slice.as_slice(), Direction::ToClient);
335
177k
}
336
337
#[no_mangle]
338
450
pub unsafe extern "C" fn rs_ike_state_get_tx(
339
450
    state: *mut std::os::raw::c_void, tx_id: u64,
340
450
) -> *mut std::os::raw::c_void {
341
450
    let state = cast_pointer!(state, IKEState);
342
450
    match state.get_tx(tx_id) {
343
450
        Some(tx) => {
344
450
            return tx as *const _ as *mut _;
345
        }
346
        None => {
347
0
            return std::ptr::null_mut();
348
        }
349
    }
350
450
}
351
352
#[no_mangle]
353
1.10M
pub unsafe extern "C" fn rs_ike_state_get_tx_count(state: *mut std::os::raw::c_void) -> u64 {
354
1.10M
    let state = cast_pointer!(state, IKEState);
355
1.10M
    return state.tx_id;
356
1.10M
}
357
358
#[no_mangle]
359
0
pub extern "C" fn rs_ike_state_progress_completion_status(_direction: u8) -> std::os::raw::c_int {
360
0
    // This parser uses 1 to signal transaction completion status.
361
0
    return 1;
362
0
}
363
364
#[no_mangle]
365
563k
pub extern "C" fn rs_ike_tx_get_alstate_progress(
366
563k
    _tx: *mut std::os::raw::c_void, _direction: u8,
367
563k
) -> std::os::raw::c_int {
368
563k
    return 1;
369
563k
}
370
371
#[no_mangle]
372
0
pub unsafe extern "C" fn rs_ike_tx_get_logged(
373
0
    _state: *mut std::os::raw::c_void, tx: *mut std::os::raw::c_void,
374
0
) -> u32 {
375
0
    let tx = cast_pointer!(tx, IKETransaction);
376
0
    return tx.logged.get();
377
0
}
378
379
#[no_mangle]
380
0
pub unsafe extern "C" fn rs_ike_tx_set_logged(
381
0
    _state: *mut std::os::raw::c_void, tx: *mut std::os::raw::c_void, logged: u32,
382
0
) {
383
0
    let tx = cast_pointer!(tx, IKETransaction);
384
0
    tx.logged.set(logged);
385
0
}
386
387
static mut ALPROTO_IKE: AppProto = ALPROTO_UNKNOWN;
388
389
// Parser name as a C style string.
390
const PARSER_NAME: &[u8] = b"ike\0";
391
const PARSER_ALIAS: &[u8] = b"ikev2\0";
392
393
export_tx_data_get!(rs_ike_get_tx_data, IKETransaction);
394
export_state_data_get!(rs_ike_get_state_data, IKEState);
395
396
#[no_mangle]
397
34
pub unsafe extern "C" fn rs_ike_register_parser() {
398
34
    let default_port = CString::new("500").unwrap();
399
34
    let parser = RustParser {
400
34
        name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char,
401
34
        default_port: default_port.as_ptr(),
402
34
        ipproto: core::IPPROTO_UDP,
403
34
        probe_ts: Some(rs_ike_probing_parser),
404
34
        probe_tc: Some(rs_ike_probing_parser),
405
34
        min_depth: 0,
406
34
        max_depth: 16,
407
34
        state_new: rs_ike_state_new,
408
34
        state_free: rs_ike_state_free,
409
34
        tx_free: rs_ike_state_tx_free,
410
34
        parse_ts: rs_ike_parse_request,
411
34
        parse_tc: rs_ike_parse_response,
412
34
        get_tx_count: rs_ike_state_get_tx_count,
413
34
        get_tx: rs_ike_state_get_tx,
414
34
        tx_comp_st_ts: 1,
415
34
        tx_comp_st_tc: 1,
416
34
        tx_get_progress: rs_ike_tx_get_alstate_progress,
417
34
        get_eventinfo: Some(IkeEvent::get_event_info),
418
34
        get_eventinfo_byid: Some(IkeEvent::get_event_info_by_id),
419
34
        localstorage_new: None,
420
34
        localstorage_free: None,
421
34
        get_tx_files: None,
422
34
        get_tx_iterator: Some(applayer::state_get_tx_iterator::<IKEState, IKETransaction>),
423
34
        get_tx_data: rs_ike_get_tx_data,
424
34
        get_state_data: rs_ike_get_state_data,
425
34
        apply_tx_config: None,
426
34
        flags: 0,
427
34
        truncate: None,
428
34
        get_frame_id_by_name: None,
429
34
        get_frame_name_by_id: None,
430
34
    };
431
34
432
34
    let ip_proto_str = CString::new("udp").unwrap();
433
34
434
34
    if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
435
34
        let alproto = AppLayerRegisterProtocolDetection(&parser, 1);
436
34
        ALPROTO_IKE = alproto;
437
34
        if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
438
34
            let _ = AppLayerRegisterParser(&parser, alproto);
439
34
        }
440
441
34
        AppLayerRegisterParserAlias(
442
34
            PARSER_NAME.as_ptr() as *const std::os::raw::c_char,
443
34
            PARSER_ALIAS.as_ptr() as *const std::os::raw::c_char,
444
34
        );
445
        SCLogDebug!("Rust IKE parser registered.");
446
0
    } else {
447
0
        SCLogDebug!("Protocol detector and parser disabled for IKE.");
448
0
    }
449
34
}