summaryrefslogtreecommitdiff
path: root/src/backend/utils
diff options
context:
space:
mode:
authorNoah Misch <noah@leadboat.com>2014-02-17 09:33:31 -0500
committerNoah Misch <noah@leadboat.com>2014-02-17 09:33:38 -0500
commitff35425c8f81541c8dc10486ed21ef1cfdae693e (patch)
tree286fd1a45e596fee3f9d6c6e7ac5e466b39a831c /src/backend/utils
parentbde3307cc16587fce5f6874b11f2d29f776702dc (diff)
downloadpostgresql-ff35425c8f81541c8dc10486ed21ef1cfdae693e.tar.gz
Shore up ADMIN OPTION restrictions.
Granting a role without ADMIN OPTION is supposed to prevent the grantee from adding or removing members from the granted role. Issuing SET ROLE before the GRANT bypassed that, because the role itself had an implicit right to add or remove members. Plug that hole by recognizing that implicit right only when the session user matches the current role. Additionally, do not recognize it during a security-restricted operation or during execution of a SECURITY DEFINER function. The restriction on SECURITY DEFINER is not security-critical. However, it seems best for a user testing his own SECURITY DEFINER function to see the same behavior others will see. Back-patch to 8.4 (all supported versions). The SQL standards do not conflate roles and users as PostgreSQL does; only SQL roles have members, and only SQL users initiate sessions. An application using PostgreSQL users and roles as SQL users and roles will never attempt to grant membership in the role that is the session user, so the implicit right to add or remove members will never arise. The security impact was mostly that a role member could revoke access from others, contrary to the wishes of his own grantor. Unapproved role member additions are less notable, because the member can still largely achieve that by creating a view or a SECURITY DEFINER function. Reviewed by Andres Freund and Tom Lane. Reported, independently, by Jonas Sundman and Noah Misch. Security: CVE-2014-0060
Diffstat (limited to 'src/backend/utils')
-rw-r--r--src/backend/utils/adt/acl.c50
1 files changed, 40 insertions, 10 deletions
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 58167a8e03..5b61c51055 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -3902,6 +3902,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;
}
@@ -4223,11 +4228,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)
@@ -4236,14 +4239,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