mysql: Pass mysql.Conn through {Hash,PlainText,Caching}Storage interfaces

The TLS client certificates can simply be derived based on the
mysql.Conn passed through, but passing through the actual Conn, gives us
access to Conn.ClientData or anything else an Auth{Method,Server} might need to
implement.

It's arguable that if we need access to the lower level Conn, we could
simply implement the actual HandleAuthPluginData instead of these
*Storage interfaces, and that's true. But not all AuthMethods can get
away with this because they need to write to the Conn's connection using
private functions.

For example, the `mysqlCachingSha2AuthMethod` needs to write more data
on the socket after calling `UserEntryWithCacheHash`, so to write a new
`UserEntryWithCacheHash` that needs data from `mysql.Conn`, I'm left
wtih no other options since I can't vendor or reimplement the rest of
the logic without having access to the private functions/methods within
the `mysql` package.

Signed-off-by: Matt Robenolt <matt@ydekproductions.com>
This commit is contained in:
Matt Robenolt 2021-11-18 11:11:52 -08:00
Родитель 8245b11cb5
Коммит 7c247f4b82
6 изменённых файлов: 16 добавлений и 21 удалений

Просмотреть файл

@ -22,7 +22,6 @@ import (
"crypto/sha1"
"crypto/sha256"
"crypto/subtle"
"crypto/x509"
"encoding/hex"
"net"
"sync"
@ -132,7 +131,7 @@ const (
// such a hash based on the salt and auth response provided here after retrieving
// the hashed password from the storage.
type HashStorage interface {
UserEntryWithHash(userCerts []*x509.Certificate, salt []byte, user string, authResponse []byte, remoteAddr net.Addr) (Getter, error)
UserEntryWithHash(conn *Conn, salt []byte, user string, authResponse []byte, remoteAddr net.Addr) (Getter, error)
}
// PlainTextStorage describes an object that is suitable to retrieve user information
@ -146,7 +145,7 @@ type HashStorage interface {
// When comparing plain text passwords directly, please ensure to use `subtle.ConstantTimeCompare`
// to prevent timing based attacks on the password.
type PlainTextStorage interface {
UserEntryWithPassword(userCerts []*x509.Certificate, user string, password string, remoteAddr net.Addr) (Getter, error)
UserEntryWithPassword(conn *Conn, user string, password string, remoteAddr net.Addr) (Getter, error)
}
// CachingStorage describes an object that is suitable to retrieve user information
@ -159,7 +158,7 @@ type PlainTextStorage interface {
// such a hash based on the salt and auth response provided here after retrieving
// the hashed password from the cache.
type CachingStorage interface {
UserEntryWithCacheHash(userCerts []*x509.Certificate, salt []byte, user string, authResponse []byte, remoteAddr net.Addr) (Getter, CacheState, error)
UserEntryWithCacheHash(conn *Conn, salt []byte, user string, authResponse []byte, remoteAddr net.Addr) (Getter, CacheState, error)
}
// NewMysqlNativeAuthMethod will create a new AuthMethod that implements the
@ -432,7 +431,7 @@ func (n *mysqlNativePasswordAuthMethod) HandleAuthPluginData(conn *Conn, user st
}
salt := serverAuthPluginData[:len(serverAuthPluginData)-1]
return n.storage.UserEntryWithHash(conn.GetTLSClientCerts(), salt, user, clientAuthPluginData, remoteAddr)
return n.storage.UserEntryWithHash(conn, salt, user, clientAuthPluginData, remoteAddr)
}
type mysqlClearAuthMethod struct {
@ -457,7 +456,7 @@ func (n *mysqlClearAuthMethod) AllowClearTextWithoutTLS() bool {
}
func (n *mysqlClearAuthMethod) HandleAuthPluginData(conn *Conn, user string, serverAuthPluginData []byte, clientAuthPluginData []byte, remoteAddr net.Addr) (Getter, error) {
return n.storage.UserEntryWithPassword(conn.GetTLSClientCerts(), user, string(clientAuthPluginData[:len(clientAuthPluginData)-1]), remoteAddr)
return n.storage.UserEntryWithPassword(conn, user, string(clientAuthPluginData[:len(clientAuthPluginData)-1]), remoteAddr)
}
type mysqlDialogAuthMethod struct {
@ -482,7 +481,7 @@ func (n *mysqlDialogAuthMethod) AuthPluginData() ([]byte, error) {
}
func (n *mysqlDialogAuthMethod) HandleAuthPluginData(conn *Conn, user string, serverAuthPluginData []byte, clientAuthPluginData []byte, remoteAddr net.Addr) (Getter, error) {
return n.storage.UserEntryWithPassword(conn.GetTLSClientCerts(), user, string(clientAuthPluginData[:len(clientAuthPluginData)-1]), remoteAddr)
return n.storage.UserEntryWithPassword(conn, user, string(clientAuthPluginData[:len(clientAuthPluginData)-1]), remoteAddr)
}
func (n *mysqlDialogAuthMethod) AllowClearTextWithoutTLS() bool {
@ -524,7 +523,7 @@ func (n *mysqlCachingSha2AuthMethod) HandleAuthPluginData(c *Conn, user string,
}
salt := serverAuthPluginData[:len(serverAuthPluginData)-1]
result, cacheState, err := n.cache.UserEntryWithCacheHash(c.GetTLSClientCerts(), salt, user, clientAuthPluginData, remoteAddr)
result, cacheState, err := n.cache.UserEntryWithCacheHash(c, salt, user, clientAuthPluginData, remoteAddr)
if err != nil {
return nil, err
@ -560,7 +559,7 @@ func (n *mysqlCachingSha2AuthMethod) HandleAuthPluginData(c *Conn, user string,
return nil, err
}
return n.storage.UserEntryWithPassword(c.GetTLSClientCerts(), user, password, remoteAddr)
return n.storage.UserEntryWithPassword(c, user, password, remoteAddr)
default:
// Somehow someone returned an unknown state, let's error with access denied.
return nil, NewSQLError(ERAccessDeniedError, SSAccessDeniedError, "Access denied for user '%v'", user)

Просмотреть файл

@ -17,7 +17,6 @@ limitations under the License.
package mysql
import (
"crypto/x509"
"flag"
"fmt"
"net"
@ -85,7 +84,8 @@ func (asl *AuthServerClientCert) HandleUser(user string) bool {
}
// UserEntryWithPassword is part of the PlaintextStorage interface
func (asl *AuthServerClientCert) UserEntryWithPassword(userCerts []*x509.Certificate, user string, password string, remoteAddr net.Addr) (Getter, error) {
func (asl *AuthServerClientCert) UserEntryWithPassword(conn *Conn, user string, password string, remoteAddr net.Addr) (Getter, error) {
userCerts := conn.GetTLSClientCerts()
if len(userCerts) == 0 {
return nil, fmt.Errorf("no client certs for connection")
}

Просмотреть файл

@ -17,7 +17,6 @@ limitations under the License.
package mysql
import (
"crypto/x509"
"net"
querypb "vitess.io/vitess/go/vt/proto/query"
@ -51,7 +50,7 @@ func (a *AuthServerNone) HandleUser(user string) bool {
// UserEntryWithHash validates the user if it exists and returns the information.
// Always accepts any user.
func (a *AuthServerNone) UserEntryWithHash(userCerts []*x509.Certificate, salt []byte, user string, authResponse []byte, remoteAddr net.Addr) (Getter, error) {
func (a *AuthServerNone) UserEntryWithHash(conn *Conn, salt []byte, user string, authResponse []byte, remoteAddr net.Addr) (Getter, error) {
return &NoneGetter{}, nil
}

Просмотреть файл

@ -19,7 +19,6 @@ package mysql
import (
"bytes"
"crypto/subtle"
"crypto/x509"
"encoding/json"
"flag"
"net"
@ -161,7 +160,7 @@ func (a *AuthServerStatic) HandleUser(user string) bool {
// UserEntryWithPassword implements password lookup based on a plain
// text password that is negotiated with the client.
func (a *AuthServerStatic) UserEntryWithPassword(userCerts []*x509.Certificate, user string, password string, remoteAddr net.Addr) (Getter, error) {
func (a *AuthServerStatic) UserEntryWithPassword(conn *Conn, user string, password string, remoteAddr net.Addr) (Getter, error) {
a.mu.Lock()
entries, ok := a.entries[user]
a.mu.Unlock()
@ -181,7 +180,7 @@ func (a *AuthServerStatic) UserEntryWithPassword(userCerts []*x509.Certificate,
// UserEntryWithHash implements password lookup based on a
// mysql_native_password hash that is negotiated with the client.
func (a *AuthServerStatic) UserEntryWithHash(userCerts []*x509.Certificate, salt []byte, user string, authResponse []byte, remoteAddr net.Addr) (Getter, error) {
func (a *AuthServerStatic) UserEntryWithHash(conn *Conn, salt []byte, user string, authResponse []byte, remoteAddr net.Addr) (Getter, error) {
a.mu.Lock()
entries, ok := a.entries[user]
a.mu.Unlock()
@ -214,7 +213,7 @@ func (a *AuthServerStatic) UserEntryWithHash(userCerts []*x509.Certificate, salt
// UserEntryWithCacheHash implements password lookup based on a
// caching_sha2_password hash that is negotiated with the client.
func (a *AuthServerStatic) UserEntryWithCacheHash(userCerts []*x509.Certificate, salt []byte, user string, authResponse []byte, remoteAddr net.Addr) (Getter, CacheState, error) {
func (a *AuthServerStatic) UserEntryWithCacheHash(conn *Conn, salt []byte, user string, authResponse []byte, remoteAddr net.Addr) (Getter, CacheState, error) {
a.mu.Lock()
entries, ok := a.entries[user]
a.mu.Unlock()

Просмотреть файл

@ -17,7 +17,6 @@ limitations under the License.
package ldapauthserver
import (
"crypto/x509"
"encoding/json"
"flag"
"fmt"
@ -118,7 +117,7 @@ func (asl *AuthServerLdap) HandleUser(user string) bool {
// UserEntryWithPassword is part of the PlaintextStorage interface
// and called after the password is sent by the client.
func (asl *AuthServerLdap) UserEntryWithPassword(userCerts []*x509.Certificate, user string, password string, remoteAddr net.Addr) (mysql.Getter, error) {
func (asl *AuthServerLdap) UserEntryWithPassword(conn *mysql.Conn, user string, password string, remoteAddr net.Addr) (mysql.Getter, error) {
return asl.validate(user, password)
}

Просмотреть файл

@ -18,7 +18,6 @@ package vault
import (
"crypto/subtle"
"crypto/x509"
"flag"
"fmt"
"net"
@ -166,7 +165,7 @@ func (a *AuthServerVault) HandleUser(user string) bool {
}
// UserEntryWithHash is called when mysql_native_password is used.
func (a *AuthServerVault) UserEntryWithHash(userCerts []*x509.Certificate, salt []byte, user string, authResponse []byte, remoteAddr net.Addr) (mysql.Getter, error) {
func (a *AuthServerVault) UserEntryWithHash(conn *mysql.Conn, salt []byte, user string, authResponse []byte, remoteAddr net.Addr) (mysql.Getter, error) {
a.mu.Lock()
userEntries, ok := a.entries[user]
a.mu.Unlock()