diff options
Diffstat (limited to 'src/backend/utils/adt/acl.c')
-rw-r--r-- | src/backend/utils/adt/acl.c | 50 |
1 files changed, 40 insertions, 10 deletions
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c index 10729d5e506..dfac1243a40 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -4582,6 +4582,11 @@ pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode) { if (mode & ACL_GRANT_OPTION_FOR(ACL_CREATE)) { + /* + * XXX For roleid == role_oid, is_admin_of_role() also examines the + * session and call stack. That suits two-argument pg_has_role(), but + * it gives the three-argument version a lamentable whimsy. + */ if (is_admin_of_role(roleid, role_oid)) return ACLCHECK_OK; } @@ -4899,11 +4904,9 @@ is_member_of_role_nosuper(Oid member, Oid role) /* - * Is member an admin of role (directly or indirectly)? That is, is it - * a member WITH ADMIN OPTION? - * - * We could cache the result as for is_member_of_role, but currently this - * is not used in any performance-critical paths, so we don't. + * 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? */ bool is_admin_of_role(Oid member, Oid role) @@ -4912,14 +4915,41 @@ is_admin_of_role(Oid member, Oid role) List *roles_list; ListCell *l; - /* Fast path for simple case */ - if (member == role) - return true; - - /* Superusers have every privilege, so are part of every role */ if (superuser_arg(member)) return true; + if (member == role) + /* + * A role can admin itself when it matches the session user and we're + * outside any security-restricted operation, SECURITY DEFINER or + * similar context. SQL-standard roles cannot self-admin. However, + * SQL-standard users are distinct from roles, and they are not + * grantable like roles: PostgreSQL's role-user duality extends the + * standard. Checking for a session user match has the effect of + * letting a role self-admin only when it's conspicuously behaving + * like a user. Note that allowing self-admin under a mere SET ROLE + * would make WITH ADMIN OPTION largely irrelevant; any member could + * SET ROLE to issue the otherwise-forbidden command. + * + * Withholding self-admin in a security-restricted operation prevents + * object owners from harnessing the session user identity during + * administrative maintenance. Suppose Alice owns a database, has + * issued "GRANT alice TO bob", and runs a daily ANALYZE. Bob creates + * an alice-owned SECURITY DEFINER function that issues "REVOKE alice + * FROM carol". If he creates an expression index calling that + * function, Alice will attempt the REVOKE during each ANALYZE. + * Checking InSecurityRestrictedOperation() thwarts that attack. + * + * Withholding self-admin in SECURITY DEFINER functions makes their + * behavior independent of the calling user. There's no security or + * SQL-standard-conformance need for that restriction, though. + * + * A role cannot have actual WITH ADMIN OPTION on itself, because that + * would imply a membership loop. Therefore, we're done either way. + */ + return member == GetSessionUserId() && + !InLocalUserIdChange() && !InSecurityRestrictedOperation(); + /* * Find all the roles that member is a member of, including multi-level * recursion. We build a list in the same way that is_member_of_role does |