Line data Source code
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
44 8 : ExecSecLabelStmt(SecLabelStmt *stmt)
45 : {
46 8 : 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 8 : if (stmt->provider == NULL)
56 : {
57 6 : if (label_provider_list == NIL)
58 6 : ereport(ERROR,
59 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
60 : errmsg("no security label providers have been loaded")));
61 0 : if (lnext(list_head(label_provider_list)) != NULL)
62 0 : ereport(ERROR,
63 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
64 : errmsg("must specify provider when multiple security label providers have been loaded")));
65 0 : provider = (LabelProvider *) linitial(label_provider_list);
66 : }
67 : else
68 : {
69 2 : foreach(lc, label_provider_list)
70 : {
71 0 : LabelProvider *lp = lfirst(lc);
72 :
73 0 : if (strcmp(stmt->provider, lp->provider_name) == 0)
74 : {
75 0 : provider = lp;
76 0 : break;
77 : }
78 : }
79 2 : if (provider == NULL)
80 2 : 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 0 : address = get_object_address(stmt->objtype, stmt->object,
93 : &relation, ShareUpdateExclusiveLock, false);
94 :
95 : /* Require ownership of the target object. */
96 0 : check_object_ownership(GetUserId(), stmt->objtype, address,
97 : stmt->object, relation);
98 :
99 : /* Perform other integrity checks as needed. */
100 0 : 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 0 : if (relation->rd_rel->relkind != RELKIND_RELATION &&
110 0 : relation->rd_rel->relkind != RELKIND_VIEW &&
111 0 : relation->rd_rel->relkind != RELKIND_MATVIEW &&
112 0 : relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE &&
113 0 : relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
114 0 : relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
115 0 : 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 0 : break;
120 : default:
121 0 : break;
122 : }
123 :
124 : /* Provider gets control here, may throw ERROR to veto new label. */
125 0 : (*provider->hook) (&address, stmt->label);
126 :
127 : /* Apply new label. */
128 0 : 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 0 : if (relation != NULL)
137 0 : relation_close(relation, NoLock);
138 :
139 0 : 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 *
147 0 : 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 0 : char *seclabel = NULL;
156 :
157 0 : ScanKeyInit(&keys[0],
158 : Anum_pg_shseclabel_objoid,
159 : BTEqualStrategyNumber, F_OIDEQ,
160 0 : ObjectIdGetDatum(object->objectId));
161 0 : ScanKeyInit(&keys[1],
162 : Anum_pg_shseclabel_classoid,
163 : BTEqualStrategyNumber, F_OIDEQ,
164 0 : ObjectIdGetDatum(object->classId));
165 0 : ScanKeyInit(&keys[2],
166 : Anum_pg_shseclabel_provider,
167 : BTEqualStrategyNumber, F_TEXTEQ,
168 0 : CStringGetTextDatum(provider));
169 :
170 0 : pg_shseclabel = heap_open(SharedSecLabelRelationId, AccessShareLock);
171 :
172 0 : scan = systable_beginscan(pg_shseclabel, SharedSecLabelObjectIndexId, true,
173 : NULL, 3, keys);
174 :
175 0 : tuple = systable_getnext(scan);
176 0 : if (HeapTupleIsValid(tuple))
177 : {
178 0 : datum = heap_getattr(tuple, Anum_pg_shseclabel_label,
179 : RelationGetDescr(pg_shseclabel), &isnull);
180 0 : if (!isnull)
181 0 : seclabel = TextDatumGetCString(datum);
182 : }
183 0 : systable_endscan(scan);
184 :
185 0 : heap_close(pg_shseclabel, AccessShareLock);
186 :
187 0 : 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 *
195 0 : 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 0 : char *seclabel = NULL;
204 :
205 : /* Shared objects have their own security label catalog. */
206 0 : if (IsSharedRelation(object->classId))
207 0 : return GetSharedSecurityLabel(object, provider);
208 :
209 : /* Must be an unshared object, so examine pg_seclabel. */
210 0 : ScanKeyInit(&keys[0],
211 : Anum_pg_seclabel_objoid,
212 : BTEqualStrategyNumber, F_OIDEQ,
213 0 : ObjectIdGetDatum(object->objectId));
214 0 : ScanKeyInit(&keys[1],
215 : Anum_pg_seclabel_classoid,
216 : BTEqualStrategyNumber, F_OIDEQ,
217 0 : ObjectIdGetDatum(object->classId));
218 0 : ScanKeyInit(&keys[2],
219 : Anum_pg_seclabel_objsubid,
220 : BTEqualStrategyNumber, F_INT4EQ,
221 0 : Int32GetDatum(object->objectSubId));
222 0 : ScanKeyInit(&keys[3],
223 : Anum_pg_seclabel_provider,
224 : BTEqualStrategyNumber, F_TEXTEQ,
225 0 : CStringGetTextDatum(provider));
226 :
227 0 : pg_seclabel = heap_open(SecLabelRelationId, AccessShareLock);
228 :
229 0 : scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId, true,
230 : NULL, 4, keys);
231 :
232 0 : tuple = systable_getnext(scan);
233 0 : if (HeapTupleIsValid(tuple))
234 : {
235 0 : datum = heap_getattr(tuple, Anum_pg_seclabel_label,
236 : RelationGetDescr(pg_seclabel), &isnull);
237 0 : if (!isnull)
238 0 : seclabel = TextDatumGetCString(datum);
239 : }
240 0 : systable_endscan(scan);
241 :
242 0 : heap_close(pg_seclabel, AccessShareLock);
243 :
244 0 : return seclabel;
245 : }
246 :
247 : /*
248 : * SetSharedSecurityLabel is a helper function of SetSecurityLabel to
249 : * handle shared database objects.
250 : */
251 : static void
252 0 : 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 0 : 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 0 : memset(nulls, false, sizeof(nulls));
266 0 : memset(replaces, false, sizeof(replaces));
267 0 : values[Anum_pg_shseclabel_objoid - 1] = ObjectIdGetDatum(object->objectId);
268 0 : values[Anum_pg_shseclabel_classoid - 1] = ObjectIdGetDatum(object->classId);
269 0 : values[Anum_pg_shseclabel_provider - 1] = CStringGetTextDatum(provider);
270 0 : if (label != NULL)
271 0 : values[Anum_pg_shseclabel_label - 1] = CStringGetTextDatum(label);
272 :
273 : /* Use the index to search for a matching old tuple */
274 0 : ScanKeyInit(&keys[0],
275 : Anum_pg_shseclabel_objoid,
276 : BTEqualStrategyNumber, F_OIDEQ,
277 0 : ObjectIdGetDatum(object->objectId));
278 0 : ScanKeyInit(&keys[1],
279 : Anum_pg_shseclabel_classoid,
280 : BTEqualStrategyNumber, F_OIDEQ,
281 0 : ObjectIdGetDatum(object->classId));
282 0 : ScanKeyInit(&keys[2],
283 : Anum_pg_shseclabel_provider,
284 : BTEqualStrategyNumber, F_TEXTEQ,
285 0 : CStringGetTextDatum(provider));
286 :
287 0 : pg_shseclabel = heap_open(SharedSecLabelRelationId, RowExclusiveLock);
288 :
289 0 : scan = systable_beginscan(pg_shseclabel, SharedSecLabelObjectIndexId, true,
290 : NULL, 3, keys);
291 :
292 0 : oldtup = systable_getnext(scan);
293 0 : if (HeapTupleIsValid(oldtup))
294 : {
295 0 : if (label == NULL)
296 0 : CatalogTupleDelete(pg_shseclabel, &oldtup->t_self);
297 : else
298 : {
299 0 : replaces[Anum_pg_shseclabel_label - 1] = true;
300 0 : newtup = heap_modify_tuple(oldtup, RelationGetDescr(pg_shseclabel),
301 : values, nulls, replaces);
302 0 : CatalogTupleUpdate(pg_shseclabel, &oldtup->t_self, newtup);
303 : }
304 : }
305 0 : systable_endscan(scan);
306 :
307 : /* If we didn't find an old tuple, insert a new one */
308 0 : if (newtup == NULL && label != NULL)
309 : {
310 0 : newtup = heap_form_tuple(RelationGetDescr(pg_shseclabel),
311 : values, nulls);
312 0 : CatalogTupleInsert(pg_shseclabel, newtup);
313 : }
314 :
315 0 : if (newtup != NULL)
316 0 : heap_freetuple(newtup);
317 :
318 0 : heap_close(pg_shseclabel, RowExclusiveLock);
319 0 : }
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
327 0 : 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 0 : 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 0 : if (IsSharedRelation(object->classId))
341 : {
342 0 : SetSharedSecurityLabel(object, provider, label);
343 0 : return;
344 : }
345 :
346 : /* Prepare to form or update a tuple, if necessary. */
347 0 : memset(nulls, false, sizeof(nulls));
348 0 : memset(replaces, false, sizeof(replaces));
349 0 : values[Anum_pg_seclabel_objoid - 1] = ObjectIdGetDatum(object->objectId);
350 0 : values[Anum_pg_seclabel_classoid - 1] = ObjectIdGetDatum(object->classId);
351 0 : values[Anum_pg_seclabel_objsubid - 1] = Int32GetDatum(object->objectSubId);
352 0 : values[Anum_pg_seclabel_provider - 1] = CStringGetTextDatum(provider);
353 0 : if (label != NULL)
354 0 : values[Anum_pg_seclabel_label - 1] = CStringGetTextDatum(label);
355 :
356 : /* Use the index to search for a matching old tuple */
357 0 : ScanKeyInit(&keys[0],
358 : Anum_pg_seclabel_objoid,
359 : BTEqualStrategyNumber, F_OIDEQ,
360 0 : ObjectIdGetDatum(object->objectId));
361 0 : ScanKeyInit(&keys[1],
362 : Anum_pg_seclabel_classoid,
363 : BTEqualStrategyNumber, F_OIDEQ,
364 0 : ObjectIdGetDatum(object->classId));
365 0 : ScanKeyInit(&keys[2],
366 : Anum_pg_seclabel_objsubid,
367 : BTEqualStrategyNumber, F_INT4EQ,
368 0 : Int32GetDatum(object->objectSubId));
369 0 : ScanKeyInit(&keys[3],
370 : Anum_pg_seclabel_provider,
371 : BTEqualStrategyNumber, F_TEXTEQ,
372 0 : CStringGetTextDatum(provider));
373 :
374 0 : pg_seclabel = heap_open(SecLabelRelationId, RowExclusiveLock);
375 :
376 0 : scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId, true,
377 : NULL, 4, keys);
378 :
379 0 : oldtup = systable_getnext(scan);
380 0 : if (HeapTupleIsValid(oldtup))
381 : {
382 0 : if (label == NULL)
383 0 : CatalogTupleDelete(pg_seclabel, &oldtup->t_self);
384 : else
385 : {
386 0 : replaces[Anum_pg_seclabel_label - 1] = true;
387 0 : newtup = heap_modify_tuple(oldtup, RelationGetDescr(pg_seclabel),
388 : values, nulls, replaces);
389 0 : CatalogTupleUpdate(pg_seclabel, &oldtup->t_self, newtup);
390 : }
391 : }
392 0 : systable_endscan(scan);
393 :
394 : /* If we didn't find an old tuple, insert a new one */
395 0 : if (newtup == NULL && label != NULL)
396 : {
397 0 : newtup = heap_form_tuple(RelationGetDescr(pg_seclabel),
398 : values, nulls);
399 0 : CatalogTupleInsert(pg_seclabel, newtup);
400 : }
401 :
402 : /* Update indexes, if necessary */
403 0 : if (newtup != NULL)
404 0 : heap_freetuple(newtup);
405 :
406 0 : 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
414 109 : DeleteSharedSecurityLabel(Oid objectId, Oid classId)
415 : {
416 : Relation pg_shseclabel;
417 : ScanKeyData skey[2];
418 : SysScanDesc scan;
419 : HeapTuple oldtup;
420 :
421 109 : ScanKeyInit(&skey[0],
422 : Anum_pg_shseclabel_objoid,
423 : BTEqualStrategyNumber, F_OIDEQ,
424 : ObjectIdGetDatum(objectId));
425 109 : ScanKeyInit(&skey[1],
426 : Anum_pg_shseclabel_classoid,
427 : BTEqualStrategyNumber, F_OIDEQ,
428 : ObjectIdGetDatum(classId));
429 :
430 109 : pg_shseclabel = heap_open(SharedSecLabelRelationId, RowExclusiveLock);
431 :
432 109 : scan = systable_beginscan(pg_shseclabel, SharedSecLabelObjectIndexId, true,
433 : NULL, 2, skey);
434 218 : while (HeapTupleIsValid(oldtup = systable_getnext(scan)))
435 0 : CatalogTupleDelete(pg_shseclabel, &oldtup->t_self);
436 109 : systable_endscan(scan);
437 :
438 109 : heap_close(pg_shseclabel, RowExclusiveLock);
439 109 : }
440 :
441 : /*
442 : * DeleteSecurityLabel removes all security labels for an object (and any
443 : * sub-objects, if applicable).
444 : */
445 : void
446 9057 : 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 9057 : if (IsSharedRelation(object->classId))
456 : {
457 0 : Assert(object->objectSubId == 0);
458 0 : DeleteSharedSecurityLabel(object->objectId, object->classId);
459 9057 : return;
460 : }
461 :
462 9057 : ScanKeyInit(&skey[0],
463 : Anum_pg_seclabel_objoid,
464 : BTEqualStrategyNumber, F_OIDEQ,
465 9057 : ObjectIdGetDatum(object->objectId));
466 9057 : ScanKeyInit(&skey[1],
467 : Anum_pg_seclabel_classoid,
468 : BTEqualStrategyNumber, F_OIDEQ,
469 9057 : ObjectIdGetDatum(object->classId));
470 9057 : if (object->objectSubId != 0)
471 : {
472 117 : ScanKeyInit(&skey[2],
473 : Anum_pg_seclabel_objsubid,
474 : BTEqualStrategyNumber, F_INT4EQ,
475 117 : Int32GetDatum(object->objectSubId));
476 117 : nkeys = 3;
477 : }
478 : else
479 8940 : nkeys = 2;
480 :
481 9057 : pg_seclabel = heap_open(SecLabelRelationId, RowExclusiveLock);
482 :
483 9057 : scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId, true,
484 : NULL, nkeys, skey);
485 18114 : while (HeapTupleIsValid(oldtup = systable_getnext(scan)))
486 0 : CatalogTupleDelete(pg_seclabel, &oldtup->t_self);
487 9057 : systable_endscan(scan);
488 :
489 9057 : heap_close(pg_seclabel, RowExclusiveLock);
490 : }
491 :
492 : void
493 0 : register_label_provider(const char *provider_name, check_object_relabel_type hook)
494 : {
495 : LabelProvider *provider;
496 : MemoryContext oldcxt;
497 :
498 0 : oldcxt = MemoryContextSwitchTo(TopMemoryContext);
499 0 : provider = palloc(sizeof(LabelProvider));
500 0 : provider->provider_name = pstrdup(provider_name);
501 0 : provider->hook = hook;
502 0 : label_provider_list = lappend(label_provider_list, provider);
503 0 : MemoryContextSwitchTo(oldcxt);
504 0 : }
|