1 /* -------------------------------------------------------------------------
2  *
3  * seclabel.c
4  *	  routines to support security label feature.
5  *
6  * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  * -------------------------------------------------------------------------
10  */
11 #include "postgres.h"
12 
13 #include "access/genam.h"
14 #include "access/htup_details.h"
15 #include "access/relation.h"
16 #include "access/table.h"
17 #include "catalog/catalog.h"
18 #include "catalog/indexing.h"
19 #include "catalog/pg_seclabel.h"
20 #include "catalog/pg_shseclabel.h"
21 #include "commands/seclabel.h"
22 #include "miscadmin.h"
23 #include "utils/builtins.h"
24 #include "utils/fmgroids.h"
25 #include "utils/memutils.h"
26 #include "utils/rel.h"
27 
28 typedef struct
29 {
30 	const char *provider_name;
31 	check_object_relabel_type hook;
32 } LabelProvider;
33 
34 static List *label_provider_list = NIL;
35 
36 static bool
SecLabelSupportsObjectType(ObjectType objtype)37 SecLabelSupportsObjectType(ObjectType objtype)
38 {
39 	switch (objtype)
40 	{
41 		case OBJECT_AGGREGATE:
42 		case OBJECT_COLUMN:
43 		case OBJECT_DATABASE:
44 		case OBJECT_DOMAIN:
45 		case OBJECT_EVENT_TRIGGER:
46 		case OBJECT_FOREIGN_TABLE:
47 		case OBJECT_FUNCTION:
48 		case OBJECT_LANGUAGE:
49 		case OBJECT_LARGEOBJECT:
50 		case OBJECT_MATVIEW:
51 		case OBJECT_PROCEDURE:
52 		case OBJECT_PUBLICATION:
53 		case OBJECT_ROLE:
54 		case OBJECT_ROUTINE:
55 		case OBJECT_SCHEMA:
56 		case OBJECT_SEQUENCE:
57 		case OBJECT_SUBSCRIPTION:
58 		case OBJECT_TABLE:
59 		case OBJECT_TABLESPACE:
60 		case OBJECT_TYPE:
61 		case OBJECT_VIEW:
62 			return true;
63 
64 		case OBJECT_ACCESS_METHOD:
65 		case OBJECT_AMOP:
66 		case OBJECT_AMPROC:
67 		case OBJECT_ATTRIBUTE:
68 		case OBJECT_CAST:
69 		case OBJECT_COLLATION:
70 		case OBJECT_CONVERSION:
71 		case OBJECT_DEFAULT:
72 		case OBJECT_DEFACL:
73 		case OBJECT_DOMCONSTRAINT:
74 		case OBJECT_EXTENSION:
75 		case OBJECT_FDW:
76 		case OBJECT_FOREIGN_SERVER:
77 		case OBJECT_INDEX:
78 		case OBJECT_OPCLASS:
79 		case OBJECT_OPERATOR:
80 		case OBJECT_OPFAMILY:
81 		case OBJECT_POLICY:
82 		case OBJECT_PUBLICATION_REL:
83 		case OBJECT_RULE:
84 		case OBJECT_STATISTIC_EXT:
85 		case OBJECT_TABCONSTRAINT:
86 		case OBJECT_TRANSFORM:
87 		case OBJECT_TRIGGER:
88 		case OBJECT_TSCONFIGURATION:
89 		case OBJECT_TSDICTIONARY:
90 		case OBJECT_TSPARSER:
91 		case OBJECT_TSTEMPLATE:
92 		case OBJECT_USER_MAPPING:
93 			return false;
94 
95 			/*
96 			 * There's intentionally no default: case here; we want the
97 			 * compiler to warn if a new ObjectType hasn't been handled above.
98 			 */
99 	}
100 
101 	/* Shouldn't get here, but if we do, say "no support" */
102 	return false;
103 }
104 
105 /*
106  * ExecSecLabelStmt --
107  *
108  * Apply a security label to a database object.
109  *
110  * Returns the ObjectAddress of the object to which the policy was applied.
111  */
112 ObjectAddress
ExecSecLabelStmt(SecLabelStmt * stmt)113 ExecSecLabelStmt(SecLabelStmt *stmt)
114 {
115 	LabelProvider *provider = NULL;
116 	ObjectAddress address;
117 	Relation	relation;
118 	ListCell   *lc;
119 
120 	/*
121 	 * Find the named label provider, or if none specified, check whether
122 	 * there's exactly one, and if so use it.
123 	 */
124 	if (stmt->provider == NULL)
125 	{
126 		if (label_provider_list == NIL)
127 			ereport(ERROR,
128 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
129 					 errmsg("no security label providers have been loaded")));
130 		if (list_length(label_provider_list) != 1)
131 			ereport(ERROR,
132 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
133 					 errmsg("must specify provider when multiple security label providers have been loaded")));
134 		provider = (LabelProvider *) linitial(label_provider_list);
135 	}
136 	else
137 	{
138 		foreach(lc, label_provider_list)
139 		{
140 			LabelProvider *lp = lfirst(lc);
141 
142 			if (strcmp(stmt->provider, lp->provider_name) == 0)
143 			{
144 				provider = lp;
145 				break;
146 			}
147 		}
148 		if (provider == NULL)
149 			ereport(ERROR,
150 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
151 					 errmsg("security label provider \"%s\" is not loaded",
152 							stmt->provider)));
153 	}
154 
155 	if (!SecLabelSupportsObjectType(stmt->objtype))
156 		ereport(ERROR,
157 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
158 				 errmsg("security labels are not supported for this type of object")));
159 
160 	/*
161 	 * Translate the parser representation which identifies this object into
162 	 * an ObjectAddress. get_object_address() will throw an error if the
163 	 * object does not exist, and will also acquire a lock on the target to
164 	 * guard against concurrent modifications.
165 	 */
166 	address = get_object_address(stmt->objtype, stmt->object,
167 								 &relation, ShareUpdateExclusiveLock, false);
168 
169 	/* Require ownership of the target object. */
170 	check_object_ownership(GetUserId(), stmt->objtype, address,
171 						   stmt->object, relation);
172 
173 	/* Perform other integrity checks as needed. */
174 	switch (stmt->objtype)
175 	{
176 		case OBJECT_COLUMN:
177 
178 			/*
179 			 * Allow security labels only on columns of tables, views,
180 			 * materialized views, composite types, and foreign tables (which
181 			 * are the only relkinds for which pg_dump will dump labels).
182 			 */
183 			if (relation->rd_rel->relkind != RELKIND_RELATION &&
184 				relation->rd_rel->relkind != RELKIND_VIEW &&
185 				relation->rd_rel->relkind != RELKIND_MATVIEW &&
186 				relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE &&
187 				relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
188 				relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
189 				ereport(ERROR,
190 						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
191 						 errmsg("\"%s\" is not a table, view, materialized view, composite type, or foreign table",
192 								RelationGetRelationName(relation))));
193 			break;
194 		default:
195 			break;
196 	}
197 
198 	/* Provider gets control here, may throw ERROR to veto new label. */
199 	provider->hook(&address, stmt->label);
200 
201 	/* Apply new label. */
202 	SetSecurityLabel(&address, provider->provider_name, stmt->label);
203 
204 	/*
205 	 * If get_object_address() opened the relation for us, we close it to keep
206 	 * the reference count correct - but we retain any locks acquired by
207 	 * get_object_address() until commit time, to guard against concurrent
208 	 * activity.
209 	 */
210 	if (relation != NULL)
211 		relation_close(relation, NoLock);
212 
213 	return address;
214 }
215 
216 /*
217  * GetSharedSecurityLabel returns the security label for a shared object for
218  * a given provider, or NULL if there is no such label.
219  */
220 static char *
GetSharedSecurityLabel(const ObjectAddress * object,const char * provider)221 GetSharedSecurityLabel(const ObjectAddress *object, const char *provider)
222 {
223 	Relation	pg_shseclabel;
224 	ScanKeyData keys[3];
225 	SysScanDesc scan;
226 	HeapTuple	tuple;
227 	Datum		datum;
228 	bool		isnull;
229 	char	   *seclabel = NULL;
230 
231 	ScanKeyInit(&keys[0],
232 				Anum_pg_shseclabel_objoid,
233 				BTEqualStrategyNumber, F_OIDEQ,
234 				ObjectIdGetDatum(object->objectId));
235 	ScanKeyInit(&keys[1],
236 				Anum_pg_shseclabel_classoid,
237 				BTEqualStrategyNumber, F_OIDEQ,
238 				ObjectIdGetDatum(object->classId));
239 	ScanKeyInit(&keys[2],
240 				Anum_pg_shseclabel_provider,
241 				BTEqualStrategyNumber, F_TEXTEQ,
242 				CStringGetTextDatum(provider));
243 
244 	pg_shseclabel = table_open(SharedSecLabelRelationId, AccessShareLock);
245 
246 	scan = systable_beginscan(pg_shseclabel, SharedSecLabelObjectIndexId,
247 							  criticalSharedRelcachesBuilt, NULL, 3, keys);
248 
249 	tuple = systable_getnext(scan);
250 	if (HeapTupleIsValid(tuple))
251 	{
252 		datum = heap_getattr(tuple, Anum_pg_shseclabel_label,
253 							 RelationGetDescr(pg_shseclabel), &isnull);
254 		if (!isnull)
255 			seclabel = TextDatumGetCString(datum);
256 	}
257 	systable_endscan(scan);
258 
259 	table_close(pg_shseclabel, AccessShareLock);
260 
261 	return seclabel;
262 }
263 
264 /*
265  * GetSecurityLabel returns the security label for a shared or database object
266  * for a given provider, or NULL if there is no such label.
267  */
268 char *
GetSecurityLabel(const ObjectAddress * object,const char * provider)269 GetSecurityLabel(const ObjectAddress *object, const char *provider)
270 {
271 	Relation	pg_seclabel;
272 	ScanKeyData keys[4];
273 	SysScanDesc scan;
274 	HeapTuple	tuple;
275 	Datum		datum;
276 	bool		isnull;
277 	char	   *seclabel = NULL;
278 
279 	/* Shared objects have their own security label catalog. */
280 	if (IsSharedRelation(object->classId))
281 		return GetSharedSecurityLabel(object, provider);
282 
283 	/* Must be an unshared object, so examine pg_seclabel. */
284 	ScanKeyInit(&keys[0],
285 				Anum_pg_seclabel_objoid,
286 				BTEqualStrategyNumber, F_OIDEQ,
287 				ObjectIdGetDatum(object->objectId));
288 	ScanKeyInit(&keys[1],
289 				Anum_pg_seclabel_classoid,
290 				BTEqualStrategyNumber, F_OIDEQ,
291 				ObjectIdGetDatum(object->classId));
292 	ScanKeyInit(&keys[2],
293 				Anum_pg_seclabel_objsubid,
294 				BTEqualStrategyNumber, F_INT4EQ,
295 				Int32GetDatum(object->objectSubId));
296 	ScanKeyInit(&keys[3],
297 				Anum_pg_seclabel_provider,
298 				BTEqualStrategyNumber, F_TEXTEQ,
299 				CStringGetTextDatum(provider));
300 
301 	pg_seclabel = table_open(SecLabelRelationId, AccessShareLock);
302 
303 	scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId, true,
304 							  NULL, 4, keys);
305 
306 	tuple = systable_getnext(scan);
307 	if (HeapTupleIsValid(tuple))
308 	{
309 		datum = heap_getattr(tuple, Anum_pg_seclabel_label,
310 							 RelationGetDescr(pg_seclabel), &isnull);
311 		if (!isnull)
312 			seclabel = TextDatumGetCString(datum);
313 	}
314 	systable_endscan(scan);
315 
316 	table_close(pg_seclabel, AccessShareLock);
317 
318 	return seclabel;
319 }
320 
321 /*
322  * SetSharedSecurityLabel is a helper function of SetSecurityLabel to
323  * handle shared database objects.
324  */
325 static void
SetSharedSecurityLabel(const ObjectAddress * object,const char * provider,const char * label)326 SetSharedSecurityLabel(const ObjectAddress *object,
327 					   const char *provider, const char *label)
328 {
329 	Relation	pg_shseclabel;
330 	ScanKeyData keys[4];
331 	SysScanDesc scan;
332 	HeapTuple	oldtup;
333 	HeapTuple	newtup = NULL;
334 	Datum		values[Natts_pg_shseclabel];
335 	bool		nulls[Natts_pg_shseclabel];
336 	bool		replaces[Natts_pg_shseclabel];
337 
338 	/* Prepare to form or update a tuple, if necessary. */
339 	memset(nulls, false, sizeof(nulls));
340 	memset(replaces, false, sizeof(replaces));
341 	values[Anum_pg_shseclabel_objoid - 1] = ObjectIdGetDatum(object->objectId);
342 	values[Anum_pg_shseclabel_classoid - 1] = ObjectIdGetDatum(object->classId);
343 	values[Anum_pg_shseclabel_provider - 1] = CStringGetTextDatum(provider);
344 	if (label != NULL)
345 		values[Anum_pg_shseclabel_label - 1] = CStringGetTextDatum(label);
346 
347 	/* Use the index to search for a matching old tuple */
348 	ScanKeyInit(&keys[0],
349 				Anum_pg_shseclabel_objoid,
350 				BTEqualStrategyNumber, F_OIDEQ,
351 				ObjectIdGetDatum(object->objectId));
352 	ScanKeyInit(&keys[1],
353 				Anum_pg_shseclabel_classoid,
354 				BTEqualStrategyNumber, F_OIDEQ,
355 				ObjectIdGetDatum(object->classId));
356 	ScanKeyInit(&keys[2],
357 				Anum_pg_shseclabel_provider,
358 				BTEqualStrategyNumber, F_TEXTEQ,
359 				CStringGetTextDatum(provider));
360 
361 	pg_shseclabel = table_open(SharedSecLabelRelationId, RowExclusiveLock);
362 
363 	scan = systable_beginscan(pg_shseclabel, SharedSecLabelObjectIndexId, true,
364 							  NULL, 3, keys);
365 
366 	oldtup = systable_getnext(scan);
367 	if (HeapTupleIsValid(oldtup))
368 	{
369 		if (label == NULL)
370 			CatalogTupleDelete(pg_shseclabel, &oldtup->t_self);
371 		else
372 		{
373 			replaces[Anum_pg_shseclabel_label - 1] = true;
374 			newtup = heap_modify_tuple(oldtup, RelationGetDescr(pg_shseclabel),
375 									   values, nulls, replaces);
376 			CatalogTupleUpdate(pg_shseclabel, &oldtup->t_self, newtup);
377 		}
378 	}
379 	systable_endscan(scan);
380 
381 	/* If we didn't find an old tuple, insert a new one */
382 	if (newtup == NULL && label != NULL)
383 	{
384 		newtup = heap_form_tuple(RelationGetDescr(pg_shseclabel),
385 								 values, nulls);
386 		CatalogTupleInsert(pg_shseclabel, newtup);
387 	}
388 
389 	if (newtup != NULL)
390 		heap_freetuple(newtup);
391 
392 	table_close(pg_shseclabel, RowExclusiveLock);
393 }
394 
395 /*
396  * SetSecurityLabel attempts to set the security label for the specified
397  * provider on the specified object to the given value.  NULL means that any
398  * existing label should be deleted.
399  */
400 void
SetSecurityLabel(const ObjectAddress * object,const char * provider,const char * label)401 SetSecurityLabel(const ObjectAddress *object,
402 				 const char *provider, const char *label)
403 {
404 	Relation	pg_seclabel;
405 	ScanKeyData keys[4];
406 	SysScanDesc scan;
407 	HeapTuple	oldtup;
408 	HeapTuple	newtup = NULL;
409 	Datum		values[Natts_pg_seclabel];
410 	bool		nulls[Natts_pg_seclabel];
411 	bool		replaces[Natts_pg_seclabel];
412 
413 	/* Shared objects have their own security label catalog. */
414 	if (IsSharedRelation(object->classId))
415 	{
416 		SetSharedSecurityLabel(object, provider, label);
417 		return;
418 	}
419 
420 	/* Prepare to form or update a tuple, if necessary. */
421 	memset(nulls, false, sizeof(nulls));
422 	memset(replaces, false, sizeof(replaces));
423 	values[Anum_pg_seclabel_objoid - 1] = ObjectIdGetDatum(object->objectId);
424 	values[Anum_pg_seclabel_classoid - 1] = ObjectIdGetDatum(object->classId);
425 	values[Anum_pg_seclabel_objsubid - 1] = Int32GetDatum(object->objectSubId);
426 	values[Anum_pg_seclabel_provider - 1] = CStringGetTextDatum(provider);
427 	if (label != NULL)
428 		values[Anum_pg_seclabel_label - 1] = CStringGetTextDatum(label);
429 
430 	/* Use the index to search for a matching old tuple */
431 	ScanKeyInit(&keys[0],
432 				Anum_pg_seclabel_objoid,
433 				BTEqualStrategyNumber, F_OIDEQ,
434 				ObjectIdGetDatum(object->objectId));
435 	ScanKeyInit(&keys[1],
436 				Anum_pg_seclabel_classoid,
437 				BTEqualStrategyNumber, F_OIDEQ,
438 				ObjectIdGetDatum(object->classId));
439 	ScanKeyInit(&keys[2],
440 				Anum_pg_seclabel_objsubid,
441 				BTEqualStrategyNumber, F_INT4EQ,
442 				Int32GetDatum(object->objectSubId));
443 	ScanKeyInit(&keys[3],
444 				Anum_pg_seclabel_provider,
445 				BTEqualStrategyNumber, F_TEXTEQ,
446 				CStringGetTextDatum(provider));
447 
448 	pg_seclabel = table_open(SecLabelRelationId, RowExclusiveLock);
449 
450 	scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId, true,
451 							  NULL, 4, keys);
452 
453 	oldtup = systable_getnext(scan);
454 	if (HeapTupleIsValid(oldtup))
455 	{
456 		if (label == NULL)
457 			CatalogTupleDelete(pg_seclabel, &oldtup->t_self);
458 		else
459 		{
460 			replaces[Anum_pg_seclabel_label - 1] = true;
461 			newtup = heap_modify_tuple(oldtup, RelationGetDescr(pg_seclabel),
462 									   values, nulls, replaces);
463 			CatalogTupleUpdate(pg_seclabel, &oldtup->t_self, newtup);
464 		}
465 	}
466 	systable_endscan(scan);
467 
468 	/* If we didn't find an old tuple, insert a new one */
469 	if (newtup == NULL && label != NULL)
470 	{
471 		newtup = heap_form_tuple(RelationGetDescr(pg_seclabel),
472 								 values, nulls);
473 		CatalogTupleInsert(pg_seclabel, newtup);
474 	}
475 
476 	/* Update indexes, if necessary */
477 	if (newtup != NULL)
478 		heap_freetuple(newtup);
479 
480 	table_close(pg_seclabel, RowExclusiveLock);
481 }
482 
483 /*
484  * DeleteSharedSecurityLabel is a helper function of DeleteSecurityLabel
485  * to handle shared database objects.
486  */
487 void
DeleteSharedSecurityLabel(Oid objectId,Oid classId)488 DeleteSharedSecurityLabel(Oid objectId, Oid classId)
489 {
490 	Relation	pg_shseclabel;
491 	ScanKeyData skey[2];
492 	SysScanDesc scan;
493 	HeapTuple	oldtup;
494 
495 	ScanKeyInit(&skey[0],
496 				Anum_pg_shseclabel_objoid,
497 				BTEqualStrategyNumber, F_OIDEQ,
498 				ObjectIdGetDatum(objectId));
499 	ScanKeyInit(&skey[1],
500 				Anum_pg_shseclabel_classoid,
501 				BTEqualStrategyNumber, F_OIDEQ,
502 				ObjectIdGetDatum(classId));
503 
504 	pg_shseclabel = table_open(SharedSecLabelRelationId, RowExclusiveLock);
505 
506 	scan = systable_beginscan(pg_shseclabel, SharedSecLabelObjectIndexId, true,
507 							  NULL, 2, skey);
508 	while (HeapTupleIsValid(oldtup = systable_getnext(scan)))
509 		CatalogTupleDelete(pg_shseclabel, &oldtup->t_self);
510 	systable_endscan(scan);
511 
512 	table_close(pg_shseclabel, RowExclusiveLock);
513 }
514 
515 /*
516  * DeleteSecurityLabel removes all security labels for an object (and any
517  * sub-objects, if applicable).
518  */
519 void
DeleteSecurityLabel(const ObjectAddress * object)520 DeleteSecurityLabel(const ObjectAddress *object)
521 {
522 	Relation	pg_seclabel;
523 	ScanKeyData skey[3];
524 	SysScanDesc scan;
525 	HeapTuple	oldtup;
526 	int			nkeys;
527 
528 	/* Shared objects have their own security label catalog. */
529 	if (IsSharedRelation(object->classId))
530 	{
531 		Assert(object->objectSubId == 0);
532 		DeleteSharedSecurityLabel(object->objectId, object->classId);
533 		return;
534 	}
535 
536 	ScanKeyInit(&skey[0],
537 				Anum_pg_seclabel_objoid,
538 				BTEqualStrategyNumber, F_OIDEQ,
539 				ObjectIdGetDatum(object->objectId));
540 	ScanKeyInit(&skey[1],
541 				Anum_pg_seclabel_classoid,
542 				BTEqualStrategyNumber, F_OIDEQ,
543 				ObjectIdGetDatum(object->classId));
544 	if (object->objectSubId != 0)
545 	{
546 		ScanKeyInit(&skey[2],
547 					Anum_pg_seclabel_objsubid,
548 					BTEqualStrategyNumber, F_INT4EQ,
549 					Int32GetDatum(object->objectSubId));
550 		nkeys = 3;
551 	}
552 	else
553 		nkeys = 2;
554 
555 	pg_seclabel = table_open(SecLabelRelationId, RowExclusiveLock);
556 
557 	scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId, true,
558 							  NULL, nkeys, skey);
559 	while (HeapTupleIsValid(oldtup = systable_getnext(scan)))
560 		CatalogTupleDelete(pg_seclabel, &oldtup->t_self);
561 	systable_endscan(scan);
562 
563 	table_close(pg_seclabel, RowExclusiveLock);
564 }
565 
566 void
register_label_provider(const char * provider_name,check_object_relabel_type hook)567 register_label_provider(const char *provider_name, check_object_relabel_type hook)
568 {
569 	LabelProvider *provider;
570 	MemoryContext oldcxt;
571 
572 	oldcxt = MemoryContextSwitchTo(TopMemoryContext);
573 	provider = palloc(sizeof(LabelProvider));
574 	provider->provider_name = pstrdup(provider_name);
575 	provider->hook = hook;
576 	label_provider_list = lappend(label_provider_list, provider);
577 	MemoryContextSwitchTo(oldcxt);
578 }
579