Add error coes and better protocol handling

This commit is contained in:
Bolke de Bruin 2022-08-07 12:03:44 +02:00
parent ce84fd9cf1
commit dfb82889d3
5 changed files with 171 additions and 27 deletions

View file

@ -0,0 +1,124 @@
package protocol
const (
ERROR_NO = 0x0000000
ERROR_CLIENT_DISCONNECT = 0x0000001
ERROR_CLIENT_LOGOFF = 0x0000002
ERROR_NETWORK_DISCONNECT = 0x0000003
ERROR_NOT_FOUND = 0x0000104
ERROR_NO_MEM = 0x0000106
ERROR_CONNECT_TIMEOUT = 0x0000108
ERROR_SMARTCARD_SERVICE = 0x000010A
ERROR_UNAVAILABLE = 0x0000204
ERROR_SMARTCARD_READER = 0x000020A
ERROR_NETWORK = 0x0000304
ERROR_SMARTCART_NOCARD = 0x000030A
ERROR_SECURITY = 0x0000406
ERROR_INVALID_NAME = 0x0000408
ERROR_SMARTCARD_SUBSYSTEM = 0x000040A
ERROR_GENERIC = 0x0000704
ERROR_CONSOLE_EXIST = 0x0000708
ERROR_LICENSING_PROTOCOL = 0x0000808
ERROR_NETWORK_GENERIC = 0x0000904
ERROR_SECURITY_UNEXPECTED_CERTIFICATE = 0x0000907
ERROR_LICENSING_TIMEOUT = 0x0000908
ERROR_SECURITY_USER = 0x0000A07
ERROR_GENERIC_UNAVAIL = 0x0000B04
ERROR_ENCRYPTION = 0x0000B06
ERROR_SECURITY_USER_DISABLED = 0x0000B07
ERROR_SECURITY_NLA_REQUIRED = 0x0000B09
ERROR_SECURITY_USER_RESTRICTION = 0x0000C07
ERROR_DECOMPRESSION = 0x0000C08
ERROR_SECURITY_USER_LOCKED_OUT = 0x0000D07
ERROR_SECURITY_USER_DIALOG_REQUIRED = 0x0000D09
ERROR_SECURITY_FIPS_REQUIRED = 0x0000E06
ERROR_SECURITY_USER_EXPIRED = 0x0000E07
ERROR_GENERIC_FAILED = 0x0000E08
ERROR_SERVER_RA_UNAVAILABLE = 0x0000E09
ERROR_SECURITY_USER_PASSWORD_EXPIRED = 0x0000F07
ERROR_SECURITY_USER_CREDENTIALS_NOT_SENT = 0x0000F08
ERROR_SECURITY_USER_TIME_RESTRICTION = 0x0001007
ERROR_LOW_VIDEO = 0x0001008
ERROR_SECURITY_USER_COMPUTER_RANGE = 0x0001107
ERROR_SECURITY_USER_CHANGE_PASSWORD = 0x0001207
ERROR_SECURITY_USER_LOGON_TYPE = 0x0001307
ERROR_KRB_SUB_REQUIRED = 0x0001407
ERROR_SECURITY_SERVER_INVALID_CERTIFICATE = 0x0001B07
ERROR_SECURITY_SERVER_TIMESKEW = 0x0001D07
ERROR_SECURITY_SMARTCARD_LOCKEDOUT = 0x0002207
ERROR_RELAUNCH_APP = 0x0002507
ERROR_UPGRADE_CLIENT = 0x0002604
ERROR_RELAUNCH_REMOTE = 0x2000001
ERROR_REMOTEAPP_UNSUPPORTED = 0x2000002
ERROR_SECURITY_USER_PASSWORD_INVALID = 0x3000001
ERROR_SECURITY_CERTIFICATE_REVOKE_LIST_UNAVAIL = 0x3000002
ERROR_SECURITY_CERTIFICATE_INVALID = 0x3000003
ERROR_SECURITY_CERTIFICATE_REVOKED = 0x3000004
ERROR_SECURITY_GATEWAY_IDENTITY = 0x3000005
ERROR_SECURITY_GATEWAY_SUBJECT = 0x3000006
ERROR_SECURITY_GATEWAY_EXPIRED = 0x3000007
ERROR_SECURITY_REMOTE_ERROR = 0x3000008
ERROR_GATEWAY_NETWORK_SEND = 0x3000009
ERROR_GATEWAY_NETWORK_RECEIVE = 0x300000A
ERROR_SECURITY_ALTERNATE = 0x300000B
ERROR_GATEWAY_INVALID_ADDRESS = 0x300000C
ERROR_GATEWAY_TEMP_UNAVAIL = 0x300000D
ERROR_REMOTE_CLIENT_MISSING = 0x300000E
ERROR_GATEWAY_LOW_RESOURCES = 0x300000F
ERROR_GATEWAY_CLIENT_DLL = 0x3000010
ERROR_SMARTCART_NOSERVICE = 0x3000011
ERROR_SECURITY_SMARTCARD_REMOVED = 0x3000012
ERROR_SECURITY_SMARTCARD_REQUIRED = 0x3000013
ERROR_SECURITY_SMARTCARD_REMOVED2 = 0x3000014
ERROR_SECURITY_USER_PASSWORD_INVALID2 = 0x3000015
ERROR_SECURITY_TRANSPORT = 0x3000017
ERROR_GATEWAY_TERMINATE = 0x3000018
ERROR_GATEWAY_ADMIN_TERMINATE = 0x3000019
ERROR_SECURITY_USER_CREDENTIALS = 0x300001A
ERROR_SECURITY_GATEWAY_NOT_PERMITTED = 0x300001B
ERROR_SECURITY_GATEWAY_UNAUTHORIZED = 0x300001C
ERROR_SECURITY_GATEWAY_RESTRICTED = 0x300001F
ERROR_SECURITY_PROXY_AUTH = 0x3000020
ERROR_SECURITY_USER_PASSWORD_MUST_CHANGE = 0x3000021
ERROR_GATEWAY_MAX_REACHED = 0x3000022
ERROR_GATEWAY_UNSUPPORTED_REQUEST = 0x3000023
ERROR_GATEWAY_UNSUPPORTED_CAP = 0x3000024
ERROR_GATEWAY_INCOMPAT = 0x3000025
ERROR_SECURITY_SMARTCARD_INVALID_CREDENTIALS = 0x3000026
ERROR_SECURITY_NLA_INVALID = 0x3000027
ERROR_GATEWAY_NO_CERTIFICATE = 0x3000028
ERROR_GATEWAY_NOT_ALLOWED = 0x3000029
ERROR_GATEWAY_INVALID_CERTIFICATE = 0x300002A
ERROR_SECURITY_GATEWAY_USER_PASSWORD_REQUIRED = 0x300002B
ERROR_SECURITY_GATEWAY_SMARTCARD_REQUIRED = 0x300002C
ERROR_SECURITY_SMARTCARD_UNAVAIL = 0x300002D
ERROR_SECURITY_FIREWALL_NOAUTH = 0x300002F
ERROR_SECURITY_FIREWALL_AUTH = 0x3000030
ERROR_NO_INPUT = 0x3000032
ERROR_TIMEOUT = 0x3000033
ERROR_SECURITY_GATEWAY_COOKIE_INVALID = 0x3000034
ERROR_SECURITY_GATEWAY_COOKIE_REJECTED = 0x3000035
ERROR_SECURITY_GATEWAY_AUTH_METHOD = 0x3000037
ERROR_SECURITY_USER_PERIOD_AUTH = 0x3000038
ERROR_SECURITY_USER_PERIOD_AUTHZ = 0x3000039
ERROR_SECURITY_GATEWAY_POLICY = 0x300003B
ERROR_SECURITY_SMARTCARD_CERTIFICATE = 0x300003C
ERROR_LOGON_FIRST = 0x300003D
ERROR_AUTH_LOGON_FIRST = 0x300003E
ERROR_SESSION_ENDED = 0x300003F
ERROR_SESSION_ENDED_AUTH = 0x3000040
ERROR_SECURITY_GATEWAY_NAP = 0x3000041
ERROR_COOKIE_SIZE = 0x3000042
ERROR_PROXY_CONFIG = 0x3000044
ERROR_NO_PERMISSION = 0x3000045
ERROR_NO_RESOURCES = 0x3000046
ERROR_RESOURCE_ACCESS = 0x3000047
ERROR_UPGRADE_CLIENT2 = 0x3000049
ERROR_SECURITY_NETWORK_HTTPS = 0x300004A
ERROR_TEMP_FAIL = 0x300004B
ERROR_SECURITY_USER_MISMATCH = 0x300004C
ERROR_AZURE_TOO_MANY = 0x300004D
ERROR_MAX_USER = 0x300004E
ERROR_AZURE_TRIAL = 0x300004F
ERROR_AZURE_EXPIRED = 0x3000050
)

View file

@ -160,6 +160,8 @@ func (g *Gateway) handleWebsocketProtocol(ctx context.Context, c *websocket.Conn
defer websocketConnections.Dec()
inout, _ := transport.NewWS(c)
defer inout.Close()
s.TransportOut = inout
s.TransportIn = inout
handler := NewServer(s, g.ServerConf)

View file

@ -5,6 +5,7 @@ import (
"context"
"encoding/binary"
"errors"
"fmt"
"github.com/bolkedebruin/rdpgw/cmd/rdpgw/common"
"io"
"log"
@ -45,7 +46,7 @@ type ServerConf struct {
func NewServer(s *SessionInfo, conf *ServerConf) *Server {
h := &Server{
State: SERVER_STATE_INITIAL,
State: SERVER_STATE_INITIALIZED,
Session: s,
RedirectFlags: makeRedirectFlags(conf.RedirectFlags),
IdleTimeout: conf.IdleTimeout,
@ -71,12 +72,14 @@ func (s *Server) Process(ctx context.Context) error {
switch pt {
case PKT_TYPE_HANDSHAKE_REQUEST:
log.Printf("Client handshakeRequest from %s", common.GetClientIp(ctx))
if s.State != SERVER_STATE_INITIAL {
log.Printf("Handshake attempted while in wrong state %d != %d", s.State, SERVER_STATE_INITIAL)
return errors.New("wrong state")
if s.State != SERVER_STATE_INITIALIZED {
log.Printf("Handshake attempted while in wrong state %d != %d", s.State, SERVER_STATE_INITIALIZED)
msg := s.handshakeResponse(0x0, 0x0, ERROR_GENERIC)
s.Session.TransportOut.WritePacket(msg)
return fmt.Errorf("%x: wrong state", ERROR_GENERIC)
}
major, minor, _, _ := s.handshakeRequest(pkt) // todo check if auth matches what the handler can do
msg := s.handshakeResponse(major, minor)
msg := s.handshakeResponse(major, minor, ERROR_NO)
s.Session.TransportOut.WritePacket(msg)
s.State = SERVER_STATE_HANDSHAKE
case PKT_TYPE_TUNNEL_CREATE:
@ -84,16 +87,20 @@ func (s *Server) Process(ctx context.Context) error {
if s.State != SERVER_STATE_HANDSHAKE {
log.Printf("Tunnel create attempted while in wrong state %d != %d",
s.State, SERVER_STATE_HANDSHAKE)
return errors.New("wrong state")
msg := s.tunnelResponse(ERROR_SECURITY_GATEWAY_COOKIE_REJECTED)
s.Session.TransportOut.WritePacket(msg)
return fmt.Errorf("%x: PAA cookie rejected, wrong state", ERROR_SECURITY_GATEWAY_COOKIE_REJECTED)
}
_, cookie := s.tunnelRequest(pkt)
if s.VerifyTunnelCreate != nil {
if ok, _ := s.VerifyTunnelCreate(ctx, cookie); !ok {
log.Printf("Invalid PAA cookie received from client %s", common.GetClientIp(ctx))
return errors.New("invalid PAA cookie")
msg := s.tunnelResponse(ERROR_SECURITY_GATEWAY_COOKIE_INVALID)
s.Session.TransportOut.WritePacket(msg)
return fmt.Errorf("%x: invalid PAA cookie", ERROR_SECURITY_GATEWAY_COOKIE_INVALID)
}
}
msg := s.tunnelResponse()
msg := s.tunnelResponse(ERROR_NO)
s.Session.TransportOut.WritePacket(msg)
s.State = SERVER_STATE_TUNNEL_CREATE
case PKT_TYPE_TUNNEL_AUTH:
@ -101,16 +108,20 @@ func (s *Server) Process(ctx context.Context) error {
if s.State != SERVER_STATE_TUNNEL_CREATE {
log.Printf("Tunnel auth attempted while in wrong state %d != %d",
s.State, SERVER_STATE_TUNNEL_CREATE)
return errors.New("wrong state")
msg := s.tunnelAuthResponse(ERROR_GENERIC)
s.Session.TransportOut.WritePacket(msg)
return fmt.Errorf("%x: Tunnel auth rejected, wrong state", ERROR_GENERIC)
}
client := s.tunnelAuthRequest(pkt)
if s.VerifyTunnelAuthFunc != nil {
if ok, _ := s.VerifyTunnelAuthFunc(ctx, client); !ok {
log.Printf("Invalid client name: %s", client)
return errors.New("invalid client name")
msg := s.tunnelAuthResponse(ERROR_SECURITY)
s.Session.TransportOut.WritePacket(msg)
return fmt.Errorf("%x: Tunnel auth rejected, invalid client name", ERROR_SECURITY)
}
}
msg := s.tunnelAuthResponse()
msg := s.tunnelAuthResponse(ERROR_NO)
s.Session.TransportOut.WritePacket(msg)
s.State = SERVER_STATE_TUNNEL_AUTHORIZE
case PKT_TYPE_CHANNEL_CREATE:
@ -118,24 +129,30 @@ func (s *Server) Process(ctx context.Context) error {
if s.State != SERVER_STATE_TUNNEL_AUTHORIZE {
log.Printf("Channel create attempted while in wrong state %d != %d",
s.State, SERVER_STATE_TUNNEL_AUTHORIZE)
return errors.New("wrong state")
msg := s.channelResponse(ERROR_GENERIC)
s.Session.TransportOut.WritePacket(msg)
return fmt.Errorf("%x: Channel create rejected, wrong state", ERROR_GENERIC)
}
server, port := s.channelRequest(pkt)
host := net.JoinHostPort(server, strconv.Itoa(int(port)))
if s.VerifyServerFunc != nil {
if ok, _ := s.VerifyServerFunc(ctx, host); !ok {
log.Printf("Not allowed to connect to %s by policy handler", host)
return errors.New("denied by security policy")
msg := s.channelResponse(ERROR_SECURITY_GATEWAY_POLICY)
s.Session.TransportOut.WritePacket(msg)
return fmt.Errorf("%x: denied by security policy", ERROR_SECURITY_GATEWAY_POLICY)
}
}
log.Printf("Establishing connection to RDP server: %s", host)
s.Remote, err = net.DialTimeout("tcp", host, time.Second*15)
if err != nil {
log.Printf("Error connecting to %s, %s", host, err)
msg := s.channelResponse(ERROR_GENERIC)
s.Session.TransportOut.WritePacket(msg)
return err
}
log.Printf("Connection established")
msg := s.channelResponse()
msg := s.channelResponse(ERROR_NO)
s.Session.TransportOut.WritePacket(msg)
// Make sure to start the flow from the RDP server first otherwise connections
@ -164,7 +181,7 @@ func (s *Server) Process(ctx context.Context) error {
log.Printf("Channel closed while in wrong state %d != %d", s.State, SERVER_STATE_OPENED)
return errors.New("wrong state")
}
msg := s.channelCloseResponse()
msg := s.channelCloseResponse(ERROR_NO)
s.Session.TransportOut.WritePacket(msg)
//s.Session.TransportIn.Close()
//s.Session.TransportOut.Close()
@ -179,7 +196,7 @@ func (s *Server) Process(ctx context.Context) error {
// Creates a packet the is a response to a handshakeRequest request
// HTTP_EXTENDED_AUTH_SSPI_NTLM is not supported in Linux
// but could be in Windows. However the NTLM protocol is insecure
func (s *Server) handshakeResponse(major byte, minor byte) []byte {
func (s *Server) handshakeResponse(major byte, minor byte, errorCode int) []byte {
var caps uint16
if s.SmartCardAuth {
caps = caps | HTTP_EXTENDED_AUTH_SC
@ -189,7 +206,7 @@ func (s *Server) handshakeResponse(major byte, minor byte) []byte {
}
buf := new(bytes.Buffer)
binary.Write(buf, binary.LittleEndian, uint32(0)) // error_code
binary.Write(buf, binary.LittleEndian, uint32(errorCode)) // error_code
buf.Write([]byte{major, minor})
binary.Write(buf, binary.LittleEndian, uint16(0)) // server version
binary.Write(buf, binary.LittleEndian, uint16(caps)) // extended auth
@ -227,11 +244,11 @@ func (s *Server) tunnelRequest(data []byte) (caps uint32, cookie string) {
return
}
func (s *Server) tunnelResponse() []byte {
func (s *Server) tunnelResponse(errorCode int) []byte {
buf := new(bytes.Buffer)
binary.Write(buf, binary.LittleEndian, uint16(0)) // server version
binary.Write(buf, binary.LittleEndian, uint32(0)) // error code
binary.Write(buf, binary.LittleEndian, uint32(errorCode)) // error code
binary.Write(buf, binary.LittleEndian, uint16(HTTP_TUNNEL_RESPONSE_FIELD_TUNNEL_ID|HTTP_TUNNEL_RESPONSE_FIELD_CAPS)) // fields present
binary.Write(buf, binary.LittleEndian, uint16(0)) // reserved
@ -255,10 +272,10 @@ func (s *Server) tunnelAuthRequest(data []byte) string {
return clientName
}
func (s *Server) tunnelAuthResponse() []byte {
func (s *Server) tunnelAuthResponse(errorCode int) []byte {
buf := new(bytes.Buffer)
binary.Write(buf, binary.LittleEndian, uint32(0)) // error code
binary.Write(buf, binary.LittleEndian, uint32(errorCode)) // error code
binary.Write(buf, binary.LittleEndian, uint16(HTTP_TUNNEL_AUTH_RESPONSE_FIELD_REDIR_FLAGS|HTTP_TUNNEL_AUTH_RESPONSE_FIELD_IDLE_TIMEOUT)) // fields present
binary.Write(buf, binary.LittleEndian, uint16(0)) // reserved
@ -295,10 +312,10 @@ func (s *Server) channelRequest(data []byte) (server string, port uint16) {
return
}
func (s *Server) channelResponse() []byte {
func (s *Server) channelResponse(errorCode int) []byte {
buf := new(bytes.Buffer)
binary.Write(buf, binary.LittleEndian, uint32(0)) // error code
binary.Write(buf, binary.LittleEndian, uint32(errorCode)) // error code
binary.Write(buf, binary.LittleEndian, uint16(HTTP_CHANNEL_RESPONSE_FIELD_CHANNELID)) // fields present
binary.Write(buf, binary.LittleEndian, uint16(0)) // reserved
@ -314,10 +331,10 @@ func (s *Server) channelResponse() []byte {
return createPacket(PKT_TYPE_CHANNEL_RESPONSE, buf.Bytes())
}
func (s *Server) channelCloseResponse() []byte {
func (s *Server) channelCloseResponse(errorCode int) []byte {
buf := new(bytes.Buffer)
binary.Write(buf, binary.LittleEndian, uint32(0)) // error code
binary.Write(buf, binary.LittleEndian, uint32(errorCode)) // error code
binary.Write(buf, binary.LittleEndian, uint16(HTTP_CHANNEL_RESPONSE_FIELD_CHANNELID)) // fields present
binary.Write(buf, binary.LittleEndian, uint16(0)) // reserved

View file

@ -59,7 +59,7 @@ const (
)
const (
SERVER_STATE_INITIAL = 0x0
SERVER_STATE_INITIALIZED = 0x0
SERVER_STATE_HANDSHAKE = 0x1
SERVER_STATE_TUNNEL_CREATE = 0x2
SERVER_STATE_TUNNEL_AUTHORIZE = 0x3

View file

@ -25,6 +25,7 @@ RUN mkdir -p /opt/rdpgw && cd /opt/rdpgw && \
RUN adduser --disabled-password --gecos "" --home /opt/rdpgw --uid 1001 rdpgw
# build rdpgw and set rights
ARG CACHEBUST
RUN git clone https://github.com/bolkedebruin/rdpgw.git /app && \
cd /app && \
go mod tidy -compat=1.17 && \