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