summaryrefslogtreecommitdiff
path: root/src/backend/commands/seclabel.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands/seclabel.c')
-rw-r--r--src/backend/commands/seclabel.c387
1 files changed, 387 insertions, 0 deletions
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
new file mode 100644
index 00000000000..417ad88d557
--- /dev/null
+++ b/src/backend/commands/seclabel.c
@@ -0,0 +1,387 @@
+/* -------------------------------------------------------------------------
+ *
+ * seclabel.c
+ * routines to support security label feature.
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * -------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "catalog/catalog.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_seclabel.h"
+#include "commands/seclabel.h"
+#include "miscadmin.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/tqual.h"
+
+/*
+ * For most object types, the permissions-checking logic is simple enough
+ * that it makes sense to just include it in CommentObject(). However,
+ * attributes require a bit more checking.
+ */
+static void CheckAttributeSecLabel(Relation relation);
+
+typedef struct
+{
+ const char *provider_name;
+ check_object_relabel_type hook;
+} LabelProvider;
+
+static List *label_provider_list = NIL;
+
+/*
+ * ExecSecLabelStmt --
+ *
+ * Apply a security label to a database object.
+ */
+void
+ExecSecLabelStmt(SecLabelStmt *stmt)
+{
+ LabelProvider *provider = NULL;
+ ObjectAddress address;
+ Relation relation;
+ ListCell *lc;
+
+ /*
+ * Find the named label provider, or if none specified, check whether
+ * there's exactly one, and if so use it.
+ */
+ if (stmt->provider == NULL)
+ {
+ if (label_provider_list == NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("security label providers have been loaded")));
+ if (lnext(list_head(label_provider_list)) != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("must specify provider when multiple security label providers have been loaded")));
+ provider = (LabelProvider *) linitial(label_provider_list);
+ }
+ else
+ {
+ foreach (lc, label_provider_list)
+ {
+ LabelProvider *lp = lfirst(lc);
+
+ if (strcmp(stmt->provider, lp->provider_name) == 0)
+ {
+ provider = lp;
+ break;
+ }
+ }
+ if (provider == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("security label provider \"%s\" is not loaded",
+ stmt->provider)));
+ }
+
+ /*
+ * Translate the parser representation which identifies this object
+ * into an ObjectAddress. get_object_address() will throw an error if
+ * the object does not exist, and will also acquire a lock on the
+ * target to guard against concurrent modifications.
+ */
+ address = get_object_address(stmt->objtype, stmt->objname, stmt->objargs,
+ &relation, ShareUpdateExclusiveLock);
+
+ /* Privilege and integrity checks. */
+ switch (stmt->objtype)
+ {
+ case OBJECT_SEQUENCE:
+ case OBJECT_TABLE:
+ case OBJECT_VIEW:
+ if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+ RelationGetRelationName(relation));
+ break;
+ case OBJECT_COLUMN:
+ CheckAttributeSecLabel(relation);
+ break;
+ case OBJECT_TYPE:
+ if (!pg_type_ownercheck(address.objectId, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
+ format_type_be(address.objectId));
+ break;
+ case OBJECT_AGGREGATE:
+ case OBJECT_FUNCTION:
+ if (!pg_proc_ownercheck(address.objectId, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
+ NameListToString(stmt->objname));
+ break;
+ case OBJECT_SCHEMA:
+ if (!pg_namespace_ownercheck(address.objectId, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE,
+ strVal(linitial(stmt->objname)));
+ break;
+ case OBJECT_LANGUAGE:
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to comment on procedural language")));
+ break;
+ case OBJECT_LARGEOBJECT:
+ if (!pg_largeobject_ownercheck(address.objectId, GetUserId()))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be owner of large object %u",
+ address.objectId)));
+ break;
+ default:
+ elog(ERROR, "unrecognized object type: %d",
+ (int) stmt->objtype);
+ }
+
+ /* Provider gets control here, may throw ERROR to veto new label. */
+ (*provider->hook)(&address, stmt->label);
+
+ /* Apply new label. */
+ SetSecurityLabel(&address, provider->provider_name, stmt->label);
+
+ /*
+ * If get_object_address() opened the relation for us, we close it to keep
+ * the reference count correct - but we retain any locks acquired by
+ * get_object_address() until commit time, to guard against concurrent
+ * activity.
+ */
+ if (relation != NULL)
+ relation_close(relation, NoLock);
+}
+
+/*
+ * GetSecurityLabel returns the security label for a database object for a
+ * given provider, or NULL if there is no such label.
+ */
+char *
+GetSecurityLabel(const ObjectAddress *object, const char *provider)
+{
+ Relation pg_seclabel;
+ ScanKeyData keys[4];
+ SysScanDesc scan;
+ HeapTuple tuple;
+ Datum datum;
+ bool isnull;
+ char *seclabel = NULL;
+
+ Assert(!IsSharedRelation(object->classId));
+
+ ScanKeyInit(&keys[0],
+ Anum_pg_seclabel_objoid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+ ScanKeyInit(&keys[1],
+ Anum_pg_seclabel_classoid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->classId));
+ ScanKeyInit(&keys[2],
+ Anum_pg_seclabel_objsubid,
+ BTEqualStrategyNumber, F_INT4EQ,
+ Int32GetDatum(object->objectSubId));
+ ScanKeyInit(&keys[3],
+ Anum_pg_seclabel_provider,
+ BTEqualStrategyNumber, F_TEXTEQ,
+ CStringGetTextDatum(provider));
+
+ pg_seclabel = heap_open(SecLabelRelationId, AccessShareLock);
+
+ scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId, true,
+ SnapshotNow, 4, keys);
+
+ tuple = systable_getnext(scan);
+ if (HeapTupleIsValid(tuple))
+ {
+ datum = heap_getattr(tuple, Anum_pg_seclabel_label,
+ RelationGetDescr(pg_seclabel), &isnull);
+ if (!isnull)
+ seclabel = TextDatumGetCString(datum);
+ }
+ systable_endscan(scan);
+
+ heap_close(pg_seclabel, AccessShareLock);
+
+ return seclabel;
+}
+
+/*
+ * SetSecurityLabel attempts to set the security label for the specified
+ * provider on the specified object to the given value. NULL means that any
+ * any existing label should be deleted.
+ */
+void
+SetSecurityLabel(const ObjectAddress *object,
+ const char *provider, const char *label)
+{
+ Relation pg_seclabel;
+ ScanKeyData keys[4];
+ SysScanDesc scan;
+ HeapTuple oldtup;
+ HeapTuple newtup = NULL;
+ Datum values[Natts_pg_seclabel];
+ bool nulls[Natts_pg_seclabel];
+ bool replaces[Natts_pg_seclabel];
+
+ /* Security labels on shared objects are not supported. */
+ Assert(!IsSharedRelation(object->classId));
+
+ /* Prepare to form or update a tuple, if necessary. */
+ memset(nulls, false, sizeof(nulls));
+ memset(replaces, false, sizeof(replaces));
+ values[Anum_pg_seclabel_objoid - 1] = ObjectIdGetDatum(object->objectId);
+ values[Anum_pg_seclabel_classoid - 1] = ObjectIdGetDatum(object->classId);
+ values[Anum_pg_seclabel_objsubid - 1] = Int32GetDatum(object->objectSubId);
+ values[Anum_pg_seclabel_provider - 1] = CStringGetTextDatum(provider);
+ if (label != NULL)
+ values[Anum_pg_seclabel_label - 1] = CStringGetTextDatum(label);
+
+ /* Use the index to search for a matching old tuple */
+ ScanKeyInit(&keys[0],
+ Anum_pg_seclabel_objoid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+ ScanKeyInit(&keys[1],
+ Anum_pg_seclabel_classoid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->classId));
+ ScanKeyInit(&keys[2],
+ Anum_pg_seclabel_objsubid,
+ BTEqualStrategyNumber, F_INT4EQ,
+ Int32GetDatum(object->objectSubId));
+ ScanKeyInit(&keys[3],
+ Anum_pg_seclabel_provider,
+ BTEqualStrategyNumber, F_TEXTEQ,
+ CStringGetTextDatum(provider));
+
+ pg_seclabel = heap_open(SecLabelRelationId, RowExclusiveLock);
+
+ scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId, true,
+ SnapshotNow, 4, keys);
+
+ oldtup = systable_getnext(scan);
+ if (HeapTupleIsValid(oldtup))
+ {
+ if (label == NULL)
+ simple_heap_delete(pg_seclabel, &oldtup->t_self);
+ else
+ {
+ replaces[Anum_pg_seclabel_label - 1] = true;
+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(pg_seclabel),
+ values, nulls, replaces);
+ simple_heap_update(pg_seclabel, &oldtup->t_self, newtup);
+ }
+ }
+ systable_endscan(scan);
+
+ /* If we didn't find an old tuple, insert a new one */
+ if (newtup == NULL && label != NULL)
+ {
+ newtup = heap_form_tuple(RelationGetDescr(pg_seclabel),
+ values, nulls);
+ simple_heap_insert(pg_seclabel, newtup);
+ }
+
+ /* Update indexes, if necessary */
+ if (newtup != NULL)
+ {
+ CatalogUpdateIndexes(pg_seclabel, newtup);
+ heap_freetuple(newtup);
+ }
+
+ heap_close(pg_seclabel, RowExclusiveLock);
+}
+
+/*
+ * DeleteSecurityLabel removes all security labels for an object (and any
+ * sub-objects, if applicable).
+ */
+void
+DeleteSecurityLabel(const ObjectAddress *object)
+{
+ Relation pg_seclabel;
+ ScanKeyData skey[3];
+ SysScanDesc scan;
+ HeapTuple oldtup;
+ int nkeys;
+
+ /* Security labels on shared objects are not supported. */
+ if (IsSharedRelation(object->classId))
+ return;
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_seclabel_objoid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+ ScanKeyInit(&skey[1],
+ Anum_pg_seclabel_classoid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->classId));
+ if (object->objectSubId != 0)
+ {
+ ScanKeyInit(&skey[2],
+ Anum_pg_seclabel_objsubid,
+ BTEqualStrategyNumber, F_INT4EQ,
+ Int32GetDatum(object->objectSubId));
+ nkeys = 3;
+ }
+ else
+ nkeys = 2;
+
+ pg_seclabel = heap_open(SecLabelRelationId, RowExclusiveLock);
+
+ scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId, true,
+ SnapshotNow, nkeys, skey);
+ while (HeapTupleIsValid(oldtup = systable_getnext(scan)))
+ simple_heap_delete(pg_seclabel, &oldtup->t_self);
+ systable_endscan(scan);
+
+ heap_close(pg_seclabel, RowExclusiveLock);
+}
+
+/*
+ * Check whether the user is allowed to comment on an attribute of the
+ * specified relation.
+ */
+static void
+CheckAttributeSecLabel(Relation relation)
+{
+ if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+ RelationGetRelationName(relation));
+
+ /*
+ * Allow security labels only on columns of tables, views, and composite
+ * types (which are the only relkinds for which pg_dump will dump labels).
+ */
+ if (relation->rd_rel->relkind != RELKIND_RELATION &&
+ relation->rd_rel->relkind != RELKIND_VIEW &&
+ relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a table, view, or composite type",
+ RelationGetRelationName(relation))));
+}
+
+void
+register_label_provider(const char *provider_name, check_object_relabel_type hook)
+{
+ LabelProvider *provider;
+ MemoryContext oldcxt;
+
+ oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+ provider = palloc(sizeof(LabelProvider));
+ provider->provider_name = pstrdup(provider_name);
+ provider->hook = hook;
+ label_provider_list = lappend(label_provider_list, provider);
+ MemoryContextSwitchTo(oldcxt);
+}