*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/aclchk.c,v 1.48 2001/05/27 09:59:28 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/aclchk.c,v 1.49 2001/06/05 19:34:56 tgl Exp $
*
* NOTES
* See acl.h.
#include "utils/acl.h"
#include "utils/syscache.h"
-static int32 aclcheck(char *relname, Acl *acl, AclId id,
- AclIdType idtype, AclMode mode);
+static int32 aclcheck(Acl *acl, AclId id, AclIdType idtype, AclMode mode);
/* warning messages, now more explicit. */
/* MUST correspond to the order of the ACLCHK_* result codes in acl.h. */
return name;
}
+/*
+ * Is user a member of group?
+ */
static bool
in_group(AclId uid, AclId gid)
{
HeapTuple tuple;
Datum att;
bool isNull;
- IdList *tmp;
+ IdList *glist;
AclId *aidp;
int i,
num;
if (!isNull)
{
/* be sure the IdList is not toasted */
- tmp = DatumGetIdListP(att);
+ glist = DatumGetIdListP(att);
/* scan it */
- num = IDLIST_NUM(tmp);
- aidp = IDLIST_DAT(tmp);
+ num = IDLIST_NUM(glist);
+ aidp = IDLIST_DAT(glist);
for (i = 0; i < num; ++i)
{
if (aidp[i] == uid)
break;
}
}
+ /* if IdList was toasted, free detoasted copy */
+ if ((Pointer) glist != DatumGetPointer(att))
+ pfree(glist);
}
ReleaseSysCache(tuple);
}
/*
* aclcheck
- * Returns 1 if the 'id' of type 'idtype' has ACL entries in 'acl' to satisfy
- * any one of the requirements of 'mode'. Returns 0 otherwise.
+ *
+ * Returns ACLCHECK_OK if the 'id' of type 'idtype' has ACL entries in 'acl'
+ * to satisfy any one of the requirements of 'mode'. Returns an appropriate
+ * ACLCHECK_* error code otherwise.
+ *
+ * The ACL list is expected to be sorted in standard order.
*/
static int32
-aclcheck(char *relname, Acl *acl, AclId id, AclIdType idtype, AclMode mode)
+aclcheck(Acl *acl, AclId id, AclIdType idtype, AclMode mode)
{
AclItem *aip,
*aidat;
*/
if (!acl)
{
- elog(DEBUG, "aclcheck: null ACL, returning 1");
+ elog(DEBUG, "aclcheck: null ACL, returning OK");
return ACLCHECK_OK;
}
*/
if (num < 1)
{
- elog(DEBUG, "aclcheck: zero-length ACL, returning 1");
+ elog(DEBUG, "aclcheck: zero-length ACL, returning OK");
+ return ACLCHECK_OK;
+ }
+
+ /*
+ * "World" rights are applicable regardless of the passed-in ID,
+ * and since they're much the cheapest to check, check 'em first.
+ */
+ if (aidat->ai_idtype != ACL_IDTYPE_WORLD)
+ elog(ERROR, "aclcheck: first entry in ACL is not 'world' entry");
+ if (aidat->ai_mode & mode)
+ {
+#ifdef ACLDEBUG
+ elog(DEBUG, "aclcheck: using world=%d", aidat->ai_mode);
+#endif
return ACLCHECK_OK;
}
- Assert(aidat->ai_idtype == ACL_IDTYPE_WORLD);
switch (idtype)
{
case ACL_IDTYPE_UID:
- /* Look for exact match to user */
+ /* See if permission is granted directly to user */
for (i = 1, aip = aidat + 1; /* skip world entry */
i < num && aip->ai_idtype == ACL_IDTYPE_UID;
++i, ++aip)
elog(DEBUG, "aclcheck: found user %u/%d",
aip->ai_id, aip->ai_mode);
#endif
- return (aip->ai_mode & mode) ? ACLCHECK_OK : ACLCHECK_NO_PRIV;
+ if (aip->ai_mode & mode)
+ return ACLCHECK_OK;
}
}
/* See if he has the permission via any group */
}
}
}
- /* Else, look to the world entry */
break;
case ACL_IDTYPE_GID:
/* Look for this group ID */
- for (i = 1, aip = aidat + 1; /* skip world entry and
- * UIDs */
+ for (i = 1, aip = aidat + 1; /* skip world entry */
i < num && aip->ai_idtype == ACL_IDTYPE_UID;
++i, ++aip)
- ;
+ /* skip UID entry */;
for (;
i < num && aip->ai_idtype == ACL_IDTYPE_GID;
++i, ++aip)
elog(DEBUG, "aclcheck: found group %u/%d",
aip->ai_id, aip->ai_mode);
#endif
- return (aip->ai_mode & mode) ? ACLCHECK_OK : ACLCHECK_NO_PRIV;
+ if (aip->ai_mode & mode)
+ return ACLCHECK_OK;
}
}
- /* Else, look to the world entry */
break;
case ACL_IDTYPE_WORLD:
/* Only check the world entry */
break;
}
-#ifdef ACLDEBUG
- elog(DEBUG, "aclcheck: using world=%d", aidat->ai_mode);
-#endif
- return (aidat->ai_mode & mode) ? ACLCHECK_OK : ACLCHECK_NO_PRIV;
+ /* If get here, he doesn't have the privilege nohow */
+ return ACLCHECK_NO_PRIV;
}
+/*
+ * Exported routine for checking a user's access privileges to a table
+ *
+ * Returns an ACLCHECK_* result code.
+ */
int32
pg_aclcheck(char *relname, Oid userid, AclMode mode)
{
bool isNull;
Acl *acl;
+ /*
+ * Validate userid, find out if he is superuser
+ */
tuple = SearchSysCache(SHADOWSYSID,
ObjectIdGetDatum(userid),
0, 0, 0);
* pg_shadow.usecatupd is set. (This is to let superusers protect
* themselves from themselves.)
*/
- if (((mode & ACL_UPDATE) || (mode & ACL_INSERT) || (mode & ACL_DELETE)) &&
+ if ((mode & (ACL_INSERT | ACL_UPDATE | ACL_DELETE)) &&
!allowSystemTableMods && IsSystemRelationName(relname) &&
strncmp(relname, "pg_temp.", strlen("pg_temp.")) != 0 &&
!((Form_pg_shadow) GETSTRUCT(tuple))->usecatupd)
{
+#ifdef ACLDEBUG
elog(DEBUG, "pg_aclcheck: catalog update to \"%s\": permission denied",
relname);
+#endif
ReleaseSysCache(tuple);
return ACLCHECK_NO_PRIV;
}
ownerId = ((Form_pg_class) GETSTRUCT(tuple))->relowner;
acl = acldefault(relname, ownerId);
+ aclDatum = (Datum) 0;
}
else
{
- /* get a detoasted copy of the rel's ACL */
- acl = DatumGetAclPCopy(aclDatum);
+ /* detoast rel's ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
}
- result = aclcheck(relname, acl, userid, (AclIdType) ACL_IDTYPE_UID, mode);
+ result = aclcheck(acl, userid, (AclIdType) ACL_IDTYPE_UID, mode);
- if (acl)
+ /* if we have a detoasted copy, free it */
+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
pfree(acl);
+
ReleaseSysCache(tuple);
return result;
}
-int32
+/*
+ * Check ownership of an object identified by name (which will be looked
+ * up in the system cache identified by cacheid).
+ *
+ * Returns true if userid owns the item, or should be allowed to modify
+ * the item as if he owned it.
+ */
+bool
pg_ownercheck(Oid userid,
- const char *value,
+ const char *name,
int cacheid)
{
HeapTuple tuple;
usename);
#endif
ReleaseSysCache(tuple);
- return 1;
+ return true;
}
ReleaseSysCache(tuple);
/* caution: usename is inaccessible beyond this point... */
tuple = SearchSysCache(cacheid,
- PointerGetDatum(value),
+ PointerGetDatum(name),
0, 0, 0);
switch (cacheid)
{
- case OPEROID:
- if (!HeapTupleIsValid(tuple))
- elog(ERROR, "pg_ownercheck: operator %ld not found",
- PointerGetDatum(value));
- owner_id = ((Form_pg_operator) GETSTRUCT(tuple))->oprowner;
- break;
- case PROCNAME:
- if (!HeapTupleIsValid(tuple))
- elog(ERROR, "pg_ownercheck: function \"%s\" not found",
- value);
- owner_id = ((Form_pg_proc) GETSTRUCT(tuple))->proowner;
- break;
case RELNAME:
if (!HeapTupleIsValid(tuple))
elog(ERROR, "pg_ownercheck: class \"%s\" not found",
- value);
+ name);
owner_id = ((Form_pg_class) GETSTRUCT(tuple))->relowner;
break;
case TYPENAME:
if (!HeapTupleIsValid(tuple))
elog(ERROR, "pg_ownercheck: type \"%s\" not found",
- value);
+ name);
owner_id = ((Form_pg_type) GETSTRUCT(tuple))->typowner;
break;
default:
return userid == owner_id;
}
-int32
+/*
+ * Ownership check for an operator (specified by OID).
+ */
+bool
+pg_oper_ownercheck(Oid userid, Oid oprid)
+{
+ HeapTuple tuple;
+ AclId owner_id;
+ char *usename;
+
+ tuple = SearchSysCache(SHADOWSYSID,
+ ObjectIdGetDatum(userid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "pg_oper_ownercheck: invalid user id %u",
+ (unsigned) userid);
+ usename = NameStr(((Form_pg_shadow) GETSTRUCT(tuple))->usename);
+
+ /*
+ * Superusers bypass all permission-checking.
+ */
+ if (((Form_pg_shadow) GETSTRUCT(tuple))->usesuper)
+ {
+#ifdef ACLDEBUG
+ elog(DEBUG, "pg_ownercheck: user \"%s\" is superuser",
+ usename);
+#endif
+ ReleaseSysCache(tuple);
+ return true;
+ }
+
+ ReleaseSysCache(tuple);
+ /* caution: usename is inaccessible beyond this point... */
+
+ tuple = SearchSysCache(OPEROID,
+ ObjectIdGetDatum(oprid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "pg_ownercheck: operator %u not found",
+ oprid);
+
+ owner_id = ((Form_pg_operator) GETSTRUCT(tuple))->oprowner;
+
+ ReleaseSysCache(tuple);
+
+ return userid == owner_id;
+}
+
+/*
+ * Ownership check for a function (specified by name and argument types).
+ */
+bool
pg_func_ownercheck(Oid userid,
char *funcname,
int nargs,
usename);
#endif
ReleaseSysCache(tuple);
- return 1;
+ return true;
}
ReleaseSysCache(tuple);
return userid == owner_id;
}
-int32
+/*
+ * Ownership check for an aggregate function (specified by name and
+ * argument type).
+ */
+bool
pg_aggr_ownercheck(Oid userid,
char *aggname,
Oid basetypeID)
usename);
#endif
ReleaseSysCache(tuple);
- return 1;
+ return true;
}
ReleaseSysCache(tuple);
* Copyright (c) 1999, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.28 2001/05/27 09:59:29 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.29 2001/06/05 19:34:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*** First, validate user ***/
-#ifndef NO_SECURITY
if (!pg_ownercheck(GetUserId(), type, TYPENAME))
- {
elog(ERROR, "you are not permitted to comment on type '%s'",
type);
- }
-#endif
/*** Next, find the type's oid ***/
/*** Next, validate the user's attempt to comment ***/
-#ifndef NO_SECURITY
if (!pg_aggr_ownercheck(GetUserId(), aggregate, baseoid))
{
if (aggtypename)
- {
elog(ERROR, "you are not permitted to comment on aggregate '%s' %s '%s'",
aggregate, "with type", aggtypename);
- }
else
- {
elog(ERROR, "you are not permitted to comment on aggregate '%s'",
aggregate);
- }
}
-#endif
/*** Now, attempt to find the actual tuple in pg_aggregate ***/
/*** Now, validate the user's ability to comment on this function ***/
-#ifndef NO_SECURITY
if (!pg_func_ownercheck(GetUserId(), function, argcount, argoids))
elog(ERROR, "you are not permitted to comment on function '%s'",
function);
-#endif
/*** Now, find the corresponding oid for this procedure ***/
/*** Valid user's ability to comment on this operator ***/
-#ifndef NO_SECURITY
- if (!pg_ownercheck(GetUserId(), (char *) ObjectIdGetDatum(oid), OPEROID))
- {
+ if (!pg_oper_ownercheck(GetUserId(), oid))
elog(ERROR, "you are not permitted to comment on operator '%s'",
opername);
- }
-#endif
/*** Get the procedure associated with the operator ***/
/*** First, validate the user's action ***/
-#ifndef NO_SECURITY
if (!pg_ownercheck(GetUserId(), relname, RELNAME))
- {
elog(ERROR, "you are not permitted to comment on trigger '%s' %s '%s'",
trigger, "defined for relation", relname);
- }
-#endif
/*** Now, fetch the trigger oid from pg_trigger ***/
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.59 2001/05/27 09:59:30 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.60 2001/06/05 19:34:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
-#include <ctype.h>
-
#include "postgres.h"
+#include <ctype.h>
+
#include "access/heapam.h"
#include "catalog/catalog.h"
#include "catalog/pg_shadow.h"
/*
- * Add or replace an item in an ACL array.
+ * Add or replace an item in an ACL array. The result is a modified copy;
+ * the input object is not changed.
*
* NB: caller is responsible for having detoasted the input ACL, if needed.
*/
Acl *new_acl;
AclItem *old_aip,
*new_aip;
- int src,
- dst,
+ int dst,
num;
/* These checks for null input are probably dead code, but... */
if (dst < num && aclitemeq(mod_aip, old_aip + dst))
{
- /* modify in-place */
+ /* found a match, so modify existing item */
new_acl = makeacl(num);
new_aip = ACL_DAT(new_acl);
memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
- src = dst;
}
else
{
+ /* need to insert a new item */
new_acl = makeacl(num + 1);
new_aip = ACL_DAT(new_acl);
if (dst == 0)
(char *) (old_aip + dst),
(num - dst) * sizeof(AclItem));
}
+ /* initialize the new entry with no permissions */
new_aip[dst].ai_id = mod_aip->ai_id;
new_aip[dst].ai_idtype = mod_aip->ai_idtype;
+ new_aip[dst].ai_mode = 0;
num++; /* set num to the size of new_acl */
- src = 0; /* if add or del, start from world entry */
}
/* apply the permissions mod */
switch (modechg)
{
case ACL_MODECHG_ADD:
- new_aip[dst].ai_mode = old_aip[src].ai_mode | mod_aip->ai_mode;
+ new_aip[dst].ai_mode |= mod_aip->ai_mode;
break;
case ACL_MODECHG_DEL:
- new_aip[dst].ai_mode = old_aip[src].ai_mode & ~mod_aip->ai_mode;
+ new_aip[dst].ai_mode &= ~mod_aip->ai_mode;
break;
case ACL_MODECHG_EQL:
new_aip[dst].ai_mode = mod_aip->ai_mode;
*/
if (new_aip[dst].ai_mode == 0 && dst > 0)
{
- int i;
-
- for (i = dst + 1; i < num; i++)
- {
- new_aip[i - 1].ai_id = new_aip[i].ai_id;
- new_aip[i - 1].ai_idtype = new_aip[i].ai_idtype;
- new_aip[i - 1].ai_mode = new_aip[i].ai_mode;
- }
+ memmove((char *) (new_aip + dst),
+ (char *) (new_aip + dst + 1),
+ (num - dst - 1) * sizeof(AclItem));
ARR_DIMS(new_acl)[0] = num - 1;
- /* Adjust also the array size because it is used for memcpy */
ARR_SIZE(new_acl) -= sizeof(AclItem);
}