From 84f910a7076e09e551bf69e0972473ec15d33c79 Mon Sep 17 00:00:00 2001 From: Itagaki Takahiro Date: Mon, 14 Dec 2009 00:39:11 +0000 Subject: [PATCH] Additional fixes for large object access control. Use pg_largeobject_metadata.oid instead of pg_largeobject.loid to enumerate existing large objects in pg_dump, pg_restore, and contrib modules. --- contrib/lo/lo_test.sql | 6 +++--- contrib/vacuumlo/vacuumlo.c | 7 +++++-- src/bin/pg_dump/pg_backup_archiver.c | 5 ++--- src/bin/pg_dump/pg_backup_archiver.h | 3 ++- src/bin/pg_dump/pg_backup_db.c | 19 ++++++++++++++++++- src/bin/pg_dump/pg_backup_null.c | 5 ++--- src/bin/pg_dump/pg_dump.c | 10 +++++++--- src/test/regress/expected/privileges.out | 10 ++++++++++ src/test/regress/sql/privileges.sql | 7 +++++++ 9 files changed, 56 insertions(+), 16 deletions(-) diff --git a/contrib/lo/lo_test.sql b/contrib/lo/lo_test.sql index e22a8889b3..73022b2535 100644 --- a/contrib/lo/lo_test.sql +++ b/contrib/lo/lo_test.sql @@ -1,4 +1,4 @@ -/* $PostgreSQL: pgsql/contrib/lo/lo_test.sql,v 1.5 2007/11/13 04:24:28 momjian Exp $ */ +/* $PostgreSQL: pgsql/contrib/lo/lo_test.sql,v 1.6 2009/12/14 00:39:10 itagaki Exp $ */ -- Adjust this setting to control where the objects get created. SET search_path = public; @@ -12,7 +12,7 @@ SET search_path = public; -- -- Check what is in pg_largeobject -SELECT count(DISTINCT loid) FROM pg_largeobject; +SELECT count(oid) FROM pg_largeobject_metadata; -- ignore any errors here - simply drop the table if it already exists DROP TABLE a; @@ -74,6 +74,6 @@ DELETE FROM a; DROP TABLE a; -- Check what is in pg_largeobject ... if different from original, trouble -SELECT count(DISTINCT loid) FROM pg_largeobject; +SELECT count(oid) FROM pg_largeobject_metadata; -- end of tests diff --git a/contrib/vacuumlo/vacuumlo.c b/contrib/vacuumlo/vacuumlo.c index a0a7d57922..196688ce00 100644 --- a/contrib/vacuumlo/vacuumlo.c +++ b/contrib/vacuumlo/vacuumlo.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/contrib/vacuumlo/vacuumlo.c,v 1.42 2009/07/13 22:56:30 momjian Exp $ + * $PostgreSQL: pgsql/contrib/vacuumlo/vacuumlo.c,v 1.43 2009/12/14 00:39:10 itagaki Exp $ * *------------------------------------------------------------------------- */ @@ -142,7 +142,10 @@ vacuumlo(char *database, struct _param * param) */ buf[0] = '\0'; strcat(buf, "CREATE TEMP TABLE vacuum_l AS "); - strcat(buf, "SELECT DISTINCT loid AS lo FROM pg_largeobject "); + if (PQserverVersion(conn) >= 80500) + strcat(buf, "SELECT oid AS lo FROM pg_largeobject_metadata"); + else + strcat(buf, "SELECT DISTINCT loid AS lo FROM pg_largeobject"); res = PQexec(conn, buf); if (PQresultStatus(res) != PGRES_COMMAND_OK) { diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c index e15e4dbdb9..54f0bc4b6d 100644 --- a/src/bin/pg_dump/pg_backup_archiver.c +++ b/src/bin/pg_dump/pg_backup_archiver.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.176 2009/10/05 19:24:45 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.177 2009/12/14 00:39:10 itagaki Exp $ * *------------------------------------------------------------------------- */ @@ -914,8 +914,7 @@ StartRestoreBlob(ArchiveHandle *AH, Oid oid, bool drop) ahlog(AH, 2, "restoring large object with OID %u\n", oid); if (drop) - ahprintf(AH, "SELECT CASE WHEN EXISTS(SELECT 1 FROM pg_catalog.pg_largeobject WHERE loid = '%u') THEN pg_catalog.lo_unlink('%u') END;\n", - oid, oid); + DropBlobIfExists(AH, oid); if (AH->connection) { diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h index fa6db407f2..b3eb8b7b10 100644 --- a/src/bin/pg_dump/pg_backup_archiver.h +++ b/src/bin/pg_dump/pg_backup_archiver.h @@ -17,7 +17,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.h,v 1.82 2009/08/07 22:48:34 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.h,v 1.83 2009/12/14 00:39:11 itagaki Exp $ * *------------------------------------------------------------------------- */ @@ -371,6 +371,7 @@ extern void InitArchiveFmt_Tar(ArchiveHandle *AH); extern bool isValidTarHeader(char *header); extern int ReconnectToServer(ArchiveHandle *AH, const char *dbname, const char *newUser); +extern void DropBlobIfExists(ArchiveHandle *AH, Oid oid); int ahwrite(const void *ptr, size_t size, size_t nmemb, ArchiveHandle *AH); int ahprintf(ArchiveHandle *AH, const char *fmt,...) __attribute__((format(printf, 2, 3))); diff --git a/src/bin/pg_dump/pg_backup_db.c b/src/bin/pg_dump/pg_backup_db.c index 836b02cb93..1d72d6dd7e 100644 --- a/src/bin/pg_dump/pg_backup_db.c +++ b/src/bin/pg_dump/pg_backup_db.c @@ -5,7 +5,7 @@ * Implements the basic DB functions used by the archiver. * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_db.c,v 1.84 2009/06/11 14:49:07 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_db.c,v 1.85 2009/12/14 00:39:11 itagaki Exp $ * *------------------------------------------------------------------------- */ @@ -652,6 +652,23 @@ CommitTransaction(ArchiveHandle *AH) ExecuteSqlCommand(AH, "COMMIT", "could not commit database transaction"); } +void +DropBlobIfExists(ArchiveHandle *AH, Oid oid) +{ + /* Call lo_unlink only if exists to avoid not-found error. */ + if (PQserverVersion(AH->connection) >= 80500) + { + ahprintf(AH, "SELECT pg_catalog.lo_unlink(oid) " + "FROM pg_catalog.pg_largeobject_metadata " + "WHERE oid = %u;\n", oid); + } + else + { + ahprintf(AH, "SELECT CASE WHEN EXISTS(SELECT 1 FROM pg_catalog.pg_largeobject WHERE loid = '%u') THEN pg_catalog.lo_unlink('%u') END;\n", + oid, oid); + } +} + static bool _isIdentChar(unsigned char c) { diff --git a/src/bin/pg_dump/pg_backup_null.c b/src/bin/pg_dump/pg_backup_null.c index d9cb02446b..127ff57219 100644 --- a/src/bin/pg_dump/pg_backup_null.c +++ b/src/bin/pg_dump/pg_backup_null.c @@ -17,7 +17,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_null.c,v 1.22 2009/08/04 21:56:09 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_null.c,v 1.23 2009/12/14 00:39:11 itagaki Exp $ * *------------------------------------------------------------------------- */ @@ -151,8 +151,7 @@ _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid) die_horribly(AH, NULL, "invalid OID for large object\n"); if (AH->ropt->dropSchema) - ahprintf(AH, "SELECT CASE WHEN EXISTS(SELECT 1 FROM pg_catalog.pg_largeobject WHERE loid = '%u') THEN pg_catalog.lo_unlink('%u') END;\n", - oid, oid); + DropBlobIfExists(AH, oid); ahprintf(AH, "SELECT pg_catalog.lo_open(pg_catalog.lo_create('%u'), %d);\n", oid, INV_WRITE); diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 7af461bd5a..bb361b3366 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -12,7 +12,7 @@ * by PostgreSQL * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.555 2009/12/11 03:34:56 itagaki Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.556 2009/12/14 00:39:11 itagaki Exp $ * *------------------------------------------------------------------------- */ @@ -1945,7 +1945,9 @@ hasBlobs(Archive *AH) selectSourceSchema("pg_catalog"); /* Check for BLOB OIDs */ - if (AH->remoteVersion >= 70100) + if (AH->remoteVersion >= 80500) + blobQry = "SELECT oid FROM pg_largeobject_metadata LIMIT 1"; + else if (AH->remoteVersion >= 70100) blobQry = "SELECT loid FROM pg_largeobject LIMIT 1"; else blobQry = "SELECT oid FROM pg_class WHERE relkind = 'l' LIMIT 1"; @@ -1981,7 +1983,9 @@ dumpBlobs(Archive *AH, void *arg) selectSourceSchema("pg_catalog"); /* Cursor to get all BLOB OIDs */ - if (AH->remoteVersion >= 70100) + if (AH->remoteVersion >= 80500) + blobQry = "DECLARE bloboid CURSOR FOR SELECT oid FROM pg_largeobject_metadata"; + else if (AH->remoteVersion >= 70100) blobQry = "DECLARE bloboid CURSOR FOR SELECT DISTINCT loid FROM pg_largeobject"; else blobQry = "DECLARE bloboid CURSOR FOR SELECT oid FROM pg_class WHERE relkind = 'l'"; diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out index 4160cba47d..5cda2302fc 100644 --- a/src/test/regress/expected/privileges.out +++ b/src/test/regress/expected/privileges.out @@ -1041,6 +1041,16 @@ SELECT lo_unlink(1002); SELECT lo_export(1001, '/dev/null'); -- to be denied ERROR: must be superuser to use server-side lo_export() HINT: Anyone can use the client-side lo_export() provided by libpq. +-- don't allow unpriv users to access pg_largeobject contents +\c - +SELECT * FROM pg_largeobject LIMIT 0; + loid | pageno | data +------+--------+------ +(0 rows) + +SET SESSION AUTHORIZATION regressuser1; +SELECT * FROM pg_largeobject LIMIT 0; -- to be denied +ERROR: permission denied for relation pg_largeobject -- test default ACLs \c - CREATE SCHEMA testns; diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql index 8e8ff70608..a87ce77aa6 100644 --- a/src/test/regress/sql/privileges.sql +++ b/src/test/regress/sql/privileges.sql @@ -565,6 +565,13 @@ SELECT lo_truncate(lo_open(1002, x'20000'::int), 10); SELECT lo_unlink(1002); SELECT lo_export(1001, '/dev/null'); -- to be denied +-- don't allow unpriv users to access pg_largeobject contents +\c - +SELECT * FROM pg_largeobject LIMIT 0; + +SET SESSION AUTHORIZATION regressuser1; +SELECT * FROM pg_largeobject LIMIT 0; -- to be denied + -- test default ACLs \c -