1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* acl.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* Basic access control list data structures manipulation routines.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2001-01-24 20:43:33 +01:00
|
|
|
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
Commit to match discussed elog() changes. Only update is that LOG is
now just below FATAL in server_min_messages. Added more text to
highlight ordering difference between it and client_min_messages.
---------------------------------------------------------------------------
REALLYFATAL => PANIC
STOP => PANIC
New INFO level the prints to client by default
New LOG level the prints to server log by default
Cause VACUUM information to print only to the client
NOTICE => INFO where purely information messages are sent
DEBUG => LOG for purely server status messages
DEBUG removed, kept as backward compatible
DEBUG5, DEBUG4, DEBUG3, DEBUG2, DEBUG1 added
DebugLvl removed in favor of new DEBUG[1-5] symbols
New server_min_messages GUC parameter with values:
DEBUG[5-1], INFO, NOTICE, ERROR, LOG, FATAL, PANIC
New client_min_messages GUC parameter with values:
DEBUG[5-1], LOG, INFO, NOTICE, ERROR, FATAL, PANIC
Server startup now logged with LOG instead of DEBUG
Remove debug_level GUC parameter
elog() numbers now start at 10
Add test to print error message if older elog() values are passed to elog()
Bootstrap mode now has a -d that requires an argument, like postmaster
2002-03-02 22:39:36 +01:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.68 2002/03/02 21:39:32 momjian Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
1996-11-06 11:32:10 +01:00
|
|
|
|
2001-06-05 21:34:56 +02:00
|
|
|
#include <ctype.h>
|
|
|
|
|
2000-10-07 02:58:23 +02:00
|
|
|
#include "access/heapam.h"
|
1998-02-24 04:31:50 +01:00
|
|
|
#include "catalog/catalog.h"
|
1998-02-25 14:09:49 +01:00
|
|
|
#include "catalog/pg_shadow.h"
|
1999-07-09 05:28:53 +02:00
|
|
|
#include "catalog/pg_type.h"
|
1999-10-18 05:32:29 +02:00
|
|
|
#include "lib/stringinfo.h"
|
2000-10-07 02:58:23 +02:00
|
|
|
#include "miscadmin.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "utils/acl.h"
|
2000-06-05 09:29:25 +02:00
|
|
|
#include "utils/builtins.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "utils/memutils.h"
|
2001-06-14 03:09:22 +02:00
|
|
|
#include "utils/lsyscache.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "utils/syscache.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2001-06-14 03:09:22 +02:00
|
|
|
|
|
|
|
#define ACL_IDTYPE_GID_KEYWORD "group"
|
|
|
|
#define ACL_IDTYPE_UID_KEYWORD "user"
|
|
|
|
|
2001-06-10 01:21:55 +02:00
|
|
|
static const char *getid(const char *s, char *n);
|
|
|
|
static bool aclitemeq(const AclItem *a1, const AclItem *a2);
|
|
|
|
static bool aclitemgt(const AclItem *a1, const AclItem *a2);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2001-06-14 03:09:22 +02:00
|
|
|
static AclMode convert_priv_string(text *priv_type_text);
|
|
|
|
static bool has_table_privilege_cname_cname(char *username, char *relname,
|
2001-10-25 07:50:21 +02:00
|
|
|
text *priv_type_text);
|
2001-06-14 03:09:22 +02:00
|
|
|
static bool has_table_privilege_cname_id(char *username, Oid reloid,
|
2001-10-25 07:50:21 +02:00
|
|
|
text *priv_type_text);
|
2001-06-14 03:09:22 +02:00
|
|
|
static bool has_table_privilege_id_cname(int32 usesysid, char *relname,
|
2001-10-25 07:50:21 +02:00
|
|
|
text *priv_type_text);
|
2001-06-14 03:09:22 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* getid
|
1997-09-07 07:04:48 +02:00
|
|
|
* Consumes the first alphanumeric string (identifier) found in string
|
1999-05-25 18:15:34 +02:00
|
|
|
* 's', ignoring any leading white space. If it finds a double quote
|
1999-03-21 07:31:59 +01:00
|
|
|
* it returns the word inside the quotes.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* RETURNS:
|
1997-09-07 07:04:48 +02:00
|
|
|
* the string position in 's' that points to the next non-space character
|
1999-03-21 07:31:59 +01:00
|
|
|
* in 's', after any quotes. Also:
|
1997-09-07 07:04:48 +02:00
|
|
|
* - loads the identifier into 'name'. (If no identifier is found, 'name'
|
1999-10-18 05:32:29 +02:00
|
|
|
* contains an empty string.) name must be NAMEDATALEN bytes.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2001-06-10 01:21:55 +02:00
|
|
|
static const char *
|
|
|
|
getid(const char *s, char *n)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
unsigned len;
|
2001-06-10 01:21:55 +02:00
|
|
|
const char *id;
|
1999-05-25 18:15:34 +02:00
|
|
|
int in_quotes = 0;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
Assert(s && n);
|
|
|
|
|
2000-12-03 21:45:40 +01:00
|
|
|
while (isspace((unsigned char) *s))
|
1997-09-07 07:04:48 +02:00
|
|
|
++s;
|
1999-03-21 07:31:59 +01:00
|
|
|
|
|
|
|
if (*s == '"')
|
|
|
|
{
|
|
|
|
in_quotes = 1;
|
|
|
|
s++;
|
|
|
|
}
|
|
|
|
|
2000-12-03 21:45:40 +01:00
|
|
|
for (id = s, len = 0;
|
|
|
|
isalnum((unsigned char) *s) || *s == '_' || in_quotes;
|
|
|
|
++len, ++s)
|
1999-03-21 07:31:59 +01:00
|
|
|
{
|
1999-05-25 18:15:34 +02:00
|
|
|
if (in_quotes && *s == '"')
|
1999-03-21 07:31:59 +01:00
|
|
|
{
|
|
|
|
len--;
|
|
|
|
in_quotes = 0;
|
|
|
|
}
|
|
|
|
}
|
1999-10-18 05:32:29 +02:00
|
|
|
if (len >= NAMEDATALEN)
|
|
|
|
elog(ERROR, "getid: identifier must be <%d characters",
|
|
|
|
NAMEDATALEN);
|
1997-09-07 07:04:48 +02:00
|
|
|
if (len > 0)
|
|
|
|
memmove(n, id, len);
|
|
|
|
n[len] = '\0';
|
2000-12-03 21:45:40 +01:00
|
|
|
while (isspace((unsigned char) *s))
|
1997-09-07 07:04:48 +02:00
|
|
|
++s;
|
1998-09-01 05:29:17 +02:00
|
|
|
return s;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* aclparse
|
1997-09-07 07:04:48 +02:00
|
|
|
* Consumes and parses an ACL specification of the form:
|
|
|
|
* [group|user] [A-Za-z0-9]*[+-=][rwaR]*
|
|
|
|
* from string 's', ignoring any leading white space or white space
|
|
|
|
* between the optional id type keyword (group|user) and the actual
|
|
|
|
* ACL specification.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* This routine is called by the parser as well as aclitemin(), hence
|
|
|
|
* the added generality.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* RETURNS:
|
1997-09-07 07:04:48 +02:00
|
|
|
* the string position in 's' immediately following the ACL
|
|
|
|
* specification. Also:
|
|
|
|
* - loads the structure pointed to by 'aip' with the appropriate
|
|
|
|
* UID/GID, id type identifier and mode type values.
|
|
|
|
* - loads 'modechg' with the mode change flag.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2001-06-10 01:21:55 +02:00
|
|
|
const char *
|
|
|
|
aclparse(const char *s, AclItem *aip, unsigned *modechg)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
char name[NAMEDATALEN];
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
Assert(s && aip && modechg);
|
|
|
|
|
2001-05-27 11:59:30 +02:00
|
|
|
#ifdef ACLDEBUG
|
Commit to match discussed elog() changes. Only update is that LOG is
now just below FATAL in server_min_messages. Added more text to
highlight ordering difference between it and client_min_messages.
---------------------------------------------------------------------------
REALLYFATAL => PANIC
STOP => PANIC
New INFO level the prints to client by default
New LOG level the prints to server log by default
Cause VACUUM information to print only to the client
NOTICE => INFO where purely information messages are sent
DEBUG => LOG for purely server status messages
DEBUG removed, kept as backward compatible
DEBUG5, DEBUG4, DEBUG3, DEBUG2, DEBUG1 added
DebugLvl removed in favor of new DEBUG[1-5] symbols
New server_min_messages GUC parameter with values:
DEBUG[5-1], INFO, NOTICE, ERROR, LOG, FATAL, PANIC
New client_min_messages GUC parameter with values:
DEBUG[5-1], LOG, INFO, NOTICE, ERROR, FATAL, PANIC
Server startup now logged with LOG instead of DEBUG
Remove debug_level GUC parameter
elog() numbers now start at 10
Add test to print error message if older elog() values are passed to elog()
Bootstrap mode now has a -d that requires an argument, like postmaster
2002-03-02 22:39:36 +01:00
|
|
|
elog(LOG, "aclparse: input = '%s'", s);
|
1999-05-12 14:47:24 +02:00
|
|
|
#endif
|
1997-09-07 07:04:48 +02:00
|
|
|
aip->ai_idtype = ACL_IDTYPE_UID;
|
|
|
|
s = getid(s, name);
|
|
|
|
if (*s != ACL_MODECHG_ADD_CHR &&
|
|
|
|
*s != ACL_MODECHG_DEL_CHR &&
|
|
|
|
*s != ACL_MODECHG_EQL_CHR)
|
1999-03-21 07:31:59 +01:00
|
|
|
{
|
|
|
|
/* we just read a keyword, not a name */
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!strcmp(name, ACL_IDTYPE_GID_KEYWORD))
|
|
|
|
aip->ai_idtype = ACL_IDTYPE_GID;
|
|
|
|
else if (strcmp(name, ACL_IDTYPE_UID_KEYWORD))
|
1998-01-05 17:40:20 +01:00
|
|
|
elog(ERROR, "aclparse: bad keyword, must be [group|user]");
|
1997-09-07 07:04:48 +02:00
|
|
|
s = getid(s, name); /* move s to the name beyond the keyword */
|
|
|
|
if (name[0] == '\0')
|
1998-01-05 17:40:20 +01:00
|
|
|
elog(ERROR, "aclparse: a name must follow the [group|user] keyword");
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
if (name[0] == '\0')
|
1997-09-07 07:04:48 +02:00
|
|
|
aip->ai_idtype = ACL_IDTYPE_WORLD;
|
|
|
|
|
|
|
|
switch (*s)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
case ACL_MODECHG_ADD_CHR:
|
|
|
|
*modechg = ACL_MODECHG_ADD;
|
|
|
|
break;
|
|
|
|
case ACL_MODECHG_DEL_CHR:
|
|
|
|
*modechg = ACL_MODECHG_DEL;
|
|
|
|
break;
|
|
|
|
case ACL_MODECHG_EQL_CHR:
|
|
|
|
*modechg = ACL_MODECHG_EQL;
|
|
|
|
break;
|
|
|
|
default:
|
1998-01-05 17:40:20 +01:00
|
|
|
elog(ERROR, "aclparse: mode change flag must use \"%s\"",
|
1997-09-08 04:41:22 +02:00
|
|
|
ACL_MODECHG_STR);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
aip->ai_mode = ACL_NO;
|
2000-12-03 21:45:40 +01:00
|
|
|
while (isalpha((unsigned char) *++s))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
|
|
|
switch (*s)
|
|
|
|
{
|
2001-05-27 11:59:30 +02:00
|
|
|
case ACL_MODE_INSERT_CHR:
|
|
|
|
aip->ai_mode |= ACL_INSERT;
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
2001-05-27 11:59:30 +02:00
|
|
|
case ACL_MODE_SELECT_CHR:
|
|
|
|
aip->ai_mode |= ACL_SELECT;
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
2001-05-27 11:59:30 +02:00
|
|
|
case ACL_MODE_UPDATE_CHR:
|
|
|
|
aip->ai_mode |= ACL_UPDATE;
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
2001-05-27 11:59:30 +02:00
|
|
|
case ACL_MODE_DELETE_CHR:
|
|
|
|
aip->ai_mode |= ACL_DELETE;
|
|
|
|
break;
|
|
|
|
case ACL_MODE_RULE_CHR:
|
|
|
|
aip->ai_mode |= ACL_RULE;
|
|
|
|
break;
|
|
|
|
case ACL_MODE_REFERENCES_CHR:
|
|
|
|
aip->ai_mode |= ACL_REFERENCES;
|
|
|
|
break;
|
|
|
|
case ACL_MODE_TRIGGER_CHR:
|
|
|
|
aip->ai_mode |= ACL_TRIGGER;
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
default:
|
1998-01-05 17:40:20 +01:00
|
|
|
elog(ERROR, "aclparse: mode flags must use \"%s\"",
|
1997-09-08 04:41:22 +02:00
|
|
|
ACL_MODE_STR);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (aip->ai_idtype)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
case ACL_IDTYPE_UID:
|
2001-06-14 03:09:22 +02:00
|
|
|
aip->ai_id = get_usesysid(name);
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
case ACL_IDTYPE_GID:
|
|
|
|
aip->ai_id = get_grosysid(name);
|
|
|
|
break;
|
|
|
|
case ACL_IDTYPE_WORLD:
|
|
|
|
aip->ai_id = ACL_ID_WORLD;
|
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
2001-05-27 11:59:30 +02:00
|
|
|
#ifdef ACLDEBUG
|
Commit to match discussed elog() changes. Only update is that LOG is
now just below FATAL in server_min_messages. Added more text to
highlight ordering difference between it and client_min_messages.
---------------------------------------------------------------------------
REALLYFATAL => PANIC
STOP => PANIC
New INFO level the prints to client by default
New LOG level the prints to server log by default
Cause VACUUM information to print only to the client
NOTICE => INFO where purely information messages are sent
DEBUG => LOG for purely server status messages
DEBUG removed, kept as backward compatible
DEBUG5, DEBUG4, DEBUG3, DEBUG2, DEBUG1 added
DebugLvl removed in favor of new DEBUG[1-5] symbols
New server_min_messages GUC parameter with values:
DEBUG[5-1], INFO, NOTICE, ERROR, LOG, FATAL, PANIC
New client_min_messages GUC parameter with values:
DEBUG[5-1], LOG, INFO, NOTICE, ERROR, FATAL, PANIC
Server startup now logged with LOG instead of DEBUG
Remove debug_level GUC parameter
elog() numbers now start at 10
Add test to print error message if older elog() values are passed to elog()
Bootstrap mode now has a -d that requires an argument, like postmaster
2002-03-02 22:39:36 +01:00
|
|
|
elog(LOG, "aclparse: correctly read [%x %d %x], modechg=%x",
|
1997-09-07 07:04:48 +02:00
|
|
|
aip->ai_idtype, aip->ai_id, aip->ai_mode, *modechg);
|
1996-07-09 08:22:35 +02:00
|
|
|
#endif
|
1998-09-01 05:29:17 +02:00
|
|
|
return s;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* makeacl
|
1997-09-07 07:04:48 +02:00
|
|
|
* Allocates storage for a new Acl with 'n' entries.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* RETURNS:
|
1997-09-07 07:04:48 +02:00
|
|
|
* the new Acl
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
Acl *
|
1996-07-09 08:22:35 +02:00
|
|
|
makeacl(int n)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
Acl *new_acl;
|
|
|
|
Size size;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (n < 0)
|
1999-10-18 05:32:29 +02:00
|
|
|
elog(ERROR, "makeacl: invalid size: %d", n);
|
1997-09-07 07:04:48 +02:00
|
|
|
size = ACL_N_SIZE(n);
|
2000-11-03 20:01:36 +01:00
|
|
|
new_acl = (Acl *) palloc(size);
|
1997-09-18 22:22:58 +02:00
|
|
|
MemSet((char *) new_acl, 0, size);
|
1997-09-07 07:04:48 +02:00
|
|
|
new_acl->size = size;
|
|
|
|
new_acl->ndim = 1;
|
|
|
|
new_acl->flags = 0;
|
|
|
|
ARR_LBOUND(new_acl)[0] = 0;
|
|
|
|
ARR_DIMS(new_acl)[0] = n;
|
1998-09-01 05:29:17 +02:00
|
|
|
return new_acl;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* aclitemin
|
1997-09-07 07:04:48 +02:00
|
|
|
* Allocates storage for, and fills in, a new AclItem given a string
|
|
|
|
* 's' that contains an ACL specification. See aclparse for details.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* RETURNS:
|
1997-09-07 07:04:48 +02:00
|
|
|
* the new AclItem
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2000-08-01 00:39:17 +02:00
|
|
|
Datum
|
|
|
|
aclitemin(PG_FUNCTION_ARGS)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2001-06-10 01:21:55 +02:00
|
|
|
const char *s = PG_GETARG_CSTRING(0);
|
1997-09-08 04:41:22 +02:00
|
|
|
AclItem *aip;
|
2000-08-01 00:39:17 +02:00
|
|
|
unsigned modechg;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
aip = (AclItem *) palloc(sizeof(AclItem));
|
|
|
|
s = aclparse(s, aip, &modechg);
|
|
|
|
if (modechg != ACL_MODECHG_EQL)
|
1998-01-05 17:40:20 +01:00
|
|
|
elog(ERROR, "aclitemin: cannot accept anything but = ACLs");
|
2000-12-03 21:45:40 +01:00
|
|
|
while (isspace((unsigned char) *s))
|
1997-09-07 07:04:48 +02:00
|
|
|
++s;
|
|
|
|
if (*s)
|
1998-01-05 17:40:20 +01:00
|
|
|
elog(ERROR, "aclitemin: extra garbage at end of specification");
|
2000-08-01 00:39:17 +02:00
|
|
|
PG_RETURN_ACLITEM_P(aip);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* aclitemout
|
1997-09-07 07:04:48 +02:00
|
|
|
* Allocates storage for, and fills in, a new null-delimited string
|
|
|
|
* containing a formatted ACL specification. See aclparse for details.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* RETURNS:
|
1997-09-07 07:04:48 +02:00
|
|
|
* the new string
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2000-08-01 00:39:17 +02:00
|
|
|
Datum
|
|
|
|
aclitemout(PG_FUNCTION_ARGS)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
AclItem *aip = PG_GETARG_ACLITEM_P(0);
|
1998-02-26 05:46:47 +01:00
|
|
|
char *p;
|
1997-09-08 04:41:22 +02:00
|
|
|
char *out;
|
1998-08-19 04:04:17 +02:00
|
|
|
HeapTuple htup;
|
1997-09-08 04:41:22 +02:00
|
|
|
unsigned i;
|
|
|
|
char *tmpname;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-05-27 11:59:30 +02:00
|
|
|
p = out = palloc(strlen("group =" ACL_MODE_STR " ") + 1 + NAMEDATALEN);
|
1997-09-07 07:04:48 +02:00
|
|
|
*p = '\0';
|
|
|
|
|
|
|
|
switch (aip->ai_idtype)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
case ACL_IDTYPE_UID:
|
2000-11-16 23:30:52 +01:00
|
|
|
htup = SearchSysCache(SHADOWSYSID,
|
|
|
|
ObjectIdGetDatum(aip->ai_id),
|
|
|
|
0, 0, 0);
|
2000-11-29 00:42:31 +01:00
|
|
|
if (HeapTupleIsValid(htup))
|
|
|
|
{
|
|
|
|
strncat(p,
|
2001-03-22 05:01:46 +01:00
|
|
|
NameStr(((Form_pg_shadow) GETSTRUCT(htup))->usename),
|
2000-11-29 00:42:31 +01:00
|
|
|
NAMEDATALEN);
|
|
|
|
ReleaseSysCache(htup);
|
|
|
|
}
|
|
|
|
else
|
1997-09-08 04:41:22 +02:00
|
|
|
{
|
2000-06-05 09:29:25 +02:00
|
|
|
/* Generate numeric UID if we don't find an entry */
|
|
|
|
char *tmp;
|
1997-11-01 06:21:57 +01:00
|
|
|
|
2000-06-05 09:29:25 +02:00
|
|
|
tmp = DatumGetCString(DirectFunctionCall1(int4out,
|
2001-03-22 05:01:46 +01:00
|
|
|
Int32GetDatum((int32) aip->ai_id)));
|
1997-09-08 04:41:22 +02:00
|
|
|
strcat(p, tmp);
|
|
|
|
pfree(tmp);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ACL_IDTYPE_GID:
|
|
|
|
strcat(p, "group ");
|
|
|
|
tmpname = get_groname(aip->ai_id);
|
2000-11-29 00:42:31 +01:00
|
|
|
if (tmpname != NULL)
|
|
|
|
strncat(p, tmpname, NAMEDATALEN);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Generate numeric GID if we don't find an entry */
|
|
|
|
char *tmp;
|
|
|
|
|
|
|
|
tmp = DatumGetCString(DirectFunctionCall1(int4out,
|
2001-03-22 05:01:46 +01:00
|
|
|
Int32GetDatum((int32) aip->ai_id)));
|
2000-11-29 00:42:31 +01:00
|
|
|
strcat(p, tmp);
|
|
|
|
pfree(tmp);
|
|
|
|
}
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
case ACL_IDTYPE_WORLD:
|
|
|
|
break;
|
|
|
|
default:
|
1998-01-05 17:40:20 +01:00
|
|
|
elog(ERROR, "aclitemout: bad ai_idtype: %d", aip->ai_idtype);
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
while (*p)
|
|
|
|
++p;
|
|
|
|
*p++ = '=';
|
|
|
|
for (i = 0; i < N_ACL_MODES; ++i)
|
|
|
|
if ((aip->ai_mode >> i) & 01)
|
|
|
|
*p++ = ACL_MODE_STR[i];
|
|
|
|
*p = '\0';
|
|
|
|
|
2000-08-01 00:39:17 +02:00
|
|
|
PG_RETURN_CSTRING(out);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* aclitemeq
|
|
|
|
* aclitemgt
|
1997-09-07 07:04:48 +02:00
|
|
|
* AclItem equality and greater-than comparison routines.
|
2001-01-14 20:23:27 +01:00
|
|
|
* Two AclItems are considered equal iff they have the
|
|
|
|
* same identifier (and identifier type); the mode is ignored.
|
|
|
|
* Note that these routines are really only useful for sorting
|
|
|
|
* AclItems into identifier order.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* RETURNS:
|
1997-09-07 07:04:48 +02:00
|
|
|
* a boolean value indicating = or >
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2000-08-01 00:39:17 +02:00
|
|
|
static bool
|
2001-06-10 01:21:55 +02:00
|
|
|
aclitemeq(const AclItem *a1, const AclItem *a2)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-09-01 05:29:17 +02:00
|
|
|
return a1->ai_idtype == a2->ai_idtype && a1->ai_id == a2->ai_id;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2000-08-01 00:39:17 +02:00
|
|
|
static bool
|
2001-06-10 01:21:55 +02:00
|
|
|
aclitemgt(const AclItem *a1, const AclItem *a2)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
return ((a1->ai_idtype > a2->ai_idtype) ||
|
|
|
|
(a1->ai_idtype == a2->ai_idtype && a1->ai_id > a2->ai_id));
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2000-10-02 06:49:28 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* acldefault() --- create an ACL describing default access permissions
|
|
|
|
*
|
|
|
|
* Change this routine if you want to alter the default access policy for
|
|
|
|
* newly-created tables (or any table with a NULL acl entry in pg_class)
|
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
Acl *
|
2002-02-19 00:11:58 +01:00
|
|
|
acldefault(AclId ownerid)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
Acl *acl;
|
|
|
|
AclItem *aip;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-10-02 06:49:28 +02:00
|
|
|
#define ACL_WORLD_DEFAULT (ACL_NO)
|
2001-05-27 11:59:30 +02:00
|
|
|
#define ACL_OWNER_DEFAULT (ACL_INSERT|ACL_SELECT|ACL_UPDATE|ACL_DELETE|ACL_RULE|ACL_REFERENCES|ACL_TRIGGER)
|
2000-10-02 06:49:28 +02:00
|
|
|
|
2002-02-19 00:11:58 +01:00
|
|
|
acl = makeacl(ownerid ? 2 : 1);
|
1997-09-07 07:04:48 +02:00
|
|
|
aip = ACL_DAT(acl);
|
|
|
|
aip[0].ai_idtype = ACL_IDTYPE_WORLD;
|
|
|
|
aip[0].ai_id = ACL_ID_WORLD;
|
2002-02-19 00:11:58 +01:00
|
|
|
aip[0].ai_mode = ACL_WORLD_DEFAULT;
|
|
|
|
/* FIXME: The owner's default should vary with the object type. */
|
|
|
|
if (ownerid)
|
|
|
|
{
|
|
|
|
aip[1].ai_idtype = ACL_IDTYPE_UID;
|
|
|
|
aip[1].ai_id = ownerid;
|
|
|
|
aip[1].ai_mode = ACL_OWNER_DEFAULT;
|
|
|
|
}
|
1998-09-01 05:29:17 +02:00
|
|
|
return acl;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-08-01 00:39:17 +02:00
|
|
|
/*
|
2001-10-25 07:50:21 +02:00
|
|
|
* Add or replace an item in an ACL array. The result is a modified copy;
|
2001-06-05 21:34:56 +02:00
|
|
|
* the input object is not changed.
|
2000-08-01 00:39:17 +02:00
|
|
|
*
|
|
|
|
* NB: caller is responsible for having detoasted the input ACL, if needed.
|
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
Acl *
|
2001-06-10 01:21:55 +02:00
|
|
|
aclinsert3(const Acl *old_acl, const AclItem *mod_aip, unsigned modechg)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
Acl *new_acl;
|
|
|
|
AclItem *old_aip,
|
|
|
|
*new_aip;
|
2001-06-05 21:34:56 +02:00
|
|
|
int dst,
|
1997-09-08 04:41:22 +02:00
|
|
|
num;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-08-01 00:39:17 +02:00
|
|
|
/* These checks for null input are probably dead code, but... */
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!old_acl || ACL_NUM(old_acl) < 1)
|
2000-11-03 20:01:36 +01:00
|
|
|
old_acl = makeacl(1);
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!mod_aip)
|
|
|
|
{
|
|
|
|
new_acl = makeacl(ACL_NUM(old_acl));
|
2000-08-01 00:39:17 +02:00
|
|
|
memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
|
1998-09-01 05:29:17 +02:00
|
|
|
return new_acl;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
num = ACL_NUM(old_acl);
|
|
|
|
old_aip = ACL_DAT(old_acl);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Search the ACL for an existing entry for 'id'. If one exists, just
|
|
|
|
* modify the entry in-place (well, in the same position, since we
|
|
|
|
* actually return a copy); otherwise, insert the new entry in
|
|
|
|
* sort-order.
|
|
|
|
*/
|
|
|
|
/* find the first element not less than the element to be inserted */
|
|
|
|
for (dst = 0; dst < num && aclitemgt(mod_aip, old_aip + dst); ++dst)
|
|
|
|
;
|
2000-11-03 20:01:36 +01:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (dst < num && aclitemeq(mod_aip, old_aip + dst))
|
|
|
|
{
|
2001-06-05 21:34:56 +02:00
|
|
|
/* found a match, so modify existing item */
|
2000-11-03 20:01:36 +01:00
|
|
|
new_acl = makeacl(num);
|
1997-09-07 07:04:48 +02:00
|
|
|
new_aip = ACL_DAT(new_acl);
|
2000-11-03 20:01:36 +01:00
|
|
|
memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2001-06-05 21:34:56 +02:00
|
|
|
/* need to insert a new item */
|
1997-09-07 07:04:48 +02:00
|
|
|
new_acl = makeacl(num + 1);
|
|
|
|
new_aip = ACL_DAT(new_acl);
|
|
|
|
if (dst == 0)
|
|
|
|
{ /* start */
|
1998-01-05 17:40:20 +01:00
|
|
|
elog(ERROR, "aclinsert3: insertion before world ACL??");
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else if (dst >= num)
|
|
|
|
{ /* end */
|
2000-11-03 20:01:36 +01:00
|
|
|
memcpy((char *) new_aip,
|
|
|
|
(char *) old_aip,
|
|
|
|
num * sizeof(AclItem));
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{ /* middle */
|
2000-11-03 20:01:36 +01:00
|
|
|
memcpy((char *) new_aip,
|
|
|
|
(char *) old_aip,
|
|
|
|
dst * sizeof(AclItem));
|
|
|
|
memcpy((char *) (new_aip + dst + 1),
|
|
|
|
(char *) (old_aip + dst),
|
|
|
|
(num - dst) * sizeof(AclItem));
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2001-06-05 21:34:56 +02:00
|
|
|
/* initialize the new entry with no permissions */
|
1997-09-07 07:04:48 +02:00
|
|
|
new_aip[dst].ai_id = mod_aip->ai_id;
|
|
|
|
new_aip[dst].ai_idtype = mod_aip->ai_idtype;
|
2001-06-05 21:34:56 +02:00
|
|
|
new_aip[dst].ai_mode = 0;
|
1997-09-07 07:04:48 +02:00
|
|
|
num++; /* set num to the size of new_acl */
|
|
|
|
}
|
2000-11-03 20:01:36 +01:00
|
|
|
|
|
|
|
/* apply the permissions mod */
|
1997-09-07 07:04:48 +02:00
|
|
|
switch (modechg)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
case ACL_MODECHG_ADD:
|
2001-06-05 21:34:56 +02:00
|
|
|
new_aip[dst].ai_mode |= mod_aip->ai_mode;
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
case ACL_MODECHG_DEL:
|
2001-06-05 21:34:56 +02:00
|
|
|
new_aip[dst].ai_mode &= ~mod_aip->ai_mode;
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
case ACL_MODECHG_EQL:
|
1999-02-03 22:18:02 +01:00
|
|
|
new_aip[dst].ai_mode = mod_aip->ai_mode;
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* if the adjusted entry has no permissions, delete it from the list.
|
|
|
|
* For example, this helps in removing entries for users who no longer
|
|
|
|
* exist. EXCEPTION: never remove the world entry.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2000-11-03 20:01:36 +01:00
|
|
|
if (new_aip[dst].ai_mode == 0 && dst > 0)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2001-06-05 21:34:56 +02:00
|
|
|
memmove((char *) (new_aip + dst),
|
|
|
|
(char *) (new_aip + dst + 1),
|
|
|
|
(num - dst - 1) * sizeof(AclItem));
|
2000-08-01 00:39:17 +02:00
|
|
|
ARR_DIMS(new_acl)[0] = num - 1;
|
|
|
|
ARR_SIZE(new_acl) -= sizeof(AclItem);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return new_acl;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2000-08-01 00:39:17 +02:00
|
|
|
* aclinsert (exported function)
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2000-08-01 00:39:17 +02:00
|
|
|
Datum
|
|
|
|
aclinsert(PG_FUNCTION_ARGS)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-08-01 00:39:17 +02:00
|
|
|
Acl *old_acl = PG_GETARG_ACL_P(0);
|
2001-03-22 05:01:46 +01:00
|
|
|
AclItem *mod_aip = PG_GETARG_ACLITEM_P(1);
|
2000-08-01 00:39:17 +02:00
|
|
|
|
|
|
|
PG_RETURN_ACL_P(aclinsert3(old_acl, mod_aip, ACL_MODECHG_EQL));
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2000-08-01 00:39:17 +02:00
|
|
|
Datum
|
|
|
|
aclremove(PG_FUNCTION_ARGS)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-08-01 00:39:17 +02:00
|
|
|
Acl *old_acl = PG_GETARG_ACL_P(0);
|
2001-03-22 05:01:46 +01:00
|
|
|
AclItem *mod_aip = PG_GETARG_ACLITEM_P(1);
|
1997-09-08 04:41:22 +02:00
|
|
|
Acl *new_acl;
|
|
|
|
AclItem *old_aip,
|
|
|
|
*new_aip;
|
2000-08-01 00:39:17 +02:00
|
|
|
int dst,
|
1997-09-08 04:41:22 +02:00
|
|
|
old_num,
|
|
|
|
new_num;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-08-01 00:39:17 +02:00
|
|
|
/* These checks for null input should be dead code, but... */
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!old_acl || ACL_NUM(old_acl) < 1)
|
2000-11-03 20:01:36 +01:00
|
|
|
old_acl = makeacl(1);
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!mod_aip)
|
|
|
|
{
|
|
|
|
new_acl = makeacl(ACL_NUM(old_acl));
|
2000-08-01 00:39:17 +02:00
|
|
|
memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
|
|
|
|
PG_RETURN_ACL_P(new_acl);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
old_num = ACL_NUM(old_acl);
|
|
|
|
old_aip = ACL_DAT(old_acl);
|
|
|
|
|
2000-11-03 20:01:36 +01:00
|
|
|
/* Search for the matching entry */
|
1997-09-07 07:04:48 +02:00
|
|
|
for (dst = 0; dst < old_num && !aclitemeq(mod_aip, old_aip + dst); ++dst)
|
|
|
|
;
|
2000-11-03 20:01:36 +01:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (dst >= old_num)
|
2000-11-03 20:01:36 +01:00
|
|
|
{
|
|
|
|
/* Not found, so return copy of source ACL */
|
|
|
|
new_acl = makeacl(old_num);
|
2000-08-01 00:39:17 +02:00
|
|
|
memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
new_num = old_num - 1;
|
2000-08-01 00:39:17 +02:00
|
|
|
new_acl = makeacl(new_num);
|
1997-09-07 07:04:48 +02:00
|
|
|
new_aip = ACL_DAT(new_acl);
|
|
|
|
if (dst == 0)
|
|
|
|
{ /* start */
|
1998-01-05 17:40:20 +01:00
|
|
|
elog(ERROR, "aclremove: removal of the world ACL??");
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else if (dst == old_num - 1)
|
|
|
|
{ /* end */
|
2000-11-03 20:01:36 +01:00
|
|
|
memcpy((char *) new_aip,
|
|
|
|
(char *) old_aip,
|
|
|
|
new_num * sizeof(AclItem));
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{ /* middle */
|
2000-11-03 20:01:36 +01:00
|
|
|
memcpy((char *) new_aip,
|
|
|
|
(char *) old_aip,
|
|
|
|
dst * sizeof(AclItem));
|
|
|
|
memcpy((char *) (new_aip + dst),
|
|
|
|
(char *) (old_aip + dst + 1),
|
|
|
|
(new_num - dst) * sizeof(AclItem));
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
}
|
2000-11-03 20:01:36 +01:00
|
|
|
|
2000-08-01 00:39:17 +02:00
|
|
|
PG_RETURN_ACL_P(new_acl);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2000-08-01 00:39:17 +02:00
|
|
|
Datum
|
|
|
|
aclcontains(PG_FUNCTION_ARGS)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-08-01 00:39:17 +02:00
|
|
|
Acl *acl = PG_GETARG_ACL_P(0);
|
2001-03-22 05:01:46 +01:00
|
|
|
AclItem *aip = PG_GETARG_ACLITEM_P(1);
|
1997-09-08 04:41:22 +02:00
|
|
|
AclItem *aidat;
|
2000-08-01 00:39:17 +02:00
|
|
|
int i,
|
|
|
|
num;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-08-01 00:39:17 +02:00
|
|
|
num = ACL_NUM(acl);
|
1997-09-07 07:04:48 +02:00
|
|
|
aidat = ACL_DAT(acl);
|
|
|
|
for (i = 0; i < num; ++i)
|
2001-01-14 20:23:27 +01:00
|
|
|
{
|
|
|
|
/* Note that aclitemeq only considers id, not mode */
|
|
|
|
if (aclitemeq(aip, aidat + i) &&
|
|
|
|
aip->ai_mode == aidat[i].ai_mode)
|
2000-08-01 00:39:17 +02:00
|
|
|
PG_RETURN_BOOL(true);
|
2001-01-14 20:23:27 +01:00
|
|
|
}
|
2000-08-01 00:39:17 +02:00
|
|
|
PG_RETURN_BOOL(false);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2000-10-07 02:58:23 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Parser support routines for ACL-related statements.
|
|
|
|
*
|
|
|
|
* XXX CAUTION: these are called from gram.y, which is not allowed to
|
|
|
|
* do any table accesses. Therefore, it is not kosher to do things
|
|
|
|
* like trying to translate usernames to user IDs here. Keep it all
|
|
|
|
* in string form until statement execution time.
|
|
|
|
*/
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* aclmakepriv
|
1997-09-07 07:04:48 +02:00
|
|
|
* make a acl privilege string out of an existing privilege string
|
1996-07-09 08:22:35 +02:00
|
|
|
* and a new privilege
|
|
|
|
*
|
|
|
|
* does not add duplicate privileges
|
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
char *
|
2001-06-10 01:21:55 +02:00
|
|
|
aclmakepriv(const char *old_privlist, char new_priv)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
char *priv;
|
|
|
|
int i;
|
|
|
|
int l;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2001-05-27 11:59:30 +02:00
|
|
|
Assert(strlen(old_privlist) <= strlen(ACL_MODE_STR));
|
2001-10-25 07:50:21 +02:00
|
|
|
priv = palloc(strlen(ACL_MODE_STR) + 1);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (old_privlist == NULL || old_privlist[0] == '\0')
|
|
|
|
{
|
|
|
|
priv[0] = new_priv;
|
|
|
|
priv[1] = '\0';
|
|
|
|
return priv;
|
|
|
|
}
|
|
|
|
|
|
|
|
strcpy(priv, old_privlist);
|
|
|
|
|
|
|
|
l = strlen(old_privlist);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2001-05-27 11:59:30 +02:00
|
|
|
if (l == strlen(ACL_MODE_STR))
|
1997-09-07 07:04:48 +02:00
|
|
|
{ /* can't add any more privileges */
|
|
|
|
return priv;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check to see if the new privilege is already in the old string */
|
|
|
|
for (i = 0; i < l; i++)
|
|
|
|
{
|
|
|
|
if (priv[i] == new_priv)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i == l)
|
|
|
|
{ /* we really have a new privilege */
|
|
|
|
priv[l] = new_priv;
|
|
|
|
priv[l + 1] = '\0';
|
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
return priv;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* aclmakeuser
|
1997-09-07 07:04:48 +02:00
|
|
|
* user_type must be "A" - all users
|
|
|
|
* "G" - group
|
|
|
|
* "U" - user
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2000-10-07 02:58:23 +02:00
|
|
|
* Just concatenates the two strings together with a space in between.
|
|
|
|
* Per above comments, we can't try to resolve a user or group name here.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
char *
|
2001-06-10 01:21:55 +02:00
|
|
|
aclmakeuser(const char *user_type, const char *user)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
char *user_list;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-08-01 00:39:17 +02:00
|
|
|
user_list = palloc(strlen(user_type) + strlen(user) + 2);
|
1997-09-07 07:04:48 +02:00
|
|
|
sprintf(user_list, "%s %s", user_type, user);
|
|
|
|
return user_list;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2001-06-10 01:21:55 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*
|
2001-06-10 01:21:55 +02:00
|
|
|
* makeAclString: We take in the privileges and grantee as well as a
|
|
|
|
* single character '+' or '-' to indicate grant or revoke.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2000-10-07 02:58:23 +02:00
|
|
|
* We convert the information to the same external form recognized by
|
2001-06-10 01:21:55 +02:00
|
|
|
* aclitemin (see aclparse) and return that string. Conversion to
|
|
|
|
* internal form happens when the statement is executed.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2001-06-10 01:21:55 +02:00
|
|
|
char *
|
|
|
|
makeAclString(const char *privileges, const char *grantee, char grant_or_revoke)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
StringInfoData str;
|
2001-10-25 07:50:21 +02:00
|
|
|
char *ret;
|
1999-10-18 05:32:29 +02:00
|
|
|
|
|
|
|
initStringInfo(&str);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-10-18 05:32:29 +02:00
|
|
|
/* the grantee string is "G <group_name>", "U <user_name>", or "ALL" */
|
1997-09-07 07:04:48 +02:00
|
|
|
if (grantee[0] == 'G') /* group permissions */
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1999-10-18 05:32:29 +02:00
|
|
|
appendStringInfo(&str, "%s \"%s\"%c%s",
|
|
|
|
ACL_IDTYPE_GID_KEYWORD,
|
|
|
|
grantee + 2, grant_or_revoke, privileges);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else if (grantee[0] == 'U') /* user permission */
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1999-10-18 05:32:29 +02:00
|
|
|
appendStringInfo(&str, "%s \"%s\"%c%s",
|
|
|
|
ACL_IDTYPE_UID_KEYWORD,
|
|
|
|
grantee + 2, grant_or_revoke, privileges);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1999-10-18 05:32:29 +02:00
|
|
|
/* all permission */
|
|
|
|
appendStringInfo(&str, "%c%s",
|
|
|
|
grant_or_revoke, privileges);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
2001-06-10 01:21:55 +02:00
|
|
|
ret = pstrdup(str.data);
|
1999-10-18 05:32:29 +02:00
|
|
|
pfree(str.data);
|
2001-06-10 01:21:55 +02:00
|
|
|
return ret;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
2001-06-14 03:09:22 +02:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_table_privilege_name_name
|
|
|
|
* Check user privileges on a relation given
|
|
|
|
* name usename, name relname, and text priv name.
|
|
|
|
*
|
|
|
|
* RETURNS
|
|
|
|
* a boolean value
|
|
|
|
* 't' indicating user has the privilege
|
|
|
|
* 'f' indicating user does not have the privilege
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_table_privilege_name_name(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
Name username = PG_GETARG_NAME(0);
|
|
|
|
Name relname = PG_GETARG_NAME(1);
|
2001-10-25 07:50:21 +02:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_P(2);
|
2001-06-14 03:09:22 +02:00
|
|
|
bool result;
|
|
|
|
|
|
|
|
result = has_table_privilege_cname_cname(NameStr(*username),
|
|
|
|
NameStr(*relname),
|
|
|
|
priv_type_text);
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_table_privilege_name
|
|
|
|
* Check user privileges on a relation given
|
|
|
|
* name relname and text priv name.
|
|
|
|
* current_user is assumed
|
|
|
|
*
|
|
|
|
* RETURNS
|
|
|
|
* a boolean value
|
|
|
|
* 't' indicating user has the privilege
|
|
|
|
* 'f' indicating user does not have the privilege
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_table_privilege_name(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
Name relname = PG_GETARG_NAME(0);
|
2001-10-25 07:50:21 +02:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_P(1);
|
2001-06-14 03:09:22 +02:00
|
|
|
int32 usesysid;
|
|
|
|
bool result;
|
|
|
|
|
|
|
|
usesysid = GetUserId();
|
|
|
|
|
|
|
|
result = has_table_privilege_id_cname(usesysid,
|
|
|
|
NameStr(*relname),
|
|
|
|
priv_type_text);
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_table_privilege_name_id
|
|
|
|
* Check user privileges on a relation given
|
|
|
|
* name usename, rel oid, and text priv name.
|
|
|
|
*
|
|
|
|
* RETURNS
|
|
|
|
* a boolean value
|
|
|
|
* 't' indicating user has the privilege
|
|
|
|
* 'f' indicating user does not have the privilege
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_table_privilege_name_id(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
Name username = PG_GETARG_NAME(0);
|
|
|
|
Oid reloid = PG_GETARG_OID(1);
|
2001-10-25 07:50:21 +02:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_P(2);
|
2001-06-14 03:09:22 +02:00
|
|
|
bool result;
|
|
|
|
|
|
|
|
result = has_table_privilege_cname_id(NameStr(*username),
|
|
|
|
reloid,
|
|
|
|
priv_type_text);
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_table_privilege_id
|
|
|
|
* Check user privileges on a relation given
|
|
|
|
* rel oid, and text priv name.
|
|
|
|
* current_user is assumed
|
|
|
|
*
|
|
|
|
* RETURNS
|
|
|
|
* a boolean value
|
|
|
|
* 't' indicating user has the privilege
|
|
|
|
* 'f' indicating user does not have the privilege
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_table_privilege_id(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
Oid reloid = PG_GETARG_OID(0);
|
2001-10-25 07:50:21 +02:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_P(1);
|
2001-06-14 03:09:22 +02:00
|
|
|
char *relname;
|
|
|
|
int32 usesysid;
|
|
|
|
AclMode mode;
|
|
|
|
int32 result;
|
|
|
|
|
|
|
|
usesysid = GetUserId();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Lookup relname based on rel oid
|
|
|
|
*/
|
|
|
|
relname = get_rel_name(reloid);
|
|
|
|
if (relname == NULL)
|
|
|
|
elog(ERROR, "has_table_privilege: invalid relation oid %u",
|
|
|
|
reloid);
|
|
|
|
|
2001-10-25 07:50:21 +02:00
|
|
|
/*
|
2001-06-14 03:09:22 +02:00
|
|
|
* Convert priv_type_text to an AclMode
|
|
|
|
*/
|
|
|
|
mode = convert_priv_string(priv_type_text);
|
|
|
|
|
2001-10-25 07:50:21 +02:00
|
|
|
/*
|
2001-06-14 03:09:22 +02:00
|
|
|
* Finally, check for the privilege
|
|
|
|
*/
|
|
|
|
result = pg_aclcheck(relname, usesysid, mode);
|
|
|
|
|
|
|
|
if (result == ACLCHECK_OK)
|
|
|
|
PG_RETURN_BOOL(true);
|
|
|
|
else
|
|
|
|
PG_RETURN_BOOL(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_table_privilege_id_name
|
|
|
|
* Check user privileges on a relation given
|
|
|
|
* usesysid, name relname, and priv name.
|
|
|
|
*
|
|
|
|
* RETURNS
|
|
|
|
* a boolean value
|
|
|
|
* 't' indicating user has the privilege
|
|
|
|
* 'f' indicating user does not have the privilege
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_table_privilege_id_name(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
int32 usesysid = PG_GETARG_INT32(0);
|
|
|
|
Name relname = PG_GETARG_NAME(1);
|
2001-10-25 07:50:21 +02:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_P(2);
|
2001-06-14 03:09:22 +02:00
|
|
|
bool result;
|
|
|
|
|
|
|
|
result = has_table_privilege_id_cname(usesysid,
|
|
|
|
NameStr(*relname),
|
|
|
|
priv_type_text);
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_table_privilege_id_id
|
|
|
|
* Check user privileges on a relation given
|
|
|
|
* usesysid, rel oid, and priv name.
|
|
|
|
*
|
|
|
|
* RETURNS
|
|
|
|
* a boolean value
|
|
|
|
* 't' indicating user has the privilege
|
|
|
|
* 'f' indicating user does not have the privilege
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
has_table_privilege_id_id(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
int32 usesysid = PG_GETARG_INT32(0);
|
|
|
|
Oid reloid = PG_GETARG_OID(1);
|
2001-10-25 07:50:21 +02:00
|
|
|
text *priv_type_text = PG_GETARG_TEXT_P(2);
|
|
|
|
char *relname;
|
2001-06-14 03:09:22 +02:00
|
|
|
AclMode mode;
|
|
|
|
int32 result;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Lookup relname based on rel oid
|
|
|
|
*/
|
|
|
|
relname = get_rel_name(reloid);
|
|
|
|
if (relname == NULL)
|
|
|
|
elog(ERROR, "has_table_privilege: invalid relation oid %u",
|
|
|
|
reloid);
|
|
|
|
|
2001-10-25 07:50:21 +02:00
|
|
|
/*
|
2001-06-14 03:09:22 +02:00
|
|
|
* Convert priv_type_text to an AclMode
|
|
|
|
*/
|
|
|
|
mode = convert_priv_string(priv_type_text);
|
|
|
|
|
2001-10-25 07:50:21 +02:00
|
|
|
/*
|
2001-06-14 03:09:22 +02:00
|
|
|
* Finally, check for the privilege
|
|
|
|
*/
|
|
|
|
result = pg_aclcheck(relname, usesysid, mode);
|
|
|
|
|
|
|
|
if (result == ACLCHECK_OK)
|
|
|
|
PG_RETURN_BOOL(true);
|
|
|
|
else
|
|
|
|
PG_RETURN_BOOL(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Internal functions.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* convert_priv_string
|
|
|
|
* Internal function.
|
|
|
|
* Return mode from priv_type string
|
|
|
|
*
|
|
|
|
* RETURNS
|
|
|
|
* AclMode
|
|
|
|
*/
|
|
|
|
|
|
|
|
static AclMode
|
|
|
|
convert_priv_string(text *priv_type_text)
|
|
|
|
{
|
2001-10-25 07:50:21 +02:00
|
|
|
char *priv_type = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(priv_type_text)));
|
2001-06-14 03:09:22 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Return mode from priv_type string
|
|
|
|
*/
|
|
|
|
if (strcasecmp(priv_type, "SELECT") == 0)
|
|
|
|
return ACL_SELECT;
|
|
|
|
|
|
|
|
if (strcasecmp(priv_type, "INSERT") == 0)
|
|
|
|
return ACL_INSERT;
|
|
|
|
|
|
|
|
if (strcasecmp(priv_type, "UPDATE") == 0)
|
|
|
|
return ACL_UPDATE;
|
|
|
|
|
|
|
|
if (strcasecmp(priv_type, "DELETE") == 0)
|
|
|
|
return ACL_DELETE;
|
|
|
|
|
|
|
|
if (strcasecmp(priv_type, "RULE") == 0)
|
|
|
|
return ACL_RULE;
|
|
|
|
|
|
|
|
if (strcasecmp(priv_type, "REFERENCES") == 0)
|
|
|
|
return ACL_REFERENCES;
|
|
|
|
|
|
|
|
if (strcasecmp(priv_type, "TRIGGER") == 0)
|
|
|
|
return ACL_TRIGGER;
|
|
|
|
|
|
|
|
elog(ERROR, "has_table_privilege: invalid privilege type %s", priv_type);
|
2001-10-25 07:50:21 +02:00
|
|
|
|
2001-06-14 03:09:22 +02:00
|
|
|
/*
|
|
|
|
* We should never get here, but stop the compiler from complaining
|
|
|
|
*/
|
|
|
|
return ACL_NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_table_privilege_cname_cname
|
|
|
|
* Check user privileges on a relation given
|
|
|
|
* char *usename, char *relname, and text priv name.
|
|
|
|
*
|
|
|
|
* RETURNS
|
|
|
|
* a boolean value
|
|
|
|
* 't' indicating user has the privilege
|
|
|
|
* 'f' indicating user does not have the privilege
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
has_table_privilege_cname_cname(char *username, char *relname,
|
|
|
|
text *priv_type_text)
|
|
|
|
{
|
|
|
|
int32 usesysid;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Lookup userid based on username
|
|
|
|
*/
|
|
|
|
usesysid = get_usesysid(username);
|
|
|
|
|
|
|
|
/*
|
2001-10-25 07:50:21 +02:00
|
|
|
* Make use of has_table_privilege_id_cname. It accepts the arguments
|
|
|
|
* we now have.
|
2001-06-14 03:09:22 +02:00
|
|
|
*/
|
|
|
|
return has_table_privilege_id_cname(usesysid, relname, priv_type_text);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_table_privilege_cname_id
|
|
|
|
* Check user privileges on a relation given
|
|
|
|
* char *usename, rel oid, and text priv name.
|
|
|
|
*
|
|
|
|
* RETURNS
|
|
|
|
* a boolean value
|
|
|
|
* 't' indicating user has the privilege
|
|
|
|
* 'f' indicating user does not have the privilege
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
has_table_privilege_cname_id(char *username, Oid reloid,
|
|
|
|
text *priv_type_text)
|
|
|
|
{
|
|
|
|
int32 usesysid;
|
2001-10-25 07:50:21 +02:00
|
|
|
char *relname;
|
2001-06-14 03:09:22 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Lookup userid based on username
|
|
|
|
*/
|
|
|
|
usesysid = get_usesysid(username);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Lookup relname based on rel oid
|
|
|
|
*/
|
|
|
|
relname = get_rel_name(reloid);
|
|
|
|
if (relname == NULL)
|
|
|
|
elog(ERROR, "has_table_privilege: invalid relation oid %u",
|
|
|
|
reloid);
|
|
|
|
|
|
|
|
/*
|
2001-10-25 07:50:21 +02:00
|
|
|
* Make use of has_table_privilege_id_cname. It accepts the arguments
|
|
|
|
* we now have.
|
2001-06-14 03:09:22 +02:00
|
|
|
*/
|
|
|
|
return has_table_privilege_id_cname(usesysid, relname, priv_type_text);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has_table_privilege_id_cname
|
|
|
|
* Check user privileges on a relation given
|
|
|
|
* usesysid, char *relname, and text priv name.
|
|
|
|
*
|
|
|
|
* RETURNS
|
|
|
|
* a boolean value
|
|
|
|
* 't' indicating user has the privilege
|
|
|
|
* 'f' indicating user does not have the privilege
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
has_table_privilege_id_cname(int32 usesysid, char *relname,
|
|
|
|
text *priv_type_text)
|
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
|
|
|
AclMode mode;
|
|
|
|
int32 result;
|
|
|
|
|
|
|
|
/*
|
2001-10-25 07:50:21 +02:00
|
|
|
* Check relname is valid. This is needed to deal with the case when
|
|
|
|
* usename is a superuser in which case pg_aclcheck simply returns
|
|
|
|
* ACLCHECK_OK without validating relname
|
2001-06-14 03:09:22 +02:00
|
|
|
*/
|
|
|
|
tuple = SearchSysCache(RELNAME,
|
|
|
|
PointerGetDatum(relname),
|
|
|
|
0, 0, 0);
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
elog(ERROR, "has_table_privilege: relation \"%s\" does not exist",
|
|
|
|
relname);
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
2001-10-25 07:50:21 +02:00
|
|
|
/*
|
2001-06-14 03:09:22 +02:00
|
|
|
* Convert priv_type_text to an AclMode
|
|
|
|
*/
|
|
|
|
mode = convert_priv_string(priv_type_text);
|
|
|
|
|
2001-10-25 07:50:21 +02:00
|
|
|
/*
|
2001-06-14 03:09:22 +02:00
|
|
|
* Finally, check for the privilege
|
|
|
|
*/
|
|
|
|
result = pg_aclcheck(relname, usesysid, mode);
|
|
|
|
|
|
|
|
if (result == ACLCHECK_OK)
|
|
|
|
return true;
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|