diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:17:33 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:17:33 +0000 |
commit | 5e45211a64149b3c659b90ff2de6fa982a5a93ed (patch) | |
tree | 739caf8c461053357daa9f162bef34516c7bf452 /contrib/sepgsql/label.c | |
parent | Initial commit. (diff) | |
download | postgresql-15-5e45211a64149b3c659b90ff2de6fa982a5a93ed.tar.xz postgresql-15-5e45211a64149b3c659b90ff2de6fa982a5a93ed.zip |
Adding upstream version 15.5.upstream/15.5
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'contrib/sepgsql/label.c')
-rw-r--r-- | contrib/sepgsql/label.c | 937 |
1 files changed, 937 insertions, 0 deletions
diff --git a/contrib/sepgsql/label.c b/contrib/sepgsql/label.c new file mode 100644 index 0000000..e4c98b7 --- /dev/null +++ b/contrib/sepgsql/label.c @@ -0,0 +1,937 @@ +/* ------------------------------------------------------------------------- + * + * contrib/sepgsql/label.c + * + * Routines to support SELinux labels (security context) + * + * Copyright (c) 2010-2022, 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 + * + * 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); + appendStringInfoString(&result, 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 = 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); +} |