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