*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.97 2004/01/14 03:44:53 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.98 2004/05/11 17:36:12 tgl Exp $
*
* NOTES
* See acl.h.
static const char *privilege_to_string(AclMode privilege);
-static AclResult aclcheck(Acl *acl, AclId userid, AclMode mode);
+static AclMode aclmask(Acl *acl, AclId userid,
+ AclMode mask, AclMaskHow how);
#ifdef ACLDEBUG
/*
- * aclcheck
+ * aclmask --- compute bitmask of all privileges held by userid.
*
- * Returns ACLCHECK_OK if the 'userid' has ACL entries in 'acl' to
- * satisfy any one of the requirements of 'mode'. Returns an
- * appropriate ACLCHECK_* error code otherwise.
+ * When 'how' = ACLMASK_ALL, this simply returns the privilege bits
+ * held by the given userid according to the given ACL list, ANDed
+ * with 'mask'. (The point of passing 'mask' is to let the routine
+ * exit early if all privileges of interest have been found.)
+ *
+ * When 'how' = ACLMASK_ANY, returns as soon as any bit in the mask
+ * is known true. (This lets us exit soonest in cases where the
+ * caller is only going to test for zero or nonzero result.)
+ *
+ * Usage patterns:
+ *
+ * To see if any of a set of privileges are held:
+ * if (aclmask(acl, userid, privs, ACLMASK_ANY) != 0)
+ *
+ * To see if all of a set of privileges are held:
+ * if (aclmask(acl, userid, privs, ACLMASK_ALL) == privs)
+ *
+ * To determine exactly which of a set of privileges are held:
+ * heldprivs = aclmask(acl, userid, privs, ACLMASK_ALL);
*/
-static AclResult
-aclcheck(Acl *acl, AclId userid, AclMode mode)
+static AclMode
+aclmask(Acl *acl, AclId userid, AclMode mask, AclMaskHow how)
{
+ AclMode result;
+ AclMode remaining;
AclItem *aidat;
int i,
num;
* appropriate default
*/
if (acl == NULL)
- {
elog(ERROR, "null ACL");
- return ACLCHECK_NO_PRIV;
- }
+
+ /* Quick exit for mask == 0 */
+ if (mask == 0)
+ return 0;
num = ACL_NUM(acl);
aidat = ACL_DAT(acl);
+ result = 0;
+
/*
- * See if privilege is granted directly to user or to public
+ * Check privileges granted directly to user or to public
*/
for (i = 0; i < num; i++)
- if (ACLITEM_GET_IDTYPE(aidat[i]) == ACL_IDTYPE_WORLD
- || (ACLITEM_GET_IDTYPE(aidat[i]) == ACL_IDTYPE_UID
- && aidat[i].ai_grantee == userid))
+ {
+ AclItem *aidata = &aidat[i];
+
+ if (ACLITEM_GET_IDTYPE(*aidata) == ACL_IDTYPE_WORLD
+ || (ACLITEM_GET_IDTYPE(*aidata) == ACL_IDTYPE_UID
+ && aidata->ai_grantee == userid))
{
- if (aidat[i].ai_privs & mode)
- return ACLCHECK_OK;
+ result |= (aidata->ai_privs & mask);
+ if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
+ return result;
}
+ }
/*
- * See if he has the permission via any group (do this in a separate
- * pass to avoid expensive(?) lookups in pg_group)
+ * Check privileges granted via groups. We do this in a separate
+ * pass to minimize expensive lookups in pg_group.
*/
+ remaining = (mask & ~result);
for (i = 0; i < num; i++)
- if (ACLITEM_GET_IDTYPE(aidat[i]) == ACL_IDTYPE_GID
- && aidat[i].ai_privs & mode
- && in_group(userid, aidat[i].ai_grantee))
- return ACLCHECK_OK;
+ {
+ AclItem *aidata = &aidat[i];
+
+ if (ACLITEM_GET_IDTYPE(*aidata) == ACL_IDTYPE_GID
+ && (aidata->ai_privs & remaining)
+ && in_group(userid, aidata->ai_grantee))
+ {
+ result |= (aidata->ai_privs & mask);
+ if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
+ return result;
+ remaining = (mask & ~result);
+ }
+ }
- /* If here, doesn't have the privilege. */
- return ACLCHECK_NO_PRIV;
+ return result;
}
/*
- * Exported routine for checking a user's access privileges to a table
+ * Exported routine for examining a user's privileges for a table
+ *
+ * See aclmask() for a description of the API.
*
* Note: we give lookup failure the full ereport treatment because the
* has_table_privilege() family of functions allow users to pass
* any random OID to this function. Likewise for the sibling functions
* below.
*/
-AclResult
-pg_class_aclcheck(Oid table_oid, AclId userid, AclMode mode)
+AclMode
+pg_class_aclmask(Oid table_oid, AclId userid,
+ AclMode mask, AclMaskHow how)
{
- AclResult result;
+ AclMode result;
bool usesuper,
usecatupd;
HeapTuple tuple;
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_TABLE),
- errmsg("relation with OID %u does not exist", table_oid)));
+ errmsg("relation with OID %u does not exist",
+ table_oid)));
classForm = (Form_pg_class) GETSTRUCT(tuple);
/*
* be protected in this way. Assume the view rules can take care
* of themselves.
*/
- if ((mode & (ACL_INSERT | ACL_UPDATE | ACL_DELETE)) &&
+ if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE)) &&
IsSystemClass(classForm) &&
classForm->relkind != RELKIND_VIEW &&
!usecatupd &&
#ifdef ACLDEBUG
elog(DEBUG2, "permission denied for system catalog update");
#endif
- ReleaseSysCache(tuple);
- return ACLCHECK_NO_PRIV;
+ mask &= ~(ACL_INSERT | ACL_UPDATE | ACL_DELETE);
}
/*
elog(DEBUG2, "%u is superuser, home free", userid);
#endif
ReleaseSysCache(tuple);
- return ACLCHECK_OK;
+ return mask;
}
/*
acl = DatumGetAclP(aclDatum);
}
- result = aclcheck(acl, userid, mode);
+ result = aclmask(acl, userid, mask, how);
/* if we have a detoasted copy, free it */
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
}
/*
- * Exported routine for checking a user's access privileges to a database
+ * Exported routine for examining a user's privileges for a database
*/
-AclResult
-pg_database_aclcheck(Oid db_oid, AclId userid, AclMode mode)
+AclMode
+pg_database_aclmask(Oid db_oid, AclId userid,
+ AclMode mask, AclMaskHow how)
{
- AclResult result;
+ AclMode result;
Relation pg_database;
ScanKeyData entry[1];
HeapScanDesc scan;
/* Superusers bypass all permission checking. */
if (superuser_arg(userid))
- return ACLCHECK_OK;
+ return mask;
/*
* Get the database's ACL from pg_database
acl = DatumGetAclP(aclDatum);
}
- result = aclcheck(acl, userid, mode);
+ result = aclmask(acl, userid, mask, how);
/* if we have a detoasted copy, free it */
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
}
/*
- * Exported routine for checking a user's access privileges to a function
+ * Exported routine for examining a user's privileges for a function
*/
-AclResult
-pg_proc_aclcheck(Oid proc_oid, AclId userid, AclMode mode)
+AclMode
+pg_proc_aclmask(Oid proc_oid, AclId userid,
+ AclMode mask, AclMaskHow how)
{
- AclResult result;
+ AclMode result;
HeapTuple tuple;
Datum aclDatum;
bool isNull;
/* Superusers bypass all permission checking. */
if (superuser_arg(userid))
- return ACLCHECK_OK;
+ return mask;
/*
* Get the function's ACL from pg_proc
acl = DatumGetAclP(aclDatum);
}
- result = aclcheck(acl, userid, mode);
+ result = aclmask(acl, userid, mask, how);
/* if we have a detoasted copy, free it */
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
}
/*
- * Exported routine for checking a user's access privileges to a language
+ * Exported routine for examining a user's privileges for a language
*/
-AclResult
-pg_language_aclcheck(Oid lang_oid, AclId userid, AclMode mode)
+AclMode
+pg_language_aclmask(Oid lang_oid, AclId userid,
+ AclMode mask, AclMaskHow how)
{
- AclResult result;
+ AclMode result;
HeapTuple tuple;
Datum aclDatum;
bool isNull;
/* Superusers bypass all permission checking. */
if (superuser_arg(userid))
- return ACLCHECK_OK;
+ return mask;
/*
* Get the language's ACL from pg_language
acl = DatumGetAclP(aclDatum);
}
- result = aclcheck(acl, userid, mode);
+ result = aclmask(acl, userid, mask, how);
/* if we have a detoasted copy, free it */
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
}
/*
- * Exported routine for checking a user's access privileges to a namespace
+ * Exported routine for examining a user's privileges for a namespace
*/
-AclResult
-pg_namespace_aclcheck(Oid nsp_oid, AclId userid, AclMode mode)
+AclMode
+pg_namespace_aclmask(Oid nsp_oid, AclId userid,
+ AclMode mask, AclMaskHow how)
{
- AclResult result;
+ AclMode result;
HeapTuple tuple;
Datum aclDatum;
bool isNull;
* we have all grantable privileges on it.
*/
if (isTempNamespace(nsp_oid))
- return ACLCHECK_OK;
+ return mask;
/* Superusers bypass all permission checking. */
if (superuser_arg(userid))
- return ACLCHECK_OK;
+ return mask;
/*
* Get the schema's ACL from pg_namespace
acl = DatumGetAclP(aclDatum);
}
- result = aclcheck(acl, userid, mode);
+ result = aclmask(acl, userid, mask, how);
/* if we have a detoasted copy, free it */
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
}
+/*
+ * Exported routine for checking a user's access privileges to a table
+ *
+ * Returns ACLCHECK_OK if the user has any of the privileges identified by
+ * 'mode'; otherwise returns a suitable error code (in practice, always
+ * ACLCHECK_NO_PRIV).
+ */
+AclResult
+pg_class_aclcheck(Oid table_oid, AclId userid, AclMode mode)
+{
+ if (pg_class_aclmask(table_oid, userid, mode, ACLMASK_ANY) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+}
+
+/*
+ * Exported routine for checking a user's access privileges to a database
+ */
+AclResult
+pg_database_aclcheck(Oid db_oid, AclId userid, AclMode mode)
+{
+ if (pg_database_aclmask(db_oid, userid, mode, ACLMASK_ANY) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+}
+
+/*
+ * Exported routine for checking a user's access privileges to a function
+ */
+AclResult
+pg_proc_aclcheck(Oid proc_oid, AclId userid, AclMode mode)
+{
+ if (pg_proc_aclmask(proc_oid, userid, mode, ACLMASK_ANY) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+}
+
+/*
+ * Exported routine for checking a user's access privileges to a language
+ */
+AclResult
+pg_language_aclcheck(Oid lang_oid, AclId userid, AclMode mode)
+{
+ if (pg_language_aclmask(lang_oid, userid, mode, ACLMASK_ANY) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+}
+
+/*
+ * Exported routine for checking a user's access privileges to a namespace
+ */
+AclResult
+pg_namespace_aclcheck(Oid nsp_oid, AclId userid, AclMode mode)
+{
+ if (pg_namespace_aclmask(nsp_oid, userid, mode, ACLMASK_ANY) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+}
+
+
/*
* Ownership check for a relation (specified by OID).
*/