1 /* -------------------------------------------------------------------------
2  *
3  * contrib/sepgsql/relation.c
4  *
5  * Routines corresponding to relation/attribute objects
6  *
7  * Copyright (c) 2010-2021, PostgreSQL Global Development Group
8  *
9  * -------------------------------------------------------------------------
10  */
11 #include "postgres.h"
12 
13 #include "access/genam.h"
14 #include "access/htup_details.h"
15 #include "access/sysattr.h"
16 #include "access/table.h"
17 #include "catalog/dependency.h"
18 #include "catalog/pg_attribute.h"
19 #include "catalog/pg_class.h"
20 #include "catalog/pg_namespace.h"
21 #include "commands/seclabel.h"
22 #include "lib/stringinfo.h"
23 #include "sepgsql.h"
24 #include "utils/builtins.h"
25 #include "utils/catcache.h"
26 #include "utils/fmgroids.h"
27 #include "utils/lsyscache.h"
28 #include "utils/rel.h"
29 #include "utils/snapmgr.h"
30 #include "utils/syscache.h"
31 
32 static void sepgsql_index_modify(Oid indexOid);
33 
34 /*
35  * sepgsql_attribute_post_create
36  *
37  * This routine assigns a default security label on a newly defined
38  * column, using ALTER TABLE ... ADD COLUMN.
39  * Note that this routine is not invoked in the case of CREATE TABLE,
40  * although it also defines columns in addition to table.
41  */
42 void
sepgsql_attribute_post_create(Oid relOid,AttrNumber attnum)43 sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum)
44 {
45 	Relation	rel;
46 	ScanKeyData skey[2];
47 	SysScanDesc sscan;
48 	HeapTuple	tuple;
49 	char	   *scontext;
50 	char	   *tcontext;
51 	char	   *ncontext;
52 	ObjectAddress object;
53 	Form_pg_attribute attForm;
54 	StringInfoData audit_name;
55 	char		relkind = get_rel_relkind(relOid);
56 
57 	/*
58 	 * Only attributes within regular relations or partition relations have
59 	 * individual security labels.
60 	 */
61 	if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
62 		return;
63 
64 	/*
65 	 * Compute a default security label of the new column underlying the
66 	 * specified relation, and check permission to create it.
67 	 */
68 	rel = table_open(AttributeRelationId, AccessShareLock);
69 
70 	ScanKeyInit(&skey[0],
71 				Anum_pg_attribute_attrelid,
72 				BTEqualStrategyNumber, F_OIDEQ,
73 				ObjectIdGetDatum(relOid));
74 	ScanKeyInit(&skey[1],
75 				Anum_pg_attribute_attnum,
76 				BTEqualStrategyNumber, F_INT2EQ,
77 				Int16GetDatum(attnum));
78 
79 	sscan = systable_beginscan(rel, AttributeRelidNumIndexId, true,
80 							   SnapshotSelf, 2, &skey[0]);
81 
82 	tuple = systable_getnext(sscan);
83 	if (!HeapTupleIsValid(tuple))
84 		elog(ERROR, "could not find tuple for column %d of relation %u",
85 			 attnum, relOid);
86 
87 	attForm = (Form_pg_attribute) GETSTRUCT(tuple);
88 
89 	scontext = sepgsql_get_client_label();
90 	tcontext = sepgsql_get_label(RelationRelationId, relOid, 0);
91 	ncontext = sepgsql_compute_create(scontext, tcontext,
92 									  SEPG_CLASS_DB_COLUMN,
93 									  NameStr(attForm->attname));
94 
95 	/*
96 	 * check db_column:{create} permission
97 	 */
98 	object.classId = RelationRelationId;
99 	object.objectId = relOid;
100 	object.objectSubId = 0;
101 
102 	initStringInfo(&audit_name);
103 	appendStringInfo(&audit_name, "%s.%s",
104 					 getObjectIdentity(&object, false),
105 					 quote_identifier(NameStr(attForm->attname)));
106 	sepgsql_avc_check_perms_label(ncontext,
107 								  SEPG_CLASS_DB_COLUMN,
108 								  SEPG_DB_COLUMN__CREATE,
109 								  audit_name.data,
110 								  true);
111 
112 	/*
113 	 * Assign the default security label on a new procedure
114 	 */
115 	object.classId = RelationRelationId;
116 	object.objectId = relOid;
117 	object.objectSubId = attnum;
118 	SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext);
119 
120 	systable_endscan(sscan);
121 	table_close(rel, AccessShareLock);
122 
123 	pfree(tcontext);
124 	pfree(ncontext);
125 }
126 
127 /*
128  * sepgsql_attribute_drop
129  *
130  * It checks privileges to drop the supplied column.
131  */
132 void
sepgsql_attribute_drop(Oid relOid,AttrNumber attnum)133 sepgsql_attribute_drop(Oid relOid, AttrNumber attnum)
134 {
135 	ObjectAddress object;
136 	char	   *audit_name;
137 	char		relkind = get_rel_relkind(relOid);
138 
139 	if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
140 		return;
141 
142 	/*
143 	 * check db_column:{drop} permission
144 	 */
145 	object.classId = RelationRelationId;
146 	object.objectId = relOid;
147 	object.objectSubId = attnum;
148 	audit_name = getObjectIdentity(&object, false);
149 
150 	sepgsql_avc_check_perms(&object,
151 							SEPG_CLASS_DB_COLUMN,
152 							SEPG_DB_COLUMN__DROP,
153 							audit_name,
154 							true);
155 	pfree(audit_name);
156 }
157 
158 /*
159  * sepgsql_attribute_relabel
160  *
161  * It checks privileges to relabel the supplied column
162  * by the `seclabel'.
163  */
164 void
sepgsql_attribute_relabel(Oid relOid,AttrNumber attnum,const char * seclabel)165 sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum,
166 						  const char *seclabel)
167 {
168 	ObjectAddress object;
169 	char	   *audit_name;
170 	char		relkind = get_rel_relkind(relOid);
171 
172 	if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
173 		ereport(ERROR,
174 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
175 				 errmsg("cannot set security label on non-regular columns")));
176 
177 	object.classId = RelationRelationId;
178 	object.objectId = relOid;
179 	object.objectSubId = attnum;
180 	audit_name = getObjectIdentity(&object, false);
181 
182 	/*
183 	 * check db_column:{setattr relabelfrom} permission
184 	 */
185 	sepgsql_avc_check_perms(&object,
186 							SEPG_CLASS_DB_COLUMN,
187 							SEPG_DB_COLUMN__SETATTR |
188 							SEPG_DB_COLUMN__RELABELFROM,
189 							audit_name,
190 							true);
191 
192 	/*
193 	 * check db_column:{relabelto} permission
194 	 */
195 	sepgsql_avc_check_perms_label(seclabel,
196 								  SEPG_CLASS_DB_COLUMN,
197 								  SEPG_DB_PROCEDURE__RELABELTO,
198 								  audit_name,
199 								  true);
200 	pfree(audit_name);
201 }
202 
203 /*
204  * sepgsql_attribute_setattr
205  *
206  * It checks privileges to alter the supplied column.
207  */
208 void
sepgsql_attribute_setattr(Oid relOid,AttrNumber attnum)209 sepgsql_attribute_setattr(Oid relOid, AttrNumber attnum)
210 {
211 	ObjectAddress object;
212 	char	   *audit_name;
213 	char		relkind = get_rel_relkind(relOid);
214 
215 	if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
216 		return;
217 
218 	/*
219 	 * check db_column:{setattr} permission
220 	 */
221 	object.classId = RelationRelationId;
222 	object.objectId = relOid;
223 	object.objectSubId = attnum;
224 	audit_name = getObjectIdentity(&object, false);
225 
226 	sepgsql_avc_check_perms(&object,
227 							SEPG_CLASS_DB_COLUMN,
228 							SEPG_DB_COLUMN__SETATTR,
229 							audit_name,
230 							true);
231 	pfree(audit_name);
232 }
233 
234 /*
235  * sepgsql_relation_post_create
236  *
237  * The post creation hook of relation/attribute
238  */
239 void
sepgsql_relation_post_create(Oid relOid)240 sepgsql_relation_post_create(Oid relOid)
241 {
242 	Relation	rel;
243 	ScanKeyData skey;
244 	SysScanDesc sscan;
245 	HeapTuple	tuple;
246 	Form_pg_class classForm;
247 	ObjectAddress object;
248 	uint16_t	tclass;
249 	char	   *scontext;		/* subject */
250 	char	   *tcontext;		/* schema */
251 	char	   *rcontext;		/* relation */
252 	char	   *ccontext;		/* column */
253 	char	   *nsp_name;
254 	StringInfoData audit_name;
255 
256 	/*
257 	 * Fetch catalog record of the new relation. Because pg_class entry is not
258 	 * visible right now, we need to scan the catalog using SnapshotSelf.
259 	 */
260 	rel = table_open(RelationRelationId, AccessShareLock);
261 
262 	ScanKeyInit(&skey,
263 				Anum_pg_class_oid,
264 				BTEqualStrategyNumber, F_OIDEQ,
265 				ObjectIdGetDatum(relOid));
266 
267 	sscan = systable_beginscan(rel, ClassOidIndexId, true,
268 							   SnapshotSelf, 1, &skey);
269 
270 	tuple = systable_getnext(sscan);
271 	if (!HeapTupleIsValid(tuple))
272 		elog(ERROR, "could not find tuple for relation %u", relOid);
273 
274 	classForm = (Form_pg_class) GETSTRUCT(tuple);
275 
276 	/* ignore indexes on toast tables */
277 	if (classForm->relkind == RELKIND_INDEX &&
278 		classForm->relnamespace == PG_TOAST_NAMESPACE)
279 		goto out;
280 
281 	/*
282 	 * check db_schema:{add_name} permission of the namespace
283 	 */
284 	object.classId = NamespaceRelationId;
285 	object.objectId = classForm->relnamespace;
286 	object.objectSubId = 0;
287 	sepgsql_avc_check_perms(&object,
288 							SEPG_CLASS_DB_SCHEMA,
289 							SEPG_DB_SCHEMA__ADD_NAME,
290 							getObjectIdentity(&object, false),
291 							true);
292 
293 	switch (classForm->relkind)
294 	{
295 		case RELKIND_RELATION:
296 		case RELKIND_PARTITIONED_TABLE:
297 			tclass = SEPG_CLASS_DB_TABLE;
298 			break;
299 		case RELKIND_SEQUENCE:
300 			tclass = SEPG_CLASS_DB_SEQUENCE;
301 			break;
302 		case RELKIND_VIEW:
303 			tclass = SEPG_CLASS_DB_VIEW;
304 			break;
305 		case RELKIND_INDEX:
306 			/* deal with indexes specially; no need for tclass */
307 			sepgsql_index_modify(relOid);
308 			goto out;
309 		default:
310 			/* ignore other relkinds */
311 			goto out;
312 	}
313 
314 	/*
315 	 * Compute a default security label when we create a new relation object
316 	 * under the specified namespace.
317 	 */
318 	scontext = sepgsql_get_client_label();
319 	tcontext = sepgsql_get_label(NamespaceRelationId,
320 								 classForm->relnamespace, 0);
321 	rcontext = sepgsql_compute_create(scontext, tcontext, tclass,
322 									  NameStr(classForm->relname));
323 
324 	/*
325 	 * check db_xxx:{create} permission
326 	 */
327 	nsp_name = get_namespace_name(classForm->relnamespace);
328 	initStringInfo(&audit_name);
329 	appendStringInfo(&audit_name, "%s.%s",
330 					 quote_identifier(nsp_name),
331 					 quote_identifier(NameStr(classForm->relname)));
332 	sepgsql_avc_check_perms_label(rcontext,
333 								  tclass,
334 								  SEPG_DB_DATABASE__CREATE,
335 								  audit_name.data,
336 								  true);
337 
338 	/*
339 	 * Assign the default security label on the new regular or partitioned
340 	 * relation.
341 	 */
342 	object.classId = RelationRelationId;
343 	object.objectId = relOid;
344 	object.objectSubId = 0;
345 	SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, rcontext);
346 
347 	/*
348 	 * We also assign a default security label on columns of a new table.
349 	 */
350 	if (classForm->relkind == RELKIND_RELATION ||
351 		classForm->relkind == RELKIND_PARTITIONED_TABLE)
352 	{
353 		Relation	arel;
354 		ScanKeyData akey;
355 		SysScanDesc ascan;
356 		HeapTuple	atup;
357 		Form_pg_attribute attForm;
358 
359 		arel = table_open(AttributeRelationId, AccessShareLock);
360 
361 		ScanKeyInit(&akey,
362 					Anum_pg_attribute_attrelid,
363 					BTEqualStrategyNumber, F_OIDEQ,
364 					ObjectIdGetDatum(relOid));
365 
366 		ascan = systable_beginscan(arel, AttributeRelidNumIndexId, true,
367 								   SnapshotSelf, 1, &akey);
368 
369 		while (HeapTupleIsValid(atup = systable_getnext(ascan)))
370 		{
371 			attForm = (Form_pg_attribute) GETSTRUCT(atup);
372 
373 			resetStringInfo(&audit_name);
374 			appendStringInfo(&audit_name, "%s.%s.%s",
375 							 quote_identifier(nsp_name),
376 							 quote_identifier(NameStr(classForm->relname)),
377 							 quote_identifier(NameStr(attForm->attname)));
378 
379 			ccontext = sepgsql_compute_create(scontext,
380 											  rcontext,
381 											  SEPG_CLASS_DB_COLUMN,
382 											  NameStr(attForm->attname));
383 
384 			/*
385 			 * check db_column:{create} permission
386 			 */
387 			sepgsql_avc_check_perms_label(ccontext,
388 										  SEPG_CLASS_DB_COLUMN,
389 										  SEPG_DB_COLUMN__CREATE,
390 										  audit_name.data,
391 										  true);
392 
393 			object.classId = RelationRelationId;
394 			object.objectId = relOid;
395 			object.objectSubId = attForm->attnum;
396 			SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ccontext);
397 
398 			pfree(ccontext);
399 		}
400 		systable_endscan(ascan);
401 		table_close(arel, AccessShareLock);
402 	}
403 	pfree(rcontext);
404 
405 out:
406 	systable_endscan(sscan);
407 	table_close(rel, AccessShareLock);
408 }
409 
410 /*
411  * sepgsql_relation_drop
412  *
413  * It checks privileges to drop the supplied relation.
414  */
415 void
sepgsql_relation_drop(Oid relOid)416 sepgsql_relation_drop(Oid relOid)
417 {
418 	ObjectAddress object;
419 	char	   *audit_name;
420 	uint16_t	tclass = 0;
421 	char		relkind = get_rel_relkind(relOid);
422 
423 	switch (relkind)
424 	{
425 		case RELKIND_RELATION:
426 		case RELKIND_PARTITIONED_TABLE:
427 			tclass = SEPG_CLASS_DB_TABLE;
428 			break;
429 		case RELKIND_SEQUENCE:
430 			tclass = SEPG_CLASS_DB_SEQUENCE;
431 			break;
432 		case RELKIND_VIEW:
433 			tclass = SEPG_CLASS_DB_VIEW;
434 			break;
435 		case RELKIND_INDEX:
436 			/* ignore indexes on toast tables */
437 			if (get_rel_namespace(relOid) == PG_TOAST_NAMESPACE)
438 				return;
439 			/* other indexes are handled specially below; no need for tclass */
440 			break;
441 		default:
442 			/* ignore other relkinds */
443 			return;
444 	}
445 
446 	/*
447 	 * check db_schema:{remove_name} permission
448 	 */
449 	object.classId = NamespaceRelationId;
450 	object.objectId = get_rel_namespace(relOid);
451 	object.objectSubId = 0;
452 	audit_name = getObjectIdentity(&object, false);
453 
454 	sepgsql_avc_check_perms(&object,
455 							SEPG_CLASS_DB_SCHEMA,
456 							SEPG_DB_SCHEMA__REMOVE_NAME,
457 							audit_name,
458 							true);
459 	pfree(audit_name);
460 
461 	/* deal with indexes specially */
462 	if (relkind == RELKIND_INDEX)
463 	{
464 		sepgsql_index_modify(relOid);
465 		return;
466 	}
467 
468 	/*
469 	 * check db_table/sequence/view:{drop} permission
470 	 */
471 	object.classId = RelationRelationId;
472 	object.objectId = relOid;
473 	object.objectSubId = 0;
474 	audit_name = getObjectIdentity(&object, false);
475 
476 	sepgsql_avc_check_perms(&object,
477 							tclass,
478 							SEPG_DB_TABLE__DROP,
479 							audit_name,
480 							true);
481 	pfree(audit_name);
482 
483 	/*
484 	 * check db_column:{drop} permission
485 	 */
486 	if (relkind == RELKIND_RELATION || relkind == RELKIND_PARTITIONED_TABLE)
487 	{
488 		Form_pg_attribute attForm;
489 		CatCList   *attrList;
490 		HeapTuple	atttup;
491 		int			i;
492 
493 		attrList = SearchSysCacheList1(ATTNUM, ObjectIdGetDatum(relOid));
494 		for (i = 0; i < attrList->n_members; i++)
495 		{
496 			atttup = &attrList->members[i]->tuple;
497 			attForm = (Form_pg_attribute) GETSTRUCT(atttup);
498 
499 			if (attForm->attisdropped)
500 				continue;
501 
502 			object.classId = RelationRelationId;
503 			object.objectId = relOid;
504 			object.objectSubId = attForm->attnum;
505 			audit_name = getObjectIdentity(&object, false);
506 
507 			sepgsql_avc_check_perms(&object,
508 									SEPG_CLASS_DB_COLUMN,
509 									SEPG_DB_COLUMN__DROP,
510 									audit_name,
511 									true);
512 			pfree(audit_name);
513 		}
514 		ReleaseCatCacheList(attrList);
515 	}
516 }
517 
518 /*
519  * sepgsql_relation_truncate
520  *
521  * Check privileges to TRUNCATE the supplied relation.
522  */
523 void
sepgsql_relation_truncate(Oid relOid)524 sepgsql_relation_truncate(Oid relOid)
525 {
526 	ObjectAddress object;
527 	char	   *audit_name;
528 	uint16_t	tclass = 0;
529 	char		relkind = get_rel_relkind(relOid);
530 
531 	switch (relkind)
532 	{
533 		case RELKIND_RELATION:
534 		case RELKIND_PARTITIONED_TABLE:
535 			tclass = SEPG_CLASS_DB_TABLE;
536 			break;
537 		default:
538 			/* ignore other relkinds */
539 			return;
540 	}
541 
542 	/*
543 	 * check db_table:{truncate} permission
544 	 */
545 	object.classId = RelationRelationId;
546 	object.objectId = relOid;
547 	object.objectSubId = 0;
548 	audit_name = getObjectIdentity(&object, false);
549 
550 	sepgsql_avc_check_perms(&object,
551 							tclass,
552 							SEPG_DB_TABLE__TRUNCATE,
553 							audit_name,
554 							true);
555 	pfree(audit_name);
556 }
557 
558 /*
559  * sepgsql_relation_relabel
560  *
561  * It checks privileges to relabel the supplied relation by the `seclabel'.
562  */
563 void
sepgsql_relation_relabel(Oid relOid,const char * seclabel)564 sepgsql_relation_relabel(Oid relOid, const char *seclabel)
565 {
566 	ObjectAddress object;
567 	char	   *audit_name;
568 	char		relkind = get_rel_relkind(relOid);
569 	uint16_t	tclass = 0;
570 
571 	if (relkind == RELKIND_RELATION || relkind == RELKIND_PARTITIONED_TABLE)
572 		tclass = SEPG_CLASS_DB_TABLE;
573 	else if (relkind == RELKIND_SEQUENCE)
574 		tclass = SEPG_CLASS_DB_SEQUENCE;
575 	else if (relkind == RELKIND_VIEW)
576 		tclass = SEPG_CLASS_DB_VIEW;
577 	else
578 		ereport(ERROR,
579 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
580 				 errmsg("cannot set security labels on relations except "
581 						"for tables, sequences or views")));
582 
583 	object.classId = RelationRelationId;
584 	object.objectId = relOid;
585 	object.objectSubId = 0;
586 	audit_name = getObjectIdentity(&object, false);
587 
588 	/*
589 	 * check db_xxx:{setattr relabelfrom} permission
590 	 */
591 	sepgsql_avc_check_perms(&object,
592 							tclass,
593 							SEPG_DB_TABLE__SETATTR |
594 							SEPG_DB_TABLE__RELABELFROM,
595 							audit_name,
596 							true);
597 
598 	/*
599 	 * check db_xxx:{relabelto} permission
600 	 */
601 	sepgsql_avc_check_perms_label(seclabel,
602 								  tclass,
603 								  SEPG_DB_TABLE__RELABELTO,
604 								  audit_name,
605 								  true);
606 	pfree(audit_name);
607 }
608 
609 /*
610  * sepgsql_relation_setattr
611  *
612  * It checks privileges to set attribute of the supplied relation
613  */
614 void
sepgsql_relation_setattr(Oid relOid)615 sepgsql_relation_setattr(Oid relOid)
616 {
617 	Relation	rel;
618 	ScanKeyData skey;
619 	SysScanDesc sscan;
620 	HeapTuple	oldtup;
621 	HeapTuple	newtup;
622 	Form_pg_class oldform;
623 	Form_pg_class newform;
624 	ObjectAddress object;
625 	char	   *audit_name;
626 	uint16_t	tclass;
627 
628 	switch (get_rel_relkind(relOid))
629 	{
630 		case RELKIND_RELATION:
631 		case RELKIND_PARTITIONED_TABLE:
632 			tclass = SEPG_CLASS_DB_TABLE;
633 			break;
634 		case RELKIND_SEQUENCE:
635 			tclass = SEPG_CLASS_DB_SEQUENCE;
636 			break;
637 		case RELKIND_VIEW:
638 			tclass = SEPG_CLASS_DB_VIEW;
639 			break;
640 		case RELKIND_INDEX:
641 			/* deal with indexes specially */
642 			sepgsql_index_modify(relOid);
643 			return;
644 		default:
645 			/* other relkinds don't need additional work */
646 			return;
647 	}
648 
649 	/*
650 	 * Fetch newer catalog
651 	 */
652 	rel = table_open(RelationRelationId, AccessShareLock);
653 
654 	ScanKeyInit(&skey,
655 				Anum_pg_class_oid,
656 				BTEqualStrategyNumber, F_OIDEQ,
657 				ObjectIdGetDatum(relOid));
658 
659 	sscan = systable_beginscan(rel, ClassOidIndexId, true,
660 							   SnapshotSelf, 1, &skey);
661 
662 	newtup = systable_getnext(sscan);
663 	if (!HeapTupleIsValid(newtup))
664 		elog(ERROR, "could not find tuple for relation %u", relOid);
665 	newform = (Form_pg_class) GETSTRUCT(newtup);
666 
667 	/*
668 	 * Fetch older catalog
669 	 */
670 	oldtup = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
671 	if (!HeapTupleIsValid(oldtup))
672 		elog(ERROR, "cache lookup failed for relation %u", relOid);
673 	oldform = (Form_pg_class) GETSTRUCT(oldtup);
674 
675 	/*
676 	 * Does this ALTER command takes operation to namespace?
677 	 */
678 	if (newform->relnamespace != oldform->relnamespace)
679 	{
680 		sepgsql_schema_remove_name(oldform->relnamespace);
681 		sepgsql_schema_add_name(newform->relnamespace);
682 	}
683 	if (strcmp(NameStr(newform->relname), NameStr(oldform->relname)) != 0)
684 		sepgsql_schema_rename(oldform->relnamespace);
685 
686 	/*
687 	 * XXX - In the future version, db_tuple:{use} of system catalog entry
688 	 * shall be checked, if tablespace configuration is changed.
689 	 */
690 
691 	/*
692 	 * check db_xxx:{setattr} permission
693 	 */
694 	object.classId = RelationRelationId;
695 	object.objectId = relOid;
696 	object.objectSubId = 0;
697 	audit_name = getObjectIdentity(&object, false);
698 
699 	sepgsql_avc_check_perms(&object,
700 							tclass,
701 							SEPG_DB_TABLE__SETATTR,
702 							audit_name,
703 							true);
704 	pfree(audit_name);
705 
706 	ReleaseSysCache(oldtup);
707 	systable_endscan(sscan);
708 	table_close(rel, AccessShareLock);
709 }
710 
711 /*
712  * sepgsql_relation_setattr_extra
713  *
714  * It checks permission of the relation being referenced by extra attributes,
715  * such as pg_index entries. Like core PostgreSQL, sepgsql also does not deal
716  * with such entries as individual "objects", thus, modification of these
717  * entries shall be considered as setting an attribute of the underlying
718  * relation.
719  */
720 static void
sepgsql_relation_setattr_extra(Relation catalog,Oid catindex_id,Oid extra_oid,AttrNumber anum_relation_id,AttrNumber anum_extra_id)721 sepgsql_relation_setattr_extra(Relation catalog,
722 							   Oid catindex_id,
723 							   Oid extra_oid,
724 							   AttrNumber anum_relation_id,
725 							   AttrNumber anum_extra_id)
726 {
727 	ScanKeyData skey;
728 	SysScanDesc sscan;
729 	HeapTuple	tuple;
730 	Datum		datum;
731 	bool		isnull;
732 
733 	ScanKeyInit(&skey, anum_extra_id,
734 				BTEqualStrategyNumber, F_OIDEQ,
735 				ObjectIdGetDatum(extra_oid));
736 
737 	sscan = systable_beginscan(catalog, catindex_id, true,
738 							   SnapshotSelf, 1, &skey);
739 	tuple = systable_getnext(sscan);
740 	if (!HeapTupleIsValid(tuple))
741 		elog(ERROR, "could not find tuple for object %u in catalog \"%s\"",
742 			 extra_oid, RelationGetRelationName(catalog));
743 
744 	datum = heap_getattr(tuple, anum_relation_id,
745 						 RelationGetDescr(catalog), &isnull);
746 	Assert(!isnull);
747 
748 	sepgsql_relation_setattr(DatumGetObjectId(datum));
749 
750 	systable_endscan(sscan);
751 }
752 
753 /*
754  * sepgsql_index_modify
755  *		Handle index create, update, drop
756  *
757  * Unlike other relation kinds, indexes do not have their own security labels,
758  * so instead of doing checks directly, treat them as extra attributes of their
759  * owning tables; so check 'setattr' permissions on the table.
760  */
761 static void
sepgsql_index_modify(Oid indexOid)762 sepgsql_index_modify(Oid indexOid)
763 {
764 	Relation	catalog = table_open(IndexRelationId, AccessShareLock);
765 
766 	/* check db_table:{setattr} permission of the table being indexed */
767 	sepgsql_relation_setattr_extra(catalog,
768 								   IndexRelidIndexId,
769 								   indexOid,
770 								   Anum_pg_index_indrelid,
771 								   Anum_pg_index_indexrelid);
772 	table_close(catalog, AccessShareLock);
773 }
774