WL#12364: Kill administration for system users
Affects: Server-8.0
—
Status: Complete
WL#12098 introduced a new dynamic privilege 'SYSTEM_USER'. The user who is granted this privilege can be treated as power user. In other words, a user can modify another user with SYSTEM_USER privilege only if former too has SYSTEM_USER privilege. This also means that a user who is not granted 'SYSTEM_USER' privilege should not be able to kill another user's session/query who is granted 'SYSTEM_USER' privilege (i.e. a power user). Currently one can use the KILL command to kill other user's session/query only if they have the CONNECTION_ADMIN/SUPER privilege. There's no discrimination of what users they can use it on. The goal of this worklog is to disallow the KILL command terminating the power user's session/query from another user's session who is granted CONNECTION_ADMIN/SUPER but not SYSTEM_USER privilege. Only one power user should be able to kill session/query of another power user.
Definitions =========== Power_user ---------- A user who is granted at least SYSTEM_USER privilege through GRANT statement. Regular_user ------------ A user who is granted at least CONNECTION_ADMIN privilege but not the SYSTEM_USER privilege. Power_session ------------- A user session that gets SYSTEM_USER privilege through role activation. OR User already had SYSTEM_USER privilege before the session was established. Regular_session --------------- A user session that is not power_session. Functional Requirements ======================= FR-01: Regular_user MUST NOT be able to KILL the power_session. FR-01.1: Regular_user MAY be able to KILL the EXISTING connections of a user who is just elevated as power_user through GRANT statement. FR-01.2: Regular_user MAY NOT be able to KILL the EXISTING connections of a user who is just demoted from power_user through REVOKE statement. FR-02: Regular user MUST NOT be able to KILL the queries running in power_session. FR-03: A regular_session MUST become power_session by activating the role that has SYSTEM_USER privilege granted. FR-04: A power_session MUST become regular_session after deactivating the role has SYSTEM_USER privilege granted. FR-05: A regular_session MAY NOT become power_session by simply granting SYSTEM_USER privilege to the user unless connection is re-established or a role is activated/deactivated. FR-06: A power_session MAY NOT become regular_session by simply revoking SYSTEM_USER privilege from user unless connection is re-established or a role is activated/deactivated. FR-07: Running a SQL statement as definer who has SYSTEM_USER privilege MUST NOT elevate the regular_session as power_session even temporarily.
Interface Specification ======================= No changes required. High-Level Specification ======================== S-1: Design Considerations: We evaluated the following design approaches. S-1.1: Probe, the security context of current session and the security context of the session asked to be killed, for the SYSTEM_USER privilege. If former does not have SYSTEM_USER privilege but latter has then former throws error. This seems easiest and simplest approach but it would have a potential synchronization issues. Security_context may change temporarily while executing SQL statements. Imagine, there are two sessions t1 and t2; t2 wants to kill t1. t1 is in the middle of executing a query that has changed the Security_context temporarily. The changed security context does not have SYSTEM_USER privilege granted. When t2 probes the Security_context of t1 it sees that t1 doesn't have SYSTEM_USER privilege hence, it can kill the t1 session which is not correct. S-1.2 THD maintains the original Security_context as m_main_security_ctx member variable. we could consider probing the original Security_context instead of the current security context. But we identified at least following circumstances when the original security_context may be changed as well. - The client connection may change the current user by issuing the COM_CHANGE_USER command. - SET ROLE statement may change the original security_context - Plugin/component may also change the original security_context through security_context services. Hence, this approach would not solve the synchronization issue identified with the S-1.1 completely. S-1.3 Add an atomic flag in THD class. Turn ON the flag when user's session is authenticated or a role that has SYSTEM_USER privilege is activated. Turn OFF the flag when a role that has SYSTEM_USER privilege is deactivated. This approach solves the synchronization issues identified in the above two approaches. But, it has following two limitations: - The existing sessions of a user are not updated if user is granted SYSTEM_USER privilege through other user. It could mean that connections, before SYSTEM_USER privilege was granted to an user, could still be killed by the user who does not have SYSTEM_USER privilege. - There could be small window of race conditions in between of SQL statement, changing the security_context and updating the atomic flag in the THD. For instance: mysql_set_active_role() { Security_context *sctx = thd->security_context(); ret = sctx->activate_role(role->user, role->host, true); /* Here, session could still be killed just before the user got system_user privilege but flag in the THD not yet updated */ thd->set_system_user_flag(); } Either of S-1.1 or S-1.2 would require to take the lock (i.e. Lock_thd_data) when the security_context is accessed through THD::security_context() API. This may have potential performance impact. S-1.3 has two limitations but We can live them since these are uncommon scenarios. After carefully evaluating above three approaches, we decided to go with S-1.3. S-2: Rationale behind FR-03,04 is that role activation/de-activation is a session specific task. Therefore, SET ROLE statement can promote a session from regular to power or vice-versa. We probe the user's security_context for SYSTEM_USER privilege before executing the SET ROLE statement. S-3 Rationale behind Fr-05,06 is that usually privileges are granted/revoked to a user through another user's session. It would be very expensive operation to reflect the change in user's privilege to its existing connections. Therefore, we update the system_user flag in the connection either when connection establishes or roles activated/deactivated. S-4: Rationale behind FR-07 is that running a SQL statement with power user as definer does not affect the user's session in anyways. Instead, it might be required to execute the SQL statements. Therefore, regular session might get killed even if it is running a SQL statement with definer who has SYSTEM_USER privilege. S-5: Impact on replication :- None S-6: Impact on upgrade :- None S-7: Threads not created by the users are categorized as system threads in server. That raises an interesting question "Can regular user kill the system threads ?". To answer above question. We prepare following matrix threads created by server. Objective of the matrix is to categorize the threads in four quadrant so that: - we can identify which system_threads that gets the SYSTEM_USER privilege by the virtue of skip-grants flag. Hence, they fall into to (1) quadrant and it is safe to not allow end-users to kill them. Since end-users cannot them spawn them anyways. - We can identify which system threads are safe for users in (4) quadrant to kill. - We can identify system threads in (3) quadrant that needs to be moved to 1st quadrant so that users in (4) quadrant cannot kill them. - The remaining system threads in the 3rd quadrant will probably will be safe to be killed by users in 4th quadrant. Threads added in (3) quadrant need to be evaluated for the fact if they can be run by SYSTEM_USER as definer. Internal threads External threads ┌──────────────────────────────────────┬─────────────────────────────┐ │ (1) │ (2) │ With │ SYSTEM_THREAD_SLAVE_IO │ │ system │ SYSTEM_THREAD_SLAVE_SQL │ User connections with │ _user │ SYSTEM_THREAD_EVENT_SCHEDULER │ SYSTEM_USER privilege │ │ SYSTEM_THREAD_NDBCLUSTER_BINLOG │ │ │ SYSTEM_THREAD_SLAVE_WORKER │ │ │ SYSTEM_THREAD_COMPRESS_GTID_TABLE │ │ │ │ │ │ │ │ ├──────────────────────────────────────┼─────────────────────────────┤ │ (3) │ (4) │ │ │ │ │ SYSTEM_THREAD_EVENT_WORKER │ User connections without │ without │ │ SYSTEM_USER privilege │ system │ │ │ _user │ Threads that will be moved to (1) │ │ │ SYSTEM_THREAD_INFO_REPOSITORY │ │ │ SYSTEM_THREAD_DD_INITIALIZE │ │ │ SYSTEM_THREAD_DD_RESTART │ │ │ SYSTEM_THREAD_SERVER_INITIALIZE │ │ │ SYSTEM_THREAD_INIT_FILE │ │ │ SYSTEM_THREAD_SERVER_UPGRADE │ │ │ SYSTEM_THREAD_BACKGROUND │ │ │ │ │ └──────────────────────────────────────┴─────────────────────────────┘ - SYSTEM_THREAD_EVENT_WORKER Is not granted any privilege. It runs with the definer's security context at the time of executing the event. It is safe for them to be killed by users in 4th quadrant if definer does not have SYSTEM_USER privilege. Hence, we can keep them in the 3rd quadrant. We could grant SYSTEM_USER privilege to the following threads even though most of them are bootstrap threads so end-users will not see them. - SYSTEM_THREAD_INFO_REPOSITORY Created during bootstrap process or mysql startup. Used to read rpl_info table - SYSTEM_THREAD_DD_INITIALIZE Bootstrap thread. Runs during server initialization. No privileges granted. - SYSTEM_THREAD_DD_RESTART Bootstrap thread. Runs during server initialization. No privileges granted. - SYSTEM_THREAD_SERVER_INITIALIZE Runs during server initialization. No privileges granted. - SYSTEM_THREAD_INIT_FILE Runs during server initialization. No privileges granted. - SYSTEM_THREAD_SERVER_UPGRADE Bootstrap thread. Runs during server initialization. No privileges granted. - SYSTEM_THREAD_BACKGROUND Do not list processlist_id in the performance_schema.threads table. It means they cannot be killed by KILL command. Hence, we can keep them in the 3rd quadrant.
Copyright (c) 2000, 2024, Oracle Corporation and/or its affiliates. All rights reserved.