diff --git a/cmd/rdpgw/protocol/errors.go b/cmd/rdpgw/protocol/errors.go new file mode 100644 index 0000000000000000000000000000000000000000..e7c8eed7207d6aa68cca79891875e23177e7d661 --- /dev/null +++ b/cmd/rdpgw/protocol/errors.go @@ -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 +) diff --git a/cmd/rdpgw/protocol/gateway.go b/cmd/rdpgw/protocol/gateway.go index 7f95857a9a9454f5f46abddb048c28caf74c1986..2cd96a57b66e56373eec7c8ed28e314db9707b71 100644 --- a/cmd/rdpgw/protocol/gateway.go +++ b/cmd/rdpgw/protocol/gateway.go @@ -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) diff --git a/cmd/rdpgw/protocol/server.go b/cmd/rdpgw/protocol/server.go index 852fdd010d31ba1a4710fc2c48a14bc87a1cb418..7f7a70f3c2cca227cd776657be20d0d4efe0cefc 100644 --- a/cmd/rdpgw/protocol/server.go +++ b/cmd/rdpgw/protocol/server.go @@ -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 diff --git a/cmd/rdpgw/protocol/types.go b/cmd/rdpgw/protocol/types.go index a0d8e33b75162b66f67176e19bf3e8a88aef1837..eab67aa77fad88332b30ad47f03f9ebb78bff844 100644 --- a/cmd/rdpgw/protocol/types.go +++ b/cmd/rdpgw/protocol/types.go @@ -59,8 +59,8 @@ const ( ) const ( - SERVER_STATE_INITIAL = 0x0 - SERVER_STATE_HANDSHAKE = 0x1 + SERVER_STATE_INITIALIZED = 0x0 + SERVER_STATE_HANDSHAKE = 0x1 SERVER_STATE_TUNNEL_CREATE = 0x2 SERVER_STATE_TUNNEL_AUTHORIZE = 0x3 SERVER_STATE_CHANNEL_CREATE = 0x4 diff --git a/dev/docker/Dockerfile b/dev/docker/Dockerfile index 4c7dfe3659525f1bb202d9b801abba7a8860b4e0..0a8d2773dc6e116ca84e62aac9e1cbd44c4a4e36 100644 --- a/dev/docker/Dockerfile +++ b/dev/docker/Dockerfile @@ -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 && \