Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1naPQ4-0006cA-Va for pgsql-hackers@arkaria.postgresql.org; Fri, 01 Apr 2022 22:07:01 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1naPQ1-0004OE-Rv for pgsql-hackers@arkaria.postgresql.org; Fri, 01 Apr 2022 22:06:57 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1naPQ1-0004O5-Ej for pgsql-hackers@lists.postgresql.org; Fri, 01 Apr 2022 22:06:57 +0000 Received: from mail-io1-xd2c.google.com ([2607:f8b0:4864:20::d2c]) by magus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1naPPy-00054h-C5 for pgsql-hackers@postgresql.org; Fri, 01 Apr 2022 22:06:57 +0000 Received: by mail-io1-xd2c.google.com with SMTP id p22so4798499iod.2 for ; Fri, 01 Apr 2022 15:06:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=date:from:to:subject:message-id:mime-version:content-disposition; bh=9VsWeu3HXbUs2Gzd89yCJxl0zFpzf+fPU6lP5CWu+Cc=; b=pEwB4DoKw8Daln+3MeTkYYs5nTIT7xxDBsSalaLFgUielwMYM+cjvt6zxWxYVaZLy7 ft746wh5BAv8LDv6GR0BvVJvzCH/GwJSXEZbgj1cSXtFTJdi1f6SYpvVWSM49ept/RJS UB5nkHLd0iIgbW7WP1BzEaN00a97u0fPrV2+tE9q4sWQcsRQgu/5SGggB02vIUWrIaSX v5kAg6GmB+S849/LyaF1CngFRMinQURgPlAnXdwYDynUtzqq9Ys8kuUJyHvVrEfHaSkd QyxYbd00GNm1E8fXDZjhzV1hft7FpwktKojN27aMiFt+59Eq+qscvh2yioL49EEuzIuU QDaw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:from:to:subject:message-id:mime-version :content-disposition; bh=9VsWeu3HXbUs2Gzd89yCJxl0zFpzf+fPU6lP5CWu+Cc=; b=TeIYwB3o+JTWQVV2LwooQAW2rGaceuA/Gu9q8Z/7/QvyPkF9fAQggEyLb9KsBfFEDi oqHc1c1MlaIdVkLJBBPYrJBGD4Lujf+LkWm4yrRSQRY9PntDRKpFVhgXKbZQm3N9vA7V 1ezZw9pUNpnCu53bst37RSZTNTqkGcZdq2lG/Pt6RQ03xrb6bJaB2Uf9v4NGm0Jc4v4D 9TpSki4rdKzpGZHF8w6/xgUr9y1igDoSXey4LaEK49+OXN2I2gT69zzGzIHVy0rWAIla 797qIlwqX8iX6M71h6krqz9F1kyv0J1N3CtKPFw0HyWpCW8nKuyr46gHCr4q7X2LWHU0 PpKg== X-Gm-Message-State: AOAM532EH4YKSQlg8o05MlKzmuUJ7a7ityYFtaPfWLOoWJbFHGW29A/d iwbfEpIMQ9q3atyS6RB5YMHp7I+AHV4= X-Google-Smtp-Source: ABdhPJzfQj0mhEkyBtmaHpOisF7VF+bb4rYmgqTLzl3lH7zXXgWxCWpXxXtKgwdIhykd+7NFrSzS5A== X-Received: by 2002:a05:6638:14d0:b0:323:c1de:b77a with SMTP id l16-20020a05663814d000b00323c1deb77amr1574593jak.303.1648850810919; Fri, 01 Apr 2022 15:06:50 -0700 (PDT) Received: from nathanxps13 ([2600:1700:48f8:340f:b598:9c46:c716:d0d2]) by smtp.gmail.com with ESMTPSA id u11-20020a056e02080b00b002c87d5830cbsm1935625ilm.40.2022.04.01.15.06.49 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 01 Apr 2022 15:06:50 -0700 (PDT) Date: Fri, 1 Apr 2022 15:06:48 -0700 From: Nathan Bossart To: pgsql-hackers@postgresql.org Subject: use has_privs_of_role() for pg_hba.conf Message-ID: <20220401220648.GA615994@nathanxps13> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="a8Wt8u1KmwUX3Y2C" Content-Disposition: inline List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --a8Wt8u1KmwUX3Y2C Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Hi hackers, 6198420 ensured that has_privs_of_role() is used for predefined roles, which means that the role inheritance hierarchy is checked instead of mere role membership. However, inheritance is still not respected for pg_hba.conf. Specifically, "samerole", "samegroup", and "+" still use is_member_of_role_nosuper(). The attached patch introduces has_privs_of_role_nosuper() and uses it for the aforementioned pg_hba.conf functionality. I think this is desirable for consistency. If a role_a has membership in role_b but none of its privileges (i.e., NOINHERIT), does it make sense that role_a should match +role_b in pg_hba.conf? It is true that role_a could always "SET ROLE role_b", and with this change, the user won't even have the ability to log in to run SET ROLE. But I'm not sure if that's a strong enough argument for deviating from the standard role privilege checks. Thoughts? -- Nathan Bossart Amazon Web Services: https://aws.amazon.com --a8Wt8u1KmwUX3Y2C Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v1-0001-Use-has_privs_of_role-for-samerole-samegroup-and-.patch" From 5c1a5aaaff949fc25afaa6856ca2a85a54c8efdc Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Fri, 1 Apr 2022 11:40:51 -0700 Subject: [PATCH v1 1/1] Use has_privs_of_role for samerole, samegroup, and + in pg_hba.conf. 6198420 ensured that has_privs_of_role() is used for predefined roles, which means that the role privilege inheritance hierarchy is checked instead of mere role membership. However, inheritance is still not respected for pg_hba.conf. This change alters the authentication logic to consider role privileges instead of just role membership. Do not back-patch. Author: Nathan Bossart --- doc/src/sgml/client-auth.sgml | 18 +++++++++--------- src/backend/libpq/hba.c | 19 ++++++++++--------- src/backend/utils/adt/acl.c | 23 +++++++++++++++++++++++ src/include/utils/acl.h | 1 + 4 files changed, 43 insertions(+), 18 deletions(-) diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml index 142b0affcb..5c63842e52 100644 --- a/doc/src/sgml/client-auth.sgml +++ b/doc/src/sgml/client-auth.sgml @@ -221,12 +221,12 @@ hostnogssenc database usersameuser specifies that the record matches if the requested database has the same name as the requested user. The value samerole specifies that - the requested user must be a member of the role with the same + the requested user must have privileges of the role with the same name as the requested database. (samegroup is an obsolete but still accepted spelling of samerole.) - Superusers are not considered to be members of a role for the - purposes of samerole unless they are explicitly - members of the role, directly or indirectly, and not just by + Superusers are not considered to have privileges of a role for the + purposes of samerole unless they explicitly have + privileges of the role, directly or indirectly, and not just by virtue of being a superuser. The value replication specifies that the record matches if a physical replication connection is requested, however, it @@ -252,10 +252,10 @@ hostnogssenc database user+. (Recall that there is no real distinction between users and groups in PostgreSQL; a + mark really means - match any of the roles that are directly or indirectly members + match any of the roles that directly or indirectly have privileges of this role, while a name without a + mark matches only that specific role.) For this purpose, a superuser is only - considered to be a member of a role if they are explicitly a member + considered to have privileges of a role if they explicitly have privileges of the role, directly or indirectly, and not just by virtue of being a superuser. Multiple user names can be supplied by separating them with commas. @@ -788,9 +788,9 @@ host all all 192.168.0.0/16 ident map=omicro # If these are the only three lines for local connections, they will # allow local users to connect only to their own databases (databases # with the same name as their database user name) except for administrators -# and members of role "support", who can connect to all databases. The file -# $PGDATA/admins contains a list of names of administrators. Passwords -# are required in all cases. +# and roles with privileges of role "support", who can connect to all +# databases. The file $PGDATA/admins contains a list of names of +# administrators. Passwords are required in all cases. # # TYPE DATABASE USER ADDRESS METHOD local sameuser all md5 diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index f8393ca8ed..247118fa30 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -546,13 +546,13 @@ tokenize_auth_file(const char *filename, FILE *file, List **tok_lines, /* - * Does user belong to role? + * Does user have privileges of role? * * userid is the OID of the role given as the attempted login identifier. - * We check to see if it is a member of the specified role name. + * We check to see if it has privileges of the specified role name. */ static bool -is_member(Oid userid, const char *role) +has_privs(Oid userid, const char *role) { Oid roleid; @@ -565,11 +565,12 @@ is_member(Oid userid, const char *role) return false; /* if target role not exist, say "no" */ /* - * See if user is directly or indirectly a member of role. For this - * purpose, a superuser is not considered to be automatically a member of - * the role, so group auth only applies to explicit membership. + * See if user directly or indirectly has privileges of role. For this + * purpose, a superuser is not considered to automatically have + * privileges of the role, so group auth only applies to explicit + * privileges. */ - return is_member_of_role_nosuper(userid, roleid); + return has_privs_of_role_nosuper(userid, roleid); } /* @@ -586,7 +587,7 @@ check_role(const char *role, Oid roleid, List *tokens) tok = lfirst(cell); if (!tok->quoted && tok->string[0] == '+') { - if (is_member(roleid, tok->string + 1)) + if (has_privs(roleid, tok->string + 1)) return true; } else if (token_matches(tok, role) || @@ -627,7 +628,7 @@ check_db(const char *dbname, const char *role, Oid roleid, List *tokens) else if (token_is_keyword(tok, "samegroup") || token_is_keyword(tok, "samerole")) { - if (is_member(roleid, dbname)) + if (has_privs(roleid, dbname)) return true; } else if (token_is_keyword(tok, "replication")) diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c index 83cf7ac9ff..061fa171fc 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -4855,6 +4855,29 @@ has_privs_of_role(Oid member, Oid role) } +/* + * Does member have the privileges of role, not considering superuserness? + * + * This is identical to has_privs_of_role except we ignore superuser + * status. + */ +bool +has_privs_of_role_nosuper(Oid member, Oid role) +{ + /* Fast path for simple case */ + if (member == role) + return true; + + /* + * Find all the roles that member has the privileges of, including + * multi-level recursion, then see if target role is any one of them. + */ + return list_member_oid(roles_is_member_of(member, ROLERECURSE_PRIVS, + InvalidOid, NULL), + role); +} + + /* * Is member a member of role (directly or indirectly)? * diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index 91ce3d8e9c..bf9f22124e 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -208,6 +208,7 @@ extern AclMode aclmask(const Acl *acl, Oid roleid, Oid ownerId, extern int aclmembers(const Acl *acl, Oid **roleids); extern bool has_privs_of_role(Oid member, Oid role); +extern bool has_privs_of_role_nosuper(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); -- 2.25.1 --a8Wt8u1KmwUX3Y2C--