|
| 1 | +use ironrdp_acceptor::Acceptor; |
| 2 | +use ironrdp_connector::{DesktopSize, Sequence as _, Written}; |
| 3 | +use ironrdp_core::{decode, WriteBuf}; |
| 4 | +use ironrdp_pdu::nego::{self, SecurityProtocol}; |
| 5 | +use ironrdp_pdu::x224::X224; |
| 6 | + |
| 7 | +/// Build a minimal ConnectionRequest with the given protocols and encode it. |
| 8 | +fn encode_connection_request(protocol: SecurityProtocol) -> Vec<u8> { |
| 9 | + let request = nego::ConnectionRequest { |
| 10 | + nego_data: None, |
| 11 | + flags: nego::RequestFlags::empty(), |
| 12 | + protocol, |
| 13 | + }; |
| 14 | + let mut buf = WriteBuf::new(); |
| 15 | + ironrdp_core::encode_buf(&X224(request), &mut buf).unwrap(); |
| 16 | + buf.filled().to_vec() |
| 17 | +} |
| 18 | + |
| 19 | +/// When server requires TLS but client only offers HYBRID|HYBRID_EX, |
| 20 | +/// the acceptor must write an RDP_NEG_FAILURE PDU and return an error. |
| 21 | +#[test] |
| 22 | +fn neg_failure_on_protocol_mismatch() { |
| 23 | + let mut acceptor = Acceptor::new( |
| 24 | + SecurityProtocol::SSL, |
| 25 | + DesktopSize { |
| 26 | + width: 1920, |
| 27 | + height: 1080, |
| 28 | + }, |
| 29 | + Vec::new(), |
| 30 | + None, |
| 31 | + ); |
| 32 | + |
| 33 | + // Step 1: feed the connection request (HYBRID | HYBRID_EX, no SSL) |
| 34 | + let request_bytes = encode_connection_request(SecurityProtocol::HYBRID | SecurityProtocol::HYBRID_EX); |
| 35 | + let mut output = WriteBuf::new(); |
| 36 | + let written = acceptor.step(&request_bytes, &mut output).unwrap(); |
| 37 | + assert!(matches!(written, Written::Nothing)); |
| 38 | + |
| 39 | + // Step 2: acceptor tries to send confirm, finds no common protocol |
| 40 | + let mut output = WriteBuf::new(); |
| 41 | + let result = acceptor.step(&[], &mut output); |
| 42 | + |
| 43 | + // Must be an error |
| 44 | + assert!(result.is_err(), "expected error on protocol mismatch"); |
| 45 | + |
| 46 | + // Must have written an RDP_NEG_FAILURE PDU to the output buffer |
| 47 | + let response_bytes = output.filled(); |
| 48 | + assert!(!response_bytes.is_empty(), "expected RDP_NEG_FAILURE PDU in output"); |
| 49 | + |
| 50 | + // Decode the response and verify it's a Failure with the right code |
| 51 | + let confirm = decode::<X224<nego::ConnectionConfirm>>(response_bytes).unwrap().0; |
| 52 | + match confirm { |
| 53 | + nego::ConnectionConfirm::Failure { code } => { |
| 54 | + assert_eq!(code, nego::FailureCode::SSL_REQUIRED_BY_SERVER); |
| 55 | + } |
| 56 | + nego::ConnectionConfirm::Response { .. } => { |
| 57 | + panic!("expected Failure, got Response"); |
| 58 | + } |
| 59 | + } |
| 60 | +} |
| 61 | + |
| 62 | +/// When server and client agree on SSL, negotiation succeeds normally. |
| 63 | +#[test] |
| 64 | +fn neg_success_when_protocols_match() { |
| 65 | + let mut acceptor = Acceptor::new( |
| 66 | + SecurityProtocol::SSL, |
| 67 | + DesktopSize { |
| 68 | + width: 1920, |
| 69 | + height: 1080, |
| 70 | + }, |
| 71 | + Vec::new(), |
| 72 | + None, |
| 73 | + ); |
| 74 | + |
| 75 | + let request_bytes = encode_connection_request(SecurityProtocol::SSL | SecurityProtocol::HYBRID); |
| 76 | + let mut output = WriteBuf::new(); |
| 77 | + acceptor.step(&request_bytes, &mut output).unwrap(); |
| 78 | + |
| 79 | + let mut output = WriteBuf::new(); |
| 80 | + let written = acceptor.step(&[], &mut output).unwrap(); |
| 81 | + assert!(!matches!(written, Written::Nothing)); |
| 82 | + |
| 83 | + let response_bytes = output.filled(); |
| 84 | + let confirm = decode::<X224<nego::ConnectionConfirm>>(response_bytes).unwrap().0; |
| 85 | + match confirm { |
| 86 | + nego::ConnectionConfirm::Response { protocol, .. } => { |
| 87 | + assert_eq!(protocol, SecurityProtocol::SSL); |
| 88 | + } |
| 89 | + nego::ConnectionConfirm::Failure { .. } => { |
| 90 | + panic!("expected Response, got Failure"); |
| 91 | + } |
| 92 | + } |
| 93 | +} |
| 94 | + |
| 95 | +/// When server requires HYBRID but client only offers SSL, the failure code |
| 96 | +/// should be HYBRID_REQUIRED_BY_SERVER. |
| 97 | +#[test] |
| 98 | +fn neg_failure_hybrid_required() { |
| 99 | + let mut acceptor = Acceptor::new( |
| 100 | + SecurityProtocol::HYBRID | SecurityProtocol::HYBRID_EX, |
| 101 | + DesktopSize { |
| 102 | + width: 1920, |
| 103 | + height: 1080, |
| 104 | + }, |
| 105 | + Vec::new(), |
| 106 | + None, |
| 107 | + ); |
| 108 | + |
| 109 | + let request_bytes = encode_connection_request(SecurityProtocol::SSL); |
| 110 | + let mut output = WriteBuf::new(); |
| 111 | + acceptor.step(&request_bytes, &mut output).unwrap(); |
| 112 | + |
| 113 | + let mut output = WriteBuf::new(); |
| 114 | + let result = acceptor.step(&[], &mut output); |
| 115 | + assert!(result.is_err()); |
| 116 | + |
| 117 | + let response_bytes = output.filled(); |
| 118 | + let confirm = decode::<X224<nego::ConnectionConfirm>>(response_bytes).unwrap().0; |
| 119 | + match confirm { |
| 120 | + nego::ConnectionConfirm::Failure { code } => { |
| 121 | + assert_eq!(code, nego::FailureCode::HYBRID_REQUIRED_BY_SERVER); |
| 122 | + } |
| 123 | + nego::ConnectionConfirm::Response { .. } => { |
| 124 | + panic!("expected Failure, got Response"); |
| 125 | + } |
| 126 | + } |
| 127 | +} |
0 commit comments