1/* -------------------------------------------------------------------------
3 * contrib/sepgsql/label.c
5 * Routines to support SELinux labels (security context)
7 * Copyright (c) 2010-2025, PostgreSQL Global Development Group
9 * -------------------------------------------------------------------------
13#include <selinux/label.h>
32#include "utils/fmgroids.h"
39 * Saved hook entries (if stacked)
48 * security label of the database client. Initially the client security label
49 * is equal to client_label_peer, and can be changed by one or more calls to
50 * sepgsql_setcon(), and also be temporarily overridden during execution of a
53 * sepgsql_setcon() is a transaction-aware operation; a (sub-)transaction
54 * rollback should also rollback the current client security label. Therefore
55 * we use the list client_label_pending of pending_label to keep track of which
56 * labels were set during the (sub-)transactions.
62 * already committed */
72 * sepgsql_get_client_label
74 * Returns the current security label of the client. All code should use this
75 * routine to get the current label, instead of referring to the client_label_*
81 /* trusted procedure client label override */
85 /* uncommitted sepgsql_setcon() value */
102 * sepgsql_set_client_label
104 * This routine tries to switch the current security label of the client, and
105 * checks related permissions. The supplied new label shall be added to the
106 * client_label_pending list, then saved at transaction-commit time to ensure
107 * transaction-awareness.
112 const char *tcontext;
116 /* Reset to the initial client label, if NULL */
121 if (security_check_context_raw(new_label) < 0)
123 (
errcode(ERRCODE_INVALID_NAME),
124 errmsg(
"SELinux: invalid security label: \"%s\"",
126 tcontext = new_label;
129 /* Check process:{setcurrent} permission. */
135 /* Check process:{dyntransition} permission. */
143 * Append the supplied new_label on the pending list until the current
144 * transaction is committed.
158 * sepgsql_xact_callback
160 * A callback routine of transaction commit/abort/prepare. Commit or abort
161 * changes in the client_label_pending list.
185 * XXX - Note that items of client_label_pending are allocated on
186 * CurTransactionContext, thus, all acquired memory region shall
187 * be released implicitly.
197 * sepgsql_subxact_callback
199 * A callback routine of sub-transaction start/abort/commit. Releases all
200 * security labels that are set within the sub-transaction that is aborted.
214 if (plabel->
subid == mySubid)
222 * sepgsql_client_auth
224 * Entrypoint of the client authentication hook.
225 * It switches the client label according to getpeercon(), and the current
226 * performing mode according to the GUC setting.
232 (*next_client_auth_hook) (
port, status);
235 * In the case when authentication failed, the supplied socket shall be
236 * closed soon, so we don't need to do anything here.
242 * Getting security label of the peer process using API of libselinux.
246 (
errcode(ERRCODE_INTERNAL_ERROR),
247 errmsg(
"SELinux: unable to get peer label: %m")));
250 * Switch the current performing mode from INTERNAL to either DEFAULT or
260 * sepgsql_needs_fmgr_hook
262 * It informs the core whether the supplied function is trusted procedure,
263 * or not. If true, sepgsql_fmgr_hook shall be invoked at start, end, and
264 * abort time of function invocation.
276 * SELinux needs the function to be called via security_definer wrapper,
277 * if this invocation will take a domain-transition. We call these
278 * functions as trusted-procedure, if the security policy has a rule that
279 * switches security label of the client on execution.
285 * Even if not a trusted-procedure, this function should not be inlined
286 * unless the client has db_procedure:{execute} permission. Please note
287 * that it shall be actually failed later because of same reason with
290 object.
classId = ProcedureRelationId;
291 object.objectId = functionId;
292 object.objectSubId = 0;
306 * It switches security label of the client on execution of trusted
329 stack =
palloc(
sizeof(*stack));
330 stack->old_label = NULL;
332 stack->next_private = 0;
337 * process:transition permission between old and new label,
338 * when user tries to switch security label of the client on
339 * execution of trusted procedure.
341 * Also, db_procedure:entrypoint permission should be checked
342 * whether this procedure can perform as an entrypoint of the
343 * trusted procedure, or not. Note that db_procedure:execute
344 * permission shall be checked individually.
346 if (stack->new_label)
350 object.
classId = ProcedureRelationId;
351 object.objectId = flinfo->
fn_oid;
352 object.objectSubId = 0;
366 Assert(!stack->old_label);
367 if (stack->new_label)
373 (*next_fmgr_hook) (event, flinfo, &stack->next_private);
381 (*next_fmgr_hook) (event, flinfo, &stack->next_private);
383 if (stack->new_label)
386 stack->old_label = NULL;
391 elog(
ERROR,
"unexpected event type: %d", (
int) event);
397 * sepgsql_init_client_label
399 * Initializes the client security label and sets up related hooks for client
406 * Set up dummy client label.
408 * XXX - note that PostgreSQL launches background worker process like
409 * autovacuum without authentication steps. So, we initialize sepgsql_mode
410 * with SEPGSQL_MODE_INTERNAL, and client_label with the security context
411 * of server process. Later, it also launches background of user session.
412 * In this case, the process is always hooked on post-authentication, and
413 * we can initialize the sepgsql_mode and client_label correctly.
417 (
errcode(ERRCODE_INTERNAL_ERROR),
418 errmsg(
"SELinux: failed to get server security label: %m")));
420 /* Client authentication hook */
424 /* Trusted procedure hooks */
431 /* Transaction/Sub-transaction callbacks */
439 * It returns a security context of the specified database object.
440 * If unlabeled or incorrectly labeled, the system "unlabeled" label
449 object.classId = classId;
450 object.objectId = objectId;
451 object.objectSubId = subId;
454 if (!
label || security_check_context_raw(
label))
458 if (security_get_initial_context_raw(
"unlabeled", &unlabeled) < 0)
460 (
errcode(ERRCODE_INTERNAL_ERROR),
461 errmsg(
"SELinux: failed to get initial security label: %m")));
476 * sepgsql_object_relabel
478 * An entrypoint of SECURITY LABEL statement
484 * validate format of the supplied security label, if it is security
485 * context of selinux.
488 security_check_context_raw(seclabel) < 0)
490 (
errcode(ERRCODE_INVALID_NAME),
491 errmsg(
"SELinux: invalid security label: \"%s\"", seclabel)));
494 * Do actual permission checks for each object classes
498 case DatabaseRelationId:
502 case NamespaceRelationId:
506 case RelationRelationId:
516 case ProcedureRelationId:
522 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
523 errmsg(
"sepgsql provider does not support labels on %s",
530 * TEXT sepgsql_getcon(VOID)
532 * It returns the security label of the client.
549 * BOOL sepgsql_setcon(TEXT)
551 * It switches the security label of the client.
557 const char *new_label;
570 * TEXT sepgsql_mcstrans_in(TEXT)
572 * It translate the given qualified MLS/MCS range into raw format
573 * when mcstrans daemon is working.
585 (
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
586 errmsg(
"sepgsql is not enabled")));
591 (
errcode(ERRCODE_INTERNAL_ERROR),
592 errmsg(
"SELinux: could not translate security label: %m")));
608 * TEXT sepgsql_mcstrans_out(TEXT)
610 * It translate the given raw MLS/MCS range into qualified format
611 * when mcstrans daemon is working.
623 (
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
624 errmsg(
"sepgsql is not currently enabled")));
629 (
errcode(ERRCODE_INTERNAL_ERROR),
630 errmsg(
"SELinux: could not translate security label: %m")));
648 * Concatenate as many of the given strings as aren't NULL, with dots between.
649 * Quote any of the strings that wouldn't be valid identifiers otherwise.
653 const char *src3,
const char *src4)
670 * exec_object_restorecon
672 * This routine is a helper called by sepgsql_restorecon; it set up
673 * initial security labels of database objects within the supplied
683 char *namespace_name;
688 * Open the target catalog. We don't want to allow writable accesses by
689 * other session during initial labeling.
708 * The way to determine object name depends on object classes. So, any
709 * branches set up `objtype', `objname' and `object' here.
713 case DatabaseRelationId:
716 objtype = SELABEL_DB_DATABASE;
721 object.classId = DatabaseRelationId;
722 object.objectId = datForm->oid;
723 object.objectSubId = 0;
726 case NamespaceRelationId:
729 objtype = SELABEL_DB_SCHEMA;
735 object.classId = NamespaceRelationId;
736 object.objectId = nspForm->oid;
737 object.objectSubId = 0;
740 case RelationRelationId:
743 if (relForm->relkind == RELKIND_RELATION ||
744 relForm->relkind == RELKIND_PARTITIONED_TABLE)
745 objtype = SELABEL_DB_TABLE;
746 else if (relForm->relkind == RELKIND_SEQUENCE)
747 objtype = SELABEL_DB_SEQUENCE;
748 else if (relForm->relkind == RELKIND_VIEW)
749 objtype = SELABEL_DB_VIEW;
751 continue;
/* no need to assign security label */
758 pfree(namespace_name);
760 object.classId = RelationRelationId;
761 object.objectId = relForm->oid;
762 object.objectSubId = 0;
765 case AttributeRelationId:
770 continue;
/* no need to assign security label */
772 objtype = SELABEL_DB_COLUMN;
781 pfree(namespace_name);
782 pfree(relation_name);
784 object.classId = RelationRelationId;
785 object.objectId = attForm->attrelid;
786 object.objectSubId = attForm->attnum;
789 case ProcedureRelationId:
792 objtype = SELABEL_DB_PROCEDURE;
799 pfree(namespace_name);
801 object.classId = ProcedureRelationId;
802 object.objectId = proForm->oid;
803 object.objectSubId = 0;
807 elog(
ERROR,
"unexpected catalog id: %u", catalogId);
808 objname = NULL;
/* for compiler quiet */
812 if (selabel_lookup_raw(sehnd, &context, objname, objtype) == 0)
817 * Check SELinux permission to relabel the fetched object,
818 * then do the actual relabeling.
830 else if (errno == ENOENT)
832 (
errmsg(
"SELinux: no initial label assigned for %s (type=%d), skipping",
836 (
errcode(ERRCODE_INTERNAL_ERROR),
837 errmsg(
"SELinux: could not determine initial security label for %s (type=%d): %m", objname, objtype)));
847 * BOOL sepgsql_restorecon(TEXT specfile)
849 * This function tries to assign initial security labels on all the object
850 * within the current database, according to the system setting.
851 * It is typically invoked by sepgsql-install script just after initdb, to
852 * assign initial security labels.
854 * If @specfile is not NULL, it uses explicitly specified specfile, instead
855 * of the system default.
861 struct selabel_handle *sehnd;
862 struct selinux_opt seopts;
865 * SELinux has to be enabled on the running platform.
869 (
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
870 errmsg(
"sepgsql is not currently enabled")));
873 * Check DAC permission. Only superuser can set up initial security
874 * labels, like root-user in filesystems
878 (
errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
879 errmsg(
"SELinux: must be superuser to restore initial contexts")));
882 * Open selabel_lookup(3) stuff. It provides a set of mapping between an
883 * initial security label and object class/name due to the system setting.
887 seopts.type = SELABEL_OPT_UNUSED;
892 seopts.type = SELABEL_OPT_PATH;
895 sehnd = selabel_open(SELABEL_CTX_DB, &seopts, 1);
898 (
errcode(ERRCODE_INTERNAL_ERROR),
899 errmsg(
"SELinux: failed to initialize labeling handle: %m")));
910 selabel_close(sehnd);
ClientAuthentication_hook_type ClientAuthentication_hook
void(* ClientAuthentication_hook_type)(Port *, int)
#define TextDatumGetCString(d)
void sepgsql_proc_relabel(Oid functionId, const char *seclabel)
void sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum, const char *seclabel)
void sepgsql_relation_relabel(Oid relOid, const char *seclabel)
void sepgsql_database_relabel(Oid databaseId, const char *seclabel)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
PGDLLIMPORT needs_fmgr_hook_type needs_fmgr_hook
PGDLLIMPORT fmgr_hook_type fmgr_hook
#define PG_GETARG_TEXT_PP(n)
bool(* needs_fmgr_hook_type)(Oid fn_oid)
#define PG_GETARG_DATUM(n)
#define PG_RETURN_TEXT_P(x)
void(* fmgr_hook_type)(FmgrHookEventType event, FmgrInfo *flinfo, Datum *arg)
#define PG_RETURN_BOOL(x)
void systable_endscan(SysScanDesc sysscan)
HeapTuple systable_getnext(SysScanDesc sysscan)
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Assert(PointerIsAligned(start, uint64))
bool sepgsql_get_permissive(void)
#define HeapTupleIsValid(tuple)
static void * GETSTRUCT(const HeapTupleData *tuple)
Datum sepgsql_getcon(PG_FUNCTION_ARGS)
static fmgr_hook_type next_fmgr_hook
static void sepgsql_fmgr_hook(FmgrHookEventType event, FmgrInfo *flinfo, Datum *private)
void sepgsql_init_client_label(void)
static char * client_label_committed
static needs_fmgr_hook_type next_needs_fmgr_hook
static char * quote_object_name(const char *src1, const char *src2, const char *src3, const char *src4)
Datum sepgsql_mcstrans_out(PG_FUNCTION_ARGS)
static void exec_object_restorecon(struct selabel_handle *sehnd, Oid catalogId)
PG_FUNCTION_INFO_V1(sepgsql_getcon)
static char * client_label_peer
static List * client_label_pending
char * sepgsql_get_label(Oid classId, Oid objectId, int32 subId)
void sepgsql_object_relabel(const ObjectAddress *object, const char *seclabel)
Datum sepgsql_restorecon(PG_FUNCTION_ARGS)
static void sepgsql_subxact_callback(SubXactEvent event, SubTransactionId mySubid, SubTransactionId parentSubid, void *arg)
static void sepgsql_client_auth(Port *port, int status)
static char * client_label_func
char * sepgsql_get_client_label(void)
static void sepgsql_xact_callback(XactEvent event, void *arg)
Datum sepgsql_setcon(PG_FUNCTION_ARGS)
static ClientAuthentication_hook_type next_client_auth_hook
Datum sepgsql_mcstrans_in(PG_FUNCTION_ARGS)
static void sepgsql_set_client_label(const char *new_label)
static bool sepgsql_needs_fmgr_hook(Oid functionId)
List * lappend(List *list, void *datum)
char * get_rel_name(Oid relid)
char * get_database_name(Oid dbid)
char get_rel_relkind(Oid relid)
Oid get_rel_namespace(Oid relid)
char * get_namespace_name(Oid nspid)
char * MemoryContextStrdup(MemoryContext context, const char *string)
char * pstrdup(const char *in)
void pfree(void *pointer)
void * palloc0(Size size)
MemoryContext TopMemoryContext
MemoryContext CurTransactionContext
char * getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
char * getObjectDescription(const ObjectAddress *object, bool missing_ok)
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
FormData_pg_attribute * Form_pg_attribute
FormData_pg_class * Form_pg_class
FormData_pg_database * Form_pg_database
#define foreach_delete_current(lst, var_or_cell)
FormData_pg_namespace * Form_pg_namespace
FormData_pg_proc * Form_pg_proc
static Datum PointerGetDatum(const void *X)
static Pointer DatumGetPointer(Datum X)
const char * quote_identifier(const char *ident)
void sepgsql_schema_relabel(Oid namespaceId, const char *seclabel)
void SetSecurityLabel(const ObjectAddress *object, const char *provider, const char *label)
char * GetSecurityLabel(const ObjectAddress *object, const char *provider)
int sepgsql_set_mode(int new_mode)
bool sepgsql_is_enabled(void)
char * sepgsql_avc_trusted_proc(Oid functionId)
bool sepgsql_avc_check_perms_label(const char *tcontext, uint16 tclass, uint32 required, const char *audit_name, bool abort_on_violation)
#define SEPG_DB_PROCEDURE__EXECUTE
#define SEPG_PROCESS__SETCURRENT
#define SEPG_PROCESS__TRANSITION
#define SEPG_PROCESS__DYNTRANSITION
#define SEPG_DB_PROCEDURE__ENTRYPOINT
#define SEPG_CLASS_DB_PROCEDURE
#define SEPGSQL_AVC_NOAUDIT
#define SEPGSQL_LABEL_TAG
#define SEPGSQL_MODE_DEFAULT
#define SEPG_CLASS_PROCESS
#define SEPGSQL_MODE_PERMISSIVE
bool sepgsql_avc_check_perms(const ObjectAddress *tobject, uint16 tclass, uint32 required, const char *audit_name, bool abort_on_violation)
void appendStringInfo(StringInfo str, const char *fmt,...)
void appendStringInfoString(StringInfo str, const char *s)
void initStringInfo(StringInfo str)
void table_close(Relation relation, LOCKMODE lockmode)
Relation table_open(Oid relationId, LOCKMODE lockmode)
text * cstring_to_text(const char *s)
char * text_to_cstring(const text *t)
SubTransactionId GetCurrentSubTransactionId(void)
void RegisterXactCallback(XactCallback callback, void *arg)
void RegisterSubXactCallback(SubXactCallback callback, void *arg)
@ SUBXACT_EVENT_ABORT_SUB