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