Skip to content

Commit

Permalink
server: Return error when no password is specified for caching_sha2_p…
Browse files Browse the repository at this point in the history
…assword account with password (#40858)

close #40831
  • Loading branch information
dveeden authored Jan 31, 2023
1 parent c8bffd4 commit 6a55f3e
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 5 deletions.
25 changes: 20 additions & 5 deletions server/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -668,12 +668,12 @@ func (cc *clientConn) readOptionalSSLRequestAndHandshakeResponse(ctx context.Con

switch resp.AuthPlugin {
case mysql.AuthCachingSha2Password:
resp.Auth, err = cc.authSha(ctx)
resp.Auth, err = cc.authSha(ctx, resp)
if err != nil {
return err
}
case mysql.AuthTiDBSM3Password:
resp.Auth, err = cc.authSM3(ctx)
resp.Auth, err = cc.authSM3(ctx, resp)
if err != nil {
return err
}
Expand Down Expand Up @@ -727,14 +727,21 @@ func (cc *clientConn) handleAuthPlugin(ctx context.Context, resp *handshakeRespo
}

// authSha implements the caching_sha2_password specific part of the protocol.
func (cc *clientConn) authSha(ctx context.Context) ([]byte, error) {
func (cc *clientConn) authSha(ctx context.Context, resp handshakeResponse41) ([]byte, error) {
const (
shaCommand = 1
requestRsaPubKey = 2 // Not supported yet, only TLS is supported as secure channel.
fastAuthOk = 3
fastAuthFail = 4
)

// If no password is specified, we don't send the FastAuthFail to do the full authentication
// as that doesn't make sense without a password and confuses the client.
// https://github.com/pingcap/tidb/issues/40831
if len(resp.Auth) == 0 {
return []byte{}, nil
}

// Currently we always send a "FastAuthFail" as the cached part of the protocol isn't implemented yet.
// This triggers the client to send the full response.
err := cc.writePacket([]byte{0, 0, 0, 0, shaCommand, fastAuthFail})
Expand All @@ -757,8 +764,16 @@ func (cc *clientConn) authSha(ctx context.Context) ([]byte, error) {
}

// authSM3 implements the tidb_sm3_password specific part of the protocol.
func (cc *clientConn) authSM3(ctx context.Context) ([]byte, error) {
err := cc.writePacket([]byte{0, 0, 0, 0, 1, 4})
// tidb_sm3_password is very similar to caching_sha2_password.
func (cc *clientConn) authSM3(ctx context.Context, resp handshakeResponse41) ([]byte, error) {
// If no password is specified, we don't send the FastAuthFail to do the full authentication
// as that doesn't make sense without a password and confuses the client.
// https://github.com/pingcap/tidb/issues/40831
if len(resp.Auth) == 0 {
return []byte{}, nil
}

err := cc.writePacket([]byte{0, 0, 0, 0, 1, 4}) // fastAuthFail
if err != nil {
logutil.Logger(ctx).Error("authSM3 packet write failed", zap.Error(err))
return nil, err
Expand Down
45 changes: 45 additions & 0 deletions server/conn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1806,3 +1806,48 @@ func TestExtensionChangeUser(t *testing.T) {
require.Equal(t, expectedConnInfo.Error, logInfo.Error)
require.Equal(t, *(expectedConnInfo.ConnectionInfo), *(logInfo.ConnectionInfo))
}

func TestAuthSha(t *testing.T) {
store := testkit.CreateMockStore(t)

var outBuffer bytes.Buffer
tidbdrv := NewTiDBDriver(store)
cfg := newTestConfig()
cfg.Port, cfg.Status.StatusPort = 0, 0
cfg.Status.ReportStatus = false
server, err := NewServer(cfg, tidbdrv)
require.NoError(t, err)
defer server.Close()

cc := &clientConn{
connectionID: 1,
salt: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14},
server: server,
pkt: &packetIO{
bufWriter: bufio.NewWriter(&outBuffer),
},
collation: mysql.DefaultCollationID,
peerHost: "localhost",
alloc: arena.NewAllocator(512),
chunkAlloc: chunk.NewAllocator(),
capability: mysql.ClientProtocol41,
}

tk := testkit.NewTestKit(t, store)
ctx := &TiDBContext{Session: tk.Session()}
cc.setCtx(ctx)

resp := handshakeResponse41{
Capability: mysql.ClientProtocol41 | mysql.ClientPluginAuth,
AuthPlugin: mysql.AuthCachingSha2Password,
Auth: []byte{}, // No password
}

authData, err := cc.authSha(context.Background(), resp)
require.NoError(t, err)

// If no password is specified authSha() should return an empty byte slice
// which differs from when a password is specified as that should trigger
// fastAuthFail and the rest of the auth process.
require.Equal(t, authData, []byte{})
}

0 comments on commit 6a55f3e

Please sign in to comment.