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