From e9b9da788895d954076b44430ce0e60ba15ced5d Mon Sep 17 00:00:00 2001 From: Morgan Tocker Date: Thu, 22 Nov 2018 11:34:43 -0700 Subject: [PATCH 1/2] executor: privilege check USE command improves mysql compatibility --- executor/errors.go | 2 ++ executor/simple.go | 18 ++++++++++++++++++ server/server_test.go | 12 ++++++++---- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/executor/errors.go b/executor/errors.go index ad1c7d9b36344..3ad525aa068cd 100644 --- a/executor/errors.go +++ b/executor/errors.go @@ -45,6 +45,7 @@ var ( ErrCantChangeTxCharacteristics = terror.ClassExecutor.New(mysql.ErrCantChangeTxCharacteristics, mysql.MySQLErrName[mysql.ErrCantChangeTxCharacteristics]) ErrPsManyParam = terror.ClassExecutor.New(mysql.ErrPsManyParam, mysql.MySQLErrName[mysql.ErrPsManyParam]) ErrAdminCheckTable = terror.ClassExecutor.New(mysql.ErrAdminCheckTable, mysql.MySQLErrName[mysql.ErrAdminCheckTable]) + ErrDBaccessDenied = terror.ClassExecutor.New(mysql.ErrDBaccessDenied, mysql.MySQLErrName[mysql.ErrDBaccessDenied]) ) func init() { @@ -57,6 +58,7 @@ func init() { mysql.ErrCantChangeTxCharacteristics: mysql.ErrCantChangeTxCharacteristics, mysql.ErrPsManyParam: mysql.ErrPsManyParam, mysql.ErrAdminCheckTable: mysql.ErrAdminCheckTable, + mysql.ErrDBaccessDenied: mysql.ErrDBaccessDenied, } terror.ErrClassToMySQLCodes[terror.ClassExecutor] = tableMySQLErrCodes } diff --git a/executor/simple.go b/executor/simple.go index 79fd9110f3570..47e6005dfd63f 100644 --- a/executor/simple.go +++ b/executor/simple.go @@ -26,6 +26,7 @@ import ( "github.com/pingcap/tidb/config" "github.com/pingcap/tidb/domain" "github.com/pingcap/tidb/infoschema" + "github.com/pingcap/tidb/privilege" "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/sessionctx/variable" "github.com/pingcap/tidb/util/chunk" @@ -83,8 +84,25 @@ func (e *SimpleExec) Next(ctx context.Context, chk *chunk.Chunk) (err error) { return errors.Trace(err) } +func (e *SimpleExec) dbAccessDenied(dbname string) error { + user := e.ctx.GetSessionVars().User + u := user.Username + h := user.Hostname + if len(user.AuthUsername) > 0 && len(user.AuthHostname) > 0 { + u = user.AuthUsername + h = user.AuthHostname + } + return ErrDBaccessDenied.GenWithStackByArgs(u, h, dbname) +} + func (e *SimpleExec) executeUse(s *ast.UseStmt) error { dbname := model.NewCIStr(s.DBName) + + checker := privilege.GetPrivilegeManager(e.ctx) + if checker != nil && e.ctx.GetSessionVars().User != nil && !checker.DBIsVisible(fmt.Sprint(dbname)) { + return e.dbAccessDenied(dbname.O) + } + dbinfo, exists := e.is.SchemaByName(dbname) if !exists { return infoschema.ErrDatabaseNotExists.GenWithStackByArgs(dbname) diff --git a/server/server_test.go b/server/server_test.go index b488a67d6db6e..7a05ec74f35d8 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -616,13 +616,14 @@ func runTestShowProcessList(c *C) { func runTestAuth(c *C) { runTests(c, nil, func(dbt *DBTest) { dbt.mustExec(`CREATE USER 'authtest'@'%' IDENTIFIED BY '123';`) + dbt.mustExec(`GRANT ALL on test.* to 'authtest'`) dbt.mustExec(`FLUSH PRIVILEGES;`) }) runTests(c, func(config *mysql.Config) { config.User = "authtest" config.Passwd = "123" }, func(dbt *DBTest) { - dbt.mustExec(`USE mysql;`) + dbt.mustExec(`USE information_schema;`) }) db, err := sql.Open("mysql", getDSN(func(config *mysql.Config) { @@ -630,20 +631,21 @@ func runTestAuth(c *C) { config.Passwd = "456" })) c.Assert(err, IsNil) - _, err = db.Query("USE mysql;") + _, err = db.Query("USE information_schema;") c.Assert(err, NotNil, Commentf("Wrong password should be failed")) db.Close() // Test login use IP that not exists in mysql.user. runTests(c, nil, func(dbt *DBTest) { dbt.mustExec(`CREATE USER 'authtest2'@'localhost' IDENTIFIED BY '123';`) + dbt.mustExec(`GRANT ALL on test.* to 'authtest2'@'localhost'`) dbt.mustExec(`FLUSH PRIVILEGES;`) }) runTests(c, func(config *mysql.Config) { config.User = "authtest2" config.Passwd = "123" }, func(dbt *DBTest) { - dbt.mustExec(`USE mysql;`) + dbt.mustExec(`USE information_schema;`) }) } @@ -680,7 +682,9 @@ func runTestIssue3680(c *C) { func runTestIssue3682(c *C) { runTests(c, nil, func(dbt *DBTest) { dbt.mustExec(`CREATE USER 'issue3682'@'%' IDENTIFIED BY '123';`) - dbt.mustExec(`FLUSH PRIVILEGES;`) + dbt.mustExec(`GRANT ALL on test.* to 'issue3682'`) + dbt.mustExec(`GRANT ALL on mysql.* to 'issue3682'`) + dbt.mustExec(`FLUSH PRIVILEGES`) }) runTests(c, func(config *mysql.Config) { config.User = "issue3682" From 46bcab5c09b12333bc9334f6124d21564a946acb Mon Sep 17 00:00:00 2001 From: Morgan Tocker Date: Fri, 23 Nov 2018 09:13:08 -0700 Subject: [PATCH 2/2] Addressed PR feedback --- privilege/privileges/privileges_test.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/privilege/privileges/privileges_test.go b/privilege/privileges/privileges_test.go index 48cebbe1bae9f..eb33d9a70239a 100644 --- a/privilege/privileges/privileges_test.go +++ b/privilege/privileges/privileges_test.go @@ -303,6 +303,31 @@ func (s *testPrivilegeSuite) TestCheckAuthenticate(c *C) { c.Assert(se.Auth(&auth.UserIdentity{Username: "u4", Hostname: "localhost"}, nil, nil), IsFalse) } +func (s *testPrivilegeSuite) TestUseDb(c *C) { + + se := newSession(c, s.store, s.dbName) + // high privileged user + mustExec(c, se, "CREATE USER 'usesuper'") + mustExec(c, se, "CREATE USER 'usenobody'") + mustExec(c, se, "GRANT ALL ON *.* TO 'usesuper'") + mustExec(c, se, "FLUSH PRIVILEGES") + c.Assert(se.Auth(&auth.UserIdentity{Username: "usesuper", Hostname: "localhost", AuthUsername: "usesuper", AuthHostname: "%"}, nil, nil), IsTrue) + mustExec(c, se, "use mysql") + // low privileged user + c.Assert(se.Auth(&auth.UserIdentity{Username: "usenobody", Hostname: "localhost", AuthUsername: "usenobody", AuthHostname: "%"}, nil, nil), IsTrue) + _, err := se.Execute(context.Background(), "use mysql") + c.Assert(err, NotNil) + + // try again after privilege granted + c.Assert(se.Auth(&auth.UserIdentity{Username: "usesuper", Hostname: "localhost", AuthUsername: "usesuper", AuthHostname: "%"}, nil, nil), IsTrue) + mustExec(c, se, "GRANT SELECT ON mysql.* TO 'usenobody'") + mustExec(c, se, "FLUSH PRIVILEGES") + c.Assert(se.Auth(&auth.UserIdentity{Username: "usenobody", Hostname: "localhost", AuthUsername: "usenobody", AuthHostname: "%"}, nil, nil), IsTrue) + _, err = se.Execute(context.Background(), "use mysql") + c.Assert(err, IsNil) + +} + func (s *testPrivilegeSuite) TestInformationSchema(c *C) { // This test tests no privilege check for INFORMATION_SCHEMA database.