]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/acl.c
Privileges on functions and procedural languages
[postgresql] / src / backend / utils / adt / acl.c
1 /*-------------------------------------------------------------------------
2  *
3  * acl.c
4  *        Basic access control list data structures manipulation routines.
5  *
6  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.67 2002/02/18 23:11:22 petere Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include <ctype.h>
18
19 #include "access/heapam.h"
20 #include "catalog/catalog.h"
21 #include "catalog/pg_shadow.h"
22 #include "catalog/pg_type.h"
23 #include "lib/stringinfo.h"
24 #include "miscadmin.h"
25 #include "utils/acl.h"
26 #include "utils/builtins.h"
27 #include "utils/memutils.h"
28 #include "utils/lsyscache.h"
29 #include "utils/syscache.h"
30
31
32 #define ACL_IDTYPE_GID_KEYWORD  "group"
33 #define ACL_IDTYPE_UID_KEYWORD  "user"
34
35 static const char *getid(const char *s, char *n);
36 static bool aclitemeq(const AclItem *a1, const AclItem *a2);
37 static bool aclitemgt(const AclItem *a1, const AclItem *a2);
38
39 static AclMode convert_priv_string(text *priv_type_text);
40 static bool has_table_privilege_cname_cname(char *username, char *relname,
41                                                                 text *priv_type_text);
42 static bool has_table_privilege_cname_id(char *username, Oid reloid,
43                                                          text *priv_type_text);
44 static bool has_table_privilege_id_cname(int32 usesysid, char *relname,
45                                                          text *priv_type_text);
46
47
48 /*
49  * getid
50  *              Consumes the first alphanumeric string (identifier) found in string
51  *              's', ignoring any leading white space.  If it finds a double quote
52  *              it returns the word inside the quotes.
53  *
54  * RETURNS:
55  *              the string position in 's' that points to the next non-space character
56  *              in 's', after any quotes.  Also:
57  *              - loads the identifier into 'name'.  (If no identifier is found, 'name'
58  *                contains an empty string.)  name must be NAMEDATALEN bytes.
59  */
60 static const char *
61 getid(const char *s, char *n)
62 {
63         unsigned        len;
64         const char *id;
65         int                     in_quotes = 0;
66
67         Assert(s && n);
68
69         while (isspace((unsigned char) *s))
70                 ++s;
71
72         if (*s == '"')
73         {
74                 in_quotes = 1;
75                 s++;
76         }
77
78         for (id = s, len = 0;
79                  isalnum((unsigned char) *s) || *s == '_' || in_quotes;
80                  ++len, ++s)
81         {
82                 if (in_quotes && *s == '"')
83                 {
84                         len--;
85                         in_quotes = 0;
86                 }
87         }
88         if (len >= NAMEDATALEN)
89                 elog(ERROR, "getid: identifier must be <%d characters",
90                          NAMEDATALEN);
91         if (len > 0)
92                 memmove(n, id, len);
93         n[len] = '\0';
94         while (isspace((unsigned char) *s))
95                 ++s;
96         return s;
97 }
98
99 /*
100  * aclparse
101  *              Consumes and parses an ACL specification of the form:
102  *                              [group|user] [A-Za-z0-9]*[+-=][rwaR]*
103  *              from string 's', ignoring any leading white space or white space
104  *              between the optional id type keyword (group|user) and the actual
105  *              ACL specification.
106  *
107  *              This routine is called by the parser as well as aclitemin(), hence
108  *              the added generality.
109  *
110  * RETURNS:
111  *              the string position in 's' immediately following the ACL
112  *              specification.  Also:
113  *              - loads the structure pointed to by 'aip' with the appropriate
114  *                UID/GID, id type identifier and mode type values.
115  *              - loads 'modechg' with the mode change flag.
116  */
117 const char *
118 aclparse(const char *s, AclItem *aip, unsigned *modechg)
119 {
120         char            name[NAMEDATALEN];
121
122         Assert(s && aip && modechg);
123
124 #ifdef ACLDEBUG
125         elog(DEBUG, "aclparse: input = '%s'", s);
126 #endif
127         aip->ai_idtype = ACL_IDTYPE_UID;
128         s = getid(s, name);
129         if (*s != ACL_MODECHG_ADD_CHR &&
130                 *s != ACL_MODECHG_DEL_CHR &&
131                 *s != ACL_MODECHG_EQL_CHR)
132         {
133                 /* we just read a keyword, not a name */
134                 if (!strcmp(name, ACL_IDTYPE_GID_KEYWORD))
135                         aip->ai_idtype = ACL_IDTYPE_GID;
136                 else if (strcmp(name, ACL_IDTYPE_UID_KEYWORD))
137                         elog(ERROR, "aclparse: bad keyword, must be [group|user]");
138                 s = getid(s, name);             /* move s to the name beyond the keyword */
139                 if (name[0] == '\0')
140                         elog(ERROR, "aclparse: a name must follow the [group|user] keyword");
141         }
142         if (name[0] == '\0')
143                 aip->ai_idtype = ACL_IDTYPE_WORLD;
144
145         switch (*s)
146         {
147                 case ACL_MODECHG_ADD_CHR:
148                         *modechg = ACL_MODECHG_ADD;
149                         break;
150                 case ACL_MODECHG_DEL_CHR:
151                         *modechg = ACL_MODECHG_DEL;
152                         break;
153                 case ACL_MODECHG_EQL_CHR:
154                         *modechg = ACL_MODECHG_EQL;
155                         break;
156                 default:
157                         elog(ERROR, "aclparse: mode change flag must use \"%s\"",
158                                  ACL_MODECHG_STR);
159         }
160
161         aip->ai_mode = ACL_NO;
162         while (isalpha((unsigned char) *++s))
163         {
164                 switch (*s)
165                 {
166                         case ACL_MODE_INSERT_CHR:
167                                 aip->ai_mode |= ACL_INSERT;
168                                 break;
169                         case ACL_MODE_SELECT_CHR:
170                                 aip->ai_mode |= ACL_SELECT;
171                                 break;
172                         case ACL_MODE_UPDATE_CHR:
173                                 aip->ai_mode |= ACL_UPDATE;
174                                 break;
175                         case ACL_MODE_DELETE_CHR:
176                                 aip->ai_mode |= ACL_DELETE;
177                                 break;
178                         case ACL_MODE_RULE_CHR:
179                                 aip->ai_mode |= ACL_RULE;
180                                 break;
181                         case ACL_MODE_REFERENCES_CHR:
182                                 aip->ai_mode |= ACL_REFERENCES;
183                                 break;
184                         case ACL_MODE_TRIGGER_CHR:
185                                 aip->ai_mode |= ACL_TRIGGER;
186                                 break;
187                         default:
188                                 elog(ERROR, "aclparse: mode flags must use \"%s\"",
189                                          ACL_MODE_STR);
190                 }
191         }
192
193         switch (aip->ai_idtype)
194         {
195                 case ACL_IDTYPE_UID:
196                         aip->ai_id = get_usesysid(name);
197                         break;
198                 case ACL_IDTYPE_GID:
199                         aip->ai_id = get_grosysid(name);
200                         break;
201                 case ACL_IDTYPE_WORLD:
202                         aip->ai_id = ACL_ID_WORLD;
203                         break;
204         }
205
206 #ifdef ACLDEBUG
207         elog(DEBUG, "aclparse: correctly read [%x %d %x], modechg=%x",
208                  aip->ai_idtype, aip->ai_id, aip->ai_mode, *modechg);
209 #endif
210         return s;
211 }
212
213 /*
214  * makeacl
215  *              Allocates storage for a new Acl with 'n' entries.
216  *
217  * RETURNS:
218  *              the new Acl
219  */
220 Acl *
221 makeacl(int n)
222 {
223         Acl                *new_acl;
224         Size            size;
225
226         if (n < 0)
227                 elog(ERROR, "makeacl: invalid size: %d", n);
228         size = ACL_N_SIZE(n);
229         new_acl = (Acl *) palloc(size);
230         MemSet((char *) new_acl, 0, size);
231         new_acl->size = size;
232         new_acl->ndim = 1;
233         new_acl->flags = 0;
234         ARR_LBOUND(new_acl)[0] = 0;
235         ARR_DIMS(new_acl)[0] = n;
236         return new_acl;
237 }
238
239 /*
240  * aclitemin
241  *              Allocates storage for, and fills in, a new AclItem given a string
242  *              's' that contains an ACL specification.  See aclparse for details.
243  *
244  * RETURNS:
245  *              the new AclItem
246  */
247 Datum
248 aclitemin(PG_FUNCTION_ARGS)
249 {
250         const char *s = PG_GETARG_CSTRING(0);
251         AclItem    *aip;
252         unsigned        modechg;
253
254         aip = (AclItem *) palloc(sizeof(AclItem));
255         s = aclparse(s, aip, &modechg);
256         if (modechg != ACL_MODECHG_EQL)
257                 elog(ERROR, "aclitemin: cannot accept anything but = ACLs");
258         while (isspace((unsigned char) *s))
259                 ++s;
260         if (*s)
261                 elog(ERROR, "aclitemin: extra garbage at end of specification");
262         PG_RETURN_ACLITEM_P(aip);
263 }
264
265 /*
266  * aclitemout
267  *              Allocates storage for, and fills in, a new null-delimited string
268  *              containing a formatted ACL specification.  See aclparse for details.
269  *
270  * RETURNS:
271  *              the new string
272  */
273 Datum
274 aclitemout(PG_FUNCTION_ARGS)
275 {
276         AclItem    *aip = PG_GETARG_ACLITEM_P(0);
277         char       *p;
278         char       *out;
279         HeapTuple       htup;
280         unsigned        i;
281         char       *tmpname;
282
283         p = out = palloc(strlen("group =" ACL_MODE_STR " ") + 1 + NAMEDATALEN);
284         *p = '\0';
285
286         switch (aip->ai_idtype)
287         {
288                 case ACL_IDTYPE_UID:
289                         htup = SearchSysCache(SHADOWSYSID,
290                                                                   ObjectIdGetDatum(aip->ai_id),
291                                                                   0, 0, 0);
292                         if (HeapTupleIsValid(htup))
293                         {
294                                 strncat(p,
295                                         NameStr(((Form_pg_shadow) GETSTRUCT(htup))->usename),
296                                                 NAMEDATALEN);
297                                 ReleaseSysCache(htup);
298                         }
299                         else
300                         {
301                                 /* Generate numeric UID if we don't find an entry */
302                                 char       *tmp;
303
304                                 tmp = DatumGetCString(DirectFunctionCall1(int4out,
305                                                                          Int32GetDatum((int32) aip->ai_id)));
306                                 strcat(p, tmp);
307                                 pfree(tmp);
308                         }
309                         break;
310                 case ACL_IDTYPE_GID:
311                         strcat(p, "group ");
312                         tmpname = get_groname(aip->ai_id);
313                         if (tmpname != NULL)
314                                 strncat(p, tmpname, NAMEDATALEN);
315                         else
316                         {
317                                 /* Generate numeric GID if we don't find an entry */
318                                 char       *tmp;
319
320                                 tmp = DatumGetCString(DirectFunctionCall1(int4out,
321                                                                          Int32GetDatum((int32) aip->ai_id)));
322                                 strcat(p, tmp);
323                                 pfree(tmp);
324                         }
325                         break;
326                 case ACL_IDTYPE_WORLD:
327                         break;
328                 default:
329                         elog(ERROR, "aclitemout: bad ai_idtype: %d", aip->ai_idtype);
330                         break;
331         }
332         while (*p)
333                 ++p;
334         *p++ = '=';
335         for (i = 0; i < N_ACL_MODES; ++i)
336                 if ((aip->ai_mode >> i) & 01)
337                         *p++ = ACL_MODE_STR[i];
338         *p = '\0';
339
340         PG_RETURN_CSTRING(out);
341 }
342
343 /*
344  * aclitemeq
345  * aclitemgt
346  *              AclItem equality and greater-than comparison routines.
347  *              Two AclItems are considered equal iff they have the
348  *              same identifier (and identifier type); the mode is ignored.
349  *              Note that these routines are really only useful for sorting
350  *              AclItems into identifier order.
351  *
352  * RETURNS:
353  *              a boolean value indicating = or >
354  */
355 static bool
356 aclitemeq(const AclItem *a1, const AclItem *a2)
357 {
358         return a1->ai_idtype == a2->ai_idtype && a1->ai_id == a2->ai_id;
359 }
360
361 static bool
362 aclitemgt(const AclItem *a1, const AclItem *a2)
363 {
364         return ((a1->ai_idtype > a2->ai_idtype) ||
365                         (a1->ai_idtype == a2->ai_idtype && a1->ai_id > a2->ai_id));
366 }
367
368
369 /*
370  * acldefault()  --- create an ACL describing default access permissions
371  *
372  * Change this routine if you want to alter the default access policy for
373  * newly-created tables (or any table with a NULL acl entry in pg_class)
374  */
375 Acl *
376 acldefault(AclId ownerid)
377 {
378         Acl                *acl;
379         AclItem    *aip;
380
381 #define ACL_WORLD_DEFAULT               (ACL_NO)
382 #define ACL_OWNER_DEFAULT               (ACL_INSERT|ACL_SELECT|ACL_UPDATE|ACL_DELETE|ACL_RULE|ACL_REFERENCES|ACL_TRIGGER)
383
384         acl = makeacl(ownerid ? 2 : 1);
385         aip = ACL_DAT(acl);
386         aip[0].ai_idtype = ACL_IDTYPE_WORLD;
387         aip[0].ai_id = ACL_ID_WORLD;
388         aip[0].ai_mode = ACL_WORLD_DEFAULT;
389         /* FIXME: The owner's default should vary with the object type. */
390         if (ownerid)
391         {
392                 aip[1].ai_idtype = ACL_IDTYPE_UID;
393                 aip[1].ai_id = ownerid;
394                 aip[1].ai_mode = ACL_OWNER_DEFAULT;
395         }
396         return acl;
397 }
398
399
400 /*
401  * Add or replace an item in an ACL array.      The result is a modified copy;
402  * the input object is not changed.
403  *
404  * NB: caller is responsible for having detoasted the input ACL, if needed.
405  */
406 Acl *
407 aclinsert3(const Acl *old_acl, const AclItem *mod_aip, unsigned modechg)
408 {
409         Acl                *new_acl;
410         AclItem    *old_aip,
411                            *new_aip;
412         int                     dst,
413                                 num;
414
415         /* These checks for null input are probably dead code, but... */
416         if (!old_acl || ACL_NUM(old_acl) < 1)
417                 old_acl = makeacl(1);
418         if (!mod_aip)
419         {
420                 new_acl = makeacl(ACL_NUM(old_acl));
421                 memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
422                 return new_acl;
423         }
424
425         num = ACL_NUM(old_acl);
426         old_aip = ACL_DAT(old_acl);
427
428         /*
429          * Search the ACL for an existing entry for 'id'.  If one exists, just
430          * modify the entry in-place (well, in the same position, since we
431          * actually return a copy); otherwise, insert the new entry in
432          * sort-order.
433          */
434         /* find the first element not less than the element to be inserted */
435         for (dst = 0; dst < num && aclitemgt(mod_aip, old_aip + dst); ++dst)
436                 ;
437
438         if (dst < num && aclitemeq(mod_aip, old_aip + dst))
439         {
440                 /* found a match, so modify existing item */
441                 new_acl = makeacl(num);
442                 new_aip = ACL_DAT(new_acl);
443                 memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
444         }
445         else
446         {
447                 /* need to insert a new item */
448                 new_acl = makeacl(num + 1);
449                 new_aip = ACL_DAT(new_acl);
450                 if (dst == 0)
451                 {                                               /* start */
452                         elog(ERROR, "aclinsert3: insertion before world ACL??");
453                 }
454                 else if (dst >= num)
455                 {                                               /* end */
456                         memcpy((char *) new_aip,
457                                    (char *) old_aip,
458                                    num * sizeof(AclItem));
459                 }
460                 else
461                 {                                               /* middle */
462                         memcpy((char *) new_aip,
463                                    (char *) old_aip,
464                                    dst * sizeof(AclItem));
465                         memcpy((char *) (new_aip + dst + 1),
466                                    (char *) (old_aip + dst),
467                                    (num - dst) * sizeof(AclItem));
468                 }
469                 /* initialize the new entry with no permissions */
470                 new_aip[dst].ai_id = mod_aip->ai_id;
471                 new_aip[dst].ai_idtype = mod_aip->ai_idtype;
472                 new_aip[dst].ai_mode = 0;
473                 num++;                                  /* set num to the size of new_acl */
474         }
475
476         /* apply the permissions mod */
477         switch (modechg)
478         {
479                 case ACL_MODECHG_ADD:
480                         new_aip[dst].ai_mode |= mod_aip->ai_mode;
481                         break;
482                 case ACL_MODECHG_DEL:
483                         new_aip[dst].ai_mode &= ~mod_aip->ai_mode;
484                         break;
485                 case ACL_MODECHG_EQL:
486                         new_aip[dst].ai_mode = mod_aip->ai_mode;
487                         break;
488         }
489
490         /*
491          * if the adjusted entry has no permissions, delete it from the list.
492          * For example, this helps in removing entries for users who no longer
493          * exist.  EXCEPTION: never remove the world entry.
494          */
495         if (new_aip[dst].ai_mode == 0 && dst > 0)
496         {
497                 memmove((char *) (new_aip + dst),
498                                 (char *) (new_aip + dst + 1),
499                                 (num - dst - 1) * sizeof(AclItem));
500                 ARR_DIMS(new_acl)[0] = num - 1;
501                 ARR_SIZE(new_acl) -= sizeof(AclItem);
502         }
503
504         return new_acl;
505 }
506
507 /*
508  * aclinsert (exported function)
509  */
510 Datum
511 aclinsert(PG_FUNCTION_ARGS)
512 {
513         Acl                *old_acl = PG_GETARG_ACL_P(0);
514         AclItem    *mod_aip = PG_GETARG_ACLITEM_P(1);
515
516         PG_RETURN_ACL_P(aclinsert3(old_acl, mod_aip, ACL_MODECHG_EQL));
517 }
518
519 Datum
520 aclremove(PG_FUNCTION_ARGS)
521 {
522         Acl                *old_acl = PG_GETARG_ACL_P(0);
523         AclItem    *mod_aip = PG_GETARG_ACLITEM_P(1);
524         Acl                *new_acl;
525         AclItem    *old_aip,
526                            *new_aip;
527         int                     dst,
528                                 old_num,
529                                 new_num;
530
531         /* These checks for null input should be dead code, but... */
532         if (!old_acl || ACL_NUM(old_acl) < 1)
533                 old_acl = makeacl(1);
534         if (!mod_aip)
535         {
536                 new_acl = makeacl(ACL_NUM(old_acl));
537                 memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
538                 PG_RETURN_ACL_P(new_acl);
539         }
540
541         old_num = ACL_NUM(old_acl);
542         old_aip = ACL_DAT(old_acl);
543
544         /* Search for the matching entry */
545         for (dst = 0; dst < old_num && !aclitemeq(mod_aip, old_aip + dst); ++dst)
546                 ;
547
548         if (dst >= old_num)
549         {
550                 /* Not found, so return copy of source ACL */
551                 new_acl = makeacl(old_num);
552                 memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
553         }
554         else
555         {
556                 new_num = old_num - 1;
557                 new_acl = makeacl(new_num);
558                 new_aip = ACL_DAT(new_acl);
559                 if (dst == 0)
560                 {                                               /* start */
561                         elog(ERROR, "aclremove: removal of the world ACL??");
562                 }
563                 else if (dst == old_num - 1)
564                 {                                               /* end */
565                         memcpy((char *) new_aip,
566                                    (char *) old_aip,
567                                    new_num * sizeof(AclItem));
568                 }
569                 else
570                 {                                               /* middle */
571                         memcpy((char *) new_aip,
572                                    (char *) old_aip,
573                                    dst * sizeof(AclItem));
574                         memcpy((char *) (new_aip + dst),
575                                    (char *) (old_aip + dst + 1),
576                                    (new_num - dst) * sizeof(AclItem));
577                 }
578         }
579
580         PG_RETURN_ACL_P(new_acl);
581 }
582
583 Datum
584 aclcontains(PG_FUNCTION_ARGS)
585 {
586         Acl                *acl = PG_GETARG_ACL_P(0);
587         AclItem    *aip = PG_GETARG_ACLITEM_P(1);
588         AclItem    *aidat;
589         int                     i,
590                                 num;
591
592         num = ACL_NUM(acl);
593         aidat = ACL_DAT(acl);
594         for (i = 0; i < num; ++i)
595         {
596                 /* Note that aclitemeq only considers id, not mode */
597                 if (aclitemeq(aip, aidat + i) &&
598                         aip->ai_mode == aidat[i].ai_mode)
599                         PG_RETURN_BOOL(true);
600         }
601         PG_RETURN_BOOL(false);
602 }
603
604
605 /*
606  * Parser support routines for ACL-related statements.
607  *
608  * XXX CAUTION: these are called from gram.y, which is not allowed to
609  * do any table accesses.  Therefore, it is not kosher to do things
610  * like trying to translate usernames to user IDs here.  Keep it all
611  * in string form until statement execution time.
612  */
613
614 /*
615  * aclmakepriv
616  *        make a acl privilege string out of an existing privilege string
617  * and a new privilege
618  *
619  * does not add duplicate privileges
620  */
621 char *
622 aclmakepriv(const char *old_privlist, char new_priv)
623 {
624         char       *priv;
625         int                     i;
626         int                     l;
627
628         Assert(strlen(old_privlist) <= strlen(ACL_MODE_STR));
629         priv = palloc(strlen(ACL_MODE_STR) + 1);
630
631         if (old_privlist == NULL || old_privlist[0] == '\0')
632         {
633                 priv[0] = new_priv;
634                 priv[1] = '\0';
635                 return priv;
636         }
637
638         strcpy(priv, old_privlist);
639
640         l = strlen(old_privlist);
641
642         if (l == strlen(ACL_MODE_STR))
643         {                                                       /* can't add any more privileges */
644                 return priv;
645         }
646
647         /* check to see if the new privilege is already in the old string */
648         for (i = 0; i < l; i++)
649         {
650                 if (priv[i] == new_priv)
651                         break;
652         }
653         if (i == l)
654         {                                                       /* we really have a new privilege */
655                 priv[l] = new_priv;
656                 priv[l + 1] = '\0';
657         }
658
659         return priv;
660 }
661
662 /*
663  * aclmakeuser
664  *        user_type must be "A"  - all users
665  *                                              "G"  - group
666  *                                              "U"  - user
667  *
668  * Just concatenates the two strings together with a space in between.
669  * Per above comments, we can't try to resolve a user or group name here.
670  */
671 char *
672 aclmakeuser(const char *user_type, const char *user)
673 {
674         char       *user_list;
675
676         user_list = palloc(strlen(user_type) + strlen(user) + 2);
677         sprintf(user_list, "%s %s", user_type, user);
678         return user_list;
679 }
680
681
682 /*
683  * makeAclString:  We take in the privileges and grantee as well as a
684  * single character '+' or '-' to indicate grant or revoke.
685  *
686  * We convert the information to the same external form recognized by
687  * aclitemin (see aclparse) and return that string.  Conversion to
688  * internal form happens when the statement is executed.
689  */
690 char *
691 makeAclString(const char *privileges, const char *grantee, char grant_or_revoke)
692 {
693         StringInfoData str;
694         char       *ret;
695
696         initStringInfo(&str);
697
698         /* the grantee string is "G <group_name>", "U <user_name>", or "ALL" */
699         if (grantee[0] == 'G')          /* group permissions */
700         {
701                 appendStringInfo(&str, "%s \"%s\"%c%s",
702                                                  ACL_IDTYPE_GID_KEYWORD,
703                                                  grantee + 2, grant_or_revoke, privileges);
704         }
705         else if (grantee[0] == 'U') /* user permission */
706         {
707                 appendStringInfo(&str, "%s \"%s\"%c%s",
708                                                  ACL_IDTYPE_UID_KEYWORD,
709                                                  grantee + 2, grant_or_revoke, privileges);
710         }
711         else
712         {
713                 /* all permission */
714                 appendStringInfo(&str, "%c%s",
715                                                  grant_or_revoke, privileges);
716         }
717         ret = pstrdup(str.data);
718         pfree(str.data);
719         return ret;
720 }
721
722
723 /*
724  * has_table_privilege_name_name
725  *              Check user privileges on a relation given
726  *              name usename, name relname, and text priv name.
727  *
728  * RETURNS
729  *              a boolean value
730  *              't' indicating user has the privilege
731  *              'f' indicating user does not have the privilege
732  */
733 Datum
734 has_table_privilege_name_name(PG_FUNCTION_ARGS)
735 {
736         Name            username = PG_GETARG_NAME(0);
737         Name            relname = PG_GETARG_NAME(1);
738         text       *priv_type_text = PG_GETARG_TEXT_P(2);
739         bool            result;
740
741         result = has_table_privilege_cname_cname(NameStr(*username),
742                                                                                          NameStr(*relname),
743                                                                                          priv_type_text);
744
745         PG_RETURN_BOOL(result);
746 }
747
748
749 /*
750  * has_table_privilege_name
751  *              Check user privileges on a relation given
752  *              name relname and text priv name.
753  *              current_user is assumed
754  *
755  * RETURNS
756  *              a boolean value
757  *              't' indicating user has the privilege
758  *              'f' indicating user does not have the privilege
759  */
760 Datum
761 has_table_privilege_name(PG_FUNCTION_ARGS)
762 {
763         Name            relname = PG_GETARG_NAME(0);
764         text       *priv_type_text = PG_GETARG_TEXT_P(1);
765         int32           usesysid;
766         bool            result;
767
768         usesysid = GetUserId();
769
770         result = has_table_privilege_id_cname(usesysid,
771                                                                                   NameStr(*relname),
772                                                                                   priv_type_text);
773
774         PG_RETURN_BOOL(result);
775 }
776
777
778 /*
779  * has_table_privilege_name_id
780  *              Check user privileges on a relation given
781  *              name usename, rel oid, and text priv name.
782  *
783  * RETURNS
784  *              a boolean value
785  *              't' indicating user has the privilege
786  *              'f' indicating user does not have the privilege
787  */
788 Datum
789 has_table_privilege_name_id(PG_FUNCTION_ARGS)
790 {
791         Name            username = PG_GETARG_NAME(0);
792         Oid                     reloid = PG_GETARG_OID(1);
793         text       *priv_type_text = PG_GETARG_TEXT_P(2);
794         bool            result;
795
796         result = has_table_privilege_cname_id(NameStr(*username),
797                                                                                   reloid,
798                                                                                   priv_type_text);
799
800         PG_RETURN_BOOL(result);
801 }
802
803
804 /*
805  * has_table_privilege_id
806  *              Check user privileges on a relation given
807  *              rel oid, and text priv name.
808  *              current_user is assumed
809  *
810  * RETURNS
811  *              a boolean value
812  *              't' indicating user has the privilege
813  *              'f' indicating user does not have the privilege
814  */
815 Datum
816 has_table_privilege_id(PG_FUNCTION_ARGS)
817 {
818         Oid                     reloid = PG_GETARG_OID(0);
819         text       *priv_type_text = PG_GETARG_TEXT_P(1);
820         char       *relname;
821         int32           usesysid;
822         AclMode         mode;
823         int32           result;
824
825         usesysid = GetUserId();
826
827         /*
828          * Lookup relname based on rel oid
829          */
830         relname = get_rel_name(reloid);
831         if (relname == NULL)
832                 elog(ERROR, "has_table_privilege: invalid relation oid %u",
833                          reloid);
834
835         /*
836          * Convert priv_type_text to an AclMode
837          */
838         mode = convert_priv_string(priv_type_text);
839
840         /*
841          * Finally, check for the privilege
842          */
843         result = pg_aclcheck(relname, usesysid, mode);
844
845         if (result == ACLCHECK_OK)
846                 PG_RETURN_BOOL(true);
847         else
848                 PG_RETURN_BOOL(false);
849 }
850
851
852 /*
853  * has_table_privilege_id_name
854  *              Check user privileges on a relation given
855  *              usesysid, name relname, and priv name.
856  *
857  * RETURNS
858  *              a boolean value
859  *              't' indicating user has the privilege
860  *              'f' indicating user does not have the privilege
861  */
862 Datum
863 has_table_privilege_id_name(PG_FUNCTION_ARGS)
864 {
865         int32           usesysid = PG_GETARG_INT32(0);
866         Name            relname = PG_GETARG_NAME(1);
867         text       *priv_type_text = PG_GETARG_TEXT_P(2);
868         bool            result;
869
870         result = has_table_privilege_id_cname(usesysid,
871                                                                                   NameStr(*relname),
872                                                                                   priv_type_text);
873
874         PG_RETURN_BOOL(result);
875 }
876
877
878 /*
879  * has_table_privilege_id_id
880  *              Check user privileges on a relation given
881  *              usesysid, rel oid, and priv name.
882  *
883  * RETURNS
884  *              a boolean value
885  *              't' indicating user has the privilege
886  *              'f' indicating user does not have the privilege
887  */
888 Datum
889 has_table_privilege_id_id(PG_FUNCTION_ARGS)
890 {
891         int32           usesysid = PG_GETARG_INT32(0);
892         Oid                     reloid = PG_GETARG_OID(1);
893         text       *priv_type_text = PG_GETARG_TEXT_P(2);
894         char       *relname;
895         AclMode         mode;
896         int32           result;
897
898         /*
899          * Lookup relname based on rel oid
900          */
901         relname = get_rel_name(reloid);
902         if (relname == NULL)
903                 elog(ERROR, "has_table_privilege: invalid relation oid %u",
904                          reloid);
905
906         /*
907          * Convert priv_type_text to an AclMode
908          */
909         mode = convert_priv_string(priv_type_text);
910
911         /*
912          * Finally, check for the privilege
913          */
914         result = pg_aclcheck(relname, usesysid, mode);
915
916         if (result == ACLCHECK_OK)
917                 PG_RETURN_BOOL(true);
918         else
919                 PG_RETURN_BOOL(false);
920 }
921
922 /*
923  *              Internal functions.
924  */
925
926 /*
927  * convert_priv_string
928  *              Internal function.
929  *              Return mode from priv_type string
930  *
931  * RETURNS
932  *              AclMode
933  */
934
935 static AclMode
936 convert_priv_string(text *priv_type_text)
937 {
938         char       *priv_type = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(priv_type_text)));
939
940         /*
941          * Return mode from priv_type string
942          */
943         if (strcasecmp(priv_type, "SELECT") == 0)
944                 return ACL_SELECT;
945
946         if (strcasecmp(priv_type, "INSERT") == 0)
947                 return ACL_INSERT;
948
949         if (strcasecmp(priv_type, "UPDATE") == 0)
950                 return ACL_UPDATE;
951
952         if (strcasecmp(priv_type, "DELETE") == 0)
953                 return ACL_DELETE;
954
955         if (strcasecmp(priv_type, "RULE") == 0)
956                 return ACL_RULE;
957
958         if (strcasecmp(priv_type, "REFERENCES") == 0)
959                 return ACL_REFERENCES;
960
961         if (strcasecmp(priv_type, "TRIGGER") == 0)
962                 return ACL_TRIGGER;
963
964         elog(ERROR, "has_table_privilege: invalid privilege type %s", priv_type);
965
966         /*
967          * We should never get here, but stop the compiler from complaining
968          */
969         return ACL_NO;
970 }
971
972 /*
973  * has_table_privilege_cname_cname
974  *              Check user privileges on a relation given
975  *              char *usename, char *relname, and text priv name.
976  *
977  * RETURNS
978  *              a boolean value
979  *              't' indicating user has the privilege
980  *              'f' indicating user does not have the privilege
981  */
982 static bool
983 has_table_privilege_cname_cname(char *username, char *relname,
984                                                                 text *priv_type_text)
985 {
986         int32           usesysid;
987
988         /*
989          * Lookup userid based on username
990          */
991         usesysid = get_usesysid(username);
992
993         /*
994          * Make use of has_table_privilege_id_cname. It accepts the arguments
995          * we now have.
996          */
997         return has_table_privilege_id_cname(usesysid, relname, priv_type_text);
998 }
999
1000
1001 /*
1002  * has_table_privilege_cname_id
1003  *              Check user privileges on a relation given
1004  *              char *usename, rel oid, and text priv name.
1005  *
1006  * RETURNS
1007  *              a boolean value
1008  *              't' indicating user has the privilege
1009  *              'f' indicating user does not have the privilege
1010  */
1011 static bool
1012 has_table_privilege_cname_id(char *username, Oid reloid,
1013                                                          text *priv_type_text)
1014 {
1015         int32           usesysid;
1016         char       *relname;
1017
1018         /*
1019          * Lookup userid based on username
1020          */
1021         usesysid = get_usesysid(username);
1022
1023         /*
1024          * Lookup relname based on rel oid
1025          */
1026         relname = get_rel_name(reloid);
1027         if (relname == NULL)
1028                 elog(ERROR, "has_table_privilege: invalid relation oid %u",
1029                          reloid);
1030
1031         /*
1032          * Make use of has_table_privilege_id_cname. It accepts the arguments
1033          * we now have.
1034          */
1035         return has_table_privilege_id_cname(usesysid, relname, priv_type_text);
1036 }
1037
1038
1039 /*
1040  * has_table_privilege_id_cname
1041  *              Check user privileges on a relation given
1042  *              usesysid, char *relname, and text priv name.
1043  *
1044  * RETURNS
1045  *              a boolean value
1046  *              't' indicating user has the privilege
1047  *              'f' indicating user does not have the privilege
1048  */
1049 static bool
1050 has_table_privilege_id_cname(int32 usesysid, char *relname,
1051                                                          text *priv_type_text)
1052 {
1053         HeapTuple       tuple;
1054         AclMode         mode;
1055         int32           result;
1056
1057         /*
1058          * Check relname is valid. This is needed to deal with the case when
1059          * usename is a superuser in which case pg_aclcheck simply returns
1060          * ACLCHECK_OK without validating relname
1061          */
1062         tuple = SearchSysCache(RELNAME,
1063                                                    PointerGetDatum(relname),
1064                                                    0, 0, 0);
1065         if (!HeapTupleIsValid(tuple))
1066                 elog(ERROR, "has_table_privilege: relation \"%s\" does not exist",
1067                          relname);
1068         ReleaseSysCache(tuple);
1069
1070         /*
1071          * Convert priv_type_text to an AclMode
1072          */
1073         mode = convert_priv_string(priv_type_text);
1074
1075         /*
1076          * Finally, check for the privilege
1077          */
1078         result = pg_aclcheck(relname, usesysid, mode);
1079
1080         if (result == ACLCHECK_OK)
1081                 return true;
1082         else
1083                 return false;
1084 }