]> git.ipfire.org Git - people/ms/suricata.git/blame - rust/src/rdp/rdp.rs
app-layer: remove logged API calls
[people/ms/suricata.git] / rust / src / rdp / rdp.rs
CommitLineData
caef8b5b
ZK
1/* Copyright (C) 2019 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: Zach Kelly <zach.kelly@lmco.com>
19
20//! RDP application layer
21
e68dfa46 22use crate::core::{
caef8b5b
ZK
23 self, AppProto, DetectEngineState, Flow, ALPROTO_UNKNOWN, IPPROTO_TCP,
24};
25use nom;
2f5834cd 26use crate::applayer::*;
e68dfa46 27use crate::rdp::parser::*;
caef8b5b
ZK
28use std;
29use std::mem::transmute;
30use tls_parser::{
31 parse_tls_plaintext, TlsMessage, TlsMessageHandshake, TlsRecordType,
32};
33
34static mut ALPROTO_RDP: AppProto = ALPROTO_UNKNOWN;
35
36//
37// transactions
38//
39
40#[derive(Debug, PartialEq)]
41pub struct CertificateBlob {
42 pub data: Vec<u8>,
43}
44
45#[derive(Debug, PartialEq)]
46pub enum RdpTransactionItem {
47 X224ConnectionRequest(X224ConnectionRequest),
48 X224ConnectionConfirm(X224ConnectionConfirm),
49 McsConnectRequest(McsConnectRequest),
50 McsConnectResponse(McsConnectResponse),
51 TlsCertificateChain(Vec<CertificateBlob>),
52}
53
54#[derive(Debug, PartialEq)]
55pub struct RdpTransaction {
56 pub id: u64,
57 pub item: RdpTransactionItem,
58 // managed by macros `export_tx_get_detect_state!` and `export_tx_set_detect_state!`
59 de_state: Option<*mut DetectEngineState>,
60}
61
62impl RdpTransaction {
63 fn new(id: u64, item: RdpTransactionItem) -> Self {
64 Self {
65 id,
66 item,
67 de_state: None,
68 }
69 }
70
71 fn free(&mut self) {
72 if let Some(de_state) = self.de_state {
73 core::sc_detect_engine_state_free(de_state);
74 }
75 }
76}
77
78impl Drop for RdpTransaction {
79 fn drop(&mut self) {
80 self.free();
81 }
82}
83
84#[no_mangle]
85pub extern "C" fn rs_rdp_state_get_tx(
86 state: *mut std::os::raw::c_void,
87 tx_id: u64,
88) -> *mut std::os::raw::c_void {
89 let state = cast_pointer!(state, RdpState);
90 match state.get_tx(tx_id) {
91 Some(tx) => {
92 return unsafe { transmute(tx) };
93 }
94 None => {
95 return std::ptr::null_mut();
96 }
97 }
98}
99
100#[no_mangle]
101pub extern "C" fn rs_rdp_state_get_tx_count(
102 state: *mut std::os::raw::c_void,
103) -> u64 {
104 let state = cast_pointer!(state, RdpState);
105 return state.next_id;
106}
107
108#[no_mangle]
109pub extern "C" fn rs_rdp_tx_get_progress_complete(
110 _direction: u8,
111) -> std::os::raw::c_int {
112 // a parser can implement a multi-step tx completion by using an arbitrary `n`
113 return 1;
114}
115
116#[no_mangle]
117pub extern "C" fn rs_rdp_tx_get_progress(
118 _tx: *mut std::os::raw::c_void,
119 _direction: u8,
120) -> std::os::raw::c_int {
121 // tx complete when `rs_rdp_tx_get_progress(...) == rs_rdp_tx_get_progress_complete(...)`
122 // here, all transactions are immediately complete on insert
123 return 1;
124}
125
126//
127// state
128//
129
130#[derive(Debug, PartialEq)]
131pub struct RdpState {
132 next_id: u64,
133 to_client: Vec<u8>,
134 to_server: Vec<u8>,
135 transactions: Vec<RdpTransaction>,
136 tls_parsing: bool,
137 bypass_parsing: bool,
138}
139
140impl RdpState {
141 fn new() -> Self {
142 Self {
143 next_id: 0,
144 to_client: Vec::new(),
145 to_server: Vec::new(),
146 transactions: Vec::new(),
147 tls_parsing: false,
148 bypass_parsing: false,
149 }
150 }
151
152 fn free_tx(&mut self, tx_id: u64) {
153 let len = self.transactions.len();
154 let mut found = false;
155 let mut index = 0;
156 for ii in 0..len {
157 let tx = &self.transactions[ii];
158 if tx.id == tx_id {
159 found = true;
160 index = ii;
161 break;
162 }
163 }
164 if found {
165 self.transactions.remove(index);
166 }
167 }
168
169 fn get_tx(&self, tx_id: u64) -> Option<&RdpTransaction> {
170 for tx in &self.transactions {
171 if tx.id == tx_id {
172 return Some(tx);
173 }
174 }
175 return None;
176 }
177
178 fn new_tx(&mut self, item: RdpTransactionItem) -> RdpTransaction {
179 let tx = RdpTransaction::new(self.next_id, item);
180 self.next_id += 1;
181 return tx;
182 }
183
184 /// parse buffer captures from client to server
185 fn parse_ts(&mut self, input: &[u8]) -> bool {
186 // no need to process input buffer
187 if self.bypass_parsing {
188 return true;
189 }
190 // combine residual buffer with provided buffer
191 self.to_server.extend(input);
192 let temp: Vec<u8> = self.to_server.split_off(0);
193 let mut available = temp.as_slice();
194
195 loop {
196 if available.len() == 0 {
197 return true;
198 }
199 if self.tls_parsing {
200 match parse_tls_plaintext(&available) {
201 Ok((remainder, _tls)) => {
202 // update awaiting-parsing buffer
203 available = remainder;
204 }
205
206 Err(nom::Err::Incomplete(_)) => {
207 // save unparsed residual buffer for next parse
208 self.to_server.extend(available);
209 return true;
210 }
211
212 Err(nom::Err::Failure(_)) | Err(nom::Err::Error(_)) => {
213 return false;
214 }
215 }
216 } else {
217 // every message should be encapsulated within a T.123 tpkt
218 match parse_t123_tpkt(&available) {
219 // success
220 Ok((remainder, t123)) => {
221 // update awaiting-parsing buffer
222 available = remainder;
223 // evaluate message within the tpkt
224 match t123.child {
225 // X.224 connection request
226 T123TpktChild::X224ConnectionRequest(x224) => {
227 let tx = self.new_tx(
228 RdpTransactionItem::X224ConnectionRequest(
229 x224,
230 ),
231 );
232 self.transactions.push(tx);
233 }
234
235 // X.223 data packet, evaluate what it encapsulates
236 T123TpktChild::Data(x223) => {
237 match x223.child {
238 X223DataChild::McsConnectRequest(mcs) => {
239 let tx =
240 self.new_tx(RdpTransactionItem::McsConnectRequest(mcs));
241 self.transactions.push(tx);
242 }
243 // unknown message in X.223, skip
244 _ => (),
245 }
246 }
247
248 // unknown message in T.123, skip
249 _ => (),
250 }
251 }
252
253 Err(nom::Err::Incomplete(_)) => {
254 // save unparsed residual buffer for next parse
255 self.to_server.extend(available);
256 return true;
257 }
258
259 Err(nom::Err::Failure(_)) | Err(nom::Err::Error(_)) => {
260 if probe_tls_handshake(available) {
261 self.tls_parsing = true;
262 return self.parse_ts(available);
263 } else {
264 return false;
265 }
266 }
267 }
268 }
269 }
270 }
271
272 /// parse buffer captures from server to client
273 fn parse_tc(&mut self, input: &[u8]) -> bool {
274 // no need to process input buffer
275 if self.bypass_parsing {
276 return true;
277 }
278 // combine residual buffer with provided buffer
279 self.to_client.extend(input);
280 let temp: Vec<u8> = self.to_client.split_off(0);
281 let mut available = temp.as_slice();
282
283 loop {
284 if available.len() == 0 {
285 return true;
286 }
287 if self.tls_parsing {
288 match parse_tls_plaintext(&available) {
289 Ok((remainder, tls)) => {
290 // update awaiting-parsing buffer
291 available = remainder;
292 for message in &tls.msg {
293 match message {
294 TlsMessage::Handshake(
295 TlsMessageHandshake::Certificate(contents),
296 ) => {
297 let mut chain = Vec::new();
298 for cert in &contents.cert_chain {
299 chain.push(CertificateBlob {
300 data: cert.data.to_vec(),
301 });
302 }
303 let tx = self.new_tx(
304 RdpTransactionItem::TlsCertificateChain(
305 chain,
306 ),
307 );
308 self.transactions.push(tx);
309 self.bypass_parsing = true;
310 }
311 _ => {}
312 }
313 }
314 }
315
316 Err(nom::Err::Incomplete(_)) => {
317 // save unparsed residual buffer for next parse
318 self.to_client.extend(available);
319 return true;
320 }
321
322 Err(nom::Err::Failure(_)) | Err(nom::Err::Error(_)) => {
323 return false;
324 }
325 }
326 } else {
327 // every message should be encapsulated within a T.123 tpkt
328 match parse_t123_tpkt(&available) {
329 // success
330 Ok((remainder, t123)) => {
331 // update awaiting-parsing buffer
332 available = remainder;
333 // evaluate message within the tpkt
334 match t123.child {
335 // X.224 connection confirm
336 T123TpktChild::X224ConnectionConfirm(x224) => {
337 let tx = self.new_tx(
338 RdpTransactionItem::X224ConnectionConfirm(
339 x224,
340 ),
341 );
342 self.transactions.push(tx);
343 }
344
345 // X.223 data packet, evaluate what it encapsulates
346 T123TpktChild::Data(x223) => {
347 match x223.child {
348 X223DataChild::McsConnectResponse(mcs) => {
349 let tx = self
350 .new_tx(RdpTransactionItem::McsConnectResponse(mcs));
351 self.transactions.push(tx);
352 self.bypass_parsing = true;
353 return true;
354 }
355
356 // unknown message in X.223, skip
357 _ => (),
358 }
359 }
360
361 // unknown message in T.123, skip
362 _ => (),
363 }
364 }
365
366 Err(nom::Err::Incomplete(_)) => {
367 self.to_client.extend(available);
368 return true;
369 }
370
371 Err(nom::Err::Failure(_)) | Err(nom::Err::Error(_)) => {
372 if probe_tls_handshake(available) {
373 self.tls_parsing = true;
374 return self.parse_tc(available);
375 } else {
376 return false;
377 }
378 }
379 }
380 }
381 }
382 }
383}
384
385#[no_mangle]
386pub extern "C" fn rs_rdp_state_new() -> *mut std::os::raw::c_void {
387 let state = RdpState::new();
388 let boxed = Box::new(state);
389 return unsafe { std::mem::transmute(boxed) };
390}
391
392#[no_mangle]
393pub extern "C" fn rs_rdp_state_free(state: *mut std::os::raw::c_void) {
394 let _drop: Box<RdpState> = unsafe { std::mem::transmute(state) };
395}
396
397#[no_mangle]
398pub extern "C" fn rs_rdp_state_tx_free(
399 state: *mut std::os::raw::c_void,
400 tx_id: u64,
401) {
402 let state = cast_pointer!(state, RdpState);
403 state.free_tx(tx_id);
404}
405
406//
407// detection state
408//
409
410export_tx_get_detect_state!(rs_rdp_tx_get_detect_state, RdpTransaction);
411export_tx_set_detect_state!(rs_rdp_tx_set_detect_state, RdpTransaction);
412
413//
414// probe
415//
416
417/// probe for T.123 type identifier, as each message is encapsulated in T.123
418fn probe_rdp(input: &[u8]) -> bool {
419 input.len() > 0 && input[0] == TpktVersion::T123 as u8
420}
421
422/// probe for T.123 message, whether to client or to server
423#[no_mangle]
424pub extern "C" fn rs_rdp_probe_ts_tc(
425 _flow: *const Flow,
426 _direction: u8,
427 input: *const u8,
428 input_len: u32,
429 _rdir: *mut u8,
430) -> AppProto {
431 if input != std::ptr::null_mut() {
432 // probe bytes for `rdp` protocol pattern
433 let slice = build_slice!(input, input_len as usize);
434
435 // Some sessions immediately (first byte) switch to TLS/SSL, e.g.
436 // https://wiki.wireshark.org/SampleCaptures?action=AttachFile&do=view&target=rdp-ssl.pcap.gz
437 // but this callback will not be exercised, so `probe_tls_handshake` not needed here.
438 if probe_rdp(slice) {
439 return unsafe { ALPROTO_RDP };
440 }
441 }
442 return ALPROTO_UNKNOWN;
443}
444
445/// probe for TLS
446fn probe_tls_handshake(input: &[u8]) -> bool {
447 input.len() > 0 && input[0] == u8::from(TlsRecordType::Handshake)
448}
449
450//
451// parse
452//
453
454#[no_mangle]
455pub extern "C" fn rs_rdp_parse_ts(
456 _flow: *const Flow,
457 state: *mut std::os::raw::c_void,
458 _pstate: *mut std::os::raw::c_void,
459 input: *const u8,
460 input_len: u32,
461 _data: *const std::os::raw::c_void,
462 _flags: u8,
44d3f264 463) -> AppLayerResult {
caef8b5b
ZK
464 let state = cast_pointer!(state, RdpState);
465 let buf = build_slice!(input, input_len as usize);
466 // attempt to parse bytes as `rdp` protocol
467 if state.parse_ts(buf) {
44d3f264
VJ
468 AppLayerResult::ok()
469 } else {
470 // no need for further parsing
471 AppLayerResult::err()
caef8b5b 472 }
caef8b5b
ZK
473}
474
475#[no_mangle]
476pub extern "C" fn rs_rdp_parse_tc(
477 _flow: *const Flow,
478 state: *mut std::os::raw::c_void,
479 _pstate: *mut std::os::raw::c_void,
480 input: *const u8,
481 input_len: u32,
482 _data: *const std::os::raw::c_void,
483 _flags: u8,
44d3f264 484) -> AppLayerResult {
caef8b5b
ZK
485 let state = cast_pointer!(state, RdpState);
486 let buf = build_slice!(input, input_len as usize);
487 // attempt to parse bytes as `rdp` protocol
488 if state.parse_tc(buf) {
44d3f264
VJ
489 AppLayerResult::ok()
490 } else {
491 // no need for further parsing
492 AppLayerResult::err()
caef8b5b 493 }
caef8b5b
ZK
494}
495
496//
497// registration
498//
499
500const PARSER_NAME: &'static [u8] = b"rdp\0";
501
502#[no_mangle]
503pub unsafe extern "C" fn rs_rdp_register_parser() {
504 let default_port = std::ffi::CString::new("[3389]").unwrap();
505 let parser = RustParser {
506 name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char,
507 default_port: default_port.as_ptr(),
508 ipproto: IPPROTO_TCP,
66632465
PA
509 probe_ts: Some(rs_rdp_probe_ts_tc),
510 probe_tc: Some(rs_rdp_probe_ts_tc),
caef8b5b
ZK
511 min_depth: 0,
512 max_depth: 16,
513 state_new: rs_rdp_state_new,
514 state_free: rs_rdp_state_free,
515 tx_free: rs_rdp_state_tx_free,
516 parse_ts: rs_rdp_parse_ts,
517 parse_tc: rs_rdp_parse_tc,
518 get_tx_count: rs_rdp_state_get_tx_count,
519 get_tx: rs_rdp_state_get_tx,
520 tx_get_comp_st: rs_rdp_tx_get_progress_complete,
521 tx_get_progress: rs_rdp_tx_get_progress,
caef8b5b
ZK
522 get_de_state: rs_rdp_tx_get_detect_state,
523 set_de_state: rs_rdp_tx_set_detect_state,
524 get_events: None,
525 get_eventinfo: None,
526 get_eventinfo_byid: None,
527 localstorage_new: None,
528 localstorage_free: None,
caef8b5b
ZK
529 get_files: None,
530 get_tx_iterator: None,
cde49ec2
JI
531 get_tx_detect_flags: None,
532 set_tx_detect_flags: None,
411f428a 533 get_tx_data: None,
5665fc83 534 apply_tx_config: None,
caef8b5b
ZK
535 };
536
537 let ip_proto_str = std::ffi::CString::new("tcp").unwrap();
538
539 if AppLayerProtoDetectConfProtoDetectionEnabled(
540 ip_proto_str.as_ptr(),
541 parser.name,
542 ) != 0
543 {
544 let alproto = AppLayerRegisterProtocolDetection(&parser, 1);
545 ALPROTO_RDP = alproto;
546 if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name)
547 != 0
548 {
549 let _ = AppLayerRegisterParser(&parser, alproto);
550 }
551 }
552}
553
554#[cfg(test)]
555mod tests {
556 use super::*;
e68dfa46 557 use crate::rdp::parser::{RdpCookie, X224ConnectionRequest};
caef8b5b
ZK
558
559 #[test]
560 fn test_probe_rdp() {
561 let buf: &[u8] = &[0x03, 0x00];
562 assert_eq!(true, probe_rdp(&buf));
563 }
564
565 #[test]
566 fn test_probe_rdp_other() {
567 let buf: &[u8] = &[0x04, 0x00];
568 assert_eq!(false, probe_rdp(&buf));
569 }
570
571 #[test]
572 fn test_probe_tls_handshake() {
573 let buf: &[u8] = &[0x16, 0x00];
574 assert_eq!(true, probe_tls_handshake(&buf));
575 }
576
577 #[test]
578 fn test_probe_tls_handshake_other() {
579 let buf: &[u8] = &[0x17, 0x00];
580 assert_eq!(false, probe_tls_handshake(&buf));
581 }
582
583 #[test]
584 fn test_parse_ts_rdp() {
585 let buf_1: &[u8] = &[0x03, 0x00, 0x00, 0x25, 0x20, 0xe0, 0x00, 0x00];
586 let buf_2: &[u8] = &[
587 0x00, 0x00, 0x00, 0x43, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x3a, 0x20,
588 0x6d, 0x73, 0x74, 0x73, 0x68, 0x61, 0x73, 0x68, 0x3d, 0x75, 0x73,
589 0x65, 0x72, 0x31, 0x32, 0x33, 0x0d, 0x0a,
590 ];
591 let mut state = RdpState::new();
592 assert_eq!(true, state.parse_ts(&buf_1));
593 assert_eq!(0, state.transactions.len());
594 assert_eq!(true, state.parse_ts(&buf_2));
595 assert_eq!(1, state.transactions.len());
596 let item =
597 RdpTransactionItem::X224ConnectionRequest(X224ConnectionRequest {
598 cdt: 0,
599 dst_ref: 0,
600 src_ref: 0,
601 class: 0,
602 options: 0,
603 cookie: Some(RdpCookie {
604 mstshash: String::from("user123"),
605 }),
606 negotiation_request: None,
607 data: Vec::new(),
608 });
609 assert_eq!(item, state.transactions[0].item);
610 }
611
612 #[test]
613 fn test_parse_ts_other() {
614 let buf: &[u8] = &[0x03, 0x00, 0x00, 0x01, 0x00];
615 let mut state = RdpState::new();
616 assert_eq!(false, state.parse_ts(&buf));
617 }
618
619 #[test]
620 fn test_parse_tc_rdp() {
621 let buf_1: &[u8] = &[0x03, 0x00, 0x00, 0x09, 0x02];
622 let buf_2: &[u8] = &[0xf0, 0x80, 0x7f, 0x66];
623 let mut state = RdpState::new();
624 assert_eq!(true, state.parse_tc(&buf_1));
625 assert_eq!(0, state.transactions.len());
626 assert_eq!(true, state.parse_tc(&buf_2));
627 assert_eq!(1, state.transactions.len());
628 let item =
629 RdpTransactionItem::McsConnectResponse(McsConnectResponse {});
630 assert_eq!(item, state.transactions[0].item);
631 }
632
633 #[test]
634 fn test_parse_tc_other() {
635 let buf: &[u8] = &[0x03, 0x00, 0x00, 0x01, 0x00];
636 let mut state = RdpState::new();
637 assert_eq!(false, state.parse_tc(&buf));
638 }
639
640 #[test]
641 fn test_state_new_tx() {
642 let mut state = RdpState::new();
643 let item0 = RdpTransactionItem::McsConnectRequest(McsConnectRequest {
644 children: Vec::new(),
645 });
646 let item1 = RdpTransactionItem::McsConnectRequest(McsConnectRequest {
647 children: Vec::new(),
648 });
649 let tx0 = state.new_tx(item0);
650 let tx1 = state.new_tx(item1);
651 assert_eq!(2, state.next_id);
652 state.transactions.push(tx0);
653 state.transactions.push(tx1);
654 assert_eq!(2, state.transactions.len());
655 assert_eq!(0, state.transactions[0].id);
656 assert_eq!(1, state.transactions[1].id);
657 assert_eq!(false, state.tls_parsing);
658 assert_eq!(false, state.bypass_parsing);
659 }
660
661 #[test]
662 fn test_state_get_tx() {
663 let mut state = RdpState::new();
664 let item0 = RdpTransactionItem::McsConnectRequest(McsConnectRequest {
665 children: Vec::new(),
666 });
667 let item1 = RdpTransactionItem::McsConnectRequest(McsConnectRequest {
668 children: Vec::new(),
669 });
670 let item2 = RdpTransactionItem::McsConnectRequest(McsConnectRequest {
671 children: Vec::new(),
672 });
673 let tx0 = state.new_tx(item0);
674 let tx1 = state.new_tx(item1);
675 let tx2 = state.new_tx(item2);
676 state.transactions.push(tx0);
677 state.transactions.push(tx1);
678 state.transactions.push(tx2);
679 assert_eq!(Some(&state.transactions[1]), state.get_tx(1));
680 }
681
682 #[test]
683 fn test_state_free_tx() {
684 let mut state = RdpState::new();
685 let item0 = RdpTransactionItem::McsConnectRequest(McsConnectRequest {
686 children: Vec::new(),
687 });
688 let item1 = RdpTransactionItem::McsConnectRequest(McsConnectRequest {
689 children: Vec::new(),
690 });
691 let item2 = RdpTransactionItem::McsConnectRequest(McsConnectRequest {
692 children: Vec::new(),
693 });
694 let tx0 = state.new_tx(item0);
695 let tx1 = state.new_tx(item1);
696 let tx2 = state.new_tx(item2);
697 state.transactions.push(tx0);
698 state.transactions.push(tx1);
699 state.transactions.push(tx2);
700 state.free_tx(1);
701 assert_eq!(3, state.next_id);
702 assert_eq!(2, state.transactions.len());
703 assert_eq!(0, state.transactions[0].id);
704 assert_eq!(2, state.transactions[1].id);
705 assert_eq!(None, state.get_tx(1));
706 }
707}