From 395ccbe22d5e0d0f6412f33d9bbf8d90fe52cc66 Mon Sep 17 00:00:00 2001 From: tangenta Date: Mon, 13 Jun 2022 18:42:32 +0800 Subject: [PATCH] privilege: limit the privileges in memory schemas (#35260) close pingcap/tidb#35205 --- executor/grant.go | 29 --------------- executor/grant_test.go | 47 ++++++++----------------- planner/core/planbuilder.go | 24 +++++++++++-- privilege/privileges/privileges.go | 26 ++++---------- privilege/privileges/privileges_test.go | 3 ++ 5 files changed, 46 insertions(+), 83 deletions(-) diff --git a/executor/grant.go b/executor/grant.go index da10998845f94..04e7ffc5914c3 100644 --- a/executor/grant.go +++ b/executor/grant.go @@ -511,13 +511,6 @@ func (e *GrantExec) grantDBLevel(priv *ast.PrivElem, user *ast.UserSpec, interna dbName = e.ctx.GetSessionVars().CurrentDB } - // Some privilege can not be granted to performance_schema.* in MySQL. - // As TiDB ignores the privilege management part for this system database, - // check is performed here - if strings.EqualFold(dbName, "performance_schema") && e.checkPerformanceSchemaPriv(priv.Priv) { - return e.dbAccessDenied(dbName) - } - sql := new(strings.Builder) sqlexec.MustFormatSQL(sql, "UPDATE %n.%n SET ", mysql.SystemDB, mysql.DBTable) err := composeDBPrivUpdate(sql, priv.Priv, "Y") @@ -582,28 +575,6 @@ func (e *GrantExec) grantColumnLevel(priv *ast.PrivElem, user *ast.UserSpec, int return nil } -func (e *GrantExec) 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) -} - -// If the privilege can not be granted, return true -func (e *GrantExec) checkPerformanceSchemaPriv(privType mysql.PrivilegeType) bool { - // Attempts to use GRANT ALL as shorthand for granting privileges - // at the database leval fail with an error - // See https://dev.mysql.com/doc/refman/8.0/en/performance-schema-table-characteristics.html for more detail - // Others are rejected in MySQL 8.0 - return privType == mysql.AllPriv || privType == mysql.CreatePriv || - privType == mysql.ReferencesPriv || privType == mysql.AlterPriv || privType == mysql.ExecutePriv || - privType == mysql.IndexPriv || privType == mysql.CreateViewPriv || privType == mysql.ShowViewPriv -} - // composeGlobalPrivUpdate composes update stmt assignment list string for global scope privilege update. func composeGlobalPrivUpdate(sql *strings.Builder, priv mysql.PrivilegeType, value string) error { if priv != mysql.AllPriv { diff --git a/executor/grant_test.go b/executor/grant_test.go index 05d64b0d7b698..89b6c3c82e425 100644 --- a/executor/grant_test.go +++ b/executor/grant_test.go @@ -19,6 +19,7 @@ import ( "strings" "testing" + "github.com/pingcap/tidb/errno" "github.com/pingcap/tidb/executor" "github.com/pingcap/tidb/infoschema" "github.com/pingcap/tidb/parser/auth" @@ -522,41 +523,23 @@ func TestPerformanceSchemaPrivGrant(t *testing.T) { tk.MustExec("drop user issue27867;") }() require.True(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost"}, nil, nil)) - err := tk.ExecToErr("grant all on performance_schema.* to issue27867;") - require.Error(t, err) - require.EqualError(t, err, "[executor:1044]Access denied for user 'root'@'%' to database 'performance_schema'") + tk.MustGetErrCode("grant all on performance_schema.* to issue27867;", errno.ErrDBaccessDenied) // Check case insensitivity - err = tk.ExecToErr("grant all on PERFormanCE_scHemA.* to issue27867;") - require.Error(t, err) - require.EqualError(t, err, "[executor:1044]Access denied for user 'root'@'%' to database 'PERFormanCE_scHemA'") + tk.MustGetErrCode("grant all on PERFormanCE_scHemA.* to issue27867;", errno.ErrDBaccessDenied) // Check other database privileges tk.MustExec("grant select on performance_schema.* to issue27867;") - tk.MustExec("grant insert on performance_schema.* to issue27867;") - tk.MustExec("grant update on performance_schema.* to issue27867;") - tk.MustExec("grant delete on performance_schema.* to issue27867;") - tk.MustExec("grant drop on performance_schema.* to issue27867;") - tk.MustExec("grant lock tables on performance_schema.* to issue27867;") - err = tk.ExecToErr("grant create on performance_schema.* to issue27867;") - require.Error(t, err) - require.EqualError(t, err, "[executor:1044]Access denied for user 'root'@'%' to database 'performance_schema'") - err = tk.ExecToErr("grant references on performance_schema.* to issue27867;") - require.Error(t, err) - require.EqualError(t, err, "[executor:1044]Access denied for user 'root'@'%' to database 'performance_schema'") - err = tk.ExecToErr("grant alter on PERFormAnCE_scHemA.* to issue27867;") - require.Error(t, err) - require.EqualError(t, err, "[executor:1044]Access denied for user 'root'@'%' to database 'PERFormAnCE_scHemA'") - err = tk.ExecToErr("grant execute on performance_schema.* to issue27867;") - require.Error(t, err) - require.EqualError(t, err, "[executor:1044]Access denied for user 'root'@'%' to database 'performance_schema'") - err = tk.ExecToErr("grant index on PERFormanCE_scHemA.* to issue27867;") - require.Error(t, err) - require.EqualError(t, err, "[executor:1044]Access denied for user 'root'@'%' to database 'PERFormanCE_scHemA'") - err = tk.ExecToErr("grant create view on performance_schema.* to issue27867;") - require.Error(t, err) - require.EqualError(t, err, "[executor:1044]Access denied for user 'root'@'%' to database 'performance_schema'") - err = tk.ExecToErr("grant show view on performance_schema.* to issue27867;") - require.Error(t, err) - require.EqualError(t, err, "[executor:1044]Access denied for user 'root'@'%' to database 'performance_schema'") + tk.MustGetErrCode("grant insert on performance_schema.* to issue27867;", errno.ErrDBaccessDenied) + tk.MustGetErrCode("grant update on performance_schema.* to issue27867;", errno.ErrDBaccessDenied) + tk.MustGetErrCode("grant delete on performance_schema.* to issue27867;", errno.ErrDBaccessDenied) + tk.MustGetErrCode("grant drop on performance_schema.* to issue27867;", errno.ErrDBaccessDenied) + tk.MustGetErrCode("grant lock tables on performance_schema.* to issue27867;", errno.ErrDBaccessDenied) + tk.MustGetErrCode("grant create on performance_schema.* to issue27867;", errno.ErrDBaccessDenied) + tk.MustGetErrCode("grant references on performance_schema.* to issue27867;", errno.ErrDBaccessDenied) + tk.MustGetErrCode("grant alter on PERFormAnCE_scHemA.* to issue27867;", errno.ErrDBaccessDenied) + tk.MustGetErrCode("grant execute on performance_schema.* to issue27867;", errno.ErrDBaccessDenied) + tk.MustGetErrCode("grant index on PERFormanCE_scHemA.* to issue27867;", errno.ErrDBaccessDenied) + tk.MustGetErrCode("grant create view on performance_schema.* to issue27867;", errno.ErrDBaccessDenied) + tk.MustGetErrCode("grant show view on performance_schema.* to issue27867;", errno.ErrDBaccessDenied) } func TestGrantDynamicPrivs(t *testing.T) { diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index 7f09e4a4f6476..040573420a076 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -3233,6 +3233,7 @@ func collectVisitInfoFromGrantStmt(sctx sessionctx.Context, vi []visitInfo, stmt } var nonDynamicPrivilege bool var allPrivs []mysql.PrivilegeType + authErr := genAuthErrForGrantStmt(sctx, dbName) for _, item := range stmt.Privs { if item.Priv == mysql.ExtendedPriv { // The observed MySQL behavior is that the error is: @@ -3262,20 +3263,37 @@ func collectVisitInfoFromGrantStmt(sctx sessionctx.Context, vi []visitInfo, stmt } break } - vi = appendVisitInfo(vi, item.Priv, dbName, tableName, "", nil) + vi = appendVisitInfo(vi, item.Priv, dbName, tableName, "", authErr) } for _, priv := range allPrivs { - vi = appendVisitInfo(vi, priv, dbName, tableName, "", nil) + vi = appendVisitInfo(vi, priv, dbName, tableName, "", authErr) } if nonDynamicPrivilege { // Dynamic privileges use their own GRANT OPTION. If there were any non-dynamic privilege requests, // we need to attach the "GLOBAL" version of the GRANT OPTION. - vi = appendVisitInfo(vi, mysql.GrantPriv, dbName, tableName, "", nil) + vi = appendVisitInfo(vi, mysql.GrantPriv, dbName, tableName, "", authErr) } return vi, nil } +func genAuthErrForGrantStmt(sctx sessionctx.Context, dbName string) error { + if !strings.EqualFold(dbName, variable.PerformanceSchema) { + return nil + } + user := sctx.GetSessionVars().User + if user == nil { + return nil + } + u := user.Username + h := user.Hostname + if len(user.AuthUsername) > 0 && len(user.AuthHostname) > 0 { + u = user.AuthUsername + h = user.AuthHostname + } + return ErrDBaccessDenied.FastGenByArgs(u, h, dbName) +} + func (b *PlanBuilder) getDefaultValue(col *table.Column) (*expression.Constant, error) { var ( value types.Datum diff --git a/privilege/privileges/privileges.go b/privilege/privileges/privileges.go index 7b499bdd64100..e6633b03f1d5d 100644 --- a/privilege/privileges/privileges.go +++ b/privilege/privileges/privileges.go @@ -23,7 +23,6 @@ import ( "sync" "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/infoschema/perfschema" "github.com/pingcap/tidb/parser/auth" "github.com/pingcap/tidb/parser/mysql" "github.com/pingcap/tidb/privilege" @@ -136,29 +135,18 @@ func (p *UserPrivileges) RequestVerification(activeRoles []*auth.RoleIdentity, d } } - switch dbLowerName { - case util.InformationSchemaName.L: + if util.IsMemDB(dbLowerName) { switch priv { case mysql.CreatePriv, mysql.AlterPriv, mysql.DropPriv, mysql.IndexPriv, mysql.CreateViewPriv, - mysql.InsertPriv, mysql.UpdatePriv, mysql.DeletePriv: + mysql.InsertPriv, mysql.UpdatePriv, mysql.DeletePriv, mysql.ReferencesPriv, mysql.ExecutePriv, + mysql.ShowViewPriv, mysql.LockTablesPriv: return false } - return true - // We should be very careful of limiting privileges, so ignore `mysql` for now. - case util.PerformanceSchemaName.L: - if perfschema.IsPredefinedTable(table) { - switch priv { - case mysql.CreatePriv, mysql.AlterPriv, mysql.DropPriv, mysql.IndexPriv, mysql.InsertPriv, mysql.UpdatePriv, mysql.DeletePriv: - return false - } - } - case util.MetricSchemaName.L: - if infoschema.IsMetricTable(table) { - switch priv { - case mysql.CreatePriv, mysql.AlterPriv, mysql.DropPriv, mysql.IndexPriv, mysql.InsertPriv, mysql.UpdatePriv, mysql.DeletePriv: - return false + if dbLowerName == util.InformationSchemaName.L { + return true + } else if dbLowerName == util.MetricSchemaName.L { // PROCESS is the same with SELECT for metrics_schema. - case mysql.SelectPriv: + if priv == mysql.SelectPriv && infoschema.IsMetricTable(table) { priv |= mysql.ProcessPriv } } diff --git a/privilege/privileges/privileges_test.go b/privilege/privileges/privileges_test.go index 81ed179605c1a..c7d465c52006e 100644 --- a/privilege/privileges/privileges_test.go +++ b/privilege/privileges/privileges_test.go @@ -1277,6 +1277,9 @@ func TestSystemSchema(t *testing.T) { err = tk.ExecToErr("create table metric_schema.t(a int)") require.Error(t, err) require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) + + tk.MustGetErrCode("create table metrics_schema.t (id int);", errno.ErrTableaccessDenied) + tk.MustGetErrCode("create table performance_schema.t (id int);", errno.ErrTableaccessDenied) } func TestPerformanceSchema(t *testing.T) {