From 1a6bb6d877f60c377635c4e83a1b75ca2f437951 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Wed, 13 Jun 2001 21:44:41 +0000 Subject: [PATCH] Allow a non-superuser database owner to vacuum all tables in his database, including system catalogs (but not the shared catalogs, since they don't really belong to his database). This is per recent mailing list discussion. Clean up some other code that also checks for database ownerness by introducing a test function is_dbadmin(). --- src/backend/commands/analyze.c | 12 ++++--- src/backend/commands/comment.c | 35 ++++--------------- src/backend/commands/define.c | 8 ++--- src/backend/commands/indexcmds.c | 32 ++++------------- src/backend/commands/proclang.c | 16 +++++---- src/backend/commands/vacuum.c | 13 ++++--- src/backend/libpq/be-fsstubs.c | 4 +-- src/backend/utils/misc/superuser.c | 56 +++++++++++++++++++++++------- src/include/catalog/pg_shadow.h | 6 +--- src/include/miscadmin.h | 8 +++-- 10 files changed, 95 insertions(+), 95 deletions(-) diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 28ec8d648e..c650ff88a1 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/analyze.c,v 1.19 2001/06/06 21:29:17 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/analyze.c,v 1.20 2001/06/13 21:44:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -193,16 +193,18 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt) ReleaseSysCache(tuple); /* - * Open the class, getting only a read lock on it, and check permissions + * Open the class, getting only a read lock on it, and check permissions. + * Permissions check should match vacuum's check! */ onerel = heap_open(relid, AccessShareLock); - if (!pg_ownercheck(GetUserId(), RelationGetRelationName(onerel), - RELNAME)) + if (! (pg_ownercheck(GetUserId(), RelationGetRelationName(onerel), + RELNAME) || + (is_dbadmin(MyDatabaseId) && !onerel->rd_rel->relisshared))) { /* No need for a notice if we already complained during VACUUM */ if (!vacstmt->vacuum) - elog(NOTICE, "Skipping \"%s\" --- only table owner can ANALYZE it", + elog(NOTICE, "Skipping \"%s\" --- only table or database owner can ANALYZE it", RelationGetRelationName(onerel)); heap_close(onerel, NoLock); CommitTransactionCommand(); diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c index 695c7401e7..39668f867e 100644 --- a/src/backend/commands/comment.c +++ b/src/backend/commands/comment.c @@ -4,10 +4,10 @@ * * PostgreSQL object comments utility code. * - * Copyright (c) 1999, PostgreSQL Global Development Group + * Copyright (c) 1999-2001, PostgreSQL Global Development Group * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.29 2001/06/05 19:34:56 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.30 2001/06/13 21:44:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -21,7 +21,6 @@ #include "catalog/pg_database.h" #include "catalog/pg_description.h" #include "catalog/pg_operator.h" -#include "catalog/pg_shadow.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" #include "catalog/pg_class.h" @@ -389,16 +388,11 @@ CommentAttribute(char *relname, char *attrname, char *comment) static void CommentDatabase(char *database, char *comment) { - Relation pg_database; - HeapTuple dbtuple, - usertuple; ScanKeyData entry; HeapScanDesc scan; + HeapTuple dbtuple; Oid oid; - bool superuser; - int32 dba; - Oid userid; /*** First find the tuple in pg_database for the database ***/ @@ -408,33 +402,17 @@ CommentDatabase(char *database, char *comment) scan = heap_beginscan(pg_database, 0, SnapshotNow, 1, &entry); dbtuple = heap_getnext(scan, 0); - /*** Validate database exists, and fetch the dba id and oid ***/ + /*** Validate database exists, and fetch the db oid ***/ if (!HeapTupleIsValid(dbtuple)) elog(ERROR, "database '%s' does not exist", database); - dba = ((Form_pg_database) GETSTRUCT(dbtuple))->datdba; oid = dbtuple->t_data->t_oid; - /*** Now, fetch user information ***/ + /*** Allow if the user matches the database dba or is a superuser ***/ - userid = GetUserId(); - usertuple = SearchSysCache(SHADOWSYSID, - ObjectIdGetDatum(userid), - 0, 0, 0); - if (!HeapTupleIsValid(usertuple)) - elog(ERROR, "invalid user id %u", (unsigned) userid); - superuser = ((Form_pg_shadow) GETSTRUCT(usertuple))->usesuper; - ReleaseSysCache(usertuple); - - /*** Allow if the userid matches the database dba or is a superuser ***/ - -#ifndef NO_SECURITY - if (!(superuser || (userid == dba))) - { + if (!(superuser() || is_dbadmin(oid))) elog(ERROR, "you are not permitted to comment on database '%s'", database); - } -#endif /*** Create the comments with the pg_database oid ***/ @@ -444,7 +422,6 @@ CommentDatabase(char *database, char *comment) heap_endscan(scan); heap_close(pg_database, AccessShareLock); - } /*------------------------------------------------------------------ diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c index 98a6e35147..b5852b6fff 100644 --- a/src/backend/commands/define.c +++ b/src/backend/commands/define.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.55 2001/05/18 21:24:18 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.56 2001/06/13 21:44:40 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -33,21 +33,21 @@ * *------------------------------------------------------------------------- */ +#include "postgres.h" + #include #include -#include "postgres.h" - #include "access/heapam.h" #include "catalog/catname.h" #include "catalog/pg_aggregate.h" #include "catalog/pg_language.h" #include "catalog/pg_operator.h" #include "catalog/pg_proc.h" -#include "catalog/pg_shadow.h" #include "catalog/pg_type.h" #include "commands/defrem.h" #include "fmgr.h" +#include "miscadmin.h" #include "optimizer/cost.h" #include "parser/parse_expr.h" #include "tcop/dest.h" diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 373a68fbf3..108c4ea378 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.49 2001/05/31 18:16:55 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.50 2001/06/13 21:44:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -28,7 +28,6 @@ #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" #include "catalog/pg_proc.h" -#include "catalog/pg_shadow.h" #include "commands/defrem.h" #include "miscadmin.h" #include "optimizer/clauses.h" @@ -712,14 +711,9 @@ ReindexTable(const char *name, bool force) void ReindexDatabase(const char *dbname, bool force, bool all) { - Relation relation, - relationRelation; - HeapTuple dbtuple, - tuple; + Relation relationRelation; HeapScanDesc scan; - int4 db_owner; - Oid db_id; - ScanKeyData scankey; + HeapTuple tuple; MemoryContext private_context; MemoryContext old; int relcnt, @@ -730,24 +724,12 @@ ReindexDatabase(const char *dbname, bool force, bool all) AssertArg(dbname); - relation = heap_openr(DatabaseRelationName, AccessShareLock); - ScanKeyEntryInitialize(&scankey, 0, Anum_pg_database_datname, - F_NAMEEQ, NameGetDatum(dbname)); - scan = heap_beginscan(relation, 0, SnapshotNow, 1, &scankey); - dbtuple = heap_getnext(scan, 0); - if (!HeapTupleIsValid(dbtuple)) - elog(ERROR, "Database \"%s\" does not exist", dbname); - db_id = dbtuple->t_data->t_oid; - db_owner = ((Form_pg_database) GETSTRUCT(dbtuple))->datdba; - heap_endscan(scan); - heap_close(relation, NoLock); - - if (GetUserId() != db_owner && !superuser()) - elog(ERROR, "REINDEX DATABASE: Permission denied."); - - if (db_id != MyDatabaseId) + if (strcmp(dbname, DatabaseName) != 0) elog(ERROR, "REINDEX DATABASE: Can be executed only on the currently open database."); + if (! (superuser() || is_dbadmin(MyDatabaseId))) + elog(ERROR, "REINDEX DATABASE: Permission denied."); + /* * We cannot run inside a user transaction block; if we were inside a * transaction, then our commit- and start-transaction-command calls diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c index 43327c7a65..120c5d8b00 100644 --- a/src/backend/commands/proclang.c +++ b/src/backend/commands/proclang.c @@ -3,20 +3,26 @@ * proclang.c * PostgreSQL PROCEDURAL LANGUAGE support code. * + * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/proclang.c,v 1.28 2001/06/13 21:44:40 tgl Exp $ + * *------------------------------------------------------------------------- */ -#include - #include "postgres.h" +#include + #include "access/heapam.h" #include "catalog/catname.h" #include "catalog/indexing.h" #include "catalog/pg_language.h" #include "catalog/pg_proc.h" -#include "catalog/pg_shadow.h" #include "commands/proclang.h" #include "fmgr.h" +#include "miscadmin.h" #include "utils/builtins.h" #include "utils/syscache.h" @@ -63,10 +69,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) * Check permission */ if (!superuser()) - { elog(ERROR, "Only users with Postgres superuser privilege are " "permitted to create procedural languages"); - } /* * Translate the language name and check that this language doesn't @@ -150,10 +154,8 @@ DropProceduralLanguage(DropPLangStmt *stmt) * Check permission */ if (!superuser()) - { elog(ERROR, "Only users with Postgres superuser privilege are " "permitted to drop procedural languages"); - } /* * Translate the language name, check that this language exist and is diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 3cba039520..33d5409364 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.195 2001/05/25 15:45:32 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.196 2001/06/13 21:44:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -437,15 +437,20 @@ vacuum_rel(Oid relid) /* * Open the class, get an exclusive lock on it, and check permissions. * + * We allow the user to vacuum a table if he is superuser, the table + * owner, or the database owner (but in the latter case, only if it's + * not a shared relation). pg_ownercheck includes the superuser case. + * * Note we choose to treat permissions failure as a NOTICE and keep * trying to vacuum the rest of the DB --- is this appropriate? */ onerel = heap_open(relid, AccessExclusiveLock); - if (!pg_ownercheck(GetUserId(), RelationGetRelationName(onerel), - RELNAME)) + if (! (pg_ownercheck(GetUserId(), RelationGetRelationName(onerel), + RELNAME) || + (is_dbadmin(MyDatabaseId) && !onerel->rd_rel->relisshared))) { - elog(NOTICE, "Skipping \"%s\" --- only table owner can VACUUM it", + elog(NOTICE, "Skipping \"%s\" --- only table or database owner can VACUUM it", RelationGetRelationName(onerel)); heap_close(onerel, AccessExclusiveLock); CommitTransactionCommand(); diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c index 4d50ee1ae1..b0a759bc00 100644 --- a/src/backend/libpq/be-fsstubs.c +++ b/src/backend/libpq/be-fsstubs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/libpq/be-fsstubs.c,v 1.58 2001/03/22 03:59:30 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/be-fsstubs.c,v 1.59 2001/06/13 21:44:41 tgl Exp $ * * NOTES * This should be moved to a more appropriate place. It is here @@ -39,9 +39,9 @@ #include #include -#include "catalog/pg_shadow.h" #include "libpq/be-fsstubs.h" #include "libpq/libpq-fs.h" +#include "miscadmin.h" #include "storage/large_object.h" #include "utils/memutils.h" diff --git a/src/backend/utils/misc/superuser.c b/src/backend/utils/misc/superuser.c index e539d57347..73cfe8cb97 100644 --- a/src/backend/utils/misc/superuser.c +++ b/src/backend/utils/misc/superuser.c @@ -1,35 +1,38 @@ /*------------------------------------------------------------------------- * * superuser.c - * * The superuser() function. Determines if user has superuser privilege. + * Also, a function to check for the owner (datdba) of a database. + * * * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/misc/superuser.c,v 1.17 2001/01/24 19:43:16 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/misc/superuser.c,v 1.18 2001/06/13 21:44:41 tgl Exp $ * - * DESCRIPTION - * See superuser(). *------------------------------------------------------------------------- */ - #include "postgres.h" + +#include "access/heapam.h" +#include "catalog/catname.h" +#include "catalog/pg_database.h" #include "catalog/pg_shadow.h" #include "utils/syscache.h" #include "miscadmin.h" +#include "utils/fmgroids.h" + +/* + * The Postgres user running this command has Postgres superuser privileges + */ bool superuser(void) { -/*-------------------------------------------------------------------------- - The Postgres user running this command has Postgres superuser - privileges. ---------------------------------------------------------------------------*/ + bool result = false; HeapTuple utup; - bool result; utup = SearchSysCache(SHADOWSYSID, ObjectIdGetDatum(GetUserId()), @@ -38,7 +41,36 @@ superuser(void) { result = ((Form_pg_shadow) GETSTRUCT(utup))->usesuper; ReleaseSysCache(utup); - return result; } - return false; + return result; +} + +/* + * The Postgres user running this command is the owner of the specified + * database. + */ +bool +is_dbadmin(Oid dbid) +{ + Relation pg_database; + ScanKeyData entry[1]; + HeapScanDesc scan; + HeapTuple dbtuple; + int32 dba; + + /* There's no syscache for pg_database, so must look the hard way */ + pg_database = heap_openr(DatabaseRelationName, AccessShareLock); + ScanKeyEntryInitialize(&entry[0], 0x0, + ObjectIdAttributeNumber, F_OIDEQ, + ObjectIdGetDatum(dbid)); + scan = heap_beginscan(pg_database, 0, SnapshotNow, 1, entry); + dbtuple = heap_getnext(scan, 0); + if (!HeapTupleIsValid(dbtuple)) + elog(ERROR, "database %u does not exist", dbid); + dba = ((Form_pg_database) GETSTRUCT(dbtuple))->datdba; + heap_endscan(scan); + heap_close(pg_database, AccessShareLock); + + /* XXX some confusion about whether userids are OID or int4 ... */ + return (GetUserId() == (Oid) dba); } diff --git a/src/include/catalog/pg_shadow.h b/src/include/catalog/pg_shadow.h index e0b087a9d9..fdec3fa82c 100644 --- a/src/include/catalog/pg_shadow.h +++ b/src/include/catalog/pg_shadow.h @@ -9,7 +9,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_shadow.h,v 1.11 2001/03/09 22:10:13 tgl Exp $ + * $Id: pg_shadow.h,v 1.12 2001/06/13 21:44:41 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -24,10 +24,6 @@ #define PG_SHADOW_H -/* Prototype required for superuser() from superuser.c */ - -bool superuser(void); - /* ---------------- * pg_shadow definition. cpp turns this into * typedef struct FormData_pg_shadow diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 742f58137c..588c2f99de 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -12,7 +12,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: miscadmin.h,v 1.85 2001/05/12 01:48:49 petere Exp $ + * $Id: miscadmin.h,v 1.86 2001/06/13 21:44:41 tgl Exp $ * * NOTES * some of the information in this file should be moved to @@ -220,9 +220,13 @@ extern int CheckPathAccess(char *path, char *name, int open_mode); extern void GetCharSetByHost(char *TableName, int host, const char *DataDir); extern void SetCharSet(void); extern char *convertstr(unsigned char *buff, int len, int dest); - #endif +/* in utils/misc/superuser.c */ +extern bool superuser(void); /* current user is superuser */ +extern bool is_dbadmin(Oid dbid); /* current user is owner of database */ + + /***************************************************************************** * pmod.h -- * * POSTGRES processing mode definitions. *