summaryrefslogtreecommitdiff
path: root/src/backend/catalog/aclchk.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/catalog/aclchk.c')
-rw-r--r--src/backend/catalog/aclchk.c568
1 files changed, 531 insertions, 37 deletions
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 932eeeedbf..0a1948dea2 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.53 2001/11/05 17:46:24 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/aclchk.c,v 1.54 2002/02/18 23:11:07 petere Exp $
*
* NOTES
* See acl.h.
@@ -18,22 +18,35 @@
#include "postgres.h"
#include "access/heapam.h"
+#include "access/transam.h"
#include "catalog/catalog.h"
#include "catalog/catname.h"
#include "catalog/indexing.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_group.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 "miscadmin.h"
+#include "nodes/parsenodes.h"
+#include "parser/keywords.h"
+#include "parser/parse.h"
#include "parser/parse_agg.h"
#include "parser/parse_func.h"
+#include "parser/parse_expr.h"
#include "utils/acl.h"
#include "utils/syscache.h"
#include "utils/temprel.h"
+
+static void ExecuteGrantStmt_Table(GrantStmt *stmt);
+static void ExecuteGrantStmt_Function(GrantStmt *stmt);
+static void ExecuteGrantStmt_Lang(GrantStmt *stmt);
+
+static const char *privilege_token_string(int token);
+
static int32 aclcheck(Acl *acl, AclId id, AclIdType idtype, AclMode mode);
/* warning messages, now more explicit. */
@@ -65,18 +78,127 @@ dumpacl(Acl *acl)
/*
+ * If is_grant is true, adds the given privileges for the list of
+ * grantees to the existing old_acl. If is_grant is false, the
+ * privileges for the given grantees are removed from old_acl.
+ */
+static Acl*
+merge_acl_with_grant(Acl *old_acl, bool is_grant, List *grantees, char *privileges)
+{
+ List *j;
+ Acl *new_acl;
+
+#ifdef ACLDEBUG
+ dumpacl(old_acl);
+#endif
+ new_acl = old_acl;
+
+ foreach(j, grantees)
+ {
+ PrivGrantee *grantee = (PrivGrantee *) lfirst(j);
+ char *granteeString;
+ char *aclString;
+ AclItem aclitem;
+ unsigned modechg;
+
+ if (grantee->username)
+ granteeString = aclmakeuser("U", grantee->username);
+ else if (grantee->groupname)
+ granteeString = aclmakeuser("G", grantee->groupname);
+ else
+ granteeString = aclmakeuser("A", "");
+
+ aclString = makeAclString(privileges, granteeString,
+ is_grant ? '+' : '-');
+
+ /* Convert string ACL spec into internal form */
+ aclparse(aclString, &aclitem, &modechg);
+ new_acl = aclinsert3(new_acl, &aclitem, modechg);
+
+#ifdef ACLDEBUG
+ dumpacl(new_acl);
+#endif
+ }
+
+ return new_acl;
+}
+
+
+/*
* Called to execute the utility commands GRANT and REVOKE
*/
void
ExecuteGrantStmt(GrantStmt *stmt)
{
- List *i;
- List *j;
-
/* see comment in pg_type.h */
Assert(ACLITEMSIZE == sizeof(AclItem));
- foreach(i, stmt->relnames)
+ switch(stmt->objtype)
+ {
+ case TABLE:
+ ExecuteGrantStmt_Table(stmt);
+ break;
+ case FUNCTION:
+ ExecuteGrantStmt_Function(stmt);
+ break;
+ case LANGUAGE:
+ ExecuteGrantStmt_Lang(stmt);
+ break;
+ default:
+ elog(ERROR, "bogus GrantStmt.objtype %d", stmt->objtype);
+ }
+}
+
+
+static void
+ExecuteGrantStmt_Table(GrantStmt *stmt)
+{
+ List *i;
+ char *privstring;
+
+ if (lfirsti(stmt->privileges) == ALL)
+ privstring = aclmakepriv(ACL_MODE_STR, 0);
+ else
+ {
+ privstring = "";
+ foreach(i, stmt->privileges)
+ {
+ int c = 0;
+
+ switch(lfirsti(i))
+ {
+ case SELECT:
+ c = ACL_MODE_SELECT_CHR;
+ break;
+ case INSERT:
+ c = ACL_MODE_INSERT_CHR;
+ break;
+ case UPDATE:
+ c = ACL_MODE_UPDATE_CHR;
+ break;
+ case DELETE:
+ c = ACL_MODE_DELETE_CHR;
+ break;
+ case RULE:
+ c = ACL_MODE_RULE_CHR;
+ break;
+ case REFERENCES:
+ c = ACL_MODE_REFERENCES_CHR;
+ break;
+ case TRIGGER:
+ c = ACL_MODE_TRIGGER_CHR;
+ break;
+ default:
+ elog(ERROR, "invalid privilege type %s for table object",
+ privilege_token_string(lfirsti(i)));
+ }
+
+ privstring = aclmakepriv(privstring, c);
+ }
+ }
+
+
+ foreach(i, stmt->objects)
{
char *relname = strVal(lfirst(i));
Relation relation;
@@ -120,41 +242,13 @@ ExecuteGrantStmt(GrantStmt *stmt)
aclDatum = SysCacheGetAttr(RELNAME, tuple, Anum_pg_class_relacl,
&isNull);
if (isNull)
- old_acl = acldefault(relname, pg_class_tuple->relowner);
+ old_acl = acldefault(pg_class_tuple->relowner);
else
/* get a detoasted copy of the rel's ACL */
old_acl = DatumGetAclPCopy(aclDatum);
-#ifdef ACLDEBUG
- dumpacl(old_acl);
-#endif
- new_acl = old_acl;
-
- foreach(j, stmt->grantees)
- {
- PrivGrantee *grantee = (PrivGrantee *) lfirst(j);
- char *granteeString;
- char *aclString;
- AclItem aclitem;
- unsigned modechg;
-
- if (grantee->username)
- granteeString = aclmakeuser("U", grantee->username);
- else if (grantee->groupname)
- granteeString = aclmakeuser("G", grantee->groupname);
- else
- granteeString = aclmakeuser("A", "");
-
- aclString = makeAclString(stmt->privileges, granteeString,
- stmt->is_grant ? '+' : '-');
-
- /* Convert string ACL spec into internal form */
- aclparse(aclString, &aclitem, &modechg);
- new_acl = aclinsert3(new_acl, &aclitem, modechg);
-#ifdef ACLDEBUG
- dumpacl(new_acl);
-#endif
- }
+ new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
+ stmt->grantees, privstring);
/* finished building new ACL value, now insert it */
for (i = 0; i < Natts_pg_class; ++i)
@@ -190,6 +284,267 @@ ExecuteGrantStmt(GrantStmt *stmt)
}
+static Oid
+find_function_with_arglist(char *name, List *arguments)
+{
+ Oid oid;
+ Oid argoids[FUNC_MAX_ARGS];
+ int i;
+ int16 argcount;
+
+ MemSet(argoids, 0, FUNC_MAX_ARGS * sizeof(Oid));
+ argcount = length(arguments);
+ if (argcount > FUNC_MAX_ARGS)
+ elog(ERROR, "functions cannot have more than %d arguments",
+ FUNC_MAX_ARGS);
+
+ for (i = 0; i < argcount; i++)
+ {
+ TypeName *t = (TypeName *) lfirst(arguments);
+ char *typnam = TypeNameToInternalName(t);
+
+ arguments = lnext(arguments);
+
+ if (strcmp(typnam, "opaque") == 0)
+ argoids[i] = InvalidOid;
+ else
+ {
+ argoids[i] = GetSysCacheOid(TYPENAME,
+ PointerGetDatum(typnam),
+ 0, 0, 0);
+ if (!OidIsValid(argoids[i]))
+ elog(ERROR, "type '%s' not found", typnam);
+ }
+ }
+
+ oid = GetSysCacheOid(PROCNAME,
+ PointerGetDatum(name),
+ Int16GetDatum(argcount),
+ PointerGetDatum(argoids),
+ 0);
+
+ if (!OidIsValid(oid))
+ func_error(NULL, name, argcount, argoids, NULL);
+
+ return oid;
+}
+
+
+static void
+ExecuteGrantStmt_Function(GrantStmt *stmt)
+{
+ List *i;
+ char *privstring = NULL;
+
+ if (lfirsti(stmt->privileges) == ALL)
+ privstring = aclmakepriv("", ACL_MODE_SELECT_CHR);
+ else
+ {
+ foreach(i, stmt->privileges)
+ {
+ if (lfirsti(i) != EXECUTE)
+ elog(ERROR, "invalid privilege type %s for function object",
+ privilege_token_string(lfirsti(i)));
+ }
+
+ privstring = aclmakepriv("", ACL_MODE_SELECT_CHR);
+ }
+
+ foreach(i, stmt->objects)
+ {
+ FuncWithArgs *func = (FuncWithArgs *) lfirst(i);
+ Oid oid;
+ Relation relation;
+ HeapTuple tuple;
+ Form_pg_proc pg_proc_tuple;
+ Datum aclDatum;
+ bool isNull;
+ Acl *old_acl;
+ Acl *new_acl;
+ unsigned i;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_proc];
+ char nulls[Natts_pg_proc];
+ char replaces[Natts_pg_proc];
+
+ oid = find_function_with_arglist(func->funcname, func->funcargs);
+ relation = heap_openr(ProcedureRelationName, RowExclusiveLock);
+ tuple = SearchSysCache(PROCOID, ObjectIdGetDatum(oid), 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ {
+ heap_close(relation, RowExclusiveLock);
+ elog(ERROR, "function %u not found", oid);
+ }
+ pg_proc_tuple = (Form_pg_proc) GETSTRUCT(tuple);
+
+ if (pg_proc_tuple->proowner != GetUserId())
+ elog(ERROR, "permission denied");
+
+ /*
+ * If there's no ACL, create a default using the pg_proc.proowner
+ * field.
+ */
+ aclDatum = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_proacl,
+ &isNull);
+ if (isNull)
+ old_acl = acldefault(pg_proc_tuple->proowner);
+ else
+ /* get a detoasted copy of the rel's ACL */
+ old_acl = DatumGetAclPCopy(aclDatum);
+
+ new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
+ stmt->grantees, privstring);
+
+ /* finished building new ACL value, now insert it */
+ for (i = 0; i < Natts_pg_proc; ++i)
+ {
+ replaces[i] = ' ';
+ nulls[i] = ' '; /* ignored if replaces[i]==' ' anyway */
+ values[i] = (Datum) NULL; /* ignored if replaces[i]==' '
+ * anyway */
+ }
+ replaces[Anum_pg_proc_proacl - 1] = 'r';
+ values[Anum_pg_proc_proacl - 1] = PointerGetDatum(new_acl);
+ newtuple = heap_modifytuple(tuple, relation, values, nulls, replaces);
+
+ ReleaseSysCache(tuple);
+
+ simple_heap_update(relation, &newtuple->t_self, newtuple);
+
+ {
+ /* keep the catalog indexes up to date */
+ Relation idescs[Num_pg_proc_indices];
+
+ CatalogOpenIndices(Num_pg_proc_indices, Name_pg_proc_indices,
+ idescs);
+ CatalogIndexInsert(idescs, Num_pg_proc_indices, relation, newtuple);
+ CatalogCloseIndices(Num_pg_proc_indices, idescs);
+ }
+
+ pfree(old_acl);
+ pfree(new_acl);
+
+ heap_close(relation, RowExclusiveLock);
+ }
+}
+
+
+static void
+ExecuteGrantStmt_Lang(GrantStmt *stmt)
+{
+ List *i;
+ char *privstring = NULL;
+
+ if (lfirsti(stmt->privileges) == ALL)
+ privstring = aclmakepriv("", ACL_MODE_SELECT_CHR);
+ else
+ {
+ foreach(i, stmt->privileges)
+ {
+ if (lfirsti(i) != USAGE)
+ elog(ERROR, "invalid privilege type %s for language object",
+ privilege_token_string(lfirsti(i)));
+ }
+
+ privstring = aclmakepriv("", ACL_MODE_SELECT_CHR);
+ }
+
+ foreach(i, stmt->objects)
+ {
+ char *langname = strVal(lfirst(i));
+ Relation relation;
+ HeapTuple tuple;
+ Form_pg_language pg_language_tuple;
+ Datum aclDatum;
+ bool isNull;
+ Acl *old_acl;
+ Acl *new_acl;
+ unsigned i;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_language];
+ char nulls[Natts_pg_language];
+ char replaces[Natts_pg_language];
+
+ if (!superuser())
+ elog(ERROR, "permission denied");
+
+ relation = heap_openr(LanguageRelationName, RowExclusiveLock);
+ tuple = SearchSysCache(LANGNAME, PointerGetDatum(langname), 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ {
+ heap_close(relation, RowExclusiveLock);
+ elog(ERROR, "language \"%s\" not found", langname);
+ }
+ pg_language_tuple = (Form_pg_language) GETSTRUCT(tuple);
+
+ if (!pg_language_tuple->lanpltrusted)
+ {
+ heap_close(relation, RowExclusiveLock);
+ elog(ERROR, "language \"%s\" is not trusted", langname);
+ }
+
+ /*
+ * If there's no ACL, create a default.
+ */
+ aclDatum = SysCacheGetAttr(LANGNAME, tuple, Anum_pg_language_lanacl,
+ &isNull);
+ if (isNull)
+ old_acl = acldefault(InvalidOid);
+ else
+ /* get a detoasted copy of the rel's ACL */
+ old_acl = DatumGetAclPCopy(aclDatum);
+
+ new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
+ stmt->grantees, privstring);
+
+ /* finished building new ACL value, now insert it */
+ for (i = 0; i < Natts_pg_language; ++i)
+ {
+ replaces[i] = ' ';
+ nulls[i] = ' '; /* ignored if replaces[i]==' ' anyway */
+ values[i] = (Datum) NULL; /* ignored if replaces[i]==' '
+ * anyway */
+ }
+ replaces[Anum_pg_language_lanacl - 1] = 'r';
+ values[Anum_pg_language_lanacl - 1] = PointerGetDatum(new_acl);
+ newtuple = heap_modifytuple(tuple, relation, values, nulls, replaces);
+
+ ReleaseSysCache(tuple);
+
+ simple_heap_update(relation, &newtuple->t_self, newtuple);
+
+ {
+ /* keep the catalog indexes up to date */
+ Relation idescs[Num_pg_language_indices];
+
+ CatalogOpenIndices(Num_pg_language_indices, Name_pg_language_indices,
+ idescs);
+ CatalogIndexInsert(idescs, Num_pg_language_indices, relation, newtuple);
+ CatalogCloseIndices(Num_pg_language_indices, idescs);
+ }
+
+ pfree(old_acl);
+ pfree(new_acl);
+
+ heap_close(relation, RowExclusiveLock);
+ }
+}
+
+
+
+static const char *
+privilege_token_string(int token)
+{
+ const char *s = TokenString(token);
+
+ if (s)
+ return s;
+ else
+ elog(ERROR, "privilege_token_string: invalid token number");
+ return NULL; /* appease compiler */
+}
+
+
AclId
get_grosysid(char *groname)
@@ -483,7 +838,7 @@ pg_aclcheck(char *relname, Oid userid, AclMode mode)
AclId ownerId;
ownerId = ((Form_pg_class) GETSTRUCT(tuple))->relowner;
- acl = acldefault(relname, ownerId);
+ acl = acldefault(ownerId);
aclDatum = (Datum) 0;
}
else
@@ -721,3 +1076,142 @@ pg_aggr_ownercheck(Oid userid,
return userid == owner_id;
}
+
+
+
+/*
+ * Exported routine for checking a user's access privileges to a function
+ *
+ * Returns an ACLCHECK_* result code.
+ */
+int32
+pg_proc_aclcheck(Oid proc_oid, Oid userid)
+{
+ int32 result;
+ HeapTuple tuple;
+ Datum aclDatum;
+ bool isNull;
+ Acl *acl;
+
+ if (superuser_arg(userid))
+ return ACLCHECK_OK;
+
+ /*
+ * Validate userid
+ */
+ tuple = SearchSysCache(SHADOWSYSID,
+ ObjectIdGetDatum(userid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "pg_proc_aclcheck: invalid user id %u",
+ (unsigned) userid);
+ ReleaseSysCache(tuple);
+
+ /*
+ * Normal case: get the function's ACL from pg_proc
+ */
+ tuple = SearchSysCache(PROCOID,
+ ObjectIdGetDatum(proc_oid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "pg_proc_aclcheck: function %u not found", proc_oid);
+
+ aclDatum = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_proacl,
+ &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ AclId ownerId;
+
+ ownerId = ((Form_pg_proc) GETSTRUCT(tuple))->proowner;
+ acl = acldefault(ownerId);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ /*
+ * Functions only have one kind of privilege, which is encoded as
+ * "SELECT" here.
+ */
+ result = aclcheck(acl, userid, (AclIdType) ACL_IDTYPE_UID, ACL_SELECT);
+
+ /* if we have a detoasted copy, free it */
+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
+ pfree(acl);
+
+ ReleaseSysCache(tuple);
+
+ return result;
+}
+
+
+
+/*
+ * Exported routine for checking a user's access privileges to a language
+ *
+ * Returns an ACLCHECK_* result code.
+ */
+int32
+pg_language_aclcheck(Oid lang_oid, Oid userid)
+{
+ int32 result;
+ HeapTuple tuple;
+ Datum aclDatum;
+ bool isNull;
+ Acl *acl;
+
+ if (superuser_arg(userid))
+ return ACLCHECK_OK;
+
+ /*
+ * Validate userid
+ */
+ tuple = SearchSysCache(SHADOWSYSID,
+ ObjectIdGetDatum(userid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "pg_language_aclcheck: invalid user id %u",
+ (unsigned) userid);
+ ReleaseSysCache(tuple);
+
+ /*
+ * Normal case: get the function's ACL from pg_language
+ */
+ tuple = SearchSysCache(LANGOID,
+ ObjectIdGetDatum(lang_oid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "pg_language_aclcheck: language %u not found", lang_oid);
+
+ aclDatum = SysCacheGetAttr(LANGOID, tuple, Anum_pg_language_lanacl,
+ &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(InvalidOid);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ /*
+ * Languages only have one kind of privilege, which is encoded as
+ * "SELECT" here.
+ */
+ result = aclcheck(acl, userid, (AclIdType) ACL_IDTYPE_UID, ACL_SELECT);
+
+ /* if we have a detoasted copy, free it */
+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
+ pfree(acl);
+
+ ReleaseSysCache(tuple);
+
+ return result;
+}