aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/acl.c
diff options
context:
space:
mode:
authorMarc G. Fournier <scrappy@hub.org>1996-07-09 06:22:35 +0000
committerMarc G. Fournier <scrappy@hub.org>1996-07-09 06:22:35 +0000
commitd31084e9d1118b25fd16580d9d8c2924b5740dff (patch)
tree3179e66307d54df9c7b966543550e601eb55e668 /src/backend/utils/adt/acl.c
downloadpostgresql-PG95-1_01.tar.gz
postgresql-PG95-1_01.zip
Postgres95 1.01 Distribution - Virgin SourcesPG95-1_01
Diffstat (limited to 'src/backend/utils/adt/acl.c')
-rw-r--r--src/backend/utils/adt/acl.c618
1 files changed, 618 insertions, 0 deletions
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
new file mode 100644
index 00000000000..289b4575c7b
--- /dev/null
+++ b/src/backend/utils/adt/acl.c
@@ -0,0 +1,618 @@
+/*-------------------------------------------------------------------------
+ *
+ * acl.c--
+ * Basic access control list data structures manipulation routines.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <ctype.h>
+#include <string.h>
+#include "postgres.h"
+#include "c.h"
+#include "utils/acl.h"
+#include "access/htup.h"
+#include "catalog/pg_user.h"
+#include "utils/syscache.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+static char *getid(char *s, char *n);
+static int32 aclitemeq(AclItem *a1, AclItem *a2);
+static int32 aclitemgt(AclItem *a1, AclItem *a2);
+
+#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.
+ *
+ * RETURNS:
+ * the string position in 's' that points to the next non-space character
+ * in 's'. 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;
+
+ Assert(s && n);
+
+ while (isspace(*s))
+ ++s;
+ for (id = s, len = 0; isalnum(*s); ++len, ++s)
+ ;
+ if (len > sizeof(NameData))
+ elog(WARN, "getid: identifier cannot be >%d characters",
+ sizeof(NameData));
+ if (len > 0)
+ memmove(n, id, len);
+ n[len] = '\0';
+ 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.
+ */
+char *
+aclparse(char *s, AclItem *aip, unsigned *modechg)
+{
+ HeapTuple htp;
+ char name[NAMEDATALEN+1];
+ extern AclId get_grosysid();
+
+ Assert(s && aip && modechg);
+
+ 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(WARN, "aclparse: bad keyword, must be [group|user]");
+ }
+ s = getid(s, name); /* move s to the name beyond the keyword */
+ if (name[0] == '\0')
+ elog(WARN, "aclparse: a name must follow the [group|user] keyword");
+ }
+ if (name[0] == '\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(WARN, "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(WARN, "aclparse: mode flags must use \"%s\"",
+ ACL_MODE_STR);
+ }
+ }
+
+ switch (aip->ai_idtype) {
+ case ACL_IDTYPE_UID:
+ htp = SearchSysCacheTuple(USENAME, PointerGetDatum(name),
+ 0,0,0);
+ if (!HeapTupleIsValid(htp))
+ elog(WARN, "aclparse: non-existent user \"%s\"", name);
+ aip->ai_id = ((Form_pg_user) GETSTRUCT(htp))->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(WARN, "makeacl: invalid size: %d\n", n);
+ size = ACL_N_SIZE(n);
+ if (!(new_acl = (Acl *) palloc(size)))
+ elog(WARN, "makeacl: palloc failed on %d\n", 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(WARN, "aclitemin: null string");
+
+ aip = (AclItem *) palloc(sizeof(AclItem));
+ if (!aip)
+ elog(WARN, "aclitemin: palloc failed");
+ s = aclparse(s, aip, &modechg);
+ if (modechg != ACL_MODECHG_EQL)
+ elog(WARN, "aclitemin: cannot accept anything but = ACLs");
+ while (isspace(*s))
+ ++s;
+ if (*s)
+ elog(WARN, "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)
+{
+ register char *p;
+ char *out;
+ HeapTuple htp;
+ 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(WARN, "aclitemout: palloc failed");
+ *p = '\0';
+
+ switch (aip->ai_idtype) {
+ case ACL_IDTYPE_UID:
+ htp = SearchSysCacheTuple(USESYSID, ObjectIdGetDatum(aip->ai_id),
+ 0,0,0);
+ if (!HeapTupleIsValid(htp)) {
+ char *tmp = int2out(aip->ai_id);
+
+ elog(NOTICE, "aclitemout: usesysid %d not found",
+ aip->ai_id);
+ (void) strcat(p, tmp);
+ pfree(tmp);
+ } else
+ (void) strncat(p, (char *) &((Form_pg_user)
+ GETSTRUCT(htp))->usename,
+ sizeof(NameData));
+ break;
+ case ACL_IDTYPE_GID:
+ (void) strcat(p, "group ");
+ tmpname = get_groname(aip->ai_id);
+ (void) strncat(p, tmpname, NAMEDATALEN);
+ pfree(tmpname);
+ break;
+ case ACL_IDTYPE_WORLD:
+ break;
+ default:
+ elog(WARN, "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 = '\0';
+
+ 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(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 = 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()
+{
+ 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 = 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(WARN, "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 ;
+ 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(WARN, "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
+ *
+ * the CALLER is reponsible for free'ing the string returned
+ */
+
+char*
+aclmakepriv(char* old_privlist, char new_priv)
+{
+ char* priv;
+ int i;
+ int l;
+
+ Assert(strlen(old_privlist)<5);
+ priv = malloc(5); /* at most "rwaR" */;
+
+ 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);
+
+ 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] = '\0';
+ }
+
+ 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
+ *
+ * the CALLER is responsible for freeing the memory allocated
+ */
+
+char*
+aclmakeuser(char* user_type, char* user)
+{
+ char* user_list;
+
+ user_list = malloc(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];
+
+ 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 %s%c%s",
+ ACL_IDTYPE_GID_KEYWORD,
+ grantee+2, grant_or_revoke,privileges);
+ }
+ else if (grantee[0] == 'U') /* user permission */
+ {
+ sprintf(str,"%s %s%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;
+}
+
+