]> granicus.if.org Git - postgresql/blob - src/backend/catalog/aclchk.c
Privileges on functions and procedural languages
[postgresql] / src / backend / catalog / aclchk.c
1 /*-------------------------------------------------------------------------
2  *
3  * aclchk.c
4  *        Routines to check access control permissions.
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/catalog/aclchk.c,v 1.54 2002/02/18 23:11:07 petere Exp $
12  *
13  * NOTES
14  *        See acl.h.
15  *
16  *-------------------------------------------------------------------------
17  */
18 #include "postgres.h"
19
20 #include "access/heapam.h"
21 #include "access/transam.h"
22 #include "catalog/catalog.h"
23 #include "catalog/catname.h"
24 #include "catalog/indexing.h"
25 #include "catalog/pg_aggregate.h"
26 #include "catalog/pg_group.h"
27 #include "catalog/pg_language.h"
28 #include "catalog/pg_operator.h"
29 #include "catalog/pg_proc.h"
30 #include "catalog/pg_shadow.h"
31 #include "catalog/pg_type.h"
32 #include "miscadmin.h"
33 #include "nodes/parsenodes.h"
34 #include "parser/keywords.h"
35 #include "parser/parse.h"
36 #include "parser/parse_agg.h"
37 #include "parser/parse_func.h"
38 #include "parser/parse_expr.h"
39 #include "utils/acl.h"
40 #include "utils/syscache.h"
41 #include "utils/temprel.h"
42
43
44 static void ExecuteGrantStmt_Table(GrantStmt *stmt);
45 static void ExecuteGrantStmt_Function(GrantStmt *stmt);
46 static void ExecuteGrantStmt_Lang(GrantStmt *stmt);
47
48 static const char *privilege_token_string(int token);
49
50 static int32 aclcheck(Acl *acl, AclId id, AclIdType idtype, AclMode mode);
51
52 /* warning messages, now more explicit. */
53 /* MUST correspond to the order of the ACLCHK_* result codes in acl.h. */
54 char       *aclcheck_error_strings[] = {
55         "No error.",
56         "Permission denied.",
57         "Table does not exist.",
58         "Must be table owner."
59 };
60
61
62 #ifdef ACLDEBUG
63 static
64 dumpacl(Acl *acl)
65 {
66         int                     i;
67         AclItem    *aip;
68
69         elog(DEBUG, "acl size = %d, # acls = %d",
70                  ACL_SIZE(acl), ACL_NUM(acl));
71         aip = ACL_DAT(acl);
72         for (i = 0; i < ACL_NUM(acl); ++i)
73                 elog(DEBUG, "   acl[%d]: %s", i,
74                          DatumGetCString(DirectFunctionCall1(aclitemout,
75                                                                                          PointerGetDatum(aip + i))));
76 }
77 #endif   /* ACLDEBUG */
78
79
80 /*
81  * If is_grant is true, adds the given privileges for the list of
82  * grantees to the existing old_acl.  If is_grant is false, the
83  * privileges for the given grantees are removed from old_acl.
84  */
85 static Acl*
86 merge_acl_with_grant(Acl *old_acl, bool is_grant, List *grantees, char *privileges)
87 {
88         List       *j;
89         Acl                *new_acl;
90
91 #ifdef ACLDEBUG
92         dumpacl(old_acl);
93 #endif
94         new_acl = old_acl;
95
96         foreach(j, grantees)
97         {
98                 PrivGrantee *grantee = (PrivGrantee *) lfirst(j);
99                 char       *granteeString;
100                 char       *aclString;
101                 AclItem aclitem;
102                 unsigned        modechg;
103
104                 if (grantee->username)
105                         granteeString = aclmakeuser("U", grantee->username);
106                 else if (grantee->groupname)
107                         granteeString = aclmakeuser("G", grantee->groupname);
108                 else
109                         granteeString = aclmakeuser("A", "");
110
111                 aclString = makeAclString(privileges, granteeString,
112                                                                   is_grant ? '+' : '-');
113
114                 /* Convert string ACL spec into internal form */
115                 aclparse(aclString, &aclitem, &modechg);
116                 new_acl = aclinsert3(new_acl, &aclitem, modechg);
117
118 #ifdef ACLDEBUG
119                 dumpacl(new_acl);
120 #endif
121         }
122
123         return new_acl;
124 }
125
126
127 /*
128  * Called to execute the utility commands GRANT and REVOKE
129  */
130 void
131 ExecuteGrantStmt(GrantStmt *stmt)
132 {
133         /* see comment in pg_type.h */
134         Assert(ACLITEMSIZE == sizeof(AclItem));
135
136         switch(stmt->objtype)
137         {
138                 case TABLE:
139                         ExecuteGrantStmt_Table(stmt);
140                         break;
141                 case FUNCTION:
142                         ExecuteGrantStmt_Function(stmt);
143                         break;
144                 case LANGUAGE:
145                         ExecuteGrantStmt_Lang(stmt);
146                         break;
147                 default:
148                         elog(ERROR, "bogus GrantStmt.objtype %d", stmt->objtype);
149         }
150 }
151
152
153 static void
154 ExecuteGrantStmt_Table(GrantStmt *stmt)
155 {
156         List       *i;
157         char       *privstring;
158
159         if (lfirsti(stmt->privileges) == ALL)
160                 privstring = aclmakepriv(ACL_MODE_STR, 0);
161         else
162         {
163                 privstring = "";
164                 foreach(i, stmt->privileges)
165                 {
166                         int c = 0;
167
168                         switch(lfirsti(i))
169                         {
170                                 case SELECT:
171                                         c = ACL_MODE_SELECT_CHR;
172                                         break;
173                                 case INSERT:
174                                         c = ACL_MODE_INSERT_CHR;
175                                         break;
176                                 case UPDATE:
177                                         c = ACL_MODE_UPDATE_CHR;
178                                         break;
179                                 case DELETE:
180                                         c = ACL_MODE_DELETE_CHR;
181                                         break;
182                                 case RULE:
183                                         c = ACL_MODE_RULE_CHR;
184                                         break;
185                                 case REFERENCES:
186                                         c = ACL_MODE_REFERENCES_CHR;
187                                         break;
188                                 case TRIGGER:
189                                         c = ACL_MODE_TRIGGER_CHR;
190                                         break;
191                                 default:
192                                         elog(ERROR, "invalid privilege type %s for table object",
193                                                  privilege_token_string(lfirsti(i)));
194                         }
195
196                         privstring = aclmakepriv(privstring, c);
197                 }
198         }
199
200
201         foreach(i, stmt->objects)
202         {
203                 char       *relname = strVal(lfirst(i));
204                 Relation        relation;
205                 HeapTuple       tuple;
206                 Form_pg_class pg_class_tuple;
207                 Datum           aclDatum;
208                 bool            isNull;
209                 Acl                *old_acl;
210                 Acl                *new_acl;
211                 unsigned        i;
212                 HeapTuple       newtuple;
213                 Datum           values[Natts_pg_class];
214                 char            nulls[Natts_pg_class];
215                 char            replaces[Natts_pg_class];
216
217
218                 if (!pg_ownercheck(GetUserId(), relname, RELNAME))
219                         elog(ERROR, "permission denied");
220
221                 /* open pg_class */
222                 relation = heap_openr(RelationRelationName, RowExclusiveLock);
223                 tuple = SearchSysCache(RELNAME,
224                                                            PointerGetDatum(relname),
225                                                            0, 0, 0);
226                 if (!HeapTupleIsValid(tuple))
227                 {
228                         heap_close(relation, RowExclusiveLock);
229                         elog(ERROR, "relation \"%s\" not found",
230                                  relname);
231                 }
232                 pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
233
234                 if (pg_class_tuple->relkind == RELKIND_INDEX)
235                         elog(ERROR, "\"%s\" is an index",
236                                  relname);
237
238                 /*
239                  * If there's no ACL, create a default using the pg_class.relowner
240                  * field.
241                  */
242                 aclDatum = SysCacheGetAttr(RELNAME, tuple, Anum_pg_class_relacl,
243                                                                    &isNull);
244                 if (isNull)
245                         old_acl = acldefault(pg_class_tuple->relowner);
246                 else
247                         /* get a detoasted copy of the rel's ACL */
248                         old_acl = DatumGetAclPCopy(aclDatum);
249
250                 new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
251                                                                            stmt->grantees, privstring);
252
253                 /* finished building new ACL value, now insert it */
254                 for (i = 0; i < Natts_pg_class; ++i)
255                 {
256                         replaces[i] = ' ';
257                         nulls[i] = ' ';         /* ignored if replaces[i]==' ' anyway */
258                         values[i] = (Datum) NULL;       /* ignored if replaces[i]==' '
259                                                                                  * anyway */
260                 }
261                 replaces[Anum_pg_class_relacl - 1] = 'r';
262                 values[Anum_pg_class_relacl - 1] = PointerGetDatum(new_acl);
263                 newtuple = heap_modifytuple(tuple, relation, values, nulls, replaces);
264
265                 ReleaseSysCache(tuple);
266
267                 simple_heap_update(relation, &newtuple->t_self, newtuple);
268
269                 {
270                         /* keep the catalog indexes up to date */
271                         Relation        idescs[Num_pg_class_indices];
272
273                         CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices,
274                                                            idescs);
275                         CatalogIndexInsert(idescs, Num_pg_class_indices, relation, newtuple);
276                         CatalogCloseIndices(Num_pg_class_indices, idescs);
277                 }
278
279                 pfree(old_acl);
280                 pfree(new_acl);
281
282                 heap_close(relation, RowExclusiveLock);
283         }
284 }
285
286
287 static Oid
288 find_function_with_arglist(char *name, List *arguments)
289 {
290         Oid             oid;
291         Oid             argoids[FUNC_MAX_ARGS];
292         int             i;
293         int16   argcount;
294  
295         MemSet(argoids, 0, FUNC_MAX_ARGS * sizeof(Oid));
296         argcount = length(arguments);
297         if (argcount > FUNC_MAX_ARGS)
298                 elog(ERROR, "functions cannot have more than %d arguments",
299                          FUNC_MAX_ARGS);
300
301         for (i = 0; i < argcount; i++)
302         {
303                 TypeName   *t = (TypeName *) lfirst(arguments);
304                 char       *typnam = TypeNameToInternalName(t);
305  
306                 arguments = lnext(arguments);
307  
308                 if (strcmp(typnam, "opaque") == 0)
309                         argoids[i] = InvalidOid;
310                 else
311                 {
312                         argoids[i] = GetSysCacheOid(TYPENAME,
313                                                                                 PointerGetDatum(typnam),
314                                                                                 0, 0, 0);
315                         if (!OidIsValid(argoids[i]))
316                                 elog(ERROR, "type '%s' not found", typnam);
317                 }
318         }
319
320         oid = GetSysCacheOid(PROCNAME,
321                                                  PointerGetDatum(name),
322                                                  Int16GetDatum(argcount),
323                                                  PointerGetDatum(argoids),
324                                                  0);
325
326         if (!OidIsValid(oid))
327                 func_error(NULL, name, argcount, argoids, NULL);
328
329         return oid;
330
331
332
333 static void
334 ExecuteGrantStmt_Function(GrantStmt *stmt)
335 {
336         List       *i;
337         char       *privstring = NULL;
338
339         if (lfirsti(stmt->privileges) == ALL)
340                 privstring = aclmakepriv("", ACL_MODE_SELECT_CHR);
341         else
342         {
343                 foreach(i, stmt->privileges)
344                 {
345                         if (lfirsti(i) != EXECUTE)
346                                 elog(ERROR, "invalid privilege type %s for function object",
347                                          privilege_token_string(lfirsti(i)));
348                 }
349
350                 privstring = aclmakepriv("", ACL_MODE_SELECT_CHR);
351         }
352
353         foreach(i, stmt->objects)
354         {
355                 FuncWithArgs *func = (FuncWithArgs *) lfirst(i);
356                 Oid                     oid;
357                 Relation        relation;
358                 HeapTuple       tuple;
359                 Form_pg_proc pg_proc_tuple;
360                 Datum           aclDatum;
361                 bool            isNull;
362                 Acl                *old_acl;
363                 Acl                *new_acl;
364                 unsigned        i;
365                 HeapTuple       newtuple;
366                 Datum           values[Natts_pg_proc];
367                 char            nulls[Natts_pg_proc];
368                 char            replaces[Natts_pg_proc];
369
370                 oid = find_function_with_arglist(func->funcname, func->funcargs);
371                 relation = heap_openr(ProcedureRelationName, RowExclusiveLock);
372                 tuple = SearchSysCache(PROCOID, ObjectIdGetDatum(oid), 0, 0, 0);
373                 if (!HeapTupleIsValid(tuple))
374                 {
375                         heap_close(relation, RowExclusiveLock);
376                         elog(ERROR, "function %u not found", oid);
377                 }
378                 pg_proc_tuple = (Form_pg_proc) GETSTRUCT(tuple);
379
380                 if (pg_proc_tuple->proowner != GetUserId())
381                         elog(ERROR, "permission denied");
382
383                 /*
384                  * If there's no ACL, create a default using the pg_proc.proowner
385                  * field.
386                  */
387                 aclDatum = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_proacl,
388                                                                    &isNull);
389                 if (isNull)
390                         old_acl = acldefault(pg_proc_tuple->proowner);
391                 else
392                         /* get a detoasted copy of the rel's ACL */
393                         old_acl = DatumGetAclPCopy(aclDatum);
394
395                 new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
396                                                                            stmt->grantees, privstring);
397
398                 /* finished building new ACL value, now insert it */
399                 for (i = 0; i < Natts_pg_proc; ++i)
400                 {
401                         replaces[i] = ' ';
402                         nulls[i] = ' ';         /* ignored if replaces[i]==' ' anyway */
403                         values[i] = (Datum) NULL;       /* ignored if replaces[i]==' '
404                                                                                  * anyway */
405                 }
406                 replaces[Anum_pg_proc_proacl - 1] = 'r';
407                 values[Anum_pg_proc_proacl - 1] = PointerGetDatum(new_acl);
408                 newtuple = heap_modifytuple(tuple, relation, values, nulls, replaces);
409
410                 ReleaseSysCache(tuple);
411
412                 simple_heap_update(relation, &newtuple->t_self, newtuple);
413
414                 {
415                         /* keep the catalog indexes up to date */
416                         Relation        idescs[Num_pg_proc_indices];
417
418                         CatalogOpenIndices(Num_pg_proc_indices, Name_pg_proc_indices,
419                                                            idescs);
420                         CatalogIndexInsert(idescs, Num_pg_proc_indices, relation, newtuple);
421                         CatalogCloseIndices(Num_pg_proc_indices, idescs);
422                 }
423
424                 pfree(old_acl);
425                 pfree(new_acl);
426
427                 heap_close(relation, RowExclusiveLock);
428         }
429 }
430
431
432 static void
433 ExecuteGrantStmt_Lang(GrantStmt *stmt)
434 {
435         List       *i;
436         char       *privstring = NULL;
437
438         if (lfirsti(stmt->privileges) == ALL)
439                 privstring = aclmakepriv("", ACL_MODE_SELECT_CHR);
440         else
441         {
442                 foreach(i, stmt->privileges)
443                 {
444                         if (lfirsti(i) != USAGE)
445                                 elog(ERROR, "invalid privilege type %s for language object",
446                                          privilege_token_string(lfirsti(i)));
447                 }
448
449                 privstring = aclmakepriv("", ACL_MODE_SELECT_CHR);
450         }
451
452         foreach(i, stmt->objects)
453         {
454                 char       *langname = strVal(lfirst(i));
455                 Relation        relation;
456                 HeapTuple       tuple;
457                 Form_pg_language pg_language_tuple;
458                 Datum           aclDatum;
459                 bool            isNull;
460                 Acl                *old_acl;
461                 Acl                *new_acl;
462                 unsigned        i;
463                 HeapTuple       newtuple;
464                 Datum           values[Natts_pg_language];
465                 char            nulls[Natts_pg_language];
466                 char            replaces[Natts_pg_language];
467
468                 if (!superuser())
469                         elog(ERROR, "permission denied");
470
471                 relation = heap_openr(LanguageRelationName, RowExclusiveLock);
472                 tuple = SearchSysCache(LANGNAME, PointerGetDatum(langname), 0, 0, 0);
473                 if (!HeapTupleIsValid(tuple))
474                 {
475                         heap_close(relation, RowExclusiveLock);
476                         elog(ERROR, "language \"%s\" not found", langname);
477                 }
478                 pg_language_tuple = (Form_pg_language) GETSTRUCT(tuple);
479
480                 if (!pg_language_tuple->lanpltrusted)
481                 {
482                         heap_close(relation, RowExclusiveLock);
483                         elog(ERROR, "language \"%s\" is not trusted", langname);
484                 }
485
486                 /*
487                  * If there's no ACL, create a default.
488                  */
489                 aclDatum = SysCacheGetAttr(LANGNAME, tuple, Anum_pg_language_lanacl,
490                                                                    &isNull);
491                 if (isNull)
492                         old_acl = acldefault(InvalidOid);
493                 else
494                         /* get a detoasted copy of the rel's ACL */
495                         old_acl = DatumGetAclPCopy(aclDatum);
496
497                 new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
498                                                                            stmt->grantees, privstring);
499
500                 /* finished building new ACL value, now insert it */
501                 for (i = 0; i < Natts_pg_language; ++i)
502                 {
503                         replaces[i] = ' ';
504                         nulls[i] = ' ';         /* ignored if replaces[i]==' ' anyway */
505                         values[i] = (Datum) NULL;       /* ignored if replaces[i]==' '
506                                                                                  * anyway */
507                 }
508                 replaces[Anum_pg_language_lanacl - 1] = 'r';
509                 values[Anum_pg_language_lanacl - 1] = PointerGetDatum(new_acl);
510                 newtuple = heap_modifytuple(tuple, relation, values, nulls, replaces);
511
512                 ReleaseSysCache(tuple);
513
514                 simple_heap_update(relation, &newtuple->t_self, newtuple);
515
516                 {
517                         /* keep the catalog indexes up to date */
518                         Relation        idescs[Num_pg_language_indices];
519
520                         CatalogOpenIndices(Num_pg_language_indices, Name_pg_language_indices,
521                                                            idescs);
522                         CatalogIndexInsert(idescs, Num_pg_language_indices, relation, newtuple);
523                         CatalogCloseIndices(Num_pg_language_indices, idescs);
524                 }
525
526                 pfree(old_acl);
527                 pfree(new_acl);
528
529                 heap_close(relation, RowExclusiveLock);
530         }
531 }
532
533
534
535 static const char *
536 privilege_token_string(int token)
537 {
538         const char *s = TokenString(token);
539
540         if (s)
541                 return s;
542         else
543                 elog(ERROR, "privilege_token_string: invalid token number");
544         return NULL; /* appease compiler */
545 }
546
547
548
549 AclId
550 get_grosysid(char *groname)
551 {
552         HeapTuple       tuple;
553         AclId           id = 0;
554
555         tuple = SearchSysCache(GRONAME,
556                                                    PointerGetDatum(groname),
557                                                    0, 0, 0);
558         if (HeapTupleIsValid(tuple))
559         {
560                 id = ((Form_pg_group) GETSTRUCT(tuple))->grosysid;
561                 ReleaseSysCache(tuple);
562         }
563         else
564                 elog(ERROR, "non-existent group \"%s\"", groname);
565         return id;
566 }
567
568 /*
569  * Convert group ID to name, or return NULL if group can't be found
570  */
571 char *
572 get_groname(AclId grosysid)
573 {
574         HeapTuple       tuple;
575         char       *name = NULL;
576
577         tuple = SearchSysCache(GROSYSID,
578                                                    ObjectIdGetDatum(grosysid),
579                                                    0, 0, 0);
580         if (HeapTupleIsValid(tuple))
581         {
582                 name = pstrdup(NameStr(((Form_pg_group) GETSTRUCT(tuple))->groname));
583                 ReleaseSysCache(tuple);
584         }
585         return name;
586 }
587
588 /*
589  * Is user a member of group?
590  */
591 static bool
592 in_group(AclId uid, AclId gid)
593 {
594         bool            result = false;
595         HeapTuple       tuple;
596         Datum           att;
597         bool            isNull;
598         IdList     *glist;
599         AclId      *aidp;
600         int                     i,
601                                 num;
602
603         tuple = SearchSysCache(GROSYSID,
604                                                    ObjectIdGetDatum(gid),
605                                                    0, 0, 0);
606         if (HeapTupleIsValid(tuple))
607         {
608                 att = SysCacheGetAttr(GROSYSID,
609                                                           tuple,
610                                                           Anum_pg_group_grolist,
611                                                           &isNull);
612                 if (!isNull)
613                 {
614                         /* be sure the IdList is not toasted */
615                         glist = DatumGetIdListP(att);
616                         /* scan it */
617                         num = IDLIST_NUM(glist);
618                         aidp = IDLIST_DAT(glist);
619                         for (i = 0; i < num; ++i)
620                         {
621                                 if (aidp[i] == uid)
622                                 {
623                                         result = true;
624                                         break;
625                                 }
626                         }
627                         /* if IdList was toasted, free detoasted copy */
628                         if ((Pointer) glist != DatumGetPointer(att))
629                                 pfree(glist);
630                 }
631                 ReleaseSysCache(tuple);
632         }
633         else
634                 elog(NOTICE, "in_group: group %u not found", gid);
635         return result;
636 }
637
638 /*
639  * aclcheck
640  *
641  * Returns ACLCHECK_OK if the 'id' of type 'idtype' has ACL entries in 'acl'
642  * to satisfy any one of the requirements of 'mode'.  Returns an appropriate
643  * ACLCHECK_* error code otherwise.
644  *
645  * The ACL list is expected to be sorted in standard order.
646  */
647 static int32
648 aclcheck(Acl *acl, AclId id, AclIdType idtype, AclMode mode)
649 {
650         AclItem    *aip,
651                            *aidat;
652         int                     i,
653                                 num;
654
655         /*
656          * If ACL is null, default to "OK" --- this should not happen, since
657          * caller should have inserted appropriate default
658          */
659         if (!acl)
660         {
661                 elog(DEBUG, "aclcheck: null ACL, returning OK");
662                 return ACLCHECK_OK;
663         }
664
665         num = ACL_NUM(acl);
666         aidat = ACL_DAT(acl);
667
668         /*
669          * We'll treat the empty ACL like that, too, although this is more
670          * like an error (i.e., you manually blew away your ACL array) -- the
671          * system never creates an empty ACL, since there must always be a
672          * "world" entry in the first slot.
673          */
674         if (num < 1)
675         {
676                 elog(DEBUG, "aclcheck: zero-length ACL, returning OK");
677                 return ACLCHECK_OK;
678         }
679
680         /*
681          * "World" rights are applicable regardless of the passed-in ID, and
682          * since they're much the cheapest to check, check 'em first.
683          */
684         if (aidat->ai_idtype != ACL_IDTYPE_WORLD)
685                 elog(ERROR, "aclcheck: first entry in ACL is not 'world' entry");
686         if (aidat->ai_mode & mode)
687         {
688 #ifdef ACLDEBUG
689                 elog(DEBUG, "aclcheck: using world=%d", aidat->ai_mode);
690 #endif
691                 return ACLCHECK_OK;
692         }
693
694         switch (idtype)
695         {
696                 case ACL_IDTYPE_UID:
697                         /* See if permission is granted directly to user */
698                         for (i = 1, aip = aidat + 1;            /* skip world entry */
699                                  i < num && aip->ai_idtype == ACL_IDTYPE_UID;
700                                  ++i, ++aip)
701                         {
702                                 if (aip->ai_id == id)
703                                 {
704 #ifdef ACLDEBUG
705                                         elog(DEBUG, "aclcheck: found user %u/%d",
706                                                  aip->ai_id, aip->ai_mode);
707 #endif
708                                         if (aip->ai_mode & mode)
709                                                 return ACLCHECK_OK;
710                                 }
711                         }
712                         /* See if he has the permission via any group */
713                         for (;
714                                  i < num && aip->ai_idtype == ACL_IDTYPE_GID;
715                                  ++i, ++aip)
716                         {
717                                 if (aip->ai_mode & mode)
718                                 {
719                                         if (in_group(id, aip->ai_id))
720                                         {
721 #ifdef ACLDEBUG
722                                                 elog(DEBUG, "aclcheck: found group %u/%d",
723                                                          aip->ai_id, aip->ai_mode);
724 #endif
725                                                 return ACLCHECK_OK;
726                                         }
727                                 }
728                         }
729                         break;
730                 case ACL_IDTYPE_GID:
731                         /* Look for this group ID */
732                         for (i = 1, aip = aidat + 1;            /* skip world entry */
733                                  i < num && aip->ai_idtype == ACL_IDTYPE_UID;
734                                  ++i, ++aip)
735                                  /* skip UID entry */ ;
736                         for (;
737                                  i < num && aip->ai_idtype == ACL_IDTYPE_GID;
738                                  ++i, ++aip)
739                         {
740                                 if (aip->ai_id == id)
741                                 {
742 #ifdef ACLDEBUG
743                                         elog(DEBUG, "aclcheck: found group %u/%d",
744                                                  aip->ai_id, aip->ai_mode);
745 #endif
746                                         if (aip->ai_mode & mode)
747                                                 return ACLCHECK_OK;
748                                 }
749                         }
750                         break;
751                 case ACL_IDTYPE_WORLD:
752                         /* Only check the world entry */
753                         break;
754                 default:
755                         elog(ERROR, "aclcheck: bogus ACL id type: %d", idtype);
756                         break;
757         }
758
759         /* If get here, he doesn't have the privilege nohow */
760         return ACLCHECK_NO_PRIV;
761 }
762
763 /*
764  * Exported routine for checking a user's access privileges to a table
765  *
766  * Returns an ACLCHECK_* result code.
767  */
768 int32
769 pg_aclcheck(char *relname, Oid userid, AclMode mode)
770 {
771         int32           result;
772         HeapTuple       tuple;
773         char       *usename;
774         Datum           aclDatum;
775         bool            isNull;
776         Acl                *acl;
777
778         /*
779          * Validate userid, find out if he is superuser
780          */
781         tuple = SearchSysCache(SHADOWSYSID,
782                                                    ObjectIdGetDatum(userid),
783                                                    0, 0, 0);
784         if (!HeapTupleIsValid(tuple))
785                 elog(ERROR, "pg_aclcheck: invalid user id %u",
786                          (unsigned) userid);
787
788         usename = NameStr(((Form_pg_shadow) GETSTRUCT(tuple))->usename);
789
790         /*
791          * Deny anyone permission to update a system catalog unless
792          * pg_shadow.usecatupd is set.  (This is to let superusers protect
793          * themselves from themselves.)
794          */
795         if ((mode & (ACL_INSERT | ACL_UPDATE | ACL_DELETE)) &&
796                 !allowSystemTableMods && IsSystemRelationName(relname) &&
797                 !is_temp_relname(relname) &&
798                 !((Form_pg_shadow) GETSTRUCT(tuple))->usecatupd)
799         {
800 #ifdef ACLDEBUG
801                 elog(DEBUG, "pg_aclcheck: catalog update to \"%s\": permission denied",
802                          relname);
803 #endif
804                 ReleaseSysCache(tuple);
805                 return ACLCHECK_NO_PRIV;
806         }
807
808         /*
809          * Otherwise, superusers bypass all permission-checking.
810          */
811         if (((Form_pg_shadow) GETSTRUCT(tuple))->usesuper)
812         {
813 #ifdef ACLDEBUG
814                 elog(DEBUG, "pg_aclcheck: \"%s\" is superuser",
815                          usename);
816 #endif
817                 ReleaseSysCache(tuple);
818                 return ACLCHECK_OK;
819         }
820
821         ReleaseSysCache(tuple);
822         /* caution: usename is inaccessible beyond this point... */
823
824         /*
825          * Normal case: get the relation's ACL from pg_class
826          */
827         tuple = SearchSysCache(RELNAME,
828                                                    PointerGetDatum(relname),
829                                                    0, 0, 0);
830         if (!HeapTupleIsValid(tuple))
831                 elog(ERROR, "pg_aclcheck: class \"%s\" not found", relname);
832
833         aclDatum = SysCacheGetAttr(RELNAME, tuple, Anum_pg_class_relacl,
834                                                            &isNull);
835         if (isNull)
836         {
837                 /* No ACL, so build default ACL for rel */
838                 AclId           ownerId;
839
840                 ownerId = ((Form_pg_class) GETSTRUCT(tuple))->relowner;
841                 acl = acldefault(ownerId);
842                 aclDatum = (Datum) 0;
843         }
844         else
845         {
846                 /* detoast rel's ACL if necessary */
847                 acl = DatumGetAclP(aclDatum);
848         }
849
850         result = aclcheck(acl, userid, (AclIdType) ACL_IDTYPE_UID, mode);
851
852         /* if we have a detoasted copy, free it */
853         if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
854                 pfree(acl);
855
856         ReleaseSysCache(tuple);
857
858         return result;
859 }
860
861 /*
862  * Check ownership of an object identified by name (which will be looked
863  * up in the system cache identified by cacheid).
864  *
865  * Returns true if userid owns the item, or should be allowed to modify
866  * the item as if he owned it.
867  */
868 bool
869 pg_ownercheck(Oid userid,
870                           const char *name,
871                           int cacheid)
872 {
873         HeapTuple       tuple;
874         AclId           owner_id;
875         char       *usename;
876
877         tuple = SearchSysCache(SHADOWSYSID,
878                                                    ObjectIdGetDatum(userid),
879                                                    0, 0, 0);
880         if (!HeapTupleIsValid(tuple))
881                 elog(ERROR, "pg_ownercheck: invalid user id %u",
882                          (unsigned) userid);
883         usename = NameStr(((Form_pg_shadow) GETSTRUCT(tuple))->usename);
884
885         /*
886          * Superusers bypass all permission-checking.
887          */
888         if (((Form_pg_shadow) GETSTRUCT(tuple))->usesuper)
889         {
890 #ifdef ACLDEBUG
891                 elog(DEBUG, "pg_ownercheck: user \"%s\" is superuser",
892                          usename);
893 #endif
894                 ReleaseSysCache(tuple);
895                 return true;
896         }
897
898         ReleaseSysCache(tuple);
899         /* caution: usename is inaccessible beyond this point... */
900
901         tuple = SearchSysCache(cacheid,
902                                                    PointerGetDatum(name),
903                                                    0, 0, 0);
904         switch (cacheid)
905         {
906                 case RELNAME:
907                         if (!HeapTupleIsValid(tuple))
908                                 elog(ERROR, "pg_ownercheck: class \"%s\" not found",
909                                          name);
910                         owner_id = ((Form_pg_class) GETSTRUCT(tuple))->relowner;
911                         break;
912                 case TYPENAME:
913                         if (!HeapTupleIsValid(tuple))
914                                 elog(ERROR, "pg_ownercheck: type \"%s\" not found",
915                                          name);
916                         owner_id = ((Form_pg_type) GETSTRUCT(tuple))->typowner;
917                         break;
918                 default:
919                         elog(ERROR, "pg_ownercheck: invalid cache id: %d", cacheid);
920                         owner_id = 0;           /* keep compiler quiet */
921                         break;
922         }
923
924         ReleaseSysCache(tuple);
925
926         return userid == owner_id;
927 }
928
929 /*
930  * Ownership check for an operator (specified by OID).
931  */
932 bool
933 pg_oper_ownercheck(Oid userid, Oid oprid)
934 {
935         HeapTuple       tuple;
936         AclId           owner_id;
937         char       *usename;
938
939         tuple = SearchSysCache(SHADOWSYSID,
940                                                    ObjectIdGetDatum(userid),
941                                                    0, 0, 0);
942         if (!HeapTupleIsValid(tuple))
943                 elog(ERROR, "pg_oper_ownercheck: invalid user id %u",
944                          (unsigned) userid);
945         usename = NameStr(((Form_pg_shadow) GETSTRUCT(tuple))->usename);
946
947         /*
948          * Superusers bypass all permission-checking.
949          */
950         if (((Form_pg_shadow) GETSTRUCT(tuple))->usesuper)
951         {
952 #ifdef ACLDEBUG
953                 elog(DEBUG, "pg_ownercheck: user \"%s\" is superuser",
954                          usename);
955 #endif
956                 ReleaseSysCache(tuple);
957                 return true;
958         }
959
960         ReleaseSysCache(tuple);
961         /* caution: usename is inaccessible beyond this point... */
962
963         tuple = SearchSysCache(OPEROID,
964                                                    ObjectIdGetDatum(oprid),
965                                                    0, 0, 0);
966         if (!HeapTupleIsValid(tuple))
967                 elog(ERROR, "pg_ownercheck: operator %u not found",
968                          oprid);
969
970         owner_id = ((Form_pg_operator) GETSTRUCT(tuple))->oprowner;
971
972         ReleaseSysCache(tuple);
973
974         return userid == owner_id;
975 }
976
977 /*
978  * Ownership check for a function (specified by name and argument types).
979  */
980 bool
981 pg_func_ownercheck(Oid userid,
982                                    char *funcname,
983                                    int nargs,
984                                    Oid *arglist)
985 {
986         HeapTuple       tuple;
987         AclId           owner_id;
988         char       *usename;
989
990         tuple = SearchSysCache(SHADOWSYSID,
991                                                    ObjectIdGetDatum(userid),
992                                                    0, 0, 0);
993         if (!HeapTupleIsValid(tuple))
994                 elog(ERROR, "pg_func_ownercheck: invalid user id %u",
995                          (unsigned) userid);
996         usename = NameStr(((Form_pg_shadow) GETSTRUCT(tuple))->usename);
997
998         /*
999          * Superusers bypass all permission-checking.
1000          */
1001         if (((Form_pg_shadow) GETSTRUCT(tuple))->usesuper)
1002         {
1003 #ifdef ACLDEBUG
1004                 elog(DEBUG, "pg_ownercheck: user \"%s\" is superuser",
1005                          usename);
1006 #endif
1007                 ReleaseSysCache(tuple);
1008                 return true;
1009         }
1010
1011         ReleaseSysCache(tuple);
1012         /* caution: usename is inaccessible beyond this point... */
1013
1014         tuple = SearchSysCache(PROCNAME,
1015                                                    PointerGetDatum(funcname),
1016                                                    Int32GetDatum(nargs),
1017                                                    PointerGetDatum(arglist),
1018                                                    0);
1019         if (!HeapTupleIsValid(tuple))
1020                 func_error("pg_func_ownercheck", funcname, nargs, arglist, NULL);
1021
1022         owner_id = ((Form_pg_proc) GETSTRUCT(tuple))->proowner;
1023
1024         ReleaseSysCache(tuple);
1025
1026         return userid == owner_id;
1027 }
1028
1029 /*
1030  * Ownership check for an aggregate function (specified by name and
1031  * argument type).
1032  */
1033 bool
1034 pg_aggr_ownercheck(Oid userid,
1035                                    char *aggname,
1036                                    Oid basetypeID)
1037 {
1038         HeapTuple       tuple;
1039         AclId           owner_id;
1040         char       *usename;
1041
1042         tuple = SearchSysCache(SHADOWSYSID,
1043                                                    PointerGetDatum(userid),
1044                                                    0, 0, 0);
1045         if (!HeapTupleIsValid(tuple))
1046                 elog(ERROR, "pg_aggr_ownercheck: invalid user id %u",
1047                          (unsigned) userid);
1048         usename = NameStr(((Form_pg_shadow) GETSTRUCT(tuple))->usename);
1049
1050         /*
1051          * Superusers bypass all permission-checking.
1052          */
1053         if (((Form_pg_shadow) GETSTRUCT(tuple))->usesuper)
1054         {
1055 #ifdef ACLDEBUG
1056                 elog(DEBUG, "pg_aggr_ownercheck: user \"%s\" is superuser",
1057                          usename);
1058 #endif
1059                 ReleaseSysCache(tuple);
1060                 return true;
1061         }
1062
1063         ReleaseSysCache(tuple);
1064         /* caution: usename is inaccessible beyond this point... */
1065
1066         tuple = SearchSysCache(AGGNAME,
1067                                                    PointerGetDatum(aggname),
1068                                                    ObjectIdGetDatum(basetypeID),
1069                                                    0, 0);
1070         if (!HeapTupleIsValid(tuple))
1071                 agg_error("pg_aggr_ownercheck", aggname, basetypeID);
1072
1073         owner_id = ((Form_pg_aggregate) GETSTRUCT(tuple))->aggowner;
1074
1075         ReleaseSysCache(tuple);
1076
1077         return userid == owner_id;
1078 }
1079
1080
1081
1082 /*
1083  * Exported routine for checking a user's access privileges to a function
1084  *
1085  * Returns an ACLCHECK_* result code.
1086  */
1087 int32
1088 pg_proc_aclcheck(Oid proc_oid, Oid userid)
1089 {
1090         int32           result;
1091         HeapTuple       tuple;
1092         Datum           aclDatum;
1093         bool            isNull;
1094         Acl                *acl;
1095
1096         if (superuser_arg(userid))
1097                 return ACLCHECK_OK;
1098
1099         /*
1100          * Validate userid
1101          */
1102         tuple = SearchSysCache(SHADOWSYSID,
1103                                                    ObjectIdGetDatum(userid),
1104                                                    0, 0, 0);
1105         if (!HeapTupleIsValid(tuple))
1106                 elog(ERROR, "pg_proc_aclcheck: invalid user id %u",
1107                          (unsigned) userid);
1108         ReleaseSysCache(tuple);
1109
1110         /*
1111          * Normal case: get the function's ACL from pg_proc
1112          */
1113         tuple = SearchSysCache(PROCOID,
1114                                                    ObjectIdGetDatum(proc_oid),
1115                                                    0, 0, 0);
1116         if (!HeapTupleIsValid(tuple))
1117                 elog(ERROR, "pg_proc_aclcheck: function %u not found", proc_oid);
1118
1119         aclDatum = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_proacl,
1120                                                            &isNull);
1121         if (isNull)
1122         {
1123                 /* No ACL, so build default ACL */
1124                 AclId           ownerId;
1125
1126                 ownerId = ((Form_pg_proc) GETSTRUCT(tuple))->proowner;
1127                 acl = acldefault(ownerId);
1128                 aclDatum = (Datum) 0;
1129         }
1130         else
1131         {
1132                 /* detoast ACL if necessary */
1133                 acl = DatumGetAclP(aclDatum);
1134         }
1135
1136         /*
1137          * Functions only have one kind of privilege, which is encoded as
1138          * "SELECT" here.
1139          */
1140         result = aclcheck(acl, userid, (AclIdType) ACL_IDTYPE_UID, ACL_SELECT);
1141
1142         /* if we have a detoasted copy, free it */
1143         if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
1144                 pfree(acl);
1145
1146         ReleaseSysCache(tuple);
1147
1148         return result;
1149 }
1150
1151
1152
1153 /*
1154  * Exported routine for checking a user's access privileges to a language
1155  *
1156  * Returns an ACLCHECK_* result code.
1157  */
1158 int32
1159 pg_language_aclcheck(Oid lang_oid, Oid userid)
1160 {
1161         int32           result;
1162         HeapTuple       tuple;
1163         Datum           aclDatum;
1164         bool            isNull;
1165         Acl                *acl;
1166
1167         if (superuser_arg(userid))
1168                 return ACLCHECK_OK;
1169
1170         /*
1171          * Validate userid
1172          */
1173         tuple = SearchSysCache(SHADOWSYSID,
1174                                                    ObjectIdGetDatum(userid),
1175                                                    0, 0, 0);
1176         if (!HeapTupleIsValid(tuple))
1177                 elog(ERROR, "pg_language_aclcheck: invalid user id %u",
1178                          (unsigned) userid);
1179         ReleaseSysCache(tuple);
1180
1181         /*
1182          * Normal case: get the function's ACL from pg_language
1183          */
1184         tuple = SearchSysCache(LANGOID,
1185                                                    ObjectIdGetDatum(lang_oid),
1186                                                    0, 0, 0);
1187         if (!HeapTupleIsValid(tuple))
1188                 elog(ERROR, "pg_language_aclcheck: language %u not found", lang_oid);
1189
1190         aclDatum = SysCacheGetAttr(LANGOID, tuple, Anum_pg_language_lanacl,
1191                                                            &isNull);
1192         if (isNull)
1193         {
1194                 /* No ACL, so build default ACL */
1195                 acl = acldefault(InvalidOid);
1196                 aclDatum = (Datum) 0;
1197         }
1198         else
1199         {
1200                 /* detoast ACL if necessary */
1201                 acl = DatumGetAclP(aclDatum);
1202         }
1203
1204         /*
1205          * Languages only have one kind of privilege, which is encoded as
1206          * "SELECT" here.
1207          */
1208         result = aclcheck(acl, userid, (AclIdType) ACL_IDTYPE_UID, ACL_SELECT);
1209
1210         /* if we have a detoasted copy, free it */
1211         if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
1212                 pfree(acl);
1213
1214         ReleaseSysCache(tuple);
1215
1216         return result;
1217 }