Re: [HACKERS] VACUUM as a denial-of-service attack - Mailing list pgsql-hackers
From | Keith Parks |
---|---|
Subject | Re: [HACKERS] VACUUM as a denial-of-service attack |
Date | |
Msg-id | 199911232218.WAA07783@mtcc.demon.co.uk Whole thread Raw |
Responses |
Re: [HACKERS] VACUUM as a denial-of-service attack
|
List | pgsql-hackers |
>From: Tom Lane <tgl@sss.pgh.pa.us> >I think a reasonable answer to this is to restrict VACUUM on any >table to be allowed only to the table owner and Postgres superuser. >Does anyone have an objection or better idea? In the dim and distant past I produced a patch that put vacuum into the list of things that you could GRANT on a per-table basis. I don't know what effort it would take to rework that for current or if it would be worth it. I think your suggestion above would be perfect if you never need to allow anyone else to vacuum a table. I'va attached the old patch below. Keith. *** backend/parser/gram.y.orig Thu May 22 18:16:19 1997 --- backend/parser/gram.y Thu May 22 18:18:22 1997 *************** *** 646,656 **** privileges: ALL PRIVILEGES { ! $$ = aclmakepriv("rwaR",0); } | ALL { ! $$ = aclmakepriv("rwaR",0); } | operation_commalist { $$ = $1; --- 646,656 ---- privileges: ALL PRIVILEGES { ! $$ = aclmakepriv("rwaRv",0); } | ALL { ! $$ = aclmakepriv("rwaRv",0); } | operation_commalist { $$ = $1; *************** *** 681,686 **** --- 681,689 ---- } | RULE { $$ = ACL_MODE_RU_CHR; + } + | VACUUM { + $$ = ACL_MODE_VA_CHR; } ; *** backend/tcop/aclchk.c.orig Thu May 22 18:18:43 1997 --- backend/tcop/aclchk.c Thu May 22 18:25:07 1997 *************** *** 32,37 **** --- 32,38 ---- #include "catalog/pg_aggregate.h" #include "catalog/pg_proc.h" #include "catalog/pg_user.h" + #include "catalog/pg_database.h" #include "utils/syscache.h" #include "parser/catalog_utils.h" #include "fmgr.h" *************** *** 62,68 **** "No error.", "Permission denied.", "Table does not exist.", ! "Must be table owner." }; #ifdef ACLDEBUG_TRACE --- 63,70 ---- "No error.", "Permission denied.", "Table does not exist.", ! "Must be table owner.", ! "Must be database dba." }; #ifdef ACLDEBUG_TRACE *************** *** 514,525 **** --- 516,537 ---- NAMEDATALEN, value); owner_id = ((TypeTupleForm) GETSTRUCT(htp))->typowner; break; + case DATABASEN: + if (!HeapTupleIsValid(htp)) + elog(WARN, "pg_ownercheck: database \"%-.*s\" not found", + NAMEDATALEN, value); + owner_id = ((Form_pg_database) GETSTRUCT(htp))->datdba; + break; default: elog(WARN, "pg_ownercheck: invalid cache id: %d", cacheid); break; } + #ifdef ACLDEBUG_TRACE + elog(DEBUG, "pg_ownercheck: user = %d owner = %d", user_id, owner_id); + #endif + return(user_id == owner_id); } *** backend/tcop/utility.c.orig Thu May 22 18:25:23 1997 --- backend/tcop/utility.c Thu May 22 18:33:04 1997 *************** *** 614,625 **** --- 614,644 ---- break; case T_VacuumStmt: + { + int aclcheck_result; + char* dbasename; + VacuumStmt *stmt = (VacuumStmt *)parsetree; + + #ifndef NO_SECURITY + relname = stmt->vacrel; + if (relname != NULL) { + aclcheck_result = pg_aclcheck(relname, userName, ACL_VA); + if(aclcheck_result != ACLCHECK_OK) + elog(WARN, "%s: %s", relname, aclcheck_error_strings[aclcheck_result]); + } else { /* Need to check we own the DB for a full vacuum */ + dbasename = GetDatabaseName(); + if (!pg_ownercheck(userName, dbasename, DATABASEN)) + elog(WARN, "%s: %s", dbasename, aclcheck_error_strings[ACLCHECK_NOT_DBA]); + } + #endif + commandTag = "VACUUM"; CHECK_IF_ABORTED(); vacuum( ((VacuumStmt *) parsetree)->vacrel, ((VacuumStmt*) parsetree)->verbose, ((VacuumStmt *) parsetree)->analyze, ((VacuumStmt *) parsetree)->va_spec); + } break; case T_ExplainStmt: *** backend/utils/adt/acl.c.orig Sat May 10 21:25:18 1997 --- backend/utils/adt/acl.c Sun May 11 22:27:58 1997 *************** *** 66,72 **** /* * aclparse * Consumes and parses an ACL specification of the form: ! * [group|user] [A-Za-z0-9]*[+-=][rwaR]* * from string 's', ignoring any leading white space or white space * between the optional id type keyword (group|user) and the actual * ACL specification. --- 66,72 ---- /* * aclparse * Consumes and parses an ACL specification of the form: ! * [group|user] [A-Za-z0-9]*[+-=][rwaRv]* * from string 's', ignoring any leading white space or white space * between the optional id type keyword (group|user) and the actual * ACL specification. *************** *** 123,128 **** --- 123,129 ---- case ACL_MODE_RD_CHR: aip->ai_mode |= ACL_RD; break; case ACL_MODE_WR_CHR: aip->ai_mode |= ACL_WR;break; case ACL_MODE_RU_CHR: aip->ai_mode |= ACL_RU; break; + case ACL_MODE_VA_CHR: aip->ai_mode |= ACL_VA; break; default: elog(WARN, "aclparse: mode flags must use \"%s\"", ACL_MODE_STR); } *************** *** 517,524 **** int i; int l; ! Assert(strlen(old_privlist)<5); ! priv = malloc(5); /* at most "rwaR" */; if (old_privlist == NULL || old_privlist[0] == '\0') { priv[0] = new_priv; --- 518,525 ---- int i; int l; ! Assert(strlen(old_privlist)<(N_ACL_MODES+1)); ! priv = malloc(N_ACL_MODES+1); /* at most N_ACL_MODES */; if (old_privlist == NULL || old_privlist[0] == '\0'){ priv[0] = new_priv; *************** *** 530,536 **** l = strlen(old_privlist); ! if (l == 4) { /* can't add any more privileges */ return priv; } --- 531,537 ---- l = strlen(old_privlist); ! if (l == N_ACL_MODES) { /* can't add any more privileges */ return priv; } *** backend/utils/cache/syscache.c.orig Sun May 11 12:59:32 1997 --- backend/utils/cache/syscache.c Sun May 11 20:23:39 1997 *************** *** 45,50 **** --- 45,51 ---- #include "catalog/pg_operator.h" #include "catalog/pg_proc.h" #include "catalog/pg_class.h" + #include "catalog/pg_database.h" #include "catalog/pg_type.h" #include "catalog/pg_rewrite.h" #include "catalog/pg_aggregate.h" *************** *** 315,320 **** --- 316,330 ---- 0, 0 }, sizeof(FormData_pg_opclass), + NULL, + (ScanFunc) NULL }, + { DatabaseRelationName, /* DATABASEN */ + 1, + { Anum_pg_database_datname, + 0, + 0, + 0 }, + sizeof(FormData_pg_database), NULL, (ScanFunc) NULL } }; *** include/utils/acl.h.orig Thu May 22 18:33:30 1997 --- include/utils/acl.h Thu May 22 18:43:16 1997 *************** *** 52,58 **** #define ACL_RD (1<<1) /* read */ #define ACL_WR (1<<2) /* write (append/delete/replace)*/ #define ACL_RU (1<<3) /* place rules */ ! #define N_ACL_MODES 4 #define ACL_MODECHG_ADD 1 #define ACL_MODECHG_DEL 2 --- 52,59 ---- #define ACL_RD (1<<1) /* read */ #define ACL_WR (1<<2) /* write (append/delete/replace)*/ #define ACL_RU (1<<3) /* place rules */ ! #define ACL_VA (1<<4) /* vacuum */ ! #define N_ACL_MODES 5 #define ACL_MODECHG_ADD 1 #define ACL_MODECHG_DEL 2 *************** *** 61,67 **** /* change this line if you want to set the default acl permission */ #define ACL_WORLD_DEFAULT (ACL_RD)/* #define ACL_WORLD_DEFAULT (ACL_RD|ACL_WR|ACL_AP|ACL_RU) */ ! #define ACL_OWNER_DEFAULT (ACL_RD|ACL_WR|ACL_AP|ACL_RU) /* * AclItem --- 62,68 ---- /* change this line if you want to set the default acl permission */ #define ACL_WORLD_DEFAULT (ACL_RD)/* #define ACL_WORLD_DEFAULT (ACL_RD|ACL_WR|ACL_AP|ACL_RU) */ ! #define ACL_OWNER_DEFAULT (ACL_RD|ACL_WR|ACL_AP|ACL_RU|ACL_VA) /* * AclItem *************** *** 105,121 **** #define ACL_MODECHG_ADD_CHR '+' #define ACL_MODECHG_DEL_CHR '-' #define ACL_MODECHG_EQL_CHR '=' ! #define ACL_MODE_STR "arwR" /* list of valid characters */ #define ACL_MODE_AP_CHR 'a' #define ACL_MODE_RD_CHR 'r' #define ACL_MODE_WR_CHR 'w' #define ACL_MODE_RU_CHR 'R' /* result codesfor pg_aclcheck */ #define ACLCHECK_OK 0 #define ACLCHECK_NO_PRIV 1 #define ACLCHECK_NO_CLASS 2 #define ACLCHECK_NOT_OWNER 3 /* warning messages. set these in aclchk.c. */ extern char*aclcheck_error_strings[]; --- 106,124 ---- #define ACL_MODECHG_ADD_CHR '+' #define ACL_MODECHG_DEL_CHR '-' #define ACL_MODECHG_EQL_CHR '=' ! #define ACL_MODE_STR "arwRv" /* list of valid characters */ #define ACL_MODE_AP_CHR 'a' #define ACL_MODE_RD_CHR 'r' #define ACL_MODE_WR_CHR 'w' #define ACL_MODE_RU_CHR 'R' + #define ACL_MODE_VA_CHR 'v' /* If you add a mode don't forget to change N_ACL_MODES */ /* result codes for pg_aclcheck*/ #define ACLCHECK_OK 0 #define ACLCHECK_NO_PRIV 1 #define ACLCHECK_NO_CLASS 2#define ACLCHECK_NOT_OWNER 3 + #define ACLCHECK_NOT_DBA 4 /* warning messages. set these in aclchk.c. */ extern char *aclcheck_error_strings[]; *** include/utils/syscache.h.orig Sun May 11 13:19:12 1997 --- include/utils/syscache.h Sun May 11 13:20:42 1997 *************** *** 59,64 **** --- 59,65 ---- #define REWRITENAME 25 #define PROSRC 26 #define CLADEFTYPE 27 + #define DATABASEN 28 /* ---------------- * struct cachedesc: information needed for a call to InitSysCache() *** bin/psql/psqlHelp.h.orig Thu May 22 18:43:41 1997 --- bin/psql/psqlHelp.h Fri May 23 15:32:48 1997 *************** *** 136,142 **** "fetch [forward|backward] [<number>|all] [in <cursorname>];"}, { "grant", "grant access controlto a user or group", ! "grant <privilege[,privilege,...]> on <rel1>[,...<reln>] to \n[public | group <group> | <username>]\n\t privilegeis {ALL | SELECT | INSERT | UPDATE | DELETE | RULE}"}, { "insert", "insert tuples", "insert into <class_name>[(<attr1>...<attrN>)]\n\t[values (<expr1>...<exprN>); |\n\tselect <expr1>,...<exprN> [from <from_clause>] [where<qual>];"}, --- 136,142 ---- "fetch [forward|backward] [<number>|all] [in <cursorname>];"}, { "grant", "grant access controlto a user or group", ! "grant <privilege[,privilege,...]> on <rel1>[,...<reln>] to \n[public | group <group> | <username>]\n\t privilegeis {ALL | SELECT | INSERT | UPDATE | DELETE | RULE | VACUUM}"}, { "insert", "insert tuples", "insertinto <class_name> [(<attr1>...<attrN>)]\n\t[values (<expr1>...<exprN>); |\n\tselect <expr1>,...<exprN> [from <from_clause>][where <qual>];"}, *************** *** 157,163 **** "reset {DateStyle | GEQO}"}, { "revoke", "revoke access control from a user or group", ! "revoke <privilege[,privilege,...]> on <rel1>[,...<reln>] from \n[public | group <group> | <username>]\n\t privilegeis {ALL | SELECT | INSERT | UPDATE | DELETE | RULE}"}, { "rollback", "abort a transaction", "rollback[transaction|work]"}, --- 157,163 ---- "reset {DateStyle | GEQO}"}, { "revoke", "revoke access control from a user or group", ! "revoke <privilege[,privilege,...]> on <rel1>[,...<reln>] from \n[public | group <group> | <username>]\n\t privilegeis {ALL | SELECT | INSERT | UPDATE | DELETE | RULE | VACUUM}"}, { "rollback", "abort a transaction", "rollback [transaction|work]"}, *** man/grant.l.orig Mon May 12 08:18:55 1997 --- man/grant.l Mon May 12 08:20:15 1997 *************** *** 10,16 **** \fBon\fR <rel1>[,...<reln>] \fBto\fR [\fBpublic\fR | group <group> | <username>] ! \fBprivilege\fR is {\fBALL\fR | \fBSELECT\fR | \fBINSERT\fR | \fBUPDATE\fR | \fBDELETE\fR | \fBRULE\fR} .fi .SH DESCRIPTION.PP --- 10,16 ---- \fBon\fR <rel1>[,...<reln>] \fBto\fR [\fBpublic\fR | group <group> | <username>] ! \fBprivilege\fR is {\fBALL\fR | \fBSELECT\fR | \fBINSERT\fR | \fBUPDATE\fR | \fBDELETE\fR | \fBRULE\fR | \fBVACUUM\R}.fi .SH DESCRIPTION .PP *** man/revoke.l.orig Mon May 12 08:19:14 1997 --- man/revoke.l Mon May 12 08:20:42 1997 *************** *** 10,16 **** \fBon\fR <rel1>[,...<reln>] \fBfrom\fR [\fBpublic\fR | group <group> | <username>] ! \fBprivilege\fR is {\fBALL\fR | \fBSELECT\fR | \fBINSERT\fR | \fBUPDATE\fR | \fBDELETE\fR | \fBRULE\fR} .fi .SH DESCRIPTION.PP --- 10,16 ---- \fBon\fR <rel1>[,...<reln>] \fBfrom\fR [\fBpublic\fR | group <group> | <username>] ! \fBprivilege\fR is {\fBALL\fR | \fBSELECT\fR | \fBINSERT\fR | \fBUPDATE\fR | \fBDELETE\fR | \fBRULE\fR | \fBVACUUM\R}.fi .SH DESCRIPTION .PP *** man/vacuum.l.orig Thu May 22 18:47:54 1997 --- man/vacuum.l Thu May 22 18:56:00 1997 *************** *** 1,7 **** .\" This is -*-nroff-*- .\" XXX standard disclaimer belongs here.... .\" $Header: /usr/local/cvsroot/postgres95/src/man/vacuum.l,v1.4 1997/05/13 04:41:54 momjian Exp $ ! .TH VACUUM SQL 11/05/95 PostgreSQL PostgreSQL .SH NAME vacuum \(em vacuum a database .SH SYNOPSIS --- 1,7 ---- .\" This is -*-nroff-*- .\" XXX standard disclaimer belongs here.... .\" $Header: /usr/local/cvsroot/postgres95/src/man/vacuum.l,v1.4 1997/05/13 04:41:54 momjian Exp $ ! .TH VACUUM SQL 22/05/97 PostgreSQL PostgreSQL .SH NAME vacuum \(em vacuum a database .SH SYNOPSIS *************** *** 40,44 **** .PP The purge(l) command can be used to control the archive retention characteristics of a given table. .SH"SEE ALSO" ! purge(l). --- 40,53 ---- .PP The purge(l) command can be used to control the archive retention characteristics of a given table. + .PP + Only the database owner (DBA) is allowed to vacuum the whole database. + Table vacuum rights may be granted to or revoked from individual users + with the + .BR grant(l) + and + .BR revoke(l) + commands. + .SH "SEE ALSO" ! purge(l) grant(l) revoke(l).
pgsql-hackers by date: