From 31c775adeb2251a9c66328cbc9016877e5e4f085 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sat, 27 Apr 2002 03:45:03 +0000 Subject: [PATCH] Restructure aclcheck error reporting to make permission-failure messages more uniform and internationalizable: the global array aclcheck_error_strings[] is gone in favor of a subroutine aclcheck_error(). Partial implementation of namespace-related permission checks --- not all done yet. --- src/backend/catalog/aclchk.c | 81 +++++++++++++----------- src/backend/catalog/namespace.c | 13 +++- src/backend/catalog/pg_operator.c | 10 ++- src/backend/commands/aggregatecmds.c | 30 ++++----- src/backend/commands/comment.c | 34 +++------- src/backend/commands/copy.c | 10 ++- src/backend/commands/functioncmds.c | 40 +++++++++--- src/backend/commands/indexcmds.c | 21 +++++- src/backend/commands/lockcmds.c | 7 +- src/backend/commands/operatorcmds.c | 18 ++++-- src/backend/commands/schemacmds.c | 15 ++++- src/backend/commands/tablecmds.c | 51 ++++++++------- src/backend/commands/trigger.c | 14 ++-- src/backend/commands/typecmds.c | 32 ++++++++-- src/backend/executor/execMain.c | 27 +++++--- src/backend/executor/execQual.c | 5 +- src/backend/rewrite/rewriteDefine.c | 12 ++-- src/backend/rewrite/rewriteRemove.c | 13 ++-- src/backend/tcop/utility.c | 12 ++-- src/backend/utils/adt/acl.c | 14 ++-- src/backend/utils/cache/fcache.c | 11 +++- src/backend/utils/cache/lsyscache.c | 29 ++++++++- src/include/utils/acl.h | 30 ++++----- src/include/utils/fcache.h | 4 +- src/include/utils/lsyscache.h | 3 +- src/test/regress/expected/privileges.out | 46 +++++++------- 26 files changed, 354 insertions(+), 228 deletions(-) diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index 48cca5fcb8..194292bb42 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/aclchk.c,v 1.66 2002/04/21 00:26:42 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/aclchk.c,v 1.67 2002/04/27 03:45:00 tgl Exp $ * * NOTES * See acl.h. @@ -46,16 +46,7 @@ static void ExecuteGrantStmt_Namespace(GrantStmt *stmt); static const char *privilege_to_string(AclMode privilege); -static int32 aclcheck(Acl *acl, AclId id, uint32 idtype, AclMode mode); - -/* warning messages, now more explicit. */ -/* MUST correspond to the order of the ACLCHECK_* result codes in acl.h. */ -const char * const aclcheck_error_strings[] = { - "No error.", - "Permission denied.", - "Table does not exist.", - "Must be table owner." -}; +static AclResult aclcheck(Acl *acl, AclId id, uint32 idtype, AclMode mode); #ifdef ACLDEBUG @@ -208,8 +199,7 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt) pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple); if (!pg_class_ownercheck(relOid, GetUserId())) - elog(ERROR, "%s: permission denied", - relvar->relname); + aclcheck_error(ACLCHECK_NOT_OWNER, relvar->relname); if (pg_class_tuple->relkind == RELKIND_INDEX) elog(ERROR, "\"%s\" is an index", @@ -409,7 +399,8 @@ ExecuteGrantStmt_Function(GrantStmt *stmt) pg_proc_tuple = (Form_pg_proc) GETSTRUCT(tuple); if (!pg_proc_ownercheck(oid, GetUserId())) - elog(ERROR, "permission denied"); + aclcheck_error(ACLCHECK_NOT_OWNER, + NameStr(pg_proc_tuple->proname)); /* * If there's no ACL, create a default using the pg_proc.proowner @@ -601,7 +592,7 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt) pg_namespace_tuple = (Form_pg_namespace) GETSTRUCT(tuple); if (!pg_namespace_ownercheck(tuple->t_data->t_oid, GetUserId())) - elog(ERROR, "permission denied"); + aclcheck_error(ACLCHECK_NOT_OWNER, nspname); /* * If there's no ACL, create a default using the pg_namespace.nspowner @@ -776,6 +767,7 @@ in_group(AclId uid, AclId gid) return result; } + /* * aclcheck * @@ -785,7 +777,7 @@ in_group(AclId uid, AclId gid) * * The ACL list is expected to be sorted in standard order. */ -static int32 +static AclResult aclcheck(Acl *acl, AclId id, uint32 idtype, AclMode mode) { AclItem *aip, @@ -903,14 +895,37 @@ aclcheck(Acl *acl, AclId id, uint32 idtype, AclMode mode) /* - * Exported routine for checking a user's access privileges to a table - * - * Returns an ACLCHECK_* result code. + * Standardized reporting of aclcheck permissions failures. */ -int32 +void +aclcheck_error(AclResult errcode, const char *objectname) +{ + switch (errcode) + { + case ACLCHECK_OK: + /* no error, so return to caller */ + break; + case ACLCHECK_NO_PRIV: + elog(ERROR, "%s: permission denied", objectname); + break; + case ACLCHECK_NOT_OWNER: + elog(ERROR, "%s: must be owner", objectname); + break; + default: + elog(ERROR, "%s: unexpected AclResult %d", + objectname, (int) errcode); + break; + } +} + + +/* + * Exported routine for checking a user's access privileges to a table + */ +AclResult pg_class_aclcheck(Oid table_oid, Oid userid, AclMode mode) { - int32 result; + AclResult result; bool usesuper, usecatupd; HeapTuple tuple; @@ -1004,13 +1019,11 @@ pg_class_aclcheck(Oid table_oid, Oid userid, AclMode mode) /* * Exported routine for checking a user's access privileges to a database - * - * Returns an ACLCHECK_* result code. */ -int32 +AclResult pg_database_aclcheck(Oid db_oid, Oid userid, AclMode mode) { - int32 result; + AclResult result; Relation pg_database; ScanKeyData entry[1]; HeapScanDesc scan; @@ -1069,13 +1082,11 @@ pg_database_aclcheck(Oid db_oid, Oid userid, AclMode mode) /* * Exported routine for checking a user's access privileges to a function - * - * Returns an ACLCHECK_* result code. */ -int32 +AclResult pg_proc_aclcheck(Oid proc_oid, Oid userid, AclMode mode) { - int32 result; + AclResult result; HeapTuple tuple; Datum aclDatum; bool isNull; @@ -1124,13 +1135,11 @@ pg_proc_aclcheck(Oid proc_oid, Oid userid, AclMode mode) /* * Exported routine for checking a user's access privileges to a language - * - * Returns an ACLCHECK_* result code. */ -int32 +AclResult pg_language_aclcheck(Oid lang_oid, Oid userid, AclMode mode) { - int32 result; + AclResult result; HeapTuple tuple; Datum aclDatum; bool isNull; @@ -1176,13 +1185,11 @@ pg_language_aclcheck(Oid lang_oid, Oid userid, AclMode mode) /* * Exported routine for checking a user's access privileges to a namespace - * - * Returns an ACLCHECK_* result code. */ -int32 +AclResult pg_namespace_aclcheck(Oid nsp_oid, Oid userid, AclMode mode) { - int32 result; + AclResult result; HeapTuple tuple; Datum aclDatum; bool isNull; diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index be9dc9f373..902af68969 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -13,7 +13,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.13 2002/04/26 01:24:08 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.14 2002/04/27 03:45:00 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -35,6 +35,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "storage/backendid.h" +#include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/guc.h" @@ -974,6 +975,16 @@ GetTempTableNamespace(void) char namespaceName[NAMEDATALEN]; Oid namespaceId; + /* + * First, do permission check to see if we are authorized to make + * temp tables. We use a nonstandard error message here since + * "databasename: permission denied" might be a tad cryptic. + */ + if (pg_database_aclcheck(MyDatabaseId, GetUserId(), + ACL_CREATE_TEMP) != ACLCHECK_OK) + elog(ERROR, "%s: not authorized to create temp tables", + DatabaseName); + snprintf(namespaceName, NAMEDATALEN, "pg_temp_%d", MyBackendId); namespaceId = GetSysCacheOid(NAMESPACENAME, diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c index 52cb26ee6a..53a5d61cd9 100644 --- a/src/backend/catalog/pg_operator.c +++ b/src/backend/catalog/pg_operator.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/pg_operator.c,v 1.67 2002/04/25 02:56:55 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/pg_operator.c,v 1.68 2002/04/27 03:45:00 tgl Exp $ * * NOTES * these routines moved here from commands/define.c and somewhat cleaned up. @@ -26,6 +26,7 @@ #include "miscadmin.h" #include "parser/parse_func.h" #include "parser/parse_oper.h" +#include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" #include "utils/syscache.h" @@ -697,6 +698,7 @@ get_other_operator(List *otherOp, Oid otherLeftTypeId, Oid otherRightTypeId, bool otherDefined; char *otherName; Oid otherNamespace; + AclResult aclresult; other_oid = OperatorLookup(otherOp, otherLeftTypeId, @@ -727,6 +729,12 @@ get_other_operator(List *otherOp, Oid otherLeftTypeId, Oid otherRightTypeId, } /* not in catalogs, different from operator, so make shell */ + + aclresult = pg_namespace_aclcheck(otherNamespace, GetUserId(), + ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, get_namespace_name(otherNamespace)); + other_oid = OperatorShellMake(otherName, otherNamespace, otherLeftTypeId, diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c index 7ee0c8f078..c3c1ed16df 100644 --- a/src/backend/commands/aggregatecmds.c +++ b/src/backend/commands/aggregatecmds.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.1 2002/04/15 05:22:03 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.2 2002/04/27 03:45:00 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -26,6 +26,7 @@ #include "catalog/catname.h" #include "catalog/namespace.h" #include "catalog/pg_aggregate.h" +#include "catalog/pg_proc.h" #include "commands/comment.h" #include "commands/defrem.h" #include "miscadmin.h" @@ -45,6 +46,7 @@ DefineAggregate(List *names, List *parameters) { char *aggName; Oid aggNamespace; + AclResult aclresult; List *transfuncName = NIL; List *finalfuncName = NIL; TypeName *baseType = NULL; @@ -57,6 +59,11 @@ DefineAggregate(List *names, List *parameters) /* Convert list of names to a name and namespace */ aggNamespace = QualifiedNameGetCreationNamespace(names, &aggName); + /* Check we have creation rights in target namespace */ + aclresult = pg_namespace_aclcheck(aggNamespace, GetUserId(), ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, get_namespace_name(aggNamespace)); + foreach(pl, parameters) { DefElem *defel = (DefElem *) lfirst(pl); @@ -157,20 +164,6 @@ RemoveAggregate(List *aggName, TypeName *aggType) procOid = find_aggregate_func("RemoveAggregate", aggName, basetypeID); - /* Permission check */ - - if (!pg_proc_ownercheck(procOid, GetUserId())) - { - if (basetypeID == InvalidOid) - elog(ERROR, "RemoveAggregate: aggregate %s for all types: permission denied", - NameListToString(aggName)); - else - elog(ERROR, "RemoveAggregate: aggregate %s for type %s: permission denied", - NameListToString(aggName), format_type_be(basetypeID)); - } - - /* Remove the pg_proc tuple */ - relation = heap_openr(ProcedureRelationName, RowExclusiveLock); tup = SearchSysCache(PROCOID, @@ -180,9 +173,16 @@ RemoveAggregate(List *aggName, TypeName *aggType) elog(ERROR, "RemoveAggregate: couldn't find pg_proc tuple for %s", NameListToString(aggName)); + /* Permission check: must own agg or its namespace */ + if (!pg_proc_ownercheck(procOid, GetUserId()) && + !pg_namespace_ownercheck(((Form_pg_proc) GETSTRUCT(tup))->pronamespace, + GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(aggName)); + /* Delete any comments associated with this function */ DeleteComments(procOid, RelationGetRelid(relation)); + /* Remove the pg_proc tuple */ simple_heap_delete(relation, &tup->t_self); ReleaseSysCache(tup); diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c index 3968ef14ef..0eb59b912f 100644 --- a/src/backend/commands/comment.c +++ b/src/backend/commands/comment.c @@ -7,7 +7,7 @@ * Copyright (c) 1999-2001, PostgreSQL Global Development Group * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.44 2002/04/24 02:50:30 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.45 2002/04/27 03:45:00 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -324,8 +324,7 @@ CommentRelation(int objtype, List *relname, char *comment) /* Check object security */ if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId())) - elog(ERROR, "you are not permitted to comment on class '%s'", - RelationGetRelationName(relation)); + aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(relation)); /* Next, verify that the relation type matches the intent */ @@ -395,8 +394,7 @@ CommentAttribute(List *qualname, char *comment) /* Check object security */ if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId())) - elog(ERROR, "you are not permitted to comment on class '%s'", - RelationGetRelationName(relation)); + aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(relation)); /* Now, fetch the attribute number from the system cache */ @@ -498,7 +496,7 @@ CommentRule(List *qualname, char *comment) Oid reloid; Oid ruleoid; Oid classoid; - int32 aclcheck; + AclResult aclcheck; /* Separate relname and trig name */ nnames = length(qualname); @@ -573,8 +571,7 @@ CommentRule(List *qualname, char *comment) aclcheck = pg_class_aclcheck(reloid, GetUserId(), ACL_RULE); if (aclcheck != ACLCHECK_OK) - elog(ERROR, "you are not permitted to comment on rule '%s'", - rulename); + aclcheck_error(aclcheck, rulename); /* pg_rewrite doesn't have a hard-coded OID, so must look it up */ @@ -613,8 +610,7 @@ CommentType(List *typename, char *comment) /* Check object security */ if (!pg_type_ownercheck(oid, GetUserId())) - elog(ERROR, "you are not permitted to comment on type %s", - TypeNameToString(tname)); + aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(tname)); /* Call CreateComments() to create/drop the comments */ @@ -649,14 +645,7 @@ CommentAggregate(List *aggregate, List *arguments, char *comment) /* Next, validate the user's attempt to comment */ if (!pg_proc_ownercheck(oid, GetUserId())) - { - if (baseoid == InvalidOid) - elog(ERROR, "you are not permitted to comment on aggregate %s for all types", - NameListToString(aggregate)); - else - elog(ERROR, "you are not permitted to comment on aggregate %s for type %s", - NameListToString(aggregate), format_type_be(baseoid)); - } + aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(aggregate)); /* Call CreateComments() to create/drop the comments */ @@ -685,8 +674,7 @@ CommentProc(List *function, List *arguments, char *comment) /* Now, validate the user's ability to comment on this function */ if (!pg_proc_ownercheck(oid, GetUserId())) - elog(ERROR, "you are not permitted to comment on function %s", - NameListToString(function)); + aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(function)); /* Call CreateComments() to create/drop the comments */ @@ -723,8 +711,7 @@ CommentOperator(List *opername, List *arguments, char *comment) /* Valid user's ability to comment on this operator */ if (!pg_oper_ownercheck(oid, GetUserId())) - elog(ERROR, "you are not permitted to comment on operator '%s'", - NameListToString(opername)); + aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(opername)); /* Get the procedure associated with the operator */ @@ -775,8 +762,7 @@ CommentTrigger(List *qualname, char *comment) /* Check object security */ if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId())) - elog(ERROR, "you are not permitted to comment on trigger '%s' for relation '%s'", - trigname, RelationGetRelationName(relation)); + aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(relation)); /* * Fetch the trigger tuple from pg_trigger. There can be only one diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 45e108027f..1ab3ae14fc 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.152 2002/03/29 19:06:05 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.153 2002/04/27 03:45:00 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -266,8 +266,8 @@ DoCopy(const RangeVar *relation, bool binary, bool oids, bool from, bool pipe, { FILE *fp; Relation rel; - const AclMode required_access = (from ? ACL_INSERT : ACL_SELECT); - int32 aclresult; + AclMode required_access = (from ? ACL_INSERT : ACL_SELECT); + AclResult aclresult; /* * Open and lock the relation, using the appropriate lock type. @@ -278,9 +278,7 @@ DoCopy(const RangeVar *relation, bool binary, bool oids, bool from, bool pipe, aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), required_access); if (aclresult != ACLCHECK_OK) - elog(ERROR, "%s: %s", - RelationGetRelationName(rel), - aclcheck_error_strings[aclresult]); + aclcheck_error(aclresult, RelationGetRelationName(rel)); if (!pipe && !superuser()) elog(ERROR, "You must have Postgres superuser privilege to do a COPY " "directly to or from a file. Anyone can COPY to stdout or " diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 86b7cd50ec..ce4b43b593 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.2 2002/04/21 00:26:42 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.3 2002/04/27 03:45:01 tgl Exp $ * * DESCRIPTION * These routines take the parse tree and pick out the @@ -86,6 +86,7 @@ compute_return_type(TypeName *returnType, Oid languageOid, else { Oid namespaceId; + AclResult aclresult; char *typname; if (languageOid == SQLlanguageId) @@ -94,6 +95,10 @@ compute_return_type(TypeName *returnType, Oid languageOid, typnam); namespaceId = QualifiedNameGetCreationNamespace(returnType->names, &typname); + aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), + ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, get_namespace_name(namespaceId)); rettype = TypeShellMake(typname, namespaceId); if (!OidIsValid(rettype)) elog(ERROR, "could not create type %s", typnam); @@ -295,6 +300,7 @@ CreateFunction(ProcedureStmt *stmt) Oid languageOid; char *funcname; Oid namespaceId; + AclResult aclresult; int parameterCount; Oid parameterTypes[FUNC_MAX_ARGS]; int32 byte_pct, @@ -311,6 +317,11 @@ CreateFunction(ProcedureStmt *stmt) namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname, &funcname); + /* Check we have creation rights in target namespace */ + aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, get_namespace_name(namespaceId)); + /* Convert language name to canonical case */ case_translate_language_name(stmt->language, languageName); @@ -324,10 +335,21 @@ CreateFunction(ProcedureStmt *stmt) languageOid = languageTuple->t_data->t_oid; languageStruct = (Form_pg_language) GETSTRUCT(languageTuple); - if (!((languageStruct->lanpltrusted - && pg_language_aclcheck(languageOid, GetUserId(), ACL_USAGE) == ACLCHECK_OK) - || superuser())) - elog(ERROR, "permission denied"); + if (languageStruct->lanpltrusted) + { + /* if trusted language, need USAGE privilege */ + AclResult aclresult; + + aclresult = pg_language_aclcheck(languageOid, GetUserId(), ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, NameStr(languageStruct->lanname)); + } + else + { + /* if untrusted language, must be superuser */ + if (!superuser()) + aclcheck_error(ACLCHECK_NO_PRIV, NameStr(languageStruct->lanname)); + } ReleaseSysCache(languageTuple); @@ -404,9 +426,11 @@ RemoveFunction(List *functionName, /* function name to be removed */ elog(ERROR, "RemoveFunction: couldn't find tuple for function %s", NameListToString(functionName)); - if (!pg_proc_ownercheck(funcOid, GetUserId())) - elog(ERROR, "RemoveFunction: function '%s': permission denied", - NameListToString(functionName)); + /* Permission check: must own func or its namespace */ + if (!pg_proc_ownercheck(funcOid, GetUserId()) && + !pg_namespace_ownercheck(((Form_pg_proc) GETSTRUCT(tup))->pronamespace, + GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(functionName)); if (((Form_pg_proc) GETSTRUCT(tup))->proisagg) elog(ERROR, "RemoveFunction: function '%s' is an aggregate" diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 14889a4f0c..df6f81cd1d 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.71 2002/04/17 20:57:56 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.72 2002/04/27 03:45:01 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -30,6 +30,7 @@ #include "parser/parsetree.h" #include "parser/parse_coerce.h" #include "parser/parse_func.h" +#include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" #include "utils/syscache.h" @@ -73,6 +74,7 @@ DefineIndex(RangeVar *heapRelation, Oid *classObjectId; Oid accessMethodId; Oid relationId; + Oid namespaceId; Relation rel; HeapTuple tuple; Form_pg_am accessMethodForm; @@ -102,6 +104,7 @@ DefineIndex(RangeVar *heapRelation, heapRelation->relname); relationId = RelationGetRelid(rel); + namespaceId = RelationGetNamespace(rel); if (!IsBootstrapProcessingMode() && IsSystemRelation(rel) && @@ -110,6 +113,22 @@ DefineIndex(RangeVar *heapRelation, heap_close(rel, NoLock); + /* + * Verify we (still) have CREATE rights in the rel's namespace. + * (Presumably we did when the rel was created, but maybe not anymore.) + * Skip check if bootstrapping, since permissions machinery may not + * be working yet; also, always allow if it's a temp table. + */ + if (!IsBootstrapProcessingMode() && !isTempNamespace(namespaceId)) + { + AclResult aclresult; + + aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), + ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, get_namespace_name(namespaceId)); + } + /* * look up the access method, verify it can handle the requested * features diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c index c91669572e..80e42e3aa1 100644 --- a/src/backend/commands/lockcmds.c +++ b/src/backend/commands/lockcmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/lockcmds.c,v 1.1 2002/04/15 05:22:03 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/lockcmds.c,v 1.2 2002/04/27 03:45:01 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,6 +19,7 @@ #include "commands/lockcmds.h" #include "miscadmin.h" #include "utils/acl.h" +#include "utils/lsyscache.h" /* @@ -38,7 +39,7 @@ LockTableCommand(LockStmt *lockstmt) { RangeVar *relation = lfirst(p); Oid reloid; - int32 aclresult; + AclResult aclresult; Relation rel; /* @@ -55,7 +56,7 @@ LockTableCommand(LockStmt *lockstmt) ACL_UPDATE | ACL_DELETE); if (aclresult != ACLCHECK_OK) - elog(ERROR, "LOCK TABLE: permission denied"); + aclcheck_error(aclresult, get_rel_name(reloid)); rel = relation_open(reloid, lockstmt->mode); diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c index ffc35ea1ae..de8ec06acb 100644 --- a/src/backend/commands/operatorcmds.c +++ b/src/backend/commands/operatorcmds.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/operatorcmds.c,v 1.2 2002/04/16 23:08:10 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/operatorcmds.c,v 1.3 2002/04/27 03:45:01 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -44,6 +44,7 @@ #include "parser/parse_oper.h" #include "parser/parse_type.h" #include "utils/acl.h" +#include "utils/lsyscache.h" #include "utils/syscache.h" @@ -60,6 +61,7 @@ DefineOperator(List *names, List *parameters) { char *oprName; Oid oprNamespace; + AclResult aclresult; uint16 precedence = 0; /* operator precedence */ bool canHash = false; /* operator hashes */ bool canMerge = false; /* operator merges */ @@ -85,6 +87,11 @@ DefineOperator(List *names, List *parameters) /* Convert list of names to a name and namespace */ oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName); + /* Check we have creation rights in target namespace */ + aclresult = pg_namespace_aclcheck(oprNamespace, GetUserId(), ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, get_namespace_name(oprNamespace)); + /* * loop over the definition list and extract the information we need. */ @@ -226,14 +233,15 @@ RemoveOperator(List *operatorName, /* operator name */ tup = SearchSysCacheCopy(OPEROID, ObjectIdGetDatum(operOid), 0, 0, 0); - if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "RemoveOperator: failed to find tuple for operator '%s'", NameListToString(operatorName)); - if (!pg_oper_ownercheck(operOid, GetUserId())) - elog(ERROR, "RemoveOperator: operator '%s': permission denied", - NameListToString(operatorName)); + /* Permission check: must own operator or its namespace */ + if (!pg_oper_ownercheck(operOid, GetUserId()) && + !pg_namespace_ownercheck(((Form_pg_operator) GETSTRUCT(tup))->oprnamespace, + GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(operatorName)); /* Delete any comments associated with this operator */ DeleteComments(operOid, RelationGetRelid(relation)); diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c index 191d5e329b..30bc4077d3 100644 --- a/src/backend/commands/schemacmds.c +++ b/src/backend/commands/schemacmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/schemacmds.c,v 1.1 2002/04/15 05:22:03 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/schemacmds.c,v 1.2 2002/04/27 03:45:01 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,6 +20,7 @@ #include "miscadmin.h" #include "parser/analyze.h" #include "tcop/utility.h" +#include "utils/acl.h" #include "utils/lsyscache.h" @@ -36,9 +37,14 @@ CreateSchemaCommand(CreateSchemaStmt *stmt) const char *owner_name; Oid owner_userid; Oid saved_userid; + AclResult aclresult; saved_userid = GetUserId(); + /* + * Figure out user identities. + */ + if (!authId) { owner_userid = saved_userid; @@ -67,6 +73,13 @@ CreateSchemaCommand(CreateSchemaStmt *stmt) owner_name, authId); } + /* + * Permissions checks. + */ + aclresult = pg_database_aclcheck(MyDatabaseId, saved_userid, ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, DatabaseName); + if (!allowSystemTableMods && IsReservedName(schemaName)) elog(ERROR, "CREATE SCHEMA: Illegal schema name: \"%s\" -- pg_ is reserved for system schemas", schemaName); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 7bdc238e77..32930710c6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.10 2002/04/26 19:29:47 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.11 2002/04/27 03:45:01 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -106,10 +106,22 @@ DefineRelation(CreateStmt *stmt, char relkind) /* * Look up the namespace in which we are supposed to create the - * relation. + * relation. Check we have permission to create there. + * Skip check if bootstrapping, since permissions machinery may not + * be working yet; also, always allow if it's a temp table. */ namespaceId = RangeVarGetCreationNamespace(stmt->relation); + if (!IsBootstrapProcessingMode() && !isTempNamespace(namespaceId)) + { + AclResult aclresult; + + aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), + ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, get_namespace_name(namespaceId)); + } + /* * Merge domain attributes into the known columns before processing table * inheritance. Otherwise we risk adding double constraints to a @@ -307,8 +319,7 @@ TruncateRelation(const RangeVar *relation) RelationGetRelationName(rel)); if (!pg_class_ownercheck(relid, GetUserId())) - elog(ERROR, "you do not own relation \"%s\"", - RelationGetRelationName(rel)); + aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel)); /* Keep the lock until transaction commit */ heap_close(rel, NoLock); @@ -483,8 +494,8 @@ MergeAttributes(List *schema, List *supers, bool istemp, * demand that creator of a child table own the parent. */ if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId())) - elog(ERROR, "you do not own table \"%s\"", - parent->relname); + aclcheck_error(ACLCHECK_NOT_OWNER, + RelationGetRelationName(relation)); /* * Reject duplications in the list of parents. @@ -1003,8 +1014,8 @@ renameatt(Oid relid, elog(ERROR, "renameatt: class \"%s\" is a system catalog", RelationGetRelationName(targetrelation)); if (!pg_class_ownercheck(relid, GetUserId())) - elog(ERROR, "renameatt: you do not own class \"%s\"", - RelationGetRelationName(targetrelation)); + aclcheck_error(ACLCHECK_NOT_OWNER, + RelationGetRelationName(targetrelation)); /* * if the 'recurse' flag is set then we are supposed to rename this @@ -1558,8 +1569,7 @@ AlterTableAddColumn(Oid myrelid, elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog", RelationGetRelationName(rel)); if (!pg_class_ownercheck(myrelid, GetUserId())) - elog(ERROR, "ALTER TABLE: \"%s\": permission denied", - RelationGetRelationName(rel)); + aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel)); /* * Recurse to add the column to child classes, if requested. @@ -1761,8 +1771,7 @@ AlterTableAlterColumnDropNotNull(Oid myrelid, RelationGetRelationName(rel)); if (!pg_class_ownercheck(myrelid, GetUserId())) - elog(ERROR, "ALTER TABLE: \"%s\": permission denied", - RelationGetRelationName(rel)); + aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel)); /* * Propagate to children if desired @@ -1912,8 +1921,7 @@ AlterTableAlterColumnSetNotNull(Oid myrelid, RelationGetRelationName(rel)); if (!pg_class_ownercheck(myrelid, GetUserId())) - elog(ERROR, "ALTER TABLE: \"%s\": permission denied", - RelationGetRelationName(rel)); + aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel)); /* * Propagate to children if desired @@ -2048,8 +2056,7 @@ AlterTableAlterColumnDefault(Oid myrelid, RelationGetRelationName(rel)); if (!pg_class_ownercheck(myrelid, GetUserId())) - elog(ERROR, "ALTER TABLE: \"%s\": permission denied", - RelationGetRelationName(rel)); + aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel)); /* * Propagate to children if desired @@ -2208,8 +2215,7 @@ AlterTableAlterColumnFlags(Oid myrelid, RelationGetRelationName(rel)); if (!pg_class_ownercheck(myrelid, GetUserId())) - elog(ERROR, "ALTER TABLE: \"%s\": permission denied", - RelationGetRelationName(rel)); + aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel)); /* * Check the supplied parameters before anything else @@ -2370,8 +2376,7 @@ AlterTableAddConstraint(Oid myrelid, RelationGetRelationName(rel)); if (!pg_class_ownercheck(myrelid, GetUserId())) - elog(ERROR, "ALTER TABLE: \"%s\": permission denied", - RelationGetRelationName(rel)); + aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel)); if (inh) { @@ -2695,8 +2700,7 @@ AlterTableDropConstraint(Oid myrelid, RelationGetRelationName(rel)); if (!pg_class_ownercheck(myrelid, GetUserId())) - elog(ERROR, "ALTER TABLE: \"%s\": permission denied", - RelationGetRelationName(rel)); + aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel)); /* * Since all we have is the name of the constraint, we have to look @@ -2857,8 +2861,7 @@ AlterTableCreateToastTable(Oid relOid, bool silent) RelationGetRelationName(rel)); if (!pg_class_ownercheck(relOid, GetUserId())) - elog(ERROR, "ALTER TABLE: \"%s\": permission denied", - RelationGetRelationName(rel)); + aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel)); /* * lock the pg_class tuple for update (is that really needed?) diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index b826297a71..52e9761d75 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.115 2002/04/26 19:29:47 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.116 2002/04/27 03:45:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -58,6 +58,7 @@ CreateTrigger(CreateTrigStmt *stmt) Datum values[Natts_pg_trigger]; char nulls[Natts_pg_trigger]; Relation rel; + AclResult aclresult; Relation tgrel; SysScanDesc tgscan; ScanKeyData key; @@ -84,10 +85,10 @@ CreateTrigger(CreateTrigStmt *stmt) elog(ERROR, "CreateTrigger: can't create trigger for system relation %s", stmt->relation->relname); - if (pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), - stmt->isconstraint ? ACL_REFERENCES : ACL_TRIGGER) - != ACLCHECK_OK) - elog(ERROR, "permission denied"); + aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), + stmt->isconstraint ? ACL_REFERENCES : ACL_TRIGGER); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, RelationGetRelationName(rel)); /* * If trigger is an RI constraint, use trigger name as constraint name @@ -337,8 +338,7 @@ DropTrigger(Oid relid, const char *trigname) RelationGetRelationName(rel)); if (!pg_class_ownercheck(relid, GetUserId())) - elog(ERROR, "%s: %s", RelationGetRelationName(rel), - aclcheck_error_strings[ACLCHECK_NOT_OWNER]); + aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel)); /* * Search pg_trigger, delete target trigger, count remaining triggers diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 13dbf04aaa..74e5a15cc8 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.1 2002/04/15 05:22:03 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.2 2002/04/27 03:45:02 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -45,6 +45,7 @@ #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" +#include "utils/lsyscache.h" #include "utils/syscache.h" @@ -60,6 +61,7 @@ DefineType(List *names, List *parameters) { char *typeName; Oid typeNamespace; + AclResult aclresult; int16 internalLength = -1; /* int2 */ int16 externalLength = -1; /* int2 */ Oid elemType = InvalidOid; @@ -83,6 +85,11 @@ DefineType(List *names, List *parameters) /* Convert list of names to a name and namespace */ typeNamespace = QualifiedNameGetCreationNamespace(names, &typeName); + /* Check we have creation rights in target namespace */ + aclresult = pg_namespace_aclcheck(typeNamespace, GetUserId(), ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, get_namespace_name(typeNamespace)); + /* * Type names must be one character shorter than other names, allowing * room to create the corresponding array type name with prepended @@ -288,9 +295,11 @@ RemoveType(List *names) elog(ERROR, "Type \"%s\" does not exist", TypeNameToString(typename)); - if (!pg_type_ownercheck(typeoid, GetUserId())) - elog(ERROR, "RemoveType: type '%s': permission denied", - TypeNameToString(typename)); + /* Permission check: must own type or its namespace */ + if (!pg_type_ownercheck(typeoid, GetUserId()) && + !pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace, + GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename)); /* Delete any comments associated with this type */ DeleteComments(typeoid, RelationGetRelid(relation)); @@ -334,6 +343,7 @@ DefineDomain(CreateDomainStmt *stmt) { char *domainName; Oid domainNamespace; + AclResult aclresult; int16 internalLength; int16 externalLength; Oid inputProcedure; @@ -360,6 +370,12 @@ DefineDomain(CreateDomainStmt *stmt) domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname, &domainName); + /* Check we have creation rights in target namespace */ + aclresult = pg_namespace_aclcheck(domainNamespace, GetUserId(), + ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, get_namespace_name(domainNamespace)); + /* * Domainnames, unlike typenames don't need to account for the '_' * prefix. So they can be one character longer. @@ -586,9 +602,11 @@ RemoveDomain(List *names, int behavior) elog(ERROR, "RemoveDomain: type '%s' does not exist", TypeNameToString(typename)); - if (!pg_type_ownercheck(typeoid, GetUserId())) - elog(ERROR, "RemoveDomain: type '%s': permission denied", - TypeNameToString(typename)); + /* Permission check: must own type or its namespace */ + if (!pg_type_ownercheck(typeoid, GetUserId()) && + !pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace, + GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename)); /* Check that this is actually a domain */ typtype = ((Form_pg_type) GETSTRUCT(tup))->typtype; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index a2045a9eb8..da12fa9a1d 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -27,7 +27,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.158 2002/04/15 05:22:04 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.159 2002/04/27 03:45:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -360,7 +360,7 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation) { Oid relOid; Oid userid; - int32 aclcheck_result; + AclResult aclcheck_result; /* * If it's a subquery RTE, ignore it --- it will be checked when @@ -388,9 +388,7 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation) { aclcheck_result = CHECK(ACL_SELECT); if (aclcheck_result != ACLCHECK_OK) - elog(ERROR, "%s: %s", - get_rel_name(relOid), - aclcheck_error_strings[aclcheck_result]); + aclcheck_error(aclcheck_result, get_rel_name(relOid)); } if (rte->checkForWrite) @@ -419,9 +417,7 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation) break; } if (aclcheck_result != ACLCHECK_OK) - elog(ERROR, "%s: %s", - get_rel_name(relOid), - aclcheck_error_strings[aclcheck_result]); + aclcheck_error(aclcheck_result, get_rel_name(relOid)); } } @@ -701,7 +697,7 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) if (!parseTree->isPortal) { /* - * a select into table + * a select into table --- need to create the "into" table */ if (parseTree->into != NULL) { @@ -711,11 +707,22 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) TupleDesc tupdesc; /* - * create the "into" relation + * find namespace to create in, check permissions */ intoName = parseTree->into->relname; namespaceId = RangeVarGetCreationNamespace(parseTree->into); + if (!isTempNamespace(namespaceId)) + { + AclResult aclresult; + + aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), + ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, + get_namespace_name(namespaceId)); + } + /* * have to copy tupType to get rid of constraints */ diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index e77eeed803..5cc1bbaa71 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.90 2002/02/18 23:11:13 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.91 2002/04/27 03:45:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -658,9 +658,6 @@ ExecMakeFunctionResult(FunctionCachePtr fcache, bool hasSetArg; int i; - if (!fcache->permission_ok) - elog(ERROR, "permission denied"); - /* * arguments is a list of expressions to evaluate before passing to * the function manager. We skip the evaluation if it was already diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 922030c4be..6d4c81c70a 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.68 2002/04/19 23:13:54 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.69 2002/04/27 03:45:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -127,7 +127,7 @@ DefineQueryRewrite(RuleStmt *stmt) *event_qualP; List *l; Query *query; - int32 aclcheck_result; + AclResult aclresult; bool RelisBecomingView = false; /* @@ -144,11 +144,9 @@ DefineQueryRewrite(RuleStmt *stmt) /* * Check user has permission to apply rules to this relation. */ - aclcheck_result = pg_class_aclcheck(ev_relid, GetUserId(), ACL_RULE); - if (aclcheck_result != ACLCHECK_OK) - elog(ERROR, "%s: %s", - RelationGetRelationName(event_relation), - aclcheck_error_strings[aclcheck_result]); + aclresult = pg_class_aclcheck(ev_relid, GetUserId(), ACL_RULE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, RelationGetRelationName(event_relation)); /* * No rule actions that modify OLD or NEW diff --git a/src/backend/rewrite/rewriteRemove.c b/src/backend/rewrite/rewriteRemove.c index df85fa504a..97bf6589c9 100644 --- a/src/backend/rewrite/rewriteRemove.c +++ b/src/backend/rewrite/rewriteRemove.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.48 2002/04/18 20:01:09 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.49 2002/04/27 03:45:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -42,7 +42,7 @@ RemoveRewriteRule(Oid owningRel, const char *ruleName) Oid ruleId; Oid eventRelationOid; bool hasMoreRules; - int32 aclcheck_result; + AclResult aclresult; /* * Open the pg_rewrite relation. @@ -82,12 +82,9 @@ RemoveRewriteRule(Oid owningRel, const char *ruleName) /* * Verify user has appropriate permissions. */ - aclcheck_result = pg_class_aclcheck(eventRelationOid, GetUserId(), - ACL_RULE); - if (aclcheck_result != ACLCHECK_OK) - elog(ERROR, "%s: %s", - RelationGetRelationName(event_relation), - aclcheck_error_strings[aclcheck_result]); + aclresult = pg_class_aclcheck(eventRelationOid, GetUserId(), ACL_RULE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, RelationGetRelationName(event_relation)); /* do not allow the removal of a view's SELECT rule */ if (event_relation->rd_rel->relkind == RELKIND_VIEW && diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index d1d670228f..771f3d30cc 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.151 2002/04/24 02:48:55 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.152 2002/04/27 03:45:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -124,9 +124,10 @@ CheckDropPermissions(RangeVar *rel, char rightkind) if (classform->relkind != rightkind) DropErrorMsg(rel->relname, classform->relkind, rightkind); - if (!pg_class_ownercheck(relOid, GetUserId())) - elog(ERROR, "you do not own %s \"%s\"", - rentry->name, rel->relname); + /* Allow DROP to either table owner or schema owner */ + if (!pg_class_ownercheck(relOid, GetUserId()) && + !pg_namespace_ownercheck(classform->relnamespace, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, rel->relname); if (!allowSystemTableMods && IsSystemClass(classform)) elog(ERROR, "%s \"%s\" is a system %s", @@ -149,8 +150,7 @@ CheckOwnership(RangeVar *rel, bool noCatalogs) elog(ERROR, "Relation \"%s\" does not exist", rel->relname); if (!pg_class_ownercheck(relOid, GetUserId())) - elog(ERROR, "%s: %s", rel->relname, - aclcheck_error_strings[ACLCHECK_NOT_OWNER]); + aclcheck_error(ACLCHECK_NOT_OWNER, rel->relname); if (noCatalogs) { diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c index 14a06504c5..1f1de3e498 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.72 2002/04/26 01:24:08 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.73 2002/04/27 03:45:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -664,7 +664,7 @@ has_table_privilege_name_name(PG_FUNCTION_ARGS) int32 usesysid; Oid reloid; AclMode mode; - int32 aclresult; + AclResult aclresult; /* * Lookup userid based on username @@ -709,7 +709,7 @@ has_table_privilege_name(PG_FUNCTION_ARGS) int32 usesysid; Oid reloid; AclMode mode; - int32 aclresult; + AclResult aclresult; usesysid = GetUserId(); @@ -750,7 +750,7 @@ has_table_privilege_name_id(PG_FUNCTION_ARGS) text *priv_type_text = PG_GETARG_TEXT_P(2); int32 usesysid; AclMode mode; - int32 aclresult; + AclResult aclresult; /* * Lookup userid based on username @@ -789,7 +789,7 @@ has_table_privilege_id(PG_FUNCTION_ARGS) text *priv_type_text = PG_GETARG_TEXT_P(1); int32 usesysid; AclMode mode; - int32 aclresult; + AclResult aclresult; usesysid = GetUserId(); @@ -825,7 +825,7 @@ has_table_privilege_id_name(PG_FUNCTION_ARGS) text *priv_type_text = PG_GETARG_TEXT_P(2); Oid reloid; AclMode mode; - int32 aclresult; + AclResult aclresult; /* * Lookup rel OID based on relname @@ -863,7 +863,7 @@ has_table_privilege_id_id(PG_FUNCTION_ARGS) Oid reloid = PG_GETARG_OID(1); text *priv_type_text = PG_GETARG_TEXT_P(2); AclMode mode; - int32 aclresult; + AclResult aclresult; /* * Convert priv_type_text to an AclMode diff --git a/src/backend/utils/cache/fcache.c b/src/backend/utils/cache/fcache.c index cb11cbe4e2..6d6efdbaa3 100644 --- a/src/backend/utils/cache/fcache.c +++ b/src/backend/utils/cache/fcache.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/fcache.c,v 1.43 2002/04/21 00:26:43 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/fcache.c,v 1.44 2002/04/27 03:45:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,6 +17,7 @@ #include "miscadmin.h" #include "utils/acl.h" #include "utils/fcache.h" +#include "utils/lsyscache.h" /* @@ -26,6 +27,12 @@ FunctionCachePtr init_fcache(Oid foid, int nargs, MemoryContext fcacheCxt) { FunctionCachePtr retval; + AclResult aclresult; + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(foid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, get_func_name(foid)); /* Safety check (should never fail, as parser should check sooner) */ if (nargs > FUNC_MAX_ARGS) @@ -42,7 +49,5 @@ init_fcache(Oid foid, int nargs, MemoryContext fcacheCxt) /* Initialize additional info */ retval->setArgsValid = false; - retval->permission_ok = pg_proc_aclcheck(foid, GetUserId(), ACL_EXECUTE) == ACLCHECK_OK; - return retval; } diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 6699a179d3..f6b98c5bee 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.70 2002/04/16 23:08:11 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.71 2002/04/27 03:45:03 tgl Exp $ * * NOTES * Eventually, the index information should go through here, too. @@ -564,6 +564,33 @@ get_oprjoin(Oid opno) /* ---------- FUNCTION CACHE ---------- */ +/* + * get_func_name + * returns the name of the function with the given funcid + * + * Note: returns a palloc'd copy of the string, or NULL if no such function. + */ +char * +get_func_name(Oid funcid) +{ + HeapTuple tp; + + tp = SearchSysCache(PROCOID, + ObjectIdGetDatum(funcid), + 0, 0, 0); + if (HeapTupleIsValid(tp)) + { + Form_pg_proc functup = (Form_pg_proc) GETSTRUCT(tp); + char *result; + + result = pstrdup(NameStr(functup->proname)); + ReleaseSysCache(tp); + return result; + } + else + return NULL; +} + /* * get_func_rettype * Given procedure id, return the function's result type. diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index 146f04a27a..cf06ee7471 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: acl.h,v 1.43 2002/04/21 00:26:44 tgl Exp $ + * $Id: acl.h,v 1.44 2002/04/27 03:45:03 tgl Exp $ * * NOTES * For backward-compatibility purposes we have to allow there @@ -165,13 +165,12 @@ typedef ArrayType IdList; /* result codes for pg_*_aclcheck */ -#define ACLCHECK_OK 0 -#define ACLCHECK_NO_PRIV 1 -#define ACLCHECK_NO_CLASS 2 -#define ACLCHECK_NOT_OWNER 3 - -/* error messages (index by ACLCHECK_* result code). set in aclchk.c. */ -extern const char * const aclcheck_error_strings[]; +typedef enum +{ + ACLCHECK_OK = 0, + ACLCHECK_NO_PRIV, + ACLCHECK_NOT_OWNER +} AclResult; /* * routines used internally @@ -181,7 +180,7 @@ extern Acl *aclinsert3(const Acl *old_acl, const AclItem *mod_aip, unsigned modechg); /* - * exported routines (from acl.c) + * SQL functions (from acl.c) */ extern Datum aclitemin(PG_FUNCTION_ARGS); extern Datum aclitemout(PG_FUNCTION_ARGS); @@ -196,12 +195,13 @@ extern void ExecuteGrantStmt(GrantStmt *stmt); extern AclId get_grosysid(char *groname); extern char *get_groname(AclId grosysid); -/* these return ACLCHECK_* result codes */ -extern int32 pg_class_aclcheck(Oid table_oid, Oid userid, AclMode mode); -extern int32 pg_database_aclcheck(Oid db_oid, Oid userid, AclMode mode); -extern int32 pg_proc_aclcheck(Oid proc_oid, Oid userid, AclMode mode); -extern int32 pg_language_aclcheck(Oid lang_oid, Oid userid, AclMode mode); -extern int32 pg_namespace_aclcheck(Oid nsp_oid, Oid userid, AclMode mode); +extern AclResult pg_class_aclcheck(Oid table_oid, Oid userid, AclMode mode); +extern AclResult pg_database_aclcheck(Oid db_oid, Oid userid, AclMode mode); +extern AclResult pg_proc_aclcheck(Oid proc_oid, Oid userid, AclMode mode); +extern AclResult pg_language_aclcheck(Oid lang_oid, Oid userid, AclMode mode); +extern AclResult pg_namespace_aclcheck(Oid nsp_oid, Oid userid, AclMode mode); + +extern void aclcheck_error(AclResult errcode, const char *objectname); /* ownercheck routines just return true (owner) or false (not) */ extern bool pg_class_ownercheck(Oid class_oid, Oid userid); diff --git a/src/include/utils/fcache.h b/src/include/utils/fcache.h index fdc84c2122..6bb75f52df 100644 --- a/src/include/utils/fcache.h +++ b/src/include/utils/fcache.h @@ -11,7 +11,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: fcache.h,v 1.21 2002/02/18 23:11:46 petere Exp $ + * $Id: fcache.h,v 1.22 2002/04/27 03:45:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -41,8 +41,6 @@ typedef struct FunctionCache */ FmgrInfo func; - bool permission_ok; - /* * setArgsValid is true when we are evaluating a set-valued function * and we are in the middle of a call series; we want to pass the same diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index 2ad672b1e4..f57219df11 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: lsyscache.h,v 1.49 2002/04/05 00:31:35 tgl Exp $ + * $Id: lsyscache.h,v 1.50 2002/04/27 03:45:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -37,6 +37,7 @@ extern Oid get_commutator(Oid opno); extern Oid get_negator(Oid opno); extern RegProcedure get_oprrest(Oid opno); extern RegProcedure get_oprjoin(Oid opno); +extern char *get_func_name(Oid funcid); extern Oid get_func_rettype(Oid funcid); extern char func_volatile(Oid funcid); extern Oid get_relname_relid(const char *relname, Oid relnamespace); diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out index 8f77a00ad1..c9c013e758 100644 --- a/src/test/regress/expected/privileges.out +++ b/src/test/regress/expected/privileges.out @@ -69,11 +69,11 @@ SELECT * FROM atest2; -- ok INSERT INTO atest1 VALUES (2, 'two'); -- ok INSERT INTO atest2 VALUES ('foo', true); -- fail -ERROR: atest2: Permission denied. +ERROR: atest2: permission denied INSERT INTO atest1 SELECT 1, b FROM atest1; -- ok UPDATE atest1 SET a = 1 WHERE a = 2; -- ok UPDATE atest2 SET col2 = NOT col2; -- fail -ERROR: atest2: Permission denied. +ERROR: atest2: permission denied SELECT * FROM atest1 FOR UPDATE; -- ok a | b ---+----- @@ -82,15 +82,15 @@ SELECT * FROM atest1 FOR UPDATE; -- ok (2 rows) SELECT * FROM atest2 FOR UPDATE; -- fail -ERROR: atest2: Permission denied. +ERROR: atest2: permission denied DELETE FROM atest2; -- fail -ERROR: atest2: Permission denied. +ERROR: atest2: permission denied LOCK atest2 IN ACCESS EXCLUSIVE MODE; -- fail -ERROR: LOCK TABLE: permission denied +ERROR: atest2: permission denied COPY atest2 FROM stdin; -- fail -ERROR: atest2: Permission denied. +ERROR: atest2: permission denied GRANT ALL ON atest1 TO PUBLIC; -- fail -ERROR: atest1: permission denied +ERROR: atest1: must be owner -- checks in subquery, both ok SELECT * FROM atest1 WHERE ( b IN ( SELECT col1 FROM atest2 ) ); a | b @@ -117,33 +117,33 @@ SELECT * FROM atest1; -- ok (2 rows) SELECT * FROM atest2; -- fail -ERROR: atest2: Permission denied. +ERROR: atest2: permission denied INSERT INTO atest1 VALUES (2, 'two'); -- fail -ERROR: atest1: Permission denied. +ERROR: atest1: permission denied INSERT INTO atest2 VALUES ('foo', true); -- fail -ERROR: atest2: Permission denied. +ERROR: atest2: permission denied INSERT INTO atest1 SELECT 1, b FROM atest1; -- fail -ERROR: atest1: Permission denied. +ERROR: atest1: permission denied UPDATE atest1 SET a = 1 WHERE a = 2; -- fail -ERROR: atest1: Permission denied. +ERROR: atest1: permission denied UPDATE atest2 SET col2 = NULL; -- ok UPDATE atest2 SET col2 = NOT col2; -- fails; requires SELECT on atest2 -ERROR: atest2: Permission denied. +ERROR: atest2: permission denied UPDATE atest2 SET col2 = true WHERE atest1.a = 5; -- ok SELECT * FROM atest1 FOR UPDATE; -- fail -ERROR: atest1: Permission denied. +ERROR: atest1: permission denied SELECT * FROM atest2 FOR UPDATE; -- fail -ERROR: atest2: Permission denied. +ERROR: atest2: permission denied DELETE FROM atest2; -- fail -ERROR: atest2: Permission denied. +ERROR: atest2: permission denied LOCK atest2 IN ACCESS EXCLUSIVE MODE; -- ok COPY atest2 FROM stdin; -- fail -ERROR: atest2: Permission denied. +ERROR: atest2: permission denied -- checks in subquery, both fail SELECT * FROM atest1 WHERE ( b IN ( SELECT col1 FROM atest2 ) ); -ERROR: atest2: Permission denied. +ERROR: atest2: permission denied SELECT * FROM atest2 WHERE ( col1 IN ( SELECT b FROM atest1 ) ); -ERROR: atest2: Permission denied. +ERROR: atest2: permission denied SET SESSION AUTHORIZATION regressuser4; COPY atest2 FROM stdin; -- ok SELECT * FROM atest1; -- ok @@ -159,7 +159,7 @@ CREATE TABLE atest3 (one int, two int, three int); GRANT DELETE ON atest3 TO GROUP regressgroup2; SET SESSION AUTHORIZATION regressuser1; SELECT * FROM atest3; -- fail -ERROR: atest3: Permission denied. +ERROR: atest3: permission denied DELETE FROM atest3; -- ok -- views SET SESSION AUTHORIZATION regressuser3; @@ -214,10 +214,10 @@ SELECT testfunc1(5), testfunc2(5); -- ok (1 row) CREATE FUNCTION testfunc3(int) RETURNS int AS 'select 2 * $1;' LANGUAGE sql; -- fail -ERROR: permission denied +ERROR: sql: permission denied SET SESSION AUTHORIZATION regressuser3; SELECT testfunc1(5); -- fail -ERROR: permission denied +ERROR: testfunc1: permission denied SET SESSION AUTHORIZATION regressuser4; SELECT testfunc1(5); -- ok testfunc1 @@ -226,7 +226,7 @@ SELECT testfunc1(5); -- ok (1 row) DROP FUNCTION testfunc1(int); -- fail -ERROR: RemoveFunction: function 'testfunc1': permission denied +ERROR: testfunc1: must be owner \c - DROP FUNCTION testfunc1(int); -- ok -- restore to sanity