summaryrefslogtreecommitdiff
path: root/contrib/sepgsql/label.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/sepgsql/label.c')
-rw-r--r--contrib/sepgsql/label.c528
1 files changed, 528 insertions, 0 deletions
diff --git a/contrib/sepgsql/label.c b/contrib/sepgsql/label.c
new file mode 100644
index 0000000000..669ee35ac3
--- /dev/null
+++ b/contrib/sepgsql/label.c
@@ -0,0 +1,528 @@
+/* -------------------------------------------------------------------------
+ *
+ * contrib/sepgsql/label.c
+ *
+ * Routines to support SELinux labels (security context)
+ *
+ * Copyright (c) 2010-2011, PostgreSQL Global Development Group
+ *
+ * -------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_proc.h"
+#include "commands/dbcommands.h"
+#include "commands/seclabel.h"
+#include "libpq/libpq-be.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+#include "utils/tqual.h"
+
+#include "sepgsql.h"
+
+#include <selinux/label.h>
+
+/*
+ * client_label
+ *
+ * security label of the client process
+ */
+static char *client_label = NULL;
+
+char *
+sepgsql_get_client_label(void)
+{
+ return client_label;
+}
+
+char *
+sepgsql_set_client_label(char *new_label)
+{
+ char *old_label = client_label;
+
+ client_label = new_label;
+
+ return old_label;
+}
+
+/*
+ * sepgsql_get_label
+ *
+ * It returns a security context of the specified database object.
+ * If unlabeled or incorrectly labeled, the system "unlabeled" label
+ * shall be returned.
+ */
+char *
+sepgsql_get_label(Oid classId, Oid objectId, int32 subId)
+{
+ ObjectAddress object;
+ char *label;
+
+ object.classId = classId;
+ object.objectId = objectId;
+ object.objectSubId = subId;
+
+ label = GetSecurityLabel(&object, SEPGSQL_LABEL_TAG);
+ if (!label || security_check_context_raw((security_context_t) label))
+ {
+ security_context_t unlabeled;
+
+ if (security_get_initial_context_raw("unlabeled", &unlabeled) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INTERNAL_ERROR),
+ errmsg("SELinux: failed to get initial security label: %m")));
+ PG_TRY();
+ {
+ label = pstrdup(unlabeled);
+ }
+ PG_CATCH();
+ {
+ freecon(unlabeled);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+
+ freecon(unlabeled);
+ }
+ return label;
+}
+
+/*
+ * sepgsql_object_relabel
+ *
+ * An entrypoint of SECURITY LABEL statement
+ */
+void
+sepgsql_object_relabel(const ObjectAddress *object, const char *seclabel)
+{
+ /*
+ * validate format of the supplied security label, if it is security
+ * context of selinux.
+ */
+ if (seclabel &&
+ security_check_context_raw((security_context_t) seclabel) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("SELinux: invalid security label: \"%s\"", seclabel)));
+
+ /*
+ * Do actual permission checks for each object classes
+ */
+ switch (object->classId)
+ {
+ case NamespaceRelationId:
+ sepgsql_schema_relabel(object->objectId, seclabel);
+ break;
+ case RelationRelationId:
+ if (object->objectSubId == 0)
+ sepgsql_relation_relabel(object->objectId,
+ seclabel);
+ else
+ sepgsql_attribute_relabel(object->objectId,
+ object->objectSubId,
+ seclabel);
+ break;
+ case ProcedureRelationId:
+ sepgsql_proc_relabel(object->objectId, seclabel);
+ break;
+
+ default:
+ elog(ERROR, "unsupported object type: %u", object->classId);
+ break;
+ }
+}
+
+/*
+ * TEXT sepgsql_getcon(VOID)
+ *
+ * It returns the security label of the client.
+ */
+PG_FUNCTION_INFO_V1(sepgsql_getcon);
+Datum
+sepgsql_getcon(PG_FUNCTION_ARGS)
+{
+ char *client_label;
+
+ if (!sepgsql_is_enabled())
+ PG_RETURN_NULL();
+
+ client_label = sepgsql_get_client_label();
+
+ PG_RETURN_TEXT_P(cstring_to_text(client_label));
+}
+
+/*
+ * TEXT sepgsql_mcstrans_in(TEXT)
+ *
+ * It translate the given qualified MLS/MCS range into raw format
+ * when mcstrans daemon is working.
+ */
+PG_FUNCTION_INFO_V1(sepgsql_mcstrans_in);
+Datum
+sepgsql_mcstrans_in(PG_FUNCTION_ARGS)
+{
+ text *label = PG_GETARG_TEXT_P(0);
+ char *raw_label;
+ char *result;
+
+ if (!sepgsql_is_enabled())
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("sepgsql is not enabled")));
+
+ if (selinux_trans_to_raw_context(text_to_cstring(label),
+ &raw_label) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INTERNAL_ERROR),
+ errmsg("SELinux: could not translate security label: %m")));
+
+ PG_TRY();
+ {
+ result = pstrdup(raw_label);
+ }
+ PG_CATCH();
+ {
+ freecon(raw_label);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ freecon(raw_label);
+
+ PG_RETURN_TEXT_P(cstring_to_text(result));
+}
+
+/*
+ * TEXT sepgsql_mcstrans_out(TEXT)
+ *
+ * It translate the given raw MLS/MCS range into qualified format
+ * when mcstrans daemon is working.
+ */
+PG_FUNCTION_INFO_V1(sepgsql_mcstrans_out);
+Datum
+sepgsql_mcstrans_out(PG_FUNCTION_ARGS)
+{
+ text *label = PG_GETARG_TEXT_P(0);
+ char *qual_label;
+ char *result;
+
+ if (!sepgsql_is_enabled())
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("sepgsql is not currently enabled")));
+
+ if (selinux_raw_to_trans_context(text_to_cstring(label),
+ &qual_label) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INTERNAL_ERROR),
+ errmsg("SELinux: could not translate security label: %m")));
+
+ PG_TRY();
+ {
+ result = pstrdup(qual_label);
+ }
+ PG_CATCH();
+ {
+ freecon(qual_label);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ freecon(qual_label);
+
+ PG_RETURN_TEXT_P(cstring_to_text(result));
+}
+
+/*
+ * quote_object_names
+ *
+ * It tries to quote the supplied identifiers
+ */
+static char *
+quote_object_name(const char *src1, const char *src2,
+ const char *src3, const char *src4)
+{
+ StringInfoData result;
+ const char *temp;
+
+ initStringInfo(&result);
+
+ if (src1)
+ {
+ temp = quote_identifier(src1);
+ appendStringInfo(&result, "%s", temp);
+ if (src1 != temp)
+ pfree((void *) temp);
+ }
+ if (src2)
+ {
+ temp = quote_identifier(src2);
+ appendStringInfo(&result, ".%s", temp);
+ if (src2 != temp)
+ pfree((void *) temp);
+ }
+ if (src3)
+ {
+ temp = quote_identifier(src3);
+ appendStringInfo(&result, ".%s", temp);
+ if (src3 != temp)
+ pfree((void *) temp);
+ }
+ if (src4)
+ {
+ temp = quote_identifier(src4);
+ appendStringInfo(&result, ".%s", temp);
+ if (src4 != temp)
+ pfree((void *) temp);
+ }
+ return result.data;
+}
+
+/*
+ * exec_object_restorecon
+ *
+ * This routine is a helper called by sepgsql_restorecon; it set up
+ * initial security labels of database objects within the supplied
+ * catalog OID.
+ */
+static void
+exec_object_restorecon(struct selabel_handle * sehnd, Oid catalogId)
+{
+ Relation rel;
+ SysScanDesc sscan;
+ HeapTuple tuple;
+ char *database_name = get_database_name(MyDatabaseId);
+ char *namespace_name;
+ Oid namespace_id;
+ char *relation_name;
+
+ /*
+ * Open the target catalog. We don't want to allow writable accesses by
+ * other session during initial labeling.
+ */
+ rel = heap_open(catalogId, AccessShareLock);
+
+ sscan = systable_beginscan(rel, InvalidOid, false,
+ SnapshotNow, 0, NULL);
+ while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
+ {
+ Form_pg_namespace nspForm;
+ Form_pg_class relForm;
+ Form_pg_attribute attForm;
+ Form_pg_proc proForm;
+ char *objname;
+ int objtype = 1234;
+ ObjectAddress object;
+ security_context_t context;
+
+ /*
+ * The way to determine object name depends on object classes. So, any
+ * branches set up `objtype', `objname' and `object' here.
+ */
+ switch (catalogId)
+ {
+ case NamespaceRelationId:
+ nspForm = (Form_pg_namespace) GETSTRUCT(tuple);
+
+ objtype = SELABEL_DB_SCHEMA;
+
+ objname = quote_object_name(database_name,
+ NameStr(nspForm->nspname),
+ NULL, NULL);
+
+ object.classId = NamespaceRelationId;
+ object.objectId = HeapTupleGetOid(tuple);
+ object.objectSubId = 0;
+ break;
+
+ case RelationRelationId:
+ relForm = (Form_pg_class) GETSTRUCT(tuple);
+
+ if (relForm->relkind == RELKIND_RELATION)
+ objtype = SELABEL_DB_TABLE;
+ else if (relForm->relkind == RELKIND_SEQUENCE)
+ objtype = SELABEL_DB_SEQUENCE;
+ else if (relForm->relkind == RELKIND_VIEW)
+ objtype = SELABEL_DB_VIEW;
+ else
+ continue; /* no need to assign security label */
+
+ namespace_name = get_namespace_name(relForm->relnamespace);
+ objname = quote_object_name(database_name,
+ namespace_name,
+ NameStr(relForm->relname),
+ NULL);
+ pfree(namespace_name);
+
+ object.classId = RelationRelationId;
+ object.objectId = HeapTupleGetOid(tuple);
+ object.objectSubId = 0;
+ break;
+
+ case AttributeRelationId:
+ attForm = (Form_pg_attribute) GETSTRUCT(tuple);
+
+ if (get_rel_relkind(attForm->attrelid) != RELKIND_RELATION)
+ continue; /* no need to assign security label */
+
+ objtype = SELABEL_DB_COLUMN;
+
+ namespace_id = get_rel_namespace(attForm->attrelid);
+ namespace_name = get_namespace_name(namespace_id);
+ relation_name = get_rel_name(attForm->attrelid);
+ objname = quote_object_name(database_name,
+ namespace_name,
+ relation_name,
+ NameStr(attForm->attname));
+ pfree(namespace_name);
+ pfree(relation_name);
+
+ object.classId = RelationRelationId;
+ object.objectId = attForm->attrelid;
+ object.objectSubId = attForm->attnum;
+ break;
+
+ case ProcedureRelationId:
+ proForm = (Form_pg_proc) GETSTRUCT(tuple);
+
+ objtype = SELABEL_DB_PROCEDURE;
+
+ namespace_name = get_namespace_name(proForm->pronamespace);
+ objname = quote_object_name(database_name,
+ namespace_name,
+ NameStr(proForm->proname),
+ NULL);
+ pfree(namespace_name);
+
+ object.classId = ProcedureRelationId;
+ object.objectId = HeapTupleGetOid(tuple);
+ object.objectSubId = 0;
+ break;
+
+ default:
+ elog(ERROR, "unexpected catalog id: %u", catalogId);
+ objname = NULL; /* for compiler quiet */
+ break;
+ }
+
+ if (selabel_lookup_raw(sehnd, &context, objname, objtype) == 0)
+ {
+ PG_TRY();
+ {
+ /*
+ * Check SELinux permission to relabel the fetched object,
+ * then do the actual relabeling.
+ */
+ sepgsql_object_relabel(&object, context);
+
+ SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, context);
+ }
+ PG_CATCH();
+ {
+ freecon(context);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ freecon(context);
+ }
+ else if (errno == ENOENT)
+ ereport(WARNING,
+ (errmsg("SELinux: no initial label assigned for %s (type=%d), skipping",
+ objname, objtype)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INTERNAL_ERROR),
+ errmsg("SELinux: could not determine initial security label for %s (type=%d): %m", objname, objtype)));
+
+ pfree(objname);
+ }
+ systable_endscan(sscan);
+
+ heap_close(rel, NoLock);
+}
+
+/*
+ * BOOL sepgsql_restorecon(TEXT specfile)
+ *
+ * This function tries to assign initial security labels on all the object
+ * within the current database, according to the system setting.
+ * It is typically invoked by sepgsql-install script just after initdb, to
+ * assign initial security labels.
+ *
+ * If @specfile is not NULL, it uses explicitly specified specfile, instead
+ * of the system default.
+ */
+PG_FUNCTION_INFO_V1(sepgsql_restorecon);
+Datum
+sepgsql_restorecon(PG_FUNCTION_ARGS)
+{
+ struct selabel_handle *sehnd;
+ struct selinux_opt seopts;
+
+ /*
+ * SELinux has to be enabled on the running platform.
+ */
+ if (!sepgsql_is_enabled())
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("sepgsql is not currently enabled")));
+
+ /*
+ * Check DAC permission. Only superuser can set up initial security
+ * labels, like root-user in filesystems
+ */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("SELinux: must be superuser to restore initial contexts")));
+
+ /*
+ * Open selabel_lookup(3) stuff. It provides a set of mapping between an
+ * initial security label and object class/name due to the system setting.
+ */
+ if (PG_ARGISNULL(0))
+ {
+ seopts.type = SELABEL_OPT_UNUSED;
+ seopts.value = NULL;
+ }
+ else
+ {
+ seopts.type = SELABEL_OPT_PATH;
+ seopts.value = TextDatumGetCString(PG_GETARG_DATUM(0));
+ }
+ sehnd = selabel_open(SELABEL_CTX_DB, &seopts, 1);
+ if (!sehnd)
+ ereport(ERROR,
+ (errcode(ERRCODE_INTERNAL_ERROR),
+ errmsg("SELinux: failed to initialize labeling handle: %m")));
+ PG_TRY();
+ {
+ /*
+ * Right now, we have no support labeling on the shared database
+ * objects, such as database, role, or tablespace.
+ */
+ exec_object_restorecon(sehnd, NamespaceRelationId);
+ exec_object_restorecon(sehnd, RelationRelationId);
+ exec_object_restorecon(sehnd, AttributeRelationId);
+ exec_object_restorecon(sehnd, ProcedureRelationId);
+ }
+ PG_CATCH();
+ {
+ selabel_close(sehnd);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+
+ selabel_close(sehnd);
+
+ PG_RETURN_BOOL(true);
+}