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