sawp_ike/lib.rs
1//! An Internet Key Exchange (IKE) v1 and v2 parser.
2//!
3//! Given bytes and a [`sawp::parser::Direction`], it will attempt to parse the bytes
4//! and return a [`Message`]. The parser will inform the caller about errors if no
5//! message is returned and warnings if it was parsed but had nonstandard or erroneous
6//! data (see [`sawp::parser::Parse`] for details on possible return types).
7//!
8//! This parser keeps state for the current session so it is expected to create one
9//! parser per session.
10//!
11//! The following references were used to create this module:
12//!
13//! [ISAKMP](https://www.rfc-editor.org/rfc/rfc2408.html)
14//!
15//! [IKE v1](https://www.rfc-editor.org/rfc/rfc2409.html)
16//!
17//! [IKE v2 Fibre Channel](https://www.rfc-editor.org/rfc/rfc4595.html)
18//!
19//! [IKE v2](https://www.rfc-editor.org/rfc/rfc7296.html)
20//!
21//! [Group Key Management using IKEv2](https://datatracker.ietf.org/doc/draft-yeung-g-ikev2)
22//!
23//! # Example
24//! ```
25//! use sawp::parser::{Direction, Parse};
26//! use sawp::error::Error;
27//! use sawp::error::ErrorKind;
28//! use sawp_ike::{Ike, Message};
29//!
30//! fn parse_bytes(input: &[u8]) -> std::result::Result<&[u8], Error> {
31//! let ike = Ike::default();
32//! let mut bytes = input;
33//! while bytes.len() > 0 {
34//! // If we know that this is a request or response, change the Direction
35//! // for a more accurate parsing
36//! match ike.parse(bytes, Direction::Unknown) {
37//! // The parser succeeded and returned the remaining bytes and the parsed ike message
38//! Ok((rest, Some(message))) => {
39//! println!("IKE message: {:?}", message);
40//! bytes = rest;
41//! }
42//! // The parser recognized that this might be ike and made some progress,
43//! // but more bytes are needed to parse a full message
44//! Ok((rest, None)) => return Ok(rest),
45//! // The parser was unable to determine whether this was ike or not and more
46//! // bytes are needed
47//! Err(Error { kind: ErrorKind::Incomplete(_) }) => return Ok(bytes),
48//! // The parser determined that this was not ike
49//! Err(e) => return Err(e)
50//! }
51//! }
52//!
53//! Ok(bytes)
54//! }
55//! ```
56
57#![deny(clippy::integer_arithmetic)]
58
59pub mod header;
60pub mod payloads;
61
62use header::{Header, IkeFlags, HEADER_LEN};
63use payloads::{Payload, PayloadType};
64
65use sawp::error::Result;
66use sawp::parser::{Direction, Parse};
67use sawp::probe::Probe;
68use sawp::protocol::Protocol;
69use sawp_flags::{BitFlags, Flag, Flags};
70
71/// FFI structs and Accessors
72#[cfg(feature = "ffi")]
73mod ffi;
74
75#[cfg(feature = "ffi")]
76use sawp_ffi::GenerateFFI;
77
78use nom::bytes::streaming::{tag, take};
79use nom::combinator::opt;
80use nom::number::streaming::be_u32;
81use nom::sequence::tuple;
82
83type IResult<'a, O> = nom::IResult<&'a [u8], O, sawp::error::NomError<&'a [u8]>>;
84
85/// Classes of errors that can be returned by this parser.
86#[repr(u16)]
87#[derive(Clone, Copy, Debug, PartialEq, Eq, BitFlags)]
88pub enum ErrorFlags {
89 /// Unknown Exchange number
90 UnknownExchange = 0b0000_0000_0000_0001,
91 /// Unknown Payload number
92 UnknownPayload = 0b0000_0000_0000_0010,
93 /// Found a payload in an invalid location
94 InvalidPayload = 0b0000_0000_0000_0100,
95 /// Known payload found which we have no parser for
96 UnimplementedPayload = 0b0000_0000_0000_1000,
97 /// Message ID was nonzero in an initiation message
98 NonZeroMessageIdInInit = 0b0000_0000_0001_0000,
99 /// Responder SPI was nonzero in an initiation message
100 NonZeroResponderSpiInInit = 0b0000_0000_0010_0000,
101 /// Responder SPI was not set in a response message
102 ZeroResponderSpiInResponse = 0b0000_0000_0100_0000,
103 /// Non-Zero reserved field found
104 NonZeroReserved = 0b0000_0000_1000_0000,
105 /// Invalid length in a payload
106 ///
107 /// Typically indicative of a length which is too short to accomodate the generic payload
108 /// header
109 InvalidLength = 0b0000_0001_0000_0000,
110 /// Header flags were invalid.
111 ///
112 /// Either a nonexistant flag bit was set or both IKEv1 and IKEv2 flags were set at the same
113 /// time.
114 InvalidFlags = 0b0000_0010_0000_0000,
115}
116
117impl ErrorFlags {
118 fn flatten(input: &[Flags<Self, u16>]) -> Flags<Self, u16> {
119 input.iter().fold(Self::none(), |acc, e| acc | *e)
120 }
121}
122
123/// The parsed message.
124///
125/// [`Message::Ike`] is a parsed IKE v1 or v2 message.
126///
127/// [`Message::Esp`] is an encapsulated security payload. These are seen when the
128/// encrypted communications are sent over UDP on the same 5-tuple as the IKE messages, typically on port 4500.
129/// When IKE operates over TCP, no ESP will be parsed as they encrypted data is sent without a
130/// transport layer (i.e. layer 3 Ethernet header followed by encrypted payload).
131#[cfg_attr(feature = "ffi", derive(GenerateFFI))]
132#[cfg_attr(feature = "ffi", sawp_ffi(prefix = "sawp_ike"))]
133#[derive(Debug, PartialEq, Eq)]
134pub enum Message {
135 /// An IKE payload
136 Ike(IkeMessage),
137 /// Encapsulating Security Payload
138 Esp(EspMessage),
139}
140
141/// The parsed IKEv1 or v2 message
142#[cfg_attr(feature = "ffi", derive(GenerateFFI))]
143#[cfg_attr(feature = "ffi", sawp_ffi(prefix = "sawp_ike"))]
144#[derive(Debug, PartialEq, Eq)]
145pub struct IkeMessage {
146 /// The header
147 pub header: Header,
148 /// The array of payloads following the header
149 pub payloads: Vec<Payload>,
150 /// Encrypted Data, if IKEv1 and ENCRYPTED flag is set
151 pub encrypted_data: Vec<u8>,
152 /// Errors encountered while parsing
153 #[cfg_attr(feature = "ffi", sawp_ffi(flag = "u16"))]
154 pub error_flags: Flags<ErrorFlags>,
155}
156
157/// If UDP encapsulation is present, the metadata associated with it is parsed.
158///
159/// The full encrypted payload, tail padding, and integrity check is not parsed.
160#[cfg_attr(feature = "ffi", derive(GenerateFFI))]
161#[cfg_attr(feature = "ffi", sawp_ffi(prefix = "sawp_ike"))]
162#[derive(Debug, PartialEq, Eq)]
163pub struct EspMessage {
164 pub spi: u32,
165 pub sequence: u32,
166}
167
168/// Parser handle.
169///
170/// # Notes
171/// The parser assumes one parser per session as it stores session state. A given session should
172/// re-use the same parser as more data is made available and each session should have its own
173/// parser.
174///
175/// # FFI SAFETY
176/// This type is not [`Sync`] and this must be considered in FFI uses. This struct may be sent from
177/// one thread to another but it may not be shared between threads without locking access. In C++
178/// this means a std::shared_ptr<Ike> is not enough! std::mutex or other locking primitives must be
179/// used to ensure data races do not occur.
180#[derive(Debug, Default)]
181pub struct Ike {
182 // On port 4500 ESP payloads are encapsulated in UDP but on port 500 they are not.
183 // When UDP encapsulation for ESP is present IKE payloads are prefixed with 4 octets
184 // 0x00 to differentiate them from ESP.
185 //
186 // When an IKE payload was prefixed with 0x00 we should treat any seen packets without
187 // it as ESP payloads. When IKE was not prefixed with 0x00 then all packets should
188 // be IKE. As such we have 3 states - ESP encapsulation present (Some(true)), ESP
189 // encapsulation not present (Some(false)), and not yet determined (None).
190 saw_udp_encapsulation: std::cell::Cell<Option<bool>>,
191}
192
193impl Probe<'_> for Ike {}
194
195impl Protocol<'_> for Ike {
196 type Message = Message;
197
198 fn name() -> &'static str {
199 "ike"
200 }
201}
202
203impl<'a> Parse<'a> for Ike {
204 fn parse(
205 &self,
206 input: &'a [u8],
207 _direction: Direction,
208 ) -> Result<(&'a [u8], Option<Self::Message>)> {
209 let input = match self.saw_udp_encapsulation.get() {
210 // Previously saw encapsulation
211 Some(true) => {
212 let (input, non_esp_marker) = opt(tag(b"\x00\x00\x00\x00"))(input)?;
213 if non_esp_marker.is_some() {
214 // marker present, must be IKE. Continue
215 input
216 } else {
217 let (input, (spi, sequence)) = tuple((be_u32, be_u32))(input)?;
218 return Ok((input, Some(Message::Esp(EspMessage { spi, sequence }))));
219 }
220 }
221 // Previously saw no encapsulation
222 Some(false) => {
223 // Parse like normal
224 input
225 }
226 // Not yet determined
227 None => {
228 let (input, non_esp_marker) = opt(tag(b"\x00\x00\x00\x00"))(input)?;
229 self.saw_udp_encapsulation
230 .set(Some(non_esp_marker.is_some()));
231 input
232 }
233 };
234
235 let (input, (header, header_error_flags)) = Header::parse(input)?;
236
237 // subtracting HEADER_LEN is safe, length verified in Header::parse
238 let (input, mut payload_input) = take(header.length.saturating_sub(HEADER_LEN))(input)?;
239
240 let mut next_payload = header.next_payload;
241 let mut payloads = Vec::new();
242 let mut payload_error_flags = ErrorFlags::none();
243
244 if header.major_version == 1 && header.flags.contains(IkeFlags::ENCRYPTED) {
245 let message = Message::Ike(IkeMessage {
246 header,
247 payloads: Vec::new(),
248 encrypted_data: payload_input.to_vec(),
249 error_flags: ErrorFlags::none(),
250 });
251 return Ok((input, Some(message)));
252 }
253
254 let parse = if header.major_version == 1 {
255 Payload::parse_v1
256 } else {
257 Payload::parse_v2
258 };
259
260 // While we have a next payload and they are not encrypted
261 // In the case of encryption, all the payloads are encrypted and
262 // are inside the encrypted data block.
263 while next_payload != PayloadType::NoNextPayload {
264 let should_early_break = next_payload == PayloadType::EncryptedAndAuthenticated
265 || next_payload == PayloadType::EncryptedAndAuthenticatedFragment;
266 let (tmp_payload_input, (payload, errors)) = parse(payload_input, next_payload)?;
267 // We need _payload_input as an interim variable until de structuring assignments are
268 // supported in our MSRV rust version
269 payload_input = tmp_payload_input;
270 next_payload = payload.next_payload;
271 payloads.push(payload);
272 payload_error_flags |= errors;
273
274 if should_early_break {
275 break;
276 }
277 }
278
279 let error_flags = header_error_flags | payload_error_flags;
280
281 let message = Message::Ike(IkeMessage {
282 header,
283 payloads,
284 encrypted_data: Vec::with_capacity(0),
285 error_flags,
286 });
287
288 Ok((input, Some(message)))
289 }
290}