Align GRANT/REVOKE behavior more closely with the SQL spec, per discussion
of bug report #1150. Also, arrange that the object owner's irrevocable grant-option permissions are handled implicitly by the system rather than being listed in the ACL as self-granted rights (which was wrong anyway). I did not take the further step of showing these permissions in an explicit 'granted by _SYSTEM' ACL entry, as that seemed more likely to bollix up existing clients than to do anything really useful. It's still a possible future direction, though.
This commit is contained in:
parent
f35e8d8431
commit
4b2dafcc0b
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
$PostgreSQL: pgsql/doc/src/sgml/ref/grant.sgml,v 1.39 2004/03/22 03:38:24 momjian Exp $
|
||||
$PostgreSQL: pgsql/doc/src/sgml/ref/grant.sgml,v 1.40 2004/06/01 21:49:21 tgl Exp $
|
||||
PostgreSQL documentation
|
||||
-->
|
||||
|
||||
@ -67,9 +67,10 @@ GRANT { { CREATE | USAGE } [,...] | ALL [ PRIVILEGES ] }
|
||||
|
||||
<para>
|
||||
If <literal>WITH GRANT OPTION</literal> is specified, the recipient
|
||||
of the privilege may in turn grant it to others. By default this
|
||||
is not allowed. Grant options can only be granted to individual
|
||||
users, not to groups or <literal>PUBLIC</literal>.
|
||||
of the privilege may in turn grant it to others. Without a grant
|
||||
option, the recipient cannot do that. At present, grant options can
|
||||
only be granted to individual users, not to groups or
|
||||
<literal>PUBLIC</literal>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@ -79,8 +80,8 @@ GRANT { { CREATE | USAGE } [,...] | ALL [ PRIVILEGES ] }
|
||||
however, choose to revoke some of his own privileges for safety.)
|
||||
The right to drop an object, or to alter its definition in any way is
|
||||
not described by a grantable privilege; it is inherent in the owner,
|
||||
and cannot be granted or revoked. It is not possible for the owner's
|
||||
grant options to be revoked, either.
|
||||
and cannot be granted or revoked. The owner implicitly has all grant
|
||||
options for the object, too.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@ -150,7 +151,7 @@ GRANT { { CREATE | USAGE } [,...] | ALL [ PRIVILEGES ] }
|
||||
<term>RULE</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Allows the creation of a rule on the table/view. (See <xref
|
||||
Allows the creation of a rule on the table/view. (See the <xref
|
||||
linkend="sql-createrule" endterm="sql-createrule-title"> statement.)
|
||||
</para>
|
||||
</listitem>
|
||||
@ -171,7 +172,7 @@ GRANT { { CREATE | USAGE } [,...] | ALL [ PRIVILEGES ] }
|
||||
<term>TRIGGER</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Allows the creation of a trigger on the specified table. (See
|
||||
Allows the creation of a trigger on the specified table. (See the
|
||||
<xref linkend="sql-createtrigger" endterm="sql-createtrigger-title"> statement.)
|
||||
</para>
|
||||
</listitem>
|
||||
@ -234,7 +235,7 @@ GRANT { { CREATE | USAGE } [,...] | ALL [ PRIVILEGES ] }
|
||||
<term>ALL PRIVILEGES</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Grant all of the privileges applicable to the object at once.
|
||||
Grant all of the available privileges at once.
|
||||
The <literal>PRIVILEGES</literal> key word is optional in
|
||||
<productname>PostgreSQL</productname>, though it is required by
|
||||
strict SQL.
|
||||
@ -257,6 +258,20 @@ GRANT { { CREATE | USAGE } [,...] | ALL [ PRIVILEGES ] }
|
||||
to revoke access privileges.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
When a non-owner of an object attempts to <command>GRANT</> privileges
|
||||
on the object, the command will fail outright if the user has no
|
||||
privileges whatsoever on the object. As long as some privilege is
|
||||
available, the command will proceed, but it will grant only those
|
||||
privileges for which the user has grant options. The <command>GRANT ALL
|
||||
PRIVILEGES</> forms will issue a warning message if no grant options are
|
||||
held, while the other forms will issue a warning if grant options for
|
||||
any of the privileges specifically named in the command are not held.
|
||||
(In principle these statements apply to the object owner as well, but
|
||||
since the owner is always treated as holding all grant options, the
|
||||
cases can never occur.)
|
||||
</para>
|
||||
|
||||
<para>
|
||||
It should be noted that database superusers can access
|
||||
all objects regardless of object privilege settings. This
|
||||
@ -273,10 +288,10 @@ GRANT { { CREATE | USAGE } [,...] | ALL [ PRIVILEGES ] }
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Currently, to grant privileges in <productname>PostgreSQL</productname>
|
||||
to only a few columns, you must
|
||||
create a view having the desired columns and then grant privileges
|
||||
to that view.
|
||||
Currently, <productname>PostgreSQL</productname> does not support
|
||||
granting or revoking privileges for individual columns of a table.
|
||||
One possible workaround is to create a view having just the desired
|
||||
columns and then grant privileges to that view.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@ -286,9 +301,9 @@ GRANT { { CREATE | USAGE } [,...] | ALL [ PRIVILEGES ] }
|
||||
=> \z mytable
|
||||
|
||||
Access privileges for database "lusitania"
|
||||
Schema | Name | Type | Access privileges
|
||||
--------+---------+-------+-----------------------------------------------------------------
|
||||
public | mytable | table | {=r/postgres,miriam=arwdRxt/postgres,"group todos=arw/postgres"}
|
||||
Schema | Name | Type | Access privileges
|
||||
--------+---------+-------+------------------------------------------------------------
|
||||
public | mytable | table | {miriam=arwdRxt/miriam,=r/miriam,"group todos=arw/miriam"}
|
||||
(1 row)
|
||||
</programlisting>
|
||||
The entries shown by <command>\z</command> are interpreted thus:
|
||||
@ -331,7 +346,14 @@ and may include some privileges for <literal>PUBLIC</> depending on the
|
||||
object type, as explained above. The first <command>GRANT</> or
|
||||
<command>REVOKE</> on an object
|
||||
will instantiate the default privileges (producing, for example,
|
||||
<literal>{=,miriam=arwdRxt}</>) and then modify them per the specified request.
|
||||
<literal>{miriam=arwdRxt/miriam}</>) and then modify them per the
|
||||
specified request.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Notice that the owner's implicit grant options are not marked in the
|
||||
access privileges display. A <literal>*</> will appear only when
|
||||
grant options have been explicitly granted to someone.
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
@ -347,11 +369,17 @@ GRANT INSERT ON films TO PUBLIC;
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Grant all privileges to user <literal>manuel</literal> on view <literal>kinds</literal>:
|
||||
Grant all available privileges to user <literal>manuel</literal> on view
|
||||
<literal>kinds</literal>:
|
||||
|
||||
<programlisting>
|
||||
GRANT ALL PRIVILEGES ON kinds TO manuel;
|
||||
</programlisting>
|
||||
|
||||
Note that while the above will indeed grant all privileges if executed by a
|
||||
superuser or the owner of <literal>kinds</literal>, when executed by someone
|
||||
else it will only grant those permissions for which the someone else has
|
||||
grant options.
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
$PostgreSQL: pgsql/doc/src/sgml/ref/revoke.sgml,v 1.29 2003/11/29 19:51:39 pgsql Exp $
|
||||
$PostgreSQL: pgsql/doc/src/sgml/ref/revoke.sgml,v 1.30 2004/06/01 21:49:21 tgl Exp $
|
||||
PostgreSQL documentation
|
||||
-->
|
||||
|
||||
@ -81,6 +81,7 @@ REVOKE [ GRANT OPTION FOR ]
|
||||
<para>
|
||||
If <literal>GRANT OPTION FOR</literal> is specified, only the grant
|
||||
option for the privilege is revoked, not the privilege itself.
|
||||
Otherwise, both the privilege and the grant option are revoked.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@ -103,7 +104,7 @@ REVOKE [ GRANT OPTION FOR ]
|
||||
|
||||
<para>
|
||||
Use <xref linkend="app-psql">'s <command>\z</command> command to
|
||||
display the privileges granted on existing objects. See also <xref
|
||||
display the privileges granted on existing objects. See <xref
|
||||
linkend="sql-grant" endterm="sql-grant-title"> for information about the format.
|
||||
</para>
|
||||
|
||||
@ -114,9 +115,25 @@ REVOKE [ GRANT OPTION FOR ]
|
||||
C, then user A cannot revoke the privilege directly from C.
|
||||
Instead, user A could revoke the grant option from user B and use
|
||||
the <literal>CASCADE</literal> option so that the privilege is
|
||||
automatically revoked from user C.
|
||||
in turn revoked from user C. For another example, if both A and B
|
||||
have granted the same privilege to C, A can revoke his own grant
|
||||
but not B's grant, so C will still effectively have the privilege.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
When a non-owner of an object attempts to <command>REVOKE</> privileges
|
||||
on the object, the command will fail outright if the user has no
|
||||
privileges whatsoever on the object. As long as some privilege is
|
||||
available, the command will proceed, but it will revoke only those
|
||||
privileges for which the user has grant options. The <command>REVOKE ALL
|
||||
PRIVILEGES</> forms will issue a warning message if no grant options are
|
||||
held, while the other forms will issue a warning if grant options for
|
||||
any of the privileges specifically named in the command are not held.
|
||||
(In principle these statements apply to the object owner as well, but
|
||||
since the owner is always treated as holding all grant options, the
|
||||
cases can never occur.)
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If a superuser chooses to issue a <command>GRANT</> or <command>REVOKE</>
|
||||
command, the command is performed as though it were issued by the
|
||||
@ -140,11 +157,15 @@ REVOKE INSERT ON films FROM PUBLIC;
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Revoke all privileges from user <literal>manuel</literal> on view <literal>kinds</literal>:
|
||||
Revoke all privileges from user <literal>manuel</literal> on view
|
||||
<literal>kinds</literal>:
|
||||
|
||||
<programlisting>
|
||||
<programlisting>
|
||||
REVOKE ALL PRIVILEGES ON kinds FROM manuel;
|
||||
</programlisting>
|
||||
|
||||
Note that this actually means <quote>revoke all privileges that I
|
||||
granted</>.
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.102 2004/05/28 16:37:11 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.103 2004/06/01 21:49:22 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* See acl.h.
|
||||
@ -48,9 +48,6 @@ static void ExecuteGrantStmt_Namespace(GrantStmt *stmt);
|
||||
|
||||
static const char *privilege_to_string(AclMode privilege);
|
||||
|
||||
static AclMode aclmask(Acl *acl, AclId userid,
|
||||
AclMode mask, AclMaskHow how);
|
||||
|
||||
|
||||
#ifdef ACLDEBUG
|
||||
static
|
||||
@ -126,15 +123,12 @@ merge_acl_with_grant(Acl *old_acl, bool is_grant,
|
||||
AclItem aclitem;
|
||||
uint32 idtype;
|
||||
Acl *newer_acl;
|
||||
bool grantee_is_owner = false;
|
||||
|
||||
if (grantee->username)
|
||||
{
|
||||
aclitem.ai_grantee = get_usesysid(grantee->username);
|
||||
|
||||
idtype = ACL_IDTYPE_UID;
|
||||
|
||||
grantee_is_owner = (aclitem.ai_grantee == owner_uid);
|
||||
}
|
||||
else if (grantee->groupname)
|
||||
{
|
||||
@ -161,19 +155,21 @@ merge_acl_with_grant(Acl *old_acl, bool is_grant,
|
||||
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
|
||||
errmsg("grant options can only be granted to individual users")));
|
||||
|
||||
if (!is_grant && grant_option && grantee_is_owner)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
|
||||
errmsg("cannot revoke grant options from owner")));
|
||||
|
||||
aclitem.ai_grantor = grantor_uid;
|
||||
|
||||
/*
|
||||
* The asymmetry in the conditions here comes from the spec. In
|
||||
* GRANT, the grant_option flag signals WITH GRANT OPTION, which means
|
||||
* to grant both the basic privilege and its grant option. But in
|
||||
* REVOKE, plain revoke revokes both the basic privilege and its
|
||||
* grant option, while REVOKE GRANT OPTION revokes only the option.
|
||||
*/
|
||||
ACLITEM_SET_PRIVS_IDTYPE(aclitem,
|
||||
(is_grant || !grant_option) ? privileges : ACL_NO_RIGHTS,
|
||||
(grant_option || (!is_grant && !grantee_is_owner)) ? privileges : ACL_NO_RIGHTS,
|
||||
(!is_grant || grant_option) ? privileges : ACL_NO_RIGHTS,
|
||||
idtype);
|
||||
|
||||
newer_acl = aclinsert3(new_acl, &aclitem, modechg, behavior);
|
||||
newer_acl = aclupdate(new_acl, &aclitem, modechg, owner_uid, behavior);
|
||||
|
||||
/* avoid memory leak when there are many grantees */
|
||||
pfree(new_acl);
|
||||
@ -221,12 +217,17 @@ static void
|
||||
ExecuteGrantStmt_Relation(GrantStmt *stmt)
|
||||
{
|
||||
AclMode privileges;
|
||||
bool all_privs;
|
||||
ListCell *i;
|
||||
|
||||
if (linitial_int(stmt->privileges) == ACL_ALL_RIGHTS)
|
||||
{
|
||||
all_privs = true;
|
||||
privileges = ACL_ALL_RIGHTS_RELATION;
|
||||
}
|
||||
else
|
||||
{
|
||||
all_privs = false;
|
||||
privileges = ACL_NO_RIGHTS;
|
||||
foreach(i, stmt->privileges)
|
||||
{
|
||||
@ -250,6 +251,8 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt)
|
||||
Form_pg_class pg_class_tuple;
|
||||
Datum aclDatum;
|
||||
bool isNull;
|
||||
AclMode my_goptions;
|
||||
AclMode this_privileges;
|
||||
Acl *old_acl;
|
||||
Acl *new_acl;
|
||||
AclId grantorId;
|
||||
@ -269,15 +272,6 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt)
|
||||
elog(ERROR, "cache lookup failed for relation %u", relOid);
|
||||
pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
|
||||
|
||||
ownerId = pg_class_tuple->relowner;
|
||||
grantorId = select_grantor(ownerId);
|
||||
|
||||
if (stmt->is_grant
|
||||
&& !pg_class_ownercheck(relOid, GetUserId())
|
||||
&& pg_class_aclcheck(relOid, GetUserId(),
|
||||
ACL_GRANT_OPTION_FOR(privileges)) != ACLCHECK_OK)
|
||||
aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, relvar->relname);
|
||||
|
||||
/* Not sensible to grant on an index */
|
||||
if (pg_class_tuple->relkind == RELKIND_INDEX)
|
||||
ereport(ERROR,
|
||||
@ -285,6 +279,69 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt)
|
||||
errmsg("\"%s\" is an index",
|
||||
relvar->relname)));
|
||||
|
||||
/* Composite types aren't tables either */
|
||||
if (pg_class_tuple->relkind == RELKIND_COMPOSITE_TYPE)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
errmsg("\"%s\" is a composite type",
|
||||
relvar->relname)));
|
||||
|
||||
ownerId = pg_class_tuple->relowner;
|
||||
grantorId = select_grantor(ownerId);
|
||||
|
||||
/*
|
||||
* Must be owner or have some privilege on the object (per spec,
|
||||
* any privilege will get you by here). The owner is always
|
||||
* treated as having all grant options.
|
||||
*/
|
||||
if (pg_class_ownercheck(relOid, GetUserId()))
|
||||
my_goptions = ACL_ALL_RIGHTS_RELATION;
|
||||
else
|
||||
{
|
||||
AclMode my_rights;
|
||||
|
||||
my_rights = pg_class_aclmask(relOid,
|
||||
GetUserId(),
|
||||
ACL_ALL_RIGHTS_RELATION | ACL_GRANT_OPTION_FOR(ACL_ALL_RIGHTS_RELATION),
|
||||
ACLMASK_ALL);
|
||||
if (my_rights == ACL_NO_RIGHTS)
|
||||
aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS,
|
||||
relvar->relname);
|
||||
my_goptions = ACL_OPTION_TO_PRIVS(my_rights);
|
||||
}
|
||||
|
||||
/*
|
||||
* Restrict the operation to what we can actually grant or revoke,
|
||||
* and issue a warning if appropriate. (For REVOKE this isn't quite
|
||||
* what the spec says to do: the spec seems to want a warning only
|
||||
* if no privilege bits actually change in the ACL. In practice
|
||||
* that behavior seems much too noisy, as well as inconsistent with
|
||||
* the GRANT case.)
|
||||
*/
|
||||
this_privileges = privileges & my_goptions;
|
||||
if (stmt->is_grant)
|
||||
{
|
||||
if (this_privileges == 0)
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
|
||||
errmsg("no privileges were granted")));
|
||||
else if (!all_privs && this_privileges != privileges)
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
|
||||
errmsg("not all privileges were granted")));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this_privileges == 0)
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
|
||||
errmsg("no privileges could be revoked")));
|
||||
else if (!all_privs && this_privileges != privileges)
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
|
||||
errmsg("not all privileges could be revoked")));
|
||||
}
|
||||
|
||||
/*
|
||||
* If there's no ACL, substitute the proper default.
|
||||
*/
|
||||
@ -298,7 +355,7 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt)
|
||||
|
||||
new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
|
||||
stmt->grant_option, stmt->behavior,
|
||||
stmt->grantees, privileges,
|
||||
stmt->grantees, this_privileges,
|
||||
grantorId, ownerId);
|
||||
|
||||
/* finished building new ACL value, now insert it */
|
||||
@ -328,12 +385,17 @@ static void
|
||||
ExecuteGrantStmt_Database(GrantStmt *stmt)
|
||||
{
|
||||
AclMode privileges;
|
||||
bool all_privs;
|
||||
ListCell *i;
|
||||
|
||||
if (linitial_int(stmt->privileges) == ACL_ALL_RIGHTS)
|
||||
{
|
||||
all_privs = true;
|
||||
privileges = ACL_ALL_RIGHTS_DATABASE;
|
||||
}
|
||||
else
|
||||
{
|
||||
all_privs = false;
|
||||
privileges = ACL_NO_RIGHTS;
|
||||
foreach(i, stmt->privileges)
|
||||
{
|
||||
@ -358,6 +420,8 @@ ExecuteGrantStmt_Database(GrantStmt *stmt)
|
||||
Form_pg_database pg_database_tuple;
|
||||
Datum aclDatum;
|
||||
bool isNull;
|
||||
AclMode my_goptions;
|
||||
AclMode this_privileges;
|
||||
Acl *old_acl;
|
||||
Acl *new_acl;
|
||||
AclId grantorId;
|
||||
@ -383,12 +447,58 @@ ExecuteGrantStmt_Database(GrantStmt *stmt)
|
||||
ownerId = pg_database_tuple->datdba;
|
||||
grantorId = select_grantor(ownerId);
|
||||
|
||||
if (stmt->is_grant
|
||||
&& !pg_database_ownercheck(HeapTupleGetOid(tuple), GetUserId())
|
||||
&& pg_database_aclcheck(HeapTupleGetOid(tuple), GetUserId(),
|
||||
ACL_GRANT_OPTION_FOR(privileges)) != ACLCHECK_OK)
|
||||
aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_DATABASE,
|
||||
NameStr(pg_database_tuple->datname));
|
||||
/*
|
||||
* Must be owner or have some privilege on the object (per spec,
|
||||
* any privilege will get you by here). The owner is always
|
||||
* treated as having all grant options.
|
||||
*/
|
||||
if (pg_database_ownercheck(HeapTupleGetOid(tuple), GetUserId()))
|
||||
my_goptions = ACL_ALL_RIGHTS_DATABASE;
|
||||
else
|
||||
{
|
||||
AclMode my_rights;
|
||||
|
||||
my_rights = pg_database_aclmask(HeapTupleGetOid(tuple),
|
||||
GetUserId(),
|
||||
ACL_ALL_RIGHTS_DATABASE | ACL_GRANT_OPTION_FOR(ACL_ALL_RIGHTS_DATABASE),
|
||||
ACLMASK_ALL);
|
||||
if (my_rights == ACL_NO_RIGHTS)
|
||||
aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_DATABASE,
|
||||
NameStr(pg_database_tuple->datname));
|
||||
my_goptions = ACL_OPTION_TO_PRIVS(my_rights);
|
||||
}
|
||||
|
||||
/*
|
||||
* Restrict the operation to what we can actually grant or revoke,
|
||||
* and issue a warning if appropriate. (For REVOKE this isn't quite
|
||||
* what the spec says to do: the spec seems to want a warning only
|
||||
* if no privilege bits actually change in the ACL. In practice
|
||||
* that behavior seems much too noisy, as well as inconsistent with
|
||||
* the GRANT case.)
|
||||
*/
|
||||
this_privileges = privileges & my_goptions;
|
||||
if (stmt->is_grant)
|
||||
{
|
||||
if (this_privileges == 0)
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
|
||||
errmsg("no privileges were granted")));
|
||||
else if (!all_privs && this_privileges != privileges)
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
|
||||
errmsg("not all privileges were granted")));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this_privileges == 0)
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
|
||||
errmsg("no privileges could be revoked")));
|
||||
else if (!all_privs && this_privileges != privileges)
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
|
||||
errmsg("not all privileges could be revoked")));
|
||||
}
|
||||
|
||||
/*
|
||||
* If there's no ACL, substitute the proper default.
|
||||
@ -403,7 +513,7 @@ ExecuteGrantStmt_Database(GrantStmt *stmt)
|
||||
|
||||
new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
|
||||
stmt->grant_option, stmt->behavior,
|
||||
stmt->grantees, privileges,
|
||||
stmt->grantees, this_privileges,
|
||||
grantorId, ownerId);
|
||||
|
||||
/* finished building new ACL value, now insert it */
|
||||
@ -433,12 +543,17 @@ static void
|
||||
ExecuteGrantStmt_Function(GrantStmt *stmt)
|
||||
{
|
||||
AclMode privileges;
|
||||
bool all_privs;
|
||||
ListCell *i;
|
||||
|
||||
if (linitial_int(stmt->privileges) == ACL_ALL_RIGHTS)
|
||||
{
|
||||
all_privs = true;
|
||||
privileges = ACL_ALL_RIGHTS_FUNCTION;
|
||||
}
|
||||
else
|
||||
{
|
||||
all_privs = false;
|
||||
privileges = ACL_NO_RIGHTS;
|
||||
foreach(i, stmt->privileges)
|
||||
{
|
||||
@ -462,6 +577,8 @@ ExecuteGrantStmt_Function(GrantStmt *stmt)
|
||||
Form_pg_proc pg_proc_tuple;
|
||||
Datum aclDatum;
|
||||
bool isNull;
|
||||
AclMode my_goptions;
|
||||
AclMode this_privileges;
|
||||
Acl *old_acl;
|
||||
Acl *new_acl;
|
||||
AclId grantorId;
|
||||
@ -484,12 +601,58 @@ ExecuteGrantStmt_Function(GrantStmt *stmt)
|
||||
ownerId = pg_proc_tuple->proowner;
|
||||
grantorId = select_grantor(ownerId);
|
||||
|
||||
if (stmt->is_grant
|
||||
&& !pg_proc_ownercheck(oid, GetUserId())
|
||||
&& pg_proc_aclcheck(oid, GetUserId(),
|
||||
ACL_GRANT_OPTION_FOR(privileges)) != ACLCHECK_OK)
|
||||
aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_PROC,
|
||||
NameStr(pg_proc_tuple->proname));
|
||||
/*
|
||||
* Must be owner or have some privilege on the object (per spec,
|
||||
* any privilege will get you by here). The owner is always
|
||||
* treated as having all grant options.
|
||||
*/
|
||||
if (pg_proc_ownercheck(oid, GetUserId()))
|
||||
my_goptions = ACL_ALL_RIGHTS_FUNCTION;
|
||||
else
|
||||
{
|
||||
AclMode my_rights;
|
||||
|
||||
my_rights = pg_proc_aclmask(oid,
|
||||
GetUserId(),
|
||||
ACL_ALL_RIGHTS_FUNCTION | ACL_GRANT_OPTION_FOR(ACL_ALL_RIGHTS_FUNCTION),
|
||||
ACLMASK_ALL);
|
||||
if (my_rights == ACL_NO_RIGHTS)
|
||||
aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_PROC,
|
||||
NameStr(pg_proc_tuple->proname));
|
||||
my_goptions = ACL_OPTION_TO_PRIVS(my_rights);
|
||||
}
|
||||
|
||||
/*
|
||||
* Restrict the operation to what we can actually grant or revoke,
|
||||
* and issue a warning if appropriate. (For REVOKE this isn't quite
|
||||
* what the spec says to do: the spec seems to want a warning only
|
||||
* if no privilege bits actually change in the ACL. In practice
|
||||
* that behavior seems much too noisy, as well as inconsistent with
|
||||
* the GRANT case.)
|
||||
*/
|
||||
this_privileges = privileges & my_goptions;
|
||||
if (stmt->is_grant)
|
||||
{
|
||||
if (this_privileges == 0)
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
|
||||
errmsg("no privileges were granted")));
|
||||
else if (!all_privs && this_privileges != privileges)
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
|
||||
errmsg("not all privileges were granted")));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this_privileges == 0)
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
|
||||
errmsg("no privileges could be revoked")));
|
||||
else if (!all_privs && this_privileges != privileges)
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
|
||||
errmsg("not all privileges could be revoked")));
|
||||
}
|
||||
|
||||
/*
|
||||
* If there's no ACL, substitute the proper default.
|
||||
@ -504,7 +667,7 @@ ExecuteGrantStmt_Function(GrantStmt *stmt)
|
||||
|
||||
new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
|
||||
stmt->grant_option, stmt->behavior,
|
||||
stmt->grantees, privileges,
|
||||
stmt->grantees, this_privileges,
|
||||
grantorId, ownerId);
|
||||
|
||||
/* finished building new ACL value, now insert it */
|
||||
@ -534,12 +697,17 @@ static void
|
||||
ExecuteGrantStmt_Language(GrantStmt *stmt)
|
||||
{
|
||||
AclMode privileges;
|
||||
bool all_privs;
|
||||
ListCell *i;
|
||||
|
||||
if (linitial_int(stmt->privileges) == ACL_ALL_RIGHTS)
|
||||
{
|
||||
all_privs = true;
|
||||
privileges = ACL_ALL_RIGHTS_LANGUAGE;
|
||||
}
|
||||
else
|
||||
{
|
||||
all_privs = false;
|
||||
privileges = ACL_NO_RIGHTS;
|
||||
foreach(i, stmt->privileges)
|
||||
{
|
||||
@ -562,6 +730,8 @@ ExecuteGrantStmt_Language(GrantStmt *stmt)
|
||||
Form_pg_language pg_language_tuple;
|
||||
Datum aclDatum;
|
||||
bool isNull;
|
||||
AclMode my_goptions;
|
||||
AclMode this_privileges;
|
||||
Acl *old_acl;
|
||||
Acl *new_acl;
|
||||
AclId grantorId;
|
||||
@ -581,6 +751,11 @@ ExecuteGrantStmt_Language(GrantStmt *stmt)
|
||||
errmsg("language \"%s\" does not exist", langname)));
|
||||
pg_language_tuple = (Form_pg_language) GETSTRUCT(tuple);
|
||||
|
||||
if (!pg_language_tuple->lanpltrusted)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
errmsg("language \"%s\" is not trusted", langname)));
|
||||
|
||||
/*
|
||||
* Note: for now, languages are treated as owned by the bootstrap
|
||||
* user. We should add an owner column to pg_language instead.
|
||||
@ -588,17 +763,58 @@ ExecuteGrantStmt_Language(GrantStmt *stmt)
|
||||
ownerId = BOOTSTRAP_USESYSID;
|
||||
grantorId = select_grantor(ownerId);
|
||||
|
||||
if (stmt->is_grant
|
||||
&& !superuser() /* XXX no ownercheck() available */
|
||||
&& pg_language_aclcheck(HeapTupleGetOid(tuple), GetUserId(),
|
||||
ACL_GRANT_OPTION_FOR(privileges)) != ACLCHECK_OK)
|
||||
aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_LANGUAGE,
|
||||
NameStr(pg_language_tuple->lanname));
|
||||
/*
|
||||
* Must be owner or have some privilege on the object (per spec,
|
||||
* any privilege will get you by here). The owner is always
|
||||
* treated as having all grant options.
|
||||
*/
|
||||
if (superuser()) /* XXX no ownercheck() available */
|
||||
my_goptions = ACL_ALL_RIGHTS_LANGUAGE;
|
||||
else
|
||||
{
|
||||
AclMode my_rights;
|
||||
|
||||
if (!pg_language_tuple->lanpltrusted)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
errmsg("language \"%s\" is not trusted", langname)));
|
||||
my_rights = pg_language_aclmask(HeapTupleGetOid(tuple),
|
||||
GetUserId(),
|
||||
ACL_ALL_RIGHTS_LANGUAGE | ACL_GRANT_OPTION_FOR(ACL_ALL_RIGHTS_LANGUAGE),
|
||||
ACLMASK_ALL);
|
||||
if (my_rights == ACL_NO_RIGHTS)
|
||||
aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_LANGUAGE,
|
||||
NameStr(pg_language_tuple->lanname));
|
||||
my_goptions = ACL_OPTION_TO_PRIVS(my_rights);
|
||||
}
|
||||
|
||||
/*
|
||||
* Restrict the operation to what we can actually grant or revoke,
|
||||
* and issue a warning if appropriate. (For REVOKE this isn't quite
|
||||
* what the spec says to do: the spec seems to want a warning only
|
||||
* if no privilege bits actually change in the ACL. In practice
|
||||
* that behavior seems much too noisy, as well as inconsistent with
|
||||
* the GRANT case.)
|
||||
*/
|
||||
this_privileges = privileges & my_goptions;
|
||||
if (stmt->is_grant)
|
||||
{
|
||||
if (this_privileges == 0)
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
|
||||
errmsg("no privileges were granted")));
|
||||
else if (!all_privs && this_privileges != privileges)
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
|
||||
errmsg("not all privileges were granted")));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this_privileges == 0)
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
|
||||
errmsg("no privileges could be revoked")));
|
||||
else if (!all_privs && this_privileges != privileges)
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
|
||||
errmsg("not all privileges could be revoked")));
|
||||
}
|
||||
|
||||
/*
|
||||
* If there's no ACL, substitute the proper default.
|
||||
@ -613,7 +829,7 @@ ExecuteGrantStmt_Language(GrantStmt *stmt)
|
||||
|
||||
new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
|
||||
stmt->grant_option, stmt->behavior,
|
||||
stmt->grantees, privileges,
|
||||
stmt->grantees, this_privileges,
|
||||
grantorId, ownerId);
|
||||
|
||||
/* finished building new ACL value, now insert it */
|
||||
@ -643,12 +859,17 @@ static void
|
||||
ExecuteGrantStmt_Namespace(GrantStmt *stmt)
|
||||
{
|
||||
AclMode privileges;
|
||||
bool all_privs;
|
||||
ListCell *i;
|
||||
|
||||
if (linitial_int(stmt->privileges) == ACL_ALL_RIGHTS)
|
||||
{
|
||||
all_privs = true;
|
||||
privileges = ACL_ALL_RIGHTS_NAMESPACE;
|
||||
}
|
||||
else
|
||||
{
|
||||
all_privs = false;
|
||||
privileges = ACL_NO_RIGHTS;
|
||||
foreach(i, stmt->privileges)
|
||||
{
|
||||
@ -671,6 +892,8 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt)
|
||||
Form_pg_namespace pg_namespace_tuple;
|
||||
Datum aclDatum;
|
||||
bool isNull;
|
||||
AclMode my_goptions;
|
||||
AclMode this_privileges;
|
||||
Acl *old_acl;
|
||||
Acl *new_acl;
|
||||
AclId grantorId;
|
||||
@ -693,12 +916,58 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt)
|
||||
ownerId = pg_namespace_tuple->nspowner;
|
||||
grantorId = select_grantor(ownerId);
|
||||
|
||||
if (stmt->is_grant
|
||||
&& !pg_namespace_ownercheck(HeapTupleGetOid(tuple), GetUserId())
|
||||
&& pg_namespace_aclcheck(HeapTupleGetOid(tuple), GetUserId(),
|
||||
ACL_GRANT_OPTION_FOR(privileges)) != ACLCHECK_OK)
|
||||
aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_NAMESPACE,
|
||||
nspname);
|
||||
/*
|
||||
* Must be owner or have some privilege on the object (per spec,
|
||||
* any privilege will get you by here). The owner is always
|
||||
* treated as having all grant options.
|
||||
*/
|
||||
if (pg_namespace_ownercheck(HeapTupleGetOid(tuple), GetUserId()))
|
||||
my_goptions = ACL_ALL_RIGHTS_NAMESPACE;
|
||||
else
|
||||
{
|
||||
AclMode my_rights;
|
||||
|
||||
my_rights = pg_namespace_aclmask(HeapTupleGetOid(tuple),
|
||||
GetUserId(),
|
||||
ACL_ALL_RIGHTS_NAMESPACE | ACL_GRANT_OPTION_FOR(ACL_ALL_RIGHTS_NAMESPACE),
|
||||
ACLMASK_ALL);
|
||||
if (my_rights == ACL_NO_RIGHTS)
|
||||
aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_NAMESPACE,
|
||||
nspname);
|
||||
my_goptions = ACL_OPTION_TO_PRIVS(my_rights);
|
||||
}
|
||||
|
||||
/*
|
||||
* Restrict the operation to what we can actually grant or revoke,
|
||||
* and issue a warning if appropriate. (For REVOKE this isn't quite
|
||||
* what the spec says to do: the spec seems to want a warning only
|
||||
* if no privilege bits actually change in the ACL. In practice
|
||||
* that behavior seems much too noisy, as well as inconsistent with
|
||||
* the GRANT case.)
|
||||
*/
|
||||
this_privileges = privileges & my_goptions;
|
||||
if (stmt->is_grant)
|
||||
{
|
||||
if (this_privileges == 0)
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
|
||||
errmsg("no privileges were granted")));
|
||||
else if (!all_privs && this_privileges != privileges)
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
|
||||
errmsg("not all privileges were granted")));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this_privileges == 0)
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
|
||||
errmsg("no privileges could be revoked")));
|
||||
else if (!all_privs && this_privileges != privileges)
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
|
||||
errmsg("not all privileges could be revoked")));
|
||||
}
|
||||
|
||||
/*
|
||||
* If there's no ACL, substitute the proper default.
|
||||
@ -714,7 +983,7 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt)
|
||||
|
||||
new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
|
||||
stmt->grant_option, stmt->behavior,
|
||||
stmt->grantees, privileges,
|
||||
stmt->grantees, this_privileges,
|
||||
grantorId, ownerId);
|
||||
|
||||
/* finished building new ACL value, now insert it */
|
||||
@ -816,147 +1085,6 @@ get_groname(AclId grosysid)
|
||||
return name;
|
||||
}
|
||||
|
||||
/*
|
||||
* Is user a member of group?
|
||||
*/
|
||||
static bool
|
||||
in_group(AclId uid, AclId gid)
|
||||
{
|
||||
bool result = false;
|
||||
HeapTuple tuple;
|
||||
Datum att;
|
||||
bool isNull;
|
||||
IdList *glist;
|
||||
AclId *aidp;
|
||||
int i,
|
||||
num;
|
||||
|
||||
tuple = SearchSysCache(GROSYSID,
|
||||
ObjectIdGetDatum(gid),
|
||||
0, 0, 0);
|
||||
if (HeapTupleIsValid(tuple))
|
||||
{
|
||||
att = SysCacheGetAttr(GROSYSID,
|
||||
tuple,
|
||||
Anum_pg_group_grolist,
|
||||
&isNull);
|
||||
if (!isNull)
|
||||
{
|
||||
/* be sure the IdList is not toasted */
|
||||
glist = DatumGetIdListP(att);
|
||||
/* scan it */
|
||||
num = IDLIST_NUM(glist);
|
||||
aidp = IDLIST_DAT(glist);
|
||||
for (i = 0; i < num; ++i)
|
||||
{
|
||||
if (aidp[i] == uid)
|
||||
{
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* if IdList was toasted, free detoasted copy */
|
||||
if ((Pointer) glist != DatumGetPointer(att))
|
||||
pfree(glist);
|
||||
}
|
||||
ReleaseSysCache(tuple);
|
||||
}
|
||||
else
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("group with ID %u does not exist", gid)));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* aclmask --- compute bitmask of all privileges held by userid.
|
||||
*
|
||||
* 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 AclMode
|
||||
aclmask(Acl *acl, AclId userid, AclMode mask, AclMaskHow how)
|
||||
{
|
||||
AclMode result;
|
||||
AclMode remaining;
|
||||
AclItem *aidat;
|
||||
int i,
|
||||
num;
|
||||
|
||||
/*
|
||||
* Null ACL should not happen, since caller should have inserted
|
||||
* appropriate default
|
||||
*/
|
||||
if (acl == NULL)
|
||||
elog(ERROR, "null ACL");
|
||||
|
||||
/* Quick exit for mask == 0 */
|
||||
if (mask == 0)
|
||||
return 0;
|
||||
|
||||
num = ACL_NUM(acl);
|
||||
aidat = ACL_DAT(acl);
|
||||
|
||||
result = 0;
|
||||
|
||||
/*
|
||||
* Check privileges granted directly to user or to public
|
||||
*/
|
||||
for (i = 0; i < num; i++)
|
||||
{
|
||||
AclItem *aidata = &aidat[i];
|
||||
|
||||
if (ACLITEM_GET_IDTYPE(*aidata) == ACL_IDTYPE_WORLD
|
||||
|| (ACLITEM_GET_IDTYPE(*aidata) == ACL_IDTYPE_UID
|
||||
&& aidata->ai_grantee == userid))
|
||||
{
|
||||
result |= (aidata->ai_privs & mask);
|
||||
if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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++)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Standardized reporting of aclcheck permissions failures.
|
||||
@ -1058,6 +1186,7 @@ pg_class_aclmask(Oid table_oid, AclId userid,
|
||||
Datum aclDatum;
|
||||
bool isNull;
|
||||
Acl *acl;
|
||||
AclId ownerId;
|
||||
|
||||
/*
|
||||
* Validate userid, find out if he is superuser, also get usecatupd
|
||||
@ -1125,13 +1254,13 @@ pg_class_aclmask(Oid table_oid, AclId userid,
|
||||
/*
|
||||
* Normal case: get the relation's ACL from pg_class
|
||||
*/
|
||||
ownerId = classForm->relowner;
|
||||
|
||||
aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl,
|
||||
&isNull);
|
||||
if (isNull)
|
||||
{
|
||||
/* No ACL, so build default ACL */
|
||||
AclId ownerId = classForm->relowner;
|
||||
|
||||
acl = acldefault(ACL_OBJECT_RELATION, ownerId);
|
||||
aclDatum = (Datum) 0;
|
||||
}
|
||||
@ -1141,7 +1270,7 @@ pg_class_aclmask(Oid table_oid, AclId userid,
|
||||
acl = DatumGetAclP(aclDatum);
|
||||
}
|
||||
|
||||
result = aclmask(acl, userid, mask, how);
|
||||
result = aclmask(acl, userid, ownerId, mask, how);
|
||||
|
||||
/* if we have a detoasted copy, free it */
|
||||
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
|
||||
@ -1167,6 +1296,7 @@ pg_database_aclmask(Oid db_oid, AclId userid,
|
||||
Datum aclDatum;
|
||||
bool isNull;
|
||||
Acl *acl;
|
||||
AclId ownerId;
|
||||
|
||||
/* Superusers bypass all permission checking. */
|
||||
if (superuser_arg(userid))
|
||||
@ -1189,15 +1319,14 @@ pg_database_aclmask(Oid db_oid, AclId userid,
|
||||
(errcode(ERRCODE_UNDEFINED_DATABASE),
|
||||
errmsg("database with OID %u does not exist", db_oid)));
|
||||
|
||||
ownerId = ((Form_pg_database) GETSTRUCT(tuple))->datdba;
|
||||
|
||||
aclDatum = heap_getattr(tuple, Anum_pg_database_datacl,
|
||||
RelationGetDescr(pg_database), &isNull);
|
||||
|
||||
if (isNull)
|
||||
{
|
||||
/* No ACL, so build default ACL */
|
||||
AclId ownerId;
|
||||
|
||||
ownerId = ((Form_pg_database) GETSTRUCT(tuple))->datdba;
|
||||
acl = acldefault(ACL_OBJECT_DATABASE, ownerId);
|
||||
aclDatum = (Datum) 0;
|
||||
}
|
||||
@ -1207,7 +1336,7 @@ pg_database_aclmask(Oid db_oid, AclId userid,
|
||||
acl = DatumGetAclP(aclDatum);
|
||||
}
|
||||
|
||||
result = aclmask(acl, userid, mask, how);
|
||||
result = aclmask(acl, userid, ownerId, mask, how);
|
||||
|
||||
/* if we have a detoasted copy, free it */
|
||||
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
|
||||
@ -1231,6 +1360,7 @@ pg_proc_aclmask(Oid proc_oid, AclId userid,
|
||||
Datum aclDatum;
|
||||
bool isNull;
|
||||
Acl *acl;
|
||||
AclId ownerId;
|
||||
|
||||
/* Superusers bypass all permission checking. */
|
||||
if (superuser_arg(userid))
|
||||
@ -1247,14 +1377,13 @@ pg_proc_aclmask(Oid proc_oid, AclId userid,
|
||||
(errcode(ERRCODE_UNDEFINED_FUNCTION),
|
||||
errmsg("function with OID %u does not exist", proc_oid)));
|
||||
|
||||
ownerId = ((Form_pg_proc) GETSTRUCT(tuple))->proowner;
|
||||
|
||||
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(ACL_OBJECT_FUNCTION, ownerId);
|
||||
aclDatum = (Datum) 0;
|
||||
}
|
||||
@ -1264,7 +1393,7 @@ pg_proc_aclmask(Oid proc_oid, AclId userid,
|
||||
acl = DatumGetAclP(aclDatum);
|
||||
}
|
||||
|
||||
result = aclmask(acl, userid, mask, how);
|
||||
result = aclmask(acl, userid, ownerId, mask, how);
|
||||
|
||||
/* if we have a detoasted copy, free it */
|
||||
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
|
||||
@ -1287,6 +1416,7 @@ pg_language_aclmask(Oid lang_oid, AclId userid,
|
||||
Datum aclDatum;
|
||||
bool isNull;
|
||||
Acl *acl;
|
||||
AclId ownerId;
|
||||
|
||||
/* Superusers bypass all permission checking. */
|
||||
if (superuser_arg(userid))
|
||||
@ -1303,13 +1433,15 @@ pg_language_aclmask(Oid lang_oid, AclId userid,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("language with OID %u does not exist", lang_oid)));
|
||||
|
||||
/* XXX pg_language should have an owner column, but doesn't */
|
||||
ownerId = BOOTSTRAP_USESYSID;
|
||||
|
||||
aclDatum = SysCacheGetAttr(LANGOID, tuple, Anum_pg_language_lanacl,
|
||||
&isNull);
|
||||
if (isNull)
|
||||
{
|
||||
/* No ACL, so build default ACL */
|
||||
/* XXX pg_language should have an owner column, but doesn't */
|
||||
acl = acldefault(ACL_OBJECT_LANGUAGE, BOOTSTRAP_USESYSID);
|
||||
acl = acldefault(ACL_OBJECT_LANGUAGE, ownerId);
|
||||
aclDatum = (Datum) 0;
|
||||
}
|
||||
else
|
||||
@ -1318,7 +1450,7 @@ pg_language_aclmask(Oid lang_oid, AclId userid,
|
||||
acl = DatumGetAclP(aclDatum);
|
||||
}
|
||||
|
||||
result = aclmask(acl, userid, mask, how);
|
||||
result = aclmask(acl, userid, ownerId, mask, how);
|
||||
|
||||
/* if we have a detoasted copy, free it */
|
||||
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
|
||||
@ -1341,6 +1473,7 @@ pg_namespace_aclmask(Oid nsp_oid, AclId userid,
|
||||
Datum aclDatum;
|
||||
bool isNull;
|
||||
Acl *acl;
|
||||
AclId ownerId;
|
||||
|
||||
/* Superusers bypass all permission checking. */
|
||||
if (superuser_arg(userid))
|
||||
@ -1385,14 +1518,13 @@ pg_namespace_aclmask(Oid nsp_oid, AclId userid,
|
||||
(errcode(ERRCODE_UNDEFINED_SCHEMA),
|
||||
errmsg("schema with OID %u does not exist", nsp_oid)));
|
||||
|
||||
ownerId = ((Form_pg_namespace) GETSTRUCT(tuple))->nspowner;
|
||||
|
||||
aclDatum = SysCacheGetAttr(NAMESPACEOID, tuple, Anum_pg_namespace_nspacl,
|
||||
&isNull);
|
||||
if (isNull)
|
||||
{
|
||||
/* No ACL, so build default ACL */
|
||||
AclId ownerId;
|
||||
|
||||
ownerId = ((Form_pg_namespace) GETSTRUCT(tuple))->nspowner;
|
||||
acl = acldefault(ACL_OBJECT_NAMESPACE, ownerId);
|
||||
aclDatum = (Datum) 0;
|
||||
}
|
||||
@ -1402,7 +1534,7 @@ pg_namespace_aclmask(Oid nsp_oid, AclId userid,
|
||||
acl = DatumGetAclP(aclDatum);
|
||||
}
|
||||
|
||||
result = aclmask(acl, userid, mask, how);
|
||||
result = aclmask(acl, userid, ownerId, mask, how);
|
||||
|
||||
/* if we have a detoasted copy, free it */
|
||||
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.104 2004/05/07 00:24:57 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.105 2004/06/01 21:49:22 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -17,6 +17,7 @@
|
||||
#include <ctype.h>
|
||||
|
||||
#include "catalog/namespace.h"
|
||||
#include "catalog/pg_group.h"
|
||||
#include "catalog/pg_shadow.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "commands/dbcommands.h"
|
||||
@ -35,8 +36,11 @@ static void putid(char *p, const char *s);
|
||||
static Acl *allocacl(int n);
|
||||
static const char *aclparse(const char *s, AclItem *aip);
|
||||
static bool aclitem_match(const AclItem *a1, const AclItem *a2);
|
||||
static Acl *recursive_revoke(Acl *acl, AclId grantee,
|
||||
AclMode revoke_privs, DropBehavior behavior);
|
||||
static void check_circularity(const Acl *old_acl, const AclItem *mod_aip,
|
||||
AclId ownerid);
|
||||
static Acl *recursive_revoke(Acl *acl, AclId grantee, AclMode revoke_privs,
|
||||
AclId ownerid, DropBehavior behavior);
|
||||
static bool in_group(AclId uid, AclId gid);
|
||||
|
||||
static AclMode convert_priv_string(text *priv_type_text);
|
||||
|
||||
@ -554,10 +558,19 @@ acldefault(GrantObjectType objtype, AclId ownerid)
|
||||
aip++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that the owner's entry shows all ordinary privileges but no
|
||||
* grant options. This is because his grant options come "from the
|
||||
* system" and not from his own efforts. (The SQL spec says that
|
||||
* the owner's rights come from a "_SYSTEM" authid.) However, we do
|
||||
* consider that the owner's ordinary privileges are self-granted;
|
||||
* this lets him revoke them. We implement the owner's grant options
|
||||
* without any explicit "_SYSTEM"-like ACL entry, by internally
|
||||
* special-casing the owner whereever we are testing grant options.
|
||||
*/
|
||||
aip->ai_grantee = ownerid;
|
||||
aip->ai_grantor = ownerid;
|
||||
/* owner gets default privileges with grant option */
|
||||
ACLITEM_SET_PRIVS_IDTYPE(*aip, owner_default, owner_default,
|
||||
ACLITEM_SET_PRIVS_IDTYPE(*aip, owner_default, ACL_NO_RIGHTS,
|
||||
ACL_IDTYPE_UID);
|
||||
|
||||
return acl;
|
||||
@ -565,21 +578,31 @@ acldefault(GrantObjectType objtype, AclId ownerid)
|
||||
|
||||
|
||||
/*
|
||||
* Add or replace an item in an ACL array. The result is a modified copy;
|
||||
* the input object is not changed.
|
||||
* Update an ACL array to add or remove specified privileges.
|
||||
*
|
||||
* old_acl: the input ACL array
|
||||
* mod_aip: defines the privileges to be added, removed, or substituted
|
||||
* modechg: ACL_MODECHG_ADD, ACL_MODECHG_DEL, or ACL_MODECHG_EQL
|
||||
* ownerid: AclId of object owner
|
||||
* behavior: RESTRICT or CASCADE behavior for recursive removal
|
||||
*
|
||||
* ownerid and behavior are only relevant when the update operation specifies
|
||||
* deletion of grant options.
|
||||
*
|
||||
* 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 *
|
||||
aclinsert3(const Acl *old_acl, const AclItem *mod_aip,
|
||||
unsigned modechg, DropBehavior behavior)
|
||||
aclupdate(const Acl *old_acl, const AclItem *mod_aip,
|
||||
int modechg, AclId ownerid, DropBehavior behavior)
|
||||
{
|
||||
Acl *new_acl = NULL;
|
||||
AclItem *old_aip,
|
||||
*new_aip = NULL;
|
||||
AclMode old_privs,
|
||||
AclMode old_rights,
|
||||
old_goptions,
|
||||
new_privs,
|
||||
new_rights,
|
||||
new_goptions;
|
||||
int dst,
|
||||
num;
|
||||
@ -590,10 +613,15 @@ aclinsert3(const Acl *old_acl, const AclItem *mod_aip,
|
||||
if (!mod_aip)
|
||||
{
|
||||
new_acl = allocacl(ACL_NUM(old_acl));
|
||||
memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
|
||||
memcpy(new_acl, old_acl, ACL_SIZE(old_acl));
|
||||
return new_acl;
|
||||
}
|
||||
|
||||
/* If granting grant options, check for circularity */
|
||||
if (modechg != ACL_MODECHG_DEL &&
|
||||
ACLITEM_GET_GOPTIONS(*mod_aip) != ACL_NO_RIGHTS)
|
||||
check_circularity(old_acl, mod_aip, ownerid);
|
||||
|
||||
num = ACL_NUM(old_acl);
|
||||
old_aip = ACL_DAT(old_acl);
|
||||
|
||||
@ -626,44 +654,39 @@ aclinsert3(const Acl *old_acl, const AclItem *mod_aip,
|
||||
/* initialize the new entry with no permissions */
|
||||
new_aip[dst].ai_grantee = mod_aip->ai_grantee;
|
||||
new_aip[dst].ai_grantor = mod_aip->ai_grantor;
|
||||
ACLITEM_SET_PRIVS_IDTYPE(new_aip[dst], ACL_NO_RIGHTS, ACL_NO_RIGHTS,
|
||||
ACLITEM_SET_PRIVS_IDTYPE(new_aip[dst],
|
||||
ACL_NO_RIGHTS, ACL_NO_RIGHTS,
|
||||
ACLITEM_GET_IDTYPE(*mod_aip));
|
||||
num++; /* set num to the size of new_acl */
|
||||
}
|
||||
|
||||
old_privs = ACLITEM_GET_PRIVS(new_aip[dst]);
|
||||
old_rights = ACLITEM_GET_RIGHTS(new_aip[dst]);
|
||||
old_goptions = ACLITEM_GET_GOPTIONS(new_aip[dst]);
|
||||
|
||||
/* apply the specified permissions change */
|
||||
switch (modechg)
|
||||
{
|
||||
case ACL_MODECHG_ADD:
|
||||
ACLITEM_SET_PRIVS(new_aip[dst],
|
||||
old_privs | ACLITEM_GET_PRIVS(*mod_aip));
|
||||
ACLITEM_SET_GOPTIONS(new_aip[dst],
|
||||
old_goptions | ACLITEM_GET_GOPTIONS(*mod_aip));
|
||||
ACLITEM_SET_RIGHTS(new_aip[dst],
|
||||
old_rights | ACLITEM_GET_RIGHTS(*mod_aip));
|
||||
break;
|
||||
case ACL_MODECHG_DEL:
|
||||
ACLITEM_SET_PRIVS(new_aip[dst],
|
||||
old_privs & ~ACLITEM_GET_PRIVS(*mod_aip));
|
||||
ACLITEM_SET_GOPTIONS(new_aip[dst],
|
||||
old_goptions & ~ACLITEM_GET_GOPTIONS(*mod_aip));
|
||||
ACLITEM_SET_RIGHTS(new_aip[dst],
|
||||
old_rights & ~ACLITEM_GET_RIGHTS(*mod_aip));
|
||||
break;
|
||||
case ACL_MODECHG_EQL:
|
||||
ACLITEM_SET_PRIVS_IDTYPE(new_aip[dst],
|
||||
ACLITEM_GET_PRIVS(*mod_aip),
|
||||
ACLITEM_GET_GOPTIONS(*mod_aip),
|
||||
ACLITEM_GET_IDTYPE(new_aip[dst]));
|
||||
ACLITEM_SET_RIGHTS(new_aip[dst],
|
||||
ACLITEM_GET_RIGHTS(*mod_aip));
|
||||
break;
|
||||
}
|
||||
|
||||
new_privs = ACLITEM_GET_PRIVS(new_aip[dst]);
|
||||
new_rights = ACLITEM_GET_RIGHTS(new_aip[dst]);
|
||||
new_goptions = ACLITEM_GET_GOPTIONS(new_aip[dst]);
|
||||
|
||||
/*
|
||||
* If the adjusted entry has no permissions, delete it from the list.
|
||||
*/
|
||||
if (new_privs == ACL_NO_RIGHTS && new_goptions == ACL_NO_RIGHTS)
|
||||
if (new_rights == ACL_NO_RIGHTS)
|
||||
{
|
||||
memmove(new_aip + dst,
|
||||
new_aip + dst + 1,
|
||||
@ -676,40 +699,143 @@ aclinsert3(const Acl *old_acl, const AclItem *mod_aip,
|
||||
* Remove abandoned privileges (cascading revoke). Currently we
|
||||
* can only handle this when the grantee is a user.
|
||||
*/
|
||||
if ((old_goptions & ~new_goptions) != 0
|
||||
&& ACLITEM_GET_IDTYPE(*mod_aip) == ACL_IDTYPE_UID)
|
||||
if ((old_goptions & ~new_goptions) != 0)
|
||||
{
|
||||
Assert(ACLITEM_GET_IDTYPE(*mod_aip) == ACL_IDTYPE_UID);
|
||||
new_acl = recursive_revoke(new_acl, mod_aip->ai_grantee,
|
||||
(old_goptions & ~new_goptions),
|
||||
behavior);
|
||||
ownerid, behavior);
|
||||
}
|
||||
|
||||
return new_acl;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* When granting grant options, we must disallow attempts to set up circular
|
||||
* chains of grant options. Suppose A (the object owner) grants B some
|
||||
* privileges with grant option, and B re-grants them to C. If C could
|
||||
* grant the privileges to B as well, then A would be unable to effectively
|
||||
* revoke the privileges from B, since recursive_revoke would consider that
|
||||
* B still has 'em from C.
|
||||
*
|
||||
* We check for this by recursively deleting all grant options belonging to
|
||||
* the target grantee, and then seeing if the would-be grantor still has the
|
||||
* grant option or not.
|
||||
*/
|
||||
static void
|
||||
check_circularity(const Acl *old_acl, const AclItem *mod_aip,
|
||||
AclId ownerid)
|
||||
{
|
||||
Acl *acl;
|
||||
AclItem *aip;
|
||||
int i,
|
||||
num;
|
||||
AclMode own_privs;
|
||||
|
||||
/*
|
||||
* For now, grant options can only be granted to users, not groups or
|
||||
* PUBLIC. Otherwise we'd have to work a bit harder here.
|
||||
*/
|
||||
Assert(ACLITEM_GET_IDTYPE(*mod_aip) == ACL_IDTYPE_UID);
|
||||
|
||||
/* The owner always has grant options, no need to check */
|
||||
if (mod_aip->ai_grantor == ownerid)
|
||||
return;
|
||||
|
||||
/* Make a working copy */
|
||||
acl = allocacl(ACL_NUM(old_acl));
|
||||
memcpy(acl, old_acl, ACL_SIZE(old_acl));
|
||||
|
||||
/* Zap all grant options of target grantee, plus what depends on 'em */
|
||||
cc_restart:
|
||||
num = ACL_NUM(acl);
|
||||
aip = ACL_DAT(acl);
|
||||
for (i = 0; i < num; i++)
|
||||
{
|
||||
if (ACLITEM_GET_IDTYPE(aip[i]) == ACL_IDTYPE_UID &&
|
||||
aip[i].ai_grantee == mod_aip->ai_grantee &&
|
||||
ACLITEM_GET_GOPTIONS(aip[i]) != ACL_NO_RIGHTS)
|
||||
{
|
||||
Acl *new_acl;
|
||||
|
||||
/* We'll actually zap ordinary privs too, but no matter */
|
||||
new_acl = aclupdate(acl, &aip[i], ACL_MODECHG_DEL,
|
||||
ownerid, DROP_CASCADE);
|
||||
|
||||
pfree(acl);
|
||||
acl = new_acl;
|
||||
|
||||
goto cc_restart;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now we can compute grantor's independently-derived privileges */
|
||||
own_privs = aclmask(acl,
|
||||
mod_aip->ai_grantor,
|
||||
ownerid,
|
||||
ACL_GRANT_OPTION_FOR(ACLITEM_GET_GOPTIONS(*mod_aip)),
|
||||
ACLMASK_ALL);
|
||||
own_privs = ACL_OPTION_TO_PRIVS(own_privs);
|
||||
|
||||
if ((ACLITEM_GET_GOPTIONS(*mod_aip) & ~own_privs) != 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
|
||||
errmsg("grant options cannot be granted back to your own grantor")));
|
||||
|
||||
pfree(acl);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Ensure that no privilege is "abandoned". A privilege is abandoned
|
||||
* if the user that granted the privilege loses the grant option. (So
|
||||
* the chain through which it was granted is broken.) Either the
|
||||
* abandoned privileges are revoked as well, or an error message is
|
||||
* printed, depending on the drop behavior option.
|
||||
*
|
||||
* acl: the input ACL list
|
||||
* grantee: the user from whom some grant options have been revoked
|
||||
* revoke_privs: the grant options being revoked
|
||||
* ownerid: AclId of object owner
|
||||
* behavior: RESTRICT or CASCADE behavior for recursive removal
|
||||
*
|
||||
* The input Acl object is pfree'd if replaced.
|
||||
*/
|
||||
static Acl *
|
||||
recursive_revoke(Acl *acl,
|
||||
AclId grantee,
|
||||
AclMode revoke_privs,
|
||||
AclId ownerid,
|
||||
DropBehavior behavior)
|
||||
{
|
||||
int i;
|
||||
AclMode still_has;
|
||||
AclItem *aip;
|
||||
int i,
|
||||
num;
|
||||
|
||||
/* The owner can never truly lose grant options, so short-circuit */
|
||||
if (grantee == ownerid)
|
||||
return acl;
|
||||
|
||||
/* The grantee might still have the privileges via another grantor */
|
||||
still_has = aclmask(acl, grantee, ownerid,
|
||||
ACL_GRANT_OPTION_FOR(revoke_privs),
|
||||
ACLMASK_ALL);
|
||||
revoke_privs &= ~still_has;
|
||||
if (revoke_privs == ACL_NO_RIGHTS)
|
||||
return acl;
|
||||
|
||||
restart:
|
||||
for (i = 0; i < ACL_NUM(acl); i++)
|
||||
num = ACL_NUM(acl);
|
||||
aip = ACL_DAT(acl);
|
||||
for (i = 0; i < num; i++)
|
||||
{
|
||||
AclItem *aip = ACL_DAT(acl);
|
||||
|
||||
if (aip[i].ai_grantor == grantee
|
||||
&& (ACLITEM_GET_PRIVS(aip[i]) & revoke_privs) != 0)
|
||||
{
|
||||
AclItem mod_acl;
|
||||
Acl *new_acl;
|
||||
|
||||
if (behavior == DROP_RESTRICT)
|
||||
ereport(ERROR,
|
||||
@ -724,7 +850,12 @@ restart:
|
||||
revoke_privs,
|
||||
ACLITEM_GET_IDTYPE(aip[i]));
|
||||
|
||||
acl = aclinsert3(acl, &mod_acl, ACL_MODECHG_DEL, behavior);
|
||||
new_acl = aclupdate(acl, &mod_acl, ACL_MODECHG_DEL,
|
||||
ownerid, behavior);
|
||||
|
||||
pfree(acl);
|
||||
acl = new_acl;
|
||||
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
@ -733,71 +864,178 @@ restart:
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* aclmask --- compute bitmask of all privileges held by userid.
|
||||
*
|
||||
* 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, ownerid, privs, ACLMASK_ANY) != 0)
|
||||
*
|
||||
* To see if all of a set of privileges are held:
|
||||
* if (aclmask(acl, userid, ownerid, privs, ACLMASK_ALL) == privs)
|
||||
*
|
||||
* To determine exactly which of a set of privileges are held:
|
||||
* heldprivs = aclmask(acl, userid, ownerid, privs, ACLMASK_ALL);
|
||||
*/
|
||||
AclMode
|
||||
aclmask(const Acl *acl, AclId userid, AclId ownerid,
|
||||
AclMode mask, AclMaskHow how)
|
||||
{
|
||||
AclMode result;
|
||||
AclMode remaining;
|
||||
AclItem *aidat;
|
||||
int i,
|
||||
num;
|
||||
|
||||
/*
|
||||
* Null ACL should not happen, since caller should have inserted
|
||||
* appropriate default
|
||||
*/
|
||||
if (acl == NULL)
|
||||
elog(ERROR, "null ACL");
|
||||
|
||||
/* Quick exit for mask == 0 */
|
||||
if (mask == 0)
|
||||
return 0;
|
||||
|
||||
result = 0;
|
||||
|
||||
/* Owner always implicitly has all grant options */
|
||||
if (userid == ownerid)
|
||||
{
|
||||
result = mask & ACLITEM_ALL_GOPTION_BITS;
|
||||
if (result == mask)
|
||||
return result;
|
||||
}
|
||||
|
||||
num = ACL_NUM(acl);
|
||||
aidat = ACL_DAT(acl);
|
||||
|
||||
/*
|
||||
* Check privileges granted directly to user or to public
|
||||
*/
|
||||
for (i = 0; i < num; i++)
|
||||
{
|
||||
AclItem *aidata = &aidat[i];
|
||||
|
||||
if (ACLITEM_GET_IDTYPE(*aidata) == ACL_IDTYPE_WORLD
|
||||
|| (ACLITEM_GET_IDTYPE(*aidata) == ACL_IDTYPE_UID
|
||||
&& aidata->ai_grantee == userid))
|
||||
{
|
||||
result |= (aidata->ai_privs & mask);
|
||||
if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0))
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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++)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Is user a member of group?
|
||||
*/
|
||||
static bool
|
||||
in_group(AclId uid, AclId gid)
|
||||
{
|
||||
bool result = false;
|
||||
HeapTuple tuple;
|
||||
Datum att;
|
||||
bool isNull;
|
||||
IdList *glist;
|
||||
AclId *aidp;
|
||||
int i,
|
||||
num;
|
||||
|
||||
tuple = SearchSysCache(GROSYSID,
|
||||
ObjectIdGetDatum(gid),
|
||||
0, 0, 0);
|
||||
if (HeapTupleIsValid(tuple))
|
||||
{
|
||||
att = SysCacheGetAttr(GROSYSID,
|
||||
tuple,
|
||||
Anum_pg_group_grolist,
|
||||
&isNull);
|
||||
if (!isNull)
|
||||
{
|
||||
/* be sure the IdList is not toasted */
|
||||
glist = DatumGetIdListP(att);
|
||||
/* scan it */
|
||||
num = IDLIST_NUM(glist);
|
||||
aidp = IDLIST_DAT(glist);
|
||||
for (i = 0; i < num; ++i)
|
||||
{
|
||||
if (aidp[i] == uid)
|
||||
{
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* if IdList was toasted, free detoasted copy */
|
||||
if ((Pointer) glist != DatumGetPointer(att))
|
||||
pfree(glist);
|
||||
}
|
||||
ReleaseSysCache(tuple);
|
||||
}
|
||||
else
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("group with ID %u does not exist", gid)));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* aclinsert (exported function)
|
||||
*/
|
||||
Datum
|
||||
aclinsert(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Acl *old_acl = PG_GETARG_ACL_P(0);
|
||||
AclItem *mod_aip = PG_GETARG_ACLITEM_P(1);
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("aclinsert is no longer supported")));
|
||||
|
||||
PG_RETURN_ACL_P(aclinsert3(old_acl, mod_aip, ACL_MODECHG_EQL, DROP_CASCADE));
|
||||
PG_RETURN_NULL(); /* keep compiler quiet */
|
||||
}
|
||||
|
||||
Datum
|
||||
aclremove(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Acl *old_acl = PG_GETARG_ACL_P(0);
|
||||
AclItem *mod_aip = PG_GETARG_ACLITEM_P(1);
|
||||
Acl *new_acl;
|
||||
AclItem *old_aip,
|
||||
*new_aip;
|
||||
int dst,
|
||||
old_num,
|
||||
new_num;
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("aclremove is no longer supported")));
|
||||
|
||||
/* These checks for null input should be dead code, but... */
|
||||
if (!old_acl || ACL_NUM(old_acl) < 0)
|
||||
old_acl = allocacl(0);
|
||||
if (!mod_aip)
|
||||
{
|
||||
new_acl = allocacl(ACL_NUM(old_acl));
|
||||
memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
|
||||
PG_RETURN_ACL_P(new_acl);
|
||||
}
|
||||
|
||||
old_num = ACL_NUM(old_acl);
|
||||
old_aip = ACL_DAT(old_acl);
|
||||
|
||||
/* Search for the matching entry */
|
||||
for (dst = 0;
|
||||
dst < old_num && !aclitem_match(mod_aip, old_aip + dst);
|
||||
++dst)
|
||||
/* continue */ ;
|
||||
|
||||
if (dst >= old_num)
|
||||
{
|
||||
/* Not found, so return copy of source ACL */
|
||||
new_acl = allocacl(old_num);
|
||||
memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
|
||||
}
|
||||
else
|
||||
{
|
||||
new_num = old_num - 1;
|
||||
new_acl = allocacl(new_num);
|
||||
new_aip = ACL_DAT(new_acl);
|
||||
if (dst > 0)
|
||||
memcpy((char *) new_aip,
|
||||
(char *) old_aip,
|
||||
dst * sizeof(AclItem));
|
||||
if (dst < new_num)
|
||||
memcpy((char *) (new_aip + dst),
|
||||
(char *) (old_aip + dst + 1),
|
||||
(new_num - dst) * sizeof(AclItem));
|
||||
}
|
||||
|
||||
PG_RETURN_ACL_P(new_acl);
|
||||
PG_RETURN_NULL(); /* keep compiler quiet */
|
||||
}
|
||||
|
||||
Datum
|
||||
@ -816,8 +1054,7 @@ aclcontains(PG_FUNCTION_ARGS)
|
||||
if (aip->ai_grantee == aidat[i].ai_grantee
|
||||
&& ACLITEM_GET_IDTYPE(*aip) == ACLITEM_GET_IDTYPE(aidat[i])
|
||||
&& aip->ai_grantor == aidat[i].ai_grantor
|
||||
&& (ACLITEM_GET_PRIVS(*aip) & ACLITEM_GET_PRIVS(aidat[i])) == ACLITEM_GET_PRIVS(*aip)
|
||||
&& (ACLITEM_GET_GOPTIONS(*aip) & ACLITEM_GET_GOPTIONS(aidat[i])) == ACLITEM_GET_GOPTIONS(*aip))
|
||||
&& (ACLITEM_GET_RIGHTS(*aip) & ACLITEM_GET_RIGHTS(aidat[i])) == ACLITEM_GET_RIGHTS(*aip))
|
||||
PG_RETURN_BOOL(true);
|
||||
}
|
||||
PG_RETURN_BOOL(false);
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.69 2004/05/11 17:36:13 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.70 2004/06/01 21:49:22 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* An ACL array is simply an array of AclItems, representing the union
|
||||
@ -63,13 +63,16 @@ typedef struct AclItem
|
||||
/*
|
||||
* The AclIdType is stored in the top two bits of the ai_privs field
|
||||
* of an AclItem. The middle 15 bits are the grant option markers,
|
||||
* and the lower 15 bits are the actual privileges.
|
||||
* and the lower 15 bits are the actual privileges. We use "rights"
|
||||
* to mean the combined grant option and privilege bits fields.
|
||||
*/
|
||||
#define ACLITEM_GET_PRIVS(item) ((item).ai_privs & 0x7FFF)
|
||||
#define ACLITEM_GET_GOPTIONS(item) (((item).ai_privs >> 15) & 0x7FFF)
|
||||
#define ACLITEM_GET_RIGHTS(item) ((item).ai_privs & 0x3FFFFFFF)
|
||||
#define ACLITEM_GET_IDTYPE(item) ((item).ai_privs >> 30)
|
||||
|
||||
#define ACL_GRANT_OPTION_FOR(privs) (((AclMode) (privs) & 0x7FFF) << 15)
|
||||
#define ACL_OPTION_TO_PRIVS(privs) (((AclMode) (privs) >> 15) & 0x7FFF)
|
||||
|
||||
#define ACLITEM_SET_PRIVS(item,privs) \
|
||||
((item).ai_privs = ((item).ai_privs & ~((AclMode) 0x7FFF)) | \
|
||||
@ -77,6 +80,9 @@ typedef struct AclItem
|
||||
#define ACLITEM_SET_GOPTIONS(item,goptions) \
|
||||
((item).ai_privs = ((item).ai_privs & ~(((AclMode) 0x7FFF) << 15)) | \
|
||||
(((AclMode) (goptions) & 0x7FFF) << 15))
|
||||
#define ACLITEM_SET_RIGHTS(item,rights) \
|
||||
((item).ai_privs = ((item).ai_privs & ~((AclMode) 0x3FFFFFFF)) | \
|
||||
((AclMode) (rights) & 0x3FFFFFFF))
|
||||
#define ACLITEM_SET_IDTYPE(item,idtype) \
|
||||
((item).ai_privs = ((item).ai_privs & ~(((AclMode) 0x03) << 30)) | \
|
||||
(((AclMode) (idtype) & 0x03) << 30))
|
||||
@ -86,6 +92,8 @@ typedef struct AclItem
|
||||
(((AclMode) (goption) & 0x7FFF) << 15) | \
|
||||
((AclMode) (idtype) << 30))
|
||||
|
||||
#define ACLITEM_ALL_PRIV_BITS ((AclMode) 0x7FFF)
|
||||
#define ACLITEM_ALL_GOPTION_BITS ((AclMode) 0x7FFF << 15)
|
||||
|
||||
/*
|
||||
* Definitions for convenient access to Acl (array of AclItem) and IdList
|
||||
@ -143,7 +151,7 @@ typedef ArrayType IdList;
|
||||
|
||||
|
||||
/*
|
||||
* ACL modification opcodes
|
||||
* ACL modification opcodes for aclupdate
|
||||
*/
|
||||
#define ACL_MODECHG_ADD 1
|
||||
#define ACL_MODECHG_DEL 2
|
||||
@ -212,8 +220,10 @@ typedef enum AclObjectKind
|
||||
* routines used internally
|
||||
*/
|
||||
extern Acl *acldefault(GrantObjectType objtype, AclId ownerid);
|
||||
extern Acl *aclinsert3(const Acl *old_acl, const AclItem *mod_aip,
|
||||
unsigned modechg, DropBehavior behavior);
|
||||
extern Acl *aclupdate(const Acl *old_acl, const AclItem *mod_aip,
|
||||
int modechg, AclId ownerid, DropBehavior behavior);
|
||||
extern AclMode aclmask(const Acl *acl, AclId userid, AclId ownerid,
|
||||
AclMode mask, AclMaskHow how);
|
||||
|
||||
/*
|
||||
* SQL functions (from acl.c)
|
||||
|
@ -11,7 +11,7 @@
|
||||
*
|
||||
* Copyright (c) 2003, PostgreSQL Global Development Group
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/utils/errcodes.h,v 1.11 2004/05/16 23:18:55 neilc Exp $
|
||||
* $PostgreSQL: pgsql/src/include/utils/errcodes.h,v 1.12 2004/06/01 21:49:22 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -59,6 +59,8 @@
|
||||
#define ERRCODE_WARNING_DYNAMIC_RESULT_SETS_RETURNED MAKE_SQLSTATE('0','1', '0','0','C')
|
||||
#define ERRCODE_WARNING_IMPLICIT_ZERO_BIT_PADDING MAKE_SQLSTATE('0','1', '0','0','8')
|
||||
#define ERRCODE_WARNING_NULL_VALUE_ELIMINATED_IN_SET_FUNCTION MAKE_SQLSTATE('0','1', '0','0','3')
|
||||
#define ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED MAKE_SQLSTATE('0','1', '0','0','7')
|
||||
#define ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED MAKE_SQLSTATE('0','1', '0','0','6')
|
||||
#define ERRCODE_WARNING_STRING_DATA_RIGHT_TRUNCATION MAKE_SQLSTATE('0','1', '0','0','4')
|
||||
#define ERRCODE_WARNING_DEPRECATED_FEATURE MAKE_SQLSTATE('0','1', 'P','0','1')
|
||||
|
||||
|
@ -89,7 +89,7 @@ ERROR: permission denied for relation atest2
|
||||
COPY atest2 FROM stdin; -- fail
|
||||
ERROR: permission denied for relation atest2
|
||||
GRANT ALL ON atest1 TO PUBLIC; -- fail
|
||||
ERROR: permission denied for relation atest1
|
||||
WARNING: no privileges were granted
|
||||
-- checks in subquery, both ok
|
||||
SELECT * FROM atest1 WHERE ( b IN ( SELECT col1 FROM atest2 ) );
|
||||
a | b
|
||||
@ -225,7 +225,7 @@ GRANT USAGE ON LANGUAGE c TO PUBLIC; -- fail
|
||||
ERROR: language "c" is not trusted
|
||||
SET SESSION AUTHORIZATION regressuser1;
|
||||
GRANT USAGE ON LANGUAGE sql TO regressuser2; -- fail
|
||||
ERROR: permission denied for language sql
|
||||
WARNING: no privileges were granted
|
||||
CREATE FUNCTION testfunc1(int) RETURNS int AS 'select 2 * $1;' LANGUAGE sql;
|
||||
CREATE FUNCTION testfunc2(int) RETURNS int AS 'select 3 * $1;' LANGUAGE sql;
|
||||
REVOKE ALL ON FUNCTION testfunc1(int), testfunc2(int) FROM PUBLIC;
|
||||
@ -550,7 +550,7 @@ ERROR: grant options can only be granted to individual users
|
||||
SET SESSION AUTHORIZATION regressuser2;
|
||||
GRANT SELECT ON atest4 TO regressuser3;
|
||||
GRANT UPDATE ON atest4 TO regressuser3; -- fail
|
||||
ERROR: permission denied for relation atest4
|
||||
WARNING: no privileges were granted
|
||||
SET SESSION AUTHORIZATION regressuser1;
|
||||
REVOKE SELECT ON atest4 FROM regressuser3; -- does nothing
|
||||
SELECT has_table_privilege('regressuser3', 'atest4', 'SELECT'); -- true
|
||||
|
Loading…
Reference in New Issue
Block a user