1 /*-------------------------------------------------------------------------
4 * Routines to check access control permissions.
6 * Copyright (c) 1994, Regents of the University of California
10 * $Header: /cvsroot/pgsql/src/backend/catalog/aclchk.c,v 1.21 1999/05/10 00:44:53 momjian Exp $
15 *-------------------------------------------------------------------------
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"
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"
43 static int32 aclcheck(char *relname, Acl *acl, AclId id, AclIdType idtype, AclMode mode);
46 * Enable use of user relations in place of real system catalogs.
52 * Fool the code below into thinking that "pgacls" is pg_class.
53 * relname and relowner are in the same place, happily.
55 #undef Anum_pg_class_relacl
56 #define Anum_pg_class_relacl 3
58 #define Natts_pg_class 3
60 #define Name_pg_class "pgacls"
62 #define Name_pg_group "pggroup"
65 /* warning messages, now more explicit. */
66 /* should correspond to the order of the ACLCHK_* result codes above. */
67 char *aclcheck_error_strings[] = {
70 "Table does not exist.",
71 "Must be table owner."
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));
94 ChangeAcl(char *relname,
99 Acl *old_acl = (Acl *) NULL,
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;
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.
113 * We can't use the syscache here, since we need to do a heap_replace on
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),
123 if (!HeapTupleIsValid(tuple))
125 heap_close(relation);
126 elog(ERROR, "ChangeAcl: class \"%s\" not found",
131 if (!heap_attisnull(tuple, Anum_pg_class_relacl))
132 old_acl = (Acl *) heap_getattr(tuple,
133 Anum_pg_class_relacl,
134 RelationGetDescr(relation),
136 if (!old_acl || ACL_NUM(old_acl) < 1)
138 #ifdef ACLDEBUG_TRACE
139 elog(DEBUG, "ChangeAcl: using default ACL");
141 /* old_acl = acldefault(((Form_pg_class) GETSTRUCT(tuple))->relowner); */
142 old_acl = acldefault(relname);
146 #ifdef ACLDEBUG_TRACE
149 new_acl = aclinsert3(old_acl, mod_aip, modechg);
150 #ifdef ACLDEBUG_TRACE
154 for (i = 0; i < Natts_pg_class; ++i)
157 nulls[i] = ' '; /* ignored if replaces[i] == ' ' anyway */
158 values[i] = (Datum) NULL; /* ignored if replaces[i] == ' '
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);
169 /* keep the catalog indices up to date */
170 CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices,
172 CatalogIndexInsert(idescs, Num_pg_class_indices, relation, tuple);
173 CatalogCloseIndices(Num_pg_class_indices, idescs);
175 heap_close(relation);
182 get_grosysid(char *groname)
187 tuple = SearchSysCacheTuple(GRONAME,
188 PointerGetDatum(groname),
190 if (HeapTupleIsValid(tuple))
191 id = ((Form_pg_group) GETSTRUCT(tuple))->grosysid;
193 elog(ERROR, "non-existent group \"%s\"", groname);
198 get_groname(AclId grosysid)
203 tuple = SearchSysCacheTuple(GROSYSID,
204 ObjectIdGetDatum(grosysid),
206 if (HeapTupleIsValid(tuple))
207 name = (((Form_pg_group) GETSTRUCT(tuple))->groname).data;
209 elog(NOTICE, "get_groname: group %d not found", grosysid);
214 in_group(AclId uid, AclId gid)
224 relation = heap_openr(GroupRelationName);
225 if (!RelationIsValid(relation))
227 elog(NOTICE, "in_group: could not open \"%s\"??",
231 tuple = SearchSysCacheTuple(GROSYSID,
232 ObjectIdGetDatum(gid),
234 if (HeapTupleIsValid(tuple) &&
235 !heap_attisnull(tuple, Anum_pg_group_grolist))
237 tmp = (IdList *) heap_getattr(tuple,
238 Anum_pg_group_grolist,
239 RelationGetDescr(relation),
241 /* XXX make me a function */
242 num = IDLIST_NUM(tmp);
243 aidp = IDLIST_DAT(tmp);
244 for (i = 0; i < num; ++i)
252 elog(NOTICE, "in_group: group %d not found", gid);
253 heap_close(relation);
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.
263 aclcheck(char *relname, Acl *acl, AclId id, AclIdType idtype, AclMode mode)
271 /* if no acl is found, use world default */
273 acl = acldefault(relname);
276 aidat = ACL_DAT(acl);
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.
285 #if ACLDEBUG_TRACE || 1
286 elog(DEBUG, "aclcheck: zero-length ACL, returning 1");
294 for (i = 1, aip = aidat + 1; /* skip world entry */
295 i < num && aip->ai_idtype == ACL_IDTYPE_UID;
298 if (aip->ai_id == id)
300 #ifdef ACLDEBUG_TRACE
301 elog(DEBUG, "aclcheck: found %d/%d",
302 aip->ai_id, aip->ai_mode);
304 return (aip->ai_mode & mode) ? ACLCHECK_OK : ACLCHECK_NO_PRIV;
307 for (found_group = 0;
308 i < num && aip->ai_idtype == ACL_IDTYPE_GID;
311 if (in_group(id, aip->ai_id))
313 if (aip->ai_mode & mode)
322 #ifdef ACLDEBUG_TRACE
323 elog(DEBUG, "aclcheck: all groups ok");
329 for (i = 1, aip = aidat + 1; /* skip world entry and
331 i < num && aip->ai_idtype == ACL_IDTYPE_UID;
335 i < num && aip->ai_idtype == ACL_IDTYPE_GID;
338 if (aip->ai_id == id)
340 #ifdef ACLDEBUG_TRACE
341 elog(DEBUG, "aclcheck: found %d/%d",
342 aip->ai_id, aip->ai_mode);
344 return (aip->ai_mode & mode) ? ACLCHECK_OK : ACLCHECK_NO_PRIV;
348 case ACL_IDTYPE_WORLD:
351 elog(ERROR, "aclcheck: bogus ACL id type: %d", idtype);
355 #ifdef ACLDEBUG_TRACE
356 elog(DEBUG, "aclcheck: using world=%d", aidat->ai_mode);
358 return (aidat->ai_mode & mode) ? ACLCHECK_OK : ACLCHECK_NO_PRIV;
362 pg_aclcheck(char *relname, char *usename, AclMode mode)
366 Acl *acl = (Acl *) NULL,
371 tuple = SearchSysCacheTuple(USENAME,
372 PointerGetDatum(usename),
374 if (!HeapTupleIsValid(tuple))
375 elog(ERROR, "pg_aclcheck: user \"%s\" not found",
377 id = (AclId) ((Form_pg_shadow) GETSTRUCT(tuple))->usesysid;
380 * for the 'pg_database' relation, check the usecreatedb field before
381 * checking normal permissions
383 if (strcmp(DatabaseRelationName, relname) == 0 &&
384 (((Form_pg_shadow) GETSTRUCT(tuple))->usecreatedb))
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
392 if ((mode & ACL_WR) || (mode & ACL_AP))
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.)
401 if (((mode & ACL_WR) || (mode & ACL_AP)) &&
402 !allowSystemTableMods && IsSystemRelationName(relname) &&
403 !((Form_pg_shadow) GETSTRUCT(tuple))->usecatupd)
405 elog(DEBUG, "pg_aclcheck: catalog update to \"%s\": permission denied",
407 return ACLCHECK_NO_PRIV;
411 * Otherwise, superusers bypass all permission-checking.
413 if (((Form_pg_shadow) GETSTRUCT(tuple))->usesuper)
415 #ifdef ACLDEBUG_TRACE
416 elog(DEBUG, "pg_aclcheck: \"%s\" is superuser",
423 tuple = SearchSysCacheTuple(RELNAME,
424 PointerGetDatum(relname),
426 if (!HeapTupleIsValid(tuple))
428 elog(ERROR, "pg_aclcheck: class \"%s\" not found",
430 /* an elog(ERROR) kills us, so no need to return anything. */
432 if (!heap_attisnull(tuple, Anum_pg_class_relacl))
434 relation = heap_openr(RelationRelationName);
435 tmp = (Acl *) heap_getattr(tuple,
436 Anum_pg_class_relacl,
437 RelationGetDescr(relation),
439 acl = makeacl(ACL_NUM(tmp));
440 memmove((char *) acl, (char *) tmp, ACL_SIZE(tmp));
441 heap_close(relation);
447 * if the acl is null, by default the owner can do whatever he
452 relation = heap_openr(RelationRelationName);
453 ownerId = (int4) heap_getattr(tuple,
454 Anum_pg_class_relowner,
455 RelationGetDescr(relation),
457 acl = aclownerdefault(relname, (AclId) ownerId);
460 { /* This is why the syscache is great... */
461 static ScanKeyData relkey[1] = {
462 {0, Anum_pg_class_relname, F_NAMEEQ}
465 relation = heap_openr(RelationRelationName);
466 if (!RelationIsValid(relation))
468 elog(NOTICE, "pg_checkacl: could not open \"%-.*s\"??",
469 RelationRelationName);
470 return ACLCHECK_NO_CLASS;
472 tuple = SearchSysCacheTuple(RELNAME,
473 PointerGetDatum(relname),
475 if (HeapTupleIsValid(tuple) &&
476 !heap_attisnull(tuple, Anum_pg_class_relacl))
478 tmp = (Acl *) heap_getattr(tuple,
479 Anum_pg_class_relacl,
480 RelationGetDescr(relation),
482 acl = makeacl(ACL_NUM(tmp));
483 memmove((char *) acl, (char *) tmp, ACL_SIZE(tmp));
485 heap_close(relation);
488 result = aclcheck(relname, acl, id, (AclIdType) ACL_IDTYPE_UID, mode);
495 pg_ownercheck(char *usename,
503 tuple = SearchSysCacheTuple(USENAME,
504 PointerGetDatum(usename),
506 if (!HeapTupleIsValid(tuple))
507 elog(ERROR, "pg_ownercheck: user \"%s\" not found",
509 user_id = (AclId) ((Form_pg_shadow) GETSTRUCT(tuple))->usesysid;
512 * Superusers bypass all permission-checking.
514 if (((Form_pg_shadow) GETSTRUCT(tuple))->usesuper)
516 #ifdef ACLDEBUG_TRACE
517 elog(DEBUG, "pg_ownercheck: user \"%s\" is superuser",
523 tuple = SearchSysCacheTuple(cacheid, PointerGetDatum(value),
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;
534 if (!HeapTupleIsValid(tuple))
535 elog(ERROR, "pg_ownercheck: function \"%s\" not found",
537 owner_id = ((Form_pg_proc) GETSTRUCT(tuple))->proowner;
540 if (!HeapTupleIsValid(tuple))
541 elog(ERROR, "pg_ownercheck: class \"%s\" not found",
543 owner_id = ((Form_pg_class) GETSTRUCT(tuple))->relowner;
546 if (!HeapTupleIsValid(tuple))
547 elog(ERROR, "pg_ownercheck: type \"%s\" not found",
549 owner_id = ((Form_pg_type) GETSTRUCT(tuple))->typowner;
552 elog(ERROR, "pg_ownercheck: invalid cache id: %d", cacheid);
556 return user_id == owner_id;
560 pg_func_ownercheck(char *usename,
569 tuple = SearchSysCacheTuple(USENAME,
570 PointerGetDatum(usename),
572 if (!HeapTupleIsValid(tuple))
573 elog(ERROR, "pg_func_ownercheck: user \"%s\" not found",
575 user_id = (AclId) ((Form_pg_shadow) GETSTRUCT(tuple))->usesysid;
578 * Superusers bypass all permission-checking.
580 if (((Form_pg_shadow) GETSTRUCT(tuple))->usesuper)
582 #ifdef ACLDEBUG_TRACE
583 elog(DEBUG, "pg_ownercheck: user \"%s\" is superuser",
589 tuple = SearchSysCacheTuple(PRONAME,
590 PointerGetDatum(funcname),
591 Int32GetDatum(nargs),
592 PointerGetDatum(arglist),
594 if (!HeapTupleIsValid(tuple))
595 func_error("pg_func_ownercheck", funcname, nargs, arglist, NULL);
597 owner_id = ((Form_pg_proc) GETSTRUCT(tuple))->proowner;
599 return user_id == owner_id;
603 pg_aggr_ownercheck(char *usename,
611 tuple = SearchSysCacheTuple(USENAME,
612 PointerGetDatum(usename),
614 if (!HeapTupleIsValid(tuple))
615 elog(ERROR, "pg_aggr_ownercheck: user \"%s\" not found",
617 user_id = (AclId) ((Form_pg_shadow) GETSTRUCT(tuple))->usesysid;
620 * Superusers bypass all permission-checking.
622 if (((Form_pg_shadow) GETSTRUCT(tuple))->usesuper)
624 #ifdef ACLDEBUG_TRACE
625 elog(DEBUG, "pg_aggr_ownercheck: user \"%s\" is superuser",
631 tuple = SearchSysCacheTuple(AGGNAME,
632 PointerGetDatum(aggname),
633 ObjectIdGetDatum(basetypeID),
636 if (!HeapTupleIsValid(tuple))
637 agg_error("pg_aggr_ownercheck", aggname, basetypeID);
639 owner_id = ((Form_pg_aggregate) GETSTRUCT(tuple))->aggowner;
641 return user_id == owner_id;