diff options
| author | Noah Misch <noah@leadboat.com> | 2020-11-09 07:32:09 -0800 |
|---|---|---|
| committer | Noah Misch <noah@leadboat.com> | 2020-11-09 07:32:09 -0800 |
| commit | 0c3185e963d9f9dd0608214f7d732b84aa0888fe (patch) | |
| tree | e12049825b26d0190b69ae3dad84391a11542f79 /src/test | |
| parent | 8f113698b63b15a4e0a4b15d3ee37238c1d1821d (diff) | |
In security-restricted operations, block enqueue of at-commit user code.
Specifically, this blocks DECLARE ... WITH HOLD and firing of deferred
triggers within index expressions and materialized view queries. An
attacker having permission to create non-temp objects in at least one
schema could execute arbitrary SQL functions under the identity of the
bootstrap superuser. One can work around the vulnerability by disabling
autovacuum and not manually running ANALYZE, CLUSTER, REINDEX, CREATE
INDEX, VACUUM FULL, or REFRESH MATERIALIZED VIEW. (Don't restore from
pg_dump, since it runs some of those commands.) Plain VACUUM (without
FULL) is safe, and all commands are fine when a trusted user owns the
target object. Performance may degrade quickly under this workaround,
however. Back-patch to 9.5 (all supported versions).
Reviewed by Robert Haas. Reported by Etienne Stalmans.
Security: CVE-2020-25695
Diffstat (limited to 'src/test')
| -rw-r--r-- | src/test/regress/expected/privileges.out | 42 | ||||
| -rw-r--r-- | src/test/regress/sql/privileges.sql | 34 |
2 files changed, 76 insertions, 0 deletions
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out index 3ec22c20ea1..0a2dd37ac0b 100644 --- a/src/test/regress/expected/privileges.out +++ b/src/test/regress/expected/privileges.out @@ -1319,6 +1319,48 @@ SELECT has_table_privilege('regress_priv_user1', 'atest4', 'SELECT WITH GRANT OP t (1 row) +-- security-restricted operations +\c - +CREATE ROLE regress_sro_user; +SET SESSION AUTHORIZATION regress_sro_user; +CREATE FUNCTION unwanted_grant() RETURNS void LANGUAGE sql AS + 'GRANT regress_priv_group2 TO regress_sro_user'; +CREATE FUNCTION mv_action() RETURNS bool LANGUAGE sql AS + 'DECLARE c CURSOR WITH HOLD FOR SELECT unwanted_grant(); SELECT true'; +-- REFRESH of this MV will queue a GRANT at end of transaction +CREATE MATERIALIZED VIEW sro_mv AS SELECT mv_action() WITH NO DATA; +REFRESH MATERIALIZED VIEW sro_mv; +ERROR: cannot create a cursor WITH HOLD within security-restricted operation +CONTEXT: SQL function "mv_action" statement 1 +\c - +REFRESH MATERIALIZED VIEW sro_mv; +ERROR: cannot create a cursor WITH HOLD within security-restricted operation +CONTEXT: SQL function "mv_action" statement 1 +SET SESSION AUTHORIZATION regress_sro_user; +-- INSERT to this table will queue a GRANT at end of transaction +CREATE TABLE sro_trojan_table (); +CREATE FUNCTION sro_trojan() RETURNS trigger LANGUAGE plpgsql AS + 'BEGIN PERFORM unwanted_grant(); RETURN NULL; END'; +CREATE CONSTRAINT TRIGGER t AFTER INSERT ON sro_trojan_table + INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE sro_trojan(); +-- Now, REFRESH will issue such an INSERT, queueing the GRANT +CREATE OR REPLACE FUNCTION mv_action() RETURNS bool LANGUAGE sql AS + 'INSERT INTO sro_trojan_table DEFAULT VALUES; SELECT true'; +REFRESH MATERIALIZED VIEW sro_mv; +ERROR: cannot fire deferred trigger within security-restricted operation +CONTEXT: SQL function "mv_action" statement 1 +\c - +REFRESH MATERIALIZED VIEW sro_mv; +ERROR: cannot fire deferred trigger within security-restricted operation +CONTEXT: SQL function "mv_action" statement 1 +BEGIN; SET CONSTRAINTS ALL IMMEDIATE; REFRESH MATERIALIZED VIEW sro_mv; COMMIT; +ERROR: must have admin option on role "regress_priv_group2" +CONTEXT: SQL function "unwanted_grant" statement 1 +SQL statement "SELECT unwanted_grant()" +PL/pgSQL function sro_trojan() line 1 at PERFORM +SQL function "mv_action" statement 1 +DROP OWNED BY regress_sro_user; +DROP ROLE regress_sro_user; -- Admin options SET SESSION AUTHORIZATION regress_priv_user4; CREATE FUNCTION dogrant_ok() RETURNS void LANGUAGE sql SECURITY DEFINER AS diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql index 3550f61587a..e0c1a29c069 100644 --- a/src/test/regress/sql/privileges.sql +++ b/src/test/regress/sql/privileges.sql @@ -802,6 +802,40 @@ SELECT has_table_privilege('regress_priv_user3', 'atest4', 'SELECT'); -- false SELECT has_table_privilege('regress_priv_user1', 'atest4', 'SELECT WITH GRANT OPTION'); -- true +-- security-restricted operations +\c - +CREATE ROLE regress_sro_user; + +SET SESSION AUTHORIZATION regress_sro_user; +CREATE FUNCTION unwanted_grant() RETURNS void LANGUAGE sql AS + 'GRANT regress_priv_group2 TO regress_sro_user'; +CREATE FUNCTION mv_action() RETURNS bool LANGUAGE sql AS + 'DECLARE c CURSOR WITH HOLD FOR SELECT unwanted_grant(); SELECT true'; +-- REFRESH of this MV will queue a GRANT at end of transaction +CREATE MATERIALIZED VIEW sro_mv AS SELECT mv_action() WITH NO DATA; +REFRESH MATERIALIZED VIEW sro_mv; +\c - +REFRESH MATERIALIZED VIEW sro_mv; + +SET SESSION AUTHORIZATION regress_sro_user; +-- INSERT to this table will queue a GRANT at end of transaction +CREATE TABLE sro_trojan_table (); +CREATE FUNCTION sro_trojan() RETURNS trigger LANGUAGE plpgsql AS + 'BEGIN PERFORM unwanted_grant(); RETURN NULL; END'; +CREATE CONSTRAINT TRIGGER t AFTER INSERT ON sro_trojan_table + INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE sro_trojan(); +-- Now, REFRESH will issue such an INSERT, queueing the GRANT +CREATE OR REPLACE FUNCTION mv_action() RETURNS bool LANGUAGE sql AS + 'INSERT INTO sro_trojan_table DEFAULT VALUES; SELECT true'; +REFRESH MATERIALIZED VIEW sro_mv; +\c - +REFRESH MATERIALIZED VIEW sro_mv; +BEGIN; SET CONSTRAINTS ALL IMMEDIATE; REFRESH MATERIALIZED VIEW sro_mv; COMMIT; + +DROP OWNED BY regress_sro_user; +DROP ROLE regress_sro_user; + + -- Admin options SET SESSION AUTHORIZATION regress_priv_user4; |
