LCOV - code coverage report
Current view: top level - src/backend/commands - seclabel.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 125 180 69.4 %
Date: 2020-06-01 08:06:25 Functions: 6 8 75.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -------------------------------------------------------------------------
       2             :  *
       3             :  * seclabel.c
       4             :  *    routines to support security label feature.
       5             :  *
       6             :  * Portions Copyright (c) 1996-2020, 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             : /*
      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          84 : ExecSecLabelStmt(SecLabelStmt *stmt)
      45             : {
      46          84 :     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          84 :     if (stmt->provider == NULL)
      56             :     {
      57          68 :         if (label_provider_list == NIL)
      58          24 :             ereport(ERROR,
      59             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
      60             :                      errmsg("no security label providers have been loaded")));
      61          44 :         if (list_length(label_provider_list) != 1)
      62           0 :             ereport(ERROR,
      63             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
      64             :                      errmsg("must specify provider when multiple security label providers have been loaded")));
      65          44 :         provider = (LabelProvider *) linitial(label_provider_list);
      66             :     }
      67             :     else
      68             :     {
      69          20 :         foreach(lc, label_provider_list)
      70             :         {
      71           8 :             LabelProvider *lp = lfirst(lc);
      72             : 
      73           8 :             if (strcmp(stmt->provider, lp->provider_name) == 0)
      74             :             {
      75           4 :                 provider = lp;
      76           4 :                 break;
      77             :             }
      78             :         }
      79          16 :         if (provider == NULL)
      80          12 :             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          48 :     address = get_object_address(stmt->objtype, stmt->object,
      93             :                                  &relation, ShareUpdateExclusiveLock, false);
      94             : 
      95             :     /* Require ownership of the target object. */
      96          42 :     check_object_ownership(GetUserId(), stmt->objtype, address,
      97             :                            stmt->object, relation);
      98             : 
      99             :     /* Perform other integrity checks as needed. */
     100          36 :     switch (stmt->objtype)
     101             :     {
     102           2 :         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           2 :             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           2 :             break;
     120          34 :         default:
     121          34 :             break;
     122             :     }
     123             : 
     124             :     /* Provider gets control here, may throw ERROR to veto new label. */
     125          36 :     provider->hook(&address, stmt->label);
     126             : 
     127             :     /* Apply new label. */
     128          28 :     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          28 :     if (relation != NULL)
     137          14 :         relation_close(relation, NoLock);
     138             : 
     139          28 :     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 = table_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 :     table_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 = table_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 :     table_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           6 : 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           6 :     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           6 :     memset(nulls, false, sizeof(nulls));
     266           6 :     memset(replaces, false, sizeof(replaces));
     267           6 :     values[Anum_pg_shseclabel_objoid - 1] = ObjectIdGetDatum(object->objectId);
     268           6 :     values[Anum_pg_shseclabel_classoid - 1] = ObjectIdGetDatum(object->classId);
     269           6 :     values[Anum_pg_shseclabel_provider - 1] = CStringGetTextDatum(provider);
     270           6 :     if (label != NULL)
     271           6 :         values[Anum_pg_shseclabel_label - 1] = CStringGetTextDatum(label);
     272             : 
     273             :     /* Use the index to search for a matching old tuple */
     274           6 :     ScanKeyInit(&keys[0],
     275             :                 Anum_pg_shseclabel_objoid,
     276             :                 BTEqualStrategyNumber, F_OIDEQ,
     277           6 :                 ObjectIdGetDatum(object->objectId));
     278           6 :     ScanKeyInit(&keys[1],
     279             :                 Anum_pg_shseclabel_classoid,
     280             :                 BTEqualStrategyNumber, F_OIDEQ,
     281           6 :                 ObjectIdGetDatum(object->classId));
     282           6 :     ScanKeyInit(&keys[2],
     283             :                 Anum_pg_shseclabel_provider,
     284             :                 BTEqualStrategyNumber, F_TEXTEQ,
     285           6 :                 CStringGetTextDatum(provider));
     286             : 
     287           6 :     pg_shseclabel = table_open(SharedSecLabelRelationId, RowExclusiveLock);
     288             : 
     289           6 :     scan = systable_beginscan(pg_shseclabel, SharedSecLabelObjectIndexId, true,
     290             :                               NULL, 3, keys);
     291             : 
     292           6 :     oldtup = systable_getnext(scan);
     293           6 :     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           6 :     systable_endscan(scan);
     306             : 
     307             :     /* If we didn't find an old tuple, insert a new one */
     308           6 :     if (newtup == NULL && label != NULL)
     309             :     {
     310           6 :         newtup = heap_form_tuple(RelationGetDescr(pg_shseclabel),
     311             :                                  values, nulls);
     312           6 :         CatalogTupleInsert(pg_shseclabel, newtup);
     313             :     }
     314             : 
     315           6 :     if (newtup != NULL)
     316           6 :         heap_freetuple(newtup);
     317             : 
     318           6 :     table_close(pg_shseclabel, RowExclusiveLock);
     319           6 : }
     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             :  */
     326             : void
     327          28 : 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          28 :     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          28 :     if (IsSharedRelation(object->classId))
     341             :     {
     342           6 :         SetSharedSecurityLabel(object, provider, label);
     343           6 :         return;
     344             :     }
     345             : 
     346             :     /* Prepare to form or update a tuple, if necessary. */
     347          22 :     memset(nulls, false, sizeof(nulls));
     348          22 :     memset(replaces, false, sizeof(replaces));
     349          22 :     values[Anum_pg_seclabel_objoid - 1] = ObjectIdGetDatum(object->objectId);
     350          22 :     values[Anum_pg_seclabel_classoid - 1] = ObjectIdGetDatum(object->classId);
     351          22 :     values[Anum_pg_seclabel_objsubid - 1] = Int32GetDatum(object->objectSubId);
     352          22 :     values[Anum_pg_seclabel_provider - 1] = CStringGetTextDatum(provider);
     353          22 :     if (label != NULL)
     354          22 :         values[Anum_pg_seclabel_label - 1] = CStringGetTextDatum(label);
     355             : 
     356             :     /* Use the index to search for a matching old tuple */
     357          22 :     ScanKeyInit(&keys[0],
     358             :                 Anum_pg_seclabel_objoid,
     359             :                 BTEqualStrategyNumber, F_OIDEQ,
     360          22 :                 ObjectIdGetDatum(object->objectId));
     361          22 :     ScanKeyInit(&keys[1],
     362             :                 Anum_pg_seclabel_classoid,
     363             :                 BTEqualStrategyNumber, F_OIDEQ,
     364          22 :                 ObjectIdGetDatum(object->classId));
     365          22 :     ScanKeyInit(&keys[2],
     366             :                 Anum_pg_seclabel_objsubid,
     367             :                 BTEqualStrategyNumber, F_INT4EQ,
     368          22 :                 Int32GetDatum(object->objectSubId));
     369          22 :     ScanKeyInit(&keys[3],
     370             :                 Anum_pg_seclabel_provider,
     371             :                 BTEqualStrategyNumber, F_TEXTEQ,
     372          22 :                 CStringGetTextDatum(provider));
     373             : 
     374          22 :     pg_seclabel = table_open(SecLabelRelationId, RowExclusiveLock);
     375             : 
     376          22 :     scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId, true,
     377             :                               NULL, 4, keys);
     378             : 
     379          22 :     oldtup = systable_getnext(scan);
     380          22 :     if (HeapTupleIsValid(oldtup))
     381             :     {
     382           6 :         if (label == NULL)
     383           0 :             CatalogTupleDelete(pg_seclabel, &oldtup->t_self);
     384             :         else
     385             :         {
     386           6 :             replaces[Anum_pg_seclabel_label - 1] = true;
     387           6 :             newtup = heap_modify_tuple(oldtup, RelationGetDescr(pg_seclabel),
     388             :                                        values, nulls, replaces);
     389           6 :             CatalogTupleUpdate(pg_seclabel, &oldtup->t_self, newtup);
     390             :         }
     391             :     }
     392          22 :     systable_endscan(scan);
     393             : 
     394             :     /* If we didn't find an old tuple, insert a new one */
     395          22 :     if (newtup == NULL && label != NULL)
     396             :     {
     397          16 :         newtup = heap_form_tuple(RelationGetDescr(pg_seclabel),
     398             :                                  values, nulls);
     399          16 :         CatalogTupleInsert(pg_seclabel, newtup);
     400             :     }
     401             : 
     402             :     /* Update indexes, if necessary */
     403          22 :     if (newtup != NULL)
     404          22 :         heap_freetuple(newtup);
     405             : 
     406          22 :     table_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         626 : DeleteSharedSecurityLabel(Oid objectId, Oid classId)
     415             : {
     416             :     Relation    pg_shseclabel;
     417             :     ScanKeyData skey[2];
     418             :     SysScanDesc scan;
     419             :     HeapTuple   oldtup;
     420             : 
     421         626 :     ScanKeyInit(&skey[0],
     422             :                 Anum_pg_shseclabel_objoid,
     423             :                 BTEqualStrategyNumber, F_OIDEQ,
     424             :                 ObjectIdGetDatum(objectId));
     425         626 :     ScanKeyInit(&skey[1],
     426             :                 Anum_pg_shseclabel_classoid,
     427             :                 BTEqualStrategyNumber, F_OIDEQ,
     428             :                 ObjectIdGetDatum(classId));
     429             : 
     430         626 :     pg_shseclabel = table_open(SharedSecLabelRelationId, RowExclusiveLock);
     431             : 
     432         626 :     scan = systable_beginscan(pg_shseclabel, SharedSecLabelObjectIndexId, true,
     433             :                               NULL, 2, skey);
     434         630 :     while (HeapTupleIsValid(oldtup = systable_getnext(scan)))
     435           4 :         CatalogTupleDelete(pg_shseclabel, &oldtup->t_self);
     436         626 :     systable_endscan(scan);
     437             : 
     438         626 :     table_close(pg_shseclabel, RowExclusiveLock);
     439         626 : }
     440             : 
     441             : /*
     442             :  * DeleteSecurityLabel removes all security labels for an object (and any
     443             :  * sub-objects, if applicable).
     444             :  */
     445             : void
     446      101622 : 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      101622 :     if (IsSharedRelation(object->classId))
     456             :     {
     457             :         Assert(object->objectSubId == 0);
     458           0 :         DeleteSharedSecurityLabel(object->objectId, object->classId);
     459           0 :         return;
     460             :     }
     461             : 
     462      101622 :     ScanKeyInit(&skey[0],
     463             :                 Anum_pg_seclabel_objoid,
     464             :                 BTEqualStrategyNumber, F_OIDEQ,
     465      101622 :                 ObjectIdGetDatum(object->objectId));
     466      101622 :     ScanKeyInit(&skey[1],
     467             :                 Anum_pg_seclabel_classoid,
     468             :                 BTEqualStrategyNumber, F_OIDEQ,
     469      101622 :                 ObjectIdGetDatum(object->classId));
     470      101622 :     if (object->objectSubId != 0)
     471             :     {
     472        1286 :         ScanKeyInit(&skey[2],
     473             :                     Anum_pg_seclabel_objsubid,
     474             :                     BTEqualStrategyNumber, F_INT4EQ,
     475        1286 :                     Int32GetDatum(object->objectSubId));
     476        1286 :         nkeys = 3;
     477             :     }
     478             :     else
     479      100336 :         nkeys = 2;
     480             : 
     481      101622 :     pg_seclabel = table_open(SecLabelRelationId, RowExclusiveLock);
     482             : 
     483      101622 :     scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId, true,
     484             :                               NULL, nkeys, skey);
     485      101632 :     while (HeapTupleIsValid(oldtup = systable_getnext(scan)))
     486          10 :         CatalogTupleDelete(pg_seclabel, &oldtup->t_self);
     487      101622 :     systable_endscan(scan);
     488             : 
     489      101622 :     table_close(pg_seclabel, RowExclusiveLock);
     490             : }
     491             : 
     492             : void
     493           2 : register_label_provider(const char *provider_name, check_object_relabel_type hook)
     494             : {
     495             :     LabelProvider *provider;
     496             :     MemoryContext oldcxt;
     497             : 
     498           2 :     oldcxt = MemoryContextSwitchTo(TopMemoryContext);
     499           2 :     provider = palloc(sizeof(LabelProvider));
     500           2 :     provider->provider_name = pstrdup(provider_name);
     501           2 :     provider->hook = hook;
     502           2 :     label_provider_list = lappend(label_provider_list, provider);
     503           2 :     MemoryContextSwitchTo(oldcxt);
     504           2 : }

Generated by: LCOV version 1.13