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