1 /* -------------------------------------------------------------------------
3 * contrib/sepgsql/label.c
5 * Routines to support SELinux labels (security context)
7 * Copyright (c) 2010-2024, PostgreSQL Global Development Group
9 * -------------------------------------------------------------------------
13 #include <selinux/label.h>
15 #include "access/genam.h"
16 #include "access/htup_details.h"
17 #include "access/table.h"
18 #include "access/xact.h"
19 #include "catalog/catalog.h"
20 #include "catalog/dependency.h"
21 #include "catalog/pg_attribute.h"
22 #include "catalog/pg_class.h"
23 #include "catalog/pg_database.h"
24 #include "catalog/pg_namespace.h"
25 #include "catalog/pg_proc.h"
26 #include "commands/dbcommands.h"
27 #include "commands/seclabel.h"
28 #include "libpq/auth.h"
29 #include "libpq/libpq-be.h"
30 #include "miscadmin.h"
32 #include "utils/builtins.h"
33 #include "utils/fmgroids.h"
34 #include "utils/guc.h"
35 #include "utils/lsyscache.h"
36 #include "utils/memutils.h"
37 #include "utils/rel.h"
40 * Saved hook entries (if stacked)
42 static ClientAuthentication_hook_type next_client_auth_hook
= NULL
;
43 static needs_fmgr_hook_type next_needs_fmgr_hook
= NULL
;
44 static fmgr_hook_type next_fmgr_hook
= NULL
;
49 * security label of the database client. Initially the client security label
50 * is equal to client_label_peer, and can be changed by one or more calls to
51 * sepgsql_setcon(), and also be temporarily overridden during execution of a
54 * sepgsql_setcon() is a transaction-aware operation; a (sub-)transaction
55 * rollback should also rollback the current client security label. Therefore
56 * we use the list client_label_pending of pending_label to keep track of which
57 * labels were set during the (sub-)transactions.
59 static char *client_label_peer
= NULL
; /* set by getpeercon(3) */
60 static List
*client_label_pending
= NIL
; /* pending list being set by
62 static char *client_label_committed
= NULL
; /* set by sepgsql_setcon(), and
63 * already committed */
64 static char *client_label_func
= NULL
; /* set by trusted procedure */
68 SubTransactionId subid
;
73 * sepgsql_get_client_label
75 * Returns the current security label of the client. All code should use this
76 * routine to get the current label, instead of referring to the client_label_*
80 sepgsql_get_client_label(void)
82 /* trusted procedure client label override */
83 if (client_label_func
)
84 return client_label_func
;
86 /* uncommitted sepgsql_setcon() value */
87 if (client_label_pending
)
89 pending_label
*plabel
= llast(client_label_pending
);
94 else if (client_label_committed
)
95 return client_label_committed
; /* set by sepgsql_setcon() committed */
98 Assert(client_label_peer
!= NULL
);
99 return client_label_peer
;
103 * sepgsql_set_client_label
105 * This routine tries to switch the current security label of the client, and
106 * checks related permissions. The supplied new label shall be added to the
107 * client_label_pending list, then saved at transaction-commit time to ensure
108 * transaction-awareness.
111 sepgsql_set_client_label(const char *new_label
)
113 const char *tcontext
;
114 MemoryContext oldcxt
;
115 pending_label
*plabel
;
117 /* Reset to the initial client label, if NULL */
119 tcontext
= client_label_peer
;
122 if (security_check_context_raw(new_label
) < 0)
124 (errcode(ERRCODE_INVALID_NAME
),
125 errmsg("SELinux: invalid security label: \"%s\"",
127 tcontext
= new_label
;
130 /* Check process:{setcurrent} permission. */
131 sepgsql_avc_check_perms_label(sepgsql_get_client_label(),
133 SEPG_PROCESS__SETCURRENT
,
136 /* Check process:{dyntransition} permission. */
137 sepgsql_avc_check_perms_label(tcontext
,
139 SEPG_PROCESS__DYNTRANSITION
,
144 * Append the supplied new_label on the pending list until the current
145 * transaction is committed.
147 oldcxt
= MemoryContextSwitchTo(CurTransactionContext
);
149 plabel
= palloc0(sizeof(pending_label
));
150 plabel
->subid
= GetCurrentSubTransactionId();
152 plabel
->label
= pstrdup(new_label
);
153 client_label_pending
= lappend(client_label_pending
, plabel
);
155 MemoryContextSwitchTo(oldcxt
);
159 * sepgsql_xact_callback
161 * A callback routine of transaction commit/abort/prepare. Commit or abort
162 * changes in the client_label_pending list.
165 sepgsql_xact_callback(XactEvent event
, void *arg
)
167 if (event
== XACT_EVENT_COMMIT
)
169 if (client_label_pending
!= NIL
)
171 pending_label
*plabel
= llast(client_label_pending
);
175 new_label
= MemoryContextStrdup(TopMemoryContext
,
180 if (client_label_committed
)
181 pfree(client_label_committed
);
183 client_label_committed
= new_label
;
186 * XXX - Note that items of client_label_pending are allocated on
187 * CurTransactionContext, thus, all acquired memory region shall
188 * be released implicitly.
190 client_label_pending
= NIL
;
193 else if (event
== XACT_EVENT_ABORT
)
194 client_label_pending
= NIL
;
198 * sepgsql_subxact_callback
200 * A callback routine of sub-transaction start/abort/commit. Releases all
201 * security labels that are set within the sub-transaction that is aborted.
204 sepgsql_subxact_callback(SubXactEvent event
, SubTransactionId mySubid
,
205 SubTransactionId parentSubid
, void *arg
)
209 if (event
== SUBXACT_EVENT_ABORT_SUB
)
211 foreach(cell
, client_label_pending
)
213 pending_label
*plabel
= lfirst(cell
);
215 if (plabel
->subid
== mySubid
)
217 = foreach_delete_current(client_label_pending
, cell
);
223 * sepgsql_client_auth
225 * Entrypoint of the client authentication hook.
226 * It switches the client label according to getpeercon(), and the current
227 * performing mode according to the GUC setting.
230 sepgsql_client_auth(Port
*port
, int status
)
232 if (next_client_auth_hook
)
233 (*next_client_auth_hook
) (port
, status
);
236 * In the case when authentication failed, the supplied socket shall be
237 * closed soon, so we don't need to do anything here.
239 if (status
!= STATUS_OK
)
243 * Getting security label of the peer process using API of libselinux.
245 if (getpeercon_raw(port
->sock
, &client_label_peer
) < 0)
247 (errcode(ERRCODE_INTERNAL_ERROR
),
248 errmsg("SELinux: unable to get peer label: %m")));
251 * Switch the current performing mode from INTERNAL to either DEFAULT or
254 if (sepgsql_get_permissive())
255 sepgsql_set_mode(SEPGSQL_MODE_PERMISSIVE
);
257 sepgsql_set_mode(SEPGSQL_MODE_DEFAULT
);
261 * sepgsql_needs_fmgr_hook
263 * It informs the core whether the supplied function is trusted procedure,
264 * or not. If true, sepgsql_fmgr_hook shall be invoked at start, end, and
265 * abort time of function invocation.
268 sepgsql_needs_fmgr_hook(Oid functionId
)
270 ObjectAddress object
;
272 if (next_needs_fmgr_hook
&&
273 (*next_needs_fmgr_hook
) (functionId
))
277 * SELinux needs the function to be called via security_definer wrapper,
278 * if this invocation will take a domain-transition. We call these
279 * functions as trusted-procedure, if the security policy has a rule that
280 * switches security label of the client on execution.
282 if (sepgsql_avc_trusted_proc(functionId
) != NULL
)
286 * Even if not a trusted-procedure, this function should not be inlined
287 * unless the client has db_procedure:{execute} permission. Please note
288 * that it shall be actually failed later because of same reason with
291 object
.classId
= ProcedureRelationId
;
292 object
.objectId
= functionId
;
293 object
.objectSubId
= 0;
294 if (!sepgsql_avc_check_perms(&object
,
295 SEPG_CLASS_DB_PROCEDURE
,
296 SEPG_DB_PROCEDURE__EXECUTE
|
297 SEPG_DB_PROCEDURE__ENTRYPOINT
,
298 SEPGSQL_AVC_NOAUDIT
, false))
307 * It switches security label of the client on execution of trusted
311 sepgsql_fmgr_hook(FmgrHookEventType event
,
312 FmgrInfo
*flinfo
, Datum
*private)
324 stack
= (void *) DatumGetPointer(*private);
327 MemoryContext oldcxt
;
329 oldcxt
= MemoryContextSwitchTo(flinfo
->fn_mcxt
);
330 stack
= palloc(sizeof(*stack
));
331 stack
->old_label
= NULL
;
332 stack
->new_label
= sepgsql_avc_trusted_proc(flinfo
->fn_oid
);
333 stack
->next_private
= 0;
335 MemoryContextSwitchTo(oldcxt
);
338 * process:transition permission between old and new label,
339 * when user tries to switch security label of the client on
340 * execution of trusted procedure.
342 * Also, db_procedure:entrypoint permission should be checked
343 * whether this procedure can perform as an entrypoint of the
344 * trusted procedure, or not. Note that db_procedure:execute
345 * permission shall be checked individually.
347 if (stack
->new_label
)
349 ObjectAddress object
;
351 object
.classId
= ProcedureRelationId
;
352 object
.objectId
= flinfo
->fn_oid
;
353 object
.objectSubId
= 0;
354 sepgsql_avc_check_perms(&object
,
355 SEPG_CLASS_DB_PROCEDURE
,
356 SEPG_DB_PROCEDURE__ENTRYPOINT
,
357 getObjectDescription(&object
, false),
360 sepgsql_avc_check_perms_label(stack
->new_label
,
362 SEPG_PROCESS__TRANSITION
,
365 *private = PointerGetDatum(stack
);
367 Assert(!stack
->old_label
);
368 if (stack
->new_label
)
370 stack
->old_label
= client_label_func
;
371 client_label_func
= stack
->new_label
;
374 (*next_fmgr_hook
) (event
, flinfo
, &stack
->next_private
);
379 stack
= (void *) DatumGetPointer(*private);
382 (*next_fmgr_hook
) (event
, flinfo
, &stack
->next_private
);
384 if (stack
->new_label
)
386 client_label_func
= stack
->old_label
;
387 stack
->old_label
= NULL
;
392 elog(ERROR
, "unexpected event type: %d", (int) event
);
398 * sepgsql_init_client_label
400 * Initializes the client security label and sets up related hooks for client
404 sepgsql_init_client_label(void)
407 * Set up dummy client label.
409 * XXX - note that PostgreSQL launches background worker process like
410 * autovacuum without authentication steps. So, we initialize sepgsql_mode
411 * with SEPGSQL_MODE_INTERNAL, and client_label with the security context
412 * of server process. Later, it also launches background of user session.
413 * In this case, the process is always hooked on post-authentication, and
414 * we can initialize the sepgsql_mode and client_label correctly.
416 if (getcon_raw(&client_label_peer
) < 0)
418 (errcode(ERRCODE_INTERNAL_ERROR
),
419 errmsg("SELinux: failed to get server security label: %m")));
421 /* Client authentication hook */
422 next_client_auth_hook
= ClientAuthentication_hook
;
423 ClientAuthentication_hook
= sepgsql_client_auth
;
425 /* Trusted procedure hooks */
426 next_needs_fmgr_hook
= needs_fmgr_hook
;
427 needs_fmgr_hook
= sepgsql_needs_fmgr_hook
;
429 next_fmgr_hook
= fmgr_hook
;
430 fmgr_hook
= sepgsql_fmgr_hook
;
432 /* Transaction/Sub-transaction callbacks */
433 RegisterXactCallback(sepgsql_xact_callback
, NULL
);
434 RegisterSubXactCallback(sepgsql_subxact_callback
, NULL
);
440 * It returns a security context of the specified database object.
441 * If unlabeled or incorrectly labeled, the system "unlabeled" label
445 sepgsql_get_label(Oid classId
, Oid objectId
, int32 subId
)
447 ObjectAddress object
;
450 object
.classId
= classId
;
451 object
.objectId
= objectId
;
452 object
.objectSubId
= subId
;
454 label
= GetSecurityLabel(&object
, SEPGSQL_LABEL_TAG
);
455 if (!label
|| security_check_context_raw(label
))
459 if (security_get_initial_context_raw("unlabeled", &unlabeled
) < 0)
461 (errcode(ERRCODE_INTERNAL_ERROR
),
462 errmsg("SELinux: failed to get initial security label: %m")));
465 label
= pstrdup(unlabeled
);
477 * sepgsql_object_relabel
479 * An entrypoint of SECURITY LABEL statement
482 sepgsql_object_relabel(const ObjectAddress
*object
, const char *seclabel
)
485 * validate format of the supplied security label, if it is security
486 * context of selinux.
489 security_check_context_raw(seclabel
) < 0)
491 (errcode(ERRCODE_INVALID_NAME
),
492 errmsg("SELinux: invalid security label: \"%s\"", seclabel
)));
495 * Do actual permission checks for each object classes
497 switch (object
->classId
)
499 case DatabaseRelationId
:
500 sepgsql_database_relabel(object
->objectId
, seclabel
);
503 case NamespaceRelationId
:
504 sepgsql_schema_relabel(object
->objectId
, seclabel
);
507 case RelationRelationId
:
508 if (object
->objectSubId
== 0)
509 sepgsql_relation_relabel(object
->objectId
,
512 sepgsql_attribute_relabel(object
->objectId
,
517 case ProcedureRelationId
:
518 sepgsql_proc_relabel(object
->objectId
, seclabel
);
523 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
524 errmsg("sepgsql provider does not support labels on %s",
525 getObjectTypeDescription(object
, false))));
531 * TEXT sepgsql_getcon(VOID)
533 * It returns the security label of the client.
535 PG_FUNCTION_INFO_V1(sepgsql_getcon
);
537 sepgsql_getcon(PG_FUNCTION_ARGS
)
541 if (!sepgsql_is_enabled())
544 client_label
= sepgsql_get_client_label();
546 PG_RETURN_TEXT_P(cstring_to_text(client_label
));
550 * BOOL sepgsql_setcon(TEXT)
552 * It switches the security label of the client.
554 PG_FUNCTION_INFO_V1(sepgsql_setcon
);
556 sepgsql_setcon(PG_FUNCTION_ARGS
)
558 const char *new_label
;
563 new_label
= TextDatumGetCString(PG_GETARG_DATUM(0));
565 sepgsql_set_client_label(new_label
);
567 PG_RETURN_BOOL(true);
571 * TEXT sepgsql_mcstrans_in(TEXT)
573 * It translate the given qualified MLS/MCS range into raw format
574 * when mcstrans daemon is working.
576 PG_FUNCTION_INFO_V1(sepgsql_mcstrans_in
);
578 sepgsql_mcstrans_in(PG_FUNCTION_ARGS
)
580 text
*label
= PG_GETARG_TEXT_PP(0);
584 if (!sepgsql_is_enabled())
586 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
587 errmsg("sepgsql is not enabled")));
589 if (selinux_trans_to_raw_context(text_to_cstring(label
),
592 (errcode(ERRCODE_INTERNAL_ERROR
),
593 errmsg("SELinux: could not translate security label: %m")));
597 result
= pstrdup(raw_label
);
605 PG_RETURN_TEXT_P(cstring_to_text(result
));
609 * TEXT sepgsql_mcstrans_out(TEXT)
611 * It translate the given raw MLS/MCS range into qualified format
612 * when mcstrans daemon is working.
614 PG_FUNCTION_INFO_V1(sepgsql_mcstrans_out
);
616 sepgsql_mcstrans_out(PG_FUNCTION_ARGS
)
618 text
*label
= PG_GETARG_TEXT_PP(0);
622 if (!sepgsql_is_enabled())
624 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
625 errmsg("sepgsql is not currently enabled")));
627 if (selinux_raw_to_trans_context(text_to_cstring(label
),
630 (errcode(ERRCODE_INTERNAL_ERROR
),
631 errmsg("SELinux: could not translate security label: %m")));
635 result
= pstrdup(qual_label
);
643 PG_RETURN_TEXT_P(cstring_to_text(result
));
649 * Concatenate as many of the given strings as aren't NULL, with dots between.
650 * Quote any of the strings that wouldn't be valid identifiers otherwise.
653 quote_object_name(const char *src1
, const char *src2
,
654 const char *src3
, const char *src4
)
656 StringInfoData result
;
658 initStringInfo(&result
);
660 appendStringInfoString(&result
, quote_identifier(src1
));
662 appendStringInfo(&result
, ".%s", quote_identifier(src2
));
664 appendStringInfo(&result
, ".%s", quote_identifier(src3
));
666 appendStringInfo(&result
, ".%s", quote_identifier(src4
));
671 * exec_object_restorecon
673 * This routine is a helper called by sepgsql_restorecon; it set up
674 * initial security labels of database objects within the supplied
678 exec_object_restorecon(struct selabel_handle
*sehnd
, Oid catalogId
)
683 char *database_name
= get_database_name(MyDatabaseId
);
684 char *namespace_name
;
689 * Open the target catalog. We don't want to allow writable accesses by
690 * other session during initial labeling.
692 rel
= table_open(catalogId
, AccessShareLock
);
694 sscan
= systable_beginscan(rel
, InvalidOid
, false,
696 while (HeapTupleIsValid(tuple
= systable_getnext(sscan
)))
698 Form_pg_database datForm
;
699 Form_pg_namespace nspForm
;
700 Form_pg_class relForm
;
701 Form_pg_attribute attForm
;
702 Form_pg_proc proForm
;
705 ObjectAddress object
;
709 * The way to determine object name depends on object classes. So, any
710 * branches set up `objtype', `objname' and `object' here.
714 case DatabaseRelationId
:
715 datForm
= (Form_pg_database
) GETSTRUCT(tuple
);
717 objtype
= SELABEL_DB_DATABASE
;
719 objname
= quote_object_name(NameStr(datForm
->datname
),
722 object
.classId
= DatabaseRelationId
;
723 object
.objectId
= datForm
->oid
;
724 object
.objectSubId
= 0;
727 case NamespaceRelationId
:
728 nspForm
= (Form_pg_namespace
) GETSTRUCT(tuple
);
730 objtype
= SELABEL_DB_SCHEMA
;
732 objname
= quote_object_name(database_name
,
733 NameStr(nspForm
->nspname
),
736 object
.classId
= NamespaceRelationId
;
737 object
.objectId
= nspForm
->oid
;
738 object
.objectSubId
= 0;
741 case RelationRelationId
:
742 relForm
= (Form_pg_class
) GETSTRUCT(tuple
);
744 if (relForm
->relkind
== RELKIND_RELATION
||
745 relForm
->relkind
== RELKIND_PARTITIONED_TABLE
)
746 objtype
= SELABEL_DB_TABLE
;
747 else if (relForm
->relkind
== RELKIND_SEQUENCE
)
748 objtype
= SELABEL_DB_SEQUENCE
;
749 else if (relForm
->relkind
== RELKIND_VIEW
)
750 objtype
= SELABEL_DB_VIEW
;
752 continue; /* no need to assign security label */
754 namespace_name
= get_namespace_name(relForm
->relnamespace
);
755 objname
= quote_object_name(database_name
,
757 NameStr(relForm
->relname
),
759 pfree(namespace_name
);
761 object
.classId
= RelationRelationId
;
762 object
.objectId
= relForm
->oid
;
763 object
.objectSubId
= 0;
766 case AttributeRelationId
:
767 attForm
= (Form_pg_attribute
) GETSTRUCT(tuple
);
769 if (get_rel_relkind(attForm
->attrelid
) != RELKIND_RELATION
&&
770 get_rel_relkind(attForm
->attrelid
) != RELKIND_PARTITIONED_TABLE
)
771 continue; /* no need to assign security label */
773 objtype
= SELABEL_DB_COLUMN
;
775 namespace_id
= get_rel_namespace(attForm
->attrelid
);
776 namespace_name
= get_namespace_name(namespace_id
);
777 relation_name
= get_rel_name(attForm
->attrelid
);
778 objname
= quote_object_name(database_name
,
781 NameStr(attForm
->attname
));
782 pfree(namespace_name
);
783 pfree(relation_name
);
785 object
.classId
= RelationRelationId
;
786 object
.objectId
= attForm
->attrelid
;
787 object
.objectSubId
= attForm
->attnum
;
790 case ProcedureRelationId
:
791 proForm
= (Form_pg_proc
) GETSTRUCT(tuple
);
793 objtype
= SELABEL_DB_PROCEDURE
;
795 namespace_name
= get_namespace_name(proForm
->pronamespace
);
796 objname
= quote_object_name(database_name
,
798 NameStr(proForm
->proname
),
800 pfree(namespace_name
);
802 object
.classId
= ProcedureRelationId
;
803 object
.objectId
= proForm
->oid
;
804 object
.objectSubId
= 0;
808 elog(ERROR
, "unexpected catalog id: %u", catalogId
);
809 objname
= NULL
; /* for compiler quiet */
813 if (selabel_lookup_raw(sehnd
, &context
, objname
, objtype
) == 0)
818 * Check SELinux permission to relabel the fetched object,
819 * then do the actual relabeling.
821 sepgsql_object_relabel(&object
, context
);
823 SetSecurityLabel(&object
, SEPGSQL_LABEL_TAG
, context
);
831 else if (errno
== ENOENT
)
833 (errmsg("SELinux: no initial label assigned for %s (type=%d), skipping",
837 (errcode(ERRCODE_INTERNAL_ERROR
),
838 errmsg("SELinux: could not determine initial security label for %s (type=%d): %m", objname
, objtype
)));
842 systable_endscan(sscan
);
844 table_close(rel
, NoLock
);
848 * BOOL sepgsql_restorecon(TEXT specfile)
850 * This function tries to assign initial security labels on all the object
851 * within the current database, according to the system setting.
852 * It is typically invoked by sepgsql-install script just after initdb, to
853 * assign initial security labels.
855 * If @specfile is not NULL, it uses explicitly specified specfile, instead
856 * of the system default.
858 PG_FUNCTION_INFO_V1(sepgsql_restorecon
);
860 sepgsql_restorecon(PG_FUNCTION_ARGS
)
862 struct selabel_handle
*sehnd
;
863 struct selinux_opt seopts
;
866 * SELinux has to be enabled on the running platform.
868 if (!sepgsql_is_enabled())
870 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
871 errmsg("sepgsql is not currently enabled")));
874 * Check DAC permission. Only superuser can set up initial security
875 * labels, like root-user in filesystems
879 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
880 errmsg("SELinux: must be superuser to restore initial contexts")));
883 * Open selabel_lookup(3) stuff. It provides a set of mapping between an
884 * initial security label and object class/name due to the system setting.
888 seopts
.type
= SELABEL_OPT_UNUSED
;
893 seopts
.type
= SELABEL_OPT_PATH
;
894 seopts
.value
= TextDatumGetCString(PG_GETARG_DATUM(0));
896 sehnd
= selabel_open(SELABEL_CTX_DB
, &seopts
, 1);
899 (errcode(ERRCODE_INTERNAL_ERROR
),
900 errmsg("SELinux: failed to initialize labeling handle: %m")));
903 exec_object_restorecon(sehnd
, DatabaseRelationId
);
904 exec_object_restorecon(sehnd
, NamespaceRelationId
);
905 exec_object_restorecon(sehnd
, RelationRelationId
);
906 exec_object_restorecon(sehnd
, AttributeRelationId
);
907 exec_object_restorecon(sehnd
, ProcedureRelationId
);
911 selabel_close(sehnd
);
915 PG_RETURN_BOOL(true);