acl.c
上传用户:blenddy
上传日期:2007-01-07
资源大小:6495k
文件大小:16k
- /*-------------------------------------------------------------------------
- *
- * acl.c
- * Basic access control list data structures manipulation routines.
- *
- * Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /usr/local/cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.37.2.1 1999/08/02 05:24:49 scrappy Exp $
- *
- *-------------------------------------------------------------------------
- */
- #include <ctype.h>
- #include "postgres.h"
- #include "catalog/catalog.h"
- #include "catalog/pg_shadow.h"
- #include "catalog/pg_type.h"
- #include "miscadmin.h"
- #include "utils/acl.h"
- #include "utils/memutils.h"
- #include "utils/syscache.h"
- static char *getid(char *s, char *n);
- static int32 aclitemeq(AclItem *a1, AclItem *a2);
- static int32 aclitemgt(AclItem *a1, AclItem *a2);
- static char *aclparse(char *s, AclItem *aip, unsigned *modechg);
- #define ACL_IDTYPE_GID_KEYWORD "group"
- #define ACL_IDTYPE_UID_KEYWORD "user"
- /*
- * getid
- * Consumes the first alphanumeric string (identifier) found in string
- * 's', ignoring any leading white space. If it finds a double quote
- * it returns the word inside the quotes.
- *
- * RETURNS:
- * the string position in 's' that points to the next non-space character
- * in 's', after any quotes. Also:
- * - loads the identifier into 'name'. (If no identifier is found, 'name'
- * contains an empty string).
- */
- static char *
- getid(char *s, char *n)
- {
- unsigned len;
- char *id;
- int in_quotes = 0;
- Assert(s && n);
- while (isspace(*s))
- ++s;
- if (*s == '"')
- {
- in_quotes = 1;
- s++;
- }
- for (id = s, len = 0; isalnum(*s) || *s == '_' || in_quotes; ++len, ++s)
- {
- if (in_quotes && *s == '"')
- {
- len--;
- in_quotes = 0;
- }
- }
- if (len > sizeof(NameData))
- elog(ERROR, "getid: identifier cannot be >%d characters",
- sizeof(NameData));
- if (len > 0)
- memmove(n, id, len);
- n[len] = ' ';
- while (isspace(*s))
- ++s;
- return s;
- }
- /*
- * aclparse
- * 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.
- *
- * This routine is called by the parser as well as aclitemin(), hence
- * the added generality.
- *
- * RETURNS:
- * 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.
- */
- static char *
- aclparse(char *s, AclItem *aip, unsigned *modechg)
- {
- HeapTuple htup;
- char name[NAMEDATALEN];
- Assert(s && aip && modechg);
- #ifdef ACLDEBUG_TRACE
- printf("aclparse: input = '%s'n", s);
- #endif
- 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)
- {
- /* we just read a keyword, not a name */
- if (!strcmp(name, ACL_IDTYPE_GID_KEYWORD))
- aip->ai_idtype = ACL_IDTYPE_GID;
- else if (strcmp(name, ACL_IDTYPE_UID_KEYWORD))
- elog(ERROR, "aclparse: bad keyword, must be [group|user]");
- s = getid(s, name); /* move s to the name beyond the keyword */
- if (name[0] == ' ')
- elog(ERROR, "aclparse: a name must follow the [group|user] keyword");
- }
- if (name[0] == ' ')
- aip->ai_idtype = ACL_IDTYPE_WORLD;
- switch (*s)
- {
- 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:
- elog(ERROR, "aclparse: mode change flag must use "%s"",
- ACL_MODECHG_STR);
- }
- aip->ai_mode = ACL_NO;
- while (isalpha(*++s))
- {
- switch (*s)
- {
- case ACL_MODE_AP_CHR:
- aip->ai_mode |= ACL_AP;
- break;
- case ACL_MODE_RD_CHR:
- aip->ai_mode |= ACL_RD;
- break;
- case ACL_MODE_WR_CHR:
- aip->ai_mode |= ACL_WR;
- break;
- case ACL_MODE_RU_CHR:
- aip->ai_mode |= ACL_RU;
- break;
- default:
- elog(ERROR, "aclparse: mode flags must use "%s"",
- ACL_MODE_STR);
- }
- }
- switch (aip->ai_idtype)
- {
- case ACL_IDTYPE_UID:
- htup = SearchSysCacheTuple(USENAME,
- PointerGetDatum(name),
- 0, 0, 0);
- if (!HeapTupleIsValid(htup))
- elog(ERROR, "aclparse: non-existent user "%s"", name);
- aip->ai_id = ((Form_pg_shadow) GETSTRUCT(htup))->usesysid;
- break;
- case ACL_IDTYPE_GID:
- aip->ai_id = get_grosysid(name);
- break;
- case ACL_IDTYPE_WORLD:
- aip->ai_id = ACL_ID_WORLD;
- break;
- }
- #ifdef ACLDEBUG_TRACE
- elog(DEBUG, "aclparse: correctly read [%x %d %x], modechg=%x",
- aip->ai_idtype, aip->ai_id, aip->ai_mode, *modechg);
- #endif
- return s;
- }
- /*
- * makeacl
- * Allocates storage for a new Acl with 'n' entries.
- *
- * RETURNS:
- * the new Acl
- */
- Acl *
- makeacl(int n)
- {
- Acl *new_acl;
- Size size;
- if (n < 0)
- elog(ERROR, "makeacl: invalid size: %dn", n);
- size = ACL_N_SIZE(n);
- if (!(new_acl = (Acl *) palloc(size)))
- elog(ERROR, "makeacl: palloc failed on %dn", size);
- MemSet((char *) new_acl, 0, size);
- new_acl->size = size;
- new_acl->ndim = 1;
- new_acl->flags = 0;
- ARR_LBOUND(new_acl)[0] = 0;
- ARR_DIMS(new_acl)[0] = n;
- return new_acl;
- }
- /*
- * aclitemin
- * Allocates storage for, and fills in, a new AclItem given a string
- * 's' that contains an ACL specification. See aclparse for details.
- *
- * RETURNS:
- * the new AclItem
- */
- AclItem *
- aclitemin(char *s)
- {
- unsigned modechg;
- AclItem *aip;
- if (!s)
- elog(ERROR, "aclitemin: null string");
- aip = (AclItem *) palloc(sizeof(AclItem));
- if (!aip)
- elog(ERROR, "aclitemin: palloc failed");
- s = aclparse(s, aip, &modechg);
- if (modechg != ACL_MODECHG_EQL)
- elog(ERROR, "aclitemin: cannot accept anything but = ACLs");
- while (isspace(*s))
- ++s;
- if (*s)
- elog(ERROR, "aclitemin: extra garbage at end of specification");
- return aip;
- }
- /*
- * aclitemout
- * Allocates storage for, and fills in, a new null-delimited string
- * containing a formatted ACL specification. See aclparse for details.
- *
- * RETURNS:
- * the new string
- */
- char *
- aclitemout(AclItem *aip)
- {
- char *p;
- char *out;
- HeapTuple htup;
- unsigned i;
- static AclItem default_aclitem = {ACL_ID_WORLD,
- ACL_IDTYPE_WORLD,
- ACL_WORLD_DEFAULT};
- extern char *int2out();
- char *tmpname;
- if (!aip)
- aip = &default_aclitem;
- p = out = palloc(strlen("group =arwR ") + 1 + NAMEDATALEN);
- if (!out)
- elog(ERROR, "aclitemout: palloc failed");
- *p = ' ';
- switch (aip->ai_idtype)
- {
- case ACL_IDTYPE_UID:
- htup = SearchSysCacheTuple(USESYSID,
- ObjectIdGetDatum(aip->ai_id),
- 0, 0, 0);
- if (!HeapTupleIsValid(htup))
- {
- char *tmp = int2out(aip->ai_id);
- #ifdef NOT_USED
- When this elog(NOTICE) goes to the libpq client,
- it crashes the
- client because the NOTICE protocol is coming right in the middle
- of a request for a field value.We skip the NOTICE for now.
- elog(NOTICE, "aclitemout: usesysid %d not found",
- aip->ai_id);
- #endif
- strcat(p, tmp);
- pfree(tmp);
- }
- else
- strncat(p, (char *) &((Form_pg_shadow)
- GETSTRUCT(htup))->usename,
- sizeof(NameData));
- break;
- case ACL_IDTYPE_GID:
- strcat(p, "group ");
- tmpname = get_groname(aip->ai_id);
- strncat(p, tmpname, NAMEDATALEN);
- break;
- case ACL_IDTYPE_WORLD:
- break;
- default:
- elog(ERROR, "aclitemout: bad ai_idtype: %d", aip->ai_idtype);
- break;
- }
- while (*p)
- ++p;
- *p++ = '=';
- for (i = 0; i < N_ACL_MODES; ++i)
- if ((aip->ai_mode >> i) & 01)
- *p++ = ACL_MODE_STR[i];
- *p = ' ';
- return out;
- }
- /*
- * aclitemeq
- * aclitemgt
- * AclItem equality and greater-than comparison routines.
- * Two AclItems are equal iff they are both NULL or they have the
- * same identifier (and identifier type).
- *
- * RETURNS:
- * a boolean value indicating = or >
- */
- static int32
- aclitemeq(AclItem *a1, AclItem *a2)
- {
- if (!a1 && !a2)
- return 1;
- if (!a1 || !a2)
- return 0;
- return a1->ai_idtype == a2->ai_idtype && a1->ai_id == a2->ai_id;
- }
- static int32
- aclitemgt(AclItem *a1, AclItem *a2)
- {
- if (a1 && !a2)
- return 1;
- if (!a1 || !a2)
- return 0;
- return ((a1->ai_idtype > a2->ai_idtype) ||
- (a1->ai_idtype == a2->ai_idtype && a1->ai_id > a2->ai_id));
- }
- Acl *
- aclownerdefault(char *relname, AclId ownerid)
- {
- Acl *acl;
- AclItem *aip;
- acl = makeacl(2);
- aip = ACL_DAT(acl);
- aip[0].ai_idtype = ACL_IDTYPE_WORLD;
- aip[0].ai_id = ACL_ID_WORLD;
- aip[0].ai_mode = IsSystemRelationName(relname) ? ACL_RD : ACL_WORLD_DEFAULT;
- aip[1].ai_idtype = ACL_IDTYPE_UID;
- aip[1].ai_id = ownerid;
- aip[1].ai_mode = ACL_OWNER_DEFAULT;
- return acl;
- }
- Acl *
- acldefault(char *relname)
- {
- Acl *acl;
- AclItem *aip;
- acl = makeacl(1);
- aip = ACL_DAT(acl);
- aip[0].ai_idtype = ACL_IDTYPE_WORLD;
- aip[0].ai_id = ACL_ID_WORLD;
- aip[0].ai_mode = IsSystemRelationName(relname) ? ACL_RD : ACL_WORLD_DEFAULT;
- return acl;
- }
- Acl *
- aclinsert3(Acl *old_acl, AclItem *mod_aip, unsigned modechg)
- {
- Acl *new_acl;
- AclItem *old_aip,
- *new_aip;
- unsigned src,
- dst,
- num;
- if (!old_acl || ACL_NUM(old_acl) < 1)
- {
- new_acl = makeacl(0);
- return new_acl;
- }
- if (!mod_aip)
- {
- new_acl = makeacl(ACL_NUM(old_acl));
- memmove((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
- return new_acl;
- }
- 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)
- ;
- if (dst < num && aclitemeq(mod_aip, old_aip + dst))
- {
- /* modify in-place */
- new_acl = makeacl(ACL_NUM(old_acl));
- memmove((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
- new_aip = ACL_DAT(new_acl);
- src = dst;
- }
- else
- {
- new_acl = makeacl(num + 1);
- new_aip = ACL_DAT(new_acl);
- if (dst == 0)
- { /* start */
- elog(ERROR, "aclinsert3: insertion before world ACL??");
- }
- else if (dst >= num)
- { /* end */
- memmove((char *) new_aip,
- (char *) old_aip,
- num * sizeof(AclItem));
- }
- else
- { /* middle */
- memmove((char *) new_aip,
- (char *) old_aip,
- dst * sizeof(AclItem));
- memmove((char *) (new_aip + dst + 1),
- (char *) (old_aip + dst),
- (num - dst) * sizeof(AclItem));
- }
- new_aip[dst].ai_id = mod_aip->ai_id;
- new_aip[dst].ai_idtype = mod_aip->ai_idtype;
- num++; /* set num to the size of new_acl */
- src = 0; /* world entry */
- }
- switch (modechg)
- {
- case ACL_MODECHG_ADD:
- new_aip[dst].ai_mode = old_aip[src].ai_mode | mod_aip->ai_mode;
- break;
- case ACL_MODECHG_DEL:
- new_aip[dst].ai_mode = old_aip[src].ai_mode & ~mod_aip->ai_mode;
- break;
- case ACL_MODECHG_EQL:
- new_aip[dst].ai_mode = mod_aip->ai_mode;
- break;
- }
- /*
- * if the newly added entry has no permissions, delete it from the
- * list. For example, this helps in removing entries for users who no
- * longer exists...
- */
- for (dst = 1; dst < num; dst++)
- {
- if (new_aip[dst].ai_mode == 0)
- {
- int i;
- for (i = dst + 1; i < num; i++)
- {
- new_aip[i - 1].ai_id = new_aip[i].ai_id;
- new_aip[i - 1].ai_idtype = new_aip[i].ai_idtype;
- new_aip[i - 1].ai_mode = new_aip[i].ai_mode;
- }
- ARR_DIMS(new_acl)[0] = num - 1;
- /* Adjust also the array size because it is used for memmove */
- ARR_SIZE(new_acl) -= sizeof(AclItem);
- break;
- }
- }
- return new_acl;
- }
- /*
- * aclinsert
- *
- */
- Acl *
- aclinsert(Acl *old_acl, AclItem *mod_aip)
- {
- return aclinsert3(old_acl, mod_aip, ACL_MODECHG_EQL);
- }
- Acl *
- aclremove(Acl *old_acl, AclItem *mod_aip)
- {
- Acl *new_acl;
- AclItem *old_aip,
- *new_aip;
- unsigned dst,
- old_num,
- new_num;
- if (!old_acl || ACL_NUM(old_acl) < 1)
- {
- new_acl = makeacl(0);
- return new_acl;
- }
- if (!mod_aip)
- {
- new_acl = makeacl(ACL_NUM(old_acl));
- memmove((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
- return new_acl;
- }
- old_num = ACL_NUM(old_acl);
- old_aip = ACL_DAT(old_acl);
- for (dst = 0; dst < old_num && !aclitemeq(mod_aip, old_aip + dst); ++dst)
- ;
- if (dst >= old_num)
- { /* not found or empty */
- new_acl = makeacl(ACL_NUM(old_acl));
- memmove((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
- }
- else
- {
- new_num = old_num - 1;
- new_acl = makeacl(ACL_NUM(old_acl) - 1);
- new_aip = ACL_DAT(new_acl);
- if (dst == 0)
- { /* start */
- elog(ERROR, "aclremove: removal of the world ACL??");
- }
- else if (dst == old_num - 1)
- { /* end */
- memmove((char *) new_aip,
- (char *) old_aip,
- new_num * sizeof(AclItem));
- }
- else
- { /* middle */
- memmove((char *) new_aip,
- (char *) old_aip,
- dst * sizeof(AclItem));
- memmove((char *) (new_aip + dst),
- (char *) (old_aip + dst + 1),
- (new_num - dst) * sizeof(AclItem));
- }
- }
- return new_acl;
- }
- int32
- aclcontains(Acl *acl, AclItem *aip)
- {
- unsigned i,
- num;
- AclItem *aidat;
- if (!acl || !aip || ((num = ACL_NUM(acl)) < 1))
- return 0;
- aidat = ACL_DAT(acl);
- for (i = 0; i < num; ++i)
- if (aclitemeq(aip, aidat + i))
- return 1;
- return 0;
- }
- /* parser support routines */
- /*
- * aclmakepriv
- * make a acl privilege string out of an existing privilege string
- * and a new privilege
- *
- * does not add duplicate privileges
- *
- */
- char *
- aclmakepriv(char *old_privlist, char new_priv)
- {
- char *priv;
- int i;
- int l;
- Assert(strlen(old_privlist) < 5);
- priv = palloc(5); /* at most "rwaR" */ ;
- if (old_privlist == NULL || old_privlist[0] == ' ')
- {
- priv[0] = new_priv;
- priv[1] = ' ';
- return priv;
- }
- strcpy(priv, old_privlist);
- l = strlen(old_privlist);
- if (l == 4)
- { /* 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] = ' ';
- }
- return priv;
- }
- /*
- * aclmakeuser
- * user_type must be "A" - all users
- * "G" - group
- * "U" - user
- *
- * concatentates the two strings together with a space in between
- *
- * this routine is used in the parser
- *
- */
- char *
- aclmakeuser(char *user_type, char *user)
- {
- char *user_list;
- user_list = palloc(strlen(user) + 3);
- sprintf(user_list, "%s %s", user_type, user);
- return user_list;
- }
- /*
- * makeAclStmt:
- * this is a helper routine called by the parser
- * create a ChangeAclStmt
- * we take in the privilegs, relation_name_list, and grantee
- * as well as a single character '+' or '-' to indicate grant or revoke
- *
- * returns a new ChangeACLStmt*
- *
- * this routines works by creating a old-style changle acl string and
- * then calling aclparse;
- */
- ChangeACLStmt *
- makeAclStmt(char *privileges, List *rel_list, char *grantee,
- char grant_or_revoke)
- {
- ChangeACLStmt *n = makeNode(ChangeACLStmt);
- char str[MAX_PARSE_BUFFER];
- /* see comment in pg_type.h */
- Assert(ACLITEMSIZE == sizeof(AclItem));
- n->aclitem = (AclItem *) palloc(sizeof(AclItem));
- /* the grantee string is "G <group_name>", "U <user_name>", or "ALL" */
- if (grantee[0] == 'G') /* group permissions */
- {
- sprintf(str, "%s %c%s%c%c%s",
- ACL_IDTYPE_GID_KEYWORD,
- '"', grantee + 2, '"', grant_or_revoke, privileges);
- }
- else if (grantee[0] == 'U') /* user permission */
- {
- sprintf(str, "%s %c%s%c%c%s",
- ACL_IDTYPE_UID_KEYWORD,
- '"', grantee + 2, '"', grant_or_revoke, privileges);
- }
- else
- /* all permission */
- {
- sprintf(str, "%c%s",
- grant_or_revoke, privileges);
- }
- n->relNames = rel_list;
- aclparse(str, n->aclitem, (unsigned *) &n->modechg);
- return n;
- }