1 /* -------------------------------------------------------------------------
3 * contrib/sepgsql/label.c
5 * Routines to support SELinux labels (security context)
7 * Copyright (c) 2010-2013, PostgreSQL Global Development Group
9 * -------------------------------------------------------------------------
13 #include "access/heapam.h"
14 #include "access/htup_details.h"
15 #include "access/genam.h"
16 #include "access/xact.h"
17 #include "catalog/catalog.h"
18 #include "catalog/dependency.h"
19 #include "catalog/indexing.h"
20 #include "catalog/pg_attribute.h"
21 #include "catalog/pg_class.h"
22 #include "catalog/pg_database.h"
23 #include "catalog/pg_namespace.h"
24 #include "catalog/pg_proc.h"
25 #include "commands/dbcommands.h"
26 #include "commands/seclabel.h"
27 #include "libpq/auth.h"
28 #include "libpq/libpq-be.h"
29 #include "miscadmin.h"
30 #include "utils/builtins.h"
31 #include "utils/fmgroids.h"
32 #include "utils/guc.h"
33 #include "utils/lsyscache.h"
34 #include "utils/memutils.h"
35 #include "utils/rel.h"
36 #include "utils/tqual.h"
40 #include <selinux/label.h>
43 * Saved hook entries (if stacked)
45 static ClientAuthentication_hook_type next_client_auth_hook = NULL;
46 static needs_fmgr_hook_type next_needs_fmgr_hook = NULL;
47 static fmgr_hook_type next_fmgr_hook = NULL;
52 * security label of the database client. Initially the client security label
53 * is equal to client_label_peer, and can be changed by one or more calls to
54 * sepgsql_setcon(), and also be temporarily overridden during execution of a
57 * sepgsql_setcon() is a transaction-aware operation; a (sub-)transaction
58 * rollback should also rollback the current client security label. Therefore
59 * we use the list client_label_pending of pending_label to keep track of which
60 * labels were set during the (sub-)transactions.
62 static char *client_label_peer = NULL; /* set by getpeercon(3) */
63 static List *client_label_pending = NIL; /* pending list being set by
65 static char *client_label_committed = NULL; /* set by sepgsql_setcon(),
66 * and already committed */
67 static char *client_label_func = NULL; /* set by trusted procedure */
71 SubTransactionId subid;
76 * sepgsql_get_client_label
78 * Returns the current security label of the client. All code should use this
79 * routine to get the current label, instead of referring to the client_label_*
83 sepgsql_get_client_label(void)
85 /* trusted procedure client label override */
86 if (client_label_func)
87 return client_label_func;
89 /* uncommitted sepgsql_setcon() value */
90 if (client_label_pending)
92 pending_label *plabel = llast(client_label_pending);
97 else if (client_label_committed)
98 return client_label_committed; /* set by sepgsql_setcon() committed */
101 Assert(client_label_peer != NULL);
102 return client_label_peer;
106 * sepgsql_set_client_label
108 * This routine tries to switch the current security label of the client, and
109 * checks related permissions. The supplied new label shall be added to the
110 * client_label_pending list, then saved at transaction-commit time to ensure
111 * transaction-awareness.
114 sepgsql_set_client_label(const char *new_label)
116 const char *tcontext;
117 MemoryContext oldcxt;
118 pending_label *plabel;
120 /* Reset to the initial client label, if NULL */
122 tcontext = client_label_peer;
125 if (security_check_context_raw((security_context_t) new_label) < 0)
127 (errcode(ERRCODE_INVALID_NAME),
128 errmsg("SELinux: invalid security label: \"%s\"",
130 tcontext = new_label;
133 /* Check process:{setcurrent} permission. */
134 sepgsql_avc_check_perms_label(sepgsql_get_client_label(),
136 SEPG_PROCESS__SETCURRENT,
139 /* Check process:{dyntransition} permission. */
140 sepgsql_avc_check_perms_label(tcontext,
142 SEPG_PROCESS__DYNTRANSITION,
147 * Append the supplied new_label on the pending list until the current
148 * transaction is committed.
150 oldcxt = MemoryContextSwitchTo(CurTransactionContext);
152 plabel = palloc0(sizeof(pending_label));
153 plabel->subid = GetCurrentSubTransactionId();
155 plabel->label = pstrdup(new_label);
156 client_label_pending = lappend(client_label_pending, plabel);
158 MemoryContextSwitchTo(oldcxt);
162 * sepgsql_xact_callback
164 * A callback routine of transaction commit/abort/prepare. Commmit or abort
165 * changes in the client_label_pending list.
168 sepgsql_xact_callback(XactEvent event, void *arg)
170 if (event == XACT_EVENT_COMMIT)
172 if (client_label_pending != NIL)
174 pending_label *plabel = llast(client_label_pending);
178 new_label = MemoryContextStrdup(TopMemoryContext,
183 if (client_label_committed)
184 pfree(client_label_committed);
186 client_label_committed = new_label;
189 * XXX - Note that items of client_label_pending are allocated on
190 * CurTransactionContext, thus, all acquired memory region shall
191 * be released implicitly.
193 client_label_pending = NIL;
196 else if (event == XACT_EVENT_ABORT)
197 client_label_pending = NIL;
201 * sepgsql_subxact_callback
203 * A callback routine of sub-transaction start/abort/commit. Releases all
204 * security labels that are set within the sub-transaction that is aborted.
207 sepgsql_subxact_callback(SubXactEvent event, SubTransactionId mySubid,
208 SubTransactionId parentSubid, void *arg)
214 if (event == SUBXACT_EVENT_ABORT_SUB)
217 for (cell = list_head(client_label_pending); cell; cell = next)
219 pending_label *plabel = lfirst(cell);
223 if (plabel->subid == mySubid)
225 = list_delete_cell(client_label_pending, cell, prev);
233 * sepgsql_client_auth
235 * Entrypoint of the client authentication hook.
236 * It switches the client label according to getpeercon(), and the current
237 * performing mode according to the GUC setting.
240 sepgsql_client_auth(Port *port, int status)
242 if (next_client_auth_hook)
243 (*next_client_auth_hook) (port, status);
246 * In the case when authentication failed, the supplied socket shall be
247 * closed soon, so we don't need to do anything here.
249 if (status != STATUS_OK)
253 * Getting security label of the peer process using API of libselinux.
255 if (getpeercon_raw(port->sock, &client_label_peer) < 0)
257 (errcode(ERRCODE_INTERNAL_ERROR),
258 errmsg("SELinux: unable to get peer label: %m")));
261 * Switch the current performing mode from INTERNAL to either DEFAULT or
264 if (sepgsql_get_permissive())
265 sepgsql_set_mode(SEPGSQL_MODE_PERMISSIVE);
267 sepgsql_set_mode(SEPGSQL_MODE_DEFAULT);
271 * sepgsql_needs_fmgr_hook
273 * It informs the core whether the supplied function is trusted procedure,
274 * or not. If true, sepgsql_fmgr_hook shall be invoked at start, end, and
275 * abort time of function invocation.
278 sepgsql_needs_fmgr_hook(Oid functionId)
280 ObjectAddress object;
282 if (next_needs_fmgr_hook &&
283 (*next_needs_fmgr_hook) (functionId))
287 * SELinux needs the function to be called via security_definer wrapper,
288 * if this invocation will take a domain-transition. We call these
289 * functions as trusted-procedure, if the security policy has a rule that
290 * switches security label of the client on execution.
292 if (sepgsql_avc_trusted_proc(functionId) != NULL)
296 * Even if not a trusted-procedure, this function should not be inlined
297 * unless the client has db_procedure:{execute} permission. Please note
298 * that it shall be actually failed later because of same reason with
301 object.classId = ProcedureRelationId;
302 object.objectId = functionId;
303 object.objectSubId = 0;
304 if (!sepgsql_avc_check_perms(&object,
305 SEPG_CLASS_DB_PROCEDURE,
306 SEPG_DB_PROCEDURE__EXECUTE,
307 SEPGSQL_AVC_NOAUDIT, false))
316 * It switches security label of the client on execution of trusted
320 sepgsql_fmgr_hook(FmgrHookEventType event,
321 FmgrInfo *flinfo, Datum *private)
333 stack = (void *) DatumGetPointer(*private);
336 MemoryContext oldcxt;
338 oldcxt = MemoryContextSwitchTo(flinfo->fn_mcxt);
339 stack = palloc(sizeof(*stack));
340 stack->old_label = NULL;
341 stack->new_label = sepgsql_avc_trusted_proc(flinfo->fn_oid);
342 stack->next_private = 0;
344 MemoryContextSwitchTo(oldcxt);
347 * process:transition permission between old and new label,
348 * when user tries to switch security label of the client on
349 * execution of trusted procedure.
351 if (stack->new_label)
352 sepgsql_avc_check_perms_label(stack->new_label,
354 SEPG_PROCESS__TRANSITION,
357 *private = PointerGetDatum(stack);
359 Assert(!stack->old_label);
360 if (stack->new_label)
362 stack->old_label = client_label_func;
363 client_label_func = stack->new_label;
366 (*next_fmgr_hook) (event, flinfo, &stack->next_private);
371 stack = (void *) DatumGetPointer(*private);
374 (*next_fmgr_hook) (event, flinfo, &stack->next_private);
376 if (stack->new_label)
378 client_label_func = stack->old_label;
379 stack->old_label = NULL;
384 elog(ERROR, "unexpected event type: %d", (int) event);
390 * sepgsql_init_client_label
392 * Initializes the client security label and sets up related hooks for client
396 sepgsql_init_client_label(void)
399 * Set up dummy client label.
401 * XXX - note that PostgreSQL launches background worker process like
402 * autovacuum without authentication steps. So, we initialize sepgsql_mode
403 * with SEPGSQL_MODE_INTERNAL, and client_label with the security context
404 * of server process. Later, it also launches background of user session.
405 * In this case, the process is always hooked on post-authentication, and
406 * we can initialize the sepgsql_mode and client_label correctly.
408 if (getcon_raw(&client_label_peer) < 0)
410 (errcode(ERRCODE_INTERNAL_ERROR),
411 errmsg("SELinux: failed to get server security label: %m")));
413 /* Client authentication hook */
414 next_client_auth_hook = ClientAuthentication_hook;
415 ClientAuthentication_hook = sepgsql_client_auth;
417 /* Trusted procedure hooks */
418 next_needs_fmgr_hook = needs_fmgr_hook;
419 needs_fmgr_hook = sepgsql_needs_fmgr_hook;
421 next_fmgr_hook = fmgr_hook;
422 fmgr_hook = sepgsql_fmgr_hook;
424 /* Transaction/Sub-transaction callbacks */
425 RegisterXactCallback(sepgsql_xact_callback, NULL);
426 RegisterSubXactCallback(sepgsql_subxact_callback, NULL);
432 * It returns a security context of the specified database object.
433 * If unlabeled or incorrectly labeled, the system "unlabeled" label
437 sepgsql_get_label(Oid classId, Oid objectId, int32 subId)
439 ObjectAddress object;
442 object.classId = classId;
443 object.objectId = objectId;
444 object.objectSubId = subId;
446 label = GetSecurityLabel(&object, SEPGSQL_LABEL_TAG);
447 if (!label || security_check_context_raw((security_context_t) label))
449 security_context_t unlabeled;
451 if (security_get_initial_context_raw("unlabeled", &unlabeled) < 0)
453 (errcode(ERRCODE_INTERNAL_ERROR),
454 errmsg("SELinux: failed to get initial security label: %m")));
457 label = pstrdup(unlabeled);
472 * sepgsql_object_relabel
474 * An entrypoint of SECURITY LABEL statement
477 sepgsql_object_relabel(const ObjectAddress *object, const char *seclabel)
480 * validate format of the supplied security label, if it is security
481 * context of selinux.
484 security_check_context_raw((security_context_t) seclabel) < 0)
486 (errcode(ERRCODE_INVALID_NAME),
487 errmsg("SELinux: invalid security label: \"%s\"", seclabel)));
490 * Do actual permission checks for each object classes
492 switch (object->classId)
494 case DatabaseRelationId:
495 sepgsql_database_relabel(object->objectId, seclabel);
498 case NamespaceRelationId:
499 sepgsql_schema_relabel(object->objectId, seclabel);
502 case RelationRelationId:
503 if (object->objectSubId == 0)
504 sepgsql_relation_relabel(object->objectId,
507 sepgsql_attribute_relabel(object->objectId,
512 case ProcedureRelationId:
513 sepgsql_proc_relabel(object->objectId, seclabel);
517 elog(ERROR, "unsupported object type: %u", object->classId);
523 * TEXT sepgsql_getcon(VOID)
525 * It returns the security label of the client.
527 PG_FUNCTION_INFO_V1(sepgsql_getcon);
529 sepgsql_getcon(PG_FUNCTION_ARGS)
533 if (!sepgsql_is_enabled())
536 client_label = sepgsql_get_client_label();
538 PG_RETURN_TEXT_P(cstring_to_text(client_label));
542 * BOOL sepgsql_setcon(TEXT)
544 * It switches the security label of the client.
546 PG_FUNCTION_INFO_V1(sepgsql_setcon);
548 sepgsql_setcon(PG_FUNCTION_ARGS)
550 const char *new_label;
555 new_label = TextDatumGetCString(PG_GETARG_DATUM(0));
557 sepgsql_set_client_label(new_label);
559 PG_RETURN_BOOL(true);
563 * TEXT sepgsql_mcstrans_in(TEXT)
565 * It translate the given qualified MLS/MCS range into raw format
566 * when mcstrans daemon is working.
568 PG_FUNCTION_INFO_V1(sepgsql_mcstrans_in);
570 sepgsql_mcstrans_in(PG_FUNCTION_ARGS)
572 text *label = PG_GETARG_TEXT_P(0);
576 if (!sepgsql_is_enabled())
578 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
579 errmsg("sepgsql is not enabled")));
581 if (selinux_trans_to_raw_context(text_to_cstring(label),
584 (errcode(ERRCODE_INTERNAL_ERROR),
585 errmsg("SELinux: could not translate security label: %m")));
589 result = pstrdup(raw_label);
599 PG_RETURN_TEXT_P(cstring_to_text(result));
603 * TEXT sepgsql_mcstrans_out(TEXT)
605 * It translate the given raw MLS/MCS range into qualified format
606 * when mcstrans daemon is working.
608 PG_FUNCTION_INFO_V1(sepgsql_mcstrans_out);
610 sepgsql_mcstrans_out(PG_FUNCTION_ARGS)
612 text *label = PG_GETARG_TEXT_P(0);
616 if (!sepgsql_is_enabled())
618 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
619 errmsg("sepgsql is not currently enabled")));
621 if (selinux_raw_to_trans_context(text_to_cstring(label),
624 (errcode(ERRCODE_INTERNAL_ERROR),
625 errmsg("SELinux: could not translate security label: %m")));
629 result = pstrdup(qual_label);
639 PG_RETURN_TEXT_P(cstring_to_text(result));
645 * It tries to quote the supplied identifiers
648 quote_object_name(const char *src1, const char *src2,
649 const char *src3, const char *src4)
651 StringInfoData result;
654 initStringInfo(&result);
658 temp = quote_identifier(src1);
659 appendStringInfo(&result, "%s", temp);
661 pfree((void *) temp);
665 temp = quote_identifier(src2);
666 appendStringInfo(&result, ".%s", temp);
668 pfree((void *) temp);
672 temp = quote_identifier(src3);
673 appendStringInfo(&result, ".%s", temp);
675 pfree((void *) temp);
679 temp = quote_identifier(src4);
680 appendStringInfo(&result, ".%s", temp);
682 pfree((void *) temp);
688 * exec_object_restorecon
690 * This routine is a helper called by sepgsql_restorecon; it set up
691 * initial security labels of database objects within the supplied
695 exec_object_restorecon(struct selabel_handle * sehnd, Oid catalogId)
700 char *database_name = get_database_name(MyDatabaseId);
701 char *namespace_name;
706 * Open the target catalog. We don't want to allow writable accesses by
707 * other session during initial labeling.
709 rel = heap_open(catalogId, AccessShareLock);
711 sscan = systable_beginscan(rel, InvalidOid, false,
712 SnapshotNow, 0, NULL);
713 while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
715 Form_pg_database datForm;
716 Form_pg_namespace nspForm;
717 Form_pg_class relForm;
718 Form_pg_attribute attForm;
719 Form_pg_proc proForm;
722 ObjectAddress object;
723 security_context_t context;
726 * The way to determine object name depends on object classes. So, any
727 * branches set up `objtype', `objname' and `object' here.
731 case DatabaseRelationId:
732 datForm = (Form_pg_database) GETSTRUCT(tuple);
734 objtype = SELABEL_DB_DATABASE;
736 objname = quote_object_name(NameStr(datForm->datname),
739 object.classId = DatabaseRelationId;
740 object.objectId = HeapTupleGetOid(tuple);
741 object.objectSubId = 0;
744 case NamespaceRelationId:
745 nspForm = (Form_pg_namespace) GETSTRUCT(tuple);
747 objtype = SELABEL_DB_SCHEMA;
749 objname = quote_object_name(database_name,
750 NameStr(nspForm->nspname),
753 object.classId = NamespaceRelationId;
754 object.objectId = HeapTupleGetOid(tuple);
755 object.objectSubId = 0;
758 case RelationRelationId:
759 relForm = (Form_pg_class) GETSTRUCT(tuple);
761 if (relForm->relkind == RELKIND_RELATION)
762 objtype = SELABEL_DB_TABLE;
763 else if (relForm->relkind == RELKIND_SEQUENCE)
764 objtype = SELABEL_DB_SEQUENCE;
765 else if (relForm->relkind == RELKIND_VIEW)
766 objtype = SELABEL_DB_VIEW;
768 continue; /* no need to assign security label */
770 namespace_name = get_namespace_name(relForm->relnamespace);
771 objname = quote_object_name(database_name,
773 NameStr(relForm->relname),
775 pfree(namespace_name);
777 object.classId = RelationRelationId;
778 object.objectId = HeapTupleGetOid(tuple);
779 object.objectSubId = 0;
782 case AttributeRelationId:
783 attForm = (Form_pg_attribute) GETSTRUCT(tuple);
785 if (get_rel_relkind(attForm->attrelid) != RELKIND_RELATION)
786 continue; /* no need to assign security label */
788 objtype = SELABEL_DB_COLUMN;
790 namespace_id = get_rel_namespace(attForm->attrelid);
791 namespace_name = get_namespace_name(namespace_id);
792 relation_name = get_rel_name(attForm->attrelid);
793 objname = quote_object_name(database_name,
796 NameStr(attForm->attname));
797 pfree(namespace_name);
798 pfree(relation_name);
800 object.classId = RelationRelationId;
801 object.objectId = attForm->attrelid;
802 object.objectSubId = attForm->attnum;
805 case ProcedureRelationId:
806 proForm = (Form_pg_proc) GETSTRUCT(tuple);
808 objtype = SELABEL_DB_PROCEDURE;
810 namespace_name = get_namespace_name(proForm->pronamespace);
811 objname = quote_object_name(database_name,
813 NameStr(proForm->proname),
815 pfree(namespace_name);
817 object.classId = ProcedureRelationId;
818 object.objectId = HeapTupleGetOid(tuple);
819 object.objectSubId = 0;
823 elog(ERROR, "unexpected catalog id: %u", catalogId);
824 objname = NULL; /* for compiler quiet */
828 if (selabel_lookup_raw(sehnd, &context, objname, objtype) == 0)
833 * Check SELinux permission to relabel the fetched object,
834 * then do the actual relabeling.
836 sepgsql_object_relabel(&object, context);
838 SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, context);
848 else if (errno == ENOENT)
850 (errmsg("SELinux: no initial label assigned for %s (type=%d), skipping",
854 (errcode(ERRCODE_INTERNAL_ERROR),
855 errmsg("SELinux: could not determine initial security label for %s (type=%d): %m", objname, objtype)));
859 systable_endscan(sscan);
861 heap_close(rel, NoLock);
865 * BOOL sepgsql_restorecon(TEXT specfile)
867 * This function tries to assign initial security labels on all the object
868 * within the current database, according to the system setting.
869 * It is typically invoked by sepgsql-install script just after initdb, to
870 * assign initial security labels.
872 * If @specfile is not NULL, it uses explicitly specified specfile, instead
873 * of the system default.
875 PG_FUNCTION_INFO_V1(sepgsql_restorecon);
877 sepgsql_restorecon(PG_FUNCTION_ARGS)
879 struct selabel_handle *sehnd;
880 struct selinux_opt seopts;
883 * SELinux has to be enabled on the running platform.
885 if (!sepgsql_is_enabled())
887 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
888 errmsg("sepgsql is not currently enabled")));
891 * Check DAC permission. Only superuser can set up initial security
892 * labels, like root-user in filesystems
896 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
897 errmsg("SELinux: must be superuser to restore initial contexts")));
900 * Open selabel_lookup(3) stuff. It provides a set of mapping between an
901 * initial security label and object class/name due to the system setting.
905 seopts.type = SELABEL_OPT_UNUSED;
910 seopts.type = SELABEL_OPT_PATH;
911 seopts.value = TextDatumGetCString(PG_GETARG_DATUM(0));
913 sehnd = selabel_open(SELABEL_CTX_DB, &seopts, 1);
916 (errcode(ERRCODE_INTERNAL_ERROR),
917 errmsg("SELinux: failed to initialize labeling handle: %m")));
920 exec_object_restorecon(sehnd, DatabaseRelationId);
921 exec_object_restorecon(sehnd, NamespaceRelationId);
922 exec_object_restorecon(sehnd, RelationRelationId);
923 exec_object_restorecon(sehnd, AttributeRelationId);
924 exec_object_restorecon(sehnd, ProcedureRelationId);
928 selabel_close(sehnd);
933 selabel_close(sehnd);
935 PG_RETURN_BOOL(true);