summaryrefslogtreecommitdiffstats
path: root/contrib/sepgsql/label.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 13:44:03 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 13:44:03 +0000
commit293913568e6a7a86fd1479e1cff8e2ecb58d6568 (patch)
treefc3b469a3ec5ab71b36ea97cc7aaddb838423a0c /contrib/sepgsql/label.c
parentInitial commit. (diff)
downloadpostgresql-16-293913568e6a7a86fd1479e1cff8e2ecb58d6568.tar.xz
postgresql-16-293913568e6a7a86fd1479e1cff8e2ecb58d6568.zip
Adding upstream version 16.2.upstream/16.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'contrib/sepgsql/label.c')
-rw-r--r--contrib/sepgsql/label.c916
1 files changed, 916 insertions, 0 deletions
diff --git a/contrib/sepgsql/label.c b/contrib/sepgsql/label.c
new file mode 100644
index 0000000..38ff406
--- /dev/null
+++ b/contrib/sepgsql/label.c
@@ -0,0 +1,916 @@
+/* -------------------------------------------------------------------------
+ *
+ * contrib/sepgsql/label.c
+ *
+ * Routines to support SELinux labels (security context)
+ *
+ * Copyright (c) 2010-2023, PostgreSQL Global Development Group
+ *
+ * -------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <selinux/label.h>
+
+#include "access/genam.h"
+#include "access/htup_details.h"
+#include "access/table.h"
+#include "access/xact.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_database.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_proc.h"
+#include "commands/dbcommands.h"
+#include "commands/seclabel.h"
+#include "libpq/auth.h"
+#include "libpq/libpq-be.h"
+#include "miscadmin.h"
+#include "sepgsql.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/guc.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+
+/*
+ * Saved hook entries (if stacked)
+ */
+static ClientAuthentication_hook_type next_client_auth_hook = NULL;
+static needs_fmgr_hook_type next_needs_fmgr_hook = NULL;
+static fmgr_hook_type next_fmgr_hook = NULL;
+
+/*
+ * client_label_*
+ *
+ * security label of the database client. Initially the client security label
+ * is equal to client_label_peer, and can be changed by one or more calls to
+ * sepgsql_setcon(), and also be temporarily overridden during execution of a
+ * trusted-procedure.
+ *
+ * sepgsql_setcon() is a transaction-aware operation; a (sub-)transaction
+ * rollback should also rollback the current client security label. Therefore
+ * we use the list client_label_pending of pending_label to keep track of which
+ * labels were set during the (sub-)transactions.
+ */
+static char *client_label_peer = NULL; /* set by getpeercon(3) */
+static List *client_label_pending = NIL; /* pending list being set by
+ * sepgsql_setcon() */
+static char *client_label_committed = NULL; /* set by sepgsql_setcon(), and
+ * already committed */
+static char *client_label_func = NULL; /* set by trusted procedure */
+
+typedef struct
+{
+ SubTransactionId subid;
+ char *label;
+} pending_label;
+
+/*
+ * sepgsql_get_client_label
+ *
+ * Returns the current security label of the client. All code should use this
+ * routine to get the current label, instead of referring to the client_label_*
+ * variables above.
+ */
+char *
+sepgsql_get_client_label(void)
+{
+ /* trusted procedure client label override */
+ if (client_label_func)
+ return client_label_func;
+
+ /* uncommitted sepgsql_setcon() value */
+ if (client_label_pending)
+ {
+ pending_label *plabel = llast(client_label_pending);
+
+ if (plabel->label)
+ return plabel->label;
+ }
+ else if (client_label_committed)
+ return client_label_committed; /* set by sepgsql_setcon() committed */
+
+ /* default label */
+ Assert(client_label_peer != NULL);
+ return client_label_peer;
+}
+
+/*
+ * sepgsql_set_client_label
+ *
+ * This routine tries to switch the current security label of the client, and
+ * checks related permissions. The supplied new label shall be added to the
+ * client_label_pending list, then saved at transaction-commit time to ensure
+ * transaction-awareness.
+ */
+static void
+sepgsql_set_client_label(const char *new_label)
+{
+ const char *tcontext;
+ MemoryContext oldcxt;
+ pending_label *plabel;
+
+ /* Reset to the initial client label, if NULL */
+ if (!new_label)
+ tcontext = client_label_peer;
+ else
+ {
+ if (security_check_context_raw(new_label) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("SELinux: invalid security label: \"%s\"",
+ new_label)));
+ tcontext = new_label;
+ }
+
+ /* Check process:{setcurrent} permission. */
+ sepgsql_avc_check_perms_label(sepgsql_get_client_label(),
+ SEPG_CLASS_PROCESS,
+ SEPG_PROCESS__SETCURRENT,
+ NULL,
+ true);
+ /* Check process:{dyntransition} permission. */
+ sepgsql_avc_check_perms_label(tcontext,
+ SEPG_CLASS_PROCESS,
+ SEPG_PROCESS__DYNTRANSITION,
+ NULL,
+ true);
+
+ /*
+ * Append the supplied new_label on the pending list until the current
+ * transaction is committed.
+ */
+ oldcxt = MemoryContextSwitchTo(CurTransactionContext);
+
+ plabel = palloc0(sizeof(pending_label));
+ plabel->subid = GetCurrentSubTransactionId();
+ if (new_label)
+ plabel->label = pstrdup(new_label);
+ client_label_pending = lappend(client_label_pending, plabel);
+
+ MemoryContextSwitchTo(oldcxt);
+}
+
+/*
+ * sepgsql_xact_callback
+ *
+ * A callback routine of transaction commit/abort/prepare. Commit or abort
+ * changes in the client_label_pending list.
+ */
+static void
+sepgsql_xact_callback(XactEvent event, void *arg)
+{
+ if (event == XACT_EVENT_COMMIT)
+ {
+ if (client_label_pending != NIL)
+ {
+ pending_label *plabel = llast(client_label_pending);
+ char *new_label;
+
+ if (plabel->label)
+ new_label = MemoryContextStrdup(TopMemoryContext,
+ plabel->label);
+ else
+ new_label = NULL;
+
+ if (client_label_committed)
+ pfree(client_label_committed);
+
+ client_label_committed = new_label;
+
+ /*
+ * XXX - Note that items of client_label_pending are allocated on
+ * CurTransactionContext, thus, all acquired memory region shall
+ * be released implicitly.
+ */
+ client_label_pending = NIL;
+ }
+ }
+ else if (event == XACT_EVENT_ABORT)
+ client_label_pending = NIL;
+}
+
+/*
+ * sepgsql_subxact_callback
+ *
+ * A callback routine of sub-transaction start/abort/commit. Releases all
+ * security labels that are set within the sub-transaction that is aborted.
+ */
+static void
+sepgsql_subxact_callback(SubXactEvent event, SubTransactionId mySubid,
+ SubTransactionId parentSubid, void *arg)
+{
+ ListCell *cell;
+
+ if (event == SUBXACT_EVENT_ABORT_SUB)
+ {
+ foreach(cell, client_label_pending)
+ {
+ pending_label *plabel = lfirst(cell);
+
+ if (plabel->subid == mySubid)
+ client_label_pending
+ = foreach_delete_current(client_label_pending, cell);
+ }
+ }
+}
+
+/*
+ * sepgsql_client_auth
+ *
+ * Entrypoint of the client authentication hook.
+ * It switches the client label according to getpeercon(), and the current
+ * performing mode according to the GUC setting.
+ */
+static void
+sepgsql_client_auth(Port *port, int status)
+{
+ if (next_client_auth_hook)
+ (*next_client_auth_hook) (port, status);
+
+ /*
+ * In the case when authentication failed, the supplied socket shall be
+ * closed soon, so we don't need to do anything here.
+ */
+ if (status != STATUS_OK)
+ return;
+
+ /*
+ * Getting security label of the peer process using API of libselinux.
+ */
+ if (getpeercon_raw(port->sock, &client_label_peer) < 0)
+ ereport(FATAL,
+ (errcode(ERRCODE_INTERNAL_ERROR),
+ errmsg("SELinux: unable to get peer label: %m")));
+
+ /*
+ * Switch the current performing mode from INTERNAL to either DEFAULT or
+ * PERMISSIVE.
+ */
+ if (sepgsql_get_permissive())
+ sepgsql_set_mode(SEPGSQL_MODE_PERMISSIVE);
+ else
+ sepgsql_set_mode(SEPGSQL_MODE_DEFAULT);
+}
+
+/*
+ * sepgsql_needs_fmgr_hook
+ *
+ * It informs the core whether the supplied function is trusted procedure,
+ * or not. If true, sepgsql_fmgr_hook shall be invoked at start, end, and
+ * abort time of function invocation.
+ */
+static bool
+sepgsql_needs_fmgr_hook(Oid functionId)
+{
+ ObjectAddress object;
+
+ if (next_needs_fmgr_hook &&
+ (*next_needs_fmgr_hook) (functionId))
+ return true;
+
+ /*
+ * SELinux needs the function to be called via security_definer wrapper,
+ * if this invocation will take a domain-transition. We call these
+ * functions as trusted-procedure, if the security policy has a rule that
+ * switches security label of the client on execution.
+ */
+ if (sepgsql_avc_trusted_proc(functionId) != NULL)
+ return true;
+
+ /*
+ * Even if not a trusted-procedure, this function should not be inlined
+ * unless the client has db_procedure:{execute} permission. Please note
+ * that it shall be actually failed later because of same reason with
+ * ACL_EXECUTE.
+ */
+ object.classId = ProcedureRelationId;
+ object.objectId = functionId;
+ object.objectSubId = 0;
+ if (!sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_PROCEDURE,
+ SEPG_DB_PROCEDURE__EXECUTE |
+ SEPG_DB_PROCEDURE__ENTRYPOINT,
+ SEPGSQL_AVC_NOAUDIT, false))
+ return true;
+
+ return false;
+}
+
+/*
+ * sepgsql_fmgr_hook
+ *
+ * It switches security label of the client on execution of trusted
+ * procedures.
+ */
+static void
+sepgsql_fmgr_hook(FmgrHookEventType event,
+ FmgrInfo *flinfo, Datum *private)
+{
+ struct
+ {
+ char *old_label;
+ char *new_label;
+ Datum next_private;
+ } *stack;
+
+ switch (event)
+ {
+ case FHET_START:
+ stack = (void *) DatumGetPointer(*private);
+ if (!stack)
+ {
+ MemoryContext oldcxt;
+
+ oldcxt = MemoryContextSwitchTo(flinfo->fn_mcxt);
+ stack = palloc(sizeof(*stack));
+ stack->old_label = NULL;
+ stack->new_label = sepgsql_avc_trusted_proc(flinfo->fn_oid);
+ stack->next_private = 0;
+
+ MemoryContextSwitchTo(oldcxt);
+
+ /*
+ * process:transition permission between old and new label,
+ * when user tries to switch security label of the client on
+ * execution of trusted procedure.
+ *
+ * Also, db_procedure:entrypoint permission should be checked
+ * whether this procedure can perform as an entrypoint of the
+ * trusted procedure, or not. Note that db_procedure:execute
+ * permission shall be checked individually.
+ */
+ if (stack->new_label)
+ {
+ ObjectAddress object;
+
+ object.classId = ProcedureRelationId;
+ object.objectId = flinfo->fn_oid;
+ object.objectSubId = 0;
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_PROCEDURE,
+ SEPG_DB_PROCEDURE__ENTRYPOINT,
+ getObjectDescription(&object, false),
+ true);
+
+ sepgsql_avc_check_perms_label(stack->new_label,
+ SEPG_CLASS_PROCESS,
+ SEPG_PROCESS__TRANSITION,
+ NULL, true);
+ }
+ *private = PointerGetDatum(stack);
+ }
+ Assert(!stack->old_label);
+ if (stack->new_label)
+ {
+ stack->old_label = client_label_func;
+ client_label_func = stack->new_label;
+ }
+ if (next_fmgr_hook)
+ (*next_fmgr_hook) (event, flinfo, &stack->next_private);
+ break;
+
+ case FHET_END:
+ case FHET_ABORT:
+ stack = (void *) DatumGetPointer(*private);
+
+ if (next_fmgr_hook)
+ (*next_fmgr_hook) (event, flinfo, &stack->next_private);
+
+ if (stack->new_label)
+ {
+ client_label_func = stack->old_label;
+ stack->old_label = NULL;
+ }
+ break;
+
+ default:
+ elog(ERROR, "unexpected event type: %d", (int) event);
+ break;
+ }
+}
+
+/*
+ * sepgsql_init_client_label
+ *
+ * Initializes the client security label and sets up related hooks for client
+ * label management.
+ */
+void
+sepgsql_init_client_label(void)
+{
+ /*
+ * Set up dummy client label.
+ *
+ * XXX - note that PostgreSQL launches background worker process like
+ * autovacuum without authentication steps. So, we initialize sepgsql_mode
+ * with SEPGSQL_MODE_INTERNAL, and client_label with the security context
+ * of server process. Later, it also launches background of user session.
+ * In this case, the process is always hooked on post-authentication, and
+ * we can initialize the sepgsql_mode and client_label correctly.
+ */
+ if (getcon_raw(&client_label_peer) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INTERNAL_ERROR),
+ errmsg("SELinux: failed to get server security label: %m")));
+
+ /* Client authentication hook */
+ next_client_auth_hook = ClientAuthentication_hook;
+ ClientAuthentication_hook = sepgsql_client_auth;
+
+ /* Trusted procedure hooks */
+ next_needs_fmgr_hook = needs_fmgr_hook;
+ needs_fmgr_hook = sepgsql_needs_fmgr_hook;
+
+ next_fmgr_hook = fmgr_hook;
+ fmgr_hook = sepgsql_fmgr_hook;
+
+ /* Transaction/Sub-transaction callbacks */
+ RegisterXactCallback(sepgsql_xact_callback, NULL);
+ RegisterSubXactCallback(sepgsql_subxact_callback, NULL);
+}
+
+/*
+ * 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(label))
+ {
+ char *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_FINALLY();
+ {
+ freecon(unlabeled);
+ }
+ PG_END_TRY();
+ }
+ 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(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 DatabaseRelationId:
+ sepgsql_database_relabel(object->objectId, seclabel);
+ break;
+
+ 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:
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("sepgsql provider does not support labels on %s",
+ getObjectTypeDescription(object, false))));
+ 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));
+}
+
+/*
+ * BOOL sepgsql_setcon(TEXT)
+ *
+ * It switches the security label of the client.
+ */
+PG_FUNCTION_INFO_V1(sepgsql_setcon);
+Datum
+sepgsql_setcon(PG_FUNCTION_ARGS)
+{
+ const char *new_label;
+
+ if (PG_ARGISNULL(0))
+ new_label = NULL;
+ else
+ new_label = TextDatumGetCString(PG_GETARG_DATUM(0));
+
+ sepgsql_set_client_label(new_label);
+
+ PG_RETURN_BOOL(true);
+}
+
+/*
+ * 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_PP(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_FINALLY();
+ {
+ freecon(raw_label);
+ }
+ PG_END_TRY();
+
+ 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_PP(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_FINALLY();
+ {
+ freecon(qual_label);
+ }
+ PG_END_TRY();
+
+ PG_RETURN_TEXT_P(cstring_to_text(result));
+}
+
+/*
+ * quote_object_name
+ *
+ * Concatenate as many of the given strings as aren't NULL, with dots between.
+ * Quote any of the strings that wouldn't be valid identifiers otherwise.
+ */
+static char *
+quote_object_name(const char *src1, const char *src2,
+ const char *src3, const char *src4)
+{
+ StringInfoData result;
+
+ initStringInfo(&result);
+ if (src1)
+ appendStringInfoString(&result, quote_identifier(src1));
+ if (src2)
+ appendStringInfo(&result, ".%s", quote_identifier(src2));
+ if (src3)
+ appendStringInfo(&result, ".%s", quote_identifier(src3));
+ if (src4)
+ appendStringInfo(&result, ".%s", quote_identifier(src4));
+ 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 = table_open(catalogId, AccessShareLock);
+
+ sscan = systable_beginscan(rel, InvalidOid, false,
+ NULL, 0, NULL);
+ while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
+ {
+ Form_pg_database datForm;
+ Form_pg_namespace nspForm;
+ Form_pg_class relForm;
+ Form_pg_attribute attForm;
+ Form_pg_proc proForm;
+ char *objname;
+ int objtype = 1234;
+ ObjectAddress object;
+ char *context;
+
+ /*
+ * The way to determine object name depends on object classes. So, any
+ * branches set up `objtype', `objname' and `object' here.
+ */
+ switch (catalogId)
+ {
+ case DatabaseRelationId:
+ datForm = (Form_pg_database) GETSTRUCT(tuple);
+
+ objtype = SELABEL_DB_DATABASE;
+
+ objname = quote_object_name(NameStr(datForm->datname),
+ NULL, NULL, NULL);
+
+ object.classId = DatabaseRelationId;
+ object.objectId = datForm->oid;
+ object.objectSubId = 0;
+ break;
+
+ 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 = nspForm->oid;
+ object.objectSubId = 0;
+ break;
+
+ case RelationRelationId:
+ relForm = (Form_pg_class) GETSTRUCT(tuple);
+
+ if (relForm->relkind == RELKIND_RELATION ||
+ relForm->relkind == RELKIND_PARTITIONED_TABLE)
+ 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 = relForm->oid;
+ object.objectSubId = 0;
+ break;
+
+ case AttributeRelationId:
+ attForm = (Form_pg_attribute) GETSTRUCT(tuple);
+
+ if (get_rel_relkind(attForm->attrelid) != RELKIND_RELATION &&
+ get_rel_relkind(attForm->attrelid) != RELKIND_PARTITIONED_TABLE)
+ 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 = proForm->oid;
+ 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_FINALLY();
+ {
+ freecon(context);
+ }
+ PG_END_TRY();
+ }
+ 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);
+
+ table_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();
+ {
+ exec_object_restorecon(sehnd, DatabaseRelationId);
+ exec_object_restorecon(sehnd, NamespaceRelationId);
+ exec_object_restorecon(sehnd, RelationRelationId);
+ exec_object_restorecon(sehnd, AttributeRelationId);
+ exec_object_restorecon(sehnd, ProcedureRelationId);
+ }
+ PG_FINALLY();
+ {
+ selabel_close(sehnd);
+ }
+ PG_END_TRY();
+
+ PG_RETURN_BOOL(true);
+}