From b0e55c4db4ec642a4a7ddc6dfc15591744d1d96e Mon Sep 17 00:00:00 2001 From: Andreas Lind Date: Wed, 19 Jun 2024 14:51:30 +0200 Subject: [PATCH v1 1/4] Add a BYPASSLEAKPROOF role attribute --- doc/src/sgml/catalogs.sgml | 10 ++++ doc/src/sgml/ref/alter_role.sgml | 9 ++-- doc/src/sgml/ref/createuser.sgml | 20 ++++++++ doc/src/sgml/system-views.sgml | 31 +++++++++++++ src/backend/catalog/aclchk.c | 19 ++++++++ src/backend/catalog/system_views.sql | 3 ++ src/backend/commands/user.c | 40 +++++++++++++++- src/backend/parser/gram.y | 4 ++ src/bin/pg_dump/pg_dumpall.c | 23 ++++++++++ src/bin/psql/describe.c | 17 +++++++ src/bin/scripts/createuser.c | 18 +++++++- src/bin/scripts/t/040_createuser.pl | 32 ++++++++----- src/include/catalog/pg_authid.dat | 68 ++++++++++++++-------------- src/include/catalog/pg_authid.h | 1 + src/include/utils/acl.h | 1 + 15 files changed, 245 insertions(+), 51 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index cbd4e40a320..162430ee85d 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1567,6 +1567,16 @@ + + + rolbypassleakproof bool + + + Role bypasses LEAKPROOF requirement for functions evaluated in + security sensitive contexts. + + + rolconnlimit int4 diff --git a/doc/src/sgml/ref/alter_role.sgml b/doc/src/sgml/ref/alter_role.sgml index 7b0a04bc463..259e3927e33 100644 --- a/doc/src/sgml/ref/alter_role.sgml +++ b/doc/src/sgml/ref/alter_role.sgml @@ -32,6 +32,7 @@ ALTER ROLE role_specification [ WIT | LOGIN | NOLOGIN | REPLICATION | NOREPLICATION | BYPASSRLS | NOBYPASSRLS + | BYPASSLEAKPROOF | NOBYPASSLEAKPROOF | CONNECTION LIMIT connlimit | [ ENCRYPTED ] PASSWORD 'password' | PASSWORD NULL | VALID UNTIL 'timestamp' @@ -77,9 +78,9 @@ ALTER ROLE { role_specification | A non-replication roles for which they have been granted ADMIN OPTION. Non-superusers cannot change the SUPERUSER property and can change the - CREATEDB, REPLICATION, and - BYPASSRLS properties only if they possess the - corresponding property themselves. + CREATEDB, REPLICATION, + BYPASSRLS, and BYPASSLEAKPROOF properties + only if they possess the corresponding property themselves. Ordinary roles can only change their own password. @@ -178,6 +179,8 @@ ALTER ROLE { role_specification | A NOREPLICATION BYPASSRLS NOBYPASSRLS + BYPASSLEAKPROOF + NOBYPASSLEAKPROOF CONNECTION LIMIT connlimit [ ENCRYPTED ] PASSWORD 'password' PASSWORD NULL diff --git a/doc/src/sgml/ref/createuser.sgml b/doc/src/sgml/ref/createuser.sgml index 5c34c623423..af26f1daf72 100644 --- a/doc/src/sgml/ref/createuser.sgml +++ b/doc/src/sgml/ref/createuser.sgml @@ -330,6 +330,26 @@ PostgreSQL documentation + + + + + The new user will bypass the LEAKPROOF requirement for functions evaluated + in security sensitive contexts. + + + + + + + + + The new user will not bypass the LEAKPROOF requirement for functions evaluated + in security sensitive contexts. + + + + diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml index b58c52ea50f..b67c657c0c7 100644 --- a/doc/src/sgml/system-views.sgml +++ b/doc/src/sgml/system-views.sgml @@ -3105,6 +3105,17 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx + + + + rolbypassleakproof bool + + + Role bypasses the LEAKPROOF requirement for functions evaluated in + security sensitive contexts. + + + rolconfig text[] @@ -3927,6 +3938,16 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx + + + usebypassleakproof bool + + + Role bypasses the LEAKPROOF requirement for functions evaluated in + security sensitive contexts. + + + passwd text @@ -5194,6 +5215,16 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx + + + usebypassleakproof bool + + + Role bypasses the LEAKPROOF requirement for functions evaluated in + security sensitive contexts. + + + passwd text diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index 9ca8a88dc91..f0d5ff17b18 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -4188,6 +4188,25 @@ has_bypassrls_privilege(Oid roleid) return result; } +bool +has_bypassleakproof_privilege(Oid roleid) +{ + bool result = false; + HeapTuple utup; + + /* Superusers bypass all permission checking. */ + if (superuser_arg(roleid)) + return true; + + utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid)); + if (HeapTupleIsValid(utup)) + { + result = ((Form_pg_authid) GETSTRUCT(utup))->rolbypassleakproof; + ReleaseSysCache(utup); + } + return result; +} + /* * Fetch pg_default_acl entry for given role, namespace and object type * (object type must be given in pg_default_acl's encoding). diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 15efb02badb..8b981595907 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -27,6 +27,7 @@ CREATE VIEW pg_roles AS '********'::text as rolpassword, rolvaliduntil, rolbypassrls, + rolbypassleakproof, setconfig as rolconfig, pg_authid.oid FROM pg_authid LEFT JOIN pg_db_role_setting s @@ -40,6 +41,7 @@ CREATE VIEW pg_shadow AS rolsuper AS usesuper, rolreplication AS userepl, rolbypassrls AS usebypassrls, + rolbypassleakproof AS usebypassleakproof, rolpassword AS passwd, rolvaliduntil AS valuntil, setconfig AS useconfig @@ -65,6 +67,7 @@ CREATE VIEW pg_user AS usesuper, userepl, usebypassrls, + usebypassleakproof, '********'::text as passwd, valuntil, useconfig diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index 0d638e29d00..efffec6fc0f 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -149,6 +149,8 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt) bool canlogin = false; /* Can this user login? */ bool isreplication = false; /* Is this a replication role? */ bool bypassrls = false; /* Is this a row security enabled role? */ + bool bypassleakproof = false; /* Does it bypass leakproof + * checks? */ int connlimit = -1; /* maximum connections allowed */ List *addroleto = NIL; /* roles to make this a member of */ List *rolemembers = NIL; /* roles to be members of this role */ @@ -169,6 +171,7 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt) DefElem *dadminmembers = NULL; DefElem *dvalidUntil = NULL; DefElem *dbypassRLS = NULL; + DefElem *dbypassLeakproof = NULL; GrantRoleOptions popt; /* The defaults can vary depending on the original statement type */ @@ -272,6 +275,12 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt) errorConflictingDefElem(defel, pstate); dbypassRLS = defel; } + else if (strcmp(defel->defname, "bypassleakproof") == 0) + { + if (dbypassLeakproof) + errorConflictingDefElem(defel, pstate); + dbypassLeakproof = defel; + } else elog(ERROR, "option \"%s\" not recognized", defel->defname); @@ -309,6 +318,8 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt) validUntil = strVal(dvalidUntil->arg); if (dbypassRLS) bypassrls = boolVal(dbypassRLS->arg); + if (dbypassLeakproof) + bypassleakproof = boolVal(dbypassLeakproof->arg); /* Check some permissions first */ if (!superuser_arg(currentUserId)) @@ -343,6 +354,13 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt) errmsg("permission denied to create role"), errdetail("Only roles with the %s attribute may create roles with the %s attribute.", "BYPASSRLS", "BYPASSRLS"))); + + if (bypassleakproof && !has_bypassleakproof_privilege(currentUserId)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to create role"), + errdetail("Only roles with the %s attribute may create roles with the %s attribute.", + "BYPASSLEAKPROOF", "BYPASSLEAKPROOF"))); } /* @@ -456,6 +474,7 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt) new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null; new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(bypassrls); + new_record[Anum_pg_authid_rolbypassleakproof - 1] = BoolGetDatum(bypassleakproof); /* * pg_largeobject_metadata contains pg_authid.oid's, so we use the @@ -644,6 +663,7 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt) DefElem *drolemembers = NULL; DefElem *dvalidUntil = NULL; DefElem *dbypassRLS = NULL; + DefElem *dbypassLeakproof = NULL; Oid roleid; Oid currentUserId = GetUserId(); GrantRoleOptions popt; @@ -723,6 +743,12 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt) errorConflictingDefElem(defel, pstate); dbypassRLS = defel; } + else if (strcmp(defel->defname, "bypassleakproof") == 0) + { + if (dbypassLeakproof) + errorConflictingDefElem(defel, pstate); + dbypassLeakproof = defel; + } else elog(ERROR, "option \"%s\" not recognized", defel->defname); @@ -775,7 +801,7 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt) { /* things an unprivileged user certainly can't do */ if (dinherit || dcreaterole || dcreatedb || dcanlogin || dconnlimit || - dvalidUntil || disreplication || dbypassRLS) + dvalidUntil || disreplication || dbypassRLS || dbypassLeakproof) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to alter role"), @@ -815,6 +841,12 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt) errmsg("permission denied to alter role"), errdetail("Only roles with the %s attribute may change the %s attribute.", "BYPASSRLS", "BYPASSRLS"))); + if (dbypassLeakproof && !has_bypassleakproof_privilege(currentUserId)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to alter role"), + errdetail("Only roles with the %s attribute may change the %s attribute.", + "BYPASSLEAKPROOF", "BYPASSLEAKPROOF"))); } /* To add or drop members, you need ADMIN OPTION. */ @@ -952,6 +984,12 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt) new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(boolVal(dbypassRLS->arg)); new_record_repl[Anum_pg_authid_rolbypassrls - 1] = true; } + if (dbypassLeakproof) + { + new_record[Anum_pg_authid_rolbypassleakproof - 1] = BoolGetDatum(boolVal(dbypassLeakproof->arg)); + new_record_repl[Anum_pg_authid_rolbypassleakproof - 1] = true; + } + new_tuple = heap_modify_tuple(tuple, pg_authid_dsc, new_record, new_record_nulls, new_record_repl); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 3c4268b271a..94412edd1f6 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -1272,6 +1272,10 @@ AlterOptRoleElem: $$ = makeDefElem("bypassrls", (Node *) makeBoolean(true), @1); else if (strcmp($1, "nobypassrls") == 0) $$ = makeDefElem("bypassrls", (Node *) makeBoolean(false), @1); + else if (strcmp($1, "bypassleakproof") == 0) + $$ = makeDefElem("bypassleakproof", (Node *) makeBoolean(true), @1); + else if (strcmp($1, "nobypassleakproof") == 0) + $$ = makeDefElem("bypassleakproof", (Node *) makeBoolean(false), @1); else if (strcmp($1, "noinherit") == 0) { /* diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index 946a6d0fafc..d84f47a881a 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -837,6 +837,7 @@ dumpRoles(PGconn *conn) i_rolvaliduntil, i_rolreplication, i_rolbypassrls, + i_rolbypassleakproof, i_rolcomment, i_is_current_user; int i; @@ -845,6 +846,22 @@ dumpRoles(PGconn *conn) * Notes: rolconfig is dumped later, and pg_authid must be used for * extracting rolcomment regardless of role_catalog. */ + if (server_version >= 99999) + + /* + * FIXME: When it has been assigned, set the right server_version + * where rolbypassleakproof is available + */ + printfPQExpBuffer(buf, + "SELECT oid, rolname, rolsuper, rolinherit, " + "rolcreaterole, rolcreatedb, " + "rolcanlogin, rolconnlimit, rolpassword, " + "rolvaliduntil, rolreplication, rolbypassrls, rolbypassleakproof, " + "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, " + "rolname = current_user AS is_current_user " + "FROM %s " + "WHERE rolname !~ '^pg_' " + "ORDER BY 2", role_catalog); if (server_version >= 90600) printfPQExpBuffer(buf, "SELECT oid, rolname, rolsuper, rolinherit, " @@ -892,6 +909,7 @@ dumpRoles(PGconn *conn) i_rolvaliduntil = PQfnumber(res, "rolvaliduntil"); i_rolreplication = PQfnumber(res, "rolreplication"); i_rolbypassrls = PQfnumber(res, "rolbypassrls"); + i_rolbypassleakproof = PQfnumber(res, "rolbypassleakproof"); i_rolcomment = PQfnumber(res, "rolcomment"); i_is_current_user = PQfnumber(res, "is_current_user"); @@ -971,6 +989,11 @@ dumpRoles(PGconn *conn) else appendPQExpBufferStr(buf, " NOBYPASSRLS"); + if (strcmp(PQgetvalue(res, i, i_rolbypassleakproof), "t") == 0) + appendPQExpBufferStr(buf, " BYPASSLEAKPROOF"); + else + appendPQExpBufferStr(buf, " NOBYPASSLEAKPROOF"); + if (strcmp(PQgetvalue(res, i, i_rolconnlimit), "-1") != 0) appendPQExpBuffer(buf, " CONNECTION LIMIT %s", PQgetvalue(res, i, i_rolconnlimit)); diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 1d08268393e..fc5a00f3e79 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -3742,6 +3742,15 @@ describeRoles(const char *pattern, bool verbose, bool showSystem) appendPQExpBufferStr(&buf, "\n, r.rolbypassrls"); } + if (pset.sversion >= 99999) + { + /* + * FIXME: When it has been assigned, set the right server_version + * where rolbypassleakproof is available + */ + appendPQExpBufferStr(&buf, "\n, r.rolbypassleakproof"); + } + appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n"); if (!showSystem && !pattern) @@ -3799,6 +3808,14 @@ describeRoles(const char *pattern, bool verbose, bool showSystem) if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0) add_role_attribute(&buf, _("Bypass RLS")); + /* + * FIXME: When it has been assigned, set the right server_version + * where rolbypassleakproof is available + */ + if (pset.sversion >= 99999) + if (strcmp(PQgetvalue(res, i, (verbose ? 11 : 10)), "t") == 0) + add_role_attribute(&buf, _("Bypass LEAKPROOF")); + conns = atoi(PQgetvalue(res, i, 6)); if (conns >= 0) { diff --git a/src/bin/scripts/createuser.c b/src/bin/scripts/createuser.c index 81e6abfc46e..4c1246aa181 100644 --- a/src/bin/scripts/createuser.c +++ b/src/bin/scripts/createuser.c @@ -57,6 +57,8 @@ main(int argc, char *argv[]) {"interactive", no_argument, NULL, 3}, {"bypassrls", no_argument, NULL, 4}, {"no-bypassrls", no_argument, NULL, 5}, + {"bypassleakproof", no_argument, NULL, 6}, + {"no-bypassleakproof", no_argument, NULL, 7}, {NULL, 0, NULL, 0} }; @@ -86,7 +88,8 @@ main(int argc, char *argv[]) inherit = TRI_DEFAULT, login = TRI_DEFAULT, replication = TRI_DEFAULT, - bypassrls = TRI_DEFAULT; + bypassrls = TRI_DEFAULT, + bypassleakproof = TRI_DEFAULT; PQExpBufferData sql; @@ -190,6 +193,12 @@ main(int argc, char *argv[]) case 5: bypassrls = TRI_NO; break; + case 6: + bypassleakproof = TRI_YES; + break; + case 7: + bypassleakproof = TRI_NO; + break; default: /* getopt_long already emitted a complaint */ pg_log_error_hint("Try \"%s --help\" for more information.", progname); @@ -341,6 +350,10 @@ main(int argc, char *argv[]) appendPQExpBufferStr(&sql, " BYPASSRLS"); if (bypassrls == TRI_NO) appendPQExpBufferStr(&sql, " NOBYPASSRLS"); + if (bypassleakproof == TRI_YES) + appendPQExpBufferStr(&sql, " BYPASSLEAKPROOF"); + if (bypassleakproof == TRI_NO) + appendPQExpBufferStr(&sql, " NOBYPASSLEAKPROOF"); if (conn_limit >= -1) appendPQExpBuffer(&sql, " CONNECTION LIMIT %d", conn_limit); if (pwexpiry != NULL) @@ -444,6 +457,9 @@ help(const char *progname) printf(_(" --bypassrls role can bypass row-level security (RLS) policy\n")); printf(_(" --no-bypassrls role cannot bypass row-level security (RLS) policy\n" " (default)\n")); + printf(_(" --bypassleakproof role can bypass leakproof security requirements\n")); + printf(_(" --no-bypassleakproof role cannot bypass leakproof security requirements\n" + " (default)\n")); printf(_(" --replication role can initiate replication\n")); printf(_(" --no-replication role cannot initiate replication (default)\n")); printf(_(" -?, --help show this help, then exit\n")); diff --git a/src/bin/scripts/t/040_createuser.pl b/src/bin/scripts/t/040_createuser.pl index 54af43401bb..495a19c099a 100644 --- a/src/bin/scripts/t/040_createuser.pl +++ b/src/bin/scripts/t/040_createuser.pl @@ -18,19 +18,19 @@ $node->start; $node->issues_sql_like( [ 'createuser', 'regress_user1' ], - qr/statement: CREATE ROLE regress_user1 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS;/, + qr/statement: CREATE ROLE regress_user1 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS NOBYPASSLEAKPROOF;/, 'SQL CREATE USER run'); $node->issues_sql_like( [ 'createuser', '--no-login', 'regress_role1' ], - qr/statement: CREATE ROLE regress_role1 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT NOLOGIN NOREPLICATION NOBYPASSRLS;/, + qr/statement: CREATE ROLE regress_role1 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT NOLOGIN NOREPLICATION NOBYPASSRLS NOBYPASSLEAKPROOF;/, 'create a non-login role'); $node->issues_sql_like( [ 'createuser', '--createrole', 'regress user2' ], - qr/statement: CREATE ROLE "regress user2" NOSUPERUSER NOCREATEDB CREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS;/, + qr/statement: CREATE ROLE "regress user2" NOSUPERUSER NOCREATEDB CREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS NOBYPASSLEAKPROOF;/, 'create a CREATEROLE user'); $node->issues_sql_like( [ 'createuser', '--superuser', 'regress_user3' ], - qr/statement: CREATE ROLE regress_user3 SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS;/, + qr/statement: CREATE ROLE regress_user3 SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS NOBYPASSLEAKPROOF;/, 'create a superuser'); $node->issues_sql_like( [ @@ -39,7 +39,7 @@ $node->issues_sql_like( '--with-admin' => 'regress user2', 'regress user #4' ], - qr/statement: CREATE ROLE "regress user #4" NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS ADMIN regress_user1,"regress user2";/, + qr/statement: CREATE ROLE "regress user #4" NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS NOBYPASSLEAKPROOF ADMIN regress_user1,"regress user2";/, 'add a role as a member with admin option of the newly created role'); $node->issues_sql_like( [ @@ -48,11 +48,11 @@ $node->issues_sql_like( '--with-member' => 'regress_user3', '--with-member' => 'regress user #4' ], - qr/statement: CREATE ROLE "REGRESS_USER5" NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS ROLE regress_user3,"regress user #4";/, + qr/statement: CREATE ROLE "REGRESS_USER5" NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS NOBYPASSLEAKPROOF ROLE regress_user3,"regress user #4";/, 'add a role as a member of the newly created role'); $node->issues_sql_like( [ 'createuser', '--valid-until' => '2029 12 31', 'regress_user6' ], - qr/statement: CREATE ROLE regress_user6 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS VALID UNTIL \'2029 12 31\';/, + qr/statement: CREATE ROLE regress_user6 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS NOBYPASSLEAKPROOF VALID UNTIL \'2029 12 31\';/, 'create a role with a password expiration date'); $node->issues_sql_like( [ 'createuser', '--bypassrls', 'regress_user7' ], @@ -60,24 +60,32 @@ $node->issues_sql_like( 'create a BYPASSRLS role'); $node->issues_sql_like( [ 'createuser', '--no-bypassrls', 'regress_user8' ], - qr/statement: CREATE ROLE regress_user8 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS;/, + qr/statement: CREATE ROLE regress_user8 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS NOBYPASSLEAKPROOF;/, 'create a role without BYPASSRLS'); $node->issues_sql_like( [ 'createuser', '--with-admin' => 'regress_user1', 'regress_user9' ], - qr/statement: CREATE ROLE regress_user9 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS ADMIN regress_user1;/, + qr/statement: CREATE ROLE regress_user9 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS NOBYPASSLEAKPROOF ADMIN regress_user1;/, '--with-admin'); $node->issues_sql_like( [ 'createuser', '--with-member' => 'regress_user1', 'regress_user10' ], - qr/statement: CREATE ROLE regress_user10 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS ROLE regress_user1;/, + qr/statement: CREATE ROLE regress_user10 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS NOBYPASSLEAKPROOF ROLE regress_user1;/, '--with-member'); $node->issues_sql_like( [ 'createuser', '--role' => 'regress_user1', 'regress_user11' ], - qr/statement: CREATE ROLE regress_user11 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS IN ROLE regress_user1;/, + qr/statement: CREATE ROLE regress_user11 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS NOBYPASSLEAKPROOF IN ROLE regress_user1;/, '--role'); $node->issues_sql_like( [ 'createuser', 'regress_user12', '--member-of' => 'regress_user1' ], - qr/statement: CREATE ROLE regress_user12 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS IN ROLE regress_user1;/, + qr/statement: CREATE ROLE regress_user12 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS NOBYPASSLEAKPROOF IN ROLE regress_user1;/, '--member-of'); +$node->issues_sql_like( + [ 'createuser', '--bypassleakproof', 'regress_user13' ], + qr/statement: CREATE ROLE regress_user7 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS BYPASSLEAKPROOF;/, + 'create a BYPASSLEAKPROOF role'); +$node->issues_sql_like( + [ 'createuser', '--no-bypassleakproof', 'regress_user14' ], + qr/statement: CREATE ROLE regress_user8 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS NOBYPASSLEAKPROOF;/, + 'create a role without BYPASSLEAKPROOF'); $node->command_fails([ 'createuser', 'regress_user1' ], 'fails if role already exists'); diff --git a/src/include/catalog/pg_authid.dat b/src/include/catalog/pg_authid.dat index eb4dab5c6aa..7461e6e625a 100644 --- a/src/include/catalog/pg_authid.dat +++ b/src/include/catalog/pg_authid.dat @@ -22,87 +22,87 @@ { oid => '10', oid_symbol => 'BOOTSTRAP_SUPERUSERID', rolname => 'POSTGRES', rolsuper => 't', rolinherit => 't', rolcreaterole => 't', rolcreatedb => 't', rolcanlogin => 't', - rolreplication => 't', rolbypassrls => 't', rolconnlimit => '-1', - rolpassword => '_null_', rolvaliduntil => '_null_' }, + rolreplication => 't', rolbypassrls => 't', rolbypassleakproof => 't', + rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '6171', oid_symbol => 'ROLE_PG_DATABASE_OWNER', rolname => 'pg_database_owner', rolsuper => 'f', rolinherit => 't', rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', - rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', - rolpassword => '_null_', rolvaliduntil => '_null_' }, + rolreplication => 'f', rolbypassrls => 'f', rolbypassleakproof => 'f', + rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '6181', oid_symbol => 'ROLE_PG_READ_ALL_DATA', rolname => 'pg_read_all_data', rolsuper => 'f', rolinherit => 't', rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', - rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', - rolpassword => '_null_', rolvaliduntil => '_null_' }, + rolreplication => 'f', rolbypassrls => 'f', rolbypassleakproof => 'f', + rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '6182', oid_symbol => 'ROLE_PG_WRITE_ALL_DATA', rolname => 'pg_write_all_data', rolsuper => 'f', rolinherit => 't', rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', - rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', - rolpassword => '_null_', rolvaliduntil => '_null_' }, + rolreplication => 'f', rolbypassrls => 'f', rolbypassleakproof => 'f', + rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '3373', oid_symbol => 'ROLE_PG_MONITOR', rolname => 'pg_monitor', rolsuper => 'f', rolinherit => 't', rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', - rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', - rolpassword => '_null_', rolvaliduntil => '_null_' }, + rolreplication => 'f', rolbypassrls => 'f', rolbypassleakproof => 'f', + rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '3374', oid_symbol => 'ROLE_PG_READ_ALL_SETTINGS', rolname => 'pg_read_all_settings', rolsuper => 'f', rolinherit => 't', rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', - rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', - rolpassword => '_null_', rolvaliduntil => '_null_' }, + rolreplication => 'f', rolbypassrls => 'f', rolbypassleakproof => 'f', + rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '3375', oid_symbol => 'ROLE_PG_READ_ALL_STATS', rolname => 'pg_read_all_stats', rolsuper => 'f', rolinherit => 't', rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', - rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', - rolpassword => '_null_', rolvaliduntil => '_null_' }, + rolreplication => 'f', rolbypassrls => 'f', rolbypassleakproof => 'f', + rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '3377', oid_symbol => 'ROLE_PG_STAT_SCAN_TABLES', rolname => 'pg_stat_scan_tables', rolsuper => 'f', rolinherit => 't', rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', - rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', - rolpassword => '_null_', rolvaliduntil => '_null_' }, + rolreplication => 'f', rolbypassrls => 'f', rolbypassleakproof => 'f', + rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '4569', oid_symbol => 'ROLE_PG_READ_SERVER_FILES', rolname => 'pg_read_server_files', rolsuper => 'f', rolinherit => 't', rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', - rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', - rolpassword => '_null_', rolvaliduntil => '_null_' }, + rolreplication => 'f', rolbypassrls => 'f', rolbypassleakproof => 'f', + rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '4570', oid_symbol => 'ROLE_PG_WRITE_SERVER_FILES', rolname => 'pg_write_server_files', rolsuper => 'f', rolinherit => 't', rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', - rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', - rolpassword => '_null_', rolvaliduntil => '_null_' }, + rolreplication => 'f', rolbypassrls => 'f', rolbypassleakproof => 'f', + rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '4571', oid_symbol => 'ROLE_PG_EXECUTE_SERVER_PROGRAM', rolname => 'pg_execute_server_program', rolsuper => 'f', rolinherit => 't', rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', - rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', - rolpassword => '_null_', rolvaliduntil => '_null_' }, + rolreplication => 'f', rolbypassrls => 'f', rolbypassleakproof => 'f', + rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '4200', oid_symbol => 'ROLE_PG_SIGNAL_BACKEND', rolname => 'pg_signal_backend', rolsuper => 'f', rolinherit => 't', rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', - rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', - rolpassword => '_null_', rolvaliduntil => '_null_' }, + rolreplication => 'f', rolbypassrls => 'f', rolbypassleakproof => 'f', + rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '4544', oid_symbol => 'ROLE_PG_CHECKPOINT', rolname => 'pg_checkpoint', rolsuper => 'f', rolinherit => 't', rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', - rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', - rolpassword => '_null_', rolvaliduntil => '_null_' }, + rolreplication => 'f', rolbypassrls => 'f', rolbypassleakproof => 'f', + rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '6337', oid_symbol => 'ROLE_PG_MAINTAIN', rolname => 'pg_maintain', rolsuper => 'f', rolinherit => 't', rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', - rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', - rolpassword => '_null_', rolvaliduntil => '_null_' }, + rolreplication => 'f', rolbypassrls => 'f', rolbypassleakproof => 'f', + rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '4550', oid_symbol => 'ROLE_PG_USE_RESERVED_CONNECTIONS', rolname => 'pg_use_reserved_connections', rolsuper => 'f', rolinherit => 't', rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', - rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', - rolpassword => '_null_', rolvaliduntil => '_null_' }, + rolreplication => 'f', rolbypassrls => 'f', rolbypassleakproof => 'f', + rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '6304', oid_symbol => 'ROLE_PG_CREATE_SUBSCRIPTION', rolname => 'pg_create_subscription', rolsuper => 'f', rolinherit => 't', rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', - rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', - rolpassword => '_null_', rolvaliduntil => '_null_' }, + rolreplication => 'f', rolbypassrls => 'f', rolbypassleakproof => 'f', + rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, { oid => '8916', oid_symbol => 'ROLE_PG_SIGNAL_AUTOVACUUM_WORKER', rolname => 'pg_signal_autovacuum_worker', rolsuper => 'f', rolinherit => 't', rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', - rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', - rolpassword => '_null_', rolvaliduntil => '_null_' }, + rolreplication => 'f', rolbypassrls => 'f', rolbypassleakproof => 'f', + rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, ] diff --git a/src/include/catalog/pg_authid.h b/src/include/catalog/pg_authid.h index b2f3e9d01ee..e565ef88745 100644 --- a/src/include/catalog/pg_authid.h +++ b/src/include/catalog/pg_authid.h @@ -39,6 +39,7 @@ CATALOG(pg_authid,1260,AuthIdRelationId) BKI_SHARED_RELATION BKI_ROWTYPE_OID(284 bool rolcanlogin; /* allowed to log in as session user? */ bool rolreplication; /* role used for streaming replication */ bool rolbypassrls; /* bypasses row-level security? */ + bool rolbypassleakproof; /* bypasses leakproof checks? */ int32 rolconnlimit; /* max connections allowed (-1=no limit) */ /* remaining fields may be null; use heap_getattr to read them! */ diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index 01ae5b719fd..68f5a4a76e6 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -286,5 +286,6 @@ extern void RemoveRoleFromInitPriv(Oid roleid, extern bool object_ownercheck(Oid classid, Oid objectid, Oid roleid); extern bool has_createrole_privilege(Oid roleid); extern bool has_bypassrls_privilege(Oid roleid); +extern bool has_bypassleakproof_privilege(Oid roleid); #endif /* ACL_H */ -- 2.39.2