1 /*-------------------------------------------------------------------------
4 * Basic access control list data structures manipulation routines.
6 * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.73 2002/04/27 03:45:03 tgl Exp $
13 *-------------------------------------------------------------------------
19 #include "catalog/namespace.h"
20 #include "catalog/pg_shadow.h"
21 #include "miscadmin.h"
22 #include "utils/acl.h"
23 #include "utils/builtins.h"
24 #include "utils/lsyscache.h"
25 #include "utils/syscache.h"
28 #define ACL_IDTYPE_GID_KEYWORD "group"
29 #define ACL_IDTYPE_UID_KEYWORD "user"
31 static const char *getid(const char *s, char *n);
32 static Acl *makeacl(int n);
33 static const char *aclparse(const char *s, AclItem *aip, unsigned *modechg);
34 static bool aclitemeq(const AclItem *a1, const AclItem *a2);
35 static bool aclitemgt(const AclItem *a1, const AclItem *a2);
37 static AclMode convert_priv_string(text *priv_type_text);
38 static Oid convert_rel_name(text *relname);
43 * Consumes the first alphanumeric string (identifier) found in string
44 * 's', ignoring any leading white space. If it finds a double quote
45 * it returns the word inside the quotes.
48 * the string position in 's' that points to the next non-space character
49 * in 's', after any quotes. Also:
50 * - loads the identifier into 'name'. (If no identifier is found, 'name'
51 * contains an empty string.) name must be NAMEDATALEN bytes.
54 getid(const char *s, char *n)
62 while (isspace((unsigned char) *s))
72 isalnum((unsigned char) *s) || *s == '_' || in_quotes;
75 if (in_quotes && *s == '"')
81 if (len >= NAMEDATALEN)
82 elog(ERROR, "getid: identifier must be <%d characters",
87 while (isspace((unsigned char) *s))
94 * Consumes and parses an ACL specification of the form:
95 * [group|user] [A-Za-z0-9]*[+-=][rwaR]*
96 * from string 's', ignoring any leading white space or white space
97 * between the optional id type keyword (group|user) and the actual
100 * This routine is called by the parser as well as aclitemin(), hence
101 * the added generality.
104 * the string position in 's' immediately following the ACL
105 * specification. Also:
106 * - loads the structure pointed to by 'aip' with the appropriate
107 * UID/GID, id type identifier and mode type values.
108 * - loads 'modechg' with the mode change flag.
111 aclparse(const char *s, AclItem *aip, unsigned *modechg)
115 char name[NAMEDATALEN];
117 Assert(s && aip && modechg);
120 elog(LOG, "aclparse: input = '%s'", s);
122 idtype = ACL_IDTYPE_UID;
124 if (*s != ACL_MODECHG_ADD_CHR &&
125 *s != ACL_MODECHG_DEL_CHR &&
126 *s != ACL_MODECHG_EQL_CHR)
128 /* we just read a keyword, not a name */
129 if (strcmp(name, ACL_IDTYPE_GID_KEYWORD) == 0)
130 idtype = ACL_IDTYPE_GID;
131 else if (strcmp(name, ACL_IDTYPE_UID_KEYWORD) != 0)
132 elog(ERROR, "aclparse: bad keyword, must be [group|user]");
133 s = getid(s, name); /* move s to the name beyond the keyword */
135 elog(ERROR, "aclparse: a name must follow the [group|user] keyword");
138 idtype = ACL_IDTYPE_WORLD;
142 case ACL_MODECHG_ADD_CHR:
143 *modechg = ACL_MODECHG_ADD;
145 case ACL_MODECHG_DEL_CHR:
146 *modechg = ACL_MODECHG_DEL;
148 case ACL_MODECHG_EQL_CHR:
149 *modechg = ACL_MODECHG_EQL;
152 elog(ERROR, "aclparse: mode change flag must use \"%c%c%c\"",
155 ACL_MODECHG_EQL_CHR);
158 privs = ACL_NO_RIGHTS;
160 while (isalpha((unsigned char) *++s))
179 case ACL_REFERENCES_CHR:
180 privs |= ACL_REFERENCES;
182 case ACL_TRIGGER_CHR:
183 privs |= ACL_TRIGGER;
185 case ACL_EXECUTE_CHR:
186 privs |= ACL_EXECUTE;
194 case ACL_CREATE_TEMP_CHR:
195 privs |= ACL_CREATE_TEMP;
198 elog(ERROR, "aclparse: mode flags must use \"%s\"",
206 aip->ai_id = get_usesysid(name);
209 aip->ai_id = get_grosysid(name);
211 case ACL_IDTYPE_WORLD:
212 aip->ai_id = ACL_ID_WORLD;
216 ACLITEM_SET_PRIVS_IDTYPE(*aip, privs, idtype);
219 elog(LOG, "aclparse: correctly read [%x %d %x], modechg=%x",
220 idtype, aip->ai_id, privs, *modechg);
227 * Allocates storage for a new Acl with 'n' entries.
239 elog(ERROR, "makeacl: invalid size: %d", n);
240 size = ACL_N_SIZE(n);
241 new_acl = (Acl *) palloc(size);
242 MemSet((char *) new_acl, 0, size);
243 new_acl->size = size;
246 ARR_LBOUND(new_acl)[0] = 0;
247 ARR_DIMS(new_acl)[0] = n;
253 * Allocates storage for, and fills in, a new AclItem given a string
254 * 's' that contains an ACL specification. See aclparse for details.
260 aclitemin(PG_FUNCTION_ARGS)
262 const char *s = PG_GETARG_CSTRING(0);
266 aip = (AclItem *) palloc(sizeof(AclItem));
267 s = aclparse(s, aip, &modechg);
268 if (modechg != ACL_MODECHG_EQL)
269 elog(ERROR, "aclitemin: cannot accept anything but = ACLs");
270 while (isspace((unsigned char) *s))
273 elog(ERROR, "aclitemin: extra garbage at end of specification");
274 PG_RETURN_ACLITEM_P(aip);
279 * Allocates storage for, and fills in, a new null-delimited string
280 * containing a formatted ACL specification. See aclparse for details.
286 aclitemout(PG_FUNCTION_ARGS)
288 AclItem *aip = PG_GETARG_ACLITEM_P(0);
295 p = out = palloc(strlen("group = ") + N_ACL_RIGHTS + NAMEDATALEN + 1);
298 switch (ACLITEM_GET_IDTYPE(*aip))
301 htup = SearchSysCache(SHADOWSYSID,
302 ObjectIdGetDatum(aip->ai_id),
304 if (HeapTupleIsValid(htup))
307 NameStr(((Form_pg_shadow) GETSTRUCT(htup))->usename),
309 ReleaseSysCache(htup);
313 /* Generate numeric UID if we don't find an entry */
316 tmp = DatumGetCString(DirectFunctionCall1(int4out,
317 Int32GetDatum((int32) aip->ai_id)));
324 tmpname = get_groname(aip->ai_id);
326 strncat(p, tmpname, NAMEDATALEN);
329 /* Generate numeric GID if we don't find an entry */
332 tmp = DatumGetCString(DirectFunctionCall1(int4out,
333 Int32GetDatum((int32) aip->ai_id)));
338 case ACL_IDTYPE_WORLD:
341 elog(ERROR, "aclitemout: bad idtype: %d",
342 ACLITEM_GET_IDTYPE(*aip));
348 for (i = 0; i < N_ACL_RIGHTS; ++i)
349 if (aip->ai_privs & (1 << i))
350 *p++ = ACL_ALL_RIGHTS_STR[i];
353 PG_RETURN_CSTRING(out);
359 * AclItem equality and greater-than comparison routines.
360 * Two AclItems are considered equal iff they have the same
361 * identifier (and identifier type); the privileges are ignored.
362 * Note that these routines are really only useful for sorting
363 * AclItems into identifier order.
366 * a boolean value indicating = or >
369 aclitemeq(const AclItem *a1, const AclItem *a2)
371 return ACLITEM_GET_IDTYPE(*a1) == ACLITEM_GET_IDTYPE(*a2) &&
372 a1->ai_id == a2->ai_id;
376 aclitemgt(const AclItem *a1, const AclItem *a2)
378 return ((ACLITEM_GET_IDTYPE(*a1) > ACLITEM_GET_IDTYPE(*a2)) ||
379 (ACLITEM_GET_IDTYPE(*a1) == ACLITEM_GET_IDTYPE(*a2) &&
380 a1->ai_id > a2->ai_id));
385 * acldefault() --- create an ACL describing default access permissions
387 * Change this routine if you want to alter the default access policy for
388 * newly-created tables (or any table with a NULL acl entry in pg_class)
391 acldefault(GrantObjectType objtype, AclId ownerid)
393 AclMode world_default;
394 AclMode owner_default;
400 case ACL_OBJECT_RELATION:
401 world_default = ACL_NO_RIGHTS;
402 owner_default = ACL_ALL_RIGHTS_RELATION;
404 case ACL_OBJECT_DATABASE:
405 world_default = ACL_NO_RIGHTS;
406 owner_default = ACL_ALL_RIGHTS_DATABASE;
408 case ACL_OBJECT_FUNCTION:
409 world_default = ACL_NO_RIGHTS;
410 owner_default = ACL_ALL_RIGHTS_FUNCTION;
412 case ACL_OBJECT_LANGUAGE:
413 world_default = ACL_NO_RIGHTS;
414 owner_default = ACL_ALL_RIGHTS_LANGUAGE;
416 case ACL_OBJECT_NAMESPACE:
417 world_default = ACL_NO_RIGHTS;
418 owner_default = ACL_ALL_RIGHTS_NAMESPACE;
421 elog(ERROR, "acldefault: bogus objtype %d", (int) objtype);
422 world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
423 owner_default = ACL_NO_RIGHTS;
427 acl = makeacl(ownerid ? 2 : 1);
430 aip[0].ai_id = ACL_ID_WORLD;
431 ACLITEM_SET_PRIVS_IDTYPE(aip[0], world_default, ACL_IDTYPE_WORLD);
434 aip[1].ai_id = ownerid;
435 ACLITEM_SET_PRIVS_IDTYPE(aip[1], owner_default, ACL_IDTYPE_UID);
443 * Add or replace an item in an ACL array. The result is a modified copy;
444 * the input object is not changed.
446 * NB: caller is responsible for having detoasted the input ACL, if needed.
449 aclinsert3(const Acl *old_acl, const AclItem *mod_aip, unsigned modechg)
457 /* These checks for null input are probably dead code, but... */
458 if (!old_acl || ACL_NUM(old_acl) < 1)
459 old_acl = makeacl(1);
462 new_acl = makeacl(ACL_NUM(old_acl));
463 memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
467 num = ACL_NUM(old_acl);
468 old_aip = ACL_DAT(old_acl);
471 * Search the ACL for an existing entry for 'id'. If one exists, just
472 * modify the entry in-place (well, in the same position, since we
473 * actually return a copy); otherwise, insert the new entry in
476 /* find the first element not less than the element to be inserted */
477 for (dst = 0; dst < num && aclitemgt(mod_aip, old_aip + dst); ++dst)
480 if (dst < num && aclitemeq(mod_aip, old_aip + dst))
482 /* found a match, so modify existing item */
483 new_acl = makeacl(num);
484 new_aip = ACL_DAT(new_acl);
485 memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
489 /* need to insert a new item */
490 new_acl = makeacl(num + 1);
491 new_aip = ACL_DAT(new_acl);
494 elog(ERROR, "aclinsert3: insertion before world ACL??");
498 memcpy((char *) new_aip,
500 num * sizeof(AclItem));
504 memcpy((char *) new_aip,
506 dst * sizeof(AclItem));
507 memcpy((char *) (new_aip + dst + 1),
508 (char *) (old_aip + dst),
509 (num - dst) * sizeof(AclItem));
511 /* initialize the new entry with no permissions */
512 new_aip[dst].ai_id = mod_aip->ai_id;
513 ACLITEM_SET_PRIVS_IDTYPE(new_aip[dst], ACL_NO_RIGHTS,
514 ACLITEM_GET_IDTYPE(*mod_aip));
515 num++; /* set num to the size of new_acl */
518 /* apply the permissions mod */
521 case ACL_MODECHG_ADD:
522 new_aip[dst].ai_privs |= ACLITEM_GET_PRIVS(*mod_aip);
524 case ACL_MODECHG_DEL:
525 new_aip[dst].ai_privs &= ~ACLITEM_GET_PRIVS(*mod_aip);
527 case ACL_MODECHG_EQL:
528 ACLITEM_SET_PRIVS_IDTYPE(new_aip[dst],
529 ACLITEM_GET_PRIVS(*mod_aip),
530 ACLITEM_GET_IDTYPE(new_aip[dst]));
535 * if the adjusted entry has no permissions, delete it from the list.
536 * For example, this helps in removing entries for users who no longer
537 * exist. EXCEPTION: never remove the world entry.
539 if (ACLITEM_GET_PRIVS(new_aip[dst]) == ACL_NO_RIGHTS && dst > 0)
541 memmove((char *) (new_aip + dst),
542 (char *) (new_aip + dst + 1),
543 (num - dst - 1) * sizeof(AclItem));
544 ARR_DIMS(new_acl)[0] = num - 1;
545 ARR_SIZE(new_acl) -= sizeof(AclItem);
552 * aclinsert (exported function)
555 aclinsert(PG_FUNCTION_ARGS)
557 Acl *old_acl = PG_GETARG_ACL_P(0);
558 AclItem *mod_aip = PG_GETARG_ACLITEM_P(1);
560 PG_RETURN_ACL_P(aclinsert3(old_acl, mod_aip, ACL_MODECHG_EQL));
564 aclremove(PG_FUNCTION_ARGS)
566 Acl *old_acl = PG_GETARG_ACL_P(0);
567 AclItem *mod_aip = PG_GETARG_ACLITEM_P(1);
575 /* These checks for null input should be dead code, but... */
576 if (!old_acl || ACL_NUM(old_acl) < 1)
577 old_acl = makeacl(1);
580 new_acl = makeacl(ACL_NUM(old_acl));
581 memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
582 PG_RETURN_ACL_P(new_acl);
585 old_num = ACL_NUM(old_acl);
586 old_aip = ACL_DAT(old_acl);
588 /* Search for the matching entry */
589 for (dst = 0; dst < old_num && !aclitemeq(mod_aip, old_aip + dst); ++dst)
594 /* Not found, so return copy of source ACL */
595 new_acl = makeacl(old_num);
596 memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
600 new_num = old_num - 1;
601 new_acl = makeacl(new_num);
602 new_aip = ACL_DAT(new_acl);
605 elog(ERROR, "aclremove: removal of the world ACL??");
607 else if (dst == old_num - 1)
609 memcpy((char *) new_aip,
611 new_num * sizeof(AclItem));
615 memcpy((char *) new_aip,
617 dst * sizeof(AclItem));
618 memcpy((char *) (new_aip + dst),
619 (char *) (old_aip + dst + 1),
620 (new_num - dst) * sizeof(AclItem));
624 PG_RETURN_ACL_P(new_acl);
628 aclcontains(PG_FUNCTION_ARGS)
630 Acl *acl = PG_GETARG_ACL_P(0);
631 AclItem *aip = PG_GETARG_ACLITEM_P(1);
637 aidat = ACL_DAT(acl);
638 for (i = 0; i < num; ++i)
640 if (aip->ai_id == aidat[i].ai_id &&
641 aip->ai_privs == aidat[i].ai_privs)
642 PG_RETURN_BOOL(true);
644 PG_RETURN_BOOL(false);
649 * has_table_privilege_name_name
650 * Check user privileges on a relation given
651 * name username, text relname, and text priv name.
655 * 't' indicating user has the privilege
656 * 'f' indicating user does not have the privilege
659 has_table_privilege_name_name(PG_FUNCTION_ARGS)
661 Name username = PG_GETARG_NAME(0);
662 text *relname = PG_GETARG_TEXT_P(1);
663 text *priv_type_text = PG_GETARG_TEXT_P(2);
670 * Lookup userid based on username
672 usesysid = get_usesysid(NameStr(*username));
675 * Lookup rel OID based on relname
677 reloid = convert_rel_name(relname);
680 * Convert priv_type_text to an AclMode
682 mode = convert_priv_string(priv_type_text);
685 * Check for the privilege
687 aclresult = pg_class_aclcheck(reloid, usesysid, mode);
689 PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
694 * has_table_privilege_name
695 * Check user privileges on a relation given
696 * text relname and text priv name.
697 * current_user is assumed
701 * 't' indicating user has the privilege
702 * 'f' indicating user does not have the privilege
705 has_table_privilege_name(PG_FUNCTION_ARGS)
707 text *relname = PG_GETARG_TEXT_P(0);
708 text *priv_type_text = PG_GETARG_TEXT_P(1);
714 usesysid = GetUserId();
717 * Lookup rel OID based on relname
719 reloid = convert_rel_name(relname);
722 * Convert priv_type_text to an AclMode
724 mode = convert_priv_string(priv_type_text);
727 * Check for the privilege
729 aclresult = pg_class_aclcheck(reloid, usesysid, mode);
731 PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
736 * has_table_privilege_name_id
737 * Check user privileges on a relation given
738 * name usename, rel oid, and text priv name.
742 * 't' indicating user has the privilege
743 * 'f' indicating user does not have the privilege
746 has_table_privilege_name_id(PG_FUNCTION_ARGS)
748 Name username = PG_GETARG_NAME(0);
749 Oid reloid = PG_GETARG_OID(1);
750 text *priv_type_text = PG_GETARG_TEXT_P(2);
756 * Lookup userid based on username
758 usesysid = get_usesysid(NameStr(*username));
761 * Convert priv_type_text to an AclMode
763 mode = convert_priv_string(priv_type_text);
766 * Check for the privilege
768 aclresult = pg_class_aclcheck(reloid, usesysid, mode);
770 PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
775 * has_table_privilege_id
776 * Check user privileges on a relation given
777 * rel oid, and text priv name.
778 * current_user is assumed
782 * 't' indicating user has the privilege
783 * 'f' indicating user does not have the privilege
786 has_table_privilege_id(PG_FUNCTION_ARGS)
788 Oid reloid = PG_GETARG_OID(0);
789 text *priv_type_text = PG_GETARG_TEXT_P(1);
794 usesysid = GetUserId();
797 * Convert priv_type_text to an AclMode
799 mode = convert_priv_string(priv_type_text);
802 * Check for the privilege
804 aclresult = pg_class_aclcheck(reloid, usesysid, mode);
806 PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
811 * has_table_privilege_id_name
812 * Check user privileges on a relation given
813 * usesysid, text relname, and priv name.
817 * 't' indicating user has the privilege
818 * 'f' indicating user does not have the privilege
821 has_table_privilege_id_name(PG_FUNCTION_ARGS)
823 int32 usesysid = PG_GETARG_INT32(0);
824 text *relname = PG_GETARG_TEXT_P(1);
825 text *priv_type_text = PG_GETARG_TEXT_P(2);
831 * Lookup rel OID based on relname
833 reloid = convert_rel_name(relname);
836 * Convert priv_type_text to an AclMode
838 mode = convert_priv_string(priv_type_text);
841 * Check for the privilege
843 aclresult = pg_class_aclcheck(reloid, usesysid, mode);
845 PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
850 * has_table_privilege_id_id
851 * Check user privileges on a relation given
852 * usesysid, rel oid, and priv name.
856 * 't' indicating user has the privilege
857 * 'f' indicating user does not have the privilege
860 has_table_privilege_id_id(PG_FUNCTION_ARGS)
862 int32 usesysid = PG_GETARG_INT32(0);
863 Oid reloid = PG_GETARG_OID(1);
864 text *priv_type_text = PG_GETARG_TEXT_P(2);
869 * Convert priv_type_text to an AclMode
871 mode = convert_priv_string(priv_type_text);
874 * Check for the privilege
876 aclresult = pg_class_aclcheck(reloid, usesysid, mode);
878 PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
882 * Internal functions.
886 * Given a relation name expressed as a string, look it up and return Oid
889 convert_rel_name(text *relname)
893 relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname,
894 "has_table_privilege"));
896 return RangeVarGetRelid(relrv, false);
900 * convert_priv_string
902 * Return mode from priv_type string
908 convert_priv_string(text *priv_type_text)
912 priv_type = DatumGetCString(DirectFunctionCall1(textout,
913 PointerGetDatum(priv_type_text)));
916 * Return mode from priv_type string
918 if (strcasecmp(priv_type, "SELECT") == 0)
921 if (strcasecmp(priv_type, "INSERT") == 0)
924 if (strcasecmp(priv_type, "UPDATE") == 0)
927 if (strcasecmp(priv_type, "DELETE") == 0)
930 if (strcasecmp(priv_type, "RULE") == 0)
933 if (strcasecmp(priv_type, "REFERENCES") == 0)
934 return ACL_REFERENCES;
936 if (strcasecmp(priv_type, "TRIGGER") == 0)
939 elog(ERROR, "has_table_privilege: invalid privilege type %s", priv_type);
942 * We should never get here, but stop the compiler from complaining
944 return ACL_NO_RIGHTS;