Coverage Report

Created: 2025-12-31 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/rust/src/ike/ike.rs
Line
Count
Source
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
#[derive(AppLayerEvent)]
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
271k
    fn default() -> Self {
65
271k
        Self::new()
66
271k
    }
67
}
68
69
impl IkeHeaderWrapper {
70
271k
    pub fn new() -> Self {
71
271k
        Self {
72
271k
            spi_initiator: String::new(),
73
271k
            spi_responder: String::new(),
74
271k
            maj_ver: 0,
75
271k
            min_ver: 0,
76
271k
            msg_id: 0,
77
271k
            flags: 0,
78
271k
            ikev1_transforms: Vec::new(),
79
271k
            ikev2_transforms: Vec::new(),
80
271k
            ikev1_header: IkeV1Header::default(),
81
271k
            ikev2_header: IkeV2Header {
82
271k
                init_spi: 0,
83
271k
                resp_spi: 0,
84
271k
                next_payload: IkePayloadType::NoNextPayload,
85
271k
                maj_ver: 0,
86
271k
                min_ver: 0,
87
271k
                exch_type: IkeExchangeType(0),
88
271k
                flags: 0,
89
271k
                msg_id: 0,
90
271k
                length: 0,
91
271k
            },
92
271k
        }
93
271k
    }
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
427k
    fn id(&self) -> u64 {
121
427k
        self.tx_id
122
427k
    }
123
}
124
125
impl IKETransaction {
126
271k
    pub fn new(direction: Direction) -> Self {
127
271k
  Self {
128
271k
      direction,
129
271k
      tx_data: applayer::AppLayerTxData::for_direction(direction),
130
271k
      ..Default::default()
131
271k
  }
132
271k
    }
133
134
    /// Set an event.
135
290k
    pub fn set_event(&mut self, event: IkeEvent) {
136
290k
        self.tx_data.set_event(event as u8);
137
290k
    }
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
284k
    fn get_transaction_count(&self) -> usize {
152
284k
        self.transactions.len()
153
284k
    }
154
155
213k
    fn get_transaction_by_index(&self, index: usize) -> Option<&IKETransaction> {
156
213k
        self.transactions.get(index)
157
213k
    }
158
}
159
160
impl IKEState {
161
    // Free a transaction by ID.
162
200k
    fn free_tx(&mut self, tx_id: u64) {
163
200k
        let tx = self
164
200k
            .transactions
165
200k
            .iter()
166
200k
            .position(|tx| tx.tx_id == tx_id + 1);
167
200k
        debug_assert!(tx.is_some());
168
200k
        if let Some(idx) = tx {
169
200k
            let _ = self.transactions.remove(idx);
170
200k
        }
171
200k
    }
172
173
1.15k
    pub fn get_tx(&mut self, tx_id: u64) -> Option<&mut IKETransaction> {
174
1.15k
        self.transactions.iter_mut().find(|tx| tx.tx_id == tx_id + 1)
175
1.15k
    }
176
177
271k
    pub fn new_tx(&mut self, direction: Direction) -> IKETransaction {
178
271k
        let mut tx = IKETransaction::new(direction);
179
271k
        self.tx_id += 1;
180
271k
        tx.tx_id = self.tx_id;
181
271k
        return tx;
182
271k
    }
183
184
    /// Set an event. The event is set on the most recent transaction.
185
60.0k
    pub fn set_event(&mut self, event: IkeEvent) {
186
60.0k
        if let Some(tx) = self.transactions.last_mut() {
187
0
            tx.set_event(event);
188
60.0k
        } else {
189
60.0k
            SCLogDebug!(
190
60.0k
                "IKE: trying to set event {} on non-existing transaction",
191
60.0k
                event as u32
192
60.0k
            );
193
60.0k
        }
194
60.0k
    }
195
196
273k
    fn handle_input(&mut self, input: &[u8], direction: Direction) -> AppLayerResult {
197
        // We're not interested in empty requests.
198
273k
        if input.is_empty() {
199
0
            return AppLayerResult::ok();
200
273k
        }
201
202
273k
        let mut current = input;
203
273k
        match parse_isakmp_header(current) {
204
273k
            Ok((rem, isakmp_header)) => {
205
273k
                current = rem;
206
207
273k
                if isakmp_header.maj_ver != 1 && isakmp_header.maj_ver != 2 {
208
                    SCLogDebug!("Unsupported ISAKMP major_version");
209
1.76k
                    return AppLayerResult::err();
210
271k
                }
211
212
271k
                if isakmp_header.maj_ver == 1 {
213
138k
                    handle_ikev1(self, current, isakmp_header, direction);
214
138k
                } else if isakmp_header.maj_ver == 2 {
215
132k
                    handle_ikev2(self, current, isakmp_header, direction);
216
132k
                } else {
217
0
                    return AppLayerResult::err();
218
                }
219
271k
                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
476
                return AppLayerResult::err();
224
            }
225
            Err(_) => {
226
                SCLogDebug!("Error while parsing IKE packet");
227
0
                return AppLayerResult::err();
228
            }
229
        }
230
273k
    }
231
}
232
233
/// Probe to see if this input looks like a request or response.
234
1.05k
fn probe(input: &[u8], direction: Direction, rdir: *mut u8) -> bool {
235
1.05k
    match parse_isakmp_header(input) {
236
1.05k
        Ok((_, isakmp_header)) => {
237
1.05k
            if isakmp_header.maj_ver == 1 {
238
719
                if isakmp_header.resp_spi == 0 && direction != Direction::ToServer {
239
1
                    unsafe {
240
1
                        *rdir = Direction::ToServer.into();
241
1
                    }
242
718
                }
243
719
                return true;
244
333
            } else if isakmp_header.maj_ver == 2 {
245
191
                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
67
                    return false;
252
124
                }
253
124
                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
47
                    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
4
                    return false;
261
73
                }
262
263
73
                if isakmp_header.resp_spi == 0 && direction != Direction::ToServer {
264
1
                    unsafe {
265
1
                        *rdir = Direction::ToServer.into();
266
1
                    }
267
72
                }
268
73
                return true;
269
142
            }
270
271
142
            return false;
272
        }
273
0
        Err(_) => return false,
274
    }
275
1.05k
}
276
277
// C exports.
278
279
/// C entry point for a probing parser.
280
#[no_mangle]
281
1.06k
pub unsafe extern "C" fn rs_ike_probing_parser(
282
1.06k
    _flow: *const Flow, direction: u8, input: *const u8, input_len: u32, rdir: *mut u8,
283
1.06k
) -> AppProto {
284
1.06k
    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
1.05k
    }
288
289
1.05k
    if !input.is_null() {
290
1.05k
        let slice = build_slice!(input, input_len as usize);
291
1.05k
        if probe(slice, direction.into(), rdir) {
292
792
            return ALPROTO_IKE;
293
260
        }
294
0
    }
295
260
    return ALPROTO_FAILED;
296
1.06k
}
297
298
#[no_mangle]
299
5.81k
pub extern "C" fn rs_ike_state_new(
300
5.81k
    _orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto,
301
5.81k
) -> *mut std::os::raw::c_void {
302
5.81k
    let state = IKEState::default();
303
5.81k
    let boxed = Box::new(state);
304
5.81k
    return Box::into_raw(boxed) as *mut _;
305
5.81k
}
306
307
#[no_mangle]
308
5.81k
pub unsafe extern "C" fn rs_ike_state_free(state: *mut std::os::raw::c_void) {
309
    // Just unbox...
310
5.81k
    std::mem::drop(Box::from_raw(state as *mut IKEState));
311
5.81k
}
312
313
#[no_mangle]
314
200k
pub unsafe extern "C" fn rs_ike_state_tx_free(state: *mut std::os::raw::c_void, tx_id: u64) {
315
200k
    let state = cast_pointer!(state, IKEState);
316
200k
    state.free_tx(tx_id);
317
200k
}
318
319
#[no_mangle]
320
141k
pub unsafe extern "C" fn rs_ike_parse_request(
321
141k
    _flow: *const Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void,
322
141k
    stream_slice: StreamSlice, _data: *const std::os::raw::c_void,
323
141k
) -> AppLayerResult {
324
141k
    let state = cast_pointer!(state, IKEState);
325
141k
    return state.handle_input(stream_slice.as_slice(), Direction::ToServer);
326
141k
}
327
328
#[no_mangle]
329
131k
pub unsafe extern "C" fn rs_ike_parse_response(
330
131k
    _flow: *const Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void,
331
131k
    stream_slice: StreamSlice, _data: *const std::os::raw::c_void,
332
131k
) -> AppLayerResult {
333
131k
    let state = cast_pointer!(state, IKEState);
334
131k
    return state.handle_input(stream_slice.as_slice(), Direction::ToClient);
335
131k
}
336
337
#[no_mangle]
338
1.15k
pub unsafe extern "C" fn rs_ike_state_get_tx(
339
1.15k
    state: *mut std::os::raw::c_void, tx_id: u64,
340
1.15k
) -> *mut std::os::raw::c_void {
341
1.15k
    let state = cast_pointer!(state, IKEState);
342
1.15k
    match state.get_tx(tx_id) {
343
1.15k
        Some(tx) => {
344
1.15k
            return tx as *const _ as *mut _;
345
        }
346
        None => {
347
2
            return std::ptr::null_mut();
348
        }
349
    }
350
1.15k
}
351
352
#[no_mangle]
353
829k
pub unsafe extern "C" fn rs_ike_state_get_tx_count(state: *mut std::os::raw::c_void) -> u64 {
354
829k
    let state = cast_pointer!(state, IKEState);
355
829k
    return state.tx_id;
356
829k
}
357
358
#[no_mangle]
359
0
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.
361
0
    return 1;
362
0
}
363
364
#[no_mangle]
365
422k
pub extern "C" fn rs_ike_tx_get_alstate_progress(
366
422k
    _tx: *mut std::os::raw::c_void, _direction: u8,
367
422k
) -> std::os::raw::c_int {
368
422k
    return 1;
369
422k
}
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
432
34
    let ip_proto_str = CString::new("udp").unwrap();
433
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
        );
445
        SCLogDebug!("Rust IKE parser registered.");
446
0
    } else {
447
0
        SCLogDebug!("Protocol detector and parser disabled for IKE.");
448
0
    }
449
34
}