diff --git a/lib/backend/backends/v0/server.go b/lib/backend/backends/v0/server.go index d40e36e46ae304a69910593fb265e5c73bfe6b2b..ea0257ca2dab0987072d7519edfb64fda4f524bc 100644 --- a/lib/backend/backends/v0/server.go +++ b/lib/backend/backends/v0/server.go @@ -4,6 +4,7 @@ import ( "errors" "net" + "pggat2/lib/auth/md5" "pggat2/lib/auth/sasl" "pggat2/lib/backend" "pggat2/lib/pnet" @@ -115,6 +116,20 @@ func (T *Server) authenticationSASL(mechanisms []string, username, password stri return nil } +func (T *Server) authenticationMD5(salt [4]byte, username, password string) error { + var builder packet.Builder + builder.Type(packet.AuthenticationResponse) + builder.String(md5.Encode(username, password, salt)) + return T.Write(builder.Raw()) +} + +func (T *Server) authenticationCleartext(password string) error { + var builder packet.Builder + builder.Type(packet.AuthenticationResponse) + builder.String(password) + return T.Write(builder.Raw()) +} + func (T *Server) startup0(username, password string) (bool, error) { pkt, err := T.Read() if err != nil { @@ -138,9 +153,13 @@ func (T *Server) startup0(username, password string) (bool, error) { case 2: return false, errors.New("kerberos v5 is not supported") case 3: - return false, errors.New("cleartext is not supported") + return false, T.authenticationCleartext(password) case 5: - return false, errors.New("md5 password is not supported") + salt, ok := reader.Bytes(4) + if !ok { + return false, ErrBadPacketFormat + } + return false, T.authenticationMD5([4]byte(salt), username, password) case 6: return false, errors.New("scm credential is not supported") case 7: @@ -163,10 +182,10 @@ func (T *Server) startup0(username, password string) (bool, error) { return false, T.authenticationSASL(mechanisms, username, password) default: - // we only support protocol 3.0 for now return false, errors.New("unknown authentication method") } case packet.NegotiateProtocolVersion: + // we only support protocol 3.0 for now return false, errors.New("server wanted to negotiate protocol version") default: return false, ErrProtocolError @@ -186,7 +205,7 @@ func (T *Server) startup1() (bool, error) { if !ok { return false, ErrBadPacketFormat } - copy(T.cancellationKey[:], cancellationKey) + T.cancellationKey = [8]byte(cancellationKey) return false, nil case packet.ParameterStatus: parameter, ok := reader.String() diff --git a/lib/frontend/frontends/v0/client.go b/lib/frontend/frontends/v0/client.go index 3b0a259e05b2da1e525fd6cec157b59d8fb6eb98..6678dd01ed2e5e39acf3e58f132dd951cda11f9f 100644 --- a/lib/frontend/frontends/v0/client.go +++ b/lib/frontend/frontends/v0/client.go @@ -3,6 +3,7 @@ package frontends import ( "crypto/rand" "net" + "strings" "pggat2/lib/auth/md5" "pggat2/lib/auth/sasl" @@ -46,13 +47,15 @@ type Client struct { // cancellation key data cancellationKey [8]byte + parameters map[string]string } func NewClient(conn net.Conn) *Client { client := &Client{ - conn: conn, - Reader: pnet.MakeReader(conn), - Writer: pnet.MakeWriter(conn), + conn: conn, + Reader: pnet.MakeReader(conn), + Writer: pnet.MakeWriter(conn), + parameters: make(map[string]string), } err := client.accept() if err != nil { @@ -147,7 +150,12 @@ func (T *Client) startup0() (bool, perror.Error) { "Replication mode is not supported yet", ) default: - unsupportedOptions = append(unsupportedOptions, key) + if strings.HasPrefix(key, "_pq_.") { + // we don't support protocol extensions at the moment + unsupportedOptions = append(unsupportedOptions, key) + } else { + T.parameters[key] = value + } } } @@ -309,6 +317,47 @@ func (T *Client) authenticationMD5(username, password string) perror.Error { return nil } +func (T *Client) authenticationCleartext(password string) perror.Error { + var builder packet.Builder + builder.Type(packet.Authentication) + builder.Uint32(3) + + err := T.Write(builder.Raw()) + if err != nil { + return WrapError(err) + } + + // read password + pkt, err := T.Read() + if err != nil { + return WrapError(err) + } + + reader := packet.MakeReader(pkt) + if reader.Type() != packet.AuthenticationResponse { + return perror.New( + perror.FATAL, + perror.ProtocolViolation, + "Expected password", + ) + } + + pw, ok := reader.String() + if !ok { + return ErrBadPacketFormat + } + + if pw != password { + return perror.New( + perror.FATAL, + perror.InvalidPassword, + "Invalid password", + ) + } + + return nil +} + func (T *Client) accept() perror.Error { for { done, err := T.startup0()