Coverage Report

Created: 2026-09-06 07:25

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