33package rdp
44
55import (
6+ "context"
67 "crypto/tls"
78 "encoding/asn1"
89 "io"
@@ -11,11 +12,17 @@ import (
1112 log "github.com/sirupsen/logrus"
1213)
1314
15+ const (
16+ // MS-RDPBCGR: confusingly named, actually means PROTOCOL_HYBRID (CredSSP)
17+ protocolSSL = 0x00000001
18+ protocolHybridEx = 0x00000008
19+ )
20+
1421func (p * RDCleanPathProxy ) processRDCleanPathPDU (conn * proxyConnection , pdu RDCleanPathPDU ) {
1522 log .Infof ("Processing RDCleanPath PDU: Version=%d, Destination=%s" , pdu .Version , pdu .Destination )
1623
1724 if pdu .Version != RDCleanPathVersion {
18- p .sendRDCleanPathError (conn , "Unsupported version" )
25+ p .sendRDCleanPathError (conn , newHTTPError ( 400 ) )
1926 return
2027 }
2128
@@ -24,10 +31,13 @@ func (p *RDCleanPathProxy) processRDCleanPathPDU(conn *proxyConnection, pdu RDCl
2431 destination = pdu .Destination
2532 }
2633
27- rdpConn , err := p .nbClient .Dial (conn .ctx , "tcp" , destination )
34+ ctx , cancel := context .WithTimeout (conn .ctx , rdpDialTimeout )
35+ defer cancel ()
36+
37+ rdpConn , err := p .nbClient .Dial (ctx , "tcp" , destination )
2838 if err != nil {
2939 log .Errorf ("Failed to connect to %s: %v" , destination , err )
30- p .sendRDCleanPathError (conn , "Connection failed" )
40+ p .sendRDCleanPathError (conn , newWSAError ( err ) )
3141 p .cleanupConnection (conn )
3242 return
3343 }
@@ -40,36 +50,75 @@ func (p *RDCleanPathProxy) processRDCleanPathPDU(conn *proxyConnection, pdu RDCl
4050 p .setupTLSConnection (conn , pdu )
4151}
4252
53+ // detectCredSSPFromX224 checks if the X.224 response indicates NLA/CredSSP is required.
54+ // Per MS-RDPBCGR spec: byte 11 = TYPE_RDP_NEG_RSP (0x02), bytes 15-18 = selectedProtocol flags.
55+ // Returns (requiresTLS12, selectedProtocol, detectionSuccessful).
56+ func (p * RDCleanPathProxy ) detectCredSSPFromX224 (x224Response []byte ) (bool , uint32 , bool ) {
57+ const minResponseLength = 19
58+
59+ if len (x224Response ) < minResponseLength {
60+ return false , 0 , false
61+ }
62+
63+ // Per X.224 specification:
64+ // x224Response[0] == 0x03: Length of X.224 header (3 bytes)
65+ // x224Response[5] == 0xD0: X.224 Data TPDU code
66+ if x224Response [0 ] != 0x03 || x224Response [5 ] != 0xD0 {
67+ return false , 0 , false
68+ }
69+
70+ if x224Response [11 ] == 0x02 {
71+ flags := uint32 (x224Response [15 ]) | uint32 (x224Response [16 ])<< 8 |
72+ uint32 (x224Response [17 ])<< 16 | uint32 (x224Response [18 ])<< 24
73+
74+ hasNLA := (flags & (protocolSSL | protocolHybridEx )) != 0
75+ return hasNLA , flags , true
76+ }
77+
78+ return false , 0 , false
79+ }
80+
4381func (p * RDCleanPathProxy ) setupTLSConnection (conn * proxyConnection , pdu RDCleanPathPDU ) {
4482 var x224Response []byte
4583 if len (pdu .X224ConnectionPDU ) > 0 {
4684 log .Debugf ("Forwarding X.224 Connection Request (%d bytes)" , len (pdu .X224ConnectionPDU ))
4785 _ , err := conn .rdpConn .Write (pdu .X224ConnectionPDU )
4886 if err != nil {
4987 log .Errorf ("Failed to write X.224 PDU: %v" , err )
50- p .sendRDCleanPathError (conn , "Failed to forward X.224" )
88+ p .sendRDCleanPathError (conn , newWSAError ( err ) )
5189 return
5290 }
5391
5492 response := make ([]byte , 1024 )
5593 n , err := conn .rdpConn .Read (response )
5694 if err != nil {
5795 log .Errorf ("Failed to read X.224 response: %v" , err )
58- p .sendRDCleanPathError (conn , "Failed to read X.224 response" )
96+ p .sendRDCleanPathError (conn , newWSAError ( err ) )
5997 return
6098 }
6199 x224Response = response [:n ]
62100 log .Debugf ("Received X.224 Connection Confirm (%d bytes)" , n )
63101 }
64102
65- tlsConfig := p .getTLSConfigWithValidation (conn )
103+ requiresCredSSP , selectedProtocol , detected := p .detectCredSSPFromX224 (x224Response )
104+ if detected {
105+ if requiresCredSSP {
106+ log .Warnf ("Detected NLA/CredSSP (selectedProtocol: 0x%08X), forcing TLS 1.2 for compatibility" , selectedProtocol )
107+ } else {
108+ log .Warnf ("No NLA/CredSSP detected (selectedProtocol: 0x%08X), allowing up to TLS 1.3" , selectedProtocol )
109+ }
110+ } else {
111+ log .Warnf ("Could not detect RDP security protocol, allowing up to TLS 1.3" )
112+ }
113+
114+ tlsConfig := p .getTLSConfigWithValidation (conn , requiresCredSSP )
66115
67116 tlsConn := tls .Client (conn .rdpConn , tlsConfig )
68117 conn .tlsConn = tlsConn
69118
70119 if err := tlsConn .Handshake (); err != nil {
71120 log .Errorf ("TLS handshake failed: %v" , err )
72- p .sendRDCleanPathError (conn , "TLS handshake failed" )
121+ p .sendRDCleanPathError (conn , newWSAError ( err ) )
73122 return
74123 }
75124
@@ -106,47 +155,6 @@ func (p *RDCleanPathProxy) setupTLSConnection(conn *proxyConnection, pdu RDClean
106155 p .cleanupConnection (conn )
107156}
108157
109- func (p * RDCleanPathProxy ) setupPlainConnection (conn * proxyConnection , pdu RDCleanPathPDU ) {
110- if len (pdu .X224ConnectionPDU ) > 0 {
111- log .Debugf ("Forwarding X.224 Connection Request (%d bytes)" , len (pdu .X224ConnectionPDU ))
112- _ , err := conn .rdpConn .Write (pdu .X224ConnectionPDU )
113- if err != nil {
114- log .Errorf ("Failed to write X.224 PDU: %v" , err )
115- p .sendRDCleanPathError (conn , "Failed to forward X.224" )
116- return
117- }
118-
119- response := make ([]byte , 1024 )
120- n , err := conn .rdpConn .Read (response )
121- if err != nil {
122- log .Errorf ("Failed to read X.224 response: %v" , err )
123- p .sendRDCleanPathError (conn , "Failed to read X.224 response" )
124- return
125- }
126-
127- responsePDU := RDCleanPathPDU {
128- Version : RDCleanPathVersion ,
129- X224ConnectionPDU : response [:n ],
130- ServerAddr : conn .destination ,
131- }
132-
133- p .sendRDCleanPathPDU (conn , responsePDU )
134- } else {
135- responsePDU := RDCleanPathPDU {
136- Version : RDCleanPathVersion ,
137- ServerAddr : conn .destination ,
138- }
139- p .sendRDCleanPathPDU (conn , responsePDU )
140- }
141-
142- go p .forwardConnToWS (conn , conn .rdpConn , "TCP" )
143- go p .forwardWSToConn (conn , conn .rdpConn , "TCP" )
144-
145- <- conn .ctx .Done ()
146- log .Debug ("TCP connection context done, cleaning up" )
147- p .cleanupConnection (conn )
148- }
149-
150158func (p * RDCleanPathProxy ) sendRDCleanPathPDU (conn * proxyConnection , pdu RDCleanPathPDU ) {
151159 data , err := asn1 .Marshal (pdu )
152160 if err != nil {
@@ -158,21 +166,6 @@ func (p *RDCleanPathProxy) sendRDCleanPathPDU(conn *proxyConnection, pdu RDClean
158166 p .sendToWebSocket (conn , data )
159167}
160168
161- func (p * RDCleanPathProxy ) sendRDCleanPathError (conn * proxyConnection , errorMsg string ) {
162- pdu := RDCleanPathPDU {
163- Version : RDCleanPathVersion ,
164- Error : []byte (errorMsg ),
165- }
166-
167- data , err := asn1 .Marshal (pdu )
168- if err != nil {
169- log .Errorf ("Failed to marshal error PDU: %v" , err )
170- return
171- }
172-
173- p .sendToWebSocket (conn , data )
174- }
175-
176169func (p * RDCleanPathProxy ) readWebSocketMessage (conn * proxyConnection ) ([]byte , error ) {
177170 msgChan := make (chan []byte )
178171 errChan := make (chan error )
0 commit comments