Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 2699fc0

Browse files
committed
Run REFRESH MATERIALIZED VIEW CONCURRENTLY in right security context
The internal commands in REFRESH MATERIALIZED VIEW CONCURRENTLY are correctly executed in SECURITY_RESTRICTED_OPERATION mode, except for creating the temporary "diff" table, because you cannot create temporary tables in SRO mode. But creating the temporary "diff" table is a pretty complex CTAS command that selects from another temporary table created earlier in the command. If you can cajole that CTAS command to execute code defined by the table owner, the table owner can run code with the privileges of the user running the REFRESH command. The proof-of-concept reported to the security team relied on CREATE RULE to convert the internally-built temp table to a view. That's not possible since commit b23cd18, and I was not able to find a different way to turn the SELECT on the temp table into code execution, so as far as I know this is only exploitable in v15 and below. That's a fiddly assumption though, so apply this patch to master and all stable versions. Thanks to Pedro Gallegos for the report. Security: CVE-2023-5869 Reviewed-by: Noah Misch
1 parent 0551142 commit 2699fc0

File tree

1 file changed

+26
-7
lines changed

1 file changed

+26
-7
lines changed

src/backend/commands/matview.c

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -647,13 +647,35 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner,
647647
SPI_getvalue(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1))));
648648
}
649649

650+
/*
651+
* Create the temporary "diff" table.
652+
*
653+
* Temporarily switch out of the SECURITY_RESTRICTED_OPERATION context,
654+
* because you cannot create temp tables in SRO context. For extra
655+
* paranoia, add the composite type column only after switching back to
656+
* SRO context.
657+
*/
650658
SetUserIdAndSecContext(relowner,
651659
save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
660+
resetStringInfo(&querybuf);
661+
appendStringInfo(&querybuf,
662+
"CREATE TEMP TABLE %s (tid pg_catalog.tid)",
663+
diffname);
664+
if (SPI_exec(querybuf.data, 0) != SPI_OK_UTILITY)
665+
elog(ERROR, "SPI_exec failed: %s", querybuf.data);
666+
SetUserIdAndSecContext(relowner,
667+
save_sec_context | SECURITY_RESTRICTED_OPERATION);
668+
resetStringInfo(&querybuf);
669+
appendStringInfo(&querybuf,
670+
"ALTER TABLE %s ADD COLUMN newdata %s",
671+
diffname, tempname);
672+
if (SPI_exec(querybuf.data, 0) != SPI_OK_UTILITY)
673+
elog(ERROR, "SPI_exec failed: %s", querybuf.data);
652674

653-
/* Start building the query for creating the diff table. */
675+
/* Start building the query for populating the diff table. */
654676
resetStringInfo(&querybuf);
655677
appendStringInfo(&querybuf,
656-
"CREATE TEMP TABLE %s AS "
678+
"INSERT INTO %s "
657679
"SELECT mv.ctid AS tid, newdata.*::%s AS newdata "
658680
"FROM %s mv FULL JOIN %s newdata ON (",
659681
diffname, tempname, matviewname, tempname);
@@ -782,13 +804,10 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner,
782804
"WHERE newdata.* IS NULL OR mv.* IS NULL "
783805
"ORDER BY tid");
784806

785-
/* Create the temporary "diff" table. */
786-
if (SPI_exec(querybuf.data, 0) != SPI_OK_UTILITY)
807+
/* Populate the temporary "diff" table. */
808+
if (SPI_exec(querybuf.data, 0) != SPI_OK_INSERT)
787809
elog(ERROR, "SPI_exec failed: %s", querybuf.data);
788810

789-
SetUserIdAndSecContext(relowner,
790-
save_sec_context | SECURITY_RESTRICTED_OPERATION);
791-
792811
/*
793812
* We have no further use for data from the "full-data" temp table, but we
794813
* must keep it around because its type is referenced from the diff table.

0 commit comments

Comments
 (0)