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