1 /* -------------------------------------------------------------------------
3 * contrib/sepgsql/relation.c
5 * Routines corresponding to relation/attribute objects
7 * Copyright (c) 2010-2018, PostgreSQL Global Development Group
9 * -------------------------------------------------------------------------
13 #include "access/genam.h"
14 #include "access/heapam.h"
15 #include "access/htup_details.h"
16 #include "access/sysattr.h"
17 #include "catalog/indexing.h"
18 #include "catalog/dependency.h"
19 #include "catalog/pg_attribute.h"
20 #include "catalog/pg_class.h"
21 #include "catalog/pg_namespace.h"
22 #include "commands/seclabel.h"
23 #include "lib/stringinfo.h"
24 #include "utils/builtins.h"
25 #include "utils/fmgroids.h"
26 #include "utils/catcache.h"
27 #include "utils/lsyscache.h"
28 #include "utils/rel.h"
29 #include "utils/syscache.h"
30 #include "utils/tqual.h"
34 static void sepgsql_index_modify(Oid indexOid);
37 * sepgsql_attribute_post_create
39 * This routine assigns a default security label on a newly defined
40 * column, using ALTER TABLE ... ADD COLUMN.
41 * Note that this routine is not invoked in the case of CREATE TABLE,
42 * although it also defines columns in addition to table.
45 sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum)
55 Form_pg_attribute attForm;
56 StringInfoData audit_name;
57 char relkind = get_rel_relkind(relOid);
60 * Only attributes within regular relations or partition relations have
61 * individual security labels.
63 if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
67 * Compute a default security label of the new column underlying the
68 * specified relation, and check permission to create it.
70 rel = heap_open(AttributeRelationId, AccessShareLock);
73 Anum_pg_attribute_attrelid,
74 BTEqualStrategyNumber, F_OIDEQ,
75 ObjectIdGetDatum(relOid));
77 Anum_pg_attribute_attnum,
78 BTEqualStrategyNumber, F_INT2EQ,
79 Int16GetDatum(attnum));
81 sscan = systable_beginscan(rel, AttributeRelidNumIndexId, true,
82 SnapshotSelf, 2, &skey[0]);
84 tuple = systable_getnext(sscan);
85 if (!HeapTupleIsValid(tuple))
86 elog(ERROR, "could not find tuple for column %d of relation %u",
89 attForm = (Form_pg_attribute) GETSTRUCT(tuple);
91 scontext = sepgsql_get_client_label();
92 tcontext = sepgsql_get_label(RelationRelationId, relOid, 0);
93 ncontext = sepgsql_compute_create(scontext, tcontext,
95 NameStr(attForm->attname));
98 * check db_column:{create} permission
100 object.classId = RelationRelationId;
101 object.objectId = relOid;
102 object.objectSubId = 0;
104 initStringInfo(&audit_name);
105 appendStringInfo(&audit_name, "%s.%s",
106 getObjectIdentity(&object),
107 quote_identifier(NameStr(attForm->attname)));
108 sepgsql_avc_check_perms_label(ncontext,
109 SEPG_CLASS_DB_COLUMN,
110 SEPG_DB_COLUMN__CREATE,
115 * Assign the default security label on a new procedure
117 object.classId = RelationRelationId;
118 object.objectId = relOid;
119 object.objectSubId = attnum;
120 SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext);
122 systable_endscan(sscan);
123 heap_close(rel, AccessShareLock);
130 * sepgsql_attribute_drop
132 * It checks privileges to drop the supplied column.
135 sepgsql_attribute_drop(Oid relOid, AttrNumber attnum)
137 ObjectAddress object;
139 char relkind = get_rel_relkind(relOid);
141 if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
145 * check db_column:{drop} permission
147 object.classId = RelationRelationId;
148 object.objectId = relOid;
149 object.objectSubId = attnum;
150 audit_name = getObjectIdentity(&object);
152 sepgsql_avc_check_perms(&object,
153 SEPG_CLASS_DB_COLUMN,
154 SEPG_DB_COLUMN__DROP,
161 * sepgsql_attribute_relabel
163 * It checks privileges to relabel the supplied column
167 sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum,
168 const char *seclabel)
170 ObjectAddress object;
172 char relkind = get_rel_relkind(relOid);
174 if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
176 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
177 errmsg("cannot set security label on non-regular columns")));
179 object.classId = RelationRelationId;
180 object.objectId = relOid;
181 object.objectSubId = attnum;
182 audit_name = getObjectIdentity(&object);
185 * check db_column:{setattr relabelfrom} permission
187 sepgsql_avc_check_perms(&object,
188 SEPG_CLASS_DB_COLUMN,
189 SEPG_DB_COLUMN__SETATTR |
190 SEPG_DB_COLUMN__RELABELFROM,
195 * check db_column:{relabelto} permission
197 sepgsql_avc_check_perms_label(seclabel,
198 SEPG_CLASS_DB_COLUMN,
199 SEPG_DB_PROCEDURE__RELABELTO,
206 * sepgsql_attribute_setattr
208 * It checks privileges to alter the supplied column.
211 sepgsql_attribute_setattr(Oid relOid, AttrNumber attnum)
213 ObjectAddress object;
215 char relkind = get_rel_relkind(relOid);
217 if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
221 * check db_column:{setattr} permission
223 object.classId = RelationRelationId;
224 object.objectId = relOid;
225 object.objectSubId = attnum;
226 audit_name = getObjectIdentity(&object);
228 sepgsql_avc_check_perms(&object,
229 SEPG_CLASS_DB_COLUMN,
230 SEPG_DB_COLUMN__SETATTR,
237 * sepgsql_relation_post_create
239 * The post creation hook of relation/attribute
242 sepgsql_relation_post_create(Oid relOid)
248 Form_pg_class classForm;
249 ObjectAddress object;
251 char *scontext; /* subject */
252 char *tcontext; /* schema */
253 char *rcontext; /* relation */
254 char *ccontext; /* column */
256 StringInfoData audit_name;
259 * Fetch catalog record of the new relation. Because pg_class entry is not
260 * visible right now, we need to scan the catalog using SnapshotSelf.
262 rel = heap_open(RelationRelationId, AccessShareLock);
265 ObjectIdAttributeNumber,
266 BTEqualStrategyNumber, F_OIDEQ,
267 ObjectIdGetDatum(relOid));
269 sscan = systable_beginscan(rel, ClassOidIndexId, true,
270 SnapshotSelf, 1, &skey);
272 tuple = systable_getnext(sscan);
273 if (!HeapTupleIsValid(tuple))
274 elog(ERROR, "could not find tuple for relation %u", relOid);
276 classForm = (Form_pg_class) GETSTRUCT(tuple);
278 /* ignore indexes on toast tables */
279 if (classForm->relkind == RELKIND_INDEX &&
280 classForm->relnamespace == PG_TOAST_NAMESPACE)
284 * check db_schema:{add_name} permission of the namespace
286 object.classId = NamespaceRelationId;
287 object.objectId = classForm->relnamespace;
288 object.objectSubId = 0;
289 sepgsql_avc_check_perms(&object,
290 SEPG_CLASS_DB_SCHEMA,
291 SEPG_DB_SCHEMA__ADD_NAME,
292 getObjectIdentity(&object),
295 switch (classForm->relkind)
297 case RELKIND_RELATION:
298 case RELKIND_PARTITIONED_TABLE:
299 tclass = SEPG_CLASS_DB_TABLE;
301 case RELKIND_SEQUENCE:
302 tclass = SEPG_CLASS_DB_SEQUENCE;
305 tclass = SEPG_CLASS_DB_VIEW;
308 /* deal with indexes specially; no need for tclass */
309 sepgsql_index_modify(relOid);
312 /* ignore other relkinds */
317 * Compute a default security label when we create a new relation object
318 * under the specified namespace.
320 scontext = sepgsql_get_client_label();
321 tcontext = sepgsql_get_label(NamespaceRelationId,
322 classForm->relnamespace, 0);
323 rcontext = sepgsql_compute_create(scontext, tcontext, tclass,
324 NameStr(classForm->relname));
327 * check db_xxx:{create} permission
329 nsp_name = get_namespace_name(classForm->relnamespace);
330 initStringInfo(&audit_name);
331 appendStringInfo(&audit_name, "%s.%s",
332 quote_identifier(nsp_name),
333 quote_identifier(NameStr(classForm->relname)));
334 sepgsql_avc_check_perms_label(rcontext,
336 SEPG_DB_DATABASE__CREATE,
341 * Assign the default security label on the new regular or partitioned
344 object.classId = RelationRelationId;
345 object.objectId = relOid;
346 object.objectSubId = 0;
347 SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, rcontext);
350 * We also assign a default security label on columns of a new table.
352 if (classForm->relkind == RELKIND_RELATION ||
353 classForm->relkind == RELKIND_PARTITIONED_TABLE)
359 Form_pg_attribute attForm;
361 arel = heap_open(AttributeRelationId, AccessShareLock);
364 Anum_pg_attribute_attrelid,
365 BTEqualStrategyNumber, F_OIDEQ,
366 ObjectIdGetDatum(relOid));
368 ascan = systable_beginscan(arel, AttributeRelidNumIndexId, true,
369 SnapshotSelf, 1, &akey);
371 while (HeapTupleIsValid(atup = systable_getnext(ascan)))
373 attForm = (Form_pg_attribute) GETSTRUCT(atup);
375 resetStringInfo(&audit_name);
376 appendStringInfo(&audit_name, "%s.%s.%s",
377 quote_identifier(nsp_name),
378 quote_identifier(NameStr(classForm->relname)),
379 quote_identifier(NameStr(attForm->attname)));
381 ccontext = sepgsql_compute_create(scontext,
383 SEPG_CLASS_DB_COLUMN,
384 NameStr(attForm->attname));
387 * check db_column:{create} permission
389 sepgsql_avc_check_perms_label(ccontext,
390 SEPG_CLASS_DB_COLUMN,
391 SEPG_DB_COLUMN__CREATE,
395 object.classId = RelationRelationId;
396 object.objectId = relOid;
397 object.objectSubId = attForm->attnum;
398 SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ccontext);
402 systable_endscan(ascan);
403 heap_close(arel, AccessShareLock);
408 systable_endscan(sscan);
409 heap_close(rel, AccessShareLock);
413 * sepgsql_relation_drop
415 * It checks privileges to drop the supplied relation.
418 sepgsql_relation_drop(Oid relOid)
420 ObjectAddress object;
423 char relkind = get_rel_relkind(relOid);
427 case RELKIND_RELATION:
428 case RELKIND_PARTITIONED_TABLE:
429 tclass = SEPG_CLASS_DB_TABLE;
431 case RELKIND_SEQUENCE:
432 tclass = SEPG_CLASS_DB_SEQUENCE;
435 tclass = SEPG_CLASS_DB_VIEW;
438 /* ignore indexes on toast tables */
439 if (get_rel_namespace(relOid) == PG_TOAST_NAMESPACE)
441 /* other indexes are handled specially below; no need for tclass */
444 /* ignore other relkinds */
449 * check db_schema:{remove_name} permission
451 object.classId = NamespaceRelationId;
452 object.objectId = get_rel_namespace(relOid);
453 object.objectSubId = 0;
454 audit_name = getObjectIdentity(&object);
456 sepgsql_avc_check_perms(&object,
457 SEPG_CLASS_DB_SCHEMA,
458 SEPG_DB_SCHEMA__REMOVE_NAME,
463 /* deal with indexes specially */
464 if (relkind == RELKIND_INDEX)
466 sepgsql_index_modify(relOid);
471 * check db_table/sequence/view:{drop} permission
473 object.classId = RelationRelationId;
474 object.objectId = relOid;
475 object.objectSubId = 0;
476 audit_name = getObjectIdentity(&object);
478 sepgsql_avc_check_perms(&object,
486 * check db_column:{drop} permission
488 if (relkind == RELKIND_RELATION || relkind == RELKIND_PARTITIONED_TABLE)
490 Form_pg_attribute attForm;
495 attrList = SearchSysCacheList1(ATTNUM, ObjectIdGetDatum(relOid));
496 for (i = 0; i < attrList->n_members; i++)
498 atttup = &attrList->members[i]->tuple;
499 attForm = (Form_pg_attribute) GETSTRUCT(atttup);
501 if (attForm->attisdropped)
504 object.classId = RelationRelationId;
505 object.objectId = relOid;
506 object.objectSubId = attForm->attnum;
507 audit_name = getObjectIdentity(&object);
509 sepgsql_avc_check_perms(&object,
510 SEPG_CLASS_DB_COLUMN,
511 SEPG_DB_COLUMN__DROP,
516 ReleaseCatCacheList(attrList);
521 * sepgsql_relation_relabel
523 * It checks privileges to relabel the supplied relation by the `seclabel'.
526 sepgsql_relation_relabel(Oid relOid, const char *seclabel)
528 ObjectAddress object;
530 char relkind = get_rel_relkind(relOid);
533 if (relkind == RELKIND_RELATION || relkind == RELKIND_PARTITIONED_TABLE)
534 tclass = SEPG_CLASS_DB_TABLE;
535 else if (relkind == RELKIND_SEQUENCE)
536 tclass = SEPG_CLASS_DB_SEQUENCE;
537 else if (relkind == RELKIND_VIEW)
538 tclass = SEPG_CLASS_DB_VIEW;
541 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
542 errmsg("cannot set security labels on relations except "
543 "for tables, sequences or views")));
545 object.classId = RelationRelationId;
546 object.objectId = relOid;
547 object.objectSubId = 0;
548 audit_name = getObjectIdentity(&object);
551 * check db_xxx:{setattr relabelfrom} permission
553 sepgsql_avc_check_perms(&object,
555 SEPG_DB_TABLE__SETATTR |
556 SEPG_DB_TABLE__RELABELFROM,
561 * check db_xxx:{relabelto} permission
563 sepgsql_avc_check_perms_label(seclabel,
565 SEPG_DB_TABLE__RELABELTO,
572 * sepgsql_relation_setattr
574 * It checks privileges to set attribute of the supplied relation
577 sepgsql_relation_setattr(Oid relOid)
584 Form_pg_class oldform;
585 Form_pg_class newform;
586 ObjectAddress object;
590 switch (get_rel_relkind(relOid))
592 case RELKIND_RELATION:
593 case RELKIND_PARTITIONED_TABLE:
594 tclass = SEPG_CLASS_DB_TABLE;
596 case RELKIND_SEQUENCE:
597 tclass = SEPG_CLASS_DB_SEQUENCE;
600 tclass = SEPG_CLASS_DB_VIEW;
603 /* deal with indexes specially */
604 sepgsql_index_modify(relOid);
607 /* other relkinds don't need additional work */
612 * Fetch newer catalog
614 rel = heap_open(RelationRelationId, AccessShareLock);
617 ObjectIdAttributeNumber,
618 BTEqualStrategyNumber, F_OIDEQ,
619 ObjectIdGetDatum(relOid));
621 sscan = systable_beginscan(rel, ClassOidIndexId, true,
622 SnapshotSelf, 1, &skey);
624 newtup = systable_getnext(sscan);
625 if (!HeapTupleIsValid(newtup))
626 elog(ERROR, "could not find tuple for relation %u", relOid);
627 newform = (Form_pg_class) GETSTRUCT(newtup);
630 * Fetch older catalog
632 oldtup = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
633 if (!HeapTupleIsValid(oldtup))
634 elog(ERROR, "cache lookup failed for relation %u", relOid);
635 oldform = (Form_pg_class) GETSTRUCT(oldtup);
638 * Does this ALTER command takes operation to namespace?
640 if (newform->relnamespace != oldform->relnamespace)
642 sepgsql_schema_remove_name(oldform->relnamespace);
643 sepgsql_schema_add_name(newform->relnamespace);
645 if (strcmp(NameStr(newform->relname), NameStr(oldform->relname)) != 0)
646 sepgsql_schema_rename(oldform->relnamespace);
649 * XXX - In the future version, db_tuple:{use} of system catalog entry
650 * shall be checked, if tablespace configuration is changed.
654 * check db_xxx:{setattr} permission
656 object.classId = RelationRelationId;
657 object.objectId = relOid;
658 object.objectSubId = 0;
659 audit_name = getObjectIdentity(&object);
661 sepgsql_avc_check_perms(&object,
663 SEPG_DB_TABLE__SETATTR,
668 ReleaseSysCache(oldtup);
669 systable_endscan(sscan);
670 heap_close(rel, AccessShareLock);
674 * sepgsql_relation_setattr_extra
676 * It checks permission of the relation being referenced by extra attributes,
677 * such as pg_index entries. Like core PostgreSQL, sepgsql also does not deal
678 * with such entries as individual "objects", thus, modification of these
679 * entries shall be considered as setting an attribute of the underlying
683 sepgsql_relation_setattr_extra(Relation catalog,
686 AttrNumber anum_relation_id,
687 AttrNumber anum_extra_id)
695 ScanKeyInit(&skey, anum_extra_id,
696 BTEqualStrategyNumber, F_OIDEQ,
697 ObjectIdGetDatum(extra_oid));
699 sscan = systable_beginscan(catalog, catindex_id, true,
700 SnapshotSelf, 1, &skey);
701 tuple = systable_getnext(sscan);
702 if (!HeapTupleIsValid(tuple))
703 elog(ERROR, "could not find tuple for object %u in catalog \"%s\"",
704 extra_oid, RelationGetRelationName(catalog));
706 datum = heap_getattr(tuple, anum_relation_id,
707 RelationGetDescr(catalog), &isnull);
710 sepgsql_relation_setattr(DatumGetObjectId(datum));
712 systable_endscan(sscan);
716 * sepgsql_index_modify
717 * Handle index create, update, drop
719 * Unlike other relation kinds, indexes do not have their own security labels,
720 * so instead of doing checks directly, treat them as extra attributes of their
721 * owning tables; so check 'setattr' permissions on the table.
724 sepgsql_index_modify(Oid indexOid)
726 Relation catalog = heap_open(IndexRelationId, AccessShareLock);
728 /* check db_table:{setattr} permission of the table being indexed */
729 sepgsql_relation_setattr_extra(catalog,
732 Anum_pg_index_indrelid,
733 Anum_pg_index_indexrelid);
734 heap_close(catalog, AccessShareLock);