/src/suricata8/rust/src/krb/krb5.rs
Line | Count | Source |
1 | | /* Copyright (C) 2017-2024 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 | | // written by Pierre Chifflier <chifflier@wzdftpd.net> |
19 | | |
20 | | use crate::applayer::{self, *}; |
21 | | use crate::core::*; |
22 | | use crate::direction::Direction; |
23 | | use crate::flow::Flow; |
24 | | use asn1_rs::FromDer; |
25 | | use der_parser::ber::Class; |
26 | | use der_parser::der::der_read_element_header; |
27 | | use kerberos_parser::krb5::{ |
28 | | EncryptionType, ErrorCode, KrbError, MessageType, PrincipalName, Realm, |
29 | | }; |
30 | | use kerberos_parser::krb5_parser; |
31 | | use nom7::number::streaming::be_u32; |
32 | | use nom7::{Err, IResult}; |
33 | | use std; |
34 | | use std::ffi::CString; |
35 | | use suricata_sys::sys::{ |
36 | | AppLayerParserState, AppProto, SCAppLayerParserConfParserEnabled, |
37 | | SCAppLayerParserRegisterLogger, SCAppLayerProtoDetectConfProtoDetectionEnabled, |
38 | | }; |
39 | | |
40 | | #[derive(AppLayerEvent)] |
41 | | pub enum KRB5Event { |
42 | | MalformedData, |
43 | | WeakEncryption, |
44 | | } |
45 | | |
46 | | pub struct KRB5State { |
47 | | state_data: AppLayerStateData, |
48 | | |
49 | | pub req_id: u8, |
50 | | |
51 | | pub record_ts: usize, |
52 | | pub record_tc: usize, |
53 | | |
54 | | /// List of transactions for this session |
55 | | transactions: Vec<KRB5Transaction>, |
56 | | |
57 | | /// tx counter for assigning incrementing id's to tx's |
58 | | tx_id: u64, |
59 | | } |
60 | | |
61 | | impl State<KRB5Transaction> for KRB5State { |
62 | 191k | fn get_transaction_count(&self) -> usize { |
63 | 191k | self.transactions.len() |
64 | 191k | } |
65 | | |
66 | 5 | fn get_transaction_by_index(&self, index: usize) -> Option<&KRB5Transaction> { |
67 | 5 | self.transactions.get(index) |
68 | 5 | } |
69 | | } |
70 | | |
71 | | pub struct KRB5Transaction { |
72 | | /// The message type: AS-REQ, AS-REP, etc. |
73 | | pub msg_type: MessageType, |
74 | | |
75 | | /// The client PrincipalName, if present |
76 | | pub cname: Option<PrincipalName>, |
77 | | /// The server Realm, if present |
78 | | pub realm: Option<Realm>, |
79 | | /// The server PrincipalName, if present |
80 | | pub sname: Option<PrincipalName>, |
81 | | |
82 | | /// Encryption used (only in AS-REP and TGS-REP) |
83 | | pub etype: Option<EncryptionType>, |
84 | | |
85 | | /// Encryption used for ticket |
86 | | pub ticket_etype: Option<EncryptionType>, |
87 | | |
88 | | /// Error code, if request has failed |
89 | | pub error_code: Option<ErrorCode>, |
90 | | |
91 | | /// Message type of request. For using in responses. |
92 | | pub req_type: Option<MessageType>, |
93 | | |
94 | | /// The internal transaction id |
95 | | id: u64, |
96 | | |
97 | | tx_data: applayer::AppLayerTxData, |
98 | | } |
99 | | |
100 | | impl Transaction for KRB5Transaction { |
101 | 9 | fn id(&self) -> u64 { |
102 | 9 | self.id |
103 | 9 | } |
104 | | } |
105 | | |
106 | | impl Default for KRB5State { |
107 | 0 | fn default() -> Self { |
108 | 0 | Self::new() |
109 | 0 | } |
110 | | } |
111 | | |
112 | | impl KRB5State { |
113 | 387 | pub fn new() -> KRB5State { |
114 | 387 | Self { |
115 | 387 | state_data: AppLayerStateData::new(), |
116 | 387 | req_id: 0, |
117 | 387 | record_ts: 0, |
118 | 387 | record_tc: 0, |
119 | 387 | transactions: Vec::new(), |
120 | 387 | tx_id: 0, |
121 | 387 | } |
122 | 387 | } |
123 | | |
124 | | /// Parse a Kerberos request message |
125 | | /// |
126 | | /// Returns 0 in case of success, or -1 on error |
127 | 56.7k | fn parse(&mut self, i: &[u8], flow: *mut Flow, direction: Direction) -> i32 { |
128 | 56.7k | match der_read_element_header(i) { |
129 | 56.6k | Ok((_rem, hdr)) => { |
130 | | // Kerberos messages start with an APPLICATION header |
131 | 56.6k | if hdr.class() != Class::Application { |
132 | 17.0k | return 0; |
133 | 39.5k | } |
134 | 39.5k | match hdr.tag().0 { |
135 | | 10 => { |
136 | 566 | let req = krb5_parser::parse_as_req(i); |
137 | 566 | if let Ok((_, kdc_req)) = req { |
138 | 1 | let mut tx = self.new_tx(direction); |
139 | 1 | tx.msg_type = MessageType::KRB_AS_REQ; |
140 | 1 | tx.cname = kdc_req.req_body.cname; |
141 | 1 | tx.realm = Some(kdc_req.req_body.realm); |
142 | 1 | tx.sname = kdc_req.req_body.sname; |
143 | 1 | tx.etype = None; |
144 | 1 | self.transactions.push(tx); |
145 | 1 | sc_app_layer_parser_trigger_raw_stream_inspection( |
146 | 1 | flow, |
147 | 1 | direction as i32, |
148 | 1 | ); |
149 | 565 | }; |
150 | 566 | self.req_id = 10; |
151 | | } |
152 | | 11 => { |
153 | 653 | let res = krb5_parser::parse_as_rep(i); |
154 | 653 | if let Ok((_, kdc_rep)) = res { |
155 | 0 | let mut tx = self.new_tx(direction); |
156 | 0 | tx.msg_type = MessageType::KRB_AS_REP; |
157 | 0 | if self.req_id > 0 { |
158 | 0 | // set request type only if previous message |
159 | 0 | // was a request |
160 | 0 | tx.req_type = Some(MessageType(self.req_id.into())); |
161 | 0 | } |
162 | 0 | tx.cname = Some(kdc_rep.cname); |
163 | 0 | tx.realm = Some(kdc_rep.crealm); |
164 | 0 | tx.sname = Some(kdc_rep.ticket.sname); |
165 | 0 | tx.ticket_etype = Some(kdc_rep.ticket.enc_part.etype); |
166 | 0 | tx.etype = Some(kdc_rep.enc_part.etype); |
167 | 0 | self.transactions.push(tx); |
168 | 0 | sc_app_layer_parser_trigger_raw_stream_inspection( |
169 | 0 | flow, |
170 | 0 | direction as i32, |
171 | | ); |
172 | 0 | if test_weak_encryption(kdc_rep.enc_part.etype) { |
173 | 0 | self.set_event(KRB5Event::WeakEncryption); |
174 | 0 | } |
175 | 653 | }; |
176 | 653 | self.req_id = 0; |
177 | | } |
178 | | 12 => { |
179 | 14.6k | let req = krb5_parser::parse_tgs_req(i); |
180 | 14.6k | if let Ok((_, kdc_req)) = req { |
181 | 1 | let mut tx = self.new_tx(direction); |
182 | 1 | tx.msg_type = MessageType::KRB_TGS_REQ; |
183 | 1 | tx.cname = kdc_req.req_body.cname; |
184 | 1 | tx.realm = Some(kdc_req.req_body.realm); |
185 | 1 | tx.sname = kdc_req.req_body.sname; |
186 | 1 | tx.etype = None; |
187 | 1 | self.transactions.push(tx); |
188 | 1 | sc_app_layer_parser_trigger_raw_stream_inspection( |
189 | 1 | flow, |
190 | 1 | direction as i32, |
191 | 1 | ); |
192 | 14.6k | }; |
193 | 14.6k | self.req_id = 12; |
194 | | } |
195 | | 13 => { |
196 | 601 | let res = krb5_parser::parse_tgs_rep(i); |
197 | 601 | if let Ok((_, kdc_rep)) = res { |
198 | 0 | let mut tx = self.new_tx(direction); |
199 | 0 | tx.msg_type = MessageType::KRB_TGS_REP; |
200 | 0 | if self.req_id > 0 { |
201 | 0 | // set request type only if previous message |
202 | 0 | // was a request |
203 | 0 | tx.req_type = Some(MessageType(self.req_id.into())); |
204 | 0 | } |
205 | 0 | tx.cname = Some(kdc_rep.cname); |
206 | 0 | tx.realm = Some(kdc_rep.crealm); |
207 | 0 | tx.ticket_etype = Some(kdc_rep.ticket.enc_part.etype); |
208 | 0 | tx.sname = Some(kdc_rep.ticket.sname); |
209 | 0 | tx.etype = Some(kdc_rep.enc_part.etype); |
210 | 0 | self.transactions.push(tx); |
211 | 0 | sc_app_layer_parser_trigger_raw_stream_inspection( |
212 | 0 | flow, |
213 | 0 | direction as i32, |
214 | | ); |
215 | 0 | if test_weak_encryption(kdc_rep.enc_part.etype) { |
216 | 0 | self.set_event(KRB5Event::WeakEncryption); |
217 | 0 | } |
218 | 601 | }; |
219 | 601 | self.req_id = 0; |
220 | | } |
221 | 420 | 14 => { |
222 | 420 | self.req_id = 14; |
223 | 420 | } |
224 | 374 | 15 => { |
225 | 374 | self.req_id = 0; |
226 | 374 | } |
227 | | 30 => { |
228 | 19.7k | let res = KrbError::from_der(i); |
229 | 19.7k | if let Ok((_, error)) = res { |
230 | 0 | let mut tx = self.new_tx(direction); |
231 | 0 | if self.req_id > 0 { |
232 | 0 | // set request type only if previous message |
233 | 0 | // was a request |
234 | 0 | tx.req_type = Some(MessageType(self.req_id.into())); |
235 | 0 | } |
236 | 0 | tx.msg_type = MessageType::KRB_ERROR; |
237 | 0 | tx.cname = error.cname; |
238 | 0 | tx.realm = error.crealm; |
239 | 0 | tx.sname = Some(error.sname); |
240 | 0 | tx.error_code = Some(error.error_code); |
241 | 0 | self.transactions.push(tx); |
242 | 0 | sc_app_layer_parser_trigger_raw_stream_inspection( |
243 | 0 | flow, |
244 | 0 | direction as i32, |
245 | | ); |
246 | 19.7k | }; |
247 | 19.7k | self.req_id = 0; |
248 | | } |
249 | 2.54k | _ => { |
250 | 2.54k | SCLogDebug!("unknown/unsupported tag {}", hdr.tag()); |
251 | 2.54k | } |
252 | | } |
253 | 39.5k | 0 |
254 | | } |
255 | | Err(Err::Incomplete(_)) => { |
256 | | SCLogDebug!("Insufficient data while parsing KRB5 data"); |
257 | 31 | self.set_event(KRB5Event::MalformedData); |
258 | 31 | -1 |
259 | | } |
260 | | Err(_) => { |
261 | | SCLogDebug!("Error while parsing KRB5 data"); |
262 | 24 | self.set_event(KRB5Event::MalformedData); |
263 | 24 | -1 |
264 | | } |
265 | | } |
266 | 56.7k | } |
267 | | |
268 | 387 | pub fn free(&mut self) { |
269 | | // All transactions are freed when the `transactions` object is freed. |
270 | | // But let's be explicit |
271 | 387 | self.transactions.clear(); |
272 | 387 | } |
273 | | |
274 | 2 | fn new_tx(&mut self, direction: Direction) -> KRB5Transaction { |
275 | 2 | self.tx_id += 1; |
276 | 2 | KRB5Transaction::new(direction, self.tx_id) |
277 | 2 | } |
278 | | |
279 | 0 | fn get_tx_by_id(&mut self, tx_id: u64) -> Option<&KRB5Transaction> { |
280 | 0 | self.transactions.iter().find(|&tx| tx.id == tx_id + 1) |
281 | 0 | } |
282 | | |
283 | 2 | fn free_tx(&mut self, tx_id: u64) { |
284 | 2 | let tx = self.transactions.iter().position(|tx| tx.id == tx_id + 1); |
285 | 2 | debug_assert!(tx.is_some()); |
286 | 2 | if let Some(idx) = tx { |
287 | 2 | let _ = self.transactions.remove(idx); |
288 | 2 | } |
289 | 2 | } |
290 | | |
291 | | /// Set an event. The event is set on the most recent transaction. |
292 | 55 | fn set_event(&mut self, event: KRB5Event) { |
293 | 55 | if let Some(tx) = self.transactions.last_mut() { |
294 | 0 | tx.tx_data.set_event(event as u8); |
295 | 55 | } |
296 | 55 | } |
297 | | } |
298 | | |
299 | | impl KRB5Transaction { |
300 | 2 | pub fn new(direction: Direction, id: u64) -> KRB5Transaction { |
301 | 2 | let krbtx = KRB5Transaction { |
302 | 2 | msg_type: MessageType(0), |
303 | 2 | cname: None, |
304 | 2 | realm: None, |
305 | 2 | sname: None, |
306 | 2 | etype: None, |
307 | 2 | ticket_etype: None, |
308 | 2 | error_code: None, |
309 | 2 | req_type: None, |
310 | 2 | id, |
311 | 2 | tx_data: applayer::AppLayerTxData::for_direction(direction), |
312 | 2 | }; |
313 | 2 | return krbtx; |
314 | 2 | } |
315 | | } |
316 | | |
317 | | /// Return true if Kerberos `EncryptionType` is weak |
318 | 0 | pub fn test_weak_encryption(alg: EncryptionType) -> bool { |
319 | 0 | match alg { |
320 | | EncryptionType::AES128_CTS_HMAC_SHA1_96 |
321 | | | EncryptionType::AES256_CTS_HMAC_SHA1_96 |
322 | | | EncryptionType::AES128_CTS_HMAC_SHA256_128 |
323 | | | EncryptionType::AES256_CTS_HMAC_SHA384_192 |
324 | | | EncryptionType::CAMELLIA128_CTS_CMAC |
325 | 0 | | EncryptionType::CAMELLIA256_CTS_CMAC => false, |
326 | 0 | _ => true, // all other ciphers are weak or deprecated |
327 | | } |
328 | 0 | } |
329 | | |
330 | | /// Returns *mut KRB5State |
331 | 387 | extern "C" fn krb5_state_new( |
332 | 387 | _orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto, |
333 | 387 | ) -> *mut std::os::raw::c_void { |
334 | 387 | let state = KRB5State::new(); |
335 | 387 | let boxed = Box::new(state); |
336 | 387 | return Box::into_raw(boxed) as *mut _; |
337 | 387 | } |
338 | | |
339 | | /// Params: |
340 | | /// - state: *mut KRB5State as void pointer |
341 | 387 | extern "C" fn krb5_state_free(state: *mut std::os::raw::c_void) { |
342 | 387 | let mut state: Box<KRB5State> = unsafe { Box::from_raw(state as _) }; |
343 | 387 | state.free(); |
344 | 387 | } |
345 | | |
346 | 0 | unsafe extern "C" fn krb5_state_get_tx( |
347 | 0 | state: *mut std::os::raw::c_void, tx_id: u64, |
348 | 0 | ) -> *mut std::os::raw::c_void { |
349 | 0 | let state = cast_pointer!(state, KRB5State); |
350 | 0 | match state.get_tx_by_id(tx_id) { |
351 | 0 | Some(tx) => tx as *const _ as *mut _, |
352 | 0 | None => std::ptr::null_mut(), |
353 | | } |
354 | 0 | } |
355 | | |
356 | 573k | unsafe extern "C" fn krb5_state_get_tx_count(state: *mut std::os::raw::c_void) -> u64 { |
357 | 573k | let state = cast_pointer!(state, KRB5State); |
358 | 573k | state.tx_id |
359 | 573k | } |
360 | | |
361 | 2 | unsafe extern "C" fn krb5_state_tx_free(state: *mut std::os::raw::c_void, tx_id: u64) { |
362 | 2 | let state = cast_pointer!(state, KRB5State); |
363 | 2 | state.free_tx(tx_id); |
364 | 2 | } |
365 | | |
366 | 8 | pub extern "C" fn krb5_tx_get_alstate_progress( |
367 | 8 | _tx: *mut std::os::raw::c_void, _direction: u8, |
368 | 8 | ) -> std::os::raw::c_int { |
369 | 8 | 1 |
370 | 8 | } |
371 | | |
372 | | static mut ALPROTO_KRB5: AppProto = ALPROTO_UNKNOWN; |
373 | | |
374 | 181 | unsafe extern "C" fn krb5_probing_parser( |
375 | 181 | _flow: *const Flow, _direction: u8, input: *const u8, input_len: u32, _rdir: *mut u8, |
376 | 181 | ) -> AppProto { |
377 | 181 | if input.is_null() { |
378 | 0 | return ALPROTO_UNKNOWN; |
379 | 181 | } |
380 | 181 | let slice = build_slice!(input, input_len as usize); |
381 | 181 | let alproto = ALPROTO_KRB5; |
382 | 181 | if slice.len() <= 10 { |
383 | 8 | return ALPROTO_FAILED; |
384 | 173 | } |
385 | 173 | match der_read_element_header(slice) { |
386 | 119 | Ok((rem, ref hdr)) => { |
387 | | // Kerberos messages start with an APPLICATION header |
388 | 119 | if hdr.class() != Class::Application { |
389 | 36 | return ALPROTO_FAILED; |
390 | 83 | } |
391 | | // Tag number should be <= 30 |
392 | 83 | if hdr.tag().0 > 30 { |
393 | 38 | return ALPROTO_FAILED; |
394 | 45 | } |
395 | | // Kerberos messages contain sequences |
396 | 45 | if rem.is_empty() || rem[0] != 0x30 { |
397 | 12 | return ALPROTO_FAILED; |
398 | 33 | } |
399 | | // Check kerberos version |
400 | 33 | if let Ok((rem, _hdr)) = der_read_element_header(rem) { |
401 | 19 | if rem.len() > 5 { |
402 | | #[allow(clippy::single_match)] |
403 | 18 | match (rem[2], rem[3], rem[4]) { |
404 | | // Encoding of DER integer 5 (version) |
405 | | (2, 1, 5) => { |
406 | 4 | return alproto; |
407 | | } |
408 | 14 | _ => (), |
409 | | } |
410 | 1 | } |
411 | 14 | } |
412 | 29 | return ALPROTO_FAILED; |
413 | | } |
414 | | Err(Err::Incomplete(_)) => { |
415 | 18 | return ALPROTO_UNKNOWN; |
416 | | } |
417 | | Err(_) => { |
418 | 36 | return ALPROTO_FAILED; |
419 | | } |
420 | | } |
421 | 181 | } |
422 | | |
423 | 129 | unsafe extern "C" fn krb5_probing_parser_tcp( |
424 | 129 | _flow: *const Flow, direction: u8, input: *const u8, input_len: u32, rdir: *mut u8, |
425 | 129 | ) -> AppProto { |
426 | 129 | if input.is_null() { |
427 | 4 | return ALPROTO_UNKNOWN; |
428 | 125 | } |
429 | 125 | let slice = build_slice!(input, input_len as usize); |
430 | 125 | if slice.len() <= 14 { |
431 | 14 | return ALPROTO_FAILED; |
432 | 111 | } |
433 | 111 | match be_u32(slice) as IResult<&[u8], u32> { |
434 | 111 | Ok((rem, record_mark)) => { |
435 | | // protocol implementations forbid very large requests |
436 | 111 | if record_mark > 16384 { |
437 | 42 | return ALPROTO_FAILED; |
438 | 69 | } |
439 | 69 | return krb5_probing_parser(_flow, direction, rem.as_ptr(), rem.len() as u32, rdir); |
440 | | } |
441 | | Err(Err::Incomplete(_)) => { |
442 | 0 | return ALPROTO_UNKNOWN; |
443 | | } |
444 | | Err(_) => { |
445 | 0 | return ALPROTO_FAILED; |
446 | | } |
447 | | } |
448 | 129 | } |
449 | | |
450 | 27.1k | unsafe extern "C" fn krb5_parse_request( |
451 | 27.1k | flow: *mut Flow, state: *mut std::os::raw::c_void, _pstate: *mut AppLayerParserState, |
452 | 27.1k | stream_slice: StreamSlice, _data: *const std::os::raw::c_void, |
453 | 27.1k | ) -> AppLayerResult { |
454 | 27.1k | let buf = stream_slice.as_slice(); |
455 | 27.1k | let state = cast_pointer!(state, KRB5State); |
456 | 27.1k | if state.parse(buf, flow, Direction::ToServer) < 0 { |
457 | 20 | return AppLayerResult::err(); |
458 | 27.1k | } |
459 | 27.1k | AppLayerResult::ok() |
460 | 27.1k | } |
461 | | |
462 | 27.0k | unsafe extern "C" fn krb5_parse_response( |
463 | 27.0k | flow: *mut Flow, state: *mut std::os::raw::c_void, _pstate: *mut AppLayerParserState, |
464 | 27.0k | stream_slice: StreamSlice, _data: *const std::os::raw::c_void, |
465 | 27.0k | ) -> AppLayerResult { |
466 | 27.0k | let buf = stream_slice.as_slice(); |
467 | 27.0k | let state = cast_pointer!(state, KRB5State); |
468 | 27.0k | if state.parse(buf, flow, Direction::ToClient) < 0 { |
469 | 11 | return AppLayerResult::err(); |
470 | 27.0k | } |
471 | 27.0k | AppLayerResult::ok() |
472 | 27.0k | } |
473 | | |
474 | 68.6k | unsafe extern "C" fn krb5_parse_request_tcp( |
475 | 68.6k | flow: *mut Flow, state: *mut std::os::raw::c_void, _pstate: *mut AppLayerParserState, |
476 | 68.6k | stream_slice: StreamSlice, _data: *const std::os::raw::c_void, |
477 | 68.6k | ) -> AppLayerResult { |
478 | 68.6k | let state = cast_pointer!(state, KRB5State); |
479 | 68.6k | let mut cur_i = stream_slice.as_slice(); |
480 | 68.6k | let start_len = cur_i.len(); |
481 | 69.9k | while !cur_i.is_empty() { |
482 | 69.7k | if state.record_ts == 0 { |
483 | 11.2k | match be_u32(cur_i) as IResult<&[u8], u32> { |
484 | 1.36k | Ok((rem, record)) => { |
485 | 1.36k | state.record_ts = record as usize; |
486 | 1.36k | cur_i = rem; |
487 | 1.36k | } |
488 | | Err(Err::Incomplete(_)) => { |
489 | 9.87k | return AppLayerResult::incomplete((start_len - cur_i.len()) as u32, 4u32); |
490 | | } |
491 | | _ => { |
492 | | SCLogDebug!("krb5_parse_request_tcp: reading record mark failed!"); |
493 | 0 | return AppLayerResult::err(); |
494 | | } |
495 | | } |
496 | 58.5k | } |
497 | 59.8k | if cur_i.len() >= state.record_ts { |
498 | 1.22k | if state.parse(cur_i, flow, Direction::ToServer) < 0 { |
499 | 18 | return AppLayerResult::err(); |
500 | 1.21k | } |
501 | 1.21k | cur_i = &cur_i[state.record_ts..]; |
502 | 1.21k | state.record_ts = 0; |
503 | | } else { |
504 | | // more fragments required |
505 | 58.6k | return AppLayerResult::incomplete( |
506 | 58.6k | (start_len - cur_i.len()) as u32, |
507 | 58.6k | state.record_ts as u32, |
508 | | ); |
509 | | } |
510 | | } |
511 | 162 | AppLayerResult::ok() |
512 | 68.6k | } |
513 | | |
514 | 68.5k | unsafe extern "C" fn krb5_parse_response_tcp( |
515 | 68.5k | flow: *mut Flow, state: *mut std::os::raw::c_void, _pstate: *mut AppLayerParserState, |
516 | 68.5k | stream_slice: StreamSlice, _data: *const std::os::raw::c_void, |
517 | 68.5k | ) -> AppLayerResult { |
518 | 68.5k | let state = cast_pointer!(state, KRB5State); |
519 | 68.5k | let mut cur_i = stream_slice.as_slice(); |
520 | 68.5k | let start_len = cur_i.len(); |
521 | 69.8k | while !cur_i.is_empty() { |
522 | 69.5k | if state.record_tc == 0 { |
523 | 10.3k | match be_u32(cur_i) as IResult<&[u8], _> { |
524 | 1.41k | Ok((rem, record)) => { |
525 | 1.41k | state.record_tc = record as usize; |
526 | 1.41k | cur_i = rem; |
527 | 1.41k | } |
528 | | Err(Err::Incomplete(_)) => { |
529 | 8.98k | return AppLayerResult::incomplete((start_len - cur_i.len()) as u32, 4u32); |
530 | | } |
531 | | _ => { |
532 | | SCLogDebug!("reading record mark failed!"); |
533 | 0 | return AppLayerResult::ok(); |
534 | | } |
535 | | } |
536 | 59.1k | } |
537 | 60.6k | if cur_i.len() >= state.record_tc { |
538 | 1.33k | if state.parse(cur_i, flow, Direction::ToClient) < 0 { |
539 | 6 | return AppLayerResult::err(); |
540 | 1.33k | } |
541 | 1.33k | cur_i = &cur_i[state.record_tc..]; |
542 | 1.33k | state.record_tc = 0; |
543 | | } else { |
544 | | // more fragments required |
545 | 59.2k | return AppLayerResult::incomplete( |
546 | 59.2k | (start_len - cur_i.len()) as u32, |
547 | 59.2k | state.record_tc as u32, |
548 | | ); |
549 | | } |
550 | | } |
551 | 301 | AppLayerResult::ok() |
552 | 68.5k | } |
553 | | |
554 | | export_tx_data_get!(krb5_get_tx_data, KRB5Transaction); |
555 | | export_state_data_get!(krb5_get_state_data, KRB5State); |
556 | | |
557 | | const PARSER_NAME: &[u8] = b"krb5\0"; |
558 | | |
559 | | #[no_mangle] |
560 | 6 | pub unsafe extern "C" fn SCRegisterKrb5Parser() { |
561 | 6 | let default_port = CString::new("88").unwrap(); |
562 | 6 | let mut parser = RustParser { |
563 | 6 | name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char, |
564 | 6 | default_port: default_port.as_ptr(), |
565 | 6 | ipproto: IPPROTO_UDP, |
566 | 6 | probe_ts: Some(krb5_probing_parser), |
567 | 6 | probe_tc: Some(krb5_probing_parser), |
568 | 6 | min_depth: 0, |
569 | 6 | max_depth: 16, |
570 | 6 | state_new: krb5_state_new, |
571 | 6 | state_free: krb5_state_free, |
572 | 6 | tx_free: krb5_state_tx_free, |
573 | 6 | parse_ts: krb5_parse_request, |
574 | 6 | parse_tc: krb5_parse_response, |
575 | 6 | get_tx_count: krb5_state_get_tx_count, |
576 | 6 | get_tx: krb5_state_get_tx, |
577 | 6 | tx_comp_st_ts: 1, |
578 | 6 | tx_comp_st_tc: 1, |
579 | 6 | tx_get_progress: krb5_tx_get_alstate_progress, |
580 | 6 | get_eventinfo: Some(KRB5Event::get_event_info), |
581 | 6 | get_eventinfo_byid: Some(KRB5Event::get_event_info_by_id), |
582 | 6 | localstorage_new: None, |
583 | 6 | localstorage_free: None, |
584 | 6 | get_tx_files: None, |
585 | 6 | get_tx_iterator: Some(applayer::state_get_tx_iterator::<KRB5State, KRB5Transaction>), |
586 | 6 | get_tx_data: krb5_get_tx_data, |
587 | 6 | get_state_data: krb5_get_state_data, |
588 | 6 | apply_tx_config: None, |
589 | 6 | flags: 0, |
590 | 6 | get_frame_id_by_name: None, |
591 | 6 | get_frame_name_by_id: None, |
592 | 6 | get_state_id_by_name: None, |
593 | 6 | get_state_name_by_id: None, |
594 | 6 | }; |
595 | | // register UDP parser |
596 | 6 | let ip_proto_str = CString::new("udp").unwrap(); |
597 | 6 | if SCAppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { |
598 | 6 | let alproto = AppLayerRegisterProtocolDetection(&parser, 1); |
599 | | // store the allocated ID for the probe function |
600 | 6 | ALPROTO_KRB5 = alproto; |
601 | 6 | if SCAppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { |
602 | 6 | let _ = AppLayerRegisterParser(&parser, alproto); |
603 | 6 | } |
604 | 6 | SCAppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_KRB5); |
605 | 0 | } else { |
606 | 0 | SCLogDebug!("Protocol detector and parser disabled for KRB5/UDP."); |
607 | 0 | } |
608 | | // register TCP parser |
609 | 6 | parser.ipproto = IPPROTO_TCP; |
610 | 6 | parser.probe_ts = Some(krb5_probing_parser_tcp); |
611 | 6 | parser.probe_tc = Some(krb5_probing_parser_tcp); |
612 | 6 | parser.parse_ts = krb5_parse_request_tcp; |
613 | 6 | parser.parse_tc = krb5_parse_response_tcp; |
614 | 6 | let ip_proto_str = CString::new("tcp").unwrap(); |
615 | 6 | if SCAppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { |
616 | 6 | let alproto = AppLayerRegisterProtocolDetection(&parser, 1); |
617 | | // store the allocated ID for the probe function |
618 | 6 | ALPROTO_KRB5 = alproto; |
619 | 6 | if SCAppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { |
620 | 6 | let _ = AppLayerRegisterParser(&parser, alproto); |
621 | 6 | } |
622 | 6 | SCAppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_KRB5); |
623 | 0 | } else { |
624 | 0 | SCLogDebug!("Protocol detector and parser disabled for KRB5/TCP."); |
625 | 0 | } |
626 | 6 | } |