1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-28 23:42:10 +03:00

Revoke PUBLIC CREATE from public schema, now owned by pg_database_owner.

This switches the default ACL to what the documentation has recommended
since CVE-2018-1058.  Upgrades will carry forward any old ownership and
ACL.  Sites that declined the 2018 recommendation should take a fresh
look.  Recipes for commissioning a new database cluster from scratch may
need to create a schema, grant more privileges, etc.  Out-of-tree test
suites may require such updates.

Reviewed by Peter Eisentraut.

Discussion: https://postgr.es/m/20201031163518.GB4039133@rfd.leadboat.com
This commit is contained in:
Noah Misch
2021-09-09 23:38:09 -07:00
parent cba79a1632
commit b073c3ccd0
13 changed files with 86 additions and 64 deletions

View File

@ -1633,8 +1633,7 @@ setup_privileges(FILE *cmdfd)
CppAsString2(RELKIND_VIEW) ", " CppAsString2(RELKIND_MATVIEW) ", "
CppAsString2(RELKIND_SEQUENCE) ")"
" AND relacl IS NULL;\n\n",
"GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n",
"GRANT CREATE, USAGE ON SCHEMA public TO PUBLIC;\n\n",
"GRANT USAGE ON SCHEMA pg_catalog, public TO PUBLIC;\n\n",
"REVOKE ALL ON pg_largeobject FROM PUBLIC;\n\n",
"INSERT INTO pg_init_privs "
" (objoid, classoid, objsubid, initprivs, privtype)"

View File

@ -1623,11 +1623,12 @@ selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
* no-mans-land between being a system object and a user object.
* CREATE SCHEMA would fail, so its DUMP_COMPONENT_DEFINITION is just
* a comment and an indication of ownership. If the owner is the
* default, that DUMP_COMPONENT_DEFINITION is superfluous.
* default, omit that superfluous DUMP_COMPONENT_DEFINITION. Before
* v15, the default owner was BOOTSTRAP_SUPERUSERID.
*/
nsinfo->create = false;
nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
if (nsinfo->nspowner == BOOTSTRAP_SUPERUSERID)
if (nsinfo->nspowner == ROLE_PG_DATABASE_OWNER)
nsinfo->dobj.dump &= ~DUMP_COMPONENT_DEFINITION;
nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL;
}
@ -4850,21 +4851,26 @@ getNamespaces(Archive *fout, int *numNamespaces)
PQExpBuffer init_racl_subquery = createPQExpBuffer();
/*
* Bypass pg_init_privs.initprivs for the public schema. Dropping and
* recreating the schema detaches it from its pg_init_privs row, but
* an empty destination database starts with this ACL nonetheless.
* Also, we support dump/reload of public schema ownership changes.
* ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
* initprivs continues to reflect the initial owner (the bootstrap
* superuser). Hence, synthesize the value that nspacl will have
* after the restore's ALTER SCHEMA OWNER.
* Bypass pg_init_privs.initprivs for the public schema, for several
* reasons. First, dropping and recreating the schema detaches it
* from its pg_init_privs row, but an empty destination database
* starts with this ACL nonetheless. Second, we support dump/reload
* of public schema ownership changes. ALTER SCHEMA OWNER filters
* nspacl through aclnewowner(), but initprivs continues to reflect
* the initial owner. Hence, synthesize the value that nspacl will
* have after the restore's ALTER SCHEMA OWNER. Third, this makes the
* destination database match the source's ACL, even if the latter was
* an initdb-default ACL, which changed in v15. An upgrade pulls in
* changes to most system object ACLs that the DBA had not customized.
* We've made the public schema depart from that, because changing its
* ACL so easily breaks applications.
*/
buildACLQueries(acl_subquery, racl_subquery, init_acl_subquery,
init_racl_subquery, "n.nspacl", "n.nspowner",
"CASE WHEN n.nspname = 'public' THEN array["
" format('%s=UC/%s', "
" n.nspowner::regrole, n.nspowner::regrole),"
" format('=UC/%s', n.nspowner::regrole)]::aclitem[] "
" format('=U/%s', n.nspowner::regrole)]::aclitem[] "
"ELSE pip.initprivs END",
"'n'", dopt->binary_upgrade);

View File

@ -628,7 +628,9 @@ my %tests = (
},
'ALTER SCHEMA public OWNER TO' => {
# see test "REVOKE CREATE ON SCHEMA public" for causative create_sql
create_order => 15,
create_sql =>
'ALTER SCHEMA public OWNER TO "regress_quoted \"" role";',
regexp => qr/^ALTER SCHEMA public OWNER TO .+;/m,
like => {
%full_runs, section_pre_data => 1,
@ -3472,17 +3474,12 @@ my %tests = (
unlike => { no_privs => 1, },
},
'REVOKE CREATE ON SCHEMA public FROM public' => {
'REVOKE ALL ON SCHEMA public' => {
create_order => 16,
create_sql => '
REVOKE CREATE ON SCHEMA public FROM public;
ALTER SCHEMA public OWNER TO "regress_quoted \"" role";
REVOKE ALL ON SCHEMA public FROM "regress_quoted \"" role";',
regexp => qr/^
\QREVOKE ALL ON SCHEMA public FROM "regress_quoted \E\\""\ role";
\n\QREVOKE ALL ON SCHEMA public FROM PUBLIC;\E
\n\QGRANT USAGE ON SCHEMA public TO PUBLIC;\E
/xm,
create_sql =>
'REVOKE ALL ON SCHEMA public FROM "regress_quoted \"" role";',
regexp =>
qr/^REVOKE ALL ON SCHEMA public FROM "regress_quoted \\"" role";/m,
like => { %full_runs, section_pre_data => 1, },
unlike => { no_privs => 1, },
},

View File

@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 202109061
#define CATALOG_VERSION_NO 202109101
#endif

View File

@ -21,6 +21,6 @@
# update dumpNamespace() if changing this descr
{ oid => '2200', oid_symbol => 'PG_PUBLIC_NAMESPACE',
descr => 'standard public schema',
nspname => 'public', nspacl => '_null_' },
nspname => 'public', nspowner => 'pg_database_owner', nspacl => '_null_' },
]

View File

@ -25,6 +25,9 @@ CREATE EXTENSION plperl;
CREATE EXTENSION plperlu; -- fail
ERROR: permission denied to create extension "plperlu"
HINT: Must be superuser to create this extension.
CREATE SCHEMA plperl_setup_scratch;
SET search_path = plperl_setup_scratch;
GRANT ALL ON SCHEMA plperl_setup_scratch TO regress_user2;
CREATE FUNCTION foo1() returns int language plperl as '1;';
SELECT foo1();
foo1
@ -34,6 +37,7 @@ SELECT foo1();
-- Must reconnect to avoid failure with non-MULTIPLICITY Perl interpreters
\c -
SET search_path = plperl_setup_scratch;
SET ROLE regress_user1;
-- Should be able to change privileges on the language
revoke all on language plperl from public;

View File

@ -27,12 +27,16 @@ SET ROLE regress_user1;
CREATE EXTENSION plperl;
CREATE EXTENSION plperlu; -- fail
CREATE SCHEMA plperl_setup_scratch;
SET search_path = plperl_setup_scratch;
GRANT ALL ON SCHEMA plperl_setup_scratch TO regress_user2;
CREATE FUNCTION foo1() returns int language plperl as '1;';
SELECT foo1();
-- Must reconnect to avoid failure with non-MULTIPLICITY Perl interpreters
\c -
SET search_path = plperl_setup_scratch;
SET ROLE regress_user1;

View File

@ -388,7 +388,7 @@ CREATE INDEX k ON testschema.tablespace_acl (c) TABLESPACE regress_tblspace;
ALTER TABLE testschema.tablespace_acl OWNER TO regress_tablespace_user2;
SET SESSION ROLE regress_tablespace_user2;
CREATE TABLE tablespace_table (i int) TABLESPACE regress_tblspace; -- fail
CREATE TEMP TABLE tablespace_table (i int) TABLESPACE regress_tblspace; -- fail
ALTER TABLE testschema.tablespace_acl ALTER c TYPE bigint;
REINDEX (TABLESPACE regress_tblspace) TABLE tablespace_table; -- fail
REINDEX (TABLESPACE regress_tblspace, CONCURRENTLY) TABLE tablespace_table; -- fail
@ -409,3 +409,6 @@ DROP SCHEMA testschema CASCADE;
DROP ROLE regress_tablespace_user1;
DROP ROLE regress_tablespace_user2;
-- Rest of this suite can use the public schema freely.
GRANT ALL ON SCHEMA public TO public;

View File

@ -908,7 +908,7 @@ CREATE TABLE testschema.tablespace_acl (c int);
CREATE INDEX k ON testschema.tablespace_acl (c) TABLESPACE regress_tblspace;
ALTER TABLE testschema.tablespace_acl OWNER TO regress_tablespace_user2;
SET SESSION ROLE regress_tablespace_user2;
CREATE TABLE tablespace_table (i int) TABLESPACE regress_tblspace; -- fail
CREATE TEMP TABLE tablespace_table (i int) TABLESPACE regress_tblspace; -- fail
ERROR: permission denied for tablespace regress_tblspace
ALTER TABLE testschema.tablespace_acl ALTER c TYPE bigint;
REINDEX (TABLESPACE regress_tblspace) TABLE tablespace_table; -- fail
@ -934,3 +934,5 @@ drop cascades to table testschema.atable
drop cascades to table testschema.tablespace_acl
DROP ROLE regress_tablespace_user1;
DROP ROLE regress_tablespace_user2;
-- Rest of this suite can use the public schema freely.
GRANT ALL ON SCHEMA public TO public;