]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/acl.c
Change the aclchk.c routines to uniformly use OIDs to identify the
[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.69 2002/03/21 23:27:24 tgl 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(LOG, "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(LOG, "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         int32           usesysid;
821         AclMode         mode;
822         int32           result;
823
824         usesysid = GetUserId();
825
826         /*
827          * Convert priv_type_text to an AclMode
828          */
829         mode = convert_priv_string(priv_type_text);
830
831         /*
832          * Check for the privilege
833          */
834         result = pg_class_aclcheck(reloid, usesysid, mode);
835
836         if (result == ACLCHECK_OK)
837                 PG_RETURN_BOOL(true);
838         else
839                 PG_RETURN_BOOL(false);
840 }
841
842
843 /*
844  * has_table_privilege_id_name
845  *              Check user privileges on a relation given
846  *              usesysid, name relname, and priv name.
847  *
848  * RETURNS
849  *              a boolean value
850  *              't' indicating user has the privilege
851  *              'f' indicating user does not have the privilege
852  */
853 Datum
854 has_table_privilege_id_name(PG_FUNCTION_ARGS)
855 {
856         int32           usesysid = PG_GETARG_INT32(0);
857         Name            relname = PG_GETARG_NAME(1);
858         text       *priv_type_text = PG_GETARG_TEXT_P(2);
859         bool            result;
860
861         result = has_table_privilege_id_cname(usesysid,
862                                                                                   NameStr(*relname),
863                                                                                   priv_type_text);
864
865         PG_RETURN_BOOL(result);
866 }
867
868
869 /*
870  * has_table_privilege_id_id
871  *              Check user privileges on a relation given
872  *              usesysid, rel oid, and priv name.
873  *
874  * RETURNS
875  *              a boolean value
876  *              't' indicating user has the privilege
877  *              'f' indicating user does not have the privilege
878  */
879 Datum
880 has_table_privilege_id_id(PG_FUNCTION_ARGS)
881 {
882         int32           usesysid = PG_GETARG_INT32(0);
883         Oid                     reloid = PG_GETARG_OID(1);
884         text       *priv_type_text = PG_GETARG_TEXT_P(2);
885         AclMode         mode;
886         int32           result;
887
888         /*
889          * Convert priv_type_text to an AclMode
890          */
891         mode = convert_priv_string(priv_type_text);
892
893         /*
894          * Check for the privilege
895          */
896         result = pg_class_aclcheck(reloid, usesysid, mode);
897
898         if (result == ACLCHECK_OK)
899                 PG_RETURN_BOOL(true);
900         else
901                 PG_RETURN_BOOL(false);
902 }
903
904 /*
905  *              Internal functions.
906  */
907
908 /*
909  * convert_priv_string
910  *              Internal function.
911  *              Return mode from priv_type string
912  *
913  * RETURNS
914  *              AclMode
915  */
916
917 static AclMode
918 convert_priv_string(text *priv_type_text)
919 {
920         char       *priv_type = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(priv_type_text)));
921
922         /*
923          * Return mode from priv_type string
924          */
925         if (strcasecmp(priv_type, "SELECT") == 0)
926                 return ACL_SELECT;
927
928         if (strcasecmp(priv_type, "INSERT") == 0)
929                 return ACL_INSERT;
930
931         if (strcasecmp(priv_type, "UPDATE") == 0)
932                 return ACL_UPDATE;
933
934         if (strcasecmp(priv_type, "DELETE") == 0)
935                 return ACL_DELETE;
936
937         if (strcasecmp(priv_type, "RULE") == 0)
938                 return ACL_RULE;
939
940         if (strcasecmp(priv_type, "REFERENCES") == 0)
941                 return ACL_REFERENCES;
942
943         if (strcasecmp(priv_type, "TRIGGER") == 0)
944                 return ACL_TRIGGER;
945
946         elog(ERROR, "has_table_privilege: invalid privilege type %s", priv_type);
947
948         /*
949          * We should never get here, but stop the compiler from complaining
950          */
951         return ACL_NO;
952 }
953
954 /*
955  * has_table_privilege_cname_cname
956  *              Check user privileges on a relation given
957  *              char *usename, char *relname, and text priv name.
958  *
959  * RETURNS
960  *              a boolean value
961  *              't' indicating user has the privilege
962  *              'f' indicating user does not have the privilege
963  */
964 static bool
965 has_table_privilege_cname_cname(char *username, char *relname,
966                                                                 text *priv_type_text)
967 {
968         int32           usesysid;
969
970         /*
971          * Lookup userid based on username
972          */
973         usesysid = get_usesysid(username);
974
975         /*
976          * Make use of has_table_privilege_id_cname. It accepts the arguments
977          * we now have.
978          */
979         return has_table_privilege_id_cname(usesysid, relname, priv_type_text);
980 }
981
982
983 /*
984  * has_table_privilege_cname_id
985  *              Check user privileges on a relation given
986  *              char *usename, rel oid, and text priv name.
987  *
988  * RETURNS
989  *              a boolean value
990  *              't' indicating user has the privilege
991  *              'f' indicating user does not have the privilege
992  */
993 static bool
994 has_table_privilege_cname_id(char *username, Oid reloid,
995                                                          text *priv_type_text)
996 {
997         int32           usesysid;
998         char       *relname;
999
1000         /*
1001          * Lookup userid based on username
1002          */
1003         usesysid = get_usesysid(username);
1004
1005         /*
1006          * Lookup relname based on rel oid
1007          */
1008         relname = get_rel_name(reloid);
1009         if (relname == NULL)
1010                 elog(ERROR, "has_table_privilege: invalid relation oid %u",
1011                          reloid);
1012
1013         /*
1014          * Make use of has_table_privilege_id_cname. It accepts the arguments
1015          * we now have.
1016          */
1017         return has_table_privilege_id_cname(usesysid, relname, priv_type_text);
1018 }
1019
1020
1021 /*
1022  * has_table_privilege_id_cname
1023  *              Check user privileges on a relation given
1024  *              usesysid, char *relname, and text priv name.
1025  *
1026  * RETURNS
1027  *              a boolean value
1028  *              't' indicating user has the privilege
1029  *              'f' indicating user does not have the privilege
1030  */
1031 static bool
1032 has_table_privilege_id_cname(int32 usesysid, char *relname,
1033                                                          text *priv_type_text)
1034 {
1035         Oid                     reloid;
1036         AclMode         mode;
1037         int32           result;
1038
1039         /*
1040          * Convert relname to rel OID.
1041          */
1042         reloid = GetSysCacheOid(RELNAME,
1043                                                         PointerGetDatum(relname),
1044                                                         0, 0, 0);
1045         if (!OidIsValid(reloid))
1046                 elog(ERROR, "has_table_privilege: relation \"%s\" does not exist",
1047                          relname);
1048
1049         /*
1050          * Convert priv_type_text to an AclMode
1051          */
1052         mode = convert_priv_string(priv_type_text);
1053
1054         /*
1055          * Finally, check for the privilege
1056          */
1057         result = pg_class_aclcheck(reloid, usesysid, mode);
1058
1059         if (result == ACLCHECK_OK)
1060                 return true;
1061         else
1062                 return false;
1063 }