From 946b3841e7f05980d21f9effbbf6e543068cc241 Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Fri, 16 Jul 2021 03:19:33 +0200 Subject: [PATCH] executor, privilege: require CONFIG or Process privilege for is.cluster_* (#26220) --- executor/infoschema_reader.go | 22 ++++++-- executor/memtable_reader.go | 14 +++++ privilege/privileges/privileges_test.go | 68 +++++++++++++++++++++++-- 3 files changed, 96 insertions(+), 8 deletions(-) diff --git a/executor/infoschema_reader.go b/executor/infoschema_reader.go index 274146a6161a7..c381f934af211 100644 --- a/executor/infoschema_reader.go +++ b/executor/infoschema_reader.go @@ -78,6 +78,9 @@ type memtableRetriever struct { // retrieve implements the infoschemaRetriever interface func (e *memtableRetriever) retrieve(ctx context.Context, sctx sessionctx.Context) ([][]types.Datum, error) { + if e.table.Name.O == infoschema.TableClusterInfo && !hasPriv(sctx, mysql.ProcessPriv) { + return nil, plannercore.ErrSpecificAccessDenied.GenWithStackByArgs("PROCESS") + } if e.retrieved { return nil, nil } @@ -1018,10 +1021,21 @@ func (e *memtableRetriever) dataForTiKVStoreStatus(ctx sessionctx.Context) (err } func hasPriv(ctx sessionctx.Context, priv mysql.PrivilegeType) bool { - if pm := privilege.GetPrivilegeManager(ctx); pm != nil { - return pm.RequestVerification(ctx.GetSessionVars().ActiveRoles, "", "", "", priv) - } - return false + pm := privilege.GetPrivilegeManager(ctx) + if pm == nil { + // internal session created with createSession doesn't has the PrivilegeManager. For most experienced cases before, + // we use it like this: + // ``` + // checker := privilege.GetPrivilegeManager(ctx) + // if checker != nil && !checker.RequestVerification(ctx.GetSessionVars().ActiveRoles, schema.Name.L, table.Name.L, "", mysql.AllPrivMask) { + // continue + // } + // do something. + // ``` + // So once the privilege manager is nil, it's a signature of internal sql, so just passing the checker through. + return true + } + return pm.RequestVerification(ctx.GetSessionVars().ActiveRoles, "", "", "", priv) } func (e *memtableRetriever) setDataForTableDataLockWaits(ctx sessionctx.Context) error { diff --git a/executor/memtable_reader.go b/executor/memtable_reader.go index 4d338e0a6e325..6fa43f9fe120d 100644 --- a/executor/memtable_reader.go +++ b/executor/memtable_reader.go @@ -293,6 +293,17 @@ type clusterServerInfoRetriever struct { // retrieve implements the memTableRetriever interface func (e *clusterServerInfoRetriever) retrieve(ctx context.Context, sctx sessionctx.Context) ([][]types.Datum, error) { + switch e.serverInfoType { + case diagnosticspb.ServerInfoType_LoadInfo, + diagnosticspb.ServerInfoType_SystemInfo: + if !hasPriv(sctx, mysql.ProcessPriv) { + return nil, plannercore.ErrSpecificAccessDenied.GenWithStackByArgs("PROCESS") + } + case diagnosticspb.ServerInfoType_HardwareInfo: + if !hasPriv(sctx, mysql.ConfigPriv) { + return nil, plannercore.ErrSpecificAccessDenied.GenWithStackByArgs("CONFIG") + } + } if e.extractor.SkipRequest || e.retrieved { return nil, nil } @@ -485,6 +496,9 @@ func (h *logResponseHeap) Pop() interface{} { } func (e *clusterLogRetriever) initialize(ctx context.Context, sctx sessionctx.Context) ([]chan logStreamResult, error) { + if !hasPriv(sctx, mysql.ProcessPriv) { + return nil, plannercore.ErrSpecificAccessDenied.GenWithStackByArgs("PROCESS") + } serversInfo, err := infoschema.GetClusterServerInfo(sctx) failpoint.Inject("mockClusterLogServerInfo", func(val failpoint.Value) { // erase the error diff --git a/privilege/privileges/privileges_test.go b/privilege/privileges/privileges_test.go index 3cc81da2a4ab4..f4fa0cc8c8349 100644 --- a/privilege/privileges/privileges_test.go +++ b/privilege/privileges/privileges_test.go @@ -1458,7 +1458,9 @@ func (s *testPrivilegeSuite) TestSecurityEnhancedModeInfoschema(c *C) { // Even though we have super, we still can't read protected information from tidb_servers_info, cluster_* tables tk.MustQuery(`SELECT COUNT(*) FROM information_schema.tidb_servers_info WHERE ip IS NOT NULL`).Check(testkit.Rows("0")) - tk.MustQuery(`SELECT COUNT(*) FROM information_schema.cluster_info WHERE status_address IS NOT NULL`).Check(testkit.Rows("0")) + err := tk.QueryToErr(`SELECT COUNT(*) FROM information_schema.cluster_info WHERE status_address IS NOT NULL`) + c.Assert(err, NotNil) + c.Assert(err.Error(), Equals, "[planner:1227]Access denied; you need (at least one of) the PROCESS privilege(s) for this operation") // 36 = a UUID. Normally it is an IP address. tk.MustQuery(`SELECT COUNT(*) FROM information_schema.CLUSTER_STATEMENTS_SUMMARY WHERE length(instance) != 36`).Check(testkit.Rows("0")) @@ -1476,24 +1478,82 @@ func (s *testPrivilegeSuite) TestSecurityEnhancedModeInfoschema(c *C) { func (s *testPrivilegeSuite) TestClusterConfigInfoschema(c *C) { tk := testkit.NewTestKit(c, s.store) - tk.MustExec("CREATE USER ccnobody, ccconfig") + tk.MustExec("CREATE USER ccnobody, ccconfig, ccprocess") tk.MustExec("GRANT CONFIG ON *.* TO ccconfig") + tk.MustExec("GRANT Process ON *.* TO ccprocess") - // incorrect permissions + // incorrect/no permissions tk.Se.Auth(&auth.UserIdentity{ Username: "ccnobody", Hostname: "localhost", }, nil, nil) + tk.MustQuery("SHOW GRANTS").Check(testkit.Rows("GRANT USAGE ON *.* TO 'ccnobody'@'%'")) err := tk.QueryToErr("SELECT * FROM information_schema.cluster_config") + c.Assert(err, NotNil) c.Assert(err.Error(), Equals, "[planner:1227]Access denied; you need (at least one of) the CONFIG privilege(s) for this operation") - // With correct permissions + err = tk.QueryToErr("SELECT * FROM information_schema.cluster_hardware") + c.Assert(err, NotNil) + c.Assert(err.Error(), Equals, "[planner:1227]Access denied; you need (at least one of) the CONFIG privilege(s) for this operation") + + err = tk.QueryToErr("SELECT * FROM information_schema.cluster_info") + c.Assert(err, NotNil) + c.Assert(err.Error(), Equals, "[planner:1227]Access denied; you need (at least one of) the PROCESS privilege(s) for this operation") + + err = tk.QueryToErr("SELECT * FROM information_schema.cluster_load") + c.Assert(err, NotNil) + c.Assert(err.Error(), Equals, "[planner:1227]Access denied; you need (at least one of) the PROCESS privilege(s) for this operation") + + err = tk.QueryToErr("SELECT * FROM information_schema.cluster_systeminfo") + c.Assert(err, NotNil) + c.Assert(err.Error(), Equals, "[planner:1227]Access denied; you need (at least one of) the PROCESS privilege(s) for this operation") + + err = tk.QueryToErr("SELECT * FROM information_schema.cluster_log WHERE time BETWEEN '2021-07-13 00:00:00' AND '2021-07-13 02:00:00' AND message like '%'") + c.Assert(err, NotNil) + c.Assert(err.Error(), Equals, "[planner:1227]Access denied; you need (at least one of) the PROCESS privilege(s) for this operation") + + // With correct/CONFIG permissions tk.Se.Auth(&auth.UserIdentity{ Username: "ccconfig", Hostname: "localhost", }, nil, nil) + tk.MustQuery("SHOW GRANTS").Check(testkit.Rows("GRANT CONFIG ON *.* TO 'ccconfig'@'%'")) + // Needs CONFIG privilege tk.MustQuery("SELECT * FROM information_schema.cluster_config") + tk.MustQuery("SELECT * FROM information_schema.cluster_HARDWARE") + // Missing Process privilege + err = tk.QueryToErr("SELECT * FROM information_schema.cluster_INFO") + c.Assert(err, NotNil) + c.Assert(err.Error(), Equals, "[planner:1227]Access denied; you need (at least one of) the PROCESS privilege(s) for this operation") + err = tk.QueryToErr("SELECT * FROM information_schema.cluster_LOAD") + c.Assert(err, NotNil) + c.Assert(err.Error(), Equals, "[planner:1227]Access denied; you need (at least one of) the PROCESS privilege(s) for this operation") + err = tk.QueryToErr("SELECT * FROM information_schema.cluster_SYSTEMINFO") + c.Assert(err, NotNil) + c.Assert(err.Error(), Equals, "[planner:1227]Access denied; you need (at least one of) the PROCESS privilege(s) for this operation") + err = tk.QueryToErr("SELECT * FROM information_schema.cluster_LOG WHERE time BETWEEN '2021-07-13 00:00:00' AND '2021-07-13 02:00:00' AND message like '%'") + c.Assert(err, NotNil) + c.Assert(err.Error(), Equals, "[planner:1227]Access denied; you need (at least one of) the PROCESS privilege(s) for this operation") + + // With correct/Process permissions + tk.Se.Auth(&auth.UserIdentity{ + Username: "ccprocess", + Hostname: "localhost", + }, nil, nil) + tk.MustQuery("SHOW GRANTS").Check(testkit.Rows("GRANT Process ON *.* TO 'ccprocess'@'%'")) + // Needs Process privilege + tk.MustQuery("SELECT * FROM information_schema.CLUSTER_info") + tk.MustQuery("SELECT * FROM information_schema.CLUSTER_load") + tk.MustQuery("SELECT * FROM information_schema.CLUSTER_systeminfo") + tk.MustQuery("SELECT * FROM information_schema.CLUSTER_log WHERE time BETWEEN '1970-07-13 00:00:00' AND '1970-07-13 02:00:00' AND message like '%'") + // Missing CONFIG privilege + err = tk.QueryToErr("SELECT * FROM information_schema.CLUSTER_config") + c.Assert(err, NotNil) + c.Assert(err.Error(), Equals, "[planner:1227]Access denied; you need (at least one of) the CONFIG privilege(s) for this operation") + err = tk.QueryToErr("SELECT * FROM information_schema.CLUSTER_hardware") + c.Assert(err, NotNil) + c.Assert(err.Error(), Equals, "[planner:1227]Access denied; you need (at least one of) the CONFIG privilege(s) for this operation") } func (s *testPrivilegeSuite) TestSecurityEnhancedModeStatusVars(c *C) {