OSDN Git Service

Change error messages to oids come out as %u and not %d. Change has no
[pg-rex/syncrep.git] / src / backend / catalog / aclchk.c
1 /*-------------------------------------------------------------------------
2  *
3  * aclchk.c
4  *        Routines to check access control permissions.
5  *
6  * Copyright (c) 1994, Regents of the University of California
7  *
8  *
9  * IDENTIFICATION
10  *        $Header: /cvsroot/pgsql/src/backend/catalog/aclchk.c,v 1.21 1999/05/10 00:44:53 momjian Exp $
11  *
12  * NOTES
13  *        See acl.h.
14  *
15  *-------------------------------------------------------------------------
16  */
17 #include <string.h>
18 #include "postgres.h"
19
20 #include "utils/acl.h"                  /* where declarations for this file go */
21 #include "access/heapam.h"
22 #include "access/htup.h"
23 #include "access/tupmacs.h"
24 #include "catalog/indexing.h"
25 #include "catalog/catalog.h"
26 #include "catalog/catname.h"
27 #include "catalog/pg_aggregate.h"
28 #include "catalog/pg_group.h"
29 #include "catalog/pg_operator.h"
30 #include "catalog/pg_proc.h"
31 #include "catalog/pg_shadow.h"
32 #include "catalog/pg_type.h"
33 #include "fmgr.h"
34 #include "parser/parse_agg.h"
35 #include "parser/parse_func.h"
36 #include "storage/bufmgr.h"
37 #include "utils/builtins.h"
38 #include "utils/memutils.h"
39 #include "utils/syscache.h"
40 #include "utils/tqual.h"
41 #include "miscadmin.h"
42
43 static int32 aclcheck(char *relname, Acl *acl, AclId id, AclIdType idtype, AclMode mode);
44
45 /*
46  * Enable use of user relations in place of real system catalogs.
47  */
48 /*#define ACLDEBUG*/
49
50 #ifdef ACLDEBUG
51 /*
52  * Fool the code below into thinking that "pgacls" is pg_class.
53  * relname and relowner are in the same place, happily.
54  */
55 #undef  Anum_pg_class_relacl
56 #define Anum_pg_class_relacl                    3
57 #undef  Natts_pg_class
58 #define Natts_pg_class                                  3
59 #undef  Name_pg_class
60 #define Name_pg_class                                   "pgacls"
61 #undef  Name_pg_group
62 #define Name_pg_group                                   "pggroup"
63 #endif
64
65 /* warning messages, now more explicit. */
66 /* should correspond to the order of the ACLCHK_* result codes above. */
67 char       *aclcheck_error_strings[] = {
68         "No error.",
69         "Permission denied.",
70         "Table does not exist.",
71         "Must be table owner."
72 };
73
74 #ifdef ACLDEBUG_TRACE
75 static
76 dumpacl(Acl *acl)
77 {
78         unsigned        i;
79         AclItem    *aip;
80
81         elog(DEBUG, "acl size = %d, # acls = %d",
82                  ACL_SIZE(acl), ACL_NUM(acl));
83         aip = (AclItem *) ACL_DAT(acl);
84         for (i = 0; i < ACL_NUM(acl); ++i)
85                 elog(DEBUG, "   acl[%d]: %s", i, aclitemout(aip + i));
86 }
87
88 #endif
89
90 /*
91  *
92  */
93 void
94 ChangeAcl(char *relname,
95                   AclItem *mod_aip,
96                   unsigned modechg)
97 {
98         unsigned        i;
99         Acl                *old_acl = (Acl *) NULL,
100                            *new_acl;
101         Relation        relation;
102         HeapTuple       tuple;
103         Datum           values[Natts_pg_class];
104         char            nulls[Natts_pg_class];
105         char            replaces[Natts_pg_class];
106         Relation        idescs[Num_pg_class_indices];
107         int                     free_old_acl = 0;
108
109         /*
110          * Find the pg_class tuple matching 'relname' and extract the ACL. If
111          * there's no ACL, create a default using the pg_class.relowner field.
112          *
113          * We can't use the syscache here, since we need to do a heap_replace on
114          * the tuple we find.
115          */
116         relation = heap_openr(RelationRelationName);
117         if (!RelationIsValid(relation))
118                 elog(ERROR, "ChangeAcl: could not open '%s'??",
119                          RelationRelationName);
120         tuple = SearchSysCacheTuple(RELNAME,
121                                                                 PointerGetDatum(relname),
122                                                                 0, 0, 0);
123         if (!HeapTupleIsValid(tuple))
124         {
125                 heap_close(relation);
126                 elog(ERROR, "ChangeAcl: class \"%s\" not found",
127                          relname);
128                 return;
129         }
130
131         if (!heap_attisnull(tuple, Anum_pg_class_relacl))
132                 old_acl = (Acl *) heap_getattr(tuple,
133                                                                            Anum_pg_class_relacl,
134                                                                            RelationGetDescr(relation),
135                                                                            (bool *) NULL);
136         if (!old_acl || ACL_NUM(old_acl) < 1)
137         {
138 #ifdef ACLDEBUG_TRACE
139                 elog(DEBUG, "ChangeAcl: using default ACL");
140 #endif
141 /*              old_acl = acldefault(((Form_pg_class) GETSTRUCT(tuple))->relowner); */
142                 old_acl = acldefault(relname);
143                 free_old_acl = 1;
144         }
145
146 #ifdef ACLDEBUG_TRACE
147         dumpacl(old_acl);
148 #endif
149         new_acl = aclinsert3(old_acl, mod_aip, modechg);
150 #ifdef ACLDEBUG_TRACE
151         dumpacl(new_acl);
152 #endif
153
154         for (i = 0; i < Natts_pg_class; ++i)
155         {
156                 replaces[i] = ' ';
157                 nulls[i] = ' ';                 /* ignored if replaces[i] == ' ' anyway */
158                 values[i] = (Datum) NULL;               /* ignored if replaces[i] == ' '
159                                                                                  * anyway */
160         }
161         replaces[Anum_pg_class_relacl - 1] = 'r';
162         values[Anum_pg_class_relacl - 1] = (Datum) new_acl;
163         tuple = heap_modifytuple(tuple, relation, values, nulls, replaces);
164         /* XXX handle index on pg_class? */
165         setheapoverride(true);
166         heap_replace(relation, &tuple->t_self, tuple, NULL);
167         setheapoverride(false);
168
169         /* keep the catalog indices up to date */
170         CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices,
171                                            idescs);
172         CatalogIndexInsert(idescs, Num_pg_class_indices, relation, tuple);
173         CatalogCloseIndices(Num_pg_class_indices, idescs);
174
175         heap_close(relation);
176         if (free_old_acl)
177                 pfree(old_acl);
178         pfree(new_acl);
179 }
180
181 AclId
182 get_grosysid(char *groname)
183 {
184         HeapTuple       tuple;
185         AclId           id = 0;
186
187         tuple = SearchSysCacheTuple(GRONAME,
188                                                                 PointerGetDatum(groname),
189                                                                 0, 0, 0);
190         if (HeapTupleIsValid(tuple))
191                 id = ((Form_pg_group) GETSTRUCT(tuple))->grosysid;
192         else
193                 elog(ERROR, "non-existent group \"%s\"", groname);
194         return id;
195 }
196
197 char *
198 get_groname(AclId grosysid)
199 {
200         HeapTuple       tuple;
201         char       *name = NULL;
202
203         tuple = SearchSysCacheTuple(GROSYSID,
204                                                                 ObjectIdGetDatum(grosysid),
205                                                                 0, 0, 0);
206         if (HeapTupleIsValid(tuple))
207                 name = (((Form_pg_group) GETSTRUCT(tuple))->groname).data;
208         else
209                 elog(NOTICE, "get_groname: group %d not found", grosysid);
210         return name;
211 }
212
213 static int32
214 in_group(AclId uid, AclId gid)
215 {
216         Relation        relation;
217         HeapTuple       tuple;
218         Acl                *tmp;
219         unsigned        i,
220                                 num;
221         AclId      *aidp;
222         int32           found = 0;
223
224         relation = heap_openr(GroupRelationName);
225         if (!RelationIsValid(relation))
226         {
227                 elog(NOTICE, "in_group: could not open \"%s\"??",
228                          GroupRelationName);
229                 return 0;
230         }
231         tuple = SearchSysCacheTuple(GROSYSID,
232                                                                 ObjectIdGetDatum(gid),
233                                                                 0, 0, 0);
234         if (HeapTupleIsValid(tuple) &&
235                 !heap_attisnull(tuple, Anum_pg_group_grolist))
236         {
237                 tmp = (IdList *) heap_getattr(tuple,
238                                                                           Anum_pg_group_grolist,
239                                                                           RelationGetDescr(relation),
240                                                                           (bool *) NULL);
241                 /* XXX make me a function */
242                 num = IDLIST_NUM(tmp);
243                 aidp = IDLIST_DAT(tmp);
244                 for (i = 0; i < num; ++i)
245                         if (aidp[i] == uid)
246                         {
247                                 found = 1;
248                                 break;
249                         }
250         }
251         else
252                 elog(NOTICE, "in_group: group %d not found", gid);
253         heap_close(relation);
254         return found;
255 }
256
257 /*
258  * aclcheck
259  * Returns 1 if the 'id' of type 'idtype' has ACL entries in 'acl' to satisfy
260  * any one of the requirements of 'mode'.  Returns 0 otherwise.
261  */
262 static int32
263 aclcheck(char *relname, Acl *acl, AclId id, AclIdType idtype, AclMode mode)
264 {
265         unsigned        i;
266         AclItem    *aip,
267                            *aidat;
268         unsigned        num,
269                                 found_group;
270
271         /* if no acl is found, use world default */
272         if (!acl)
273                 acl = acldefault(relname);
274
275         num = ACL_NUM(acl);
276         aidat = ACL_DAT(acl);
277
278         /*
279          * We'll treat the empty ACL like that, too, although this is more
280          * like an error (i.e., you manually blew away your ACL array) -- the
281          * system never creates an empty ACL.
282          */
283         if (num < 1)
284         {
285 #if ACLDEBUG_TRACE || 1
286                 elog(DEBUG, "aclcheck: zero-length ACL, returning 1");
287 #endif
288                 return ACLCHECK_OK;
289         }
290
291         switch (idtype)
292         {
293                 case ACL_IDTYPE_UID:
294                         for (i = 1, aip = aidat + 1;            /* skip world entry */
295                                  i < num && aip->ai_idtype == ACL_IDTYPE_UID;
296                                  ++i, ++aip)
297                         {
298                                 if (aip->ai_id == id)
299                                 {
300 #ifdef ACLDEBUG_TRACE
301                                         elog(DEBUG, "aclcheck: found %d/%d",
302                                                  aip->ai_id, aip->ai_mode);
303 #endif
304                                         return (aip->ai_mode & mode) ? ACLCHECK_OK : ACLCHECK_NO_PRIV;
305                                 }
306                         }
307                         for (found_group = 0;
308                                  i < num && aip->ai_idtype == ACL_IDTYPE_GID;
309                                  ++i, ++aip)
310                         {
311                                 if (in_group(id, aip->ai_id))
312                                 {
313                                         if (aip->ai_mode & mode)
314                                         {
315                                                 found_group = 1;
316                                                 break;
317                                         }
318                                 }
319                         }
320                         if (found_group)
321                         {
322 #ifdef ACLDEBUG_TRACE
323                                 elog(DEBUG, "aclcheck: all groups ok");
324 #endif
325                                 return ACLCHECK_OK;
326                         }
327                         break;
328                 case ACL_IDTYPE_GID:
329                         for (i = 1, aip = aidat + 1;            /* skip world entry and
330                                                                                                  * UIDs */
331                                  i < num && aip->ai_idtype == ACL_IDTYPE_UID;
332                                  ++i, ++aip)
333                                 ;
334                         for (;
335                                  i < num && aip->ai_idtype == ACL_IDTYPE_GID;
336                                  ++i, ++aip)
337                         {
338                                 if (aip->ai_id == id)
339                                 {
340 #ifdef ACLDEBUG_TRACE
341                                         elog(DEBUG, "aclcheck: found %d/%d",
342                                                  aip->ai_id, aip->ai_mode);
343 #endif
344                                         return (aip->ai_mode & mode) ? ACLCHECK_OK : ACLCHECK_NO_PRIV;
345                                 }
346                         }
347                         break;
348                 case ACL_IDTYPE_WORLD:
349                         break;
350                 default:
351                         elog(ERROR, "aclcheck: bogus ACL id type: %d", idtype);
352                         break;
353         }
354
355 #ifdef ACLDEBUG_TRACE
356         elog(DEBUG, "aclcheck: using world=%d", aidat->ai_mode);
357 #endif
358         return (aidat->ai_mode & mode) ? ACLCHECK_OK : ACLCHECK_NO_PRIV;
359 }
360
361 int32
362 pg_aclcheck(char *relname, char *usename, AclMode mode)
363 {
364         HeapTuple       tuple;
365         AclId           id;
366         Acl                *acl = (Acl *) NULL,
367                            *tmp;
368         int32           result;
369         Relation        relation;
370
371         tuple = SearchSysCacheTuple(USENAME,
372                                                                 PointerGetDatum(usename),
373                                                                 0, 0, 0);
374         if (!HeapTupleIsValid(tuple))
375                 elog(ERROR, "pg_aclcheck: user \"%s\" not found",
376                          usename);
377         id = (AclId) ((Form_pg_shadow) GETSTRUCT(tuple))->usesysid;
378
379         /*
380          * for the 'pg_database' relation, check the usecreatedb field before
381          * checking normal permissions
382          */
383         if (strcmp(DatabaseRelationName, relname) == 0 &&
384                 (((Form_pg_shadow) GETSTRUCT(tuple))->usecreatedb))
385         {
386
387                 /*
388                  * note that even though the user can now append to the
389                  * pg_database table, there is still additional permissions
390                  * checking in dbcommands.c
391                  */
392                 if ((mode & ACL_WR) || (mode & ACL_AP))
393                         return ACLCHECK_OK;
394         }
395
396         /*
397          * Deny anyone permission to update a system catalog unless
398          * pg_shadow.usecatupd is set.  (This is to let superusers protect
399          * themselves from themselves.)
400          */
401         if (((mode & ACL_WR) || (mode & ACL_AP)) &&
402                 !allowSystemTableMods && IsSystemRelationName(relname) &&
403                 !((Form_pg_shadow) GETSTRUCT(tuple))->usecatupd)
404         {
405                 elog(DEBUG, "pg_aclcheck: catalog update to \"%s\": permission denied",
406                          relname);
407                 return ACLCHECK_NO_PRIV;
408         }
409
410         /*
411          * Otherwise, superusers bypass all permission-checking.
412          */
413         if (((Form_pg_shadow) GETSTRUCT(tuple))->usesuper)
414         {
415 #ifdef ACLDEBUG_TRACE
416                 elog(DEBUG, "pg_aclcheck: \"%s\" is superuser",
417                          usename);
418 #endif
419                 return ACLCHECK_OK;
420         }
421
422 #ifndef ACLDEBUG
423         tuple = SearchSysCacheTuple(RELNAME,
424                                                                 PointerGetDatum(relname),
425                                                                 0, 0, 0);
426         if (!HeapTupleIsValid(tuple))
427         {
428                 elog(ERROR, "pg_aclcheck: class \"%s\" not found",
429                          relname);
430                 /* an elog(ERROR) kills us, so no need to return anything. */
431         }
432         if (!heap_attisnull(tuple, Anum_pg_class_relacl))
433         {
434                 relation = heap_openr(RelationRelationName);
435                 tmp = (Acl *) heap_getattr(tuple,
436                                                                    Anum_pg_class_relacl,
437                                                                    RelationGetDescr(relation),
438                                                                    (bool *) NULL);
439                 acl = makeacl(ACL_NUM(tmp));
440                 memmove((char *) acl, (char *) tmp, ACL_SIZE(tmp));
441                 heap_close(relation);
442         }
443         else
444         {
445
446                 /*
447                  * if the acl is null, by default the owner can do whatever he
448                  * wants to with it
449                  */
450                 int4            ownerId;
451
452                 relation = heap_openr(RelationRelationName);
453                 ownerId = (int4) heap_getattr(tuple,
454                                                                           Anum_pg_class_relowner,
455                                                                           RelationGetDescr(relation),
456                                                                           (bool *) NULL);
457                 acl = aclownerdefault(relname, (AclId) ownerId);
458         }
459 #else
460         {                                                       /* This is why the syscache is great... */
461                 static ScanKeyData relkey[1] = {
462                         {0, Anum_pg_class_relname, F_NAMEEQ}
463                 };
464
465                 relation = heap_openr(RelationRelationName);
466                 if (!RelationIsValid(relation))
467                 {
468                         elog(NOTICE, "pg_checkacl: could not open \"%-.*s\"??",
469                                  RelationRelationName);
470                         return ACLCHECK_NO_CLASS;
471                 }
472                 tuple = SearchSysCacheTuple(RELNAME,
473                                                                         PointerGetDatum(relname),
474                                                                         0, 0, 0);
475                 if (HeapTupleIsValid(tuple) &&
476                         !heap_attisnull(tuple, Anum_pg_class_relacl))
477                 {
478                         tmp = (Acl *) heap_getattr(tuple,
479                                                                            Anum_pg_class_relacl,
480                                                                            RelationGetDescr(relation),
481                                                                            (bool *) NULL);
482                         acl = makeacl(ACL_NUM(tmp));
483                         memmove((char *) acl, (char *) tmp, ACL_SIZE(tmp));
484                 }
485                 heap_close(relation);
486         }
487 #endif
488         result = aclcheck(relname, acl, id, (AclIdType) ACL_IDTYPE_UID, mode);
489         if (acl)
490                 pfree(acl);
491         return result;
492 }
493
494 int32
495 pg_ownercheck(char *usename,
496                           char *value,
497                           int cacheid)
498 {
499         HeapTuple       tuple;
500         AclId           user_id,
501                                 owner_id = 0;
502
503         tuple = SearchSysCacheTuple(USENAME,
504                                                                 PointerGetDatum(usename),
505                                                                 0, 0, 0);
506         if (!HeapTupleIsValid(tuple))
507                 elog(ERROR, "pg_ownercheck: user \"%s\" not found",
508                          usename);
509         user_id = (AclId) ((Form_pg_shadow) GETSTRUCT(tuple))->usesysid;
510
511         /*
512          * Superusers bypass all permission-checking.
513          */
514         if (((Form_pg_shadow) GETSTRUCT(tuple))->usesuper)
515         {
516 #ifdef ACLDEBUG_TRACE
517                 elog(DEBUG, "pg_ownercheck: user \"%s\" is superuser",
518                          usename);
519 #endif
520                 return 1;
521         }
522
523         tuple = SearchSysCacheTuple(cacheid, PointerGetDatum(value),
524                                                                 0, 0, 0);
525         switch (cacheid)
526         {
527                 case OPROID:
528                         if (!HeapTupleIsValid(tuple))
529                                 elog(ERROR, "pg_ownercheck: operator %ld not found",
530                                          PointerGetDatum(value));
531                         owner_id = ((Form_pg_operator) GETSTRUCT(tuple))->oprowner;
532                         break;
533                 case PRONAME:
534                         if (!HeapTupleIsValid(tuple))
535                                 elog(ERROR, "pg_ownercheck: function \"%s\" not found",
536                                          value);
537                         owner_id = ((Form_pg_proc) GETSTRUCT(tuple))->proowner;
538                         break;
539                 case RELNAME:
540                         if (!HeapTupleIsValid(tuple))
541                                 elog(ERROR, "pg_ownercheck: class \"%s\" not found",
542                                          value);
543                         owner_id = ((Form_pg_class) GETSTRUCT(tuple))->relowner;
544                         break;
545                 case TYPNAME:
546                         if (!HeapTupleIsValid(tuple))
547                                 elog(ERROR, "pg_ownercheck: type \"%s\" not found",
548                                          value);
549                         owner_id = ((Form_pg_type) GETSTRUCT(tuple))->typowner;
550                         break;
551                 default:
552                         elog(ERROR, "pg_ownercheck: invalid cache id: %d", cacheid);
553                         break;
554         }
555
556         return user_id == owner_id;
557 }
558
559 int32
560 pg_func_ownercheck(char *usename,
561                                    char *funcname,
562                                    int nargs,
563                                    Oid *arglist)
564 {
565         HeapTuple       tuple;
566         AclId           user_id,
567                                 owner_id;
568
569         tuple = SearchSysCacheTuple(USENAME,
570                                                                 PointerGetDatum(usename),
571                                                                 0, 0, 0);
572         if (!HeapTupleIsValid(tuple))
573                 elog(ERROR, "pg_func_ownercheck: user \"%s\" not found",
574                          usename);
575         user_id = (AclId) ((Form_pg_shadow) GETSTRUCT(tuple))->usesysid;
576
577         /*
578          * Superusers bypass all permission-checking.
579          */
580         if (((Form_pg_shadow) GETSTRUCT(tuple))->usesuper)
581         {
582 #ifdef ACLDEBUG_TRACE
583                 elog(DEBUG, "pg_ownercheck: user \"%s\" is superuser",
584                          usename);
585 #endif
586                 return 1;
587         }
588
589         tuple = SearchSysCacheTuple(PRONAME,
590                                                                 PointerGetDatum(funcname),
591                                                                 Int32GetDatum(nargs),
592                                                                 PointerGetDatum(arglist),
593                                                                 0);
594         if (!HeapTupleIsValid(tuple))
595                 func_error("pg_func_ownercheck", funcname, nargs, arglist, NULL);
596
597         owner_id = ((Form_pg_proc) GETSTRUCT(tuple))->proowner;
598
599         return user_id == owner_id;
600 }
601
602 int32
603 pg_aggr_ownercheck(char *usename,
604                                    char *aggname,
605                                    Oid basetypeID)
606 {
607         HeapTuple       tuple;
608         AclId           user_id,
609                                 owner_id;
610
611         tuple = SearchSysCacheTuple(USENAME,
612                                                                 PointerGetDatum(usename),
613                                                                 0, 0, 0);
614         if (!HeapTupleIsValid(tuple))
615                 elog(ERROR, "pg_aggr_ownercheck: user \"%s\" not found",
616                          usename);
617         user_id = (AclId) ((Form_pg_shadow) GETSTRUCT(tuple))->usesysid;
618
619         /*
620          * Superusers bypass all permission-checking.
621          */
622         if (((Form_pg_shadow) GETSTRUCT(tuple))->usesuper)
623         {
624 #ifdef ACLDEBUG_TRACE
625                 elog(DEBUG, "pg_aggr_ownercheck: user \"%s\" is superuser",
626                          usename);
627 #endif
628                 return 1;
629         }
630
631         tuple = SearchSysCacheTuple(AGGNAME,
632                                                                 PointerGetDatum(aggname),
633                                                                 ObjectIdGetDatum(basetypeID),
634                                                                 0, 0);
635
636         if (!HeapTupleIsValid(tuple))
637                 agg_error("pg_aggr_ownercheck", aggname, basetypeID);
638
639         owner_id = ((Form_pg_aggregate) GETSTRUCT(tuple))->aggowner;
640
641         return user_id == owner_id;
642 }