1/* -------------------------------------------------------------------------
2 *
3 * seclabel.c
4 * routines to support security label feature.
5 *
6 * Portions Copyright (c) 1996-2019, 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
28typedef struct
29{
30 const char *provider_name;
31 check_object_relabel_type hook;
32} LabelProvider;
33
34static 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 */
43ObjectAddress
44ExecSecLabelStmt(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 */
146static char *
147GetSharedSecurityLabel(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 = table_open(SharedSecLabelRelationId, AccessShareLock);
171
172 scan = systable_beginscan(pg_shseclabel, SharedSecLabelObjectIndexId, true,
173 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 table_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 */
194char *
195GetSecurityLabel(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 = table_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 table_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 */
251static void
252SetSharedSecurityLabel(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 = table_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 table_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 * existing label should be deleted.
325 */
326void
327SetSecurityLabel(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 = table_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 table_close(pg_seclabel, RowExclusiveLock);
407}
408
409/*
410 * DeleteSharedSecurityLabel is a helper function of DeleteSecurityLabel
411 * to handle shared database objects.
412 */
413void
414DeleteSharedSecurityLabel(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 = table_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 table_close(pg_shseclabel, RowExclusiveLock);
439}
440
441/*
442 * DeleteSecurityLabel removes all security labels for an object (and any
443 * sub-objects, if applicable).
444 */
445void
446DeleteSecurityLabel(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 = table_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 table_close(pg_seclabel, RowExclusiveLock);
490}
491
492void
493register_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