mirror of
https://github.com/postgres/postgres.git
synced 2026-10-23 13:29:09 +03:00
Fix authorization check for role membership changes.
Presently, check_role_membership_authorization() decides whether
the current user may grant or revoke membership in a role by
calling is_admin_of_role(), which recurses through all grants,
while it chooses the grantor to record for the resulting entry by
calling select_best_admin(), which recurses only through inherited
grants. When the two disagree, the permission check passes and the
grantor lookup then comes up empty, so the user sees an internal
"no possible grantors" error. ALTER GROUP ... ADD USER reaches the
same error through the separate check in AlterRole().
To fix, teach both checks to search the same way
select_best_admin() does via a new has_admin_privs_of_role(). The
new check passes exactly when the grantor lookup was going to
succeed, so nothing that works today starts failing; the internal
error simply becomes a proper permission error. Note that this
leaves the other callers of is_admin_of_role() alone, so a role
reachable only through a non-inherited grant can still be dropped,
renamed, or altered. Whether that ought to change as well is left
as a future exercise.
Oversight in commit ce6b672e44.
Reported-by: ChangAo Chen <cca5507@qq.com>
Author: ChangAo Chen <cca5507@qq.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Pretham <prezza672@gmail.com>
Reviewed-by: Robert Haas <robertmhaas@gmail.com>
Reviewed-by: Jacob Champion <jacob.champion@enterprisedb.com>
Discussion: https://postgr.es/m/tencent_ADCE2B34B230A9B631854806104FEF40C105%40qq.com
Discussion: https://postgr.es/m/CAJUn_kN%2BMhbb8fYP5xxQCq1KEziOinM6HgYx4ts_pPDnQ2y1nQ%40mail.gmail.com
Backpatch-through: 16
This commit is contained in:
@@ -819,7 +819,7 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
|
||||
}
|
||||
|
||||
/* To add or drop members, you need ADMIN OPTION. */
|
||||
if (drolemembers && !is_admin_of_role(currentUserId, roleid))
|
||||
if (drolemembers && !has_admin_privs_of_role(currentUserId, roleid))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
errmsg("permission denied to alter role"),
|
||||
@@ -2151,7 +2151,7 @@ check_role_membership_authorization(Oid currentUserId, Oid roleid,
|
||||
/*
|
||||
* Otherwise, must have admin option on the role to be changed.
|
||||
*/
|
||||
if (!is_admin_of_role(currentUserId, roleid))
|
||||
if (!has_admin_privs_of_role(currentUserId, roleid))
|
||||
{
|
||||
if (is_grant)
|
||||
ereport(ERROR,
|
||||
|
||||
@@ -5103,6 +5103,8 @@ is_member_of_role_nosuper(Oid member, Oid role)
|
||||
* Is member an admin of role? That is, is member the role itself (subject to
|
||||
* restrictions below), a member (directly or indirectly) WITH ADMIN OPTION,
|
||||
* or a superuser?
|
||||
*
|
||||
* See also has_admin_privs_of_role() below.
|
||||
*/
|
||||
bool
|
||||
is_admin_of_role(Oid member, Oid role)
|
||||
@@ -5120,6 +5122,31 @@ is_admin_of_role(Oid member, Oid role)
|
||||
return OidIsValid(admin_role);
|
||||
}
|
||||
|
||||
/*
|
||||
* Does member hold ADMIN OPTION on role, either directly or through a role
|
||||
* whose privileges member inherits?
|
||||
*
|
||||
* Unlike is_admin_of_role(), this does not recurse through grants that are not
|
||||
* inherited. Callers that must go on to record a grantor for the operation
|
||||
* should use this rather than is_admin_of_role(), since select_best_admin()
|
||||
* searches the same way.
|
||||
*/
|
||||
bool
|
||||
has_admin_privs_of_role(Oid member, Oid role)
|
||||
{
|
||||
Oid admin_role;
|
||||
|
||||
if (superuser_arg(member))
|
||||
return true;
|
||||
|
||||
/* By policy, a role cannot have WITH ADMIN OPTION on itself. */
|
||||
if (member == role)
|
||||
return false;
|
||||
|
||||
(void) roles_is_member_of(member, ROLERECURSE_PRIVS, role, &admin_role);
|
||||
return OidIsValid(admin_role);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find a role whose privileges "member" inherits which has ADMIN OPTION
|
||||
* on "role", ignoring super-userness.
|
||||
|
||||
@@ -214,6 +214,7 @@ extern void check_can_set_role(Oid member, Oid role);
|
||||
extern bool is_member_of_role(Oid member, Oid role);
|
||||
extern bool is_member_of_role_nosuper(Oid member, Oid role);
|
||||
extern bool is_admin_of_role(Oid member, Oid role);
|
||||
extern bool has_admin_privs_of_role(Oid member, Oid role);
|
||||
extern Oid select_best_admin(Oid member, Oid role);
|
||||
extern Oid get_role_oid(const char *rolname, bool missing_ok);
|
||||
extern Oid get_role_oid_or_public(const char *rolname);
|
||||
|
||||
@@ -78,6 +78,15 @@ SELECT grantor::regrole FROM pg_auth_members WHERE roleid = 'regress_priv_user1'
|
||||
regress_priv_user2
|
||||
(1 row)
|
||||
|
||||
RESET ROLE;
|
||||
REVOKE INHERIT OPTION FOR regress_priv_user2 FROM regress_priv_user3;
|
||||
SET ROLE regress_priv_user3;
|
||||
GRANT regress_priv_user1 TO regress_priv_user5; -- fail
|
||||
ERROR: permission denied to grant role "regress_priv_user1"
|
||||
DETAIL: Only roles with the ADMIN option on role "regress_priv_user1" may grant this role.
|
||||
ALTER GROUP regress_priv_user1 ADD USER regress_priv_user5; -- fail
|
||||
ERROR: permission denied to alter role
|
||||
DETAIL: Only roles with the ADMIN option on role "regress_priv_user1" may add or drop members.
|
||||
RESET ROLE;
|
||||
REVOKE regress_priv_user2 FROM regress_priv_user3;
|
||||
REVOKE regress_priv_user1 FROM regress_priv_user2 CASCADE;
|
||||
|
||||
@@ -60,6 +60,11 @@ SET ROLE regress_priv_user3;
|
||||
GRANT regress_priv_user1 TO regress_priv_user4;
|
||||
SELECT grantor::regrole FROM pg_auth_members WHERE roleid = 'regress_priv_user1'::regrole and member = 'regress_priv_user4'::regrole;
|
||||
RESET ROLE;
|
||||
REVOKE INHERIT OPTION FOR regress_priv_user2 FROM regress_priv_user3;
|
||||
SET ROLE regress_priv_user3;
|
||||
GRANT regress_priv_user1 TO regress_priv_user5; -- fail
|
||||
ALTER GROUP regress_priv_user1 ADD USER regress_priv_user5; -- fail
|
||||
RESET ROLE;
|
||||
REVOKE regress_priv_user2 FROM regress_priv_user3;
|
||||
REVOKE regress_priv_user1 FROM regress_priv_user2 CASCADE;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user